gaochy1126 发表于 2025-4-30 23:13

verilog 语法基础汇总

一.Verilog语法基础

1. 逻辑值

   ```
    0:逻辑低电平,条件为假



    1:逻辑高电平,条件为真



    z:高阻态,无驱动

    x:未知逻辑电平
   ```
2. 归约运算符,按位运算符

   ```
    以&为例,当&作为一元运算符时表示归约与,&m是将m中所有比特位相与,最后的结果为1bit



    例如:



            &4'b1111=1&1&1&1=1'b1



            &4b'1101=1&1&0&1=1'b0



    当&作为二元运算符时表示按位与,m&n是将m的每个比特位与n的相应比特位相与,在运算时要保证m和n的比特位数相等,最后的结果与m和n的比特位数相同



    例如:



            4b'1010&4b'0101=4b'0000



            4b'1101&4b'1111=4b'1101



    其它例如'~&' , '^' , '~^' , '|', '~|' 在分别作为一元和二元运算符时也有和'&'一样的性质
   ```
3. 逻辑运算符和关系运算符

   ```
    具有'&&'形式的操作符表示逻辑与



    例如:



            a=a'ha, b=4'd0, c=a&&b运算结果是c=0



    关系运算符(逻辑运算符)的使用方法和C语言完全类似
   ```
4. 移位运算符

   ```
    移位运算符时二元运算符,左移符号为'<<',右移符号为'>>',结果是将运算符左边的操作数左移或右移指定的位数,用0来填补空位。



    b <= a<<1;   #作用是将a的每一位都向左移动1位,结果赋值给b



    b <= a>>2;   #作用是将a的每一位都向右移动1位,结果赋值给b
   ```

需要注意的是:

```
    1)移位运算造成的空位自动用0来填充,因此任意一个二进制数只要一直移位其值最终都会变成0。



    例如: a=4'b1000,a>>3的结果为 4'b0001;a>>4的结果为4'b0000



    2)移位运算符在使用时可代替乘法和除法,左移一位看作是乘以2,右移移位可以看作是除以2,但是仍然要注意位宽的拓展。
```

5.位拼接运算符

```
    位拼接运算符由一对花括号加逗号组成,形式为’{, }', 拼接的不同数据之间用','隔开。



    例如:



            将8bit的a、3bit的b、5bit的c按顺序拼接成一个16位的d,表示方法为:d={a,b,c};
```

6. 条件运算符

   ```
    一般格式:(逻辑判断语句)? 语句1 : 语句2



    执行过程是:当[逻辑判断语句]为真,则[语句1]作为条件表达式的值,否则将[语句2]作为表达式的值。



    例如: a=6, b=7, c=(a>b)?a:b 语句执行后c=7
   ```
7. 运算符优先级

   ```
    归约运算符 > 算术运算符 > 移位运算符 > 关系运算符 > ’==‘ 和'!='>按位运算符>'&&' 和 '||' > 条件运算符



    归纳起来就是一元运算符 > 二元运算符 > 三元运算符。



    运算符优先级往往不需要准确**,通过括号增加优先级是更为简单且准确的方法。
   ```
8. case分支语句

   ```
    格式:



            case(<控制表达式>)



                  <分支语句1> : 语句块1;



                  <分支语句2> : 语句块2;



                     ......



                  <分支语句n> : 语句块n;



                  default : 语句块n+1;



            endcase                              #这行不要漏写



    注意:



            1)在执行了某一分支项内的语句后,跳出case语句结构,终止case语句执行。



            2)case语句中的各个<分支语句>中表达式取值必须是互不相同的。
   ```

二. 系统函数

定义:Verilog语言中预先定义了一些任务和函数,用于完成一些特殊的功能,它们被称为系统任务和系统函数。这些函数大多数都是只能在Testbench仿真中使用的,使得用户可以更方便地进行验证。

1.timescale函数

```
    格式: 'timescale   1ns/1ns   #时间尺度预编辑指令参数对应时间单位/时间精度



    其中时间单位和时间精度由值为1、10、100以及单位s, ms, us, ns, ps, fs 组成。



    时间单位:定义仿真过程所有与时间相关量的单位。



    仿真中使用"#数字"格式表示延时相应时间单位的时间,例如在前面设定的时间单位和精度前提下"#10"表示延时10个时间单位,也就是10ns



    时间精度:决定时间相关量的精度以及仿真显示的最小刻度。



    例如:



            `timescale 1ns/10ps   表示的精度为0.01ns, #10.11表示延迟10110ps



            很重要的一点:timescale前面的符号是键盘左上角的,千万不要写成单引号!



    注意:在使用timescale函数时要保证时间单位大于时间精度



            'timescale100ps/1ns 这样的写法就是错误的
```

2.display函数

```
    功能:用来输出、打印信息



    格式:$display("%b+%b=%d",a,b,c);      #"%b+%b=%d"用于输出格式控制,未指定时默认为十进制



    %h或%H    以十六进制格式输出



    %d或%D    以十进制格式输出



    %o或%O    以八进制格式输出



    %b或%B    以二进制格式输出



    注意:每次输出结束后自动换行
