☰
用Verilog在FPGA上实现UART
2026/10/14 4:15:31 网站建设 项目流程

1.RX模块,接收数据

/******************************************************************* Engineer: Dawn. Create Date: 2026-10-08 13:54:58 Description: UART接收模块 1.波特率可调 2.校验可选,校验模式模式可选 3.数据位宽可调 4.停止位的位宽可调 Revision: Additional Comments: ********************************************************************/ module uart_rx #( parameter SYS_CLOCK = 50_000_000,//系统时钟 parameter BAUD_RATE = 9600 ,//波特率 parameter DATA_WIDTH = 8 ,//数据位位宽 parameter STOP_WIDTH = "TYPE1.0" ,//停止位位宽 可选:TYPE1.0 TYPE1.5 TYPE2.0 parameter CHECK_SELECT = "YES" ,//检验位选择 "YES" "NO" parameter CHECK_TYPE = "ODD" //奇偶校验类型 奇校验:"ODD" 偶校验"EVEN" )( //----用户接口------------------------------------------------------ input clk , input rst_n , output reg [DATA_WIDTH-1:0] rx_data , output rx_datavalid, output rx_check_err,//校验位错误信号(脉冲信号) //----UART RX PIN--------------------------------------------------- input uart_rxd ); localparam IDLE = 5'b00001; localparam START = 5'b00010; localparam DATA = 5'b00100; localparam CHECK = 5'b01000; localparam STOP = 5'b10000; localparam Baud_max = SYS_CLOCK / BAUD_RATE;//计数一个波特率周期需要多少系统时钟周期 reg [4:0] state_c;//现态 reg [4:0] state_n;//次态 reg [2:0] rxd_r;//打三拍寄存器 wire rxd_n_edge; reg [$clog2(Baud_max):0] cnt_baud ;//波特率周期计数器 wire add_cnt_baud; wire end_cnt_baud; wire [$clog2(Baud_max):0] stop_baud_max; reg [$clog2(DATA_WIDTH)-1:0] cnt_bit ; wire add_cnt_bit; wire end_cnt_bit; //状态转移条件的中间信号 wire idle2start ; wire start2data ; wire data2check ; wire data2cstop ; wire check2stop ; wire stop2idle ; //状态机 always @(posedge clk or negedge rst_n)begin if(!rst_n) state_c <= IDLE; else state_c <= state_n; end always @(*) begin case(state_c) IDLE : state_n = idle2start ? START : state_c; START : state_n = start2data ? DATA : state_c; DATA : begin if(data2check) state_n = CHECK; else if(data2cstop) state_n = STOP; else state_n = state_c; end CHECK : state_n = check2stop ? STOP : state_c; STOP : state_n = stop2idle ? IDLE : state_c; default : state_n = IDLE; endcase end assign idle2start = (state_c == IDLE) && rxd_n_edge; assign start2data = (state_c == START) && end_cnt_baud; assign data2check = (state_c == DATA) && end_cnt_bit && CHECK_SELECT == "YES"; assign data2cstop = (state_c == DATA) && end_cnt_bit && CHECK_SELECT == "NO"; assign check2stop = (state_c == CHECK) && end_cnt_baud; assign stop2idle = (state_c == STOP) && end_cnt_baud; //---------<下降沿检测>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n) rxd_r <= 3'b111; else rxd_r <= {rxd_r[1:0],uart_rxd}; end assign rxd_n_edge = ~rxd_r[1] & rxd_r[2]; //---------<波特率周期计数器>---------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_baud <= 'd0; end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0; end else begin cnt_baud <= cnt_baud + 1'b1; end end end assign add_cnt_baud = state_c != IDLE; assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? (stop_baud_max - 1) : (Baud_max - 1));//优先级 // always @(*)begin // case (STOP_WIDTH) // "TYPE1.0" : stop_baud_max = Baud_max*3/4; // "TYPE1.5" : stop_baud_max = Baud_max*5/4; // "TYPE2.0" : stop_baud_max = Baud_max*7/4; // default: stop_baud_max = 0; // endcase // end assign stop_baud_max = STOP_WIDTH == "TYPE1.0" ? Baud_max*3/4 : ( STOP_WIDTH == "TYPE1.5" ? Baud_max*5/4 : (STOP_WIDTH == "TYPE2.0" ? Baud_max*7/4 : 1)); //---------<比特计数器>------------------------------------------------- always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_bit <= 'd0; end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0; end else begin cnt_bit <= cnt_bit + 1'b1; end end end assign add_cnt_bit = state_c == DATA && end_cnt_baud; assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_WIDTH - 1; //---------<检验计算>------------------------------------------------- reg cal_check_bit;//接收数据计算的校验位 reg rx_ckeck_bit; always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cal_check_bit <= 1'b0; rx_ckeck_bit <= 1'b0; end else if(state_c == CHECK && cnt_baud == (Baud_max>>1)- 1)begin cal_check_bit <= CHECK_TYPE == "ODD" ? ~^rx_data : ^rx_data; rx_ckeck_bit <= rxd_r[2]; end end //---------<输出>------------------------------------------------- //rx_data always @(posedge clk or negedge rst_n)begin if(!rst_n) rx_data <= 'd0; else if(state_c == DATA && cnt_baud == (Baud_max>>1)- 1)//采样数据时刻 rx_data[cnt_bit] <= rxd_r[2]; end assign rx_datavalid = stop2idle; assign rx_check_err = check2stop && cal_check_bit != rx_ckeck_bit; endmodule

