Skip to content

Commit

Permalink
Port RISCOF tests to new Amaranth sim API.
Browse files Browse the repository at this point in the history
  • Loading branch information
cr1901 committed Sep 27, 2024
1 parent c235d58 commit 64f3eb3
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 164 deletions.
70 changes: 38 additions & 32 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,42 +88,48 @@ def memory(request):
return mem


async def mproc_inner(mod, ctx, memory):
m = mod

stims = [m.bus.cyc & m.bus.stb, m.bus.adr, m.bus.dat_w, m.bus.we,
m.bus.sel]

while True:
clk_hit, rst_active, wb_cyc, addr, dat_w, we, sel = \
await ctx.tick().sample(*stims)

if rst_active:
pass
elif clk_hit and wb_cyc and addr in memory.range:
if we:
dat_r = memory[addr - memory.range.start]

if sel & 0x1:
dat_r = (dat_r & 0xffffff00) | (dat_w & 0x000000ff)
if sel & 0x2:
dat_r = (dat_r & 0xffff00ff) | (dat_w & 0x0000ff00)
if sel & 0x4:
dat_r = (dat_r & 0xff00ffff) | (dat_w & 0x00ff0000)
if sel & 0x8:
dat_r = (dat_r & 0x00ffffff) | (dat_w & 0xff000000)

memory[addr] = dat_r
else:
# TODO: Some memories will dup byte/half data on the
# inactive lines. Worth adding?
ctx.set(m.bus.dat_r, memory[addr - memory.range.start])
ctx.set(m.bus.ack, 1)
# TODO: Wait states? See bus_proc_aux in previous versions for
# inspiration.
await ctx.tick()
ctx.set(m.bus.ack, 0)


@pytest.fixture
def memory_process(mod, memory):
m = mod

async def memory_process(ctx):
stims = [m.bus.cyc & m.bus.stb, m.bus.adr, m.bus.dat_w, m.bus.we,
m.bus.sel]

while True:
clk_hit, rst_active, wb_cyc, addr, dat_w, we, sel = \
await ctx.tick().sample(*stims)

if rst_active:
pass
elif clk_hit and wb_cyc and addr in memory.range:
if we:
dat_r = memory[addr - memory.range.start]

if sel & 0x1:
dat_r = (dat_r & 0xffffff00) | (dat_w & 0x000000ff)
if sel & 0x2:
dat_r = (dat_r & 0xffff00ff) | (dat_w & 0x0000ff00)
if sel & 0x4:
dat_r = (dat_r & 0xff00ffff) | (dat_w & 0x00ff0000)
if sel & 0x8:
dat_r = (dat_r & 0x00ffffff) | (dat_w & 0xff000000)

memory[addr] = dat_r
else:
# TODO: Some memories will dup byte/half data on the
# inactive lines. Worth adding?
ctx.set(m.bus.dat_r, memory[addr - memory.range.start])
ctx.set(m.bus.ack, 1)
# TODO: Wait states? See bus_proc_aux in previous versions for
# inspiration.
await ctx.tick()
ctx.set(m.bus.ack, 0)
await mproc_inner(m, ctx, memory)

return memory_process
123 changes: 30 additions & 93 deletions tests/riscof/sentinel/riscof_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,28 @@
import riscof.utils as utils
from riscof.pluginTemplate import pluginTemplate

import sys

# FIXME: This is unfortunate... but we need some code from the tests :(...
this_dir = Path(os.path.realpath(__file__)).parent
sentinel_root_dir = this_dir.parent.parent.parent
sys.path.insert(0, str(sentinel_root_dir))

from tests.conftest import Memory, mproc_inner # noqa: E402
from tests.upstream.test_upstream import wfhw_inner # noqa: E402

logger = logging.getLogger()


class HOST_STATE(Enum):
WAITING_FIRST = auto()
FIRST_ACCESS_ACK = auto()
WAITING_SECOND = auto()
SECOND_ACCESS_ACK = auto()
DONE = auto()
TIMEOUT = auto()


def wait_for_host_write(top, sig_file, bin_):
def wait_for_host_write():
i = 0
state = HOST_STATE.WAITING_FIRST

