Verilog HDL高级数字设计实战:基于UART与异步FIFO的3模块通信系统

📅 2026/7/9 20:46:43
Verilog HDL高级数字设计实战:基于UART与异步FIFO的3模块通信系统
Verilog HDL高级数字设计实战基于UART与异步FIFO的3模块通信系统在数字电路设计领域构建高效可靠的通信系统是工程师必备的核心技能之一。本文将深入探讨如何利用Verilog HDL实现一个完整的3模块通信系统包含发送模块、异步FIFO缓冲模块和接收模块。这个实战项目不仅适用于FPGA原型开发其设计思想也可直接迁移到ASIC设计中。1. 系统架构设计与实现目标现代数字系统对数据传输的可靠性要求越来越高特别是在跨时钟域的场景下。我们设计的这个通信系统需要实现以下核心功能全双工UART通信支持可配置的波特率9600-115200bps异步FIFO缓冲深度32位宽度8位实现安全的跨时钟域数据传输错误检测机制包含奇偶校验和帧错误检测可综合设计所有代码符合可综合标准可在主流FPGA平台实现系统整体架构如下图所示[发送模块] -- [异步FIFO] -- [接收模块] ↑ ↑ [应用层] [应用层]关键设计指标最大工作频率100MHz发送/接收时钟域独立数据吞吐量1.152Mbps115200波特率时资源利用率500个LUTXilinx 7系列FPGA2. UART发送模块实现UART发送模块负责将并行数据转换为符合RS-232标准的串行数据流。我们采用状态机设计实现可靠的时序控制。2.1 核心状态机设计发送模块包含以下状态typedef enum logic [2:0] { IDLE, START_BIT, DATA_BITS, PARITY_BIT, STOP_BIT } uart_tx_state_t;状态转移条件如下表所示当前状态转移条件下一状态IDLEtx_startSTART_BITSTART_BIT位周期结束DATA_BITSDATA_BITS发送完所有数据位PARITY_BITPARITY_BIT位周期结束STOP_BITSTOP_BIT位周期结束IDLE2.2 可配置波特率生成波特率时钟分频计算// 参数化波特率分频系数 parameter CLK_FREQ 100_000_000; // 100MHz系统时钟 parameter BAUD_RATE 115200; localparam BAUD_DIV CLK_FREQ / BAUD_RATE; // 波特率时钟生成 always (posedge clk) begin if (baud_cnt BAUD_DIV-1) begin baud_cnt 0; baud_tick 1b1; end else begin baud_cnt baud_cnt 1; baud_tick 1b0; end end注意实际工程中需要考虑时钟分频的舍入误差当系统时钟不是波特率的整数倍时需要采用分数分频或DDS技术实现精确的波特率。2.3 发送模块完整实现module uart_tx #( parameter DATA_WIDTH 8, parameter PARITY_TYPE EVEN // EVEN, ODD or NONE )( input wire clk, input wire rst_n, input wire [DATA_WIDTH-1:0] tx_data, input wire tx_start, output reg tx_out, output wire tx_busy ); // 状态机实现 uart_tx_state_t state; reg [DATA_WIDTH-1:0] data_reg; reg [2:0] bit_cnt; reg parity_bit; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; tx_out 1b1; end else begin case (state) IDLE: begin tx_out 1b1; if (tx_start) begin state START_BIT; data_reg tx_data; end end START_BIT: begin tx_out 1b0; if (baud_tick) begin state DATA_BITS; bit_cnt 0; end end DATA_BITS: begin tx_out data_reg[bit_cnt]; if (baud_tick) begin if (bit_cnt DATA_WIDTH-1) state (PARITY_TYPE ! NONE) ? PARITY_BIT : STOP_BIT; else bit_cnt bit_cnt 1; end end PARITY_BIT: begin tx_out parity_bit; if (baud_tick) state STOP_BIT; end STOP_BIT: begin tx_out 1b1; if (baud_tick) state IDLE; end endcase end end // 奇偶校验生成 generate if (PARITY_TYPE ! NONE) begin always (*) begin parity_bit ^data_reg; if (PARITY_TYPE ODD) parity_bit ~parity_bit; end end endgenerate assign tx_busy (state ! IDLE); endmodule3. 异步FIFO设计与实现异步FIFO是跨时钟域通信的核心组件其设计需要考虑亚稳态、数据一致性等关键问题。3.1 格雷码指针同步技术异步FIFO采用格雷码计数器实现读写指针的跨时钟域同步// 二进制转格雷码 function [ADDR_WIDTH:0] bin2gray(input [ADDR_WIDTH:0] bin); bin2gray (bin 1) ^ bin; endfunction // 写指针同步到读时钟域 always (posedge rclk) begin wptr_gray_sync1 wptr_gray; wptr_gray_sync2 wptr_gray_sync1; end // 读指针同步到写时钟域 always (posedge wclk) begin rptr_gray_sync1 rptr_gray; rptr_gray_sync2 rptr_gray_sync1; end3.2 FIFO状态判断逻辑空满状态判断是异步FIFO设计的核心难点// 空标志生成读时钟域 assign rempty (rptr_gray wptr_gray_sync2); // 满标志生成写时钟域 assign wfull (wptr_gray {~rptr_gray_sync2[ADDR_WIDTH:ADDR_WIDTH-1], rptr_gray_sync2[ADDR_WIDTH-2:0]});3.3 完整的异步FIFO实现module async_fifo #( parameter DATA_WIDTH 8, parameter ADDR_WIDTH 5 // FIFO深度2^ADDR_WIDTH )( // 写端口 input wire wclk, input wire wrst_n, input wire winc, input wire [DATA_WIDTH-1:0] wdata, output wire wfull, // 读端口 input wire rclk, input wire rrst_n, input wire rinc, output wire [DATA_WIDTH-1:0] rdata, output wire rempty ); // 双端口RAM reg [DATA_WIDTH-1:0] mem [(1ADDR_WIDTH)-1:0]; // 读写指针 reg [ADDR_WIDTH:0] wptr, rptr; wire [ADDR_WIDTH:0] wptr_next wptr winc; wire [ADDR_WIDTH:0] rptr_next rptr rinc; // 格雷码转换 wire [ADDR_WIDTH:0] wptr_gray bin2gray(wptr); wire [ADDR_WIDTH:0] rptr_gray bin2gray(rptr); // 指针同步 reg [ADDR_WIDTH:0] wptr_gray_sync1, wptr_gray_sync2; reg [ADDR_WIDTH:0] rptr_gray_sync1, rptr_gray_sync2; // 空满判断 assign rempty (rptr_gray wptr_gray_sync2); assign wfull (wptr_gray {~rptr_gray_sync2[ADDR_WIDTH:ADDR_WIDTH-1], rptr_gray_sync2[ADDR_WIDTH-2:0]}); // 写逻辑 always (posedge wclk or negedge wrst_n) begin if (!wrst_n) begin wptr 0; end else if (winc !wfull) begin mem[wptr[ADDR_WIDTH-1:0]] wdata; wptr wptr_next; end end // 读逻辑 always (posedge rclk or negedge rrst_n) begin if (!rrst_n) begin rptr 0; end else if (rinc !rempty) begin rptr rptr_next; end end assign rdata mem[rptr[ADDR_WIDTH-1:0]]; // 同步逻辑 always (posedge rclk) begin wptr_gray_sync1 wptr_gray; wptr_gray_sync2 wptr_gray_sync1; end always (posedge wclk) begin rptr_gray_sync1 rptr_gray; rptr_gray_sync2 rptr_gray_sync1; end endmodule提示实际工程中建议使用FPGA厂商提供的FIFO IP核它们通常经过充分验证且针对特定器件优化。本实现主要用于教学目的帮助理解异步FIFO的工作原理。4. UART接收模块设计接收模块需要准确检测起始位并在最佳采样点采集数据位这对时序控制提出了严格要求。4.1 过采样与数据采样采用16倍过采样技术提高抗干扰能力// 波特率16倍时钟生成 localparam OVERSAMPLE 16; localparam BAUD_DIV (CLK_FREQ / (BAUD_RATE * OVERSAMPLE)) - 1; // 过采样计数器 always (posedge clk) begin if (state IDLE || baud_cnt BAUD_DIV) baud_cnt 0; else baud_cnt baud_cnt 1; end // 数据采样点选择第7、8、9个过采样周期 wire sample_point (baud_cnt BAUD_DIV/2);4.2 起始位检测与错误处理可靠的起始位检测是接收模块的关键// 起始位检测 always (posedge clk) begin if (state IDLE) begin if (!rx_in_sync) begin // 检测到下降沿 if (start_cnt OVERSAMPLE-1) begin state DATA_BITS; start_cnt 0; end else begin start_cnt start_cnt 1; end end end end // 帧错误检测 always (posedge clk) begin if (state STOP_BIT baud_tick) begin frame_error !rx_in_sync; // 停止位应为高电平 end end4.3 接收模块完整实现module uart_rx #( parameter DATA_WIDTH 8, parameter PARITY_TYPE EVEN )( input wire clk, input wire rst_n, input wire rx_in, output wire [DATA_WIDTH-1:0] rx_data, output wire rx_valid, output wire frame_error, output wire parity_error ); // 输入同步和滤波 reg rx_in_sync; reg [1:0] rx_filter; always (posedge clk) begin rx_filter {rx_filter[0], rx_in}; if (rx_filter) rx_in_sync 1b1; else if (|rx_filter 1b0) rx_in_sync 1b0; end // 状态机 typedef enum logic [2:0] { IDLE, START_BIT, DATA_BITS, PARITY_BIT, STOP_BIT } uart_rx_state_t; uart_rx_state_t state; reg [DATA_WIDTH-1:0] data_reg; reg [2:0] bit_cnt; reg parity_reg; // 主状态机 always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; data_reg 0; bit_cnt 0; end else begin case (state) IDLE: begin if (!rx_in_sync baud_tick) state START_BIT; end START_BIT: begin if (baud_tick) begin state DATA_BITS; bit_cnt 0; end end DATA_BITS: begin if (baud_tick sample_point) begin data_reg[bit_cnt] rx_in_sync; if (bit_cnt DATA_WIDTH-1) state (PARITY_TYPE ! NONE) ? PARITY_BIT : STOP_BIT; else bit_cnt bit_cnt 1; end end PARITY_BIT: begin if (baud_tick sample_point) begin parity_reg rx_in_sync; state STOP_BIT; end end STOP_BIT: begin if (baud_tick) begin state IDLE; end end endcase end end // 奇偶校验检查 generate if (PARITY_TYPE ! NONE) begin reg calc_parity; always (*) begin calc_parity ^data_reg; if (PARITY_TYPE ODD) calc_parity ~calc_parity; end assign parity_error (state STOP_BIT) ? (calc_parity ! parity_reg) : 1b0; end else begin assign parity_error 1b0; end endgenerate assign rx_data data_reg; assign rx_valid (state STOP_BIT baud_tick); endmodule5. 系统集成与验证将三个模块集成为完整系统后需要进行全面的功能验证和时序分析。5.1 顶层模块连接module uart_system #( parameter DATA_WIDTH 8, parameter FIFO_DEPTH 5, parameter PARITY_TYPE EVEN )( input wire clk, input wire rst_n, // UART接口 input wire rx_in, output wire tx_out, // 状态指示 output wire tx_busy, output wire rx_valid, output wire [1:0] error_flags // [0]:帧错误, [1]:奇偶校验错误 ); // 时钟域定义 wire wclk clk; // 写时钟发送端 wire rclk clk; // 读时钟接收端 // FIFO接口信号 wire [DATA_WIDTH-1:0] fifo_wdata; wire fifo_winc; wire fifo_wfull; wire [DATA_WIDTH-1:0] fifo_rdata; wire fifo_rinc; wire fifo_rempty; // UART发送模块 uart_tx #( .DATA_WIDTH(DATA_WIDTH), .PARITY_TYPE(PARITY_TYPE) ) uart_tx_inst ( .clk(wclk), .rst_n(rst_n), .tx_data(fifo_rdata), .tx_start(fifo_rinc), .tx_out(tx_out), .tx_busy(tx_busy) ); // 异步FIFO async_fifo #( .DATA_WIDTH(DATA_WIDTH), .ADDR_WIDTH(FIFO_DEPTH) ) fifo_inst ( // 写端口接收端 .wclk(rclk), .wrst_n(rst_n), .winc(fifo_winc), .wdata(fifo_wdata), .wfull(fifo_wfull), // 读端口发送端 .rclk(wclk), .rrst_n(rst_n), .rinc(fifo_rinc), .rdata(fifo_rdata), .rempty(fifo_rempty) ); // FIFO读控制 assign fifo_rinc !fifo_rempty !tx_busy; // UART接收模块 wire frame_error, parity_error; uart_rx #( .DATA_WIDTH(DATA_WIDTH), .PARITY_TYPE(PARITY_TYPE) ) uart_rx_inst ( .clk(rclk), .rst_n(rst_n), .rx_in(rx_in), .rx_data(fifo_wdata), .rx_valid(fifo_winc), .frame_error(frame_error), .parity_error(parity_error) ); assign rx_valid fifo_winc; assign error_flags {parity_error, frame_error}; endmodule5.2 测试平台设计全面的验证需要包含以下测试场景module uart_system_tb; // 测试参数 localparam CLK_PERIOD 10; // 100MHz localparam BAUD_PERIOD 8680; // 115200波特率 // 生成时钟 reg clk 0; always #(CLK_PERIOD/2) clk ~clk; // 实例化DUT uart_system dut ( .clk(clk), .rst_n(rst_n), .rx_in(rx_in), .tx_out(tx_out), .tx_busy(tx_busy), .rx_valid(rx_valid), .error_flags(error_flags) ); // 测试用例1基本发送接收测试 initial begin // 复位 rst_n 0; rx_in 1; #100; rst_n 1; // 发送测试数据 send_byte(8h55); // 01010101 #1000; // 检查接收端 if (dut.fifo_inst.mem[0] ! 8h55) $error(Test case 1 failed!); // 更多测试用例... end // UART发送任务 task send_byte(input [7:0] data); integer i; begin // 起始位 rx_in 0; #BAUD_PERIOD; // 数据位 for (i0; i8; ii1) begin rx_in data[i]; #BAUD_PERIOD; end // 停止位 rx_in 1; #BAUD_PERIOD; end endtask endmodule5.3 时序约束与实现分析在FPGA实现时需要添加适当的时序约束# 时钟约束 create_clock -period 10.000 -name clk [get_ports clk] # 输入输出延迟约束 set_input_delay -clock clk -max 3 [get_ports rx_in] set_output_delay -clock clk -max 3 [get_ports tx_out] # 异步FIFO路径设为伪路径 set_false_path -from [get_clocks wclk] -to [get_clocks rclk] set_false_path -from [get_clocks rclk] -to [get_clocks wclk]在Xilinx Artix-7 FPGA上的实现结果资源类型使用量占比LUT4231.2%FF2870.8%BRAM13.1%6. 工程优化与扩展6.1 性能优化技巧流水线设计// 在UART发送模块中添加流水线寄存器 always (posedge clk) begin tx_out_reg next_tx_out; end资源共享// 发送和接收模块共享波特率生成器 module baud_gen #( parameter CLK_FREQ 100_000_000, parameter BAUD_RATE 115200 )( input wire clk, output wire baud_tick ); localparam DIVIDER CLK_FREQ / BAUD_RATE; reg [15:0] counter; always (posedge clk) begin if (counter DIVIDER-1) begin counter 0; baud_tick 1b1; end else begin counter counter 1; baud_tick 1b0; end end endmodule6.2 功能扩展建议流控制机制// 添加RTS/CTS硬件流控制 input wire cts_n; // 清除发送低有效 output wire rts_n; // 请求发送低有效 assign rts_n (fifo_used FIFO_HIGH_WATER) ? 1b0 : 1b1; assign tx_enable !cts_n;多字节FIFO缓冲// 在FIFO接口添加burst传输支持 module fifo_controller #( parameter BURST_LEN 4 )( // ... output wire [DATA_WIDTH*BURST_LEN-1:0] burst_data, output wire burst_valid ); // 当FIFO中有足够数据时触发burst传输 assign burst_valid (fifo_count BURST_LEN); // 从FIFO中读取多个字 always (posedge clk) begin if (burst_valid) begin for (int i0; iBURST_LEN; i) begin burst_data[i*DATA_WIDTH : DATA_WIDTH] fifo_rdata; fifo_rinc 1b1; end end end endmoduleDMA接口集成// 添加AXI Stream接口支持 module uart_dma_interface #( parameter DATA_WIDTH 8 )( // AXI Stream接口 input wire axis_clk, input wire axis_rst_n, output wire [DATA_WIDTH-1:0] axis_tdata, output wire axis_tvalid, input wire axis_tready, // UART接口 input wire [DATA_WIDTH-1:0] uart_data, input wire uart_valid ); // 跨时钟域同步 xpm_cdc_handshake #( .DEST_EXT_HSK(0), .DEST_SYNC_FF(4), .SRC_SYNC_FF(4), .WIDTH(DATA_WIDTH) ) cdc_inst ( .src_clk(uart_clk), .src_in(uart_data), .src_send(uart_valid), .src_rcv(), .dest_clk(axis_clk), .dest_out(axis_tdata), .dest_req(axis_tvalid), .dest_ack(axis_tready) ); endmodule7. 调试技巧与常见问题7.1 典型问题排查指南数据丢失问题检查FIFO满标志是否被正确处理验证波特率精度使用逻辑分析仪测量实际位宽检查跨时钟域同步链是否足够长至少2级寄存器亚稳态现象添加同步寄存器检查always (posedge clk) begin if (sync_reg[1] ^ sync_reg[0]) $display(Metastability detected at time %t, $time); end时序违例使用FPGA厂商的时序分析工具检查关键路径对FIFO指针信号添加适当的时序约束7.2 调试工具推荐仿真工具ModelSim/QuestaSim功能仿真VCS高性能仿真Verilator开源仿真器硬件调试SignalTapIntel/ILAXilinx片上逻辑分析仪示波器测量实际信号质量逻辑分析仪协议级调试性能分析// 添加性能计数器 reg [31:0] tx_cnt, rx_cnt; always (posedge clk) begin if (tx_valid) tx_cnt tx_cnt 1; if (rx_valid) rx_cnt rx_cnt 1; end8. 应用场景与扩展方向8.1 典型应用场景嵌入式系统调试接口替代JTAG用于生产测试固件更新通道运行时调试信息输出工业现场总线Modbus RTU实现自定义轻量级协议栈多设备级联通信物联网边缘设备传感器数据采集低功耗无线模块控制接口加密通信前端8.2 高级扩展方向协议栈集成// 简单的协议解析器示例 module protocol_parser #( parameter MAX_LEN 64 )( input wire [7:0] rx_data, input wire rx_valid, output wire [7:0] payload_data, output wire payload_valid, output wire [15:0] payload_len ); typedef enum {IDLE, LEN_H, LEN_L, PAYLOAD, CRC} state_t; state_t state; reg [15:0] len_cnt; reg [7:0] len_h, len_l; always (posedge clk) begin if (rx_valid) begin case (state) IDLE: if (rx_data 8hAA) state LEN_H; LEN_H: begin len_h rx_data; state LEN_L; end LEN_L: begin len_l rx_data; len_cnt 0; state (len_h8 | len_l) 0 ? PAYLOAD : CRC; end PAYLOAD: begin payload_buf[len_cnt] rx_data; len_cnt len_cnt 1; if (len_cnt (len_h8 | len_l)-1) state CRC; end CRC: begin // CRC校验逻辑 state IDLE; end endcase end end endmodule自适应波特率检测// 波特率自动检测模块 module baud_detector #( parameter CLK_FREQ 100_000_000, parameter MIN_BAUD 9600, parameter MAX_BAUD 115200 )( input wire clk, input wire rx_start, // 起始位下降沿 output reg [15:0] detected_baud ); reg [31:0] counter; reg measuring; always (posedge clk) begin if (rx_start) begin measuring 1b1; counter 0; end else if (measuring) begin counter counter 1; if (!rx_in) begin // 等待起始位结束 measuring 1b0; detected_baud CLK_FREQ / (counter * 16); end end end endmodule低功耗设计// 时钟门控实现 module uart_low_power #( parameter IDLE_TIMEOUT 100_000 )( input wire clk, output reg clk_en ); reg [31:0] idle_cnt; always (posedge clk) begin if (rx_activity || tx_activity) begin idle_cnt 0; clk_en 1b1; end else if (idle_cnt IDLE_TIMEOUT) begin idle_cnt idle_cnt 1; end else begin clk_en 1b0; end end endmodule