Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion coreblocks/backend/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from coreblocks.params.genparams import GenParams
from coreblocks.arch import ExceptionCause
from coreblocks.interface.keys import CoreStateKey, CSRInstancesKey, InstructionPrecommitKey
from coreblocks.interface.keys import CoreStateKey, CSRInstancesKey, InstructionPrecommitKey, FTQCommitEntry
from coreblocks.priv.csr.csr_instances import CSRAddress, DoubleCounterCSR
from coreblocks.arch.isa_consts import TrapVectorMode

Expand Down Expand Up @@ -71,6 +71,8 @@ def elaborate(self, platform):
m_csr = self.dependency_manager.get_dependency(CSRInstancesKey()).m_mode
m.submodules.instret_csr = self.instret_csr

ftq_commit = self.dependency_manager.get_dependency(FTQCommitEntry())

side_fx = Signal(init=1)

def free_phys_reg(rp_dst: Value):
Expand All @@ -86,6 +88,7 @@ def retire_instr(rob_entry):

# free old rp_dst from overwritten R-RAT mapping
free_phys_reg(rat_out.old_rp_dst)
ftq_commit(m, ftq_ptr=rob_entry.rob_data.ftq_ptr)

self.instret_csr.increment(m)
self.perf_instr_ret.incr(m)
Expand Down
Empty file.
50 changes: 50 additions & 0 deletions coreblocks/frontend/bpu/bpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from amaranth import *

from transactron.core import *
from transactron.utils.transactron_helpers import make_layout
from transactron.lib import logging
from transactron.lib.connectors import Pipe

from coreblocks.params import *
from coreblocks.frontend import FrontendParams
from coreblocks.interface.layouts import CommonLayoutFields
from coreblocks.interface.layouts import BranchPredictionLayouts

log = logging.HardwareLogger("frontend.bpu")


class BranchPredictionUnit(Elaboratable):
request: Provided[Method]
write_prediction: Required[Method]
flush: Provided[Method]

def __init__(self, gen_params: GenParams) -> None:
self.gen_params = gen_params
self.layouts = gen_params.get(BranchPredictionLayouts)

self.request = Method(i=self.layouts.request)
self.write_prediction = Method(i=self.layouts.write_prediction)
self.flush = Method()

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

fparams = self.gen_params.get(FrontendParams)
fields = self.gen_params.get(CommonLayoutFields)

# TODO: 'clear' method of Pipe creates conflicts with other methods. This blocking behavior is not
# needed here and creates a lot of unnecessary logic and delays
m.submodules.pipe = pipe = Pipe(layout=make_layout(fields.pc, fields.ftq_ptr))

@def_method(m, self.request)
def _(pc, ftq_ptr):
pipe.write(m, pc=fparams.pc_from_fb(fparams.fb_addr(pc) + 1, 0), ftq_ptr=ftq_ptr)

with Transaction(name="BPU_Stage1").body(m):
self.write_prediction(m, pipe.read(m))

@def_method(m, self.flush, nonexclusive=True)
def _():
pipe.clear(m)

return m
1 change: 1 addition & 0 deletions coreblocks/frontend/decoder/decode_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def elaborate(self, platform):
),
"csr": instr_decoder.csr,
"pc": raw.pc,
"ftq_ptr": raw.ftq_ptr,
},
)

Expand Down
11 changes: 8 additions & 3 deletions coreblocks/frontend/fetch/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def elaborate(self, platform):
log.info(m, True, "Sending an instr to the backend pc=0x{:x} instr=0x{:x}", raw_instr.pc, raw_instr.instr)
self.cont(m, raw_instr)

m.submodules.fetch_requests = fetch_requests = BasicFifo(make_layout(fields.pc), depth=2)
m.submodules.fetch_requests = fetch_requests = BasicFifo(make_layout(fields.pc, fields.ftq_ptr), depth=2)

# This limits number of fetch blocks the fetch unit can process
# at a time. We start counting when sending a request to the cache and
Expand All @@ -115,18 +115,19 @@ def flush():
# - send a request to the instruction cache
#
@def_method(m, self.fetch_request)
def _(pc):
def _(pc, ftq_ptr):
log.info(m, True, "[IFU] request pc=0x{:x}", pc)
req_counter.acquire(m)
self.icache.issue_req(m, addr=pc)
fetch_requests.write(m, pc=pc)
fetch_requests.write(m, pc=pc, ftq_ptr=ftq_ptr)

