1、上升沿检测
代码如下:
module signal_pos(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output pos_pulse //输出脉冲信号
);
reg signal_reg0;
reg signal_reg1;
always @(posedge clk)begin
if(!i_rst_n)begin
signal_reg0 <= 1’b0;
signal_reg0 <= 1’b0;
end
else begin
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
end
end
assign pos_pulse = ~signal_reg1 & signal_reg0 //上升沿脉冲 宽度:一个时钟周期
endmodule
时序分析如下:
D 为输入原始信号:i_signal
2、下降沿检测
代码如下:
module signal_neg(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output neg_pulse //输出脉冲信号
);
reg signal_reg0;
reg signal_reg1;
always @(posedge clk)
begin
if(!i_rst_n)begin
signal_reg0 <= 1’b0;
signal_reg0 <= 1’b0;
end
else begin
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
end
end
assign neg_pulse = signal_reg1 & (~signal_reg0) //下降沿脉冲 宽度:一个时钟周期
endmodule
时序分析如下:
D 为输入原始信号:i_signal
3、双边沿检测
代码如下:
module signal_neg_pos(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output neg_pos_pulse //输出脉冲信号
);
reg signal_reg0;
reg signal_reg1;
always @(posedge clk)
begin
if(!i_rst_n)begin
signal_reg0 <= 1’b0;
signal_reg0 <= 1’b0;
end
else begin
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
end
end
assign neg_pos_pulse = signal_reg1 ^ signal_reg0 //上升沿、下降沿脉冲 宽度:一个时钟周期
endmodule
时序分析如下:
D 为输入原始信号:i_signa
边沿检测还可以这么写
这里有给出了一种检测边沿的方法,代码大同小异。
module signal_neg_pos(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output negedge_pulse, //检测下降沿 输出脉冲信号
output posedge_pulse, //检测上升沿 输出脉冲信号
output both_edge_pulse //检测双边沿 输出脉冲信号
);
reg [1:0]signal_edge;
always @(posedge clk) begin
if(!i_rst_n)begin
signal_edge <= 2’b0;
end
else begin
signal_edge <= {signal_edge[0],i_signal} //输入信号从低位进入,左移,bit1为旧状态,bit0 为新状态
end
end
//双边沿沿:旧状态bit1 与 新状态bit0 异或需为1,故需 signal_edge[0] ^ signal_edge[1] == 1
//上升沿:旧状态bit1应为 0,新状态bit0应为 1,故需 signal_edge[1:0] == 2‘b01
//下降沿:旧状态bit1应为 1,新状态bit0应为 0,故需 signal_edge[1:0] == 2‘b10
assign both_edge_pulse = signal_edge[0] ^ signal_edge[1] //双边沿 宽度:一个时钟周期
assign posedge_pulse = (signal_edge[1:0] == 2’b01) ; //上升沿 宽度:一个时钟周期
assign negedge_pulse = (signal_edge[1:0] == 2’b10) ; //下降沿 宽度:一个时钟周期
//类似前一种方案,还可以这样写
//assign both_edge_pulse = signal_edge[0] ^ signal_edge[1] //双边沿 宽度:一个时钟周期
//assign posedge_pulse = ~signal_edge[1] & signal_edge[0] ; //上升沿 宽度:一个时钟周期
//assign negedge_pulse = signal_edge[1] & ~signal_edge[0] ; //下降沿 宽度:一个时钟周期
endmodule
亚稳态解决
避免亚稳态,只需要将输入信号打几拍即可
方案1:
跟上面边沿检测一个思路:
module signal_buff_out(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output out_buff // 打拍缓冲输出信号
);
reg [15:0] signal_reg;
reg [3:0] bit_num;
always @(posedge clk) begin
if(!i_rst_n)begin
signal_reg <= 16’b0;
end
else begin
signal_edge <= {signal_reg[15:0],i_signal}
end
end
assign out_buff = signal_edge[bit_num]; //根据需要,选择输入信号打拍次数 :0-15 ,0:打一拍
endmodule
方案2:
这种方法使用打拍次数较少的场合
module signal_buff_out(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output out_buff // 打拍缓冲输出信号
);
- 1
reg signal_reg0;
reg signal_reg1;
reg signal_reg2;
always @(posedge clk)
begin
if(!i_rst_n)begin
signal_reg0 <= 1’b0;
signal_reg1 <= 1’b0;
signal_reg2 <= 1’b0;
end
else begin
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
signal_reg2 <= signal_reg1 ;
end
end
//assign out_buff = signal_reg0 ; //固定为1拍
//assign out_buff = signal_reg1 ; //固定为2拍
assign out_buff = signal_reg2 ; //固定为3拍
endmodule
细究下边沿检测原理
我将结合时序图 ,并以这段代码为例逐步分析下是如何实现上升沿检测的
module signal_pos(
input i_clk, //时钟输入
input i_rst_n, //复位信号
input i_signal, // 输入信号 待检测信号
output pos_pulse //输出脉冲信号
);
reg signal_reg0;
reg signal_reg1;
always @(posedge clk)begin
if(!i_rst_n)begin
signal_reg0 <= 1’b0;
signal_reg0 <= 1’b0;
end
else begin
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
end
end
assign pos_pulse = ~signal_reg1 & signal_reg0 //上升沿脉冲 宽度:一个时钟周期
endmodule
看下面这两句话:
signal_reg0 <= i_signal;
signal_reg1 <= signal_reg0 ;
- 1
- 2
为了看着方便,我将输入信号 i_signal 替换为 in, signal_reg0 替换为reg0, signal_reg1 替换为reg1
reg0 <= in;
reg1 <= reg0 ;
- 1
- 2
下面这样图,把打拍的实际电路呈现了出来,打了两排,实际上就是让数据IN经过了两个D触发器缓冲输出,这种打拍方式也用于将数据跟时钟信号同步。
至于为上升沿为啥是:~reg1 & reg0 ,根据这张图,不妨这样理解:
1、先把这两个串联的D触发器理解成一个移位寄存器;
2、上升沿的跳变0 —>1,看成两个进入移位寄存器的数据 :0进入移位寄存器,1后进入移位寄存器;
3、经历一定的时钟周期后,先进入移位寄存器的0,进入了reg1寄存器,后进入的 1,进入了reg0寄存器;
4、reg1中保存的是老状态0,reg0中保存的是新状态1,当检测到相邻的两个D触发器中的数据不同时,说明有跳变沿,所以上升沿就是 ~reg1 & reg0 = 1;
5、因为是采用的相邻的两个D触发器中的数据检测变化,所以4中产生的 ~reg1 & reg0 = 1 也只有一个时钟周期的宽度。
6、要想让检测到的上升沿脉冲信号宽一点,如果你理解了的话,应该知道怎么做了,哈哈!看下图应该懂了吧!!
还可以以下图的方式来理解,前提是要对阻塞赋值和非阻塞赋值有比较深刻的理解:
结合流程图时序图可以清楚的看到,上升沿脉冲是在2、3之间产生的。
边沿检测(上升沿 、下降沿,双边沿)_下降沿检测_正在卷IC的小白的博客-CSDN博客
边沿检测--针对输入信号的跳变进而输出判断结果,上升沿、下降沿可以分别进行寄存器打拍,而后相与或者相或。当然针对与边沿检测,还有其他方法,例如通过移位寄存器,将输入信号打入移位寄存器中,然后对移位寄存器中的信号进行相与、相或和异或。这里进行打拍处理。
针对上升沿分析:--当时钟处于上升沿时,检测输入信号是否发生从0到1的跳变。正确理解其中的逻辑就很简单了。
上升沿检测:
module up_decetor(clk,rst,d_in,d_out);
input clk;
input rst;
input d_in;
output d_out;
reg d_out_1,d_out_2;
always@(posedge clk or negedge rst) begin
if(!rst) begin //通过寄存器保存前一个数据,使后一个数据与前一个数据进行比较
d_out_1 <= 1'b0;
d_out_2 <= 1'b0;
end
else begin
d_out_1 <= d_in;
d_out_2 <= d_out_1;
end
end
assign d_out = (~d_out_2)&d_out_1; //取反相与
endmodule
tb:
module up_decetor_tb;
reg clk;
reg rst;
reg d_in;
wire d_out;
up_decetor u1(
.clk(clk),
.rst(rst),
.d_in(d_in),
.d_out(d_out)
);
initial begin
clk=1'b1;
rst=1'b0;
#5;
rst=1'b1;
end
always #5 clk = ~clk;
/*initial begin
$vcdpluson;
end*/
initial begin
repeat(40) begin
#10;
d_in = {$random}%2;
end
end
endmodule
下降沿检测
module down_decetor(clk,rst,d_in,d_out);
input clk;
input rst;
input d_in;
output d_out;
reg d_out_1,d_out_2;
always@(posedge clk or negedge rst) begin
if(!rst) begin
d_out_1 <= 1'b0;
d_out_2 <= 1'b0;
end
else begin
d_out_1 <= d_in;
d_out_2 <= d_out_1;
end
end
assign d_out = (~d_out_1)&d_out_2; //
endmodule
tb:
module down_decetor_tb;
reg clk;
reg rst;
reg d_in;
wire d_out;
down_decetor u1(
.clk(clk),
.rst(rst),
.d_in(d_in),
.d_out(d_out)
);
initial begin
clk=1'b1;
rst=1'b0;
#5;
rst=1'b1;
end
always #5 clk = ~clk;
initial begin
$vcdpluson;
end
initial begin
#10;
d_in=1'b1;
#10;
d_in=1'b0;
#10;
d_in=1'b0;
#10;
d_in=1'b1;
#10;
d_in=1'b0;
#20;
d_in=1'b1;
#10;
d_in=1'b1;
#10;
d_in=1'b0;
#30;
d_in=1'b1;
#50;
$finish();
end
endmodule
双边沿检测:
module double_decetor(clk,rst,d_in,d_out);
input clk;
input rst;
input d_in;
output d_out;
reg d_out_1,d_out_2;
always@(posedge clk or negedge rst) begin
if(!rst) begin
d_out_1 <= 1'b0;
d_out_2 <= 1'b0;
end
else begin
d_out_1 <= d_in;
d_out_2 <= d_out_1;
end
end
assign d_out = d_out_1^d_out_2; //异或
endmodule
tb:
module down_decetor_tb;
reg clk;
reg rst;
reg d_in;
wire d_out;
double_decetor u1(
.clk(clk),
.rst(rst),
.d_in(d_in),
.d_out(d_out)
);
initial begin
clk=1'b1;
rst=1'b0;
#5;
rst=1'b1;
end
always #5 clk = ~clk;
initial begin
$vcdpluson;
end
initial begin
#10;
d_in=1'b1;
#20;
d_in=1'b0;
#10;
d_in=1'b1;
#10;
d_in=1'b0;
#10;
d_in=1'b1;
#10;
d_in=1'b0;
#20;
d_in=1'b1;
#10;
d_in=1'b0;
#30;
d_in=1'b1;
#50;
$finish();
end
endmodule
https://www.cnblogs.com/shadow-fish/p/13418146.html