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.调试结果
成功!