localparam的改变方式与always块中的判断语句时序

LocalParam的改变方式与always块中的判断语句时序

问题提出

localpara的改变方式:
对于Verilog中的localpara关键词可以用于限定某个参数无法进行更改,但是当存在两个parameter参数定义localparam时,能否通过改变parameter的值而改变localparam所定义参数的值?

例如,在实现计数器时可能采用一个parameter参数用于定义系统时钟的周期(SYS_TIME)(可能使用锁相环进行变频所以定义一个频率的参数),另外通过另外一个parameter定义每次计数的时间间隔(COUNTTIME),则采用localparam定义的**计数次数(COUNTMAX)**可用以下表达式进行表达:
C O U N T M A X = S Y S _ T I M E C O U N T T I M E COUNTMAX=\frac{SYS\_TIME}{COUNTTIME} COUNTMAX=COUNTTIMESYS_TIME
以上定义的相应verilog代码如下:

parameter 	SYS_TIME=5'd20;                                 				//设置系统时间周期,单位为ns
parameter 	COUNTTIME=29'd500_000;                      					//设置计数周期,单位为ns
localparam 	COUNT_MAX=COUNTTIME/SYS_TIME;                   				//设置最大计数值

always块中的判断语句时序:
另外,在进行时序逻辑电路的代码编写,时序问题是一个及其重要的问题,为了实现精确计数,需要能够严格控制计数次数,其中对于计数次数的精确控制需要对always块中的判断时序语句有较深的理解,例如对于以下代码:

  //计数模块,其中countnum寄存器变量为实际计数值
  always@(posedge clk)
  	if(countnum<3'd5)
  		countnum<=countnum+1'b1;
  	else
  		countnum<=1'b0;
  
  //达到计数赋值模块,其中当countnum中计数达到5,便会触发full_flag寄存器中的值设1
  always@(posedge clk)
  	if(countnum==3'd5)
  		full_flag<=1'b1;
  	else
  		full_flag<=1'b0;     //进行full_flag的清零,使每次计数记满后产生的full_flag电平不会保存,另外也可防止产生锁存器

在理想情况下,信号可能为在第五次clk到来时,让countnum寄存器中的值由4变为5,另外,会在这次clk中触发达到计数赋值模块,使full_flag中的值被赋1,以下这种情况下的时序图如下:
同时触发加法与判断情况下的时序图
而如果两个always过程块中的加法语句与判断语句不是同时进行,而是先进行赋值,在下一次系统时钟到达时才进行判断,则相应的时序图会是一下这种情况:
先触发加法语句下个时钟周期触发判断语句的情况
以上两种情况会导致的不同结果为,在同时进行第一个always块的加法语句和第二个always块的赋值语句时,其full_flag上升沿的到来会提前一个时钟周期,而在第一个always块的加法语句先进行,而第二个always块的赋值语句在下一个时钟周期进行时,相比之下会晚一个时钟周期,所以对always块的时序理解值得研究。

完整代码编写

由于本问题主要是在编写一个较为具有一般性的计数代码中提出的,所以实际验证也通过一个完整的计数器实例来进行验证,其中计数器的顶层模块为:

//计数器代码编写,主要实现功能,实现最大计数并且给出一个高电平

module counter(
		input					sys_clk,										//系统时钟
		input					rst_n,											//系统复位信号
		output  	reg	 		full_flag,										//系统计数记满标志
		output  	reg	[28:0]	countmaxshow,									//用于测试COUNT_MAX的值能否进行改变
		output	    reg	[28:0]	countnum										//测试用的数值改变情况
);

//测试localparam效能模块
//reg [28:0] countmaxshow;														//保持有29位,以能够记下最大可能的COUNT_MAX,前提是SYS_TIME为整数
//reg [28:0] countnum;


parameter 	SYS_TIME=5'd20;                                 					//设置系统时间周期,单位为ns
parameter 	COUNTTIME=29'd500_000;                      						//设置计数周期,单位为ns
localparam 	COUNT_MAX=COUNTTIME/SYS_TIME;                   					//设置最大计数值(未完成测试,当使用localparam定义COUNT_MAX时,其值能否进行更改)



always@(posedge sys_clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			countnum<=29'd0;
			countmaxshow<=COUNT_MAX;											//在复位情况下将countnum进行给值
		end
	else
		if(countnum==(COUNT_MAX-1))
		begin
			countnum<=29'd0;
			countmaxshow<=COUNT_MAX;
		end
		else
		begin
			countnum<=countnum+1'b1;
			countmaxshow<=COUNT_MAX;
		end
		
end

always@(posedge sys_clk or negedge rst_n)
begin
	if(!rst_n)
		full_flag<=1'b0;
	else
		if(countnum==(COUNT_MAX-1))
			full_flag<=1'b1;
		else
			full_flag<=1'b0;
end

endmodule 

其中为了进行调试,将COUNT_MAX的值通过countmaxshow变量来进行输出,以便仿真时能够进行判断。

另外,为能够进行验证localpara定义参数值能否进行改变,采用parameter定义SYS_TIMECOUNTTIME两参数,用于表示50MHz的系统时钟的周期与500000ns进行一次计数的计数周期。

此外,通过两always块分别进行计数与判断(两always过程块其实可以合并成一个)。

另外给出仿真代码供参考:

//测试用代码
`timescale 1ns/1ns
`define TIME_PERIOD 20         														//设置系统的时钟周期为20ns

module counter_tb;
	reg 			signal_sys_clk;													//用于产生系统时钟信号
	reg 			signal_rst_n;													//用于产生系统复位信号
	wire			signal_full_flag;												//接收计数记满信号
   wire [28:0]      signal_countmaxshow;											//接收最大数值信号,用于观察localparam能不能通过parameter来进行更改
   wire [28:0]	    signal_countnum;												//接收每个时间段的信号计数,用于观察判断计数发生在哪个阶段
	
	
	initial
	begin
		signal_sys_clk=1'b1;														//令时钟起始为1有更好的效果
		signal_rst_n=1'b0;															//令复位信号复位持续5个时钟周期
		#(5*(`TIME_PERIOD)) signal_rst_n=1'b1;										//将复位信号关闭
		#(10_000_000*`TIME_PERIOD) $stop;  											//系统任务需要全部小写,才能够完成相应的值
	end
	
	always #(`TIME_PERIOD/2) signal_sys_clk=~signal_sys_clk;						//产生相应的时钟周期
	
	counter #(
		.SYS_TIME		(6'd50),
		.COUNTTIME		(29'd500_000)
	)	u0_counter(																	//进行连线与模块调用
		.sys_clk		(signal_sys_clk),
		.rst_n			(signal_rst_n),
		.full_flag		(signal_full_flag),
		.countmaxshow	(signal_countmaxshow),
		.countnum		(signal_countnum)
	);

endmodule 

在仿真模块中,通过采用模块例化中#(.模块参数名(传入模块参数值))的方式对SYS_TIMECOUNTTIME进行模块中参数的值的更改,另外还有通过defparam关键字对模块中的参数值进行更改,在这里就不重复仿真了。

另外,通过full_flag寄存器中的值的变化以及countnum中值的变化能够得出观察出两always过程块中的具体时序。

仿真结果与结论

通过仿真,能够得到相应的仿真波形,考虑到当能够进行相应的localparam参数值的改变时,每次产生full_flag的高电平信号需要计数10_000次,所以只能给出部分仿真波形,以下给出波形位于full_flag变化点:
部分仿真波形图
其中,根据波形图中的signal_countmaxshow信号值,可以发现COUNT_MAX的值为10_000ns,说明可以通过两parameter定义的参数来定义localparam,实现其参数值的改变。

另外,可以发现,对于signal_full_flag信号的改变发生在signal_countnum=29’d9999的末尾,则可证明是先进行了第一个always块中的加法语句,将signal_countnum的值加到了29’d9999,等到下一个系统时钟到来时才会同时将signal_countnum的值给0,同时标记full_flag信号为1。

小tips

在使用前面给出的计数器设计文件(counter模块文件)进行编译时,会出现警告
出现警告
这一警告说直接将输出引脚连在了VCC或GND上,出现这一警告的原因是因为在整个模块中,能够给countmaxshow寄存器变量值的只有一个localparam参数,所以会出现以上警告,实际不影响结果。

最后,原创不易,欢迎转载,但请注明出处^_^!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值