大家好,這幾天在各個論壇上,經常就有人在向我咨詢基于FPGA的串口通信代碼,大部分都是在網上下載一個現成的代碼,但是在使用中就遇到了各種問題,于是就發到了論壇上來求助。在閱讀了他們的代碼之后,我發現幾乎出自同一個版本(目前確定為特權同學的基于EPM240入門實驗的代碼)。他們在調試這個代碼的時候,經常存在這樣幾個問題:1、部分人對該串口通訊模塊完全不理解,對每句話,甚至每個模塊的功能都不理解;2、部分人采用最原始的畫波形的方式來對該模塊進行仿真,結果無法得到仿真結果;3、部分人不會使用modelsim對該設計進行仿真;4、絕大部分人不會編寫testbench;5、下板測試無法進行正確的字符串收發。在公司內部,我將這種現象和幾位老師交流之后,夏宇聞老師建議我專門針對該代碼寫一個由原理到代碼,由仿真到板級的調試筆記。爭取用最通俗,也是最笨的辦法,手把手的教會大家來調試這個代碼。 本調試筆記主要由五個部分組成:原始代碼分析;原始代碼驗證;對原始代碼進行修改;對修改后的代碼進行驗證;對修改后的設計進行板級驗證。每個部分,小梅哥都會用圖文結合的方式,教大家一步一步的來進行。 原始代碼分析 該代碼來自小梅哥最崇拜的大神,特權同學。當時小梅哥也是看著特權同學的書和視頻教程一步一步走過來的。特權同學的代碼實現了單字節的收發測試,沒有對連續字節的收發進行測試。特權同學當時也說過,這個只是一個簡單的實驗,離實際工業應用還有一定的距離。考慮到論壇上很多小伙伴都希望能夠實現連續字節的收發功能,因此小梅哥就在特權同學的代碼上進行了修改。修改后的代碼,輸入時鐘可以在一定范圍內選擇任意頻率,目前已經支持5種波特率選擇(9600、19200、38400、57600、115200),實際小梅哥還做過更高波特率的測試,目前實測在115200波特率的速率下可以實現超過9999999次連續無間斷的收發。這里,小梅哥首先將特權同學設計架構在這里列出來,以給讀者一個直觀的印象。 由上圖可知,特權同學的UART串口設計主要包含了四個模塊:串口發送模塊(my_uart_tx)、串口接收模塊(my_uart_rx)、串口接收波特率發生器(speed_rx)和串口發送波特率發生器(speed_tx),其中,串口發送波特率發生器主要用來產生串口發送模塊發送數據時所需的波特率時鐘,串口接收波特率發生器主要用來產生串口接收模塊接收數據時的波特率時鐘,串口發送模塊主要負責在指定波特率的速率下將待發送字節發送出去,串口接收模塊則主要負責接收來自其他設備發送過來的串口數據。 my_uart_top模塊即串口收發頂層模塊實現了各個模塊間的信號連接功能,通過該頂層模塊的連接,實現了將串口接收到的數據再發送出去的功能,即我們測試串口通信最常用的一種方式——回環測試。特權同學該實驗的各個端口和內部信號的意義如表1所示: 該實驗的內容為,串口接收模塊在檢測到發送設備發送過來的數據起始位時,接收中斷信號置1,該信號則作為啟動串口接收波特率發送器的控制信號,然后在每個波特率時鐘上升沿到來時讀取串口接收端口(rs232_rx)上的數據。一幀(字節)數據接收完成后,接收中斷信號拉低,停止波特率發生器工作,接收完成,系統進入等待狀態,等待下一次的數據到來。 同時,接收中斷信號的下降沿也作為串口發送模塊的發送使能信號,因為一旦接收中斷信號的下降沿出現,就表明接收模塊完成了一次數據的接收,此時,就開始使能串口發送波特率發生器,串口發送模塊則在波特率時鐘的上升沿到來時依次將接收模塊接收到的數據的每一位(發送模塊自動添加起始位和停止位)依次發送出去,當數據發送完成之后,停止串口發送波特率發生器的使能,模塊進入等待狀態,等待下一次接收中斷信號下降沿的到來。 這里,我們首先對該設計的波特率發生器模塊進行分析。該模塊相對簡單,代碼如下所示: 以下是代碼片段: 1 module speed_select ( 2 clk, rst_n , 3 bps_start , clk_bps 4 ); 5 6 input clk; // 50MHz 7 input rst_n ; // 8 input bps_start ; // 9 output clk_bps ; // clk_bps 10 11 /* 12 parameter bps9600 = 5207, // 9600bps 13 bps19200 = 2603, // 19200bps 14 bps38400 = 1301, // 38400bps 15 bps57600 = 867, // 57600bps 16 bps115200 = 433; // 115200bps 17 18 parameter bps9600_2 = 2603, 19 bps19200_2 = 1301, 20 bps38400_2 = 650, 21 bps57600_2 = 433, 22 bps115200_2 = 216; 23 */ 24 25 // 26 `define BPS_PARA 5207 // 9600 27 `define BPS_PARA_2 2603 // 9600 28 29 reg[ 12 : 0] cnt ; // 30 reg clk_bps_r ; // 31 32 //--------------------------------------------------------- 33 reg[ 2 : 0] uart_ctrl ; // uart 34 //--------------------------------------------------------- 35 36 always @ ( posedge clk or negedge rst_n ) 37 if(! rst_n ) cnt 38 else if(( cnt == `BPS_PARA ) || ! bps_start ) cnt 39 else cnt 40 41 always @ ( posedge clk or negedge rst_n ) 42 if(! rst_n ) clk_bps_r 43 else if( cnt == `BPS_PARA_2 ) clk_bps_r //clk_bps_r , 44 else clk_bps_r 45 46 assign clk_bps = clk_bps_r ; 47 48 endmodule 該代碼的12-22行用注釋的方式告訴了我們,獲得不同波特率時波特率分頻計數器的值應該為多少,以及波特率計數器計數到一半時的值為多少(該值作為對信號的采樣點,因為一般情況下,一位數據,在該位數據保持時間的中間時刻是最穩定的)。26行和27行定義的兩個參數BPS_PARA和BPS_PARA_2分別就是波特率分頻計數器的最大值和中間值。實際使用時,只需要根據你所需要的波特率,更改這兩個參數的值即可 。例如,選擇波特率為9600bps時,設定BPS_PARA=5207,BPS_PARA_2=2603。關于這個值的計算,這里暫時不提,后文會有交代。 36行至39行為波特率分頻計數器的計數進程,即波特率發生模塊沒有被使能(! bps_start)或者計數器計數到BPS_PARA后則將計數器清零,其它情況下則每來一個時鐘上升沿計數器自加1。通過此進程,則可得到相對精準的波特率定時。 41行至44行為數據采樣時刻的生成,上面提到過“一般情況下,一位數據,在該位數據保持時間的中間時刻是最穩定的”,因此這里我們在計數器計數到一半時,產生一個時鐘周期的高脈沖,此脈沖作為采樣數據的使能信號。 以上為波特率發生器的代碼及分析,波特率發生模塊在例化時被分別例化為串口發送波特率發生器和串口接收波特率發生器。接下來我們再來分析串口接收模塊的代碼。 1 module my_uart_rx ( 2 clk, rst_n , 3 rs232_rx , rx_data , rx_int , 4 clk_bps , bps_start 5 ); 6 7 input clk; // 50MHz 8 input rst_n ; // 9 input rs232_rx ; // RS232 10 input clk_bps ; // clk_bps 11 output bps_start ; // 12 output [ 7: 0] rx_data ; // 13 output rx_int ; // , 14 15 //--------------------------------------------------------- 16 reg rs232_rx0 , rs232_rx1 , rs232_rx2 , rs232_rx3 ; // 17 wire neg_rs232_rx ; // 18 19 always @ ( posedge clk or negedge rst_n ) begin 20 if(! rst_n ) begin 21 rs232_rx0 22 rs232_rx1 23 rs232_rx2 24 rs232_rx3 25 end 26 else begin 27 rs232_rx0 28 rs232_rx1 29 rs232_rx2 30 rs232_rx3 31 end 32 end 33 // 34 // 35 // 40ns 36 assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0; // neg_rs232_rx 37 38 //--------------------------------------------------------- 39 reg bps_start_r ; 40 reg[ 3: 0] num; // 41 reg rx_int ; // , 42 43 always @ ( posedge clk or negedge rst_n ) 44 if(! rst_n ) begin 45 bps_start_r 46 rx_int 47 end 48 else if( neg_rs232_rx ) begin // rs232_rx 49 bps_start_r 50 rx_int 51 end 52 else if( num==4'd12 ) begin // 53 bps_start_r 54 rx_int 55 end 56 57 assign bps_start = bps_start_r ; 58 59 //--------------------------------------------------------- 60 reg[ 7 : 0] rx_data_r ; // 61 //--------------------------------------------------------- 62 63 reg[ 7 : 0] rx_temp_data ; // 64 65 always @ ( posedge clk or negedge rst_n ) 66 if(! rst_n ) begin 67 rx_temp_data 68 num 69 rx_data_r 70 end 71 else if( rx_int ) begin // 72 if( clk_bps ) begin // , 8bit 1 2 73 num 74 case ( num) 75 4'd1:rx_temp_data[0] 76 4'd2:rx_temp_data [1] 77 4'd3:rx_temp_data [2] 78 4'd4:rx_temp_data [3] 79 4'd5:rx_temp_data [4] 80 4'd6:rx_temp_data [5] 81 4'd7:rx_temp_data [6] 82 4'd8:rx_temp_data [7] 83 default : ; 84 endcase 85 end 86 else if( num == 4'd12 ) begin // 1+8+1(2)=11bit 87 num 88 rx_data_r 89 end 90 end 91 92 assign rx_data = rx_data_r ; 93 94 endmodule 第19行到第36行為起始位檢測部分,19到32行,實現了對rs232_rx端口上電平的連續四個時鐘周期的寄存,第36行則對這連續4個時鐘上升沿時的rs232_rx端口電平進行邏輯操作,得出rs232_rx端口信號下降沿的到來。neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0,即后兩次寄存的狀態為低電平而前兩次寄存的裝填為高電平,則表明該端口上的信號發生了1->0的跳變,即有下降沿出現。neg_rs232_rx信號會產生一個周期的高脈沖。 第43行至第55行則根據neg_rs232_rx和num計數值來控制串口接收波特率發生器的工作和接收中斷信號。第65行至第90行則采用線性序列機的設計方式,進行一個字節的數據的接收。 以上為對串口接收模塊的一個簡單分析,接下來,再進行串口發送模塊的分析。 1 module my_uart_tx ( 2 clk, rst_n , 3 rx_data , rx_int , rs232_tx , 4 clk_bps , bps_start 5 ); 6 7 input clk; // 50MHz 8 input rst_n ; // 9 input clk_bps ; // clk_bps_r , 10 input [ 7 : 0] rx_data ; // 11 input rx_int ; 12 output rs232_tx ; // RS232 13 output bps_start ; // 14 15 //--------------------------------------------------------- 16 reg rx_int0 , rx_int1 , rx_int2 ; //rx_int 17 wire neg_rx_int ; // rx_int 18 19 always @ ( posedge clk or negedge rst_n ) begin 20 if(! rst_n ) begin 21 rx_int0 22 rx_int1 23 rx_int2 24 end 25 else begin 26 rx_int0 27 rx_int1 28 rx_int2 29 end 30 end 31 32 assign neg_rx_int = ~rx_int1 & rx_int2 ; // neg_rx_int 33 34 //--------------------------------------------------------- 35 reg[ 7 : 0] tx_data ; // 36 //--------------------------------------------------------- 37 reg bps_start_r ; 38 reg tx_en ; // 39 reg[ 3: 0] num; 40 41 always @ ( posedge clk or negedge rst_n ) begin 42 if(! rst_n ) begin 43 bps_start_r 44 tx_en 45 tx_data 46 end 47 else if( neg_rx_int ) begin // 48 bps_start_r 49 tx_data 50 tx_en 51 end 52 else if( num==4'd11 ) begin // 53 bps_start_r 54 tx_en 55 end 56 end 57 58 assign bps_start = bps_start_r ; 59 60 //--------------------------------------------------------- 61 reg rs232_tx_r ; 62 63 always @ ( posedge clk or negedge rst_n ) begin 64 if(! rst_n ) begin 65 num 66 rs232_tx_r 67 end 68 else if( tx_en ) begin 69 if( clk_bps ) begin 70 num 71 case ( num) 72 4'd0 : rs232_tx_r 73 4'd1 : rs232_tx_r 74 4'd2 : rs232_tx_r 75 4'd3 : rs232_tx_r 76 4'd4 : rs232_tx_r 77 4'd5 : rs232_tx_r 78 4'd6 : rs232_tx_r 79 4'd7 : rs232_tx_r 80 4'd8 : rs232_tx_r 81 4'd9 : rs232_tx_r 82 default : rs232_tx_r 83 endcase 84 end 85 else if( num==4'd11 ) num 86 end 87 end 88 89 assign rs232_tx = rs232_tx_r ; 90 91 endmodule 代碼19行到30行對串口接收模塊的接收中斷信號進行了3次寄存,第32行則通過對連續兩次寄存結果的判斷,來檢測接收中斷信號rx_int的下降沿。如果有下降沿到來,neg_rx_int信號則會產生一個時鐘周期的高脈沖信號,第47行則通過對該信號的狀態判斷,來確定是否啟動發送波特率發生器模塊。如果檢測到了該高脈沖,則使能串口發送(tx_en FPGA愛好者學習。 原始代碼驗證 前面,通對設計代碼的一個簡單分析,弄清楚了特權同學設計代碼的基本架構和思路。那么看過特權同學教學視頻的都知道,該代碼能夠實現一個字節的數據收發測試。那么這里,小梅哥就先對該設計進行一個仿真,通過仿真來分析此設計的性能。 仿真的思路很簡單,就是通過模擬串口發送過程,給該設計模塊發送數據,由前面分析可知,該設計模塊接收到數據后,會立即將數據發送出去,因此我們還需要對串口發送出來的數據進行分析,這里,熟悉Uart協議的,我們可以直接觀察發送波形。當然,為了更加直觀,我們也可以設計一個模擬串口接收數據的仿真模型,通過該模塊來讀取串口發送出來的數據?紤]到看這篇文章的大多是初學者,為了讓大家能夠更好的查看我們的仿真結果,同時教大家進行仿真模型的設計,小梅哥還是自己編寫了一個虛擬的串口仿真模型。驗證時,只需要將該仿真模型掛接到串口模塊上,則該模型便能夠自動的給串口模塊發送數據,同時接收串口發送過來的數據。并會實時的將發送的數據和接收的數據打印出來,實際在觀察仿真結果時,我們便只需要觀看打印的結果就可以了。該串口仿真模型的代碼如下所示: 以下是代碼片段: 1 `timescale 1ns/1ps 2 3 module Uart_module ( uart_rx , uart_tx , send_state ); 4 5 input uart_rx ; 6 output reg uart_tx ; 7 output reg send_state ; 8 9 reg Clk; 10 reg Rst_n ; 11 12 wire Mid_Flag_send ; 13 wire Mid_Flag_Receive ; 14 15 reg Receive_Baud_Start ; 16 reg [ 7 : 0] rx_data ; 17 18 initial Clk = 1; 19 always #10 Clk = ~Clk; 20 21 speed_select speed_select_Send ( 22 . clk( Clk), 23 . rst_n ( Rst_n ), 24 . bps_start ( 1'b1 ), 25 . clk_bps ( Mid_Flag_send ) 26 ); 27 28 speed_select speed_select_receive ( 29 . clk( Clk), 30 . rst_n ( Rst_n ), 31 . bps_start ( Receive_Baud_Start ), 32 . clk_bps ( Mid_Flag_Receive ) 33 ); 34 35 initial begin 36 Rst_n = 0; 37 uart_tx = 1; 38 send_state = 0; 39 #300 Rst_n = 1; 40 41 $display ( "Set Baud As 9600bps" ); 42 #200 ; Uart_Send ( 8'hb6 ); 43 #20 ; Uart_Send ( 8'he7 ); 44 #80 ; Uart_Send ( 8'hf0 ); 45 #500 ; Uart_Send ( 8'h02 ); 46 #300 ; Uart_Send ( 8'hb4 ); 47 #40 ; Uart_Send ( 8'he5 ); 48 #90 ; Uart_Send ( 8'hb0 ); 49 #1000 ; Uart_Send ( 8'h32 ); 50 #2000000 ; 51 $stop ; 52 end 53 54 task Uart_Send ; 55 input [ 7: 0] Data ; 56 begin 57 send_state = 1; 58 @( posedge Mid_Flag_send) #0.1 uart_tx = 0; 59 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [0]; 60 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [1]; 61 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [2]; 62 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [3]; 63 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [4]; 64 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [5]; 65 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [6]; 66 @( posedge Mid_Flag_send) #0.1 uart_tx = Data [7]; 67 @( posedge Mid_Flag_send) #0.1 uart_tx = 1; 68 $display ( "Uart_Send Data = %0h" , Data ); 69 send_state = 0; 70 end 71 endtask 72 73 initial begin 74 forever begin 75 @( negedge uart_rx ) 76 begin 77 Receive_Baud_Start = 1; 78 @( posedge Mid_Flag_Receive); 79 @( posedge Mid_Flag_Receive) rx_data [0] = uart_rx ; 80 @( posedge Mid_Flag_Receive) rx_data [1] = uart_rx ; 81 @( posedge Mid_Flag_Receive) rx_data [2] = uart_rx ; 82 @( posedge Mid_Flag_Receive) rx_data [3] = uart_rx ; 83 @( posedge Mid_Flag_Receive) rx_data [4] = uart_rx ; 84 @( posedge Mid_Flag_Receive) rx_data [5] = uart_rx ; 85 @( posedge Mid_Flag_Receive) rx_data [6] = uart_rx ; 86 @( posedge Mid_Flag_Receive) rx_data [7] = uart_rx ; 87 @( posedge Mid_Flag_Receive) Receive_Baud_Start = 0; 88 $display ( "Uart_receive Data = %0h" , rx_data ); 89 end 90 end 91 end 92 93 endmodule 94 因為在將代碼復制到word的過程中,會有一定的格式兼容問題,所以文中部分格式不是太規范,望各位理解,另外,完整的代碼,小梅哥也以pdf的形式提供了,感興趣的朋友可以下載學習。 本仿真模型的第一句話“`timescale 1ns/1ps”為仿真精度及時間的說明,定義時間精度為1ps,時間單位為1ns,那么我們在代碼編寫的過程中,如果寫成“#200”則表示延時200ns,因為時間精度為1ps,因此我們還可以進一步提高延時精度,如“#200.1”表示延時200.1ns。一般的測試文件(testbench)中,這句話作為第一句話,必寫,當然,時間精度和單位我們可以根據自己的需求更改,如寫成“`timescale 1us/1ns”或者“`timescale 1ns/1ns”等都是可以的。 該模塊作為一個仿真模型,就是虛擬了一個串口收發儀器,既然是一個串口收發儀器,則必然有串口發送端口和串口接收端口,因此在模塊名后面定義了三個端口。這與一般的testbench不同,一般的testbench作為仿真時的頂層,不需要端口,因此模塊名后就直接以“;”結束。該模塊的三個端口“uart_rx , uart_tx , send_state”分別為串口接收端口、串口發送端口、串口發送狀態信號。串口收發端口不用說,大家也已經知道了,串口發送狀態信號主要作為指示信號,指示當前仿真模型正在進行數據的發送過程。 第9行至第16行為測試文件中信號的定義,以前我們總是理解說這些信號就是待測試模塊的端口,需要在測試文件中定義。那么這里小梅哥更喜歡換一種方式來理解:我們自己的設計,本設計中即特權的串口模塊,是一個功能未知的黑盒子,這個黑盒子有一些信號線引出,有的信號線是作為輸入的,即需要外部輸入一定的信號作為激勵,而有的信號線是作為輸出的,能夠輸出一些數據,當然還有一些信號線是既能夠作為輸入,又能夠作為輸出的,即三態。我們要想知道這個黑盒子的功能,就需要給這個黑盒子的輸入信號線接上信號源,通過給這些輸入信號線一定的激勵,觀察其輸出端口上的響應,從而獲知該黑盒子的功能。那么在這里,對于待測試模塊的輸入端口,我們就接上信號發生器,對于輸出端口,我們就接上示波器或者邏輯分析儀,這樣,我們就能夠通過信號發生器給輸入端口產生一定的激勵,然后通過示波器觀察輸出端口的輸出了。即如下圖所示: 那么,我們的testbench主要實現信號發生器的功能,既然是信號發生器那么就一定有數據信號輸出,這個數據信號輸出就可以連接到我們的待測模塊上。待測試模塊的輸出端口,連接到我們的示波器或者邏輯分析儀的探頭上,這樣就實現了一個完整的測試系統,那么我們信號發生器的信號源,可能命名叫做,a,b,c,d,e……. 而我們示波器的探頭則命名為探頭1,探頭2……接下來就好理解了,在testbench,我們將信號發生器的輸出信號定義為reg型,而示波器的探頭定義為wire型。我們信號發生器的輸出信號線和示波器的探頭線都可以任意命名,實際使用時一一對應連接到待測試模塊的端口上,也可以就直接與待測模塊的各個端口名保持一致。本設計中,小梅哥讓testbench中的信號與待測模塊的端口保持一致。 第18行和19行為產生50MHz時鐘的語句。 因為本仿真模型實質上就是一個串口收發模塊,因此也需要有收發波特率發生器,這里小梅哥為了省事,直接調用了特權同學的波特率發生器模塊,來作為我仿真模型的波特率發生器。因為該波特率發生器本身也屬于待測試部分,小梅哥之所以敢放心的調用,是因為事先我已經通過仿真,確定了該波特率發生器功能的正確性。第21行至33行為分別例化得到發送波特率發生器和接收波特率發生器的代碼。 第54行至71行為發送一個完整字節的數據(自動添加起始位和停止位)的代碼,該部分寫成任務的形式,方便調用。當我們需要發送一個字節的數據時,例如,發送8'hb6,只需要寫“Uart_Send ( 8'hb6 )”即可,該任務便將自動執行,將數據發送出去。在一個字節的數據發送完成后,同時使用系統任務$display來打印當前發送的數據是多少,以方便我們直觀的觀察仿真運行過程。至于$display這個系統任務中各個部分的含義,請讀者自行閱讀verilog的語法書。代碼的42至49行便是調用此任務進行了多次數據的發送。 73行至91行為模擬串口接收部分,通過對串口模塊發送出來的數據進行接收,并將接收到的數據用$display函數打印出來。我們只需要閱讀發送數據和接收數據后打印出來的信息,即可判斷通信是否成功,待測模塊功能是否正常。 這里需要注意的是,打印出來的接收數據和發送數據是針對仿真模型來說的,send data是仿真模型發送出去的數據,對應待測模塊應該接收到的數據。receive data則是仿真模型接收到的數據,對應待測模塊發送的數據。 我們所編寫的測試文件,一定要是可控的,即在所有事務完成后,將仿真停下來,否則,仿真會一直進行下去,導致出現大量冗余波形,影響我們對仿真結果的分析。因此在第51行,當所有測試已經完成后,使用系統任務$stop將仿真停下來。 以上對小梅哥寫的串口仿真模型進行了介紹,在實際使用中,只需要將該模型與待測模塊按照如下圖所示的方式連接起來即可。 這里,小梅哥使用一個testbench文件作為頂層,將這兩個部分連接起來,同時產生my_uart_top工作所需的時鐘和復位信號。該文件詳細代碼如下: 1 `timescale 1ns /1ns 2 3 module Uart_tb ; 4 5 reg Clk; 6 reg Rst_n ; 7 8 wire uart_rx ; 9 wire uart_tx ; 10 wire send_state ; 11 12 my_uart_top u1 ( 13 . clk( Clk), 14 . rst_n ( Rst_n ), 15 . rs232_rx ( uart_tx ), 16 . rs232_tx ( uart_rx ) 17 ); 18 19 Uart_module u2 ( 20 . uart_rx ( uart_rx ), 21 . uart_tx ( uart_tx ), 22 . send_state ( send_state ) 23 ); 24 25 initial begin 26 Clk = 1; 27 Rst_n = 0; 28 #200 ; 29 Rst_n = 1; 30 end 31 32 always #10 Clk = ~Clk; 33 34 endmodule 35 該代碼實在簡單,只是實現了一個啟動時的初始化和50MHz時鐘的產生,因此小梅哥就不做任何分析了。 |