From 57bec62ee31da6d33952eaa616b665ed863b9eb6 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sun, 5 Nov 2023 12:54:30 +0100 Subject: [PATCH] Faster Wishbone --- coreblocks/frontend/icache.py | 21 +++++++++--- coreblocks/peripherals/wishbone.py | 55 +++++++++++++++++------------- test/frontend/test_icache.py | 2 ++ test/peripherals/test_wishbone.py | 5 +-- 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/coreblocks/frontend/icache.py b/coreblocks/frontend/icache.py index d5cbb8d8a..f9aa41c55 100644 --- a/coreblocks/frontend/icache.py +++ b/coreblocks/frontend/icache.py @@ -382,10 +382,15 @@ def elaborate(self, platform): refill_active = Signal() word_counter = Signal(range(self.params.words_in_block)) - with Transaction().body(m, request=refill_active): + m.submodules.address_fwd = address_fwd = Forwarder( + [("word_counter", word_counter.shape()), ("refill_address", refill_address.shape())] + ) + + with Transaction().body(m): + address = address_fwd.read(m) self.wb_master.request( m, - addr=Cat(word_counter, refill_address), + addr=Cat(address["word_counter"], address["refill_address"]), data=0, we=0, sel=Repl(1, self.wb_master.wb_params.data_width // self.wb_master.wb_params.granularity), @@ -393,19 +398,27 @@ def elaborate(self, platform): @def_method(m, self.start_refill, ready=~refill_active) def _(addr) -> None: - m.d.sync += refill_address.eq(addr[self.params.offset_bits :]) + address = addr[self.params.offset_bits :] + m.d.sync += refill_address.eq(address) m.d.sync += refill_active.eq(1) m.d.sync += word_counter.eq(0) + address_fwd.write(m, word_counter=0, refill_address=address) + @def_method(m, self.accept_refill, ready=refill_active) def _(): fetched = self.wb_master.result(m) last = (word_counter == (self.params.words_in_block - 1)) | fetched.err - m.d.sync += word_counter.eq(word_counter + 1) + next_word_counter = Signal.like(word_counter) + m.d.top_comb += next_word_counter.eq(word_counter + 1) + + m.d.sync += word_counter.eq(next_word_counter) with m.If(last): m.d.sync += refill_active.eq(0) + with m.Else(): + address_fwd.write(m, word_counter=next_word_counter, refill_address=refill_address) return { "addr": Cat(Repl(0, log2_int(self.params.word_width_bytes)), word_counter, refill_address), diff --git a/coreblocks/peripherals/wishbone.py b/coreblocks/peripherals/wishbone.py index c6035431d..9cc7390b9 100644 --- a/coreblocks/peripherals/wishbone.py +++ b/coreblocks/peripherals/wishbone.py @@ -6,9 +6,11 @@ import operator from transactron import Method, def_method, TModule +from transactron.core import Transaction from transactron.lib import AdapterTrans from coreblocks.utils.utils import OneHotSwitchDynamic, assign from coreblocks.utils.fifo import BasicFifo +from transactron.lib.connectors import Forwarder class WishboneParameters: @@ -109,8 +111,6 @@ def __init__(self, wb_params: WishboneParameters): self.request = Method(i=self.requestLayout) self.result = Method(o=self.resultLayout) - self.ready = Signal() - self.res_ready = Signal() self.result_data = Record(self.resultLayout) # latched input signals @@ -132,6 +132,10 @@ def generate_layouts(self, wb_params: WishboneParameters): def elaborate(self, platform): m = TModule() + m.submodules.result = result = Forwarder(self.resultLayout) + + request_ready = Signal() + def FSMWBCycStart(request): # noqa: N802 # internal FSM function that starts Wishbone cycle m.d.sync += self.wbMaster.cyc.eq(1) @@ -140,12 +144,6 @@ def FSMWBCycStart(request): # noqa: N802 m.d.sync += self.wbMaster.dat_w.eq(Mux(request.we, request.data, 0)) m.d.sync += self.wbMaster.we.eq(request.we) m.d.sync += self.wbMaster.sel.eq(request.sel) - m.next = "WBWaitACK" - - @def_method(m, self.result, ready=self.res_ready) - def _(): - m.d.sync += self.res_ready.eq(0) - return self.result_data with m.FSM("Reset"): with m.State("Reset"): @@ -153,17 +151,12 @@ def _(): m.next = "Idle" with m.State("Idle"): # default values for important signals - m.d.sync += self.ready.eq(1) m.d.sync += self.wbMaster.rst.eq(0) m.d.sync += self.wbMaster.stb.eq(0) m.d.sync += self.wbMaster.cyc.eq(0) - - @def_method(m, self.request, ready=(self.ready & ~self.res_ready)) - def _(arg): - m.d.sync += self.ready.eq(0) - m.d.sync += assign(self.txn_req, arg) - # do WBCycStart state in the same clock cycle - FSMWBCycStart(arg) + m.d.comb += request_ready.eq(1) + with m.If(self.request.run): + m.next = "WBWaitACK" with m.State("WBCycStart"): FSMWBCycStart(self.txn_req) @@ -171,18 +164,34 @@ def _(arg): with m.State("WBWaitACK"): with m.If(self.wbMaster.ack | self.wbMaster.err): - m.d.sync += self.wbMaster.cyc.eq(0) - m.d.sync += self.wbMaster.stb.eq(0) - m.d.sync += self.ready.eq(1) - m.d.sync += self.res_ready.eq(1) - m.d.sync += self.result_data.data.eq(Mux(self.txn_req.we, 0, self.wbMaster.dat_r)) - m.d.sync += self.result_data.err.eq(self.wbMaster.err) - m.next = "Idle" + m.d.comb += request_ready.eq(1) + tr = Transaction() + with tr.body(m): + # will be always ready, as we checked that in Idle + result.write(m, data=Mux(self.txn_req.we, 0, self.wbMaster.dat_r), err=self.wbMaster.err) + with m.If(self.request.run): + m.next = "WBWaitACK" + with m.Else(): + m.d.sync += self.wbMaster.cyc.eq(0) + m.d.sync += self.wbMaster.stb.eq(0) + m.next = "Idle" with m.If(self.wbMaster.rty): m.d.sync += self.wbMaster.cyc.eq(1) m.d.sync += self.wbMaster.stb.eq(0) m.next = "WBCycStart" + @def_method(m, self.result) + def _(): + return result.read(m) + + @def_method(m, self.request, ready=request_ready & result.write.ready) + def _(arg): + m.d.sync += assign(self.txn_req, arg) + # do WBCycStart state in the same clock cycle + FSMWBCycStart(arg) + + tr.schedule_before(self.request) + return m diff --git a/test/frontend/test_icache.py b/test/frontend/test_icache.py index 2f7f33042..c51fc719d 100644 --- a/test/frontend/test_icache.py +++ b/test/frontend/test_icache.py @@ -92,6 +92,7 @@ def wishbone_slave(self): # Wishbone is addressing words, so we need to shit it a bit to get the real address. addr = (yield self.test_module.wb_ctrl.wb.adr) << log2_int(self.cp.word_width_bytes) + yield while random.random() < 0.5: yield @@ -107,6 +108,7 @@ def wishbone_slave(self): def refiller_process(self): while self.requests: req_addr = self.requests.pop() + yield from self.test_module.start_refill.call(addr=req_addr) for i in range(self.cp.words_in_block): diff --git a/test/peripherals/test_wishbone.py b/test/peripherals/test_wishbone.py index b4a7d78f0..524af3fde 100644 --- a/test/peripherals/test_wishbone.py +++ b/test/peripherals/test_wishbone.py @@ -76,7 +76,6 @@ def process(): # read request yield from twbm.requestAdapter.call(addr=2, data=0, we=0, sel=1) yield - assert not (yield wbm.request.ready) yield from wwb.slave_verify(2, 0, 0, 1) yield from wwb.slave_respond(8) resp = yield from twbm.resultAdapter.call() @@ -97,7 +96,8 @@ def process(): yield from wwb.slave_respond(1, ack=0, err=0, rty=1) yield assert not (yield wwb.wb.stb) - assert not (yield wbm.result.ready) # verify cycle restart + resp = yield from twbm.requestAdapter.call_try() + assert resp is None # verify cycle restart yield from wwb.slave_wait() yield from wwb.slave_verify(2, 0, 0, 0) yield from wwb.slave_respond(1, ack=1, err=1, rty=0) @@ -325,6 +325,7 @@ def mem_op_process(): yield from self.m.request.call(addr=addr, data=data, we=write, sel=sel) res = yield from self.m.result.call() if write: + yield # workaround, memory state will change the next cycle! self.assertEqual((yield self.m.mem_slave.mem[addr]), mem_state[addr]) else: self.assertEqual(res["data"], mem_state[addr])