题解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