For mass generation of bus decoders, a register bit map must be associated with corresponding control, status or data signals. This SoC concept follows the MaSoCist register map design rules:
select
lines.
This allows to implement W1C
(write one to clear) behaviour, or optimized data in/out transfers.NOTE: This example supports VHDL output, only
We import the container extensions Reg
and BF
from the library:
from cyrite.library.soc.mmr import BF, Reg, generate_mmr_decoder
Add a few register with bit fields and flags:
reg01 = Reg(16,
[
BF("im", 3, 1, flags = BF.READONLY),
BF("ex", 7, 6),
BF("inv", 4, 4, flags = BF.WRITEONLY),
BF("mode", 14, 10, default = 2)
]
)
reg02 = Reg(16,
[
BF("gna", 6, 1, default = 8),
BF("reset", 7, 7, default = True)
],
flags = Reg.VOLATILE | Reg.WRITEONLY
)
# This is a description for an address map
regdesc = {
0x01: ['stat', reg01],
0x02: ['ctrl', reg02],
0x04: ['TXD', Reg(16, [ BF("DATA", 15, 0)], flags = Reg.WRITEONLY | Reg.VOLATILE) ],
0x05: ['RXD', Reg(16, [ BF("DATA", 15, 0)], flags = Reg.READONLY | Reg.VOLATILE)]
}
The register decoder for this specific memory mapped register has a dynamic registerbank
dictionary passed to the interface, containing the register in/out wires. This variable argument construct is inferred to a HDL description.
The actual register map decoder consists of the code below.
from myirl.emulation.myhdl import *
from myirl.library.portion import *
SigType = Signal.Type
Bool = SigType(bool)
Addr = SigType(intbv, 12)
Data = SigType(intbv, 16)
@block
def mmr_decode(
clk : ClkSignal,
reset : ResetSignal,
addr : Addr,
wr : Bool,
data_in : Data,
data_out : Data.Output,
REGDESC : dict,
**registerbank
):
# We use a partially assigneable signal:
idata = PASignal(intbv()[len(data_out):])
# Then generate the decoder from the register map description passed:
wk = generate_mmr_decoder(REGDESC, registerbank, clk, reset, wr, addr, data_in, idata,
RESET_DEFAULTS = True)
@always(clk.posedge)
def drive():
data_out.next = idata
return instances()
We define an interface generation function that creates a signal dictionary out of the register description:
# Interface generation:
from cyrite.library.soc.mmr import RegisterSignal
def gen_interface(rd):
d = {}
for k, rdesc in rd.items():
n, reg = rdesc[0], rdesc[1]
sig = RegisterSignal(n, reg)
sig.rename(n)
d[n] = sig
return d
We might pack all MMR signals into a port structure including auxiliary methods. We need to decorate them with @hdlmacro
in order to return a generator element usable within the myHDL @instance
.
@container()
class MMRPort:
_inputs = ['din', 'wr', 'addr']
_outputs = ['dout']
_other = ['clk', 'rst']
def __init__(self):
self.clk = ClkSignal()
self.wr = Signal(bool())
self.addr = Addr()
self.rst = ResetSignal(0, 1)
self.din, self.dout = [ Data() for _ in range(2) ]
@hdlmacro
def reset_sequence(self):
p = self
yield [
p.rst.set(True),
simulation.wait(2 * (p.clk.posedge, )),
p.rst.set(False)
]
@hdlmacro
def write_sequence(self, a, d):
p = self
yield [
p.addr.set(a),
p.din.set(d),
simulation.wait(p.clk.posedge),
p.wr.set(True),
simulation.wait(p.clk.posedge),
p.wr.set(False),
]
@hdlmacro
def assert_read(self, addr, data):
yield [
self.addr.set(addr),
self.wr.set(False),
simulation.wait(2 * (self.clk.posedge,)),
simulation.assert_(self.dout == data, "Read mismatch")
]
Finally, we run a reset/write on the decoder:
@block
def testbench(regdesc : dict):
p = MMRPort()
clk = ClkSignal('clk')
mon_gna = Signal(intbv()[6:])
mon_select = Signal(bool())
debug = Signal(bool())
interface = gen_interface(regdesc)
wires = [
mon_gna.wireup(interface['ctrl'].read.gna),
mon_select.wireup(interface['ctrl'].select.sel_w),
p.clk.wireup(clk)
]
inst = mmr_decode(clk, p.rst, p.addr, p.wr, p.din, p.dout, regdesc, **interface )
@always(delay(2))
def clkgen():
clk.next = ~clk
ctrl = interface['ctrl']
stat = interface['stat']
@instance
def stimulus():
print("START")
debug.next = False
p.wr.next = False
p.addr.next = 0x001
p.reset_sequence()
stat.read.ex.next = 0
stat.read.mode.next = 4
stat.read.im.next = 2
p.assert_read(0x001, 0x1004)
p.write_sequence(0x002, 0xfa)
debug.next = True
yield clk.posedge
assert ctrl.select.sel_w == True
assert ctrl.write.gna == 0x3d
yield clk.negedge
assert ctrl.select.sel_w == False
p.write_sequence(0x001, 0x10)
assert stat.write.inv == True
yield 2 * (clk.posedge, )
print("DONE")
raise StopSimulation
return instances()
def test():
from myirl.test.common_test import Simulator
tb = testbench(regdesc)
sim = Simulator()
# Turn 'debug' on for simulation output
sim.run(tb, 200, debug = True, vcdfile = 'testbench.vcd')
test()
FALLBACK: UNHANDLED ROOT CLASS <class 'ipykernel.zmqshell.ZMQInteractiveShell'>, create new context DEBUG: Skip virtual container member: 'ctrl_select.sel_w' DEBUG: Skip virtual container member: 'TXD_select.sel_w' DEBUG: Skip virtual container member: 'RXD_select.sel_r' DEBUG: Skip virtual container member: 'TXD_write.DATA' DEBUG: Skip virtual container member: 'ctrl_write.gna' DEBUG: Skip virtual container member: 'ctrl_write.reset' DEBUG: Skip virtual container member: 'stat_write.ex' DEBUG: Skip virtual container member: 'stat_write.inv' DEBUG: Skip virtual container member: 'stat_write.mode' Writing 'mmr_decode' to file /tmp/mmr_decode.vhdl DEBUG: Skip virtual container member: 'stat_read.ex' DEBUG: Skip virtual container member: 'stat_read.mode' DEBUG: Skip virtual container member: 'stat_read.im' Writing 'testbench' to file /tmp/testbench.vhdl Creating library file /tmp/module_defs.vhdl ==== COSIM stdout ==== START DONE simulation stopped @42ns
/home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `ctrl_select.sel_w` may be ineffective warnings.warn(msg) /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `TXD_select.sel_w` may be ineffective warnings.warn(msg) /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `RXD_select.sel_r` may be ineffective warnings.warn(msg)
# ! cat {mmr_decode.ctx.path_prefix}module_defs.vhdl
The *.vcd
format hides the MMRPort
record members from the trace. Therefore we need a few monitoring auxiliary signals.
import wavedraw
import nbwavedrom
TB = "testbench"
waveform = wavedraw.vcd2wave(TB+ ".vcd", TB + '.clk', None)
nbwavedrom.draw(waveform)