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:
parent
2c2527e8d9
commit
5f331a403c
1
Makefile
1
Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue