news 2026/7/13 21:55:21

Verilog设计:UART接口时序设计,简易控制指令设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Verilog设计:UART接口时序设计,简易控制指令设计

UART是一种简单且常见的接口,被广泛应用于各种芯片中。下文将对UART进行简单介绍,并基于Verilog进行时序设计和仿真,供大家参考。

1、简介

UART(Universal 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‘haa;rx接受的数据为8’b01101011,即8‘h6b)。

2、发送TX

UART的发送过程就是将数据转换成单比特信号,并通过一根信号线以电平的形式进行输出,实现并行到串行的转换。模块的端口信号如下所示。

信号位宽方向描述
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 = 1'b0; localparam END_BIT = 1'b1; reg [7:0]r_din_byte; reg [14:0]baud_cnt; //位宽由200,000,000/9600=20833确定,可自定义 reg [3:0]bit_cnt; //范围0~9,1bit起始位,8bit数据位,1bit停止位 always@(posedge clk or negedge rst_n) if(!rst_n) r_din_byte <= 8'd0; else if(send_en) r_din_byte <= din_byte; always@(posedge clk or negedge rst_n) if(!rst_n) uart_tx_busy <= 1'b0; else if(send_en) uart_tx_busy <= 1'b1; else if(bit_cnt==4'd9 & baud_cnt==BAUD_CNT_MAX) uart_tx_busy <= 1'b0; always@(posedge clk or negedge rst_n) if(!rst_n) uart_tx_done <= 1'b0; else if(bit_cnt==4'd9 & baud_cnt==BAUD_CNT_MAX) uart_tx_done <= 1'b1; else uart_tx_done <= 1'b0; always@(posedge clk or negedge rst_n) if(!rst_n) baud_cnt <= 15'd0; else if(send_en | baud_cnt==BAUD_CNT_MAX) baud_cnt <= 15'd0; else if(uart_tx_busy) baud_cnt <= baud_cnt + 15'd1; always@(posedge clk or negedge rst_n) if(!rst_n) bit_cnt <= 4'd0; else if(send_en | (bit_cnt==4'd9 & baud_cnt==BAUD_CNT_MAX)) bit_cnt <= 4'd0; else if(uart_tx_busy & baud_cnt==BAUD_CNT_MAX) bit_cnt <= bit_cnt + 4'd1; always@(posedge clk or negedge rst_n) if(!rst_n) UART_TX <= 1'b1; else if(uart_tx_busy) case(bit_cnt) 4'd0: UART_TX <= START_BIT; 4'd1: UART_TX <= r_din_byte[0]; 4'd2: UART_TX <= r_din_byte[1]; 4'd3: UART_TX <= r_din_byte[2]; 4'd4: UART_TX <= r_din_byte[3]; 4'd5: UART_TX <= r_din_byte[4]; 4'd6: UART_TX <= r_din_byte[5]; 4'd7: UART_TX <= r_din_byte[6]; 4'd8: UART_TX <= r_din_byte[7]; 4'd9: UART_TX <= END_BIT; default: UART_TX <= 1'b1; 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 = 1'b1; always #(clk_period/2) clk = ~clk; initial begin rst_n = 1'b0; din_byte = 8'd0; send_en = 1'b0; #10; rst_n = 1'b1; #100.1; din_byte = 8'h55;//发送数据8'h55 send_en = 1'b1; #10; send_en = 1'b0; wait(uart_tx_done);//等待发送完成 #10.1; din_byte = 8'hb9;//发送数据8'hb9 send_en = 1'b1; #10; send_en = 1'b0; wait(uart_tx_done);//等待发送完成 #10.1; din_byte = 8'ha6;//发送数据8'ha6 send_en = 1'b1; #10; send_en = 1'b0; wait(uart_tx_done);//等待发送完成 #100; $stop; end endmodule

整体仿真图如下所示:

部分细节放大图:

从上图可以看出,3字节数据通过UART_TX线成功发送,单个比特的宽度为8.68us(对应115200的波特率)。

3、接收RX

UART的接收是将单比特信号恢复成字节数据,与发送过程相反,实现串行到并行的转化。模块的端口定义如下:

