计数功能_分析

计数功能_分析

综述

有时会纠结于计数模块的复位的判断逻辑if(cnt == NUM-1'b1),不知何时需要减一操作,经常需要通过仿真图才能放心,这是非常被动的设计思路,所以,我打算彻底解决这个问题,在代码阶段就能完全掌控模块的功能和时序,刚好项目中遇见了适合的素材,值得记录。

核心观点
1、定时器和计数器的概念是不等价的,虽然它们都是基于计数功能;
2、定时器是基于周期触发加一操作,基于其自身计数完成触发复位操作;定时器的计数时序是:1、2、… 、NUM-1、0;复位需要等量时钟个数,每一位占据等量时钟;
3、计数器是基于其他变量的判断逻辑触发加一和复位操作操作;计数器的计数时序是:1、2、…、NUM(0);复位发生在最后一位多个时钟里的最后一个或数个时钟;每一位可能占据不同的时钟长度;

这些概念比较空泛,接下来用例子来观察一下。

定时器

接下来,先看看定时器,将设计一个定时器,需求是:每10个时钟产生一个1clk的脉冲信号;
功能模块:

module counter_10(
	input 	clk, 		//10M
	input 	rst_n,
	
	output	O_Pulse_Sig	//输出脉冲信号
);

reg rpulse_sig;

reg [3:0] cnt_10;

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		cnt_10 <= 4'b0;
		rpulse_sig <= 1'b0;
	end
	else if(cnt_10 == 10-1'b1)begin	//计数寄存器复位逻辑
		cnt_10 <= 4'b0;
		rpulse_sig <= 1'b1;
	end
	else begin
		cnt_10 <= cnt_10+1'b1;
		rpulse_sig <= 1'b0;
	end
end

assign O_Pulse_Sig = rpulse_sig;

endmodule

Testbench:

module counter_10_tb();

//时钟与复位
reg CLK;	//10M
reg RST_N;	//系统复位

initial begin
	RST_N = 0; #2 RST_N = 1;
	CLK = 0; forever #50 CLK = ~CLK;
end

//功能模块例化
wire O_Pulse_Sig;
counter_10 counter_10_inst(
	.clk		(CLK), 		//10M
	.rst_n		(RST_N),
	.O_Pulse_Sig(O_Pulse_Sig)	//输出脉冲信号
);	

//激励 无

endmodule

来看一下波形图:

在这里计数寄存器的复位判断是cnt_10 == 10-1'b1,两个游标的时间间隔是1052ns-52ns=1000ns刚好是10个时钟,功能时序正确;

定时器的计数寄存器cnt_10的触发条件是时钟,准确的说,是周期性的时钟上升沿,系统复位后,cnt_10为零,52ns位置到来第一个时钟沿,计数寄存器进行加一操作,其未来值为1,接着依次2、3、4、5、6、7、8、9,之后,我们必须留出一个时钟给模块进行两个操作:
1、拉高输出脉冲信号,
2、复位计数寄存器,
而这个留出来的时钟就是第十个时钟,这也就是核心观点2;

按照我的定义,那些常见的cnt <= cnt+1'b1;其实大多都是定时器!只不过是基于这个计数功能,大家就称之为“计数器”了,试着想一想,周期性的触发加一操作,等计数到了一个值就复位并重新计数,这个过程不就是表盘吗?秒针的周期是秒,到了59变成(60)0,时针的周期是3600个秒,到了23变成(24)0,每计数1天,就报个脉冲信号,这不就是上述仿真的定时器吗?所以,我更倾向于叫它基于计数功能的定时器。

计数器

项目中遇见一个实例,简单的说就是在一定时间内,判断输入的信号是125MHz或25MHz,我的思路是:对主时钟120M、待检测的125M或25M进行约分:24:25:5,如果我在24个主时钟下检测到5个txuserclk2的上升沿,那么当前就是模式1;否则就配置为模式2,配置信号也就是speed_is_10_100speed_is_100

module freq_detect(
    input       I_sys_clk_120m,
    input       I_sys_rst_n,
    
    input      S_quad0_txuserclk2_out_2,
    output     speed_is_10_100_2,
    output     speed_is_100_2 
);

(*mark_debug*)reg S_speed_is_10_100_2	;
(*mark_debug*)reg S_speed_is_100_2	;
	
reg txuserclk2_out_2_reg;
reg txuserclk2_out_2_reg_reg;
wire txuserclk2_out_2_pedge;

always@(posedge I_sys_clk_120m or negedge I_sys_rst_n)begin //±ßÑؼì²â
	if(!I_sys_rst_n)begin
		txuserclk2_out_2_reg <= 0;
		txuserclk2_out_2_reg_reg<= 0;
	end
	else begin
		txuserclk2_out_2_reg <= S_quad0_txuserclk2_out_2;
		txuserclk2_out_2_reg_reg <= txuserclk2_out_2_reg;
	end
end

assign txuserclk2_out_2_pedge = (~txuserclk2_out_2_reg_reg & txuserclk2_out_2_reg)?1:0;

(*mark_debug*)reg [4:0] cnt_sys_clk_2;
(*mark_debug*)reg [2:0] cnt_pedge_2;

always@(posedge I_sys_clk_120m or negedge I_sys_rst_n)begin
	if(!I_sys_rst_n)begin
		cnt_sys_clk_2 <= 0;
		cnt_pedge_2 <= 0;
	end
	else begin
		if(cnt_sys_clk_2 == 24-1)begin 
			cnt_sys_clk_2 <= 0;
			cnt_pedge_2 <= 0;
			if(cnt_pedge_2 == 5)begin //100M  
				S_speed_is_10_100_2 <= 1;
				S_speed_is_100_2 <= 1;
			end
			else begin //1000M
				S_speed_is_10_100_2 <= 0;
				S_speed_is_100_2 <= 0;
			end
		end
		else if(txuserclk2_out_2_pedge)begin
			cnt_pedge_2 <= cnt_pedge_2+1'b1;
			cnt_sys_clk_2 <= cnt_sys_clk_2+1'b1;
			S_speed_is_10_100_2 <= S_speed_is_10_100_2;
			S_speed_is_100_2 <= S_speed_is_100_2;		
		end
		else begin
			cnt_pedge_2 <= cnt_pedge_2;
			cnt_sys_clk_2 <= cnt_sys_clk_2+1'b1;
			S_speed_is_10_100_2 <= S_speed_is_10_100_2;
			S_speed_is_100_2 <= S_speed_is_100_2;
		end	
	end		
end

assign speed_is_10_100_2 = S_speed_is_10_100_2;
assign speed_is_100_2 = S_speed_is_100_2;


endmodule

仿真的激励:

module freq_detect_tb();

reg CLK;
reg RST_N;

initial begin
    RST_N = 0; #20 RST_N = 1;
    CLK = 1; forever #4167 CLK = ~CLK;
end

reg S_quad0_txuserclk2_out_2;
initial begin
    S_quad0_txuserclk2_out_2 = 1; 
    forever #4000   S_quad0_txuserclk2_out_2 = ~S_quad0_txuserclk2_out_2;  //20000    4000
end

wire speed_is_10_100_2;
wire speed_is_100_2;

//instance
freq_detect freq_detect_U0(
    .I_sys_clk_120m     (CLK),
    .I_sys_rst_n        (RST_N),
    
    .S_quad0_txuserclk2_out_2 (S_quad0_txuserclk2_out_2),
    .speed_is_10_100_2  (speed_is_10_100_2),
    .speed_is_100_2     (speed_is_100_2) 
);


endmodule

看看波形图
125M输入:
在这里插入图片描述
25M输入:在这里插入图片描述
模块中,每24个主时钟代码段是定时器,检测到上升沿计数的才是计数器!所以它们的计数时序有些不同,以百兆为例,定时器的计数触发是基于主时钟的上升沿,当其复位时,需要一个等长的时钟数完成计数寄存器的复位;而计数器的计数和复位触发是基于逻辑判断,计数寄存器每位占据的时间可能并不等长,这也是核心观点3。关键分析两个游标175.020ns-208.370ns的时序就很清楚了。

总结

怎么来理解这个呢,我举个栗子,模型是一个特殊的赛跑比赛,规则是在一定时间内,谁跑的圈数多谁赢,对于博尔特来说,他需要两个计数寄存器c1、c2c1用来计数时间走了多久,当规定时间到了,c1就在最后一拍完成复位,这一拍的长度跟“分辨率”有关,精度是秒的,那复位就用一秒,精度是毫秒的,那就用了一毫秒,但包括复位在内的计数过程,每一位都是等长的;c2用来计数跑了多少圈,它的触发逻辑是博尔特到达终点线!所以,博尔特可能有的圈跑的快、有的慢、也有可能一直匀速,所以c2的每一位所用时常是可能不相等的,而c2计数停止和复位则是发生在c1计数完成!复位只占了最后一个数据位的一部分;所以,这下就清楚哪个是定时器哪个是计数器了吧!
这是个很简单的例子,但我觉得挺重要的,以前也碰到过,但都是一股脑儿的叫计数器,所以理所当然的以为时序是一样的,教材也是、网上所谓的模板也是,哈哈,还是建议自己动手试试,分析过后会有更合理的理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值