当前位置:   article > 正文

【FPGA入门】实现简单的UART收发_fpga实现uart

fpga实现uart

1. 引言

  UART即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),相信常和硬件打交道的同学都不会陌生,在学习过程中最常见的就是使用在开发板和PC或另一台开发板的通信。作为一种简单的通信协议,UART在实际使用中非常广泛。如果是学习Verilog HDL或者FPGA的新手,UART也是一个必不可少的入门例程。
在这里插入图片描述

  这里本人对UART进行一次简单的实现,并且加入了LED和拨码开关作为外设实现对UART收发控制,可以直接在FPGA开发板上观察实验结果。因为这里讲解的定位是入门,实现最基础的功能,如果对各位小伙伴有帮助,还请赏个脸点个赞,如有不足,欢迎评论指出ଘ(੭ˊᵕˋ)੭

2. 设计思路

  既然前面说到UART是一种非常简单的通信传输协议,那我们这里就以此为设计目标,由目标去引导设计流程,循序渐进,一直到最后的实现。

2.1 接口选择

  假设我们希望两台设备之间进行通信,需要一次传输一个byte,也就是8-bit,那么利用8跟线一次将数据全部发送当然是最简单不过的,但实际上由于我们对数据传输的速率要求并不高(本实验中默认波特率115200),而在实际情况下芯片的IO数量是非常稀缺的资源,为了节省IO,像这样的低速接口普遍采用串行传输的方式将8-bit数据放在一根线上依次发送(如UART/IIC/SPI)以此来选择牺牲速度去节省IO资源(但实际上有些高速接口也用串行传输如SERDES,但完全是两种东西这里不讲了)。
  好了这里我们就妥协一下,选择串行的方式,将8-bit数据依次放在一根线上,一个一个依次发送好了。既然如此,如果要实现两台设备间的数据收发,首先想到的就是一根线发送,一根线接收,另一台设备也是如此,让发送对接收,接收对发送。我们最常见的UART模块设备通常就是这样使用两条线实现通信,分别是用来发送数据的TXD,和用来接收数据的RXD(实际上一根线也可以进行数据收发,如IIC的SDA)。

2.2 通信协议

  前面我们打算将8-bit数据依次放在一根线上面进行发送,但紧接着我们不得不面临两个问题:

  1. 假如发送方开始发送数据,则开始将TXD的电平按数据依次翻转。但当连续发送两个相同的电平时(如11或00),接收方如何得知发来的是一个、两个甚至更多呢?
  2. 如何得知什么时刻开始发送数据的呢。假如TXD在没有数据需要发送时,默认为高电平闲置状态,那么当需要发送1的时候,接收方如何得知此时是仍在闲置状态,还是已经开始发送1了呢。

  而UART就为了解决各种问题而制定了一系列规则。其中面对问题1,协议指明了单个电平需要持续的时间,让接收方知道发送的是1个bit,而不是2个或更多,这个时间对应的频率就是比特率/bps(由于一个符号用1-bit表示,所以也可以叫波特率)。面对问题2,协议对将要传输的8-bit开头加1-bit起始位,末尾加1-bit停止位,也就是将数据封装为一帧。在没有数据需要发送时,TXD处于闲置状态,默认为高电平,当有数据需要发送时,先传输1-bit低电平,这样接收方由闲置状态感知到电平变化,明白要开始传输数据了,这1-bit就是起始位。起始位发送完毕后,紧接着是8-bit数据位依次发送,这里采用的是LSB FIRST,也就是并串转换时先发送低位,最后发送高位。8-bit数据全部发送完毕后,紧接着发送1-bit高电平,将TXD重新拉回闲置状态,这一bit就是停止位。这样这两个问题是不是就解决了呢,我们赶紧动手写代码去试一下~
在这里插入图片描述

