Verilog 实现 UART RX 接收器

目录

1、简述

2、设计

3、实现

4、测试


 

1、简述

串口作为 CPU 最常使用的外设资源之一,常常出现在各种场合,既然最近在入坑 FPGA,那么先搞一个简单的串口接收机来玩玩;

串口相关的基本知识就不在这里重复议论了,参考我的另一篇文章《STM32F103ZET6 — USART》,总的来说,时序如下所示:

Verilog 实现 UART RX 接收器_第1张图片

站在硬件的角度上来讲,要实现一个串口接收器,需要考虑一下几点内容:

1、串口通信,默认情况没有数据传送的时候,RX 线上空闲为高电平,出现起始位,线上会被拉低,那么首先考虑做一个下降沿检测器,来检测起始位;

2、发现起始位后,便需要进行线上数据的采样,采样的点,需要在数据的中心点,才能保证数据的稳定,那么就需要根据传送的波特率来计算采样的时间,并将数据采样到内部寄存器;

3、采样数据的个数,根据配置的数据位,校验位和停止位的 bit 数决定,那么就需要根据串口的配置,来对采样数据的个数进行计数,所以需要设计一个数据计时器;

 

2、设计

好了,简单的进行了需求分析和设计分解后,那么理想中的设计框图为:

Verilog 实现 UART RX 接收器_第2张图片

1、PLL 接收来自外部的 25MHz 的晶振输入,将输出的时钟 clk 给 UART RX 模块,同时将时钟锁定的信号 Locked 接到 UART RX 的内部复位信号;

2、rx 信号直接接入到下降沿检测电路的输入,进行边沿检测,检测完成后,确定有起始位后,那么告诉波特率发生器,可以产生波特率信号;

3、波特率信号发生器根据输入的时钟和设置的波特率,进行计数,并在需要采样的点的地方,产生采样信号,供给下级电路;

4、状态机根据波特率的采样信号,来进行状态转换,从 IDLE 到 Sampling 状态,并在 Sampling 状态开始采样,并进行采样数据的计数;

5、当采样个数完成后,将数据并行的发送给后继的 FIFO,完成一帧数据的接收;

为了简化设计和仿真, 这里假设了波特率的 Counter 为 50(视具体波特率和输入时钟计算),8个数据位,没有校验位,1个停止位,并将采样后的数据直接以并行的形式输出;

 

3、实现

Verilog 源码设计如下(0 warning,0 error):

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    22:27:09 12/04/2019 
// Design Name: 
// Module Name:    uart_rx 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module uart_rx(
    input clk,
    input rst_n,
    input rx,
    output reg [9:0] data_out
    );


    // State Machine Defination
    parameter IDLE = 2'b01;
    parameter SAMP = 2'b10;
    
    // UART Configure Defination
    parameter START_BIT = 1;
    parameter DATA_BIT  = 8;
    parameter STOP_BIT  = 1;
    parameter PARI_BIT  = 0;
    parameter RECV_BIT  = START_BIT + DATA_BIT + STOP_BIT + PARI_BIT;
    
// Negedge Detection Filter
    reg [3:0] data_in;
always @ (posedge clk or negedge rst_n)
    if (!rst_n) begin
        data_in[0] <= 1'b0;
        data_in[1] <= 1'b0;
        data_in[2] <= 1'b0;
        data_in[3] <= 1'b0;
    end
    else begin
        data_in[0] <= rx;
        data_in[1] <= data_in[0];
        data_in[2] <= data_in[1];
        data_in[3] <= data_in[2];
    end

