在Verilog程序设计中,关于模块,always过程,并行赋值语句,函数及task调用等等。如何声明,调用,嵌套及赋值等一系列问题,初学者往往无所适从。关于wire,reg类型的声明,初始化及赋值等也有一系列的问题需要注意。本文对上述提到的相关问题进行深入讨论。希望读者在理解之后举一反三,做到正确使用Verilog语法规则,使得代码清晰,简洁,准确。
1. 模块(module)的组织与正确使用
模块(module)是verilog工程的基础,所有的verilog程序都是在module中进行编写的,少量的宏定义可以在模块之外。一个文件可以只包含一个模块,也可以是一个文件包含多个模块。 一个模块不能分离存储到不同的文件中。
1)通常情况下一个文件(*.v)中包含一个module。但也可以在 同一个*.v 文件中,同时存在2个或者多个 module 模块。module 的格式请参考:Verilog 模块与端口。
2)在文件中,module 级别是最高一级的,其他的如always,initial,并发赋值等只能在module 模块下进行编写和使用。 和module 同一级别 的verilog语法声明也包括timescale, include, define 等宏定义,如例1所示。
例1:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// MACRO can be defined outside module `timescale 1ns/1ps `include "test.v" `define PPP module aaa ( input clk, input xxxx, output kkk ); reg [4:0] ttt; wire [2:0] uuu; // MACRO can be also defined inside module `include "debug.v" `define BBB awlays @ (posedge clk) begin .... end always @ (*) begin .... end assign kkk = ...; assign uuu = ...; endmodule module bbb ( ... ); .... endmodule |
3)module 和 module是同一级别的。 Verilog 语法中module 模块中不能再嵌套定义module了, 虽然Systemverilog 可以嵌套定义,但也有规则限制。
例2:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module aaa ( .... ); ..... //ERROR: module nested define is not allowed in Verilog module bbb ( ... ); ... endmodule ccc ccc_inst ( .d (), .c () ); endmodule |
从例1,例2的使用可以看出:
-
- Verilog 语法中module 下不能在定义 其他module了,但是systemverilog 可以(nested module declarations)。但是verilog 和 systemverilog 在module中都可以例化其他模块(module)。
- module 和 include 是同一级别的, 即
include可以写在module 模块之外。 当然,include也可以写在模块内部。 - module 和
define 是同一级别的, 即define可以写在module 模块之外。 当然,define也可以写在模块内部。 - module 和 timescale是同一级别的, 即
timescale可以写在module 模块之外。 当然,timescale也可以写在模块内部。但是很少将timescale写在module 内部声明。 -
- 被例化的module 输入端口(input)传递关系,能否将变量传递给被例化模块的输入端口(input),关键看驱动模型。
- 符合驱动模型传递如下:
- 本级模块的input 类型端口
- 本级模块的output 类型端口
- 本级模块的定义的reg 类型变量
- 本级模块中的wire 类型变量
- 不符合驱动模型传递如下:
- 无
- 符合驱动模型传递如下:
- 被例化的module 输出端口(output)传递关系
- 符合驱动模型传递如下:
- 本级模块的output,但是有如下限制
- 本级模块的output不能连接到多个被例化模块中的output端口上
- 本级模块的output连接到1个被例化模块中的output端口上,不能在本级模块其他逻辑中赋值
- 本机模块的wire,但是有如下限制
- 本级模块的wire不能连接到多个被例化模块中的output端口上
- 本级模块的wire连接到1个被例化模块中的output端口上,不能在本级模块其他逻辑中赋值
- 本级模块的output,但是有如下限制
- 不符合驱动模型传递如下:
- 本级模块的reg 类型不能连接到被例化模块中的output 端口上
- 本级模块中的output reg 类型端口,不能连接到被例化模块中的output端口上
- 符合驱动模型传递如下:
- 被例化的module 输入端口(input)传递关系,能否将变量传递给被例化模块的输入端口(input),关键看驱动模型。
- reg声明
- wire声明
- task声明
- function 声明,
- module 例化,
- generate,
define,- include,
timescale,- parameter,
- localparam,
- always,
- assign
- initial
- IP, primitive 例化等 。
- 声明的时候reg [3:0] b = 0;
- 初始化的时候赋值 initial
- always b <= ~b;
- always b <= b + 1;
4)module嵌套时的端口传递
2. 仿真文件编写
在仿真文件中, module 内部可以编写
也就是说这些声明,例化是同一级别的。
举例
12345678910111213141516171819202122232425262728293031323334353637`timescale 1ns/1ps`include "test.v"`define KKK 3module tb();reg clk;reg clk_50 = 0;reg rst = 1;localparam PERIOD = 10;always # clk_50 = ~clk_50;initialbeginclk = 0;forever #(PERIOD/2) clk = ~ clk;endreg [3:0] cnt;always @ (posedge clk or posedge rst)if(rst) cnt <= 0;elsebeginif(cnt == PEROID) cnt <= 0;else cnt <= cnt + 1;endwire ppp = (cnt == `KKK) ? 1'b1 : 1'b0;initialbegin#2000;$stop;endendmodule在模块中可以使用wire,reg等声明,但要注意reg 声明时不能使用变量赋初值,如果需要,只能使用wire声明。
例1:
1234567891011121314reg b = 0;reg c = 0;reg a = b | c;initialbeginb = 1;c = 0;#10;b = 0;c = 1;endwire k = b | c;这里 reg a = b | c ; 是不正确的。 只能使用 wire k = b |c ; 或者是 wire k; assign = b | c; 来替代。
例2:always 多处赋值
1234567891011121314151617181920212223242526`timescale 1ns / 1psmodule altera_sim #(parameter ADD_SUB = 0)(input clk,output [7:0] led);reg [3:0] cnt = 0;always @ (posedge clk)begincnt <= cnt + 1;endreg [7:0] led_r = 0;always @ (posedge clk)if(cnt[3:0] == 0) led_r <= ADD_SUB ? led_r + 1 : led_r - 1;//always @ (posedge clk)//if(cnt[3:0] == 1) led_r <= led_r + 2;assign led = led_r;endmodule1234567891011121314151617181920212223242526272829303132333435363738394041424344`timescale 1ns / 1psmodule tb();reg clk = 0;localparam CLK_PERIOD = 10;initialbeginclk = 0;#200;forever#(CLK_PERIOD/2) clk = ~clk;endreg [3:0] b = 5;initialbeginb = 0;endalways @ (posedge clk)b <= ~b;always @ (posedge clk)b <= b + 1;initialbeginclk = 0;#2000;$stop;endwire [7:0] led;altera_sim #(.ADD_SUB (1))top_inst(.clk (clk),.led (led));assign led = 8'h80;endmoduleb 有4个地方赋值:

