UART是一种简单且常见的接口被广泛应用于各种芯片中。下文将对UART进行简单介绍并基于Verilog进行时序设计和仿真供大家参考。1、简介UARTUniversal Asynchronous Receiver/Transmitter通用异步收发传输器。UART属于异步通信不需要时钟线同步通过约定波特率数据传输速率来保证收发双方的数据同步。UART的结构简单硬件实现成本低适用于短距离、中低速的数据传输。但由于是异步通信对波特率的一致性要求较高波特率误差过大会导致数据传输错误。UART的波特率是指每秒传输码元符号的个数。对于二进制而言在数值上等于每秒传输的比特数。常见的波特率包括9600、19200、38400、57600和115200当然只要通信双方的波特率相同可以自定义波特率波特率过高也会导致误码率的上升。波特率倒数的含义是指传输单个码元需要的时间波特率为115200则传输1bit需要的时间为8680.5ns。数据以帧为单位传输一帧通常包含起始位1bit、数据位常用8bit、校验位0/1bit用于校验数据准确性和停止位1/2bit。UART一般需要2根线进行通信即发送线TXD和接收线RXD全双工通信时两根线分别负责数据的发送和接收。时序图如下所示。上图是UART的常见时序图一帧数据由1bit的起始位低电平、8bit的数据位和1bit的结束位高电平组成。至于是否使用校验位以及使用几bit的数据位和停止位可以根据设计要求而定。assist_tx和assist_rx是辅助我们观察UART时序的信号实际并不存在。tx和rx信号在空闲情况下时钟保持高电平并且可以同时工作进行全双工通信。数据位的传输通常是从低位开始图中tx发送的数据为8’b10101010即8‘haarx接受的数据为8’b01101011即8‘h6b。2、发送TXUART的发送过程就是将数据转换成单比特信号并通过一根信号线以电平的形式进行输出实现并行到串行的转换。模块的端口信号如下所示。信号位宽方向描述clk1输入时钟rst_n1输入异步复位低电平有效din_byte8输入发送的UART数据大小为1字节send_en1输入发送使能信号高脉冲高电平开始启动数据din_byte的发送UART_TX1输出UART的发送线uart_tx_busy1输出状态信号高电平表示当前模块正在发送数据此时新的发送请求无效uart_tx_done1输出指示信号高脉冲表示已完成当前数据的发送可以开启下一次发送模块的设计主要通过两个计数器配合完成一个计数器根据时钟周期来产生一个码元的时间长度例如115200对应的码元时间长度为8680.5ns时钟周期为10ns则需要大概计数到868即可另一个计数器记录当前传输的是帧数据中的哪一比特。完整设计文件uart_tx.v如下module uart_tx( clk, rst_n, din_byte, //8bit send_en, UART_TX, uart_tx_busy, uart_tx_done ); input clk; input rst_n; input [7:0]din_byte; input send_en; output reg UART_TX; output reg uart_tx_busy; output reg uart_tx_done; parameter CLK_FREQUENCY 100_000_000; //时钟频率 parameter BAUD 115200; //波特率 localparam BAUD_CNT_MAX CLK_FREQUENCY / BAUD - 1; //产生单个码元需要的最大计数值 localparam START_BIT 1b0; localparam END_BIT 1b1; reg [7:0]r_din_byte; reg [14:0]baud_cnt; //位宽由200,000,000/960020833确定可自定义 reg [3:0]bit_cnt; //范围0~91bit起始位8bit数据位1bit停止位 always(posedge clk or negedge rst_n) if(!rst_n) r_din_byte 8d0; else if(send_en) r_din_byte din_byte; always(posedge clk or negedge rst_n) if(!rst_n) uart_tx_busy 1b0; else if(send_en) uart_tx_busy 1b1; else if(bit_cnt4d9 baud_cntBAUD_CNT_MAX) uart_tx_busy 1b0; always(posedge clk or negedge rst_n) if(!rst_n) uart_tx_done 1b0; else if(bit_cnt4d9 baud_cntBAUD_CNT_MAX) uart_tx_done 1b1; else uart_tx_done 1b0; always(posedge clk or negedge rst_n) if(!rst_n) baud_cnt 15d0; else if(send_en | baud_cntBAUD_CNT_MAX) baud_cnt 15d0; else if(uart_tx_busy) baud_cnt baud_cnt 15d1; always(posedge clk or negedge rst_n) if(!rst_n) bit_cnt 4d0; else if(send_en | (bit_cnt4d9 baud_cntBAUD_CNT_MAX)) bit_cnt 4d0; else if(uart_tx_busy baud_cntBAUD_CNT_MAX) bit_cnt bit_cnt 4d1; always(posedge clk or negedge rst_n) if(!rst_n) UART_TX 1b1; else if(uart_tx_busy) case(bit_cnt) 4d0: UART_TX START_BIT; 4d1: UART_TX r_din_byte[0]; 4d2: UART_TX r_din_byte[1]; 4d3: UART_TX r_din_byte[2]; 4d4: UART_TX r_din_byte[3]; 4d5: UART_TX r_din_byte[4]; 4d6: UART_TX r_din_byte[5]; 4d7: UART_TX r_din_byte[6]; 4d8: UART_TX r_din_byte[7]; 4d9: UART_TX END_BIT; default: UART_TX 1b1; endcase endmodule仿真测试文件uart_tx_tb.v如下module uart_tx_tb; parameter clk_period 10; reg clk; reg rst_n; reg [7:0]din_byte; reg send_en; wire UART_TX; wire uart_tx_busy; wire uart_tx_done; uart_tx #( .CLK_FREQUENCY(100_000_000), .BAUD(115200) )uart_tx_inst( .clk(clk), .rst_n(rst_n), .din_byte(din_byte), .send_en(send_en), .UART_TX(UART_TX), .uart_tx_busy(uart_tx_busy), .uart_tx_done(uart_tx_done) ); initial clk 1b1; always #(clk_period/2) clk ~clk; initial begin rst_n 1b0; din_byte 8d0; send_en 1b0; #10; rst_n 1b1; #100.1; din_byte 8h55;//发送数据8h55 send_en 1b1; #10; send_en 1b0; wait(uart_tx_done);//等待发送完成 #10.1; din_byte 8hb9;//发送数据8hb9 send_en 1b1; #10; send_en 1b0; wait(uart_tx_done);//等待发送完成 #10.1; din_byte 8ha6;//发送数据8ha6 send_en 1b1; #10; send_en 1b0; wait(uart_tx_done);//等待发送完成 #100; $stop; end endmodule整体仿真图如下所示部分细节放大图从上图可以看出3字节数据通过UART_TX线成功发送单个比特的宽度为8.68us对应115200的波特率。3、接收RXUART的接收是将单比特信号恢复成字节数据与发送过程相反实现串行到并行的转化。模块的端口定义如下信号位宽方向描述clk1输入时钟rst_n1输入异步复位低电平有效UART_RX1输入UART的接收线dout_byte8输出接收到的UART数据大小为1字节uart_rx_done1输出指示信号高脉冲表示已完成当前数据的接收同时可在端口dout_byte上读取接收到的数据。uart_rx_busy1输出状态信号高电平表示当前模块正在接收数据。输入信号UART_RX与接收模块不属于同一个时钟域需要进行跨时钟域的处理。对于单比特信号可通过打两拍进行同步降低亚稳态的影响。同时对同步后的信号再打一拍用作下降沿检测定位UART的起始位。随后通过两个计数器来定位采样时刻在码元的中心位置进行采样。最后恢复出字节数据。完整设计文件uart_rx.v如下module uart_rx( clk, rst_n, UART_RX, dout_byte, //8bit uart_rx_done, uart_rx_busy ); input clk; input rst_n; input UART_RX; output reg [7:0]dout_byte; output reg uart_rx_done; output reg uart_rx_busy; parameter CLK_FREQUENCY 100_000_000; parameter BAUD 115200; localparam BAUD_CNT_MAX CLK_FREQUENCY / BAUD - 1; reg [14:0]baud_cnt; reg [3:0]bit_cnt; reg [2:0]r_uart_rx; wire uart_rx_neg; wire start; reg [7:0]r_byte; always(posedge clk or negedge rst_n) //打拍 if(!rst_n) r_uart_rx 3d0; else r_uart_rx {r_uart_rx[1:0],UART_RX}; assign uart_rx_neg r_uart_rx[2] (~r_uart_rx[1]); //下降沿检测 assign start ~uart_rx_busy uart_rx_neg; //定位UART起始位 always(posedge clk or negedge rst_n) if(!rst_n) uart_rx_busy 1b0; else if(start) uart_rx_busy 1b1; else if(uart_rx_busy (bit_cnt4d9) (baud_cntBAUD_CNT_MAX/2)) uart_rx_busy 1b0; always(posedge clk or negedge rst_n) if(!rst_n) baud_cnt 15d0; else if(start | (baud_cntBAUD_CNT_MAX)) baud_cnt 15d0; else if(uart_rx_busy) baud_cnt baud_cnt 15d1; always(posedge clk or negedge rst_n) if(!rst_n) bit_cnt 4d0; else if(start) bit_cnt 4d0; else if(uart_rx_busy (baud_cntBAUD_CNT_MAX)) bit_cnt bit_cnt 4d1; always(posedge clk or negedge rst_n) if(!rst_n) r_byte 8d0; else if(uart_rx_busy (baud_cntBAUD_CNT_MAX/2)) case(bit_cnt) 4d1: r_byte[0] r_uart_rx[2]; 4d2: r_byte[1] r_uart_rx[2]; 4d3: r_byte[2] r_uart_rx[2]; 4d4: r_byte[3] r_uart_rx[2]; 4d5: r_byte[4] r_uart_rx[2]; 4d6: r_byte[5] r_uart_rx[2]; 4d7: r_byte[6] r_uart_rx[2]; 4d8: r_byte[7] r_uart_rx[2]; default: r_byte r_byte; endcase always(posedge clk or negedge rst_n) if(!rst_n)begin dout_byte 8d0; uart_rx_done 1b0; end else if(uart_rx_busy (bit_cnt4d9) (baud_cntBAUD_CNT_MAX/2))begin dout_byte r_byte; uart_rx_done 1b1; end else uart_rx_done 1b0; endmodule仿真测试文件uart_rx_tb.v如下module uart_rx_tb; parameter clk_period 10; reg clk; reg rst_n; reg UART_RX; wire [7:0]dout_byte; wire uart_rx_done; wire uart_rx_busy; uart_rx #( .CLK_FREQUENCY(100_000_000), .BAUD(115200) ) uart_rx_inst( .clk(clk), .rst_n(rst_n), .UART_RX(UART_RX), .dout_byte(dout_byte), .uart_rx_done(uart_rx_done), .uart_rx_busy(uart_rx_busy) ); initial clk 1b1; always #(clk_period/2) clk ~clk; task Gen_UART_RX; //模拟生成UART_RX input [7:0]din_byte; integer i; begin UART_RX 0; #8680; //115200 for(i0;i8;ii1)begin UART_RX din_byte[i]; #8680; //115200 end UART_RX 1; #8680; //115200 end endtask initial begin UART_RX 1; rst_n 0; #100; rst_n 1; #100; Gen_UART_RX(8ha2); //发送数据8ha2 Gen_UART_RX(8h9d); //发送数据8h9d Gen_UART_RX(8he8); //发送数据8he8 Gen_UART_RX(8hb4); //发送数据8hb4 $stop; end endmodule整体仿真图如下所示从上图可以看出信号uart_rx_done为高脉冲时接收的UART数据在端口dout_byte上与仿真文件中发送的数据一致。4、基于UART接口的应用场景设计4.1 设计与仿真为了加深对UART接口的理解我们这里假设一个简单的应用场景。设计要求设计一个简易计算芯片该芯片的功能包含数组求和、求最大值和最小值。芯片的通信接口为UART相关的控制指令可以自定义。输入数据位宽为8bit一次性输入数据不超过16个。分析上述要求能够对数组即连续的数据进行处理需要存储单元来存储输入数据。这里在vivado中使用IP核生成一个16*8bit的FIFO下图是IP核生成的配置图。采用分布式资源DRAM配置位宽和深度使用异步复位逻辑注意IP核只提供高电平复位勾选输出有效信号勾选数据计数信号记录FIFO中的有效数据。下面我们进行指令的设计由于功能较少指令设计比较简单且随意。这里为了方便只采用两个字节来作为指令有一定概率连续的两个指令字节和连续的两个数据字节相同从而导致指令的误判。实际设计时有相应的设计和解决方法这里为了方便不做设计。指令设计表如下表所示。指令(16进制)操作对象含义aa f1 xx ......FIFO向FIFO中连续写入数据。xx表示写入数据的数量省略号为输入的具体数据即xx字节个数据。aa 01 xxjump_reg8bit向寄存器jump_reg中写入数据xx不同的数据表示跳转到状态机的不同状态。例如设计c1表示跳转到数组求和c2表示跳转到求最大值c3表示跳转到求最小值。bb f1FIFO将FIFO中的数据全部读取出来bb 02fifo_state(8bit)读取寄存器fifo_state的数值。fifo_state为{2d0,fifo_full,fifo_empty,fifo_data_count}输出FIFO的空满状态以及数据量。bb 03sum_result(12bit)读取寄存器sum_result的数值该寄存器记录了数组求和结果。bb 04max_result(8bit)读取寄存器max_result的数值该寄存器记录了数组的最大值。bb 05min_result(8bit)读取寄存器min_result的数值该寄存器记录了数组的最小值。设计主要基于有限状态机进行设计一共划分了4个状态IDEL、SUM、MAX和MIN。在IDEL状态下可以通过UART接口进行数据输入以及运算结果的读取。在其余状态下对输入的数据进行具体的运算运算完成后将结果写入对应寄存器或者FIFO中完成运算后回到IDEL状态。完整的模块设计文件compute.vmodule compute( clk, rst_n, UART_TX,//uart UART_RX, compute_busy//state ); input clk; input rst_n; output UART_TX; input UART_RX; output reg compute_busy; //module reg and wire //例化模块的输入和输出端口定义 //uart_tx reg [7:0]uart_tx_din; reg uart_tx_senden; wire uart_tx_busy; wire uart_tx_done; //uart_rx wire [7:0]uart_rx_dout; wire uart_rx_done; wire uart_rx_busy; //fifo_16_8bit reg [7:0]fifo_din; wire fifo_wren1; //fifo_wren1 fifo_wren ~fifo_full; //fifo_wren为寄存器 wire fifo_rden1; //fifo_rden1 fifo_rden ~fifo_empty;//fifo_rden为寄存器 wire [7:0]fifo_dout; wire fifo_full; wire fifo_empty; wire fifo_valid; wire [3:0]fifo_data_count; //FSM state //状态定义 parameter IDEL 2d0; parameter SUM 2d1; parameter MAX 2d2; parameter MIN 2d3; //reg and wire reg [1:0]st; reg [1:0]next_st; reg fifo_wren; //fifo_wren1 fifo_wren ~fifo_full; reg fifo_rden; //fifo_rden1 fifo_rden ~fifo_empty; reg [7:0]r_uart_rx_dout; //对uart_rx_dout打拍方便判断连续两个字节所构成的指令 reg fifo_num_wren; //fifo_num写使能 reg [7:0]fifo_num; //写入FIFO的字节数量 reg cnt_fifo_en; //cnt_fifo计数使能 reg [7:0]cnt_fifo; //控制写入FIFO的计数器cnt_fifo fifo_num-1时完成数据写入FIFO操作 reg [7:0]jump_reg; //跳转寄存器跳转到SUM、MAX、MIN等状态 reg cmd_jumpreg_wren; //jump_reg寄存器写使能 reg [7:0]fifo_state; //记录FIFO的状态{2d0,fifo_full,fifo_empty,fifo_data_count} reg r_fifo_valid; //对fifo_valid打拍,~r_fifo_valid fifo_valid信号可以指示FIFO输出的第一个数据位置可以在此时给sum_result、max_result和min_result进行初始化 reg [11:0]sum_result; //记录数组求和的结果 reg [7:0]max_result; //记录数组最大值 reg [7:0]min_result; //记录数组最小值 reg sum_result_rden; //由于sum_result有两个字节UART需要输出两次需要使能控制信号 reg fifo_uart_rden; //由于fifo有多个字节需要使能控制来将FIFO中的所有数据通过UART输出出来 //assign assign fifo_wren1 fifo_wren ~fifo_full; assign fifo_rden1 fifo_rden ~fifo_empty; //always //r_uart_rx_dout always(posedge clk or negedge rst_n) if(!rst_n) r_uart_rx_dout 8d0; else if(uart_rx_done) r_uart_rx_dout uart_rx_dout; //fifo_state always(posedge clk or negedge rst_n) if(!rst_n) fifo_state 8d0; else fifo_state {2d0,fifo_full,fifo_empty,fifo_data_count}; //FSM-1 always(posedge clk or negedge rst_n) if(!rst_n) st IDEL; else st next_st; //FSM-2 always(*) case(st) IDEL: begin if(jump_reg 8hc1) next_st SUM; else if(jump_reg 8hc2) next_st MAX; else if(jump_reg 8hc3) next_st MIN; else next_st IDEL; end SUM:begin if(fifo_empty) next_st IDEL; else next_st SUM; end MAX:begin if(fifo_empty) next_st IDEL; else next_st MAX; end MIN:begin if(fifo_empty) next_st IDEL; else next_st MIN; end default: next_st IDEL; endcase //FSM-3 //fifo_num_wren and fifo_num always(posedge clk or negedge rst_n) if(!rst_n)begin fifo_num_wren 1b0; fifo_num 8d0; end else case(st) IDEL:begin if(uart_rx_done)begin if(r_uart_rx_dout 8haa uart_rx_dout 8hf1) fifo_num_wren 1b1; else fifo_num_wren 1b0; if(fifo_num_wren uart_rx_done) fifo_num uart_rx_dout; else fifo_num fifo_num; end end default:begin fifo_num_wren 1b0; fifo_num fifo_num; end endcase //cnt_fifo_en always(posedge clk or negedge rst_n) if(!rst_n)begin cnt_fifo_en 1b0; cnt_fifo 8d0; end else case(st) IDEL: begin if(fifo_num_wren uart_rx_done) cnt_fifo_en 1b1; else if((cnt_fifo fifo_num - 8d1) uart_rx_done) cnt_fifo_en 1b0; if(cnt_fifo_en) if(uart_rx_done) cnt_fifo cnt_fifo 8d1; else cnt_fifo cnt_fifo; else cnt_fifo 8d0; end default:begin cnt_fifo_en 1b0; cnt_fifo 8d0; end endcase //fifo_din and fifo_wren always(posedge clk or negedge rst_n) if(!rst_n)begin fifo_din 8d0; fifo_wren 1b0; fifo_rden 1b0; end else case(st) IDEL: begin if(cnt_fifo_en uart_rx_done)begin fifo_din uart_rx_dout; fifo_wren 1b1; end else begin fifo_wren 1b0; end if((uart_rx_done r_uart_rx_dout 8hbb uart_rx_dout 8hf1) | (fifo_uart_rden uart_tx_done)) fifo_rden 1b1; else fifo_rden 1b0; end SUM,MAX,MIN:begin if(fifo_empty) fifo_rden 1b0; else fifo_rden 1b1; end default:begin fifo_din fifo_din; fifo_wren 1b0; fifo_rden 1b0; end endcase //cmd_jumpreg_wren and jump_reg always(posedge clk or negedge rst_n) if(!rst_n)begin cmd_jumpreg_wren 1b0; jump_reg 8d0; end else case(st) IDEL:begin if(uart_rx_done)begin if(r_uart_rx_dout 8haa uart_rx_dout 8h01) cmd_jumpreg_wren 1b1; else cmd_jumpreg_wren 1b0; end if(cmd_jumpreg_wren uart_rx_done) jump_reg uart_rx_dout; else jump_reg 8d0; end default:begin cmd_jumpreg_wren 1b0; jump_reg jump_reg; end endcase //r_fifo_valid always(posedge clk or negedge rst_n) if(!rst_n) r_fifo_valid 1b0; else r_fifo_valid fifo_valid; //sum_result always(posedge clk or negedge rst_n) if(!rst_n) sum_result 12d0; else case(st) IDEL: sum_result sum_result; SUM: begin if(~r_fifo_valid fifo_valid) sum_result fifo_dout; else if(fifo_valid) sum_result sum_result fifo_dout; end default: sum_result sum_result; endcase //max_result always(posedge clk or negedge rst_n) if(!rst_n) max_result 8d0; else case(st) IDEL: max_result max_result; MAX:begin if(~r_fifo_valid fifo_valid) max_result fifo_dout; else if(fifo_valid (fifo_dout max_result)) max_result fifo_dout; end default: max_result max_result; endcase //min_result always(posedge clk or negedge rst_n) if(!rst_n) min_result 8d0; else case(st) IDEL: min_result min_result; MIN:begin if(~r_fifo_valid fifo_valid) min_result fifo_dout; else if(fifo_valid (fifo_dout min_result)) min_result fifo_dout; end default: min_result min_result; endcase //compute_busy always(posedge clk or negedge rst_n) if(!rst_n) compute_busy 1b0; else case(st) IDEL: compute_busy 1b0; SUM,MAX,MIN: compute_busy 1b1; default: compute_busy compute_busy; endcase //sum_result_rden always(posedge clk or negedge rst_n) if(!rst_n) sum_result_rden 1b0; else case(st) IDEL: begin if(uart_rx_done r_uart_rx_dout 8hbb uart_rx_dout 8h03) sum_result_rden 1b1; else if(sum_result_rden uart_tx_done) sum_result_rden 1b0; end default: sum_result_rden 1b0; endcase //fifo_uart_rden always(posedge clk or negedge rst_n) if(!rst_n) fifo_uart_rden 1b0; else case(st) IDEL: begin if(uart_rx_done r_uart_rx_dout 8hbb uart_rx_dout 8hf1) fifo_uart_rden 1b1; else if(fifo_uart_rden fifo_empty fifo_valid) fifo_uart_rden 1b0; end default: fifo_uart_rden 1b0; endcase //uart_tx_din and uart_tx_senden; always(posedge clk or negedge rst_n) if(!rst_n)begin uart_tx_din 8d0; uart_tx_senden 1b0; end else case(st) IDEL: begin if(uart_rx_done r_uart_rx_dout 8hbb)begin if(uart_rx_dout 8h02)begin uart_tx_din fifo_state; uart_tx_senden 1b1; end else if(uart_rx_dout 8h03)begin uart_tx_din {4d0,sum_result[11:8]}; uart_tx_senden 1b1; end else if(uart_rx_dout 8h04)begin uart_tx_din max_result; uart_tx_senden 1b1; end else if(uart_rx_dout 8h05)begin uart_tx_din min_result; uart_tx_senden 1b1; end else begin uart_tx_senden 1b0; end end else if(sum_result_rden uart_tx_done)begin uart_tx_senden 1b1; uart_tx_din sum_result[7:0]; end else if(fifo_uart_rden fifo_valid)begin uart_tx_senden 1b1; uart_tx_din fifo_dout; end else begin uart_tx_senden 1b0; end end default: begin uart_tx_din uart_tx_din; uart_tx_senden 1b0; end endcase //module instance //uart_tx uart_tx #( .CLK_FREQUENCY(100_000_000), .BAUD(115200) ) uart_tx_inst0( .clk(clk), .rst_n(rst_n), .din_byte(uart_tx_din), .send_en(uart_tx_senden), .UART_TX(UART_TX), .uart_tx_busy(uart_tx_busy), .uart_tx_done(uart_tx_done) ); //uart_rx uart_rx #( .CLK_FREQUENCY(100_000_000), .BAUD(115200) ) uart_rx_inst0( .clk(clk), .rst_n(rst_n), .UART_RX(UART_RX), .dout_byte(uart_rx_dout), .uart_rx_done(uart_rx_done), .uart_rx_busy(uart_rx_busy) ); //fifo_16_8bit fifo_16_8bit fifo_16_8bit_inst ( .clk(clk), // input wire clk .rst(~rst_n), // input wire rst .din(fifo_din), // input wire [7 : 0] din .wr_en(fifo_wren1), // input wire wr_en .rd_en(fifo_rden1), // input wire rd_en .dout(fifo_dout), // output wire [7 : 0] dout .full(fifo_full), // output wire full .empty(fifo_empty), // output wire empty .valid(fifo_valid), // output wire valid .data_count(fifo_data_count) // output wire [3 : 0] data_count ); endmodule测试激励文件compute_tb.vmodule compute_tb; parameter clk_period 10; reg clk; reg rst_n; wire UART_TX; reg UART_RX; wire compute_busy; compute compute_inst0( .clk(clk), .rst_n(rst_n), .UART_TX(UART_TX),//uart .UART_RX(UART_RX), .compute_busy(compute_busy)//state ); initial clk 1b1; always #(clk_period/2) clk ~clk; task Gen_UART_RX; //生成UART_RX信号 input [7:0]din_byte; integer i; begin UART_RX 0; #8680; for(i0;i8;ii1)begin UART_RX din_byte[i]; #8680; end UART_RX 1; #8680; end endtask integer j; initial begin rst_n 1b0; UART_RX 1b1; #10; rst_n 1b1; #100.1; //write fifo //向FIFO中写入16个字节数据 Gen_UART_RX(8haa); Gen_UART_RX(8hf1); Gen_UART_RX(8h10); //16 for(j0;j16;jj1)begin Gen_UART_RX(j5); //具体的字节数据 end //read fifo_state Gen_UART_RX(8hbb); //读取fifo_state寄存器 Gen_UART_RX(8h02); #(8681*10); //rd fifo Gen_UART_RX(8hbb); //将FIFO中的所有数据读取出来 Gen_UART_RX(8hf1); wait(compute_inst0.fifo_empty); //write fifo Gen_UART_RX(8haa); //向FIFO中写入10个字节数据 Gen_UART_RX(8hf1); Gen_UART_RX(8h0a); //10 for(j0;j10;jj1)begin //具体的字节数据 Gen_UART_RX(j1); end //sum Gen_UART_RX(8haa); //写jump_reg寄存器c1跳转到求和状态 Gen_UART_RX(8h01); Gen_UART_RX(8hc1); //read sum result Gen_UART_RX(8hbb); //读取sum_result寄存器通过UART传输结果 Gen_UART_RX(8h03); #(8681*10*2); //read fifo_state Gen_UART_RX(8hbb); //读取fifo_state寄存器 Gen_UART_RX(8h02); #(8681*10); $stop; end endmodule下图为simulation仿真图图中展示了部分信号的仿真图经过检查结果与预期相符和。大家可以跑一下仿真文件仔细观察信号的产生使能信号的范围。4.2 FPGA下载验证在basys3开发板上将设计下载并进行简易验证。由于开发板上的按键按下是高电平1而设计中是低电平复位故我们在compute.v设计文件上套一个顶层top.v将按键输入信号取反后输入到模块compute.v中。module top( clk, rst, UART_TX,//uart UART_RX ); input clk; input rst; output UART_TX; input UART_RX; compute compute_inst0( .clk(clk), .rst_n(~rst), //取反 .UART_TX(UART_TX),//uart .UART_RX(UART_RX), .compute_busy()//state ); endmodule对FPGA进行IO约束1.xdccreate_clock -name clk -period 10.000 -waveform {0.000 5.000} [get_ports clk] set_property PACKAGE_PIN W5 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property PACKAGE_PIN U18 [get_ports rst] set_property IOSTANDARD LVCMOS33 [get_ports rst] set_property PACKAGE_PIN A18 [get_ports UART_TX] set_property IOSTANDARD LVCMOS33 [get_ports UART_TX] set_property PACKAGE_PIN B18 [get_ports UART_RX] set_property IOSTANDARD LVCMOS33 [get_ports UART_RX]生成bit流文件后下载到FPGA板子上通过上位机UART软件进行功能的验证实物图如下所示。下文展示部分UART上位机结果上图分两次向FIFO中写入数据每次都写入个数据。接着读取fifo_state寄存器值表示当前FIFO非空非满且有8个数据。然后将FIFO中的数据全部读取出来。最后再次读取fifo_state寄存器值表示当前FIFO已空。同理将FIFO写满后读取fifo_state值表示当前FIFO已满。向FIFO中写入1~10共10个数据接着跳转到求和状态最终读取sum_result的数据为0x00和0x37表示10进制的数据55结果正确。向FIFO中写入3个数据接着跳转到求最大值状态最后读取max_result的数据结果正确。向FIFO中写入4个数据接着跳转到求最小值状态最后读取min_result的数据结果正确。以上就是简单的功能验证过程有关UART的简单使用就分享到这里有空继续分享其他常用的接口时序和使用方式。