Add wishbone mux

This adds a simple wishbone mux. The idea is that each slave gets its
own address bit. This lends itself to extemely simple address decoding,
but uses up address space quickly. In theory, we could also give larger
addres space to some slaves, but currently lower bits have priority. The
testbench is also very simple. Since everything is combinatorial, we can
determine the outputs from the inputs exactly.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2023-02-18 22:32:12 -05:00
parent afbb64023e
commit 7c9ac42988
4 changed files with 149 additions and 0 deletions

View File

@ -127,6 +127,7 @@ MODULES += phy_core
MODULES += pmd_dp83223
MODULES += pmd_dp83223_rx
MODULES += scramble
MODULES += wb_mux
.PHONY: test
test: $(addsuffix .fst,$(MODULES)) $(addsuffix .synth.fst,$(MODULES))

60
rtl/wb_mux.v Normal file
View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
*
* A wishbone mux with extremely-simple address decoding.
*/
`include "common.vh"
module wb_mux (
/* Wishbone master */
output reg m_ack, m_err,
input m_cyc, m_stb, m_we,
input [ADDR_WIDTH + SLAVES - 1:0] m_addr,
input [DATA_WIDTH - 1:0] m_data_write,
output reg [DATA_WIDTH - 1:0] m_data_read,
input [SLAVES - 1:0] s_ack, s_err,
output reg [SLAVES - 1:0] s_cyc, s_stb, s_we,
output reg [ADDR_WIDTH * SLAVES - 1:0] s_addr,
output reg [DATA_WIDTH * SLAVES - 1:0] s_data_write,
input [DATA_WIDTH * SLAVES - 1:0] s_data_read
);
parameter ADDR_WIDTH = 5;
parameter DATA_WIDTH = 16;
parameter SLAVES = 4;
integer i;
reg selected;
always @(*) begin
s_cyc = {SLAVES{m_cyc}};
s_stb = {SLAVES{1'b0}};
s_we = {SLAVES{m_we}};
s_addr = {SLAVES{m_addr[ADDR_WIDTH - 1:0]}};
s_data_write = {SLAVES{m_data_write}};
m_ack = 0;
m_err = 0;
m_data_read = {DATA_WIDTH{1'bX}};
selected = 0;
for (i = 0; i < SLAVES; i = i + 1) begin
if (m_addr[ADDR_WIDTH + i] && !selected) begin
m_ack = s_ack[i];
m_err = s_err[i];
m_data_read = s_data_read[i * DATA_WIDTH +: DATA_WIDTH];
s_stb[i] = m_stb;
s_we[i] = m_we;
selected = 1;
end
end
if (m_cyc && m_stb && !selected) begin
m_ack = 0;
m_err = 1;
end
end
endmodule

View File

@ -19,6 +19,9 @@ async def async_iter(it):
def BIT(n):
return 1 << n
def GENMASK(h, l):
return (-1 << l) & ((1 << h + 1) - 1)
# From https://stackoverflow.com/a/7864317/5086505
class classproperty(property):
def __get__(self, cls, owner):

85
tb/wb_mux.py Normal file
View File

@ -0,0 +1,85 @@
# SPDX-License-Identifier: AGPL-3.0-Only
# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge, Timer
from cocotb.types import LogicArray
from .util import BIT, GENMASK
ADDR_WIDTH = 5
DATA_WIDTH = 16
SLAVES = 4
def get(signal, slave, width):
return (signal.value >> (slave * width)) & GENMASK(width - 1, 0)
def check(mux):
selected = False
for i in range(SLAVES):
cyc = get(mux.s_cyc, i, 1)
assert cyc == mux.m_cyc.value
if mux.m_addr.value & BIT(i + ADDR_WIDTH) and not selected:
selected = True
stb = bool(mux.s_stb.value & BIT(i))
assert stb == mux.m_stb.value
if not cyc and stb:
continue
assert get(mux.s_addr, i, ADDR_WIDTH) == mux.m_addr.value & GENMASK(ADDR_WIDTH - 1, 0)
we = get(mux.s_we, i, 1)
assert we == mux.m_we.value
if we:
assert get(mux.s_data_write, i, DATA_WIDTH) == mux.m_data_write.value
assert mux.m_ack.value == get(mux.s_ack, i, 1)
assert mux.m_err.value == get(mux.s_err, i, 1)
assert mux.m_data_read.value == get(mux.s_data_read, i, DATA_WIDTH)
else:
assert not get(mux.s_stb, i, 1)
if not selected and mux.m_cyc.value and mux.m_stb.value:
assert not mux.m_ack.value
assert mux.m_err.value
@cocotb.test(timeout_time=1, timeout_unit='us')
async def test_mdio(mux):
mux.m_cyc.value = 1
mux.m_stb.value = 1
mux.m_we.value = 1
mux.m_data_write.value = 0x1364
mux.s_ack.value = 0
mux.s_err.value = 0
mux.s_data_read.value = 0x0123456789abcdef
for i in range(4):
mux.m_addr.value = BIT(i + 5) | 0x15
mux.s_ack.value = BIT(i)
mux.s_err.value = GENMASK(SLAVES - 1, 0) ^ BIT(i)
await Timer(1)
check(mux)
mux.s_ack.value = GENMASK(SLAVES - 1, 0) ^ BIT(i)
mux.s_err.value = BIT(i)
await Timer(1)
check(mux)
mux.m_addr.value = 0
await Timer(1)
check(mux)
mux.m_stb.value = 0
await Timer(1)
check(mux)
mux.m_cyc.value = 0
mux.m_stb.value = 1
await Timer(1)
check(mux)
mux.m_cyc.value = 1
mux.m_addr.value = 0x1ff
mux.m_we.value = 0
await Timer(1)
check(mux)