Add wishbone register
This module registers all signals on a wishbone bus. This increases latency/decreases throughput, but the wishbone cores here are just for management, so that's not really critical. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
db4c225db5
commit
52bc62814e
1
Makefile
1
Makefile
|
@ -154,6 +154,7 @@ MODULES += uart_tx
|
||||||
MODULES += uart_rx
|
MODULES += uart_rx
|
||||||
MODULES += uart_wb_bridge
|
MODULES += uart_wb_bridge
|
||||||
MODULES += wb_mux
|
MODULES += wb_mux
|
||||||
|
MODULES += wb_reg
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: $(addsuffix .fst,$(MODULES)) $(addsuffix .synth.fst,$(MODULES))
|
test: $(addsuffix .fst,$(MODULES)) $(addsuffix .synth.fst,$(MODULES))
|
||||||
|
|
|
@ -241,6 +241,11 @@ This implements a simple Wishbone mux, allowing a single master to access
|
||||||
several slaves. The address decoding is greatly simplified by assigning each
|
several slaves. The address decoding is greatly simplified by assigning each
|
||||||
slave a (priority-decoded) address bit.
|
slave a (priority-decoded) address bit.
|
||||||
|
|
||||||
|
=== `wb_reg`
|
||||||
|
|
||||||
|
Add a register stage to a wishbone bus. This helps improve timing, but will add
|
||||||
|
a cycle of latency (and decrease throughput).
|
||||||
|
|
||||||
== Interfaces
|
== Interfaces
|
||||||
|
|
||||||
Throughout this project, a variety of interfaces, some standard and some
|
Throughout this project, a variety of interfaces, some standard and some
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-Only
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
`include "common.vh"
|
||||||
|
|
||||||
|
module wb_reg (
|
||||||
|
input clk, rst,
|
||||||
|
|
||||||
|
output reg s_ack, s_err,
|
||||||
|
input s_cyc, s_stb, s_we,
|
||||||
|
input [ADDR_WIDTH - 1:0] s_addr,
|
||||||
|
input [DATA_WIDTH - 1:0] s_data_write,
|
||||||
|
output reg [DATA_WIDTH - 1:0] s_data_read,
|
||||||
|
|
||||||
|
input m_ack, m_err,
|
||||||
|
output reg m_cyc, m_stb, m_we,
|
||||||
|
output reg [ADDR_WIDTH - 1:0] m_addr,
|
||||||
|
output reg [DATA_WIDTH - 1:0] m_data_write,
|
||||||
|
input [DATA_WIDTH - 1:0] m_data_read
|
||||||
|
);
|
||||||
|
|
||||||
|
parameter ADDR_WIDTH = 16;
|
||||||
|
parameter DATA_WIDTH = 16;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
s_ack = 0;
|
||||||
|
s_err = 0;
|
||||||
|
m_cyc = 0;
|
||||||
|
m_stb = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (rst) begin
|
||||||
|
s_ack <= 0;
|
||||||
|
s_err <= 0;
|
||||||
|
m_cyc <= 0;
|
||||||
|
m_stb <= 0;
|
||||||
|
end else begin
|
||||||
|
s_ack <= m_ack && s_cyc && s_stb;
|
||||||
|
s_err <= m_err && s_cyc && s_stb;
|
||||||
|
m_cyc <= s_cyc && !(m_ack || m_err);
|
||||||
|
m_stb <= s_stb && !(m_ack || m_err);
|
||||||
|
end
|
||||||
|
s_data_read <= m_data_read;
|
||||||
|
m_we <= s_we;
|
||||||
|
m_addr <= s_addr;
|
||||||
|
m_data_write <= s_data_write;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,68 @@
|
||||||
|
# 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, RisingEdge, Timer
|
||||||
|
|
||||||
|
from .mdio import wb_read, wb_write, wb_err
|
||||||
|
from .mdio_regs import wb_xfer
|
||||||
|
|
||||||
|
@cocotb.test(timeout_time=50, timeout_unit='us')
|
||||||
|
async def test_reg(reg):
|
||||||
|
reg.clk.value = BinaryValue('Z')
|
||||||
|
reg.rst.value = 0
|
||||||
|
reg.s_cyc.value = 1
|
||||||
|
reg.s_stb.value = 0
|
||||||
|
reg.m_ack.value = 0
|
||||||
|
reg.m_err.value = 0
|
||||||
|
|
||||||
|
await Timer(1)
|
||||||
|
await cocotb.start(Clock(reg.clk, 8, units='ns').start())
|
||||||
|
await FallingEdge(reg.clk)
|
||||||
|
|
||||||
|
master = {
|
||||||
|
'clk': reg.clk,
|
||||||
|
'ack': reg.m_ack,
|
||||||
|
'err': reg.m_err,
|
||||||
|
'cyc': reg.m_cyc,
|
||||||
|
'stb': reg.m_stb,
|
||||||
|
'we': reg.m_we,
|
||||||
|
'addr': reg.m_addr,
|
||||||
|
'data_write': reg.m_data_write,
|
||||||
|
'data_read': reg.m_data_read,
|
||||||
|
}
|
||||||
|
|
||||||
|
slave = {
|
||||||
|
'clk': reg.clk,
|
||||||
|
'ack': reg.s_ack,
|
||||||
|
'err': reg.s_err,
|
||||||
|
'cyc': reg.s_cyc,
|
||||||
|
'stb': reg.s_stb,
|
||||||
|
'we': reg.s_we,
|
||||||
|
'addr': reg.s_addr,
|
||||||
|
'data_write': reg.s_data_write,
|
||||||
|
'data_read': reg.s_data_read,
|
||||||
|
}
|
||||||
|
|
||||||
|
async def resp():
|
||||||
|
await wb_read(master, 0x0123, 0x4567)
|
||||||
|
await wb_write(master, 0x89ab, 0xcdef)
|
||||||
|
await wb_err(master)
|
||||||
|
|
||||||
|
await cocotb.start(resp())
|
||||||
|
|
||||||
|
assert await wb_xfer(slave, 0x0123) == 0x4567
|
||||||
|
await wb_xfer(slave, 0x89ab, 0xcdef)
|
||||||
|
assert await wb_xfer(slave, 0xdead) is None
|
||||||
|
|
||||||
|
reg.rst.value = 1
|
||||||
|
reg.s_cyc.value = 1
|
||||||
|
reg.m_ack.value = 1
|
||||||
|
reg.m_err.value = 1
|
||||||
|
await FallingEdge(reg.clk)
|
||||||
|
assert not reg.m_cyc.value
|
||||||
|
assert not reg.m_stb.value
|
||||||
|
assert not reg.s_ack.value
|
||||||
|
assert not reg.s_err.value
|
Loading…
Reference in New Issue