while True:
match state:
case HOST_STATE.WAITING_FIRST:
if ((yield top.bus.adr) == 0x4000000 >> 2) and \
(yield top.bus.sel == 0b1111) and \
(yield top.bus.cyc) and (yield top.bus.stb):
# yield top.bus.ack.eq(1)
state = HOST_STATE.FIRST_ACCESS_ACK
case HOST_STATE.FIRST_ACCESS_ACK:
begin_sig = (yield top.bus.dat_w)
# yield top.bus.ack.eq(0)
state = HOST_STATE.WAITING_SECOND
case HOST_STATE.WAITING_SECOND:
if (yield top.bus.adr) == ((0x4000000 + 4) >> 2) and \
(yield top.bus.sel == 0b1111) and \
(yield top.bus.cyc) and (yield top.bus.stb):
# yield top.bus.ack.eq(1)
state = HOST_STATE.SECOND_ACCESS_ACK
case HOST_STATE.SECOND_ACCESS_ACK:
end_sig = (yield top.bus.dat_w)
# yield top.bus.ack.eq(0)
state = HOST_STATE.DONE
case HOST_STATE.DONE:
break
case HOST_STATE.TIMEOUT:
raise AssertionError("CPU (but not microcode) probably "
"stuck in infinite loop")

yield
i += 1
if i > 65535:
state = HOST_STATE.TIMEOUT
def mk_wait_for_host_write(top, sig_file, memory):
async def wait_for_host_write(ctx):
val = await wfhw_inner(top, ctx)
begin_sig = val & 0xffffffff
end_sig = (val >> 32) & 0xffffffff

# I don't feel like doing alignment so convert the 32-bit memory
# list into bytes and index on a byte-basis.
bin_ = b"".join(b.to_bytes(4, 'little') for b in memory)

# The end state of the simulation is to print out the locations
# of the beginning and ending signatures. They can then
Expand All @@ -73,51 +46,9 @@ def wait_for_host_write():
return wait_for_host_write


def read_write_mem(top, bin_):
def read_write_mem():
yield Passive()

ios = (0x4000000 >> 2, 0x4000000 + 4 >> 2)
while True:
yield top.bus.dat_r.eq(0)
yield top.bus.ack.eq(0)

if ((yield top.bus.cyc) and (yield top.bus.stb)):
if not (yield top.bus.ack):
yield top.bus.ack.eq(1)

if ((yield top.bus.we) and (yield top.bus.ack) and
((yield top.bus.adr) not in ios)):
data_word = (yield top.bus.dat_w)
sel = (yield top.bus.sel)
word_adr = (yield top.bus.adr) << 2

if sel & 0b0001:
bin_[word_adr] = data_word & 0xff
if sel & 0b0010:
bin_[word_adr + 1] = (data_word >> 8) & 0xff
if sel & 0b0100:
bin_[word_adr + 2] = (data_word >> 16) & 0xff
if sel & 0b1000:
bin_[word_adr + 3] = (data_word >> 24) & 0xff

elif ((not (yield top.bus.ack)) and
((yield top.bus.adr) not in ios)):
data_word = 0
sel = (yield top.bus.sel)
word_adr = (yield top.bus.adr) << 2

if sel & 0b0001:
data_word |= bin_[word_adr]
if sel & 0b0010:
data_word |= bin_[word_adr + 1] << 8
if sel & 0b0100:
data_word |= bin_[word_adr + 2] << 16
if sel & 0b1000:
data_word |= bin_[word_adr + 3] << 24

yield top.bus.dat_r.eq(data_word)
yield
def mk_read_write_mem(top, memory):
async def read_write_mem(ctx):
await mproc_inner(top, ctx, memory)

return read_write_mem

Expand Down Expand Up @@ -289,11 +220,17 @@ def runTests(self, testList):
with open(test_dir / bin, "rb") as fp:
bin_ = bytearray(fp.read())

mem = Memory(start=0, size=len(bin_))