信号位宽方向描述
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 <= 3'd0; 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 <= 1'b0; else if(start) uart_rx_busy <= 1'b1; else if(uart_rx_busy & (bit_cnt==4'd9) & (baud_cnt==BAUD_CNT_MAX/2)) uart_rx_busy <= 1'b0; always@(posedge clk or negedge rst_n) if(!rst_n) baud_cnt <= 15'd0; else if(start | (baud_cnt==BAUD_CNT_MAX)) baud_cnt <= 15'd0; else if(uart_rx_busy) baud_cnt <= baud_cnt + 15'd1; always@(posedge clk or negedge rst_n) if(!rst_n) bit_cnt <= 4'd0; else if(start) bit_cnt <= 4'd0; else if(uart_rx_busy & (baud_cnt==BAUD_CNT_MAX)) bit_cnt <= bit_cnt + 4'd1; always@(posedge clk or negedge rst_n) if(!rst_n) r_byte <= 8'd0; else if(uart_rx_busy & (baud_cnt==BAUD_CNT_MAX/2)) case(bit_cnt) 4'd1: r_byte[0] <= r_uart_rx[2]; 4'd2: r_byte[1] <= r_uart_rx[2]; 4'd3: r_byte[2] <= r_uart_rx[2]; 4'd4: r_byte[3] <= r_uart_rx[2]; 4'd5: r_byte[4] <= r_uart_rx[2]; 4'd6: r_byte[5] <= r_uart_rx[2]; 4'd7: r_byte[6] <= r_uart_rx[2]; 4'd8: 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 <= 8'd0; uart_rx_done <= 1'b0; end else if(uart_rx_busy & (bit_cnt==4'd9) & (baud_cnt==BAUD_CNT_MAX/2))begin dout_byte <= r_byte; uart_rx_done <= 1'b1; end else uart_rx_done <= 1'b0; 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 = 1'b1; 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(i=0;i<8;i=i+1)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(8'ha2); //发送数据8'ha2 Gen_UART_RX(8'h9d); //发送数据8'h9d Gen_UART_RX(8'he8); //发送数据8'he8 Gen_UART_RX(8'hb4); //发送数据8'hb4 $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_reg(8bit)

向寄存器jump_reg中写入数据xx,不同的数据表示跳转到状态机的不同状态。例如,设计c1表示跳转到数组求和,c2表示跳转到求最大值,c3表示跳转到求最小值。

bb f1FIFO将FIFO中的数据全部读取出来
bb 02

fifo_state

(8bit)

读取寄存器fifo_state的数值。fifo_state为{2'd0,fifo_full,fifo_empty,fifo_data_count},输出FIFO的空满状态以及数据量。
bb 03

sum_result

(12bit)

读取寄存器sum_result的数值,该寄存器记录了数组求和结果。
bb 04

max_result

(8bit)

读取寄存器max_result的数值,该寄存器记录了数组的最大值。
bb 05

min_result

(8bit)

读取寄存器min_result的数值,该寄存器记录了数组的最小值。

设计主要基于有限状态机进行设计,一共划分了4个状态IDEL、SUM、MAX和MIN。在IDEL状态下,可以通过UART接口进行数据输入以及运算结果的读取。在其余状态下,对输入的数据进行具体的运算,运算完成后将结果写入对应寄存器(或者FIFO)中,完成运算后回到IDEL状态。

完整的模块设计文件compute.v:

module 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 = 2'd0; parameter SUM = 2'd1; parameter MAX = 2'd2; parameter MIN = 2'd3; //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的状态,{2'd0,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 <= 8'd0; 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 <= 8'd0; else fifo_state <= {2'd0,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 == 8'hc1) next_st = SUM; else if(jump_reg == 8'hc2) next_st = MAX; else if(jump_reg == 8'hc3) 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 <= 1'b0; fifo_num <= 8'd0; end else case(st) IDEL:begin if(uart_rx_done)begin if(r_uart_rx_dout == 8'haa & uart_rx_dout == 8'hf1) fifo_num_wren <= 1'b1; else fifo_num_wren <= 1'b0; if(fifo_num_wren & uart_rx_done) fifo_num <= uart_rx_dout; else fifo_num <= fifo_num; end end default:begin fifo_num_wren <= 1'b0; fifo_num <= fifo_num; end endcase //cnt_fifo_en always@(posedge clk or negedge rst_n) if(!rst_n)begin cnt_fifo_en <= 1'b0; cnt_fifo <= 8'd0; end else case(st) IDEL: begin if(fifo_num_wren & uart_rx_done) cnt_fifo_en <= 1'b1; else if((cnt_fifo == fifo_num - 8'd1) & uart_rx_done) cnt_fifo_en <= 1'b0; if(cnt_fifo_en) if(uart_rx_done) cnt_fifo <= cnt_fifo + 8'd1; else cnt_fifo <= cnt_fifo; else cnt_fifo <= 8'd0; end default:begin cnt_fifo_en <= 1'b0; cnt_fifo <= 8'd0; end endcase //fifo_din and fifo_wren always@(posedge clk or negedge rst_n) if(!rst_n)begin fifo_din <= 8'd0; fifo_wren <= 1'b0; fifo_rden <= 1'b0; end else case(st) IDEL: begin if(cnt_fifo_en & uart_rx_done)begin fifo_din <= uart_rx_dout; fifo_wren <= 1'b1; end else begin fifo_wren <= 1'b0; end if((uart_rx_done & r_uart_rx_dout == 8'hbb & uart_rx_dout == 8'hf1) | (fifo_uart_rden & uart_tx_done)) fifo_rden <= 1'b1; else fifo_rden <= 1'b0; end SUM,MAX,MIN:begin if(fifo_empty) fifo_rden <= 1'b0; else fifo_rden <= 1'b1; end default:begin fifo_din <= fifo_din; fifo_wren <= 1'b0; fifo_rden <= 1'b0; end endcase //cmd_jumpreg_wren and jump_reg always@(posedge clk or negedge rst_n) if(!rst_n)begin cmd_jumpreg_wren <= 1'b0; jump_reg <= 8'd0; end else case(st) IDEL:begin if(uart_rx_done)begin if(r_uart_rx_dout == 8'haa & uart_rx_dout == 8'h01) cmd_jumpreg_wren <= 1'b1; else cmd_jumpreg_wren <= 1'b0; end if(cmd_jumpreg_wren & uart_rx_done) jump_reg <= uart_rx_dout; else jump_reg <= 8'd0; end default:begin cmd_jumpreg_wren <= 1'b0; jump_reg <= jump_reg; end endcase //r_fifo_valid always@(posedge clk or negedge rst_n) if(!rst_n) r_fifo_valid <= 1'b0; else r_fifo_valid <= fifo_valid; //sum_result always@(posedge clk or negedge rst_n) if(!rst_n) sum_result <= 12'd0; 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 <= 8'd0; 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 <= 8'd0; 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 <= 1'b0; else case(st) IDEL: compute_busy <= 1'b0; SUM,MAX,MIN: compute_busy <= 1'b1; default: compute_busy <= compute_busy; endcase //sum_result_rden always@(posedge clk or negedge rst_n) if(!rst_n) sum_result_rden <= 1'b0; else case(st) IDEL: begin if(uart_rx_done & r_uart_rx_dout == 8'hbb & uart_rx_dout == 8'h03) sum_result_rden <= 1'b1; else if(sum_result_rden & uart_tx_done) sum_result_rden <= 1'b0; end default: sum_result_rden <= 1'b0; endcase //fifo_uart_rden always@(posedge clk or negedge rst_n) if(!rst_n) fifo_uart_rden <= 1'b0; else case(st) IDEL: begin if(uart_rx_done & r_uart_rx_dout == 8'hbb & uart_rx_dout == 8'hf1) fifo_uart_rden <= 1'b1; else if(fifo_uart_rden & fifo_empty & fifo_valid) fifo_uart_rden <= 1'b0; end default: fifo_uart_rden <= 1'b0; endcase //uart_tx_din and uart_tx_senden; always@(posedge clk or negedge rst_n) if(!rst_n)begin uart_tx_din <= 8'd0; uart_tx_senden <= 1'b0; end else case(st) IDEL: begin if(uart_rx_done & r_uart_rx_dout == 8'hbb)begin if(uart_rx_dout == 8'h02)begin uart_tx_din <= fifo_state; uart_tx_senden <= 1'b1; end else if(uart_rx_dout == 8'h03)begin uart_tx_din <= {4'd0,sum_result[11:8]}; uart_tx_senden <= 1'b1; end else if(uart_rx_dout == 8'h04)begin uart_tx_din <= max_result; uart_tx_senden <= 1'b1; end else if(uart_rx_dout == 8'h05)begin uart_tx_din <= min_result; uart_tx_senden <= 1'b1; end else begin uart_tx_senden <= 1'b0; end end else if(sum_result_rden & uart_tx_done)begin uart_tx_senden <= 1'b1; uart_tx_din <= sum_result[7:0]; end else if(fifo_uart_rden & fifo_valid)begin uart_tx_senden <= 1'b1; uart_tx_din <= fifo_dout; end else begin uart_tx_senden <= 1'b0; end end default: begin uart_tx_din <= uart_tx_din; uart_tx_senden <= 1'b0; 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.v

module 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 = 1'b1; 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(i=0;i<8;i=i+1)begin UART_RX = din_byte[i]; #8680; end UART_RX = 1; #8680; end endtask integer j; initial begin rst_n = 1'b0; UART_RX = 1'b1; #10; rst_n = 1'b1; #100.1; //write fifo //向FIFO中写入16个字节数据 Gen_UART_RX(8'haa); Gen_UART_RX(8'hf1); Gen_UART_RX(8'h10); //16 for(j=0;j<16;j=j+1)begin Gen_UART_RX(j+5); //具体的字节数据 end //read fifo_state Gen_UART_RX(8'hbb); //读取fifo_state寄存器 Gen_UART_RX(8'h02); #(8681*10); //rd fifo Gen_UART_RX(8'hbb); //将FIFO中的所有数据读取出来 Gen_UART_RX(8'hf1); wait(compute_inst0.fifo_empty); //write fifo Gen_UART_RX(8'haa); //向FIFO中写入10个字节数据 Gen_UART_RX(8'hf1); Gen_UART_RX(8'h0a); //10 for(j=0;j<10;j=j+1)begin //具体的字节数据 Gen_UART_RX(j+1); end //sum Gen_UART_RX(8'haa); //写jump_reg寄存器,c1跳转到求和状态 Gen_UART_RX(8'h01); Gen_UART_RX(8'hc1); //read sum result Gen_UART_RX(8'hbb); //读取sum_result寄存器,通过UART传输结果 Gen_UART_RX(8'h03); #(8681*10*2); //read fifo_state Gen_UART_RX(8'hbb); //读取fifo_state寄存器 Gen_UART_RX(8'h02); #(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.xdc):

create_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中写入数据,每次都写入4个数据。接着,读取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的简单使用就分享到这里,有空继续分享其他常用的接口时序和使用方式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 21:45:59

AI让个人软件开发变得简单

之前我写过一个微博备份工具。那个版本是我纯手撸出来的。能用&#xff0c;但界面和流程都不算顺。用户安装完之后&#xff0c;经常会来问我&#xff1a;软件怎么用&#xff1f;下一步点哪里&#xff1f;旧版大概是这样&#xff1a;后来我想明白了&#xff1a;一个软件如果做得…

作者头像 李华
网站建设 2026/7/13 21:45:48

Vue Carousel响应式设计完全攻略:适配各种屏幕尺寸的最佳实践

Vue Carousel响应式设计完全攻略&#xff1a;适配各种屏幕尺寸的最佳实践 【免费下载链接】vue-carousel A flexible, responsive, touch-friendly carousel for Vue.js 项目地址: https://gitcode.com/gh_mirrors/vu/vue-carousel 想要在Vue.js项目中创建完美的响应式轮…

作者头像 李华
网站建设 2026/7/13 21:43:02

如何免费跨平台下载B站视频?BilibiliDown终极指南

如何免费跨平台下载B站视频&#xff1f;BilibiliDown终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors/bi/Bi…

作者头像 李华
网站建设 2026/7/13 21:40:15

2026国内展厅设计公司推荐,从技术、案例双维度筛选优质公司

随着企业数字化转型不断深入&#xff0c;展厅已经不再只是展示产品和企业介绍的空间&#xff0c;而成为企业品牌传播、客户交流、招商展示、文化输出的重要载体。2026年的展厅设计行业&#xff0c;竞争重点已经发生变化。过去企业选择设计公司&#xff0c;更多关注“设计效果是…

作者头像 李华