3. 模块设计

  动手开始写代码之前,我们再回顾一下前面的内容,先把图纸画好。

  1. 功能
    通过拨码开关选择要发送的值,系统将拨码开关的状态读出来,并令UART发送出去;当UART接收到数据时,就将接收数据结果显示到LED。
  2. 接口
    时钟和复位作为全局输入信号
    8-bit拨码开关和8-bitLED作为面向用户的输入输出
    TXD和RXD作为UART发送和接收的接口
  3. 模块
      UART部分主要是两部分逻辑:i)波特率产生和采样逻辑 ii)发送和接收逻辑。对于波特率,实际上就是产生一个目标频率的脉冲信号,而对于发送和接收逻辑,实际上就是将这个波特率脉冲作为采样脉冲,将要发送的8-bit数据拆成8个1-bit放在TXD上依次发送,或者是对RXD采样,再拼成8-bit数据。
      这样,UART部分的逻辑就被我们分作两组模块,分别是i)发送逻辑和发送波特率 ii)接收逻辑和接收波特率。

4. Verilog HDL逻辑设计

4.1 顶层模块

在这里插入图片描述
  设计如上图所示的模块结构。顶层模块FPGA_Top实现UART模块的例化,并连接用户接口SW和LED。需要发送数据时,transmit模块获取波特率脉冲,并以此将SW的8-bit数据封装为一帧后通过TXD依次发送出来。在receive模块检测到RXD从空闲的高电平状态拉低,也就是检测到起始位,则开始按波特率脉冲对RXD进行采样,完成串并转换后传入LED显示。

4.2 UART模块

  为了实现UART的功能,我们首先要考虑的就是,如何将一个8-bit的数据放在TXD上发送出去。前面已经讲到,为了让接收方能够正确识别,需要对数据约定波特率,并封帧发送。波特率就是利用时钟分频得到一个目标频率的脉冲,封帧就是在数据首位各加1-bit,接下来按位依次发送。这样再看,逻辑是不是就清晰许多了呢。
  为了得到固定频率的脉冲,我们设计一个模块进行时钟分频;为了实现数据的最终发送,我们设计一个模块进行封帧和并串转换。接收部分亦然。
  更进一步,当需要通过UART发送数据时,transmit模块会请求波特率发生器模块开始工作,紧接着产生所需频率的脉冲,transmit模块对要发送的8-bit数据进行封帧,紧接着进行并串转换,完成数据的发送。而当外界向UART发送数据时,会将处于高电平闲置的RXD拉低,一旦receive检测到RXD从闲置状态拉低,则判定当前为起始位,紧接着请求波特率发生器模块按所需频率产生采样脉冲,将数据按脉冲一一接收,完毕后掐头去尾,得到所需要的8-bit数据。

4.2.1 波特率发生器模块

  1. 功能和接口
      波特率发生器模块负则输出一个固定频率的脉冲,要设计这样的时序逻辑电路,时钟和复位当然必不可少。为了使输出的脉冲相位可控,我们添加一个en信号,当en信号使能时,模块才开始工作。为了使输出的脉冲频率可调,我们添加一个分频控制信号prescale。

  2. 设计思路
      波特率发生器模块产生一个需要频率的脉冲,类似于分频器,实际上其逻辑就是用计数器对输入参考时钟进行计数,当计到N时,这段时间对应的频率就是所需要的采样脉冲频率。
      其中,
       N = 参 考 时 钟 频 率 f r e f / 目 标 波 特 率 f B N=参考时钟频率f_{ref}/目标波特率f_B N=fref/fB
      比如参考时钟为板载晶振50MHz,目标波特率为115200B,则N为50,000,000/115200=434

  3. 代码实现

