题解2 | #边沿检测#
边沿检测
https://www.nowcoder.com/practice/fed4247d5ef64ac68c20283ebace11f4
虽然有点复杂,但是实现了快速变化的a的边沿检测。
思路:
1. 首先用两个reg 值rise_cnt/down_cnt 记录a是否发生变化(always @(a)),一旦发生变化值为1.
在clk信号到来的时候,看rise_cnt / down_cnt 的值,若其为1,则说明a经历了上升或下降,则对应输出rise/down,并更新rise_cnt / down_cnt 的值为0
问题: 当a初始由 x/z 变为 0/1时 rise_cnt / down_cnt 会被更新,导致rise / down 出现错误输出
修改为:
2.用一个reg 值 a_temp 记录前一拍a的值, 保证当a变化时,前一拍a 的值 不是x 或者z 即可 ,这样写的话,只要a有变化,rise_cnt 或down_cnt 都会被更新,同时由于用a_temp对前一拍的值进行了记录,可以排除由初始x/z变为1/0 时错误的rise_cnt/down_cnt。
另外在写rise_cnt / down_cnt 变为1的条件的时候,注意并不是此刻 ( a & a_temp==0) 才允许 rise_cnt =1, 因为a可能有一个小的突变,如下图:
若要忽略这种突变,则可用其他友友的方式,直接在时钟里写
if (~a_temp & a) rise <= 1; // 拍1到来时更新了a_temp <= a, 此刻存的a为拍0的a的值为0,在拍1末尾更新a的值为0
// 在拍2 到来时带入表达式 (~a_temp & a) 用到的a_temp =0, a =0, 所以拍1中a的变化不会被记录
else if (a_temp & ~a) down <=1; // 拍0更新a_temp 的值为1(上一拍末尾a的值),a的值为0(拍0末尾a的值),
//在拍1时计算(a_temp & ~a) 为1, 拍1 可记录a在拍0时的下降沿
`timescale 1ns/1ns
module edge_detect(
input clk,
input rst_n,
input a,
output reg rise,
output reg down
);
reg rise_cnt = 1'b0;
reg down_cnt = 1'b0;
reg a_temp;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
a_temp <= 0;
else
a_temp <= a;
end
always @(a)
begin
if (a & a_temp==0)
begin
rise_cnt = rise_cnt + 1;
a_temp = 1;
down_cnt = down_cnt;
end
else if (!a & a_temp)
begin
rise_cnt = rise_cnt;
down_cnt = down_cnt +1;
end
else
begin
rise_cnt = 0;
down_cnt = 0;
end
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rise <= 1'b0;
rise_cnt <=1'b0;
down <= 1'b0;
down_cnt <= 1'b0;
end
else
case ({rise_cnt,down_cnt})
2'b00: begin
rise <= 0; rise_cnt <= 0;
down <= 0; down_cnt <= 0;
end
2'b10: begin
rise <= 1;
rise_cnt <= 0;
down <= 0;
down_cnt = down_cnt;
end
2'b01: begin
rise <= 0; rise_cnt <= rise_cnt;
down <= 1; down_cnt <= 0;
end
2'b11: begin
rise <= 1; rise_cnt <= 0;
down <= 1; down_cnt <= 0;
end
default:begin
rise <= 0; rise_cnt <= 0;
down <= 0; down_cnt <= 0;
end
endcase
end
endmodule
查看16道真题和解析