Add UART receive module
Add the recieve half of the UART. It's more or less the inverse of the transmit half, except we manage the state explicitly. I originally did this in hopes that yosys would recode the FSM, but it doesn't like the subtraction in the D* states. I left in the async reset anyway since it reduces the LUT count. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
3f61f85a1f
commit
a549fca957
1
Makefile
1
Makefile
|
@ -143,6 +143,7 @@ MODULES += pmd_dp83223
|
|||
MODULES += pmd_dp83223_rx
|
||||
MODULES += scramble
|
||||
MODULES += uart_tx
|
||||
MODULES += uart_rx
|
||||
MODULES += wb_mux
|
||||
|
||||
.PHONY: test
|
||||
|
|
|
@ -205,6 +205,12 @@ This module implements a scrambler as described in ANSI X3.264-1995 section
|
|||
A standard UART transmit module, accepting AXI-stream. 8n1 only. Supports
|
||||
115,200 and 4,000,000 baud.
|
||||
|
||||
=== `uart_rx`
|
||||
|
||||
A standard UART receive module, outputting AXI-stream. 8n1 only. Supports
|
||||
115,200 and 4,000,000 baud. Properly detects breaks as (single) frame errors,
|
||||
and ignores runt start bits.
|
||||
|
||||
=== `wb_mux`
|
||||
|
||||
This implements a simple Wishbone mux, allowing a single master to access
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-Only
|
||||
/*
|
||||
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
|
||||
*
|
||||
* 8n1@115200; no one uses anything else (and neither do I)
|
||||
*/
|
||||
|
||||
`include "common.vh"
|
||||
|
||||
module uart_rx (
|
||||
input clk,
|
||||
input rst,
|
||||
|
||||
output reg [7:0] data,
|
||||
input ready,
|
||||
output reg valid,
|
||||
|
||||
input rx,
|
||||
|
||||
/* Run at 4M for testing */
|
||||
input high_speed,
|
||||
/* No one ready */
|
||||
output reg overflow,
|
||||
/* Missing stop bit */
|
||||
output reg frame_error
|
||||
);
|
||||
|
||||
/* 1085 cycles, for 115200 baud with a 125 MHz clock */
|
||||
parameter SLOW_FULL = 11'h78c;
|
||||
parameter SLOW_HALF = 11'h202;
|
||||
/* 31 cycles, for 4M baud with a 125 MHz clock */
|
||||
parameter FAST_FULL = 11'h68e;
|
||||
parameter FAST_HALF = 11'h34c;
|
||||
|
||||
localparam ERROR = 11;
|
||||
localparam IDLE = 10;
|
||||
localparam START = 9;
|
||||
localparam D0 = 8;
|
||||
localparam D1 = 7;
|
||||
localparam D2 = 6;
|
||||
localparam D3 = 5;
|
||||
localparam D4 = 4;
|
||||
localparam D5 = 3;
|
||||
localparam D6 = 2;
|
||||
localparam D7 = 1;
|
||||
localparam STOP = 0;
|
||||
|
||||
reg [3:0] state, state_next;
|
||||
reg [10:0] lfsr, lfsr_next;
|
||||
reg [7:0] bits, bits_next, data_next;
|
||||
reg ready_last;
|
||||
reg valid_next, overflow_next, frame_error_next;
|
||||
|
||||
always @(*) begin
|
||||
state_next = state;
|
||||
lfsr_next = { lfsr[9:0], lfsr[10] ^ lfsr[8] };
|
||||
bits_next = bits;
|
||||
data_next = data;
|
||||
valid_next = valid && !ready_last;
|
||||
overflow_next = 0;
|
||||
frame_error_next = 0;
|
||||
|
||||
case (state)
|
||||
IDLE: if (!rx) begin
|
||||
state_next = START;
|
||||
lfsr_next = high_speed ? FAST_HALF : SLOW_HALF;
|
||||
end
|
||||
START: if (&lfsr) begin
|
||||
state_next = rx ? IDLE : D0;
|
||||
lfsr_next = high_speed ? FAST_FULL : SLOW_FULL;
|
||||
end
|
||||
D0, D1, D2, D3, D4, D5, D6, D7: if (&lfsr) begin
|
||||
lfsr_next = high_speed ? FAST_FULL : SLOW_FULL;
|
||||
bits_next = { rx, bits[7:1] };
|
||||
state_next = state - 1;
|
||||
end
|
||||
STOP: if (&lfsr) begin
|
||||
lfsr_next = high_speed ? FAST_FULL : SLOW_FULL;
|
||||
if (rx) begin
|
||||
state_next = IDLE;
|
||||
if (valid_next)
|
||||
overflow_next = 1;
|
||||
else
|
||||
data_next = bits;
|
||||
valid_next = 1;
|
||||
end else begin
|
||||
frame_error_next = 1;
|
||||
state_next = ERROR;
|
||||
end
|
||||
end
|
||||
ERROR: if (&lfsr) begin
|
||||
lfsr_next = high_speed ? FAST_FULL : SLOW_FULL;
|
||||
if (rx)
|
||||
state_next = IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
lfsr <= lfsr_next;
|
||||
bits <= bits_next;
|
||||
data <= data_next;
|
||||
end
|
||||
|
||||
always @(posedge clk, posedge rst) begin
|
||||
if (rst) begin
|
||||
state <= IDLE;
|
||||
valid <= 0;
|
||||
ready_last <= 0;
|
||||
overflow <= 0;
|
||||
frame_error <= 0;
|
||||
end else begin
|
||||
state <= state_next;
|
||||
valid <= valid_next;
|
||||
ready_last <= ready;
|
||||
overflow <= overflow_next;
|
||||
frame_error <= frame_error_next;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,80 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-Only
|
||||
# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
|
||||
|
||||
import cocotb
|
||||
from cocotb.binary import BinaryValue
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import FallingEdge, Timer
|
||||
from cocotb.utils import get_sim_time, get_sim_steps
|
||||
|
||||
from .axis_replay_buffer import recv_packet
|
||||
|
||||
BAUD = 4e6
|
||||
BIT_STEPS = get_sim_steps(1 / BAUD, 'sec', round_mode='round')
|
||||
|
||||
def as_bits(c):
|
||||
for _ in range(8):
|
||||
yield c & 1
|
||||
c >>= 1
|
||||
|
||||
@cocotb.test(timeout_time=1, timeout_unit='ms')
|
||||
async def test_rx(uart):
|
||||
uart.clk.value = BinaryValue('Z')
|
||||
uart.rst.value = 1
|
||||
uart.ready.value = 1
|
||||
uart.rx.value = 1
|
||||
uart.high_speed.value = 1
|
||||
|
||||
await Timer(1)
|
||||
uart.rst.value = 0
|
||||
await cocotb.start(Clock(uart.clk, 8, units='ns').start())
|
||||
await FallingEdge(uart.clk)
|
||||
|
||||
async def putchar(c):
|
||||
for bit in (0, *as_bits(c), 1):
|
||||
uart.rx.value = bit
|
||||
await Timer(BIT_STEPS)
|
||||
|
||||
msg = b"Hell\0"
|
||||
signals = {
|
||||
'clk': uart.clk,
|
||||
'data': uart.data,
|
||||
'valid': uart.valid,
|
||||
'ready': uart.ready,
|
||||
}
|
||||
|
||||
await cocotb.start(recv_packet(signals, msg))
|
||||
for c in msg:
|
||||
await putchar(c)
|
||||
|
||||
overflows = 0
|
||||
frame_errors = 0
|
||||
|
||||
async def count_errors():
|
||||
nonlocal overflows
|
||||
nonlocal frame_errors
|
||||
while True:
|
||||
await FallingEdge(uart.clk)
|
||||
overflows += uart.overflow.value
|
||||
frame_errors += uart.frame_error.value
|
||||
|
||||
monitor = await cocotb.start(count_errors())
|
||||
|
||||
uart.rx.value = 0
|
||||
await Timer(BIT_STEPS * 20)
|
||||
|
||||
uart.rx.value = 1
|
||||
await Timer(BIT_STEPS)
|
||||
|
||||
assert frame_errors == 1
|
||||
|
||||
uart.ready.value = 0
|
||||
await putchar(0xFF)
|
||||
await putchar(0)
|
||||
|
||||
assert overflows == 1
|
||||
|
||||
uart.ready.value = 1
|
||||
await recv_packet(signals, (0xFF,))
|
||||
|
||||
monitor.kill()
|
Loading…
Reference in New Issue