Add UART-WIshbone bridge

Add a basic bridge for debugging. It's around 50% efficient, but this
could be increased to 66% with the addition of some FIFOs. The limiting
factor is the constant overhead of the request/status bytes. If we used
a wider bus, we could get better efficiency.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2023-03-05 14:59:24 -05:00
parent 2c2527e8d9
commit 5f331a403c
4 changed files with 170 additions and 0 deletions

View File

@ -149,6 +149,7 @@ MODULES += pmd_dp83223_rx
MODULES += scramble
MODULES += uart_tx
MODULES += uart_rx
MODULES += uart_wb_bridge
MODULES += wb_mux
.PHONY: test

View File

@ -220,6 +220,15 @@ 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.
=== `uart_wb_bridge`
This module combines the above UART cores with the AXI-stream bridge from before
to allow controlling a Wishbone bus over a UART. There is no internal buffering,
but some FIFOs could easily be added to allow more in-flight transactions. At
the moment this only supports 16 data busses with 16-bit granularity. Frame
errors (breaks) reset the bridge (but not the UARTs), providing an "`out of
band`" ability for synchronization.
=== `wb_mux`
This implements a simple Wishbone mux, allowing a single master to access

81
rtl/uart_wb_bridge.v Normal file
View File

@ -0,0 +1,81 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
*/
`include "common.vh"
module uart_wb_bridge (
input clk,
input rst,
/* UART */
input rx,
output tx,
/* Wishbone */
output wb_rst,
input wb_ack, wb_err,
output wb_cyc, wb_stb, wb_we,
output [ADDR_WIDTH - 1:0] wb_addr,
output [DATA_WIDTH - 1:0] wb_data_write,
input [DATA_WIDTH - 1:0] wb_data_read,
input high_speed
);
parameter ADDR_WIDTH = 16;
localparam DATA_WIDTH = 16;
wire rx_ready, rx_valid;
wire [7:0] rx_data;
wire overflow;
uart_rx uart_rx (
.clk(clk),
.rst(rst),
.rx(rx),
.ready(rx_ready),
.valid(rx_valid),
.data(rx_data),
.high_speed(high_speed),
.overflow(overflow),
.frame_error(wb_rst)
);
wire tx_ready, tx_valid;
wire [7:0] tx_data;
axis_wb_bridge #(
.ADDR_WIDTH(ADDR_WIDTH)
) bridge (
.clk(clk),
.rst(rst || wb_rst),
.s_axis_ready(rx_ready),
.s_axis_valid(rx_valid),
.s_axis_data(rx_data),
.m_axis_ready(tx_ready),
.m_axis_valid(tx_valid),
.m_axis_data(tx_data),
.wb_ack(wb_ack),
.wb_err(wb_err),
.wb_cyc(wb_cyc),
.wb_stb(wb_stb),
.wb_we(wb_we),
.wb_addr(wb_addr),
.wb_data_write(wb_data_write),
.wb_data_read(wb_data_read),
.overflow(overflow)
);
uart_tx uart_tx (
.clk(clk),
.rst(rst),
.tx(tx),
.ready(tx_ready),
.valid(tx_valid),
.data(tx_data),
.high_speed(high_speed)
);
endmodule

79
tb/uart_wb_bridge.py Normal file
View File

@ -0,0 +1,79 @@
# 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 Event, FallingEdge, Timer
from .axis_wb_bridge import Encoder, STATUS_WE
from .mdio import wb_read, wb_write, wb_err
from .uart_rx import putchar, BIT_STEPS
from .uart_tx import getchar
@cocotb.test(timeout_time=100, timeout_unit='us')
async def test_bridge(bridge):
bridge.clk.value = BinaryValue('Z')
bridge.rst.value = 1
bridge.rx.value = 1
bridge.wb_ack.value = 0
bridge.wb_err.value = 0
bridge.high_speed.value = 1
await Timer(1)
bridge.rst.value = 0
await cocotb.start(Clock(bridge.clk, 8, units='ns').start())
await FallingEdge(bridge.clk)
wb = {
'clk': bridge.clk,
'ack': bridge.wb_ack,
'err': bridge.wb_err,
'cyc': bridge.wb_cyc,
'stb': bridge.wb_stb,
'we': bridge.wb_we,
'addr': bridge.wb_addr,
'data_write': bridge.wb_data_write,
'data_read': bridge.wb_data_read,
}
e = Encoder()
recv_ready = Event()
async def send_break():
bridge.rx.value = 0
await Timer(BIT_STEPS * 20)
bridge.rx.value = 1
await Timer(BIT_STEPS)
async def send():
for c in e.encode(0x0123, 0x4567):
await putchar(bridge.rx, c)
# Start a read
await recv_ready.wait()
await putchar(bridge.rx, e.encode(0xdead)[0])
# Cancel it
await send_break()
# And do another read
e.last_addr = None
for c in e.encode(0x89ab):
await putchar(bridge.rx, c)
await cocotb.start(send())
await cocotb.start(wb_write(wb, 0x0123, 0x4567))
uart = {
'clk': bridge.clk,
'tx': bridge.tx,
}
assert await getchar(uart) == STATUS_WE
recv_ready.set()
await cocotb.start(wb_read(wb, 0x89ab, 0xcdef))
assert await getchar(uart) == 0
assert await getchar(uart) == 0xcd
assert await getchar(uart) == 0xef