Loading... 记录Verilog学习笔记。 Verilog是一种用于描述、设计电子系统(特别是数字电路)的硬件描述语言,主要用于在集成电路设计,特别是超大规模集成电路的计算机辅助设计。Verilog是电气电子工程师学会(IEEE)的1364号标准。 Verilog能够在多种抽象级别对数字逻辑系统进行描述:既可以在晶体管级、逻辑门级进行描述,也可以在寄存器传输级对电路信号在寄存器之间的传输情况进行描述。除了对电路的逻辑功能进行描述,Verilog代码还能够被用于逻辑仿真、逻辑综合,其中后者可以把寄存器传输级的Verilog代码转换为逻辑门级的网表,从而方便在现场可编程逻辑门阵列上实现硬件电路,或者让硬件厂商制造具体的专用集成电路。设计人员还可以利用Verilog的扩展部分Verilog-AMS进行模拟电路和混合信号集成电路的设计。 Ref: [HDLBits (01xz.net)](https://hdlbits.01xz.net/wiki/Main_Page) ## Verilog Language ### Basics #### Module module是verilog中实现特定功能的代码块,多个模块可以嵌套使用。 模块的所有内容包含在关键字`module`和`endmodule`中间,其中`module`关键字后面还会包括模块名称和可选的端口描述(大部分模块包含端口描述,testbench module没有端口描述)并以`;`结尾。endmodule是模块的最后一行,不需要`;`结尾。 module的端口写在后面括号中,根据不同的verilog版本,端口类型可以直接在括号中描述,也可以在module内部描述。 ```verilog module top_module ( zero ); output zero; // Verilog-1995 endmodule ``` ```verilog module top_module ( output zero ); // Verilog-2001 endmodule ``` #### Wire 与物理导线不同,Verilog中的`wire`(以及其他`signals`)是有方向性的。这意味着信息只能在一个方向上流动,通常是从一个源端到接收端。 在Verilog中`assign left_side = right_side;`表示一种连续赋值,右侧信号的值被驱动到左侧导线上。与C语言不同的是,这种赋值关系是“连续的”,因为在任何时候右侧的值发生变化,赋值仍然一直进行,连续赋值不是一次性事件。 模块上的端口也有方向(通常是输入或输出)。输入端口由模块外部的某个源端驱动,而输出端口则驱动模块外部的接收端。 下面的图示说明了电路的每个部分如何对应于Verilog代码的每个部分。module和port声明创建了电路的黑色部分。其中`in`和`out`才是`wire`,而绿色的线并不是表示一条wire,它表示的是一种赋值关系。 ```verilog module top_module( input in, output out ); assign out = in; endmodule ```  #### Gate 使用assign赋值语句实现简单的门电路逻辑: ##### Notgate  ```verilog module top_module( input in, output out ); assign out = ~in; endmodule ```  ##### Andgate  在verilog中,也有类似C语言中的bitwise-AND (`&`)和logical-AND (`&&`)。对于1bit操作而言,这两种算法的结果是一样的。对于多bit操作,bitwise-AND的结果也是多bit,logical-AND的结果是1bit。 ```verilog module top_module( input a, input b, output out ); assign out = a & b; endmodule ```  ##### Norgate  在verilog中,也有类似C语言中的bitwise-OR (`|`)和logical-OR (`||`)。对于1bit操作而言,这两种算法的结果是一样的。对于多bit操作,bitwise-OR的结果也是多bit,logical-OR的结果是1bit。 ```verilog module top_module( input a, input b, output out ); assign out = ~(a | b); endmodule ```  ##### Xnorgate  对于异或(XOR)操作,verilog只有bitwise-XOR,没有logical-XOR。 ```verilog module top_module( input a, input b, output out ); assign out = ~(a ^ b); endmodule ```  #### Vector Vector用于使用一个名称分组相关信号,使操作更加方便。例如`wire [7:0] w;`声明了一个名字为`w`的8bit Vector,它在功能上等同于8根独立的`wire`; 请注意,与C语言不同的是,Vector声明需要将描述位宽的`[]`放在名称之前,但是从Vector中选择部分bit依然将`[]`放在名称之后。 ```verilog wire [99:0] my_vector; // 声明一个包含100个元素的矢量 assign out = my_vector[10]; // 从矢量中选择一个位 ``` **Vector声明** 必须声明为:`type [upper:lower] vector_name;`,其中type指定矢量的数据类型,通常为wire或reg。如果要声明输入或输出端口,类型还可以包括端口类型(例如input或output)。以下是一些示例: ```verilog wire [7:0] w; // 8位电线 reg [4:1] x; // 4位寄存器 output reg [0:0] y; // 1位寄存器,同时也是输出端口(仍然是矢量) input wire [3:-2] z; // 6位输入电线(允许使用负范围) output [3:0] a; // 4位输出电线。类型为'wire',除非另有说明。 wire [0:7] b; // 8位电线,其中b[0]是最高有效位。 ``` **Vector字节序** Vector字节序(或非正式地称为“方向”)是指最不显著位的索引是较低的(小端,例如[3:0])还是较高的(大端,例如[0:3])。在Verilog中,一旦用特定的字节序声明了矢量,就必须始终以相同的方式使用它。例如,对于声明为`wire [3:0] vec;`的矢量,写入`vec[0:3]`是不合法的。保持字节序一致是一种良好的实践,因为如果具有不同字节序的矢量被分配或一起使用,将导致奇怪的错误。 **隐式net** 隐式net通常是难以检测的错误源。在Verilog中,通过assign语句或将未声明的内容连接到模块端口,可以隐式地创建`net`类型信号。隐式电线始终是1bit `wire`,并且如果您打算使用矢量,则可能导致错误。可以使用`default_nettype none`指令禁用隐式电线的创建。 ```verilog wire [2:0] a, c; // 两个矢量 assign a = 3'b101; // a = 101 assign b = a; // b = 1,隐式创建的电线 assign c = b; // c = 001 <-- 错误 my_module i1 (d,e); // 如果未声明,则d和e将隐式为一位宽。 // 如果端口应为矢量,则这可能是一个错误。 ``` 添加`default_nettype none`将使错误更容易被发现。 **Unpacked vs. Packed Arrays** 您可能已经注意到,在Vector声明中,Vector索引写在Vector名称之前。这声明了数组的“打包”维度,其中Bits被“打包”在一起形成一个块(这在模拟器中是相关的,但在硬件中则不是)。未打包的维度在名称之后声明,通常用于声明内存数组。 ```verilog reg [7:0] mem [255:0]; // 256个未打包元素,每个元素都是一个8位打包寄存器的矢量。 reg mem2 [28:0]; // 29个未打包元素,每个元素都是一个1位寄存器。 ``` 访问矢量元素:部分选择。 访问整个矢量:使用名称。例如`assign w = a;` 将整个4位矢量a赋值给整个8位矢量w。如果右侧和左侧的长度不匹配,将根据需要进行零扩展或截断。 部分选择运算符可用于访问矢量的一部分: ```verilog w[3:0] // w的低4位 x[1] // x的最低位 x[1:1] // ...也是x的最低位 z[-1:-2] // z的两个最低位 b[3:0] // 不合法。矢量部分选择必须与声明的方向匹配。 b[0:3] // b的*高*4位。 assign w[3:0] = b[0:3]; // 将b的高4位赋给w的低4位。w[3]=b[0],w[2]=b[1],等等。 ``` ## TBD 最后修改:2024 年 03 月 12 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