如何将信号delay n个Clk?

转载地址:http://www.cnblogs.com/oomusou/archive/2009/06/15/verilog_dly_n_clk.html

Abstract
在實務上為了與其他信號同步,常會故意delay幾個clk,本文整理出幾種常見的coding style。

Introduction
使用環境:NC-Verilog 5.4 + Debussy 5.4 + Quartus II 9.0

為什麼需要將值delay n的clk呢?比如說我想運算A+B,目前這個clk A已經到了,但B必須delay 3個clk之後才會到,為了運算A+B,勢必使用shift register將A delay 3個clk之後,才能與B同步,所以希望先做出delay 3個clk的功能,進而實作出delay n個clk。

Method 1:
delay_3t.v / Verilog
 

复制代码
1  /*  
2  (C) OOMusou 2009  http://oomusou.cnblogs.com
3 
4  Filename    : delay_3t.v
5  Compiler    : NC-Verilog 5.4
6  Description : delay 3t method 1
7  Release     : 06/15/2009 1.0
8  */
9 
10  module  delay_3t (
11    clk,
12    rst_n,
13    d,
14    q
15  );
16 
17  input   clk;
18  input   rst_n;
19  input   d;
20  output  q;
21 
22  reg  d_dly_1t;
23  reg  d_dly_2t;
24  reg  d_dly_3t;
25 
26  assign  q  =  d_dly_3t;
27 
28  always @( posedge  clk  or   negedge  rst_n)  begin
29     if  ( ! rst_n)  begin
30      d_dly_1t  <=   0 ;
31      d_dly_2t  <=   0 ;
32      d_dly_3t  <=   0 ;
33     end
34     else   begin
35      d_dly_1t  <=  d;
36      d_dly_2t  <=  d_dly_1t;
37      d_dly_3t  <=  d_dly_2t;
38     end
39  end
40 
41  endmodule
复制代码


Testbench
delay_3t_tb.v / Verilog

