(一)数值表示与函数任务

本文详细介绍了Verilog HDL中的数值表示,包括不同类型的数值(0、1、x、z)、数据类型(如wire、reg、integer、real、time等)、表达式与操作符、以及function和task的使用。function和task用于提取重复性设计,提高代码可读性。此外,还讨论了常数函数和automatic函数的特点,以及各种循环语句的用法,如while、for、repeat、forever和generate。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于语言的设计易于移植(设计团队可以重用或修改以前的设计,以保持更先进工艺的一致性)且不依赖于工艺(随着器件物理尺寸的缩小,电路密度的提高,基于原有HDL模型进行综合生成的电路同样具有更高的性能)。

目录

数值表示

数据类型

表达式与操作符

function与task

function

task

常数函数

automatic函数

循环语句


数值表示

数值种类:0、1、x(未知)、z(高阻)

  1. 整数数值表示方法:十进制'd/'D,十六进制'h/'H,二进制'b/'B,八进制'o/'O
  2. 负数表示:-6'd15,-15
  3. 实数表示:
    1. 十进制:30.123,6.0,0.01
    2. 科学计数法:
      1. 1.2e4//大小为12000
      2. 1_0001e4//大小为10010000
      3. 1E-3//大小为0.00
  4. 字符串表示
reg[0:14*8-1]    str;
initial begin
    str="www.runoob.com";
end

数据类型

  1. 线网wire
  2. 寄存器reg:无符号数。
  3. 向量(当位宽大于1时,wire或reg即可声明为向量形式)
//1.verilog支持bit位后固定位宽的向量域选择访问
    [bit+:width] 从起始位bit开始递增,位宽位width
    [bit-:width] 从起始位bit开始递减,位宽位width
//下面两种赋值等效
    A=data1[31-:8];
    A=data1[31:24];
//2.对信号重新进行组合成新的向量时,需要借助大括号
    wire[31:0]    temp1,temp2;
    assign    temp1={byte1[0][7:0],data1[31:8]};//数据拼接
    assign    temp2={32{1'b0}};//赋值32位的数值0
  1. 整数integer:声明时不用指明位宽,位宽和编译器有关,一般为32bit。有符号数。
  2. 实数real:可用十进制或科学计数法来表示,实数声明不能带有范围,默认值为0。如果将一个实数赋值给一个整数,则只有实数的整数部分会赋值给整数。
real    data1;
integer    temp;
initial begin
    data1 = 2e3;
    data2 = 3.75;
end
initial begin
    temp = data1;//temp大小为3
end
  1. 时间time:Verilog使用特殊的时间寄存器time型变量,对仿真时间进行保存,其宽度一般为64bit,通过调用系统函数$time获取当前仿真时间。
time     current_time;
initial begin
    #100
    current_time=$time;//current_time大小为100
end
  1. 数组
integer          flag [7:0] ; //8个整数组成的数组
reg  [3:0]       counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0]       addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire             data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0]       data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组
flag [1]   = 32'd0 ; //将flag数组中第二个元素赋值为32bit的0值
counter[3] = 4'hF ;  //将数组counter中第4个元素的值赋值为4bit 十六进制数F,等效于counter[3][3:0] = 4'hF,即可省略宽度;
assign addr_bus[0]        = 8'b0 ; //将数组addr_bus中第一个元素的值赋值为0
assign data_bit[0][1]     = 1'b1;  //将数组data_bit的第1行第2列的元素赋值为1,这里不能省略第二个访问标号,即 assign data_bit[0] = 1'b1; 是非法的。
data_4d[0][0][0][0][15:0] = 15'd3 ;  //将数组data_4d中标号为[0][0][0][0]的寄存器单元的15~0bit赋值为3
  1. 存储器:本质是寄存器数组,可用来描述RAM或ROM的行为。
reg     membit[0:255];//256个的1bit存储器
reg[7:0]    mem[0:1023];1kbit存储器,有1024个位宽8bit的存储器
mem[511]=8'b0;//令第512个8bit的存储单元值为0
  1. 参数parameter
  2. 字符串

表达式与操作符

参考https://www.runoob.com/w3cnote/verilog-expression.html

function与task

在verilog中可以利用task或者function将重复性的行为级设计进行提取并在多个地方调用,来避免重复代码的多次编写,使得代码更加的简洁易懂。