#
# State passed between stage 1 and stage 2
#
m.submodules.s1_s2_pipe = s1_s2_pipe = Pipe(
[
fields.fb_addr,
fields.ftq_ptr,
("instr_valid", fetch_width),
("access_fault", 1),
("rvc", fetch_width),
Expand Down Expand Up @@ -225,6 +226,7 @@ def _(pc):
s1_s2_pipe.write(
m,
fb_addr=fetch_block_addr,
ftq_ptr=fetch_request.ftq_ptr,
instr_valid=valid_instr_mask,
access_fault=cache_resp.error,
rvc=is_rvc,
Expand Down Expand Up @@ -256,6 +258,7 @@ def _(pc):
req_counter.release(m)
s1_data = s1_s2_pipe.read(m)

ftq_ptr = s1_data.ftq_ptr
instrs = s1_data.instrs
fetch_block_addr = s1_data.fb_addr
instr_valid = s1_data.instr_valid
Expand Down Expand Up @@ -335,6 +338,7 @@ def _(pc):
raw_instrs[i].access_fault.eq(
Mux(s1_data.access_fault, FetchLayouts.AccessFaultFlag.ACCESS_FAULT, 0)
),
raw_instrs[i].ftq_ptr.eq(ftq_ptr),
]

if Extension.C in self.gen_params.isa.extensions:
Expand All @@ -357,6 +361,7 @@ def _(pc):
with m.If(access_fault | unsafe_stall | redirect):
self.fetch_writeback(
m,
ftq_ptr=ftq_ptr,
redirect=redirect,
redirect_target=predcheck_res.redirect_target,
)
Expand Down
101 changes: 19 additions & 82 deletions coreblocks/frontend/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

from coreblocks.arch.optypes import OpType
from coreblocks.params.genparams import GenParams
from coreblocks.frontend import FrontendParams
from coreblocks.frontend.ftq import FetchTargetQueue
from coreblocks.frontend.decoder.decode_stage import DecodeStage
from coreblocks.frontend.fetch.fetch import FetchUnit
from coreblocks.frontend.stall_controller import StallController
from coreblocks.frontend.bpu.bpu import BranchPredictionUnit
from coreblocks.cache.icache import ICache, ICacheBypass
from coreblocks.cache.refiller import SimpleCommonBusCacheRefiller
from coreblocks.interface.layouts import *
from coreblocks.interface.keys import BranchVerifyKey, FlushICacheKey, PredictedJumpTargetKey, RollbackKey
from coreblocks.interface.keys import FlushICacheKey, RollbackKey
from coreblocks.peripherals.bus_adapter import BusMasterInterface


Expand Down Expand Up @@ -73,49 +74,6 @@ def _(tag: Value):
return m


class FetchAddressUnit(Elaboratable):
"""
This module owns the speculative fetch program counter and arbitrates all frontend redirects.
It selects the next fetch PC based on reset, backend redirects, IFU redirects, and branch prediction results.
"""

read: Provided[Method]
ifu_redirect: Provided[Method]
beckend_redirect: Provided[Method]

def __init__(self, gen_params: GenParams):
self.gen_params = gen_params

layouts = self.gen_params.get(FetchLayouts)

self.read = Method(o=layouts.fetch_request)
self.ifu_redirect = Method(i=layouts.redirect)
self.backend_redirect = Method(i=layouts.redirect)

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

front_params = self.gen_params.get(FrontendParams)

next_fetch_pc = Signal(self.gen_params.isa.xlen, init=self.gen_params.start_pc)

@def_method(m, self.read)
def _():
# No branch prediction yet - just fallthrough to the next fetch block.
m.d.sync += next_fetch_pc.eq(front_params.pc_from_fb(front_params.fb_addr(next_fetch_pc) + 1, 0))
return next_fetch_pc

@def_method(m, self.ifu_redirect)
def _(pc):
m.d.sync += next_fetch_pc.eq(pc)

@def_method(m, self.backend_redirect)
def _(pc):
m.d.sync += next_fetch_pc.eq(pc)

return m


class CoreFrontend(Elaboratable):
"""Frontend of the core."""

Expand Down Expand Up @@ -145,21 +103,15 @@ def __init__(self, *, gen_params: GenParams, instr_bus: BusMasterInterface):

self.stall_ctrl = StallController(self.gen_params)

self.fetch_address_unit = FetchAddressUnit(self.gen_params)
self.fetch_writeback = Method(i=gen_params.get(FetchLayouts).fetch_writeback)

self.fetch = FetchUnit(self.gen_params, self.icache)
self.fetch.cont.provide(self.instr_buffer.write)
self.fetch.stall_unsafe.provide(self.stall_ctrl.stall_unsafe)

self.output_pipe = Pipe(self.gen_params.get(SchedulerLayouts).scheduler_in)
self.decode_buff = Connect(self.gen_params.get(DecodeLayouts).decoded_instr)

# TODO: move and implement these methods
jb_layouts = self.gen_params.get(JumpBranchLayouts)
self.target_pred_req = Method(i=jb_layouts.predicted_jump_target_req)
self.target_pred_resp = Method(o=jb_layouts.predicted_jump_target_resp)
DependencyContext.get().add_dependency(PredictedJumpTargetKey(), (self.target_pred_req, self.target_pred_resp))
self.bpu = BranchPredictionUnit(self.gen_params)
self.ftq = FetchTargetQueue(self.gen_params)

self.consume_instr = self.output_pipe.read
self.resume_from_exception = self.stall_ctrl.resume_from_exception
Expand All @@ -174,10 +126,21 @@ def elaborate(self, platform):
m.submodules.icache_refiller = self.icache_refiller
m.submodules.icache = self.icache

m.submodules.fetch_address_unit = self.fetch_address_unit
m.submodules.fetch = self.fetch
m.submodules.instr_buffer = self.instr_buffer

m.submodules.ftq = self.ftq
m.submodules.bpu = self.bpu

self.ftq.bpu_request.provide(self.bpu.request)
self.ftq.bpu_flush.provide(self.bpu.flush)
self.ftq.stall_lock.provide(self.stall_ctrl.stall_guard)
self.bpu.write_prediction.provide(self.ftq.bpu_response)

self.ftq.ifu_request.provide(self.fetch.fetch_request)
self.fetch.fetch_writeback.provide(self.ftq.ifu_redirect)
self.stall_ctrl.redirect_frontend.provide(self.ftq.backend_redirect)

m.submodules.decode = decode = DecodeStage(gen_params=self.gen_params)
decode.get_raw.provide(self.instr_buffer.read)
decode.push_decoded.provide(self.decode_buff.write)
Expand All @@ -191,39 +154,13 @@ def elaborate(self, platform):
m.submodules.output_pipe = self.output_pipe

m.submodules.stall_ctrl = self.stall_ctrl
self.stall_ctrl.redirect_frontend.provide(self.fetch_address_unit.backend_redirect)
self.fetch.fetch_writeback.provide(self.fetch_writeback)

# TODO: Remove when Branch Predictor implemented
with Transaction(name="DiscardBranchVerify").body(m):
read = self.connections.get_dependency(BranchVerifyKey())
read(m) # Consume to not block JB Unit

@def_method(m, self.target_pred_req)
@def_method(m, self.stall)
def _():
pass

@def_method(m, self.target_pred_resp)
def _(arg):
return {"valid": 0, "cfi_target": 0}

@def_method(m, self.fetch_writeback)
def _(redirect, redirect_target):
with m.If(redirect):
self.fetch_address_unit.ifu_redirect(m, pc=redirect_target)

with Transaction(name="FetchRequest").body(m):
self.stall_ctrl.stall_guard(m)
self.fetch.fetch_request(m, self.fetch_address_unit.read(m))

def flush_frontend():
self.fetch.flush(m)
self.instr_buffer.clear(m)
self.output_pipe.clear(m)

@def_method(m, self.stall)
def _():
flush_frontend()
self.bpu.flush(m)
self.stall_ctrl.stall_exception(m)

return m
Loading
Loading