这样的设计,在仿真的时候,可以正常使用。 但是相同的逻辑不能在综合的时候使用。原因是: 2 次 always 进行不同的赋值综合的时候会报错。调用模块alter_sim中的led (output 端口) 和 assign led = 8'h80; 赋值相冲突,最终输出为led = xx;
例3:initial 中嵌套always, 不能正常编译。
123456789reg [3:0] b = 5;initialbeginb = 0;<span style="color: #0000ff;">always @ (posedge clk)</span>b <= ~b;endinitial 中使用always 编译的时候会报错。但可以使用其他方式实现:
1234567891011reg [3:0] b = 5;initialbeginb = 0;while(1)begin@(posedge clk);b <= ~b;endend或者
1234567891011reg [3:0] b = 5;initialbeginb = 0;while(1)begin#10;b <= ~b;endend例4:always 中使用 initial 不能正常编译:
123456789always @ (posedge clk)begin<span style="color: #0000ff;">initial</span>beginb = 3;endb <= ~b;end例5:2 个模块的输出(output) 连接到一起了。
12345678910111213141516wire [7:0] <span style="color: #0000ff;">led</span>;altera_sim # (.ADD_SUB(1))top_inst(.clk (clk),.led (<span style="color: #0000ff;">led</span>));//assign led = 8'h80;altera_sim # (.ADD_SUB(0))top1_inst(.clk (clk),.led (<span style="color: #0000ff;">led</span>));
例6:always 中不能使用assign
1234always @ (posedge clk)beginassign c = a + b;end例7:always 中不能使用generate , 但是generate 中可以使用always。
12345678910111213141516`timescale 1ns/1psmodule tb();genvar i;always @ (posedge clk)generatefor (i = 0; i < 4; i = i + 1)begin:test_genb[i] <= b[i] + 1;endendgenerateendmodule例8:forever, while, if, for, case 等必须在initial, always 下使用,不能独立存在。即使有些综合器没有报错。
1234567891011121314151617`timescale 1ns/1psmodule tb();reg clk = 0;forever # clk = ~clk;reg a = 0;reg b = 1;localparam kkk = 1;if(kkk)wire c = a & b;elsewire c = a | b;endmodule例9:define
parameter可以在端口定义中使用,define 可以定义端口的种类和变量宽度,parameter 只能定义端口的宽度。举例:
1234567891011121314151617181920`define KKK 4module aaa #(parameter D = 8)(input clk,input [`KKK - 1:0]a,input [D - 1:0]b,`ifdef KKKouput [D - 1:0] led`elseouput [D - 1:0] testpin`endif);.....endmodule
例10:always 使用
1234always @ (posedge clk);begina <= a + 1;end
; 代表当前的always 结束了, 比如在仿真中使用@(posedge clk); 所以之后的begin …. end 和 always 再也没有任何关系了。在always 中一定会有一个真实的执行体,不会只有always ,说得直白一些就是always 下一定能找到一个“;”。 举例:
123456always @ (posedge clk)if(reset) st <= 0;else case (st)0: st <= 1;default : st <= 0;endcase
第一级 第二级 第三级 module wire声明 reg声明 task 声明 function 声明 不能使用 #10; 不能使用always@(posedge)等时序逻辑 generate define可以定义端口数量,改变端口声明 include timescaleparameter 可以定义端口宽度 localparam always 不能使用使用initial,assign, 模块声明,例化 ,不能声明function, 但可以调用function initial 不能使用使用always, assign, 模块声明,例化,不能声明function, 但可以调用function assign 不能包括begin .... end, 只能使用逻辑,关系,等式,位操作等运算符 IP, primitive 例化 module 例化 timescale defineinclude