Skip to content

uart_rx

Source: Verilog/fpga/tang-nano-20k/sdram-test/src/uart_rx.v

Hierarchy: not instantiated by any of the 9 build tops (elaborated by yosys).

Module hierarchy - All modules

uart_rx symbol

Schematic

Drawn from the Verilog: no build top uses this module, so it was elaborated from its own file with no defines and default parameters. Sub-modules are boxes (click the picture to open it full size; there every sub-module box links to its page, and every wire shows its Verilog name).

uart_rx schematic

Description

UART receiver (8N1) RX state machine borrowed from the ND-120 SC2661 EPCI model (Verilog/Shared/support/SC2661_UART.v), with the EPCI register interface stripped away. Half-bit start alignment, mid-bit sampling. Last reviewed: 8-JUL-2026 Ronny Hansen

Parameters

Parameter Default
DELAY_FRAMES 234

Ports

Direction Width Name Description
input 1 clk
input 1 rst_n (active low)
input 1 rxd
output [7:0] rx_data received byte, valid while rx_valid is high
output 1 rx_valid 1-cycle pulse per received byte

Verilog source

Verilog/fpga/tang-nano-20k/sdram-test/src/uart_rx.v on GitHub.

Show the Verilog of uart_rx (96 lines)
/****************************************************************************
** UART receiver (8N1)                                                     **
**                                                                         **
** RX state machine borrowed from the ND-120 SC2661 EPCI model             **
** (Verilog/Shared/support/SC2661_UART.v), with the EPCI register          **
** interface stripped away. Half-bit start alignment, mid-bit sampling.    **
**                                                                         **
** Last reviewed: 8-JUL-2026                                               **
** Ronny Hansen                                                            **
*****************************************************************************/

module uart_rx #(
    // Clock cycles per bit. 27 MHz / 115200 baud = 234 (-0.16% error)
    parameter DELAY_FRAMES = 234
) (
    input clk,
    input rst_n,

    input rxd,

    output reg [7:0] rx_data,  // received byte, valid while rx_valid is high
    output reg       rx_valid  // 1-cycle pulse per received byte
);

  localparam HALF_DELAY_WAIT = (DELAY_FRAMES >> 1);

  localparam RX_STATE_IDLE      = 3'b000;
  localparam RX_STATE_START_BIT = 3'b001;
  localparam RX_STATE_READ_WAIT = 3'b010;
  localparam RX_STATE_READ      = 3'b011;
  localparam RX_STATE_STOP_BIT  = 3'b101;

  // Two-stage synchronizer - rxd is asynchronous to clk
  reg rxd_meta, rxd_sync;
  always @(posedge clk) begin
    rxd_meta <= rxd;
    rxd_sync <= rxd_meta;
  end

  reg [ 2:0] rxState;
  reg [31:0] rxCounter;
  reg [ 2:0] rxBitNumber;

  always @(posedge clk) begin
    if (!rst_n) begin
      rxState     <= RX_STATE_IDLE;
      rxCounter   <= 0;
      rxBitNumber <= 0;
      rx_data     <= 8'b0;
      rx_valid    <= 1'b0;
    end else begin
      rx_valid <= 1'b0;
      case (rxState)
        RX_STATE_IDLE: begin
          if (rxd_sync == 1'b0) begin  // start bit edge
            rxState     <= RX_STATE_START_BIT;
            rxCounter   <= 1;
            rxBitNumber <= 0;
          end
        end
        RX_STATE_START_BIT: begin
          if (rxCounter == HALF_DELAY_WAIT) begin
            rxState   <= RX_STATE_READ_WAIT;
            rxCounter <= 1;
          end else rxCounter <= rxCounter + 1;
        end
        RX_STATE_READ_WAIT: begin
          rxCounter <= rxCounter + 1;
          if ((rxCounter + 1) == DELAY_FRAMES) begin
            rxState <= RX_STATE_READ;
          end
        end
        RX_STATE_READ: begin
          rxCounter <= 1;
          rx_data   <= {rxd_sync, rx_data[7:1]};  // shift in, LSB first
          rxBitNumber <= rxBitNumber + 1;
          if (rxBitNumber == 3'b111) begin
            rxState <= RX_STATE_STOP_BIT;
          end else begin
            rxState <= RX_STATE_READ_WAIT;
          end
        end
        RX_STATE_STOP_BIT: begin
          rxCounter <= rxCounter + 1;
          if ((rxCounter + 1) == DELAY_FRAMES) begin
            rxState   <= RX_STATE_IDLE;
            rxCounter <= 0;
            rx_valid  <= 1'b1;
          end
        end
        default: rxState <= RX_STATE_IDLE;
      endcase
    end
  end

endmodule