Search code examples
veriloguartvivado

UART Transmit and receive data does not start (Vivado)


I can't figure out why is it that when I set the clock frequency from 50MHz to 100MHz, by changing the clk period to 5 in the testbench, my output transmit and receive data stays at 0. Can anyone enlighten me on this? I need my clock frequency to be 100MHz. Your help will be much appreciated.

Testbench

`timescale 1ns / 1ps
 
 
module uart_tx_test();
parameter       periodCLK_2     = 5;

parameter       perioddump      = 10;
parameter       delay           = 1;
parameter       delay_in        = 2;

reg           CLK_TB  = 0       ;
reg           RSTN           ;
reg [7:0] data = 0;
reg clk = 0;
reg enable = 0;

wire tx_busy;
wire rdy;
wire [7:0] rxdata;

wire loopback;
reg rdy_clr = 0;

uart test_uart(.din(data),
           .wr_en(enable),
           .clk_50m(clk),
           .tx(loopback),
           .tx_busy(tx_busy),
           .rx(loopback),
           .rdy(rdy),
           .rdy_clr(rdy_clr),
           .dout(rxdata));

initial begin
    // $dumpfile("uart.vcd");
    $dumpvars(0, uart_tx_test);
    enable <= 1'b1;
    #2 enable <= 1'b0;
end

always begin
    #5 clk = ~clk; //I set period to 5; period was 1 previously.
end 

always @(posedge rdy) begin
    #2 rdy_clr <= 1;
    #2 rdy_clr <= 0;
    if (rxdata != data) begin
        $display("FAIL: rx data %x does not match tx %x", rxdata, data);
        $finish;
    end else begin
        if (rxdata == 8'hff) begin
            $display("SUCCESS: all bytes verified");
            $finish;
        end
        data <= data + 1'b1;
        enable <= 1'b1;
        #2 enable <= 1'b0;
    end
end

endmodule

Design Sources

module uart(
    input wire [7:0] din,
    input wire wr_en,
    input wire clk_50m,
    output wire tx,
    output wire tx_busy,

    input wire rx,
    input wire rdy_clr,
    output wire rdy,
    output wire [7:0] dout
);

wire rxclk_en, txclk_en;

baud_rate_gen uart_baud(
    .clk_50m(clk_50m),
    .rxclk_en(rxclk_en),
    .txclk_en(txclk_en)
);

transmitter uart_tx(
    .tx(tx),
    .din(din),
    .clk_50m(clk_50m),
    .clken(txclk_en),
    .wr_en(wr_en),
    .tx_busy(tx_busy)
);

receiver uart_rx(
    .rx(rx),
    .data(dout),
    .clk_50m(clk_50m),
    .clken(rxclk_en),
    .rdy(rdy),
    .rdy_clr(rdy_clr)
);

endmodule
/*
 * Hacky baud rate generator to divide a 50MHz clock into a 9600 baud
 * rx/tx pair where the rx clcken oversamples by 16x.
 */
module baud_rate_gen(input wire clk_50m,
             output wire rxclk_en,
             output wire txclk_en);

parameter RX_ACC_MAX = 100000000 / (9600 * 16);
parameter TX_ACC_MAX = 100000000 / 9600;
parameter RX_ACC_WIDTH = $clog2(RX_ACC_MAX);
parameter TX_ACC_WIDTH = $clog2(TX_ACC_MAX);
reg [RX_ACC_WIDTH - 1:0] rx_acc = 0;
reg [TX_ACC_WIDTH - 1:0] tx_acc = 0;

assign rxclk_en = (rx_acc == 5'd0);
assign txclk_en = (tx_acc == 9'd0);

always @(posedge clk_50m) begin
    if (rx_acc == RX_ACC_MAX[RX_ACC_WIDTH - 1:0])
        rx_acc <= 0;
    else
        rx_acc <= rx_acc + 5'b1;
end

always @(posedge clk_50m) begin
    if (tx_acc == TX_ACC_MAX[TX_ACC_WIDTH - 1:0])
        tx_acc <= 0;
    else
        tx_acc <= tx_acc + 9'b1;
end

endmodule
module transmitter(
    input wire [7:0] din,
    input wire wr_en,
    input wire clk_50m,
    input wire clken,
    output reg tx,
    output wire tx_busy
);

initial begin
     tx = 1'b1;
end

parameter STATE_IDLE    = 2'b00;
parameter STATE_START   = 2'b01;
parameter STATE_DATA    = 2'b10;
parameter STATE_STOP    = 2'b11;

reg [7:0] data = 8'h00;
reg [2:0] bitpos = 3'h0;
reg [1:0] state = STATE_IDLE;

always @(posedge clk_50m) begin
    case (state)
    STATE_IDLE: begin
        if (wr_en) begin
            state <= STATE_START;
            data <= din;
            bitpos <= 3'h0;
        end
    end
    STATE_START: begin
        if (clken) begin
            tx <= 1'b0;
            state <= STATE_DATA;
        end
    end
    STATE_DATA: begin
        if (clken) begin
            if (bitpos == 3'h7)
                state <= STATE_STOP;
            else
                bitpos <= bitpos + 3'h1;
            tx <= data[bitpos];
        end
    end
    STATE_STOP: begin
        if (clken) begin
            tx <= 1'b1;
            state <= STATE_IDLE;
        end
    end
    default: begin
        tx <= 1'b1;
        state <= STATE_IDLE;
    end
    endcase
end

assign tx_busy = (state != STATE_IDLE);

endmodule
module receiver(
    input wire rx,
    input wire rdy_clr,
    input wire clk_50m,
    input wire clken,
    output reg rdy,
    output reg [7:0] data
);

initial begin
    rdy = 0;
    data = 8'b0;
end

parameter RX_STATE_START    = 2'b00;
parameter RX_STATE_DATA     = 2'b01;
parameter RX_STATE_STOP     = 2'b10;

reg [1:0] state = RX_STATE_START;
reg [3:0] sample = 0;
reg [3:0] bitpos = 0;
reg [7:0] scratch = 8'b0;

always @(posedge clk_50m) begin
    if (rdy_clr)
        rdy <= 0;

    if (clken) begin
        case (state)
        RX_STATE_START: begin
            /*
            * Start counting from the first low sample, once we've
            * sampled a full bit, start collecting data bits.
            */
            if (!rx || sample != 0)
                sample <= sample + 4'b1;

            if (sample == 15) begin
                state <= RX_STATE_DATA;
                bitpos <= 0;
                sample <= 0;
                scratch <= 0;
            end
        end
        RX_STATE_DATA: begin
            sample <= sample + 4'b1;
            if (sample == 4'h8) begin
                scratch[bitpos[2:0]] <= rx;
                bitpos <= bitpos + 4'b1;
            end
            if (bitpos == 8 && sample == 15)
                state <= RX_STATE_STOP;
        end
        RX_STATE_STOP: begin
            /*
             * The baud clock may not be running at exactly the
             * same rate as the transmitter.  If we thing that
             * we're at least half way into the stop bit, allow
             * transition into handling the next start bit.
             */
            if (sample == 15 || (sample >= 8 && !rx)) begin
                state <= RX_STATE_START;
                data <= scratch;
                rdy <= 1'b1;
                sample <= 0;
            end else begin
                sample <= sample + 4'b1;
            end
        end
        default: begin
            state <= RX_STATE_START;
        end
        endcase
    end
end

endmodule




Solution

  • You need to scale all your other delays accordingly. Change all your #2 to #10, then you will see the SUCCESS: all bytes verified message.

    With your original clock delay of #1, your other input signal pulses (enable and rdy_clr) were wide enough for your uart design module to sample properly. For example, on the 1st posedge of clk, your design properly sampled the enable input as 1, which started the TX state machine.

    You increased the clock period by a factor of 5 when you changed the delay from #1 to #5. However, your enable pulse stayed the same width as before, which means that the design sampled enable as 0, not 1. So your TX state machine stayed in the IDLE state. By changing the enable delay from #2 to #10, you are able to properly sample enable as 1.

    You can easily prove this to yourself by dumping a VCD file, and viewing the waveforms inside the design.


    You could replace the numeric delays with a parameter to make it easier to change to different frequencies.


    Note: You stated the clk delay was originally #1. This gives the clk signal a period of 2ns, which is 500MHz, not 50MHz.