Skip to content
646 changes: 646 additions & 0 deletions coreblocks/cache/dcache.py

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion coreblocks/cache/iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from amaranth_types import HasElaborate

__all__ = ["CacheInterface", "CacheRefillerInterface"]
__all__ = ["CacheInterface", "CacheRefillerInterface", "DataCacheRefillerInterface"]


class CacheInterface(HasElaborate, Protocol):
Expand Down Expand Up @@ -40,3 +40,23 @@ class CacheRefillerInterface(HasElaborate, Protocol):

start_refill: Method
accept_refill: Method


class DataCacheRefillerInterface(CacheRefillerInterface):
"""
Data Cache Refiller Interface.

Parameters
----------
start_refill: Method
A method that is used to start a refill for a given cache line.
accept_refill: Method
A method that is used to accept one fetch block from the requested cache line.
start_writeback: Method
Writes dirty data from cache to memory
accept_writeback: Method
Accepts writeback result
"""

start_writeback: Method
accept_writeback: Method
154 changes: 150 additions & 4 deletions coreblocks/cache/refiller.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from amaranth import *
from coreblocks.cache.icache import CacheRefillerInterface
from coreblocks.params import ICacheParameters
from coreblocks.interface.layouts import ICacheLayouts
from coreblocks.cache.iface import CacheRefillerInterface, DataCacheRefillerInterface
from coreblocks.params import ICacheParameters, DCacheParameters
from coreblocks.interface.layouts import ICacheLayouts, DCacheLayouts
from coreblocks.peripherals.bus_adapter import BusMasterInterface
from transactron.core import Transaction, Method, TModule, def_method
from transactron.lib import Forwarder

from amaranth.utils import exact_log2


__all__ = ["SimpleCommonBusCacheRefiller"]
__all__ = ["SimpleCommonBusCacheRefiller", "SimpleCommonBusDataCacheRefiller"]


class SimpleCommonBusCacheRefiller(Elaboratable, CacheRefillerInterface):
Expand Down Expand Up @@ -105,3 +105,149 @@ def _():
return resp_fwd.read(m)

return m


class SimpleCommonBusDataCacheRefiller(Elaboratable, DataCacheRefillerInterface):
def __init__(self, layouts: DCacheLayouts, params: DCacheParameters, bus_master: BusMasterInterface):
if params.word_width != bus_master.params.data_width:
raise ValueError("Data cache word width must match bus data width.")
if bus_master.params.granularity != 8:
raise ValueError("Data cache refiller expects byte-granular bus selects.")

self.layouts = layouts
self.params = params
self.bus_master = bus_master

self.start_refill = Method(i=layouts.start_refill)
self.accept_refill = Method(o=layouts.accept_refill)

self.start_writeback = Method(i=layouts.start_writeback)
self.accept_writeback = Method(o=layouts.accept_writeback)
self.get_writeback_data = Method(o=layouts.provide_writeback_data)

def elaborate(self, platform):
m = TModule()

m.submodules.refill_resp_fwd = refill_resp_fwd = Forwarder(self.layouts.accept_refill)
m.submodules.writeback_resp_fwd = writeback_resp_fwd = Forwarder(self.layouts.accept_writeback)

line_addr = Signal(self.params.addr_width - self.params.offset_bits)
word_idx = Signal(range(self.params.words_in_line))
writeback_error = Signal()