module baud_gen
#(
    parameter   simPresent  = 0
)(

    input               clk,
    input               rst_n,

    input   [15:0]      prescale,       // 分频
	input				baud_en,        // 使能
    output              baud_pulse      // 采样脉冲
);

    reg     [15:0]      cnt;            // 计数器  
    reg                 baud_pulse_r;   // 采样脉冲
	wire 				rst_flag;       // 计数器复位标志
	wire                sample_flag;    // 计数器采样标志
    
	assign rst_flag = (cnt==(prescale-1));
	// 这里以cnt计到7时,也就是每8个clk输出一个采样脉冲,因此注意prescale不要小于8
	assign sample_flag = (cnt==7);
    assign baud_pulse = baud_pulse_r;
 
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)                      cnt <= 0;
        else if(rst_flag)               cnt <= 0;
        else if(baud_en)                cnt <= cnt + 1;
        else                            cnt <= 0;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)                      baud_pulse_r <= 0;
        else                            baud_pulse_r <= sample_flag;
    end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

4.2.2 发送模块

  1. 功能和接口
      发送模块需要实现并串转换,就是将8-bit作为输入数据接口,1-bit作为输出数据接口。此外还需要发送标志,来告诉模块当前的8-bit数据有效可以开始处理发送;模块进入发送状态时,会输出en信号告知波特率发生器开始工作;波特率发生器会产生采样脉冲标志,告诉发送模块可以发送下一bit;最后输出done信号告知外界本次8-bit数据发送完毕。

  2. 设计思路
      发送模块的核心思想其实就是移位寄存器实现并串转换。但是考虑到串口发送的流程中,涉及到数据封帧以及等待采样脉冲,因此这里为了使整个流程更于直观,使用状态机的方式进行设计。
    在这里插入图片描述
    i)IDLE闲置状态下,当发送标志信号tx_flag有效时,通知transmit模块开始工作,状态机首先进入SYNC同步状态,等待波特率发生器的首个采样脉冲,紧接着将baud_en使能信号拉高,通知波特率发生器开始工作。
    ii)SYNC同步状态下,当获取到波特率发生器的首个采样脉冲,transmit模块进入START起始位发送状态。与此同时移位寄存器置为{din, 1’b0},最低位为0,作为输出到TXD的起始位。
    iii)START起始位发送状态下,当获取到采样脉冲,transmit模块进入DATA数据位发送状态。与此同时移位寄存器data_shift最低位置为din[0],将8-bit数据最低位输出到TXD。
    iv)DATA数据位发送状态下,当获取到采样脉冲,计数器cnt按脉冲从0计到7共8次。与此同时每个采样脉冲下移位寄存器data_shift向右移位,依次将8-bit数据通过TXD发送。
    v)DATA数据位发送状态下,当cnt已经计到7并且获取到采样脉冲,transmit模块进入STOP停止位发送状态。与此同时移位寄存器data_shift将1移到最低为,作为TXD最后的停止位。
    vi)STOP停止位发送状态下,当获取到采样脉冲,表明停止位发送完毕,transmit模块重新进入IDLE状态,本次8-bit传输到此结束。

  3. 代码实现

