2020.8.1
一、嘰呱嘰呱
作為一名電子專業的小白菜,在大二的暑假終於開博客啦。目前開博客寫博文主要是為了在學習的過程中有所輸出+能和他人多多交流,也算是自己學習開發的一個記錄。
嘛,這個暑假到現在學了一丟丟基礎的verilog知識,短期目標是好好把FPGA學下去。
那其他廢話也不多說了,本文以串口收發的verilog代碼實現為主(基本復現黑金AX301的串口代碼),輔以一些必要的原理。
二、實驗原理
1.異步串口通信協議
串口傳輸時序圖:
從波形來看,在沒有數據傳輸時數據線保持高電平。當一次下降沿事件發生時,我們認為開始一次數據傳輸。代碼中默認一位起始位,八位數據位,無校驗位。
三、程序設計
1.串口接收模塊:
串口接收采用狀態機,如下圖:
S_IDLE:空閑狀態,表明此時無數據傳輸,當遇到輸入數據下降沿后轉換狀態至S_START;
S_START:起始狀態,此時數據線上為起始位的傳輸,當一位起始位傳輸結束后轉入S_REC_BYTE;
S_REC_BYTE:接收數據狀態,此時數據線接收串行輸入的數據,接收完畢后轉入S_STOP;
S_STOP:停止狀態,此時數據接收完畢,在等待半位數據的時間后(以免錯過下一個數據的起始位判斷),轉入S_IDLE(代碼實現中去掉了S_DATA狀態)。
2.串口發送模塊:
與接收模塊類似,
S_IDLE:空閑狀態,表明此時無數據傳輸,當遇到輸入數據下降沿后轉換狀態至S_START;
S_START:起始狀態,此時數據線上為起始位的傳輸,當一位起始位傳輸結束后轉入S_REC_BYTE;
S_SEND_BYTE:發送數據狀態,此時數據線發送輸入的並行八位數據,接收完畢后轉入S_STOP;
S_STOP:停止狀態,此時數據接收完畢,在等待一位數據的時間后,轉入S_IDLE。
3.實現功能
其他細節原理不多贅述,我的代碼基本與黑金開發板AX301的串口代碼一致,只是自己復現了一遍加了些自己理解的注釋。
實現串口調試助手發送數據至FPGA開發板,FPGA接收數據后返回對應數據,可在串口調試助手上查看。
4.具體代碼:
①串口接收模塊