word_bytes_log = exact_log2(self.params.word_width_bytes)
full_sel = C(1).replicate(self.bus_master.params.data_width // self.bus_master.params.granularity)
bus_word_addr = Cat(word_idx, line_addr)
byte_word_addr = Cat(C(0, word_bytes_log), word_idx, line_addr)
last_word = word_idx == self.params.words_in_line - 1

start_refill_req = Signal()
start_writeback_req = Signal()
bus_read_request_done = Signal()
bus_read_done = Signal()
bus_read_error = Signal()
bus_write_request_done = Signal()
bus_write_done = Signal()

with m.FSM(init="IDLE") as fsm:
with m.State("IDLE"):
with m.If(start_refill_req):
m.next = "REFILL_REQ"
with m.Elif(start_writeback_req):
m.next = "WRITEBACK_REQ"

with m.State("REFILL_REQ"):
with m.If(bus_read_request_done):
m.next = "REFILL_RESP"

with m.State("REFILL_RESP"):
with m.If(bus_read_done):
with m.If(bus_read_error | last_word):
m.next = "IDLE"
with m.Else():
m.next = "REFILL_REQ"

with m.State("WRITEBACK_REQ"):
with m.If(bus_write_request_done):
m.next = "WRITEBACK_RESP"

with m.State("WRITEBACK_RESP"):
with m.If(bus_write_done):
with m.If(last_word):
m.next = "IDLE"
with m.Else():
m.next = "WRITEBACK_REQ"

with Transaction(name="DCacheRefillRequest").body(m, ready=fsm.ongoing("REFILL_REQ")):
self.bus_master.request_read(
m,
addr=bus_word_addr,
sel=full_sel,
)
m.d.comb += bus_read_request_done.eq(1)

with Transaction(name="DCacheRefillResponse").body(m, ready=fsm.ongoing("REFILL_RESP")):
bus_response = self.bus_master.get_read_response(m)
m.d.comb += [
bus_read_done.eq(1),
bus_read_error.eq(bus_response.err),
]
refill_resp_fwd.write(
m,
addr=byte_word_addr,
data=bus_response.data,
error=bus_response.err,
last=bus_response.err | last_word,
)

with m.If(~bus_response.err & ~last_word):
m.d.sync += word_idx.eq(word_idx + 1)

with Transaction(name="DCacheWritebackRequest").body(m, ready=fsm.ongoing("WRITEBACK_REQ")):
data = self.get_writeback_data(m)
self.bus_master.request_write(
m,
addr=bus_word_addr,
data=data.data,
sel=full_sel,
)
m.d.comb += bus_write_request_done.eq(1)

with Transaction(name="DCacheWritebackResponse").body(m, ready=fsm.ongoing("WRITEBACK_RESP")):
bus_response = self.bus_master.get_write_response(m)
m.d.comb += bus_write_done.eq(1)

with m.If(last_word):
writeback_resp_fwd.write(m, error=writeback_error | bus_response.err)
with m.Else():
m.d.sync += [
writeback_error.eq(writeback_error | bus_response.err),
word_idx.eq(word_idx + 1),
]

@def_method(m, self.start_refill, ready=fsm.ongoing("IDLE"))
def _(addr) -> None:
m.d.comb += start_refill_req.eq(1)
m.d.sync += [
line_addr.eq(addr[self.params.offset_bits :]),
word_idx.eq(0),
]

@def_method(m, self.accept_refill)
def _():
return refill_resp_fwd.read(m)

@def_method(m, self.start_writeback, ready=fsm.ongoing("IDLE"))
def _(addr) -> None:
m.d.comb += start_writeback_req.eq(1)
m.d.sync += [
line_addr.eq(addr[self.params.offset_bits :]),
word_idx.eq(0),
writeback_error.eq(0),
]

@def_method(m, self.accept_writeback)
def _():
return writeback_resp_fwd.read(m)

return m
80 changes: 78 additions & 2 deletions coreblocks/func_blocks/fu/lsu/dummyLsu.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

from amaranth import *
from transactron import Method, TModule, Transaction, def_method
from transactron.lib import BasicFifo
from transactron.lib.connectors import FIFO
from transactron.lib.logging import HardwareLogger
from transactron.lib.simultaneous import condition
from transactron.utils import DependencyContext
from transactron.utils.transactron_helpers import make_layout

from coreblocks.arch import OpType
from coreblocks.cache.dcache import DCache, DCacheBypass
from coreblocks.cache.iface import CacheInterface
from coreblocks.cache.refiller import SimpleCommonBusDataCacheRefiller
from coreblocks.arch.isa_consts import ExceptionCause
from coreblocks.frontend.decoder import *
from coreblocks.func_blocks.fu.lsu.lsu_requester import LSURequester
Expand All @@ -22,13 +26,64 @@
ExceptionReportKey,
InstructionPrecommitKey,
)
from coreblocks.interface.layouts import FuncUnitLayouts, LSULayouts
from coreblocks.interface.layouts import DCacheLayouts, FuncUnitLayouts, LSULayouts
from coreblocks.params import *
from coreblocks.peripherals.bus_adapter import BusMasterInterface