module transmit(

    input           clk,
    input           rst_n,
    
    input   [7:0]   din,            // 8-bit数据输入
    input           tx_flag,        // 发送标志
    input           sample_flag,    // 采样标志
    output          baud_en,        // 使能波特率发生器
    output          txd,            // UART_TXD
    output          done            // 发送完毕
);

    reg [8:0]       data_shift;     // 移位寄存器
    reg [3:0]       cnt;            // 计数器
    
    reg [3:0]       state;          // 状态机
	reg [3:0]       state_next;
	
	localparam ST_IDLE		= 0;    // 闲置状态
    localparam ST_SYNC      = 1;    // 同步,等待波特率采样脉冲
	localparam ST_START		= 2;    // 起始位
	localparam ST_DATA		= 3;    // 数据位
	localparam ST_STOP 		= 4;    // 停止位
    
	always @(posedge clk, negedge rst_n) begin
		if(!rst_n)					state <= ST_IDLE;
		else 						state <= state_next;
	end
	
	always @(*) begin
		if(!rst_n)					state_next = ST_IDLE;
		else begin
			case(state)
                // 闲置状态下,等待tx_flag发送标志信号,进入同步状态
				ST_IDLE: begin
					if(tx_flag)     state_next = ST_SYNC;
					else 			state_next = state;
				end
                // 同步状态下,等待sample_flag采样标志信号,进入起始位发送状态
                ST_SYNC: begin
					if(sample_flag) state_next = ST_START;
					else 			state_next = state;
                end
                // 起始位发送状态下,等待sample_flag采样标志信号,进入数据位发送状态
				ST_START: begin
                    if(sample_flag) state_next = ST_DATA;
                    else 			state_next = state;
                end
                // 数据位发送状态下,等待计数器从0数到7共8-bit发送完毕,进入停止位发送状态
				ST_DATA: begin
					if((cnt==7)&sample_flag)		
                                    state_next = ST_STOP;
                    else 			state_next = state; 
				end
                // 停止位发送状态下,等待sample_flag采样标志信号,结束本次发送,进入闲置状态
                ST_STOP: begin
                    if(sample_flag) state_next = ST_IDLE;
                    else 			state_next = state;
                end
				default: 			state_next <= ST_IDLE;
			endcase
		end
	end
    
    // 计数器,在ST_DATA状态下数采样脉冲sample_flag
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)                  cnt <= 0;
        else if(state!=ST_DATA)     cnt <= 0;
        else if(sample_flag)        cnt <= cnt + 1;
    end
    
    // 移位寄存器,实现并串转换
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)                  data_shift <= 9'b1_1111_1111;
        else if(state==ST_IDLE)     data_shift <= 9'b1_1111_1111;
        else if((state==ST_SYNC)&sample_flag)  
                                    data_shift <= {din, 1'b0};
        else if(sample_flag)        data_shift <= {1'b1, data_shift[8:1]};
    end

    assign txd = data_shift[0];
    assign baud_en = (state!=ST_IDLE);  // 只要不在闲置状态,就令波特率发生器开始工作
    assign done = (state==ST_IDLE);     // 如果处于闲置状态,表明发送完毕

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

4.2.3 接收模块

  1. 功能和接口
      与发送模块相对应,接收模块要实现的是串并转换,就是将1-bit作为输入数据接口,8-bit作为输出数据接口。不同的是,接收模块不像发送模块那样可以通过发送标志来进入工作状态,而是需要检测闲置状态的RXD是否有电平变化进而进入工作状态。接收模块检测到闲置状态的RXD电平发生变化时,会输出en信号告知波特率发生器开始工作;波特率发生器会产生采样脉冲标志,告诉接收模块可以对当前bit进行采样。最后输出valid信号告知外界本次8-bit数据接收完毕,并且此时的rx_data有效。
  2. 设计思路
      相应地,接收模块的核心思想其实就是移位寄存器实现串并转换。接收过程中需要对RXD的数据流掐头去尾,留下我们需要的8-bit数据。这里同样使用状态机实现,其核心设计思想和transmit模块类似,这里不再赘述。
  3. 代码实现
module receive(

    input           clk,
    input           rst_n,
    
    input   	    rxd,            // UART_RXD
    output	[7:0]	rx_data,        // 8-bit数据输出
    input           sample_flag,    // 采样标志
    output          baud_en,        // 使能波特率发生器
    output          valid           // 接收完毕,数据有效标志
);

    reg [7:0]   	data_shift;     // 移位寄存器
    reg [3:0]   	cnt;            // 计数器
    
	reg [3:0]		state;          // 状态机
	reg [3:0]		state_next;
    
    reg [7:0]	    rx_data_r;      
    reg             valid_r;
	
	localparam ST_IDLE		= 0;    // 闲置状态
    localparam ST_SYNC      = 1;    // 同步,等待波特率采样脉冲
	localparam ST_START		= 2;    // 起始位
	localparam ST_DATA		= 3;    // 数据位
	localparam ST_STOP 		= 4;    // 停止位
	
	always @(posedge clk, negedge rst_n) begin
		if(!rst_n)					state <= ST_IDLE;
		else 						state <= state_next;
	end
	
	always @(*) begin
		if(!rst_n)					state_next = ST_IDLE;
		else begin
			case(state)
                // 闲置状态下,等待RXD电平拉低,进入同步状态
				ST_IDLE: begin
					if(!rxd)		state_next = ST_SYNC;
					else 			state_next = state;
				end
                // 同步状态下,等待sample_flag采样标志信号,进入起始位接收状态
				ST_SYNC: begin
                    if(sample_flag) state_next = ST_START;
                    else            state_next = state;
                end
                // 起始位接收状态下,等待sample_flag采样标志信号,进入数据位接收状态
				ST_START: begin
                    if(sample_flag) state_next = ST_DATA;
                    else            state_next = state;
                end
                // 数据位接收状态下,等待计数器从0数到7共8-bit接收完毕,进入停止位接收状态
				ST_DATA: begin
					if((cnt==7)&sample_flag)		
                                    state_next = ST_STOP;
                    else 			state_next = state; 
				end
                // 停止位发送状态下,等待sample_flag采样标志信号,结束本次发送,进入闲置状态
                ST_STOP:            state_next <= ST_IDLE;
				default: 			state_next <= ST_IDLE;
			endcase
		end
	end
	
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)                  cnt <= 0;
        else if(state!=ST_DATA)     cnt <= 0;
        else if(sample_flag)        cnt <= cnt + 1;
    end
    
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)          data_shift <= 0;
        else if(state==ST_IDLE)
                            data_shift <= 0;
        else if(sample_flag)
                            data_shift <= {rxd, data_shift[7:1]};
    end
    
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)          rx_data_r <= 0;
        else if((cnt==7)&sample_flag)
                            rx_data_r <= data_shift;
    end
    
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)          valid_r <= 0;
        else                valid_r <= (cnt==7)&sample_flag;
    end
    
    assign baud_en = (state!=ST_IDLE);
    assign rx_data = rx_data_r;
    assign valid = valid_r;

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

