简单UART的verilog实现

下面摘录我写的简单的UART代码,对于灵活性和健壮性做了如下设计:

1、系统时钟及串口波特率以参数形式输入,例化时可以灵活设置

2、接受模块在起始位会检测中点电平是否仍然为低,否则判定为抖动

 

接收机代码


`timescale 1ns/1ps

// 系统时钟200MHz,波特率115200
module uart_rx  #(
    parameter BAUDRATE = 115200, 
    parameter FREQ = 200_000_000)(
    input clk, nrst,
    input rx,
    output reg [7:0] rdata,
    output reg vld
    );

    localparam T = FREQ / BAUDRATE;

    // flag接受处理标志位,为1表明当前处于接受状态
    reg flag;
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            flag <= 0; 
        else if(flag == 0 && rx == 0)
            flag <= 1;
        else if(cnt_bit == 1 - 1 && cnt_clk == T / 2 - 1 && rx == 1)
            flag <= 0;
        else if(end_cnt_bit)
            flag <= 0;
    end
    
    // 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位
    reg [3:0] cnt_bit;
    reg [31:0] cnt_clk;
    assign end_cnt_clk = cnt_clk == T - 1;
    assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1;
    
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt_clk <= 0;
        else if(flag) begin
            if(end_cnt_clk)
                cnt_clk <= 0;
            else
                cnt_clk <= cnt_clk + 1'b1;
        end
        else
            cnt_clk <= 0;
    end
    
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt_bit <= 0;
        else if(end_cnt_clk) begin
            if(end_cnt_bit)
                cnt_bit <= 0;
            else
                cnt_bit <= cnt_bit + 1'b1;
        end
    end    
    
    // 读数据及数据有效指示信号
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            rdata <= 0;
        else if(cnt_clk == T / 2 - 1 && cnt_bit != 1 - 1 && cnt_bit != 10 - 1)
            rdata[cnt_bit - 1] <= rx;
    end
    
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            vld <= 0;
        else if(end_cnt_bit)
            vld <= 1;
        else
            vld <= 0;
    end
    
endmodule

发送机代码

`timescale 1ns/1ps

// 系统时钟200MHz,波特率115200,带忙闲指示信号rdy
module uart_tx #(
    parameter BAUDRATE = 115200, 
    parameter FREQ = 200_000_000)(
    input clk, nrst,
    input wrreq,
    input [7:0] wdata,
    output reg tx,
    output reg rdy
    );
    
    reg [3:0] cnt_bit;
    reg [31:0] cnt_clk;
    
    localparam T = FREQ / BAUDRATE;

    // 有写请求时将rdy信号拉底,待到数据发送完毕再将信号拉
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            rdy <= 1;
        else if(wrreq)
            rdy <= 0;
        else if(end_cnt_bit)
            rdy <= 1;
    end
    
    // 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位
    wire end_cnt_clk;
    wire end_cnt_bit;
    assign end_cnt_clk = cnt_clk == T - 1;
    assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1;
    
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt_clk <= 0;
        else if(rdy == 0) begin
            if(end_cnt_clk)
                cnt_clk <= 0;
            else
                cnt_clk <= cnt_clk + 1'b1;
        end
    end
    
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt_bit <= 0;
        else if(end_cnt_clk) begin
            if(end_cnt_bit)
                cnt_bit <= 0;
            else
                cnt_bit <= cnt_bit + 1'b1;
        end
    end
    
    // 先发送一个起始位0,然后8位数据位,最后是停止位1
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            tx <= 1;
        else if(rdy == 0 && cnt_clk == 0) begin
            if(cnt_bit == 1 - 1)
                tx <= 0;
            else if(cnt_bit == 10 - 1)
                tx <= 1;
            else
                tx <= wdata[cnt_bit - 1];
        end
    end
    
endmodule

在Xilinx Artix-7平台上验证的顶层代码

`timescale 1ns / 1ps

module uart_top(
    input clk_p, clk_n, nrst,
    input rx,
    output tx
    );
    
    localparam BAUDRATE = 115200;
    localparam FREQ = 200_000_000;
    
    // 差分时钟信号转为单端信号
    IBUFGDS #(
        .DIFF_TERM("FALSE"),
        .IBUF_LOW_PWR("TRUE"),
        .IOSTANDARD("DEFAULT")
    )  IBUFGDS_inst(
        .O(clk),
        .I(clk_p),
        .IB(clk_n)
    );
    
    wire [7:0] data;
    wire vld;
    
    uart_rx #(BAUDRATE, FREQ) uart_rx_u(
    .clk    (clk    ),
    .nrst    (nrst    ),
    .rx        (rx        ),
    .rdata    (data    ),
    .vld    (vld    )
    );
    
    uart_tx #(BAUDRATE, FREQ) uart_tx_u(
    .clk    (clk    ),
    .nrst    (nrst    ),
    .wrreq    (vld    ),
    .wdata    (data    ),
    .tx        (tx        ),
    .rdy    (        )
    );
    
    ila_0 ila_0_u(
    .clk     (clk    ), 
    .probe0  (nrst   ),
    .probe1  ({tx,rx}),
    .probe2  (data   ),
    .probe3  (vld    )
    );
    
endmodule

你可能感兴趣的:(简单UART的verilog实现)