__all__ = ["LSUDummy", "LSUComponent"]


class LSUDataPathRouter(Elaboratable, CacheInterface):
def __init__(self, gen_params: GenParams, cached_data_path: CacheInterface, mmio_data_path: CacheInterface) -> None:
self.gen_params = gen_params
layouts = gen_params.get(DCacheLayouts)

self.cached_data_path = cached_data_path
self.mmio_data_path = mmio_data_path

self.issue_req = Method(i=layouts.issue_req)
self.accept_res = Method(o=layouts.accept_res)
self.flush = Method()

def elaborate(self, platform):
m = TModule()

m.submodules.pma_checker = pma_checker = PMAChecker(self.gen_params)
m.submodules.mmio_fifo = mmio_fifo = BasicFifo([("mmio", 1)], self.gen_params.dcache_params.request_depth)

@def_method(m, self.issue_req)
def _(addr: Value, data: Value, byte_mask: Value, store: Value):
m.d.av_comb += pma_checker.addr.eq(addr)

with condition(m) as branch:
with branch(pma_checker.result["mmio"]):
self.mmio_data_path.issue_req(m, addr=addr, data=data, byte_mask=byte_mask, store=store)
with branch():
self.cached_data_path.issue_req(m, addr=addr, data=data, byte_mask=byte_mask, store=store)

mmio_fifo.write(m, mmio=pma_checker.result["mmio"])

@def_method(m, self.accept_res)
def _():
route = mmio_fifo.read(m)
response = Signal(self.gen_params.get(DCacheLayouts).accept_res)

with condition(m) as branch:
with branch(route.mmio):
m.d.comb += response.eq(self.mmio_data_path.accept_res(m))
with branch():
m.d.comb += response.eq(self.cached_data_path.accept_res(m))

return response

@def_method(m, self.flush)
def _() -> None:
self.cached_data_path.flush(m)
self.mmio_data_path.flush(m)

return m


class LSUDummy(FuncUnit, Elaboratable):
"""
Very simple LSU, which serializes all stores and loads.
Expand Down Expand Up @@ -77,7 +132,28 @@ def elaborate(self, platform):
csr = self.dependency_manager.get_dependency(CSRInstancesKey())
m.submodules.pma_checker = pma_checker = PMAChecker(self.gen_params)
m.submodules.pmp_checker = pmp_checker = PMPChecker(self.gen_params, csr.m_mode)
m.submodules.requester = requester = LSURequester(self.gen_params, self.bus)

dcache_layouts = self.gen_params.get(DCacheLayouts)
if self.gen_params.dcache_params.enable:
m.submodules.dcache_refiller = dcache_refiller = SimpleCommonBusDataCacheRefiller(
dcache_layouts, self.gen_params.dcache_params, self.bus
)
m.submodules.cached_data_path = cached_data_path = DCache(
dcache_layouts, self.gen_params.dcache_params, dcache_refiller
)
dcache_refiller.get_writeback_data.provide(cached_data_path.provide_writeback_data)
else:
m.submodules.cached_data_path = cached_data_path = DCacheBypass(
dcache_layouts, self.gen_params.dcache_params, self.bus
)

m.submodules.mmio_data_path = mmio_data_path = DCacheBypass(
dcache_layouts, self.gen_params.dcache_params, self.bus
)
m.submodules.data_path_router = data_path_router = LSUDataPathRouter(
self.gen_params, cached_data_path, mmio_data_path
)
m.submodules.requester = requester = LSURequester(self.gen_params, data_path_router)

request_layout = make_layout(
("data", self.fu_layouts.issue),
Expand Down
Loading
Loading