```

3.write函数

```
    功能:用于输出、打印信息



    格式:$write("%b+%b=%d\n",a,b,c);      #其中控制参数的用法同display函数



    注意: $write函数每次输出结束后不会自动换行,换行需要搭配"\n"实现。
```

4.strobe函数

```
    功能和用法与display和write相同,区别在于$strobe函数不论被置于程序中什么位置都是固定放在最后执行,其中变量参数也都是程序执行到最后的值。并且会自动换行。
```

5.monitor函数

```
    功能和用法与display和write相同,区别在于每改变$monitor中参数的值时就会打印一次。自动换行。
```

6.stop和finish函数

```
    $stop用于暂停仿真,$finish用于结束仿真
```

7. time和random函数

   ```
    功能:$time为时间函数,返回64位当前仿真时间;$random用于产生随机函数,返回随机数
   ```

8.readmemb/readmemh函数

```
    功能:$readmemb和$readmemh分别用于读取二进制和十六进制文件



    格式: $readmemb("<数据文件名>",<存储器名>);



            例如:$readmemb("test.txt",a);
```

三. 关键字

1. module(模块开始)---endmodule(模块结束)

   ```
   格式: module exmaple
   ```
2. input(输入)/input(输出)

   ```
    input   wire    sys_cik,               #这里注意每一个参数定义要加‘,’而定义语句结尾没有



    input   wire    sys_rst_n,



    inout   wire    sda,                     #inout定义的信号可同时用于输入或输出



    outputwire    po_flag
   ```
3. 变量类型

   ```
    1)线网型变量: wire                #在系统中会被直接认为具有一条物理连线



    2)寄存器型变量: reg      #对某一时间点的状态进行保存,在系统中会被认为是寄存器



          格式: reg a ;   #表示定义一个名为a,位宽为8bit,深度为21的寄存器变量



    两种变量类型的使用场景:



            1. 当使用always语句给变量赋值时,要用reg型



            2. 当使用assign语句给变量赋值时,用wire型。assign可以视为在电路中添加一条连线。
   ```

4.参数

```
    1)parameter



         格式: parameter   CNT_MAX = 100;



            特性:实例化时参数可修改



    2)localparam



            格式:localparam   CNT_MAX = 100;



            特性:只能在模块内部使用,不能实例化
```

5.常量(基数表示法)

```
    格式:[换算为二进制后位宽的总长度]['][数值进制符号][与数值进制符号相对应的应的数值]



    其中:[数值进制符号]:表示十六进制,表示八进制,表示二进制



    例如: 8'd171 : 位宽是8bit,十进制的171



                8'hab : 8bit的十六进制数ab



                8'o253 : 8bit的八进制数253



                8‘b1010_1011 : 8bit的二进制数1010_1011   #二进制数可以用下划线分割增强可读性



    [换算为二进制后位宽的总长度]这一项可有可无,verilog会自动为常量匹配合适的位宽。 当总位宽大于实际位宽,则自动在左边补0,总位宽小于实际位宽则自动截断左边超出的位数



    例如:'d7与8'd7表示相同数值,8'd7换算为二进制是8'b0000_0111,前五位补0



               2'd7换算为二进制是2'b11,超过2位宽的部分被截断   #这里的截断是从右往左进行的



    注意:如果直接写参数,例如100,表示位宽为32bit的十进制数100   
```

6.赋值

```
    1)阻塞赋值(“=”)



            内部语句同时并行执行



    2)非阻塞赋值(“<=")



         程序从上到下顺序执行赋值语句
```

注意一些问题:

```
    1.阻塞赋值电路结构与时钟触发沿没有关系,只与输入电平的变化有关。阻塞赋值语句是顺序执行的,先计算赋值符号右边的语句并更新赋值号左边的语句,此时不允许有来自其他任何语句的干扰,直到现行的赋值完成,才允许下一条赋值语句的执行。



    2.非阻塞赋值的电路结构与时钟触发沿有关系,只有触发沿时刻才进行相应的非阻塞赋值。非阻塞赋值语句是并行执行的,先计算所有非阻塞赋值号右边的语句再同时进行向左赋值,这期间允许其他语句进行操作。



    3.非阻塞赋值只能用于对寄存器变量进行赋值,因此只能能用于‘inital’和‘always’块中(敏感列表要采用电平触发方式/敏感列表.关于Modelsim仿真的一点提示:



    如果Modelsim报错error loading design 则最常用的解决办法是返回检查tb文件中的文件名是否与仿真项目名一致。就是always@(这里)的语句),不允许用于连续赋值‘assign’中。



    4.时序逻辑电路中推荐使用非阻塞赋值,方便实现时钟边沿触发。



    5.一个语句块中不要同时用阻塞和非阻塞赋值。同时推荐一个always语句块只对一个变量进行赋值。



    6.使用锁存器(latch)要采用非阻塞赋值。
```

三、关于Modelsim仿真的一点提示:

```
如果Modelsim报错error loading design 则最常用的解决办法是返回检查tb文件中的文件名是否与仿真项目名一致。
```
页: [1]
查看完整版本: verilog 语法基础汇总