for adr in range(0, len(bin_) // 4):
mem[adr] = int.from_bytes(bin_[4 * adr:4 * adr + 4],
byteorder="little")

logger.debug("Executing in Amaranth simulator")
sim = Simulator(top)
sim.add_clock(1/(12e6))
sim.add_sync_process(wait_for_host_write(top, sig_file, bin_))
sim.add_sync_process(read_write_mem(top, bin_))
sim.add_testbench(mk_wait_for_host_write(top, sig_file, mem))
sim.add_process(mk_read_write_mem(top, mem))

vcd = test_dir / Path(test.stem).with_suffix(".vcd")
gtkw = test_dir / Path(test.stem).with_suffix(".gtkw")
Expand Down
83 changes: 44 additions & 39 deletions tests/upstream/test_upstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ def memory(request):
return mem


@pytest.fixture
def wait_for_host_write(mod, request):
async def wfhw_inner(mod, ctx):
class HOST_STATE(Enum):
WAITING_FIRST = auto()
FIRST_ACCESS_ACK = auto()
Expand All @@ -46,46 +45,52 @@ class HOST_STATE(Enum):
TIMEOUT = auto()

m = mod
i = 0
state = HOST_STATE.WAITING_FIRST
val = 0xdeadbeef

while True:
stims = [m.bus.cyc & m.bus.stb, m.bus.adr, m.bus.dat_w, m.bus.we,
m.bus.sel]
*_, wb_cyc, addr, dat_w, we, sel = await ctx.tick().sample(*stims)

i += 1
if i > 65535:
state = HOST_STATE.TIMEOUT

match state:
case HOST_STATE.WAITING_FIRST:
if (addr == 0x4000000 >> 2) and (sel == 0b1111) and wb_cyc:
ctx.set(m.bus.ack, 1)
state = HOST_STATE.FIRST_ACCESS_ACK
case HOST_STATE.FIRST_ACCESS_ACK:
val = dat_w
ctx.set(m.bus.ack, 0)
state = HOST_STATE.WAITING_SECOND
case HOST_STATE.WAITING_SECOND:
if addr == ((0x4000000 + 4) >> 2) and (sel == 0b1111) and \
wb_cyc:
ctx.set(m.bus.ack, 1)
state = HOST_STATE.SECOND_ACCESS_ACK
case HOST_STATE.SECOND_ACCESS_ACK:
val |= (dat_w << 32)
ctx.set(m.bus.ack, 0)
state = HOST_STATE.DONE
case HOST_STATE.DONE:
break
case HOST_STATE.TIMEOUT:
raise AssertionError("CPU (but not microcode) probably "
"stuck in infinite loop")

return val


@pytest.fixture
def wait_for_host_write(mod, request):
# TODO: Convert into SoC module (use wishbone.Decoder and friends)?
async def wait_for_host_write(ctx):
i = 0
state = HOST_STATE.WAITING_FIRST
val = 0xdeadbeef

while True:
stims = [m.bus.cyc & m.bus.stb, m.bus.adr, m.bus.dat_w, m.bus.we,
m.bus.sel]
*_, wb_cyc, addr, dat_w, we, sel = await ctx.tick().sample(*stims)

i += 1
if i > 65535:
state = HOST_STATE.TIMEOUT

match state:
case HOST_STATE.WAITING_FIRST:
if (addr == 0x4000000 >> 2) and (sel == 0b1111) and wb_cyc:
ctx.set(m.bus.ack, 1)
state = HOST_STATE.FIRST_ACCESS_ACK
case HOST_STATE.FIRST_ACCESS_ACK:
val = dat_w
ctx.set(m.bus.ack, 0)
state = HOST_STATE.WAITING_SECOND
case HOST_STATE.WAITING_SECOND:
if addr == ((0x4000000 + 4) >> 2) and (sel == 0b1111) and \
wb_cyc:
ctx.set(m.bus.ack, 1)
state = HOST_STATE.SECOND_ACCESS_ACK
case HOST_STATE.SECOND_ACCESS_ACK:
val |= (dat_w << 32)
ctx.set(m.bus.ack, 0)
state = HOST_STATE.DONE
case HOST_STATE.DONE:
assert (val >> 1, val & 1) == (0, 1)
break
case HOST_STATE.TIMEOUT:
raise AssertionError("CPU (but not microcode) probably "
"stuck in infinite loop")
val = await wfhw_inner(mod, ctx)
assert (val >> 1, val & 1) == (0, 1)

return wait_for_host_write

Expand Down

0 comments on commit 64f3eb3

Please sign in to comment.