复制代码
1  /*  
2  (C) OOMusou 2009  http://oomusou.cnblogs.com
3 
4  Filename    : delay_3t_tb.v
5  Compiler    : NC-Verilog 5.4
6  Description : delay 3t method 1
7  Release     : 06/15/2009 1.0
8  */
9 
10  ` include   " delay_3t.v "
11 
12  module  delay_3t_tb;
13 
14  reg  clk;
15  reg  rst_n;
16  reg  d;
17  wire  q;
18 
19  delay_3t u0 (
20    .clk(clk),
21    .rst_n(rst_n),
22    .d(d),
23    .q(q)
24  );
25 
26  initial   begin
27    $fsdbDumpfile( " delay_3t.fsdb " );
28    $fsdbDumpvars( 0 , delay_3t_tb);
29  end
30 
31  //  clk
32  initial   begin
33    clk  =   0 ;
34     forever  # 10  clk  =   ~ clk;
35  end
36 
37  initial   begin
38    rst_n  =   0 ;
39    d  =   0 ;
40    # 20 ;
41    rst_n  =   1 ;
42    # 11 ;
43    d  =   1 ;
44    # 20 ;
45    d  =   0 ;
46    # 500 ;
47    $finish;
48  end
49 
50  endmodule
复制代码


模擬結果

del00

這是最常見,也最直覺的寫法。一個D-FF會delay 1個clk,要delay 3個clk,所以就用3個D-FF,經過模擬,q也的確delay了3個clk。

Method 2:
delay_3t.v / Verilog

复制代码
1  /*  
2  (C) OOMusou 2009  http://oomusou.cnblogs.com
3 
4  Filename    : delay_3t.v
5  Compiler    : NC-Verilog 5.4
6  Description : delay 3t method 2
7  Release     : 06/15/2009 1.0
8  */
9 
10  module  delay_3t (
11    clk,
12    rst_n,
13    d,
14    q
15  );
16 
17  input   clk;
18  input   rst_n;
19  input   d;
20  output  q;
21 
22  reg  d_dly_1t;
23  reg  d_dly_2t;
24  reg  d_dly_3t;
25 
26  assign  q  =  d_dly_3t;
27 
28  always @( posedge  clk  or   negedge  rst_n)  begin
29     if  ( ! rst_n)
30      {d_dly_3t, d_dly_2t, d_dly_1t}   <=   0 ;
31     else
32      {d_dly_3t, d_dly_2t, d_dly_1t}  <=  {d_dly_2t, d_dly_1t, d};
33  end
34 
35  endmodule
复制代码


Testbench與模擬的波型圖與Method 1完全一樣,所以加以省略。這種寫法在DE2的範例也曾經出現過,思維仍是3個D-FF,只是寫法比較tricky,利用了Verilog特有的{}語法,一行就解決,比Method 1更精簡。

不過這兩個寫法都有個問題,若要delay 10個clk,就得宣告10個reg,程式相當冗長,是否有更簡單的寫法呢?

我們知道Quartus II最後都會將我們寫的RTL code加以最佳化,來看看經過Quartus II編譯後,RTL Viewer長怎樣?

delay01

在合成之後,Quartus II僅用了一個3 bit的D-FF,將output q再接回d 2次,而達成delay 3個clk,並不是如我們原本所想的,使用了3個D-FF串聯。我們將這種硬體思維,再次用Verilog表達。

Method 3:
使用for

delay_nt.v / Verilog

复制代码
1  /*  
2  (C) OOMusou 2009  http://oomusou.cnblogs.com
3 
4  Filename    : delay_nt.v
5  Compiler    : NC-Verilog 5.4
6  Description : delay 3t method 3
7  Release     : 06/15/2009 1.0
8  */
9 
10  module  delay_nt (
11    clk,
12    rst_n,
13    d,
14    q
15  );
16 
17  parameter  n  =   1 ;
18 
19  input   clk;
20  input   rst_n;
21  input   d;
22  output  q;
23 
24  reg  [n - 1 : 0 ] r;
25 
26  assign  q  =  r[n - 1 ];
27 
28  integer  i;
29 
30  always @( posedge  clk  or   negedge  rst_n)  begin
31     if  ( ! rst_n) 
32      r  <=   0 ;
33     else   begin
34       for (i = 0 ; i < n - 1 ; i = i + 1 )
35        r[i + 1 <=  r[i];
36        
37      r[ 0 <=  d;
38     end
39  end
40 
41  endmodule
复制代码


既然要做個通用的shift register,很直覺的會想到用for,這種寫法在很多書上都曾看過,就我印象中,在J. BHASKERVerilog HDL Primer吳戈Verilog HDL與數字系統設計簡明教程這兩本書都用這種寫法。不過使用for仍不是最精簡的寫法。

Method 4:
delay_nt.v / Verilog

复制代码
1  /*  
2  (C) OOMusou 2009  http://oomusou.cnblogs.com
3 
4  Filename    : delay_nt.v
5  Compiler    : NC-Verilog 5.4
6  Description : delay 3t method 4
7  Release     : 06/15/2009 1.0
8  */
9 
10  module  delay_nt (
11    clk,
12    rst_n,
13    d,
14    q
15  );
16 
17  parameter  n  =   3 ;
18 
19  input   clk;
20  input   rst_n;
21  input   d;
22  output  q;
23 
24  reg  [n - 1 : 0 ] r;
25 
26  assign  q  =  r[n - 1 ];
27 
28  always @( posedge  clk  or   negedge  rst_n)  begin
29     if  ( ! rst_n) 
30      r  <=   0 ;
31     else
32      r  <=  {r, d};  
33  end
34 
35  endmodule
复制代码


{}寫法是Verilog的獨門絕技,這樣就不再需要for,這也是為什麼Verilog寧願從C語言搶走{}換來begin, end,因為{}這種合併的寫法非常的好用。Testbench與模擬波型圖也與Method 1與Method 2一樣,再次省略。這種寫法使用了parameter,無論要delay幾個clk,只需修改n即可,而且與Quartus II最佳化後的硬體一樣,我們再次將編譯過的RTL Viewer打開做驗證。

delay02

完整程式碼下載
delay_3t.7z (Method 1)
delay_3t2.7z (Method 2)
delay_nt2.7z (Method 3)
delay_nt.7z (Method 4)

Conclusion
這4種寫法最後合出來的硬體都一樣,表示現在的合成器都夠聰明,差別只是在哪種coding style較好,將來比較好維護。另外也是開開眼界,若將來閱讀其他人的code,馬上就知道對方想表達的意思。


#include <REGX52.H> #include <intrins.h> sbit DIO = P2^7; // TM1637 DIO ?? sbit CLK = P2^6; // TM1637 CLK ?? sbit motor_L1 = P3^3; // ?????1 sbit motor_L2 = P3^4; // ?????2 sbit motor_R1 = P3^5; // ?????1 sbit motor_R2 = P3^6; // ?????2 sbit sensor_L = P0^0; // ???? sbit sensor_R = P0^1; // ???? sbit tcrt_pin = P3^2; // TCRT5000 ???? #define DELAY_US(n) _nop_() // ???? void delayms(unsigned int xms) { unsigned int i, j; for (i = xms; i > 0; i--) { for (j = 110; j > 0; j--); } } // ?????? void stop() { motor_L1 = motor_L2 = motor_R1 = motor_R2 = 0; } void forward() { motor_L1 = 0; motor_L2 = 1; motor_R1 = 0; motor_R2 = 1; } void back() { motor_L1 = 1; motor_L2 = 0; motor_R1 = 1; motor_R2 = 0; } void left() { motor_L1 = 1; motor_L2 = 0; motor_R1 = 0; motor_R2 = 1; } void right() { motor_L1 = 0; motor_L2 = 1; motor_R1 = 1; motor_R2 = 0; } // ???? void obstacle_avoidance() { if (!sensor_R) left(); // ???? else if (!sensor_L) right(); // ???? else if (sensor_L && sensor_R) forward(); else back(); } // ???1???(??????) void Timer1_Init() { TMOD = 0x11; // ?????1???1 TL1 = 0x00; // ???? TH1 = 0x4C; // ????(?50ms) TR1 = 1; // ?????1 ET1 = 1; // ?????1?? EA = 1; // ????? } // ????1???(???????????) void Int1_Init() { IT1 = 1; // ????1????? EX1 = 1; // ??????1 EA = 1; // ????? } unsigned int M = 0, n = 0, v = 0; // ????1????(?????????) void Int1_Routine() interrupt 2 { if (!tcrt_pin) { // ?????? M++; // ???1 } } // ???1??????(????) void Timer1_Routine() interrupt 3 { TL1 = 0x00; // ???? TH1 = 0x4C; // ???? n++; // ?????1?????1 if (n == 20) { // ??1?(20 * 50ms) v = M / 20 * 3.14 * 65 * 10; // ????(????65mm,??:mm/s) n = 0; M = 0; } } // ???????TM1637 void send_bit(unsigned char bite) { if (bite) { DIO = 1; } else { DIO = 0; } DELAY_US(5); CLK = 1; DELAY_US(5); CLK = 0; } // ???????TM1637 void send_byte(unsigned char shu) { unsigned char i; for (i = 0; i < 8; i++) { send_bit(shu & 0x01); shu >>= 1; } DIO = 1; // ??DIO???? } // ???? void start_signal() { CLK = 1; DIO = 1; DELAY_US(5); DIO = 0; DELAY_US(5); CLK = 0; } // ???? void stop_signal() { CLK = 0; DIO = 0; DELAY_US(5); CLK = 1; DELAY_US(5); DIO = 1; } // ???TM1637 void tm1637_init() { DIO = 1; CLK = 1; } // ????TM1637 void write_command(unsigned char cmd) { start_signal(); send_byte(cmd); stop_signal(); } // ???? void display_data(unsigned char addr, unsigned char shu[]) { unsigned char i; start_signal(); send_byte(0x40); // ???????? stop_signal(); start_signal(); send_byte(addr); // ?????? for (i = 0; i < 4; i++) { send_byte(shu[i]); } stop_signal(); } // ?????? unsigned char seg_code[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 }; // ??? void main() { Timer1_Init(); // ??????1 Int1_Init(); // ???????1 TMOD = 0x20; // ???1??2 TH1 = 0xFD; // 9600??? TL1 = 0xFD; TR1 = 1; SCON = 0x50; // ????1,???? EA = 1; ES = 1; tm1637_init(); write_command(0xC0); // ????,???? while (1) { if (RI) { // ????????? unsigned char cmd = SBUF; RI = 0; switch (cmd) { case '1': forward(); break; case '2': back(); break; case '3': left(); delayms(200); stop(); break; // ???? case '4': right(); delayms(200); stop(); break; case '5': stop(); break; case '6': while (!RI) { // ???????? obstacle_avoidance(); delayms(100); } break; } } // ?????????? unsigned char speed_digits[4]; unsigned int temp = v; unsigned int i; for ( i = 3; i >= 0; i--) { speed_digits[i] = seg_code[temp % 10]; temp /= 10; } display_data(0xC0, speed_digits); // ???? } }有以下报错 小车基本控制.c(197): error C141: syntax error near 'unsigned', expected '__asm' 小车基本控制.c(197): error C202: 'speed_digits': undefined identifier 小车基本控制.c(198): error C141: syntax error near 'unsigned', expected '__asm' 小车基本控制.c(198): error C202: 'temp': undefined identifier 小车基本控制.c(199): error C141: syntax error near 'unsigned', expected '__asm' 小车基本控制.c(199): error C202: 'i': undefined identifier 小车基本控制.c(200): error C202: 'i': undefined identifier 小车基本控制.c(201): error C202: 'i': undefined identifier 小车基本控制.c(202): error C202: 'temp': undefined identifier 小车基本控制.c(204): error C202: 'speed_digits': undefined identifier
最新发布
05-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值