Communication
UART receiver
uart_rx
Serial receiver for 8 data bits, no parity, and 1 stop bit. The line passes a two-flop synchronizer and each bit is sampled in its middle. valid pulses for one cycle with the byte; framing_error pulses instead when the stop bit is low.
Instantiate it
Paste into your design and connect the signals.
uart_rx #(.CLKS_PER_BIT(868)) u_uart_rx (
.clk(clk), .rst(rst),
.rx(uart_rx_pin),
.data(received_byte), .valid(byte_valid), .framing_error(rx_error)
);Parameters
| Name | Default | Meaning |
|---|---|---|
| CLKS_PER_BIT | 868 | Clock cycles per bit: clock frequency divided by baud rate. |
Ports
| Name | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | Clock; everything changes on its rising edge. |
| rst | input | 1 | Synchronous reset, active high. |
| rx | input | 1 | Serial line (asynchronous; synchronized inside). |
| data | output | 8 | The last byte received. |
| valid | output | 1 | Pulses for one cycle when data holds a new byte. |
| framing_error | output | 1 | Pulses when a byte ended with a low stop bit. |
Add it to a project
It is copied to ip/uart_rx/uart_rx.sv with a README (ip/uart_rx/README.md). When your design needs it, the Architect (new designs) or the Architect (changes) suggests it and asks before using it. Or add it yourself:
- In the workspace: the Tapeo Hub button in the project header, then Add to project.
- In a terminal (in the project folder):
$ tapeo hub add uart_rx - In VS Code: Tapeo: Add from Tapeo Hub in the Command Palette.
How it is verified
Before every release Tapeo checks this block: its self-checking testbench passes in Icarus Verilog, Verilator -Wall reports nothing, and Yosys synthesizes it (187 generic cells at the default parameters). That shows it works for the tested cases; your copy is part of your project, and your own testbench checks it in your design.
Source
// UART receiver, 8N1, LSB first. The input passes a two-flop synchronizer and
// each bit is sampled in its middle. `valid` pulses for one cycle with `data`;
// `framing_error` pulses instead when the stop bit is low. Tapeo Hub (verified by Tapeo).
module uart_rx #(
parameter integer CLKS_PER_BIT = 868 // clock frequency / baud rate
) (
input logic clk,
input logic rst,
input logic rx,
output logic [7:0] data,
output logic valid,
output logic framing_error
);
localparam integer COUNT_WIDTH = (CLKS_PER_BIT > 1) ? $clog2(CLKS_PER_BIT) : 1;
localparam logic [COUNT_WIDTH-1:0] LAST_TICK = COUNT_WIDTH'(CLKS_PER_BIT - 1);
localparam logic [COUNT_WIDTH-1:0] HALF_TICK = COUNT_WIDTH'((CLKS_PER_BIT - 1) / 2);
typedef enum logic [1:0] { IDLE, START, DATA, STOP } state_t;
state_t state;
logic [1:0] sync;
logic [COUNT_WIDTH-1:0] ticks;
logic [2:0] bit_index;
logic [7:0] shift;
wire line = sync[1];
always_ff @(posedge clk) begin
if (rst) sync <= 2'b11;
else sync <= {sync[0], rx};
end
always_ff @(posedge clk) begin
if (rst) begin
state <= IDLE;
ticks <= '0;
bit_index <= '0;
shift <= '0;
data <= '0;
valid <= 1'b0;
framing_error <= 1'b0;
end else begin
valid <= 1'b0;
framing_error <= 1'b0;
case (state)
IDLE: if (!line) begin ticks <= '0; state <= START; end
START: begin
if (ticks == HALF_TICK) begin
ticks <= '0;
bit_index <= '0;
state <= line ? IDLE : DATA; // a glitch, not a start bit
end else ticks <= ticks + 1'b1;
end
DATA: begin
if (ticks == LAST_TICK) begin
ticks <= '0;
shift <= {line, shift[7:1]};
if (bit_index == 3'd7) state <= STOP;
else bit_index <= bit_index + 1'b1;
end else ticks <= ticks + 1'b1;
end
STOP: begin
if (ticks == LAST_TICK) begin
ticks <= '0;
state <= IDLE;
if (line) begin data <= shift; valid <= 1'b1; end
else framing_error <= 1'b1;
end else ticks <= ticks + 1'b1;
end
default: state <= IDLE;
endcase
end
end
endmodule