4.3 顶层设计文件和Testbench

  核心功能在自模块已经逐个实现,顶层模块只需要按照功能将自模块进行拼装。这里对子模块封装了两层,分别是UART功能的UART_Top,以及用来将模块和板载外设连接的FPGA_TOP。

module UART_Top
#(
    parameter   simPresent  = 0
)(
    input               CLK,
    input               RSTn,
    
    input               tx_flag,
    input   [7:0]       tx_data,
    output              tx_done,
    
    output              rx_valid,
    output  [7:0]       rx_data,
    
    input               UART_RXD,
    output              UART_TXD
);
    
    wire    [15:0]      prescale;
    wire                tx_baud_en;
    wire                tx_baud_pulse;
    wire                rx_baud_en;
    wire                rx_baud_pulse;

    generate 
        if(simPresent) assign prescale = 16;
        else assign prescale = 434;
    endgenerate
    
    baud_gen u_tx_baud(
        .clk                (CLK            ),
        .rst_n              (RSTn           ),
        .prescale           (prescale       ),
		.baud_en            (tx_baud_en     ),
        .baud_pulse         (tx_baud_pulse  )
    );

    transmit u_tx(
        .clk                (CLK            ),
        .rst_n              (RSTn           ),
        .din                (tx_data   		),
        .txd                (UART_TXD       ),
        .sample_flag        (tx_baud_pulse  ),
        .tx_flag            (tx_flag        ),
        .baud_en            (tx_baud_en     ),
        .done               (tx_done        )
    );
    
    baud_gen u_rx_baud(
        .clk                (CLK            ),
        .rst_n              (RSTn           ),
        .prescale           (prescale       ),
		.baud_en            (rx_baud_en     ),
        .baud_pulse         (rx_baud_pulse  )
    );

    receive u_rx(
        .clk                (CLK            ),
        .rst_n              (RSTn           ),
        .rxd                (UART_RXD    	),
        .rx_data            (rx_data        ),
        .baud_en            (rx_baud_en     ),
        .sample_flag        (rx_baud_pulse  ),
        .valid              (rx_valid       )
    );

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
module FPGA_TOP
#(
    parameter   simPresent  = 0
)(
    // global signals
    input                   CLK,
    input                   RSTn,

    // control & status signals
    input   [7:0]           SW,
    input   [0:0]           KEY,
    output  [7:0]           LED,

    // data
    input                   UART_RXD,
    output                  UART_TXD
);

    wire [7:0]  tx_data;
    wire        tx_flag;
    wire        tx_done;
    wire [7:0]  rx_data;
    
    assign tx_data = SW;
    assign tx_flag = KEY & tx_done;
    assign LED = rx_data;

    UART_Top #( .simPresent(simPresent) ) 
    u_uart
    (
        .CLK        (CLK        ),
        .RSTn       (RSTn       ),
        
        .tx_flag    (tx_flag    ),
        .tx_data    (tx_data    ),
        .tx_done    (tx_done    ),
        
        .rx_data    (rx_data    ),
        .rx_valid   (),
    
        .UART_RXD   (UART_RXD   ),
        .UART_TXD   (UART_TXD   )
    );
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

