Add UART transmit module
I join everyone and their mother in creating my own UART. 8n1 only, and 2 baud rates. Accepts AXI-stream. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
81de945030
commit
e44d381c20
1
Makefile
1
Makefile
|
@ -142,6 +142,7 @@ MODULES += phy_core
|
||||||
MODULES += pmd_dp83223
|
MODULES += pmd_dp83223
|
||||||
MODULES += pmd_dp83223_rx
|
MODULES += pmd_dp83223_rx
|
||||||
MODULES += scramble
|
MODULES += scramble
|
||||||
|
MODULES += uart_tx
|
||||||
MODULES += wb_mux
|
MODULES += wb_mux
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
|
@ -200,6 +200,11 @@ nebulously-specified, and limited-in-number iCE40 PLLs.
|
||||||
This module implements a scrambler as described in ANSI X3.264-1995 section
|
This module implements a scrambler as described in ANSI X3.264-1995 section
|
||||||
7.1.1.
|
7.1.1.
|
||||||
|
|
||||||
|
=== `uart_tx`
|
||||||
|
|
||||||
|
A standard UART transmit module, accepting AXI-stream. 8n1 only. Supports
|
||||||
|
115,200 and 4,000,000 baud.
|
||||||
|
|
||||||
=== `wb_mux`
|
=== `wb_mux`
|
||||||
|
|
||||||
This implements a simple Wishbone mux, allowing a single master to access
|
This implements a simple Wishbone mux, allowing a single master to access
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// 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_tx (
|
||||||
|
input clk,
|
||||||
|
|
||||||
|
input [7:0] data,
|
||||||
|
output reg ready,
|
||||||
|
input valid,
|
||||||
|
|
||||||
|
output reg tx,
|
||||||
|
|
||||||
|
/* Run at 4M for testing */
|
||||||
|
input high_speed
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 1085 cycles, for 115200 baud with a 125 MHz clock */
|
||||||
|
parameter SLOW_VALUE = 11'h78c;
|
||||||
|
/* 31 cycles, for 4M baud with a 125 MHz clock */
|
||||||
|
parameter FAST_VALUE = 11'h68e;
|
||||||
|
|
||||||
|
reg [7:0] data_last;
|
||||||
|
reg valid_last, ready_next;
|
||||||
|
reg [10:0] lfsr, lfsr_next;
|
||||||
|
reg [3:0] counter, counter_next;
|
||||||
|
reg [8:0] bits, bits_next;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
ready = 1'b1;
|
||||||
|
valid_last = 1'b0;
|
||||||
|
bits = 9'h1ff;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
tx = bits[0];
|
||||||
|
|
||||||
|
ready_next = ready;
|
||||||
|
counter_next = counter;
|
||||||
|
lfsr_next = { lfsr[9:0], lfsr[10] ^ lfsr[8] };
|
||||||
|
bits_next = bits;
|
||||||
|
|
||||||
|
if (&lfsr) begin
|
||||||
|
if (counter)
|
||||||
|
counter_next = counter - 1;
|
||||||
|
else
|
||||||
|
ready_next = 1;
|
||||||
|
lfsr_next = high_speed ? FAST_VALUE : SLOW_VALUE;
|
||||||
|
bits_next = { 1'b1, bits[8:1] };
|
||||||
|
end
|
||||||
|
|
||||||
|
if (valid_last && ready) begin
|
||||||
|
ready_next = 0;
|
||||||
|
counter_next = 9;
|
||||||
|
lfsr_next = high_speed ? FAST_VALUE : SLOW_VALUE;
|
||||||
|
bits_next = { data_last, 1'b0 };
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
data_last <= data;
|
||||||
|
ready <= ready_next;
|
||||||
|
valid_last <= valid;
|
||||||
|
counter <= counter_next;
|
||||||
|
lfsr <= lfsr_next;
|
||||||
|
bits <= bits_next;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
|
@ -25,7 +25,8 @@ async def send_packet(signals, packet, ratio=1, last_extra=0):
|
||||||
else:
|
else:
|
||||||
signals['data'].value = val
|
signals['data'].value = val
|
||||||
signals['valid'].value = 1
|
signals['valid'].value = 1
|
||||||
signals['last'].value = last
|
if 'last' in signals:
|
||||||
|
signals['last'].value = last
|
||||||
await RisingEdge(signals['clk'])
|
await RisingEdge(signals['clk'])
|
||||||
while True:
|
while True:
|
||||||
await FallingEdge(signals['clk'])
|
await FallingEdge(signals['clk'])
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
# 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 send_packet
|
||||||
|
|
||||||
|
BAUD = 4e6
|
||||||
|
BIT_STEPS = get_sim_steps(1 / BAUD, 'sec', round_mode='round')
|
||||||
|
|
||||||
|
@cocotb.test(timeout_time=1, timeout_unit='ms')
|
||||||
|
async def test_tx(uart):
|
||||||
|
uart.clk.value = BinaryValue('Z')
|
||||||
|
uart.valid.value = 0
|
||||||
|
uart.high_speed.value = BAUD == 4e6
|
||||||
|
|
||||||
|
await Timer(1)
|
||||||
|
await cocotb.start(Clock(uart.clk, 8, units='ns').start())
|
||||||
|
await FallingEdge(uart.clk)
|
||||||
|
|
||||||
|
msg = b"Hello"
|
||||||
|
|
||||||
|
await cocotb.start(send_packet({
|
||||||
|
'clk': uart.clk,
|
||||||
|
'data': uart.data,
|
||||||
|
'valid': uart.valid,
|
||||||
|
'ready': uart.ready,
|
||||||
|
}, msg))
|
||||||
|
|
||||||
|
async def getchar():
|
||||||
|
while not uart.tx.value:
|
||||||
|
await FallingEdge(uart.clk)
|
||||||
|
while uart.tx.value:
|
||||||
|
await FallingEdge(uart.clk)
|
||||||
|
await Timer(BIT_STEPS // 2)
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for _ in range(8):
|
||||||
|
await Timer(BIT_STEPS)
|
||||||
|
result >>= 1
|
||||||
|
result |= 0x80 if uart.tx.value else 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
then = get_sim_time()
|
||||||
|
for c in msg:
|
||||||
|
assert c == await getchar()
|
||||||
|
now = get_sim_time()
|
||||||
|
|
||||||
|
expected = BIT_STEPS * (10 * len(msg) - 1.5)
|
||||||
|
actual = now - then
|
||||||
|
assert abs(actual - expected) / expected < 0.01
|
Loading…
Reference in New Issue