2.TX模块,发送数据

/******************************************************************* Engineer: Dawn. Create Date: 2026-10-08 13:54:58 Description: UART发送模块 1.波特率可调 2.校验可选,校验模式可选 3.数据位宽可调 4.停止位的位宽可调 Revision: Additional Comments: ********************************************************************/ // 例化模板: // uart_tx #( // .SYS_CLOCK (50_000_000),//系统时钟 // .BAUD_RATE (9600 ),//波特率 // .DATA_WIDTH (8 ),//数据位位宽 // .STOP_WIDTH ("TYPE1.0" ),//停止位位宽 可选:TYPE1.0 TYPE1.5 TYPE2.0 // .CHECK_SELECT("YES" ),//校验位选择 "YES" "NO" // .CHECK_TYPE ("ODD" ) //奇偶校验类型 奇校验:"ODD" 偶校验"EVEN" // )u_uart_tx( // //----用户接口------------------------------------------------------ // /*input */.clk (), // /*input */.rst_n (), // /*input [DATA_WIDTH-1:0] */.tx_data (),//待发送数据 // /*input */.tx_start (),//发送启动信号 // /*output reg */.tx_busy (),//模块忙标志 // /*output reg */.tx_done (),//发送完成脉冲 // //----UART TX PIN--------------------------------------------------- // /*output reg */.uart_txd ()//UART发送数据线 // ); module uart_tx #( parameter SYS_CLOCK = 50_000_000,//系统时钟 parameter BAUD_RATE = 9600 ,//波特率 parameter DATA_WIDTH = 8 ,//数据位位宽 parameter STOP_WIDTH = "TYPE1.0" ,//停止位位宽 可选:TYPE1.0 TYPE1.5 TYPE2.0 parameter CHECK_SELECT = "YES" ,//校验位选择 "YES" "NO" parameter CHECK_TYPE = "ODD" //奇偶校验类型 奇校验:"ODD" 偶校验"EVEN" )( //----用户接口------------------------------------------------------ input clk , input rst_n , input [DATA_WIDTH-1:0] tx_data ,//待发送数据 input tx_start ,//发送启动信号 output reg tx_busy ,//模块忙标志 output reg tx_done ,//发送完成脉冲 //----UART TX PIN--------------------------------------------------- output reg uart_txd //UART发送数据线 ); localparam IDLE = 5'b00001; localparam START = 5'b00010; localparam DATA = 5'b00100; localparam CHECK = 5'b01000; localparam STOP = 5'b10000; localparam Baud_max = SYS_CLOCK / BAUD_RATE;//计数一个波特率周期需要多少系统时钟周期 reg [4:0] state_c;//现态 reg [4:0] state_n;//次态 reg [$clog2(Baud_max):0] cnt_baud ;//波特率周期计数器 wire add_cnt_baud; wire end_cnt_baud; reg [$clog2(Baud_max):0] stop_baud_max; reg [$clog2(DATA_WIDTH)-1:0] cnt_bit ; wire add_cnt_bit; wire end_cnt_bit; //状态转移条件的中间信号 wire idle2start ; wire start2data ; wire data2check ; wire data2stop ; wire check2stop ; wire stop2idle ; reg [DATA_WIDTH-1:0] tx_data_reg; //寄存输入待发送数据 reg tx_check_bit;//计算得到的校验位 //========状态机第一段:时序逻辑,现态更新======== always @(posedge clk or negedge rst_n)begin if(!rst_n) state_c <= IDLE; else state_c <= state_n; end //========状态机第二段:组合逻辑,次态判断======== always @(*) begin case(state_c) IDLE : state_n = idle2start ? START : state_c; START : state_n = start2data ? DATA : state_c; DATA : begin if(data2check) state_n = CHECK; else if(data2stop) state_n = STOP; else state_n = state_c; end CHECK : state_n = check2stop ? STOP : state_c; STOP : state_n = stop2idle ? IDLE : state_c; default : state_n = IDLE; endcase end //========状态跳转条件赋值======== assign idle2start = (state_c == IDLE) && tx_start; assign start2data = (state_c == START) && end_cnt_baud; assign data2check = (state_c == DATA) && end_cnt_bit && CHECK_SELECT == "YES"; assign data2stop = (state_c == DATA) && end_cnt_bit && CHECK_SELECT == "NO"; assign check2stop = (state_c == CHECK) && end_cnt_baud; assign stop2idle = (state_c == STOP) && end_cnt_baud; //======== 输入数据寄存:启动发送时锁存tx_data,防止外部变化干扰 ======== always @(posedge clk or negedge rst_n)begin if(!rst_n)begin tx_data_reg <= 'd0; end else if(idle2start)begin tx_data_reg <= tx_data; end end //======== 校验位计算:锁存数据后提前算出奇偶校验位 ======== always @(*)begin if(CHECK_TYPE == "ODD")begin tx_check_bit = ~^tx_data_reg; //奇校验:所有bit异或取反 end else begin tx_check_bit = ^tx_data_reg; //偶校验:所有bit异或 end end //======== 波特率计数器 cnt_baud ======== always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_baud <= 'd0; end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0; end else begin cnt_baud <= cnt_baud + 1'b1; end end end assign add_cnt_baud = state_c != IDLE; assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? (stop_baud_max - 1) : (Baud_max - 1)); //停止位周期配置,和rx保持一致 always @(*)begin if(!rst_n) stop_baud_max = 0; else case (STOP_WIDTH) "TYPE1.0" : stop_baud_max = Baud_max*3/4; "TYPE1.5" : stop_baud_max = Baud_max*5/4; "TYPE2.0" : stop_baud_max = Baud_max*7/4; default: stop_baud_max = 0; endcase end //======== 比特计数器 cnt_bit:DATA阶段,每发完1bit就加1 ======== always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_bit <= 'd0; end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0; end else begin cnt_bit <= cnt_bit + 1'b1; end end end assign add_cnt_bit = state_c == DATA && end_cnt_baud; assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_WIDTH - 1; //======== 串行发送 uart_txd 组合输出 ======== always @(posedge clk or negedge rst_n)begin if(!rst_n)begin uart_txd <= 1'b1; //空闲电平为高 end else begin case(state_c) IDLE : uart_txd <= 1'b1; START : uart_txd <= 1'b0; //起始位:低电平 DATA : uart_txd <= tx_data_reg[cnt_bit]; //依次输出数据bit CHECK : uart_txd <= tx_check_bit; //输出校验位 STOP : uart_txd <= 1'b1; //停止位:高电平 default: uart_txd <= 1'b1; endcase end end //======== tx_busy 忙信号:非IDLE状态就拉高 ======== always @(posedge clk or negedge rst_n)begin if(!rst_n)begin tx_busy <= 1'b0; end else begin tx_busy <= (state_n != IDLE);//和state_c保持同步 end end //======== tx_done 完成脉冲:stop跳转到idle时产生一个周期脉冲 ======== always @(*)begin tx_done <= stop2idle; end endmodule