工程综合后,通过rtl viewer可以看到整个模型的结构。
在这里插入图片描述
对于testbench,这里利用task的方式模拟UART的传输行为,并以此作为输入激励,测试我们UART模块的接收功能。

module tb;

    reg     clk;
    reg     rst_n;
    
    wire    UART_TXD;
    reg     UART_RXD;
    
    reg uart_pulse;
    
    localparam CLK_PERIOD = 20;
    
    initial begin
        clk = 0;
        rst_n = 0;
        UART_RXD = 1;
        #200 rst_n = 1;
        uart_pulse = 0;
        
        TaskUartTest(8'h20);
        TaskUartTest(8'h4d);
        TaskUartTest(8'h65);
        TaskUartTest(8'h72);
        TaskUartTest(8'h52);
        TaskUartTest(8'h79);
        TaskUartTest(8'h20);
        TaskUartTest(8'h43);
        TaskUartTest(8'h68);
        TaskUartTest(8'h72);
        TaskUartTest(8'h69);
        TaskUartTest(8'h73);
        TaskUartTest(8'h74);
        TaskUartTest(8'h6d);
        TaskUartTest(8'h61);
        TaskUartTest(8'h73);
        TaskUartTest(8'h21);
        
        #50000 $stop;
    end

    always #(CLK_PERIOD/2) clk = ~clk;
    
    FPGA_TOP 
    #( .simPresent(1) )
    dut(
        .CLK        (clk        ),
        .RSTn       (rst_n      ),
        .SW         (8'h55      ),
        .KEY        (1          ),
        .UART_RXD   (UART_RXD),
        .UART_TXD   (UART_TXD)
    );

    always #(16*CLK_PERIOD/2) uart_pulse = ~uart_pulse;
	
	task TaskUartTest;
	input [7:0] putchar;
	begin: taskUartTest
		
		automatic integer i;
		UART_RXD = 1;
		@(posedge uart_pulse) UART_RXD = 0;
		for(i=0; i<8; i=i+1)
			@(posedge uart_pulse) UART_RXD = putchar[i];
		@(posedge uart_pulse) UART_RXD = 1;
	end
	endtask

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

在发送部分,模块将0x55封装并持续发送,可以通过TXD的输出结果。
在接收部分,LED显示了UART模块的接收结果,可以看到和tb中的输入一致。
在这里插入图片描述

4.4 FPGA上板测试

引脚约束->综合->实现->生成比特流文件
SW作为要发送的数据,KEY作为发送使能。对于有些已经有串口转USB芯片的开发板,直接以此进行引脚约束,并连接电脑即可。但在大多数情况下,需要将TXD和RXD约束至开发板的GPIO,并通过CH340串口转USB模块和电脑连接。最后选择一款串口助手软件,即可观察实验结果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/641376
推荐阅读
相关标签
  

闽ICP备14008679号