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 += scramble
MODULES += uart_tx MODULES += uart_tx
MODULES += uart_rx MODULES += uart_rx
MODULES += uart_wb_bridge
MODULES += wb_mux MODULES += wb_mux
.PHONY: test .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, 115,200 and 4,000,000 baud. Properly detects breaks as (single) frame errors,
and ignores runt start bits. 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` === `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

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