3.loop_ctrl模块,运用FIFO存取数据

/******************************************************************* Engineer: Dawn. Create Date: 2026-10-09 10:38:27 Description: PC通过串口上位机发送数据到FPGA,当FPGA每缓存10字节数据后,发送PC端 Revision: Additional Comments: ********************************************************************/ module loop_ctrl#( parameter TRANS_LENTH = 10 )( input clk , input rst_n , //----uart_rx------------------------- input [7:0] rx_data , input rx_dv , //rx_datavalid //----uart_tx------------------------- output [7:0] tx_data , output tx_dv , //tx_datavalid input tx_busy , input tx_done ); wire wrreq ; wire rdreq ; wire empty ; wire full ; wire [7:0] fifo_q; wire [6:0] usedw ;//可用数据裕量/余量 reg tx_flag;//从FIFO中读数据的指示信号 reg [$clog2(TRANS_LENTH)-1 : 0] cnt_0; sfifo_8x128 u_sfifo_8x128( .aclr (~rst_n), .clock (clk ), .data (rx_data), .rdreq (rdreq ), .wrreq (wrreq), .empty (empty ), .full (full ), .q (fifo_q), .usedw (usedw ) ); assign wrreq = ~full && rx_dv; assign rdreq = ~empty && ~tx_busy && tx_flag; always @(posedge clk)begin if(!rst_n) tx_flag <= 1'b0; else if(usedw >= TRANS_LENTH) tx_flag <= 1'b1; else if(tx_flag && tx_done && cnt_0 == TRANS_LENTH - 1) //tx_done:来自 uart_tx 模块的发送完成脉冲信号。每当一个字节(8 bit数据+校验位+停止位)完整发送完毕,会产生一个时钟周期的高电平。 tx_flag <= 1'b0; end //cnt_0 always @(posedge clk)begin if(!rst_n) cnt_0 <= 'd0; else if(tx_flag && tx_done)begin if(cnt_0 == TRANS_LENTH - 1) cnt_0 <= 'd0; else cnt_0 <= cnt_0 + 1'b1; end end assign tx_data = fifo_q; assign tx_dv = rdreq; endmodule