wire rx_neg = data_in[3] & data_in[2] & (~data_in[1]) & (~data_in[0]);

    reg [1:0] current_state, next_state;
    // Current State To Next State
    always @ (posedge clk or negedge rst_n)
    if(!rst_n)
        current_state <= IDLE;
    else
        current_state <= next_state;

    // State
    reg sample_finish;
    always @ (current_state or sample_finish or rx_neg)
    begin
        next_state = 2'bx;
        case(current_state)
            IDLE : begin
                if (rx_neg) next_state = SAMP;
                else        next_state = IDLE;
            end
            SAMP : begin
                if (sample_finish) next_state = IDLE;
                else               next_state = SAMP;
            end
            default : next_state = IDLE;
        endcase
    end

    reg [3:0] recv_cnt;
    reg sample_en;
    reg [RECV_BIT - 1 : 0] data_temp;
    always @ (posedge clk or negedge rst_n)
    if (!rst_n) begin
        data_out  <= 10'bx;
        data_temp <= 10'bx;
        sample_finish <= 1'b0;
        sample_en <= 1'b0;
        recv_cnt <= 4'b0;
    end
    else begin
        case (next_state)
            IDLE : begin
                //data_out  <= 10'bx;
                data_temp <= 10'bx;
                sample_finish <= 1'b0;
                sample_en <= 1'b0;
                recv_cnt <= 4'b0;
            end
            SAMP : begin
                if (recv_cnt == RECV_BIT) begin
                    data_out  <= data_temp;
                    data_temp <= 10'bx;
                    sample_finish <= 1'b1;
                    sample_en <= 1'b0;
                    recv_cnt  <= 4'b0;
                end
                else begin
                    sample_en <= 1'b1;
                    if (baud_clk) begin
                        data_out <= data_out;
                        data_temp[recv_cnt] <= rx;
                        sample_finish <= 1'b0;
                        recv_cnt <= recv_cnt + 1'b1;
                    end
                    else begin
                        data_out <= data_out;
                        data_temp <= data_temp;
                        sample_finish <= sample_finish;
                        recv_cnt <= recv_cnt;
                    end
                end
            end
            default: begin
                data_out <= 10'bx;
                sample_finish <= 1'b0;
                sample_en <= 1'b0;
            end
        endcase
    end
        
// Sample Counter Signal Generator
    parameter BAUD_MAX   = 50;
    reg [5:0] baud_cnt;
    always @ (posedge clk or negedge rst_n)
    if (!rst_n) begin
        baud_cnt <= 6'd0;
    end
    else begin
        if (sample_en) begin
            if (baud_cnt == BAUD_MAX - 1) baud_cnt <= 6'd0;
            else baud_cnt <= baud_cnt + 1'b1;
        end
        else baud_cnt <= 6'd0;
    end

// Sample Clock Signal Generator
    parameter BAUD_CNT_H = (BAUD_MAX / 2);
    wire baud_clk = (baud_cnt == BAUD_CNT_H) ? (1'b1) : (1'b0);
endmodule

 

4、测试

测试的 testbench 为:

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   14:03:06 12/05/2019
// Design Name:   uart_rx
// Module Name:   D:/Xlinx_ISE_Projects/testbench/uart_rx_tb.v
// Project Name:  test
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: uart_rx
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module uart_rx_tb;

	// Inputs
	reg clk;
	reg rst_n;
	reg rx;

	// Outputs
	wire [9:0] data_out;

	// Instantiate the Unit Under Test (UUT)
	uart_rx uut (
		.clk(clk), 
		.rst_n(rst_n), 
		.rx(rx), 
		.data_out(data_out)
	);
always #1 clk = ~clk;
	initial begin
		// Initialize Inputs
		clk = 0;
		rst_n = 0;
		rx = 1;

		// Wait 100 ns for global reset to finish
		#20;
        
		// Add stimulus here
        rst_n = 1;
        
        #20;
        // Generate Start bit
        #50 rx = 1'b0;
        // 8 data bits
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        // Generate Stop bit
        #100 rx = 1'b1;
        
        #600;
        #100 rx = 1'b0;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
        #100 rx = 1'b1;
        #100 rx = 1'b0;
        #100 rx = 1'b1;
	end
      
endmodule

输出的波形为:

Verilog 实现 UART RX 接收器_第3张图片

后一段为:

Verilog 实现 UART RX 接收器_第4张图片

采样点为红色,均在数据中间,黄色为采样完成的信号;

 

你可能感兴趣的:(Verilog,HDL,Verilog,UART,RX)