1 module uart_rx 2 #( 3 parameter CLK_FRE = 50, //系統時鍾頻率 50(MHz) 4 parameter UART_BPS = 115200 //波特率為115200 5 ) 6 ( 7 input clk, 8 input rst_n, 9 input rx_in, 10 output reg[7:0] rx_out, 11 output reg rx_data_valid 12 ); 13 14 localparam CYCLE = CLK_FRE * 1000000 / UART_BPS; //傳輸一位數據需幾個時鍾 15 localparam S_IDLE = 3'd1; //輸入信號空閑狀態 16 localparam S_START = 3'd2; //輸入信號檢測到下降沿后,開始接收起始位 17 localparam S_REC_BYTE = 3'd3; //接收到一位起始位,開始接受數據 18 localparam S_STOP = 3'd4; //八位數據接收完畢 19 20 21 reg rx_in_0; //接收的數據延時一個時鍾 22 reg rx_in_1; //接收的數據延時兩個時鍾 23 wire rx_negedge; //rx_in下降沿判斷 24 reg [2:0] state; //當前輸入信號狀態 25 reg [2:0] next_state; //輸入信號下一個狀態 26 reg [8:0] cycle_cnt; //傳輸一位數據時,時鍾計數 27 reg [3:0] bit_cnt; //接收數據時,每位數據計數 28 reg [7:0] rx_bits; //接收數據的臨時存儲 29 30 31 //判斷輸入信號rx_in是否遇到下降沿 32 assign rx_negedge = rx_in_1 && (!rx_in_0); //輸入信號rx_in下降沿邊沿檢測 33 34 always@(posedge clk or negedge rst_n) 35 begin 36 if(rst_n == 1'b0) 37 begin 38 rx_in_0 <= 1'b0; 39 rx_in_1 <= 1'b0; 40 end 41 else 42 begin 43 rx_in_0 <= rx_in; 44 rx_in_1 <= rx_in_0; 45 end 46 end 47 48 //輸入信號狀態轉換 49 always@(posedge clk or negedge rst_n) 50 begin 51 if(rst_n == 1'b0) 52 state <= S_IDLE; 53 else 54 state <= next_state; 55 end 56 57 //輸入信號狀態機 58 always@(*) 59 begin 60 case(state) 61 S_IDLE: 62 if(rx_negedge == 1'b1) //檢測輸入信號下降沿 63 next_state = S_START; 64 else 65 next_state = S_IDLE; 66 S_START: 67 if(cycle_cnt == CYCLE - 1) //等到一位起始位后 68 next_state = S_REC_BYTE; 69 else 70 next_state = S_START; 71 S_REC_BYTE: 72 if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) //接收完八位數據后轉換狀態 73 next_state = S_STOP; 74 else 75 next_state = S_REC_BYTE; 76 S_STOP: 77 if(cycle_cnt == CYCLE/2 -1) //等待半位以免錯過下一個數據的起始位判斷 78 next_state = S_IDLE; 79 else 80 next_state = S_STOP; 81 default: 82 next_state = S_IDLE; 83 endcase 84 end 85 86 //接收一位數據時,時鍾計數 87 always@(posedge clk or negedge rst_n) 88 begin 89 if(rst_n == 1'b0) 90 cycle_cnt <= 9'b0; 91 else if(next_state != state || (state == S_REC_BYTE && cycle_cnt == CYCLE - 1)) //每當狀態轉變或接收數據時接收完一位數據,需要重新計數 92 cycle_cnt <= 9'b0; 93 else 94 cycle_cnt <= cycle_cnt + 9'b1; 95 end 96 97 //接收數據時,判斷接收了幾位數據的計數 98 always@(posedge clk or negedge rst_n) 99 begin 100 if(rst_n == 1'b0) 101 bit_cnt <= 3'b0; 102 else if(state == S_REC_BYTE) //當計數一位的時鍾完成,bit_cnt + 1 103 begin 104 if(cycle_cnt == CYCLE - 1) 105 bit_cnt <= bit_cnt + 3'b1; 106 else 107 bit_cnt <= bit_cnt; 108 end 109 else 110 bit_cnt <= 3'b0; 111 end 112 113 //接收數據臨時存儲,串轉並 114 always@(posedge clk or negedge rst_n) 115 begin 116 if(rst_n == 1'b0) 117 rx_bits <= 8'b0; 118 else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 -1) 119 rx_bits[bit_cnt] <= rx_in; 120 else 121 rx_bits <= rx_bits; 122 end 123 124 //輸出接收到的數據 125 always@(posedge clk or negedge rst_n) 126 begin 127 if(rst_n == 1'b0) 128 rx_out <= 8'b0; 129 else if(state == S_STOP && next_state != state) 130 rx_out <= rx_bits; 131 end 132 133 //判斷接收到的數據是否有效 134 always@(posedge clk or negedge rst_n) 135 begin 136 if(rst_n == 1'b0) 137 rx_data_valid <= 1'b0; 138 else if(state == S_STOP && next_state!=state) 139 rx_data_valid <= 1'b1; 140 else if(state == S_IDLE) 141 rx_data_valid <= 1'b0; 142 end 143 144 endmodule
②串口發送模塊