4.顶层模块

/******************************************************************* Engineer: Dawn. Create Date: 2026-10-09 11:04:46 Description: Revision: Additional Comments: ********************************************************************/ //---------<模块及端口声名>------------------------------------------- module uart_loop_top( input clk , input rst_n , input uart_rxd, output uart_txd ); wire [7:0] rx_data; wire rx_dv ; wire [7:0] tx_data ; wire tx_start; wire tx_busy ; wire tx_done ; uart_tx #( .SYS_CLOCK (50_000_000),//系统时钟 .BAUD_RATE (115200 ),//波特率 .DATA_WIDTH (8 ),//数据位位宽 .STOP_WIDTH ("TYPE1.0" ),//停止位位宽 可选:TYPE1.0 TYPE1.5 TYPE2.0 .CHECK_SELECT("YES" ),//校验位选择 "YES" "NO" .CHECK_TYPE ("ODD" ) //奇偶校验类型 奇校验:"ODD" 偶校验"EVEN" )u_uart_tx( //----用户接口------------------------------------------------------ /*input */.clk (clk), /*input */.rst_n (rst_n), /*input [DATA_WIDTH-1:0] */.tx_data (tx_data ),//待发送数据 /*input */.tx_start (tx_start),//发送启动信号 /*output reg */.tx_busy (tx_busy ),//模块忙标志 /*output reg */.tx_done (tx_done ),//发送完成脉冲 //----UART TX PIN--------------------------------------------------- /*output reg */.uart_txd (uart_txd)//UART发送数据线 ); loop_ctrl u_loop_ctrl( .clk (clk ), .rst_n (rst_n ), .rx_data (rx_data ), .rx_dv (rx_dv ), .tx_data (tx_data ), .tx_dv (tx_start ), .tx_busy (tx_busy ), .tx_done (tx_done ) ); uart_rx #( .SYS_CLOCK (50_000_000),//系统时钟 .BAUD_RATE (115200 ),//波特率 .DATA_WIDTH (8 ),//数据位位宽 .STOP_WIDTH ("TYPE1.0" ),//停止位位宽 可选:TYPE1.0 TYPE1.5 TYPE2.0 .CHECK_SELECT("YES" ),//校验位选择 "YES" "NO" .CHECK_TYPE ("ODD" ) //奇偶校验类型 奇校验:"ODD" 偶校验"EVEN" )u_uart_rx( //----用户接口------------------------------------------------------ /*input */.clk (clk), /*input */.rst_n (rst_n), /*output reg [DATA_WIDTH-1:0] */.rx_data (rx_data), /*output */.rx_datavalid(rx_dv), /*output */.rx_check_err(),//校验位错误信号(脉冲信号) //----UART RX PIN--------------------------------------------------- /*input */.uart_rxd (uart_rxd) ); endmodule

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询