)
一、项目简介本工程基于 50MHz 系统时钟采用分层模块化实现一套通用 UART IP兼容 8N1 标准串口协议支持自定义传输位宽、5 档波特率、大小端字节序切换、接收超时保护附带环回顶层测试工程可直接用串口助手完成双向通信验证。核心特性四层分层架构顶层业务层 → 多字节组包 / 拆包层 → 单字节底层收发层低耦合、高度复用传输位宽参数化DATA_WIDTH支持 8~256bit必须为 8 整数倍适配 ADC、DDS、图像等各类外设5 档标准波特率9600/19200/38400/57600/115200大小端可配置MSB_FIRST控制高低字节收发顺序接收抗干扰优化两级同步消除亚稳态、16 倍过采样 6 点多数判决接收超时机制未凑齐完整数据时自动输出避免半包卡死状态指示灯超时、发送完成、总线忙可视化调试自带环回顶层无需上位机软件串口助手一键验证通信。整体分层架构plaintextuart_loopback顶层环回业务层 ↓↑ uart_data_rx多字节接收组包 uart_data_tx多字节发送拆包 ↓↑ ↓↑ uart_byte_rx单字节底层接收 uart_byte_tx单字节底层发送二、全套完整源码按层级顺序1. 顶层测试模块uart_loopback.vverilogmodule uart_loopback( Clk, Rst_n, uart_rx, led, uart_tx ); parameter DATA_WIDTH 32; parameter MSB_FIRST 0; input Clk; input Rst_n; input uart_rx; output [2:0]led; output uart_tx; wire [DATA_WIDTH-1:0]rx_data; wire Rx_Done; wire [7:0]data_byte; uart_data_rx #( .DATA_WIDTH(DATA_WIDTH), .MSB_FIRST(MSB_FIRST) ) uart_data_rx( .Clk(Clk), .Rst_n(Rst_n), .uart_rx(uart_rx), .data(rx_data), .Rx_Done(Rx_Done), .timeout_flag(led[0]), .Baud_Set(3d4) ); uart_data_tx #( .DATA_WIDTH(DATA_WIDTH), .MSB_FIRST(MSB_FIRST) )uart_data_tx( .Clk(Clk), .Rst_n(Rst_n), .data(rx_data), .send_en(Rx_Done), .Baud_Set(3d4), .uart_tx(uart_tx), .Tx_Done(led[1]), .uart_state(led[2]) ); endmodule顶层逻辑说明默认参数DATA_WIDTH32一次传输 4 字节上位机发 4 字节立刻回显单字节发送会等待超时修改为8即可单字节实时响应Baud_Set3d4固定 115200 波特率匹配串口助手直通环回逻辑接收完成Rx_Done直接触发发送收到的数据原样回传给上位机LED 调试指示led [0]接收超时标志led [1]单字节发送完成脉冲led [2]串口发送总线忙标志2. 多字节接收组包模块uart_data_rx.vverilogmodule uart_data_rx( Clk, Rst_n, uart_rx, data, Rx_Done, timeout_flag, Baud_Set ); parameter DATA_WIDTH 16; parameter MSB_FIRST 1; input Clk; input Rst_n; input uart_rx; output reg [DATA_WIDTH - 1 : 0]data; input [2:0]Baud_Set; output reg Rx_Done; output reg timeout_flag; reg [DATA_WIDTH - 1 : 0]data_r; wire [7:0] data_byte; wire byte_rx_done; wire [19:0] TIMEOUT; uart_byte_rx uart_byte_rx( .Clk(Clk), .Rst_n(Rst_n), .baud_set(Baud_Set), .uart_rx(uart_rx), .data_byte(data_byte), .Rx_Done(byte_rx_done) ); // 根据波特率自动配置超时计数阈值 assign TIMEOUT (Baud_Set 3d0) ? 20d182291: (Baud_Set 3d1) ? 20d91145: (Baud_Set 3d2) ? 20d45572: (Baud_Set 3d3) ? 20d30381: 20d15190; reg [8:0]cnt; reg [1:0]state; localparam S0 0; // 空闲等待首个字节 localparam S1 1; // 接收中检测超时 localparam S2 2; // 判断是否凑齐全部字节 reg [31:0]timeout_cnt; always(posedge Clk or negedge Rst_n) if(!Rst_n) timeout_flag 1d0; else if(timeout_cnt TIMEOUT) timeout_flag 1d1; else if(state S0) timeout_flag 1d0; reg to_state; always(posedge Clk or negedge Rst_n) if(!Rst_n) to_state 0; else if(!uart_rx) to_state 1; else if(byte_rx_done) to_state 0; always(posedge Clk or negedge Rst_n) if(!Rst_n) timeout_cnt 32d0; else if(to_state)begin if(byte_rx_done) timeout_cnt 32d0; else if(timeout_cnt TIMEOUT) timeout_cnt TIMEOUT; else timeout_cnt timeout_cnt 1d1; end always(posedge Clk or negedge Rst_n) if(!Rst_n)begin data_r 0; state S0; cnt 0; data 0; end else begin case(state) S0: begin Rx_Done 0; data_r 0; if(DATA_WIDTH 8)begin data data_byte; Rx_Done byte_rx_done; end else if(byte_rx_done)begin state S1; cnt cnt 9d8; if(MSB_FIRST 1) data_r {data_r[DATA_WIDTH - 1 - 8 : 0], data_byte}; else data_r {data_byte, data_r[DATA_WIDTH - 1 : 8]}; end end S1: if(timeout_flag)begin state S0; Rx_Done 1; end else if(byte_rx_done)begin state S2; cnt cnt 9d8; if(MSB_FIRST 1) data_r {data_r[DATA_WIDTH - 1 - 8 : 0], data_byte}; else data_r {data_byte, data_r[DATA_WIDTH - 1 : 8]}; end S2: if(cnt DATA_WIDTH)begin state S0; cnt 0; data data_r; Rx_Done 1; end else begin state S1; Rx_Done 0; end default:state S0; endcase end endmodule模块核心功能调用底层uart_byte_rx接收单字节按MSB_FIRST拼接高低字节三段式状态机管理多字节缓存计数自适应波特率超时计数器长时间无新字节强制输出当前缓存数据防止半包卡死输出Rx_Done单周期脉冲代表一组完整宽数据接收完成。3. 底层单字节接收模块uart_byte_rx.vverilogmodule uart_byte_rx( Clk, Rst_n, baud_set, uart_rx, data_byte, Rx_Done ); input Clk; // 系统全局时钟 50MHz input Rst_n; // 低电平复位 input [2:0]baud_set; // 波特率档位 input uart_rx; // 串口接收输入 output [7:0]data_byte; // 单字节接收数据 output Rx_Done; // 单字节接收完成标志 reg [7:0]data_byte; reg Rx_Done; reg uart_rx_sync1; // 一级同步寄存器 reg uart_rx_sync2; // 二级同步寄存器 reg uart_rx_reg1; // 滤波寄存器1 reg uart_rx_reg2; // 滤波寄存器2 reg [15:0]bps_DR; // 波特率分频系数 reg [15:0]div_cnt; // 分频计数器 reg bps_clk; // 16倍波特率采样时钟 reg [7:0] bps_cnt; // 帧bit计数 reg uart_state; // 接收工作状态 wire uart_rx_nedge; // 下降沿检测起始位 reg [2:0]START_BIT; reg [2:0]STOP_BIT; reg [2:0]data_byte_pre [7:0]; // 两级寄存器同步异步串口输入消除亚稳态 always(posedge Clk or negedge Rst_n) if(!Rst_n)begin uart_rx_sync1 1b0; uart_rx_sync2 1b0; end else begin uart_rx_sync1 uart_rx; uart_rx_sync2 uart_rx_sync1; end // 二次寄存器滤波消除线路毛刺 always(posedge Clk or negedge Rst_n) if(!Rst_n)begin uart_rx_reg1 1b0; uart_rx_reg2 1b0; end else begin uart_rx_reg1 uart_rx_sync2; uart_rx_reg2 uart_rx_reg1; end // 检测uart_rx下降沿判定起始位到来 assign uart_rx_nedge !uart_rx_reg1 uart_rx_reg2; // 根据档位配置50MHz下的分频值 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR 16d324; else begin case(baud_set) 0:bps_DR 16d324; // 9600 1:bps_DR 16d162; // 19200 2:bps_DR 16d80; // 38400 3:bps_DR 16d53; // 57600 4:bps_DR 16d26; // 115200 default:bps_DR 16d324; endcase end // 波特率分频计数器生成16倍采样时钟 always(posedge Clk or negedge Rst_n) if(!Rst_n) div_cnt 16d0; else if(uart_state)begin if(div_cnt bps_DR) div_cnt 16d0; else div_cnt div_cnt 1b1; end else div_cnt 16d0; // 产生bps_clk采样脉冲 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk 1b0; else if(div_cnt 16d1) bps_clk 1b1; else bps_clk 1b0; // 帧bit计数器一帧共10bit1起始8数据1停止 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt 8d0; else if(bps_cnt 8d159 | (bps_cnt 8d12 (START_BIT 2))) bps_cnt 8d0; else if(bps_clk) bps_cnt bps_cnt 1b1; else bps_cnt bps_cnt; // 单字节接收完成脉冲输出 always(posedge Clk or negedge Rst_n) if(!Rst_n) Rx_Done 1b0; else if(bps_cnt 8d159) Rx_Done 1b1; else Rx_Done 1b0; // 每bit采样6次累加做多数判决抗线路噪声 always(posedge Clk or negedge Rst_n) if(!Rst_n)begin START_BIT 3d0; data_byte_pre[0] 3d0; data_byte_pre[1] 3d0; data_byte_pre[2] 3d0; data_byte_pre[3] 3d0; data_byte_pre[4] 3d0; data_byte_pre[5] 3d0; data_byte_pre[6] 3d0; data_byte_pre[7] 3d0; STOP_BIT 3d0; end else if(bps_clk)begin case(bps_cnt) 0:begin START_BIT 3d0; data_byte_pre[0] 3d0; data_byte_pre[1] 3d0; data_byte_pre[2] 3d0; data_byte_pre[3] 3d0; data_byte_pre[4] 3d0; data_byte_pre[5] 3d0; data_byte_pre[6] 3d0; data_byte_pre[7] 3d0; STOP_BIT 3d0; end 6 ,7 ,8 ,9 ,10,11:START_BIT START_BIT uart_rx_sync2; 22,23,24,25,26,27:data_byte_pre[0] data_byte_pre[0] uart_rx_sync2; 38,39,40,41,42,43:data_byte_pre[1] data_byte_pre[1] uart_rx_sync2; 54,55,56,57,58,59:data_byte_pre[2] data_byte_pre[2] uart_rx_sync2; 70,71,72,73,74,75:data_byte_pre[3] data_byte_pre[3] uart_rx_sync2; 86,87,88,89,90,91:data_byte_pre[4] data_byte_pre[4] uart_rx_sync2; 102,103,104,105,106,107:data_byte_pre[5] data_byte_pre[5] uart_rx_sync2; 118,119,120,121,122,123:data_byte_pre[6] data_byte_pre[6] uart_rx_sync2; 134,135,136,137,138,139:data_byte_pre[7] data_byte_pre[7] uart_rx_sync2; 150,151,152,153,154,155:STOP_BIT STOP_BIT uart_rx_sync2; default: begin START_BIT START_BIT; data_byte_pre[0] data_byte_pre[0]; data_byte_pre[1] data_byte_pre[1]; data_byte_pre[2] data_byte_pre[2]; data_byte_pre[3] data_byte_pre[3]; data_byte_pre[4] data_byte_pre[4]; data_byte_pre[5] data_byte_pre[5]; data_byte_pre[6] data_byte_pre[6]; data_byte_pre[7] data_byte_pre[7]; STOP_BIT STOP_BIT; end endcase end // 多数判决累加值大于3判定为高电平输出最终字节 always(posedge Clk or negedge Rst_n) if(!Rst_n) data_byte 8d0; else if(bps_cnt 8d159)begin data_byte[0] data_byte_pre[0][2]; data_byte[1] data_byte_pre[1][2]; data_byte[2] data_byte_pre[2][2]; data_byte[3] data_byte_pre[3][2]; data_byte[4] data_byte_pre[4][2]; data_byte[5] data_byte_pre[5][2]; data_byte[6] data_byte_pre[6][2]; data_byte[7] data_byte_pre[7][2]; end // 接收状态机检测到起始位下降沿启动接收帧结束退出 always(posedge Clk or negedge Rst_n) if(!Rst_n) uart_state 1b0; else if(uart_rx_nedge) uart_state 1b1; else if(Rx_Done || (bps_cnt 8d12 (START_BIT 2)) || (bps_cnt 8d155 (STOP_BIT 3))) uart_state 1b0; else uart_state uart_state; endmodule底层接收核心亮点两级同步寄存器异步串口信号跨 50MHz 时钟域彻底消除亚稳态16 倍过采样 6 点多数判决每 bit 采样 6 次累加抗线缆干扰、电平抖动工业场景稳定不丢码标准 8N1 帧解析1 起始低电平 8 位数据 1 停止高电平下降沿触发接收空闲状态低功耗不计数。4. 多字节发送拆包模块uart_data_tx.vverilogmodule uart_data_tx( Clk, Rst_n, data, send_en, Baud_Set, uart_tx, Tx_Done, uart_state ); parameter DATA_WIDTH 8; parameter MSB_FIRST 1; input Clk; input Rst_n; input [DATA_WIDTH - 1 : 0]data; input send_en; input [2:0]Baud_Set; output uart_tx; output reg Tx_Done; output uart_state; reg [DATA_WIDTH - 1 : 0]data_r; reg [7:0] data_byte; reg byte_send_en; wire byte_tx_done; uart_byte_tx uart_byte_tx( .Clk(Clk), .Rst_n(Rst_n), .data_byte(data_byte), .send_en(byte_send_en), .Baud_Set(Baud_Set), .uart_tx(uart_tx), .Tx_Done(byte_tx_done), .uart_state(uart_state) ); reg [8:0]cnt; reg [1:0]state; localparam S0 0; // 空闲等待发送使能 localparam S1 1; // 拆分单字节启动发送 localparam S2 2; // 等待单字节发送完成 localparam S3 3; // 判断全部字节是否发送完毕 always(posedge Clk or negedge Rst_n) if(!Rst_n)begin data_byte 0; byte_send_en 0; state S0; cnt 0; end else begin case(state) S0: begin data_byte 0; cnt 0; Tx_Done 0; if(send_en)begin state S1; data_r data; end else begin state S0; data_r data_r; end end S1: begin byte_send_en 1; if(MSB_FIRST 1)begin data_byte data_r[DATA_WIDTH-1:DATA_WIDTH - 8]; data_r data_r 8; end else begin data_byte data_r[7:0]; data_r data_r 8; end state S2; end S2: begin byte_send_en 0; if(byte_tx_done)begin state S3; cnt cnt 9d8; end else state S2; end S3: if(cnt DATA_WIDTH)begin state S0; cnt 0; Tx_Done 1; end else begin state S1; Tx_Done 0; end default:state S0; endcase end endmodule模块核心功能四态状态机拆分宽数据按MSB_FIRST截取高 / 低字节逐字节调用底层uart_byte_tx串行发送全部字节发送完成后输出单周期Tx_Done脉冲复用底层波特率配置与接收侧参数完全统一。5. 底层单字节发送模块uart_byte_tx.vverilogmodule uart_byte_tx( Clk, Rst_n, data_byte, send_en, Baud_Set, uart_tx, Tx_Done, uart_state ); input Clk ; // 50MHz系统时钟 input Rst_n; // 低电平复位 input [7:0]data_byte; // 待发送单字节 input send_en; // 发送启动使能 input [2:0]Baud_Set; // 波特率档位 output reg uart_tx; // 串口串行输出 output reg Tx_Done; // 单字节发送完成脉冲 output reg uart_state; // 发送忙状态标志 localparam START_BIT 1b0; localparam STOP_BIT 1b1; reg bps_clk; // 16倍波特率采样时钟 reg [15:0]div_cnt; // 分频计数器 reg [15:0]bps_DR; // 分频系数 reg [3:0]bps_cnt; // 帧bit计数 reg [7:0]data_byte_reg;// 锁存待发送字节 // 发送忙状态锁存 always(posedge Clk or negedge Rst_n) if(!Rst_n) uart_state 1b0; else if(send_en) uart_state 1b1; else if(bps_cnt 4d11) uart_state 1b0; else uart_state uart_state; // 锁存待发送字节防止中途数据变化 always(posedge Clk or negedge Rst_n) if(!Rst_n) data_byte_reg 8d0; else if(send_en) data_byte_reg data_byte; else data_byte_reg data_byte_reg; // 50MHz下各波特率分频值 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR 16d5207; else begin case(Baud_Set) 0:bps_DR 16d5207; //9600 1:bps_DR 16d2603; //19200 2:bps_DR 16d1301; //38400 3:bps_DR 16d867; //57600 4:bps_DR 16d433; //115200 default:bps_DR 16d5207; endcase end // 波特率分频计数 always(posedge Clk or negedge Rst_n) if(!Rst_n) div_cnt 16d0; else if(uart_state)begin if(div_cnt bps_DR) div_cnt 16d0; else div_cnt div_cnt 1b1; end else div_cnt 16d0; // 生成bps_clk脉冲 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk 1b0; else if(div_cnt 16d1) bps_clk 1b1; else bps_clk 1b0; // 帧bit计数器一帧11个计数周期空闲起始8数据停止 always(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt 4d0; else if(bps_cnt 4d11) bps_cnt 4d0; else if(bps_clk) bps_cnt bps_cnt 1b1; else bps_cnt bps_cnt; // 发送完成单周期脉冲 always(posedge Clk or negedge Rst_n) if(!Rst_n) Tx_Done 1b0; else if(bps_cnt 4d11) Tx_Done 1b1; else Tx_Done 1b0; // 逐bit输出标准UART帧 always(posedge Clk or negedge Rst_n) if(!Rst_n) uart_tx 1b1; else begin case(bps_cnt) 0:uart_tx 1b1; // 空闲电平高 1:uart_tx START_BIT; // 起始位低 2:uart_tx data_byte_reg[0]; 3:uart_tx data_byte_reg[1]; 4:uart_tx data_byte_reg[2]; 5:uart_tx data_byte_reg[3]; 6:uart_tx data_byte_reg[4]; 7:uart_tx data_byte_reg[5]; 8:uart_tx data_byte_reg[6]; 9:uart_tx data_byte_reg[7]; 10:uart_tx STOP_BIT; // 停止位高 default:uart_tx 1b1; endcase end endmodule底层发送核心逻辑严格遵循 UART 8N1 时序空闲高电平 → 起始低电平 → bit0~bit7 → 停止高电平发送期间锁存输入字节避免发送中途数据被篡改uart_state输出总线忙标志可用于外部流控逻辑单字节发送结束输出Tx_Done脉冲通知上层拆包模块发送下一字节。三、上位机联调实测分析配套截图说明1. 串口助手标准配置端口COM4USB-SERIAL CH340波特率115200对应代码Baud_Set4数据位 8停止位 1无校验勾选「十六进制发送」「十六进制显示」2. 实测现象解读你的截图发送01 02 03 044 字节接收区完整回显相同数据发送 / 接收字节计数均为 16无丢包、无乱码默认DATA_WIDTH32必须凑齐 4 字节才会触发Rx_Done发送仅发送 1 字节时等待超时后才回传存在短暂延迟修改顶层参数.DATA_WIDTH(8)重新综合可实现单字节实时回显无超时等待MSB_FIRST0小端模式发送01 02 03 04回显04 03 02 01可用于调试高低字节顺序。四、工程拓展方案博客增值内容拓展 1上位机指令控制外设LED/DDS删除顶层直通赋值逻辑在rx_data接收完成后增加指令解析状态机自定义简易协议1 字节指令码 N 字节参数示例0x01 0x000000FF→ 控制 LED 全亮0x02 32h00008000→ 设置 DDS 频率字指令执行完成后向上位机返回应答帧。拓展 2FPGA 主动上传数据到 PC将 ADC 采样值、等精度频率计测量值、TFT 图像像素送入uart_data_tx定时主动上传配合 PC 上位机绘制波形、存储采样数据。拓展 3适配 12 位 ADC 等非 8bit 外设高位补零对齐到 8 的整数倍位宽示例verilogwire [11:0] adc_sample; wire [15:0] tx_data {4d0, adc_sample}; // 12bit补4位0拼成16bit直接传入uart_data_tx底层串口逻辑无需修改。五、设计核心优势总结分层低耦合底层单字节收发完全独立业务层仅修改顶层逻辑即可复用适配绝大多数 FPGA 串口场景强鲁棒接收两级同步消除亚稳态、16 倍过采样多数判决长距离串口传输误码率极低全参数化配置波特率、传输位宽、字节序均可通过参数一键切换无需修改内部时序完备调试接口超时、发送完成、总线忙全部引出至 LED快速定位通信故障快速验证闭环自带环回顶层仅用串口助手即可完成硬件与 IP 逻辑全验证适合 FPGA 串口入门学习。