FPGA杂记4——脉冲边沿检测及计数

脉冲边沿检测


原理

  • 对输入脉冲信号进行 两级寄存器 锁存
  • 对两级寄存器进行逻辑运算(异或),在其边沿脉冲电平变化时获取保持一个时钟周期的高电平

适用场景

  • 同步/异步信号都可,可以使用脉冲边沿检测法对 异步控制信号 进行同步处理
  • 对异步信号的脉冲检测,一定要符合 奈奎斯特定理 也就是被检测信号的高/低电平都应该保持至少若干个周期

脉冲边沿检测用于脉冲计数、频率计数等等,下面会详细介绍一下具体的应用

脉冲计数器


功能定义

  • 输入脉冲数:一路
  • 使能信号:高电平进行计数,低电平清零计数
  • 计数器:在使能信号高电平期间对脉冲信号的上升沿进行检测并递增计数值

模块设计

`timescale 1ns/1ps
module pulse_counter(
	input i_clk,
	input i_rst_n,
	input i_pulse,
	input i_en,
	output reg[15:0] o_pulse_cnt
    );

/
//脉冲边沿检测
reg[1:0]	r_pulse;
wire w_rise_edge;	
always @(posedge i_clk)	
	if(!i_rst_n) r_pulse <= 2'b00;
	else r_pulse <= {r_pulse[0],i_pulse};
	//等价于:
	//r_pulse[0] <= i_pulse;
	//r_pulse[1] <= r_pulse[0];
	//相当于两个寄存器锁存i_pulse的数据,只是两个寄存器的数据相差了一个时钟周期
assign w_rise_edge = r_pulse[0] & ~r_pulse[1]; //脉冲信号的产生

///
//脉冲计数
always @(posedge i_clk)
	if(i_en) begin
		if(w_rise_edge) o_pulse_cnt <= o_pulse_cnt + 1;
		else	o_pulse_cnt <= o_pulse_cnt;
	end 
	else o_pulse_cnt <= 'b0;
endmodule

这只是一路的脉冲设计,在最后会有多路脉冲设计,我们可以采用模块化设计(结合generate语法设计),尽请期待

频率计数器


功能定义

  • 输入信号:
    • 周期性脉冲:要做边沿脉冲检测的脉冲频率信号
    • 使能信号:高电平频率计数,低电平清零计数器
  • 输出信号
    • 计数器:输出脉冲频率的计数值
    • 有效信号:该信号拉高时,输出的计数值有效

总的来说就是在使能控制下计算输入脉冲信号(已边沿检测)的每两个上升沿之间时钟周期数 并输出计数值,就是输出脉冲频率的计数值

模块设计

`timescale 1ns/1ps
module vlg_design(
	input i_clk,
	input i_rst_n,
	input i_pulse,
	input i_en,
	output o_vld,		//脉冲有效信号
	output reg[15:0] o_pulse_cnt	//输出时钟周期数
    );

reg[1:0] r_pulse;	
wire w_rise_edge;

//脉冲边沿检测逻辑	
always @(posedge i_clk)	
	if(!i_rst_n) r_pulse <= 2'b00;
	else r_pulse <= {r_pulse[0],i_pulse};
	
assign w_rise_edge = r_pulse[0] & ~r_pulse[1]; //脉冲信号的产生
/
//输出信号产生
reg r_flag;		//脉冲边沿检测标志位

always @(posedge i_clk)
	if(!i_rst_n) r_flag <= 'b0;
	else if (!i_en) r_flag <= 'b0;
	else if(w_rise_edge) r_flag <= 'b1;
	else ;
	
assign o_vld = w_rise_edge & r_flag;		//标志位总落后于脉冲,第一个脉冲无效,第二个开始有效计数


//脉冲计数
always @(posedge i_clk)
	if(!i_en) o_pulse_cnt <= 'b0;
	else if(o_vld) o_pulse_cnt <= 'b1;
	else o_pulse_cnt <= o_pulse_cnt + 1'b1;
	
endmodule

模块化设计


之前提到过,模块与模块之间的连接需要通过 例化 操作来实现,这里需要注意的一点是:顶层模块定义的输出端口在例化操作与子模块连接时,这个输出端口的定义应该是wire类型而不能是reg型,具体例化格式如下:

子模块名#(参数重载若需要)	例化模块名(.子模块端口1(例化模块端口1).子模块端口1(例化模块端口1)...);

上面的脉冲计数器设计只有一路脉冲,当我们有多路脉冲信号的时候就需要通过例化操作简化模块设计,即 将一路脉冲计数器设计成一个子模块,在顶层模块中分别进行例化 ,以4路信号为例,其中一路信号为:

pulse_counter	uut1_pulse_counter(
	.i_clk			(i_clk),
	.i_rst_n		(i_rst_n),
	.i_pulse		(i_pulse[0]),
	.i_en			(i_en),
	.o_pulse_cnt	(o_pulse_cnt1)
    );

但是这样的话,如果是128路脉冲信号,那岂不是要写128个例化模块???飘了呀,虽然都是ctrl+c/ctrl+v然后改一下例化端口,但是这样还是很复杂。那么这时候就需要新的语法设计来代替这些可以省略的步骤—— generate语法设计

generate语法设计

语法结构
genvar 循环变量名;	//定义genvar作为generate的循环变量
generate
	//generate for/if/case
	//或嵌套的generate语句
endgenerate

可以在generate中使用的语法语句包括module、UDP、门级原语、连续赋值语句、always语句、initial语句等等

以上假设真是128路脉冲信号,那么除了在顶层模块定义128路输出信号端口外,具体代码如下:

`timescale 1ns/1ps
module vlg_design(
	input i_clk,
	input i_rst_n,
	input[15:0] i_pulse,
	input i_en,
	output[15:0] o_pulse_cnt0,
	output[15:0] o_pulse_cnt1,
	output[15:0] o_pulse_cnt2,
	...
	output[15:0] o_pulse_cnt127		//wire型变量

    );

wire[15:0] r_pulse_cnt[127:0];	//二维向量【存储器】128个,每个r_pulse_cnt有16b的数据位宽
///
//generate的例化操作	
genvar i;

generate
	for(i = 0;i < 127;i = i+1)	begin
		pulse_counter	uut1_pulse_counter(
			.i_clk			(i_clk),
			.i_rst_n		(i_rst_n),
			.i_pulse		(i_pulse[i]),
			.i_en			(i_en),
			.o_pulse_cnt	(r_pulse_cnt[i])
			);
	end
	
endgenerate	

//generate语句最好不要将顶层模块输出端口作为例化模块的端口,即定义一个二维存储器
assign o_pulse_cnt0 = r_pulse_cnt[0];	
assign o_pulse_cnt1 = r_pulse_cnt[1];
...
assign o_pulse_cnt127 = r_pulse_cnt[127];

endmodule
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值