Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

UART transmitter and receiver loopback

Verilog Bus Protocols

A UART transmitter sends three bytes over one wire to a UART receiver (8N1, 16 clocks per bit).

Open in IDE
design.v
// UART transmitter: 8 data bits, LSB first, optional parity, one stop bit.
//   CLKS_PER_BIT  clock cycles per bit (clock frequency / baud rate)
//   PARITY        0: none, 1: even, 2: odd
// A byte is handed over with valid/ready. cts_n low means the receiver may be sent to.
module uart_tx #(
  parameter CLKS_PER_BIT = 16,
  parameter PARITY       = 0
) (
  input            clk,
  input            rst,
  input      [7:0] data,
  input            valid,
  output           ready,
  input            cts_n,                // clear to send, active low; tie low if unused
  output reg       tx
);
  localparam IDLE = 3'd0, START = 3'd1, DATA = 3'd2, PAR = 3'd3, STOP = 3'd4;

  reg [2:0]  state;
  reg [15:0] cnt;                        // clock cycles within the current bit
  reg [2:0]  bit_idx;
  reg [7:0]  shreg;
  reg        par;                        // running parity of the data bits

  wire bit_done = (cnt == CLKS_PER_BIT - 1);

  assign ready = (state == IDLE) && !cts_n;

  always @(posedge clk) begin
    if (rst) begin
      state <= IDLE;
      tx    <= 1'b1;                     // the line idles high
      cnt   <= 16'd0;
    end
    else begin
      case (state)
        IDLE: begin
          tx <= 1'b1;
          if (valid && ready) begin
            shreg <= data;
            par   <= (PARITY == 2);       // odd parity starts at 1
            cnt   <= 16'd0;
            state <= START;
          end
        end
        START: begin
          tx <= 1'b0;                    // start bit
          if (bit_done) begin
            cnt     <= 16'd0;
            bit_idx <= 3'd0;
            state   <= DATA;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        DATA: begin
          tx <= shreg[0];                // least significant bit first
          if (bit_done) begin
            cnt   <= 16'd0;
            par   <= par ^ shreg[0];
            shreg <= shreg >> 1;
            if (bit_idx == 3'd7)
              state <= (PARITY != 0) ? PAR : STOP;
            else
              bit_idx <= bit_idx + 3'd1;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        PAR: begin
          tx <= par;                     // makes the count of ones even (or odd)
          if (bit_done) begin
            cnt   <= 16'd0;
            state <= STOP;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        default: begin                   // STOP
          tx <= 1'b1;                    // stop bit
          if (bit_done) begin
            cnt   <= 16'd0;
            state <= IDLE;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
      endcase
    end
  end
endmodule

// UART receiver: samples each bit in its middle.
//   It synchronizes rx with two flip-flops, waits for a falling edge (start bit), checks the start
//   bit again half a bit later, then samples every following bit one bit time apart.
//   frame_err: the stop bit was 0. parity_err: the parity bit did not match.
module uart_rx #(
  parameter CLKS_PER_BIT = 16,
  parameter PARITY       = 0
) (
  input            clk,
  input            rst,
  input            rx,
  output reg [7:0] data,
  output reg       valid,                // one-cycle pulse when a byte has been received
  output reg       frame_err,
  output reg       parity_err
);
  localparam IDLE = 3'd0, START = 3'd1, DATA = 3'd2, PAR = 3'd3, STOP = 3'd4;

  reg        rx_s1;
  reg        rx_s2;                      // rx after the two-flip-flop synchronizer
  reg [2:0]  state;
  reg [15:0] cnt;
  reg [2:0]  bit_idx;
  reg [7:0]  shreg;
  reg        par;

  always @(posedge clk) begin
    rx_s1 <= rx;
    rx_s2 <= rx_s1;
  end

  always @(posedge clk) begin
    if (rst) begin
      state      <= IDLE;
      valid      <= 1'b0;
      frame_err  <= 1'b0;
      parity_err <= 1'b0;
    end
    else begin
      valid <= 1'b0;
      case (state)
        IDLE: begin
          if (!rx_s2) begin              // falling edge: a start bit begins
            cnt   <= 16'd0;
            state <= START;
          end
        end
        START: begin
          if (cnt == CLKS_PER_BIT / 2 - 1) begin
            if (!rx_s2) begin            // still low in the middle: a real start bit
              cnt     <= 16'd0;
              bit_idx <= 3'd0;
              par     <= (PARITY == 2);
              state   <= DATA;
            end
            else begin
              state <= IDLE;             // a glitch, not a start bit
            end
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        DATA: begin
          if (cnt == CLKS_PER_BIT - 1) begin   // the middle of the next bit
            cnt   <= 16'd0;
            shreg <= {rx_s2, shreg[7:1]};      // LSB arrives first
            par   <= par ^ rx_s2;
            if (bit_idx == 3'd7)
              state <= (PARITY != 0) ? PAR : STOP;
            else
              bit_idx <= bit_idx + 3'd1;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        PAR: begin
          if (cnt == CLKS_PER_BIT - 1) begin
            cnt        <= 16'd0;
            parity_err <= (par != rx_s2);
            state      <= STOP;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
        default: begin                   // STOP
          if (cnt == CLKS_PER_BIT - 1) begin
            data      <= shreg;
            valid     <= 1'b1;
            frame_err <= !rx_s2;         // the stop bit must be 1
            state     <= IDLE;
          end
          else begin
            cnt <= cnt + 16'd1;
          end
        end
      endcase
    end
  end
endmodule
testbench.v
// Testbench: a UART transmitter sends bytes over one wire to a UART receiver
module tb;
  parameter TX_CLKS = 16;                // transmitter clock cycles per bit
  parameter RX_CLKS = 16;                // receiver clock cycles per bit
  parameter PARITY  = 0;                 // 0: none, 1: even, 2: odd

  reg        clk   = 0;
  reg        rst   = 1;
  reg  [7:0] data  = 0;
  reg        valid = 0;
  wire       ready;
  reg        cts_n = 0;                  // the receiver lets us send
  wire       tx;
  reg        flip  = 0;                  // a noise pulse on the line
  wire       line  = tx ^ flip;
  wire [7:0] rx_data;
  wire       rx_valid;
  wire       frame_err;
  wire       parity_err;

  uart_tx #(
    .CLKS_PER_BIT (TX_CLKS),
    .PARITY       (PARITY)
  ) utx (
    .clk   (clk),
    .rst   (rst),
    .data  (data),
    .valid (valid),
    .ready (ready),
    .cts_n (cts_n),
    .tx    (tx)
  );

  uart_rx #(
    .CLKS_PER_BIT (RX_CLKS),
    .PARITY       (PARITY)
  ) urx (
    .clk        (clk),
    .rst        (rst),
    .rx         (line),
    .data       (rx_data),
    .valid      (rx_valid),
    .frame_err  (frame_err),
    .parity_err (parity_err)
  );

  always #5 clk = ~clk;                  // 100 MHz: with 16 clocks per bit, 6.25 Mbaud

  task send(input [7:0] b);
    begin
      data  <= b;
      valid <= 1;
      @(posedge clk);
      while (!ready)
        @(posedge clk);
      valid <= 0;
      $display("%0t  tx sends 0x%h", $time, b);
    end
  endtask

  always @(posedge clk) begin
    if (rx_valid)
      $display("%0t  rx got   0x%h%0s%0s", $time, rx_data, frame_err ? "  FRAMING ERROR" : "", parity_err ? "  PARITY ERROR" : "");
  end

  integer n = 0;
  always @(posedge clk)
    if (rx_valid)
      n <= n + 1;

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, tb);
    #22 rst = 0;
    send(8'h4B);
    send(8'h55);
    send(8'hA3);
    wait (n == 3);
    #200 $finish;
  end
endmodule
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.