functiontask
不可以在module外定义同function
不能含有时序逻辑(时延、非阻塞赋值、时序逻辑等)可以含有时序逻辑,但不能有always块
至少一个input,有返回值,没有输出可没有或有多个输入输出,可存在inout类型
不可以做为语句单独出现,只能出现在赋值语句右侧可以作为语句单独出现
不可以调用task,可调用function可以调用task与function

function

//返回值为宽度range的function_id,默认宽度为1
function[range-1:0]    function_id;
    input_declaration;
    other_declaration;
    procedural_statement;
endfunction

task

进行task的逻辑设计时可以把input看成wire型,output看成reg型,但是不需要用reg对output端口进行再次说明。

对output信号赋值时也不要使用关键字assign,为避免时序错乱,建议output信号采用阻塞赋值。

任务调用时,端口必须按顺序对应。输入端连接的模块内信号可以是 wire 型,也可以是 reg 型。输出端连接的模块内信号要求一定是 reg 型,这点需要注意。

task task_id;
    port_declaration;
    procedural_statement;
endtask

//实例1
task xor_poer_iner;
    input[N-1:0]    numa;
    input[N-1:0]    numb;
    output[N-1:0]    numo;
    //output reg [N-1:0]  numco ; //无需再注明 reg 类型,虽然注明也可能没错
    #3    numco=numa^numb;
    //assign #3 numco = numa ^ numb ; //不用assign,因为输出默认是reg
endtask
//实例2
task xor_oper_iner(
    input [N-1:0]   numa,
    input [N-1:0]   numb,
    output [N-1:0]  numco  ) ;
    #3  numco       = numa ^ numb ;
endtask

常数函数

仿真开始之前,在编译期间就计算出结果为常数的函数。

常数函数不允许访问全局变量或者调用系统函数,但是可以调用另一个常数函数。

//计算模块中地址总线的宽度
parameter    MEM_DEPTH = 256 ;
reg  [logb2(MEM_DEPTH)-1: 0] addr ; //可得addr的宽度为8bit
 
    function integer     logb2;
    input integer     depth ;
        //256为9bit,我们最终数据应该是8,所以需depth=2时提前停止循环
    for(logb2=0; depth>1; logb2=logb2+1) begin
        depth = depth >> 1 ;
    end
endfunction

automatic函数

一般函数的局部变量是静态的,即函数每次调用时,函数的局部变量都会使用同一个存储空间,若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。

使用automatic关键字来对函数进行说明,此类函数在调用时可以自动分配新的内存空间,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。

wire [31:0]          results3 = factorial(4);
function automatic   integer         factorial ;
    input integer     data ;
    integer           i ;
    begin
        factorial = (data>=2)? data * factorial(data-1) : 1 ;
    end
endfunction // factorial

循环语句

  • while
while(condition)begin


end
  • for
for(;;)begin

end
  • repeat
repeat(loop_times固定次数)begin

    //执行期间loops发生变化,repeat次数也不会改变

end
  • forever
forever begin
    //$finish可以退出forever
end
  • generate

通过generate循环可以产生一个对象(比如一个元件或者一个模块)的多次例化,一般在循环和条件语句中使用。

同一个文件中genvar循环变量名不能重复,也就是说,有几个循环就需要几个不同的genvar变量;

genvar定义的变量不要在always语句中使用,可以用integer变量代替

moudule buffer_1(
    input    in,
    output    out);
    assign out=~in;
endmodule
module buffer_8(
    input[7:0]    din,
    output[7:0]    dout);
    //循环变量i必须是genvar类型的,不可以是reg或integer
    genvar    i;
    generate
        for(i=0;i<8;i=i+1)begin:BLOCK1//for要有名字
            buffer_1 buffer_1_1(.in(din[i]),.out(dout[i]));
        end
    endgenerate
endmodule

//testbench
module buffer_8_tb;
    reg[7:0]    din;
    wire[7:0]    dout;
    //Instance the DUT
    buffer_8 buffer_8_1(.din(din),.dout(dout));
    //generate the stimulate
    initial begin
        din=8'd7;
        #10;din=8'd6;
        #10;din=8'd4;
        #10;din=8'd7;
        #10;$finish();
    end
endmodule

在generate语句中可以出现以下三种语句:

generate
//generate...loop循环语句
 
//generate...case分支语句
    case(condition)
    
    endcase
 
//generate...conditional条件语句
    if(condition)    statements//condition必须是静态条件
    else             statements
 
//嵌套的generate语句
endgenerate

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值