1 module uart_tx 2 #( parameter CLK_FRE = 50, 3 parameter UART_BPS = 115200 4 ) 5 ( input clk, 6 input rst_n, 7 input [7:0] tx_data_in, 8 input tx_data_valid, 9 output tx_data_out, 10 output reg tx_data_ready 11 ); 12 13 localparam CYCLE = CLK_FRE * 1000000 / UART_BPS; //傳輸一位數據需要幾個時鍾 14 localparam S_IDLE = 3'd1; 15 localparam S_START = 3'd2; 16 localparam S_SEND_BYTE = 3'd3; 17 localparam S_STOP = 3'd4; 18 19 reg[2:0] state; 20 reg[2:0] next_state; 21 reg[8:0] cycle_cnt; 22 reg[3:0] bit_cnt; 23 reg tx_data_reg; //串行輸出數據 24 reg[7:0] tx_data_latch; //輸入的要發送的數據鎖存 25 26 //發送狀態轉換 27 always@(posedge clk or negedge rst_n) 28 begin 29 if(rst_n == 1'b0) 30 state <= S_IDLE; 31 else 32 state <= next_state; 33 end 34 35 //發送狀態機 36 always@(*) 37 begin 38 case(state) 39 S_IDLE: 40 if(tx_data_valid == 1'b1) //發送請求有效時 41 next_state <= S_START; 42 else 43 next_state <= S_IDLE; 44 S_START: 45 if(cycle_cnt == CYCLE - 1) //發送完一位起始位后 46 next_state <= S_SEND_BYTE; 47 else 48 next_state <= S_START; 49 S_SEND_BYTE: 50 if(bit_cnt == 3'd7 && cycle_cnt == CYCLE - 1) //發送完八位數據后 51 next_state <= S_STOP; 52 else 53 next_state <= S_SEND_BYTE; 54 S_STOP: 55 if(cycle_cnt == CYCLE - 1) //等一位數據的時間轉到空閑狀態 56 next_state <= S_IDLE; 57 else 58 next_state <= S_STOP; 59 default: 60 next_state <= S_IDLE; 61 endcase 62 end 63 64 //發送一位數據時,時鍾計數 65 always@(posedge clk or negedge rst_n) 66 begin 67 if(rst_n == 1'b0) 68 cycle_cnt <= 0; 69 else if(next_state != state ||(state == S_SEND_BYTE && cycle_cnt == CYCLE - 1)) 70 cycle_cnt <=0; 71 else 72 cycle_cnt <= cycle_cnt + 9'b1; 73 end 74 75 //發送數據時,判斷發送了幾位數據的計數 76 always@(posedge clk or negedge rst_n) 77 begin 78 if(rst_n == 1'b0) 79 bit_cnt <= 0; 80 else if(state == S_SEND_BYTE) 81 begin 82 if(cycle_cnt == CYCLE - 1) 83 bit_cnt <= bit_cnt + 3'b1; 84 else 85 bit_cnt <= bit_cnt; 86 end 87 else 88 bit_cnt <= 0; 89 end 90 91 //輸入數據鎖存 92 always@(posedge clk or negedge rst_n) 93 begin 94 if(rst_n == 1'b0) 95 tx_data_latch <= 8'b0; 96 else if(state == S_IDLE && tx_data_valid == 1'b1) //當輸入數據線處於空閑狀態且輸入數據有效時 97 tx_data_latch <= tx_data_in; 98 end 99 100 //輸出數據,並入轉串出 101 always@(posedge clk or negedge rst_n) 102 begin 103 if(rst_n == 1'b0) 104 tx_data_reg <= 1'b1; 105 else 106 case(state) 107 S_IDLE,S_STOP: 108 tx_data_reg <= 1'b1; 109 S_START: 110 tx_data_reg <= 1'b0; 111 S_SEND_BYTE: 112 tx_data_reg <= tx_data_latch[bit_cnt]; 113 default: 114 tx_data_reg <= 1'b1; 115 endcase 116 end 117 assign tx_data_out = tx_data_reg; 118 119 //輸出數據完畢標志的檢驗 120 always@(posedge clk or negedge rst_n) 121 begin 122 if(rst_n == 1'b0) 123 tx_data_ready <= 1'b0; 124 else if(state == S_STOP && cycle_cnt == CYCLE - 1) //僅在輸出數據完畢后的一段時間,該標志置1 125 tx_data_ready <= 1'b1; 126 else if(state == S_IDLE) //我的理解是:使輸出數據完畢的標志的時間保持一段時間,以免數據的錯發漏發 127 if(tx_data_valid == 1'b1) 128 tx_data_ready <= 1'b0; 129 else 130 tx_data_ready <= 1'b1; 131 end 132 133 endmodule
③頂層模塊

1 module uart_top( 2 input clk, 3 input rst_n, 4 input uart_rx, 5 output uart_tx 6 ); 7 8 parameter CLK_FRE = 50; 9 parameter BAUD_RATE = 115200; 10 localparam IDLE = 2'd1; 11 localparam WAIT = 2'd2; 12 13 14 wire rx_data_valid; 15 wire[7:0] rx_data_out; 16 reg[7:0] tx_data_in; 17 reg tx_data_valid; 18 wire tx_data_ready; 19 reg[1:0] state; 20 reg[8:0] wait_cnt; 21 22 always@(posedge clk or negedge rst_n) 23 begin 24 if(rst_n == 1'b0) 25 begin 26 wait_cnt <= 9'b0; 27 state <= IDLE; 28 tx_data_valid <= 1'b0; 29 tx_data_in <= 8'b0; 30 end 31 else 32 case(state) 33 IDLE: 34 state <= WAIT; 35 WAIT: 36 begin 37 if(rx_data_valid == 1'b1) //當數據接收完畢后,發送有效位置1 38 begin 39 tx_data_valid = 1'b1; 40 tx_data_in <= rx_data_out; 41 end 42 else if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1) //如果數據發送完成,發送有效位置0 43 tx_data_valid <= 1'b0; 44 else 45 wait_cnt <= wait_cnt + 1; 46 if(wait_cnt >= CLK_FRE * 1000000) //如果等待超過一秒 47 begin 48 state <= IDLE; 49 wait_cnt <= 9'b0; 50 end 51 end 52 default: 53 state <= IDLE; 54 endcase 55 56 57 end 58 59 uart_rx 60 #( 61 .CLK_FRE (CLK_FRE), 62 .UART_BPS (BAUD_RATE) 63 )uart_rx_inst 64 ( 65 .clk (clk), 66 .rst_n (rst_n), 67 .rx_in (uart_rx), 68 .rx_out (rx_data_out), 69 .rx_data_valid (rx_data_valid) 70 ); 71 72 uart_tx 73 #( 74 .CLK_FRE (CLK_FRE), 75 .UART_BPS (BAUD_RATE) 76 )uart_tx_inst 77 ( 78 .clk (clk), 79 .rst_n (rst_n), 80 .tx_data_in (tx_data_in), 81 .tx_data_valid (tx_data_valid), 82 .tx_data_out (uart_tx), 83 .tx_data_ready (tx_data_ready) 84 ); 85 86 87 88 endmodule
5.調試結果
成功!