概述
脉冲边沿检测
原理
- 对输入脉冲信号进行 两级寄存器 锁存
- 对两级寄存器进行逻辑运算(异或),在其边沿脉冲电平变化时获取保持一个时钟周期的高电平
适用场景
- 同步/异步信号都可,可以使用脉冲边沿检测法对 异步控制信号 进行同步处理
- 对异步信号的脉冲检测,一定要符合 奈奎斯特定理 也就是被检测信号的高/低电平都应该保持至少若干个周期
脉冲边沿检测用于脉冲计数、频率计数等等,下面会详细介绍一下具体的应用
脉冲计数器
功能定义
- 输入脉冲数:一路
- 使能信号:高电平进行计数,低电平清零计数
- 计数器:在使能信号高电平期间对脉冲信号的上升沿进行检测并递增计数值
模块设计
`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
最后
以上就是踏实黑裤为你收集整理的FPGA杂记4——脉冲边沿检测及计数脉冲边沿检测脉冲计数器频率计数器模块化设计的全部内容,希望文章能够帮你解决FPGA杂记4——脉冲边沿检测及计数脉冲边沿检测脉冲计数器频率计数器模块化设计所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复