在用Verilog 语言进行RTL建模的时候,适当的对要完成的模块进行划分是一个很好的建模习惯,在保证功能要求的满足的前提下,能够使自己的代码容易理解,维护,并减少一些容易忽视的错误。在编写大型的程序时尤其重要。以下就用计数器四分频时钟的两种实现方式来对比以下划分模块与否的优缺点。
四分频后的波形要求是第一个时钟周期输出高电平,后三个时钟周期输出低电平,占空比为25%。可以很容易的看出来需要一个两位的计数器,并在计数的过程中进行判断,什么时候输出高电平,什么时候输出低电平。
可以看出来四分频系统设计到两个操作:对时钟进行计数和判断输出波形。这两个部分可以划分为两个模块:计数器模块和判断输出模块,即一个always模块进行计数,另一个always模块进行判断输出;也可以划分成一个模块:在计数的同时进行判断输出,这些都在一个always 模块完成。
不划分模块的Verilog代码:
1 module led0_module(
2 clk,rst_n,out
3 );
4 input clk,rst_n;
5 output out;
6
7 reg [1:0] count;
8 reg rOut;
9 always @(posedge clk,negedge rst_n)
10 if(!rst_n) begin
11 count<=2'b0;
12 rOut=1'b0;
13 end
14 else begin
15 count<=count+2'b1;
16 if(count<2'b01)
17 rOut<=1'b1;
18 else if(count<2'b11)
19 rOut<=1'b0;
20 else
21 count<=2'b0;
22 end
23
24 assign out=rOut;
25 endmodule
以上为不划分模块,计数与判断输出在一个always模块中完成。生成的RTL级别的视图:
不划分模块时可以从RTL级别的视图中看出共需要两个比较器,这从代码中可以看出,计数寄存器 count 进行了两次比较。但是有四个选择器,相信对于初学者这个不容易看出来,我也是对着RTL视图和代码开了半天才看出来。这是因为对count 进行判断后要更新两个寄存器变量的值,一个是count 本身的值,所以需要count 比较两次的结果分别取控制两个选择器,来选择判断后的值;另一个是rOut的值,同样需要count比较两次的结果来控制两个选择器,来选择相应判断后的值。这个从比较器LessThan1 和LessThan0 的输出结果分别分成两路信号应该可以看出来。
划分后的模块:
1 module led0_module(
2 clk,rst_n,out
3 );
4 input clk,rst_n;
5 output out;
6
7 reg [1:0] count;
8 always @(posedge clk,negedge rst_n)
9 if(!rst_n)
10 count<=2'b0;
11 else if(count==2'b11)
12 count<=2'b0;
13 else
14 count<=count+2'b1;
15
16 reg rOut;
17 always @(posedge clk,negedge rst_n)
18 if(!rst_n)
19 rOut<=1'b0;
20 else if(count<2'b1)
21 rOut<=1'b1;
22 else
23 rOut<=1'b0;
24
25 assign out=rOut;
26 endmodule
将四分频系统分成计数器部分和判断输出部分生成的RTL级别的视图。
可以看出对count计数器值的更新比较了一次,对rOut值的更新也比较了一次,每比较一次又需要一个选择器,所以共有两个比较器(equal0 和 LessThan0)和两个选择器(Mux21 和 LessThan0)。模块的划分以count[1..0] 计数器为界,前面为计数器模块,后面为判断输出模块。划分模块后可以看出RTL视图简单了许多,且易于分析,RTL级别的代码也易于理解。
通过上述对比可以看出,适当的划分模块可以提高代码的可读性,降低系统的复杂性。在大型系统的设计过程中尤为重要。