以前我曾一度傻乎乎的使用
always @ (posedge signal)
這樣的代碼去檢測signal的上升沿,鬧出了很多問題。
當受實驗室的一同學指教后,再也不會傻乎乎的這樣干了。當然,你看完下文也不會這樣干了。
檢測上升沿的原理:使用高頻的時鍾對信號進行采樣,因此要實現上升沿檢測,時鍾頻率至少要在信號最高頻率的2倍以上,否則就可能出現漏檢測。具體請參見下面代碼。
module edge_check(clk, rst_n, signal, pos_edge, neg_edge, both_edge); input clk; input rst_n; input signal; output pos_edge; output neg_edge; output both_edge; reg sig_r0, sig_r1; // 狀態寄存器 always @ (posedge clk or negedge rst_n) if (!rst_n) begin sig_r0 <= 1'b0; sig_r1 <= 1'b0; end else begin sig_r0 <= signal; sig_r1 <= sig_r0; end assign pos_edge = (~sig_r1) & (sig_r0); assign neg_edge = sig_r1 & (~sig_r0); assign both_edge = sig_r1 ^ sig_r0; // 雙邊沿檢測,或pos_edge|neg_edge endmodule
用Quartus II綜合布線之后的RTL視圖如下:
從RTL視圖中可以看出,電路是通過一個異步復位的D觸發器實現的。
ModelSim的仿真視圖如下,從中可看出已檢測出上升和下降沿,但存在一個延時,這是因為使用了時鍾同步的檢測。
或者上面的Verilog代碼還可以換一種寫法,效率上差不了太多;
module edge_check(clk, rst_n, signal, pos_edge, neg_edge, both_edge); input clk; input rst_n; input signal; output pos_edge; output neg_edge; output both_edge; reg [1:0]sig_fifo; reg sig_r0, sig_r1; // 狀態寄存器 always @ (posedge clk or negedge rst_n) if (!rst_n) begin sig_fifo <= 2'b0; end else begin sig_fifo <= {sig_fifo[0], signal}; end assign pos_edge = (sig_fifo == 2'b01); assign neg_edge = (sig_fifo == 2'b10); assign both_edge = sig_fifo[0] ^ sig_fifo[1]; // 雙邊沿檢測,或pos_edge|neg_edge endmodule
生成的RTL視圖為
單對於此小問題,當然采用倍頻實現雙沿計數也是可行的,但是我們不要忘記,倍頻器在很多CPLD或FPGA中是不支持的,即便支持其資源也是很寶貴的。
我看到的一些設計中,動輒采用某一信號作為時鍾,應該說這種做法是欠妥的。因為不是全局時鍾的時鍾信號最大扇出是有限的,其很難保證時鍾延時應小於信號延時的基本要求。當遇到要對某個信號的跳變沿處理時,建議采用上述小例子的處理辦法。