forked from libre-chip/cpu
WIP: adding load/store unit
This commit is contained in:
parent
bc2bcf4434
commit
f1f39187e4
4 changed files with 248 additions and 3 deletions
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, mop_enum,
|
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, mop_enum,
|
||||||
},
|
},
|
||||||
rename_execute_retire::ExecuteToUnitInterface,
|
rename_execute_retire::ExecuteToUnitInterface,
|
||||||
|
unit::load_store::LoadStoreToDCacheInterface,
|
||||||
};
|
};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
|
|
@ -18,6 +19,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub mod alu_branch;
|
pub mod alu_branch;
|
||||||
|
pub mod load_store;
|
||||||
|
|
||||||
macro_rules! all_units {
|
macro_rules! all_units {
|
||||||
(
|
(
|
||||||
|
|
@ -333,7 +335,7 @@ all_units! {
|
||||||
#[create_dyn_unit_fn = |config, unit_index, filter| todo!()]
|
#[create_dyn_unit_fn = |config, unit_index, filter| todo!()]
|
||||||
#[extract(transformed_move_mop, transformed_move_mop_sim, transformed_move_mop_sim_ref, transformed_move_mop_sim_mut)]
|
#[extract(transformed_move_mop, transformed_move_mop_sim, transformed_move_mop_sim_ref, transformed_move_mop_sim_mut)]
|
||||||
TransformedMove(TransformedMoveOp),
|
TransformedMove(TransformedMoveOp),
|
||||||
#[create_dyn_unit_fn = |config, unit_index, filter| todo!()]
|
#[create_dyn_unit_fn = |config, unit_index, filter| load_store::LoadStore::new(config, unit_index, filter).to_dyn()]
|
||||||
#[extract(load_store_mop, load_store_mop_sim, load_store_mop_sim_ref, load_store_mop_sim_mut)]
|
#[extract(load_store_mop, load_store_mop_sim, load_store_mop_sim_ref, load_store_mop_sim_mut)]
|
||||||
LoadStore(LoadStoreMOp<DestReg, SrcReg>),
|
LoadStore(LoadStoreMOp<DestReg, SrcReg>),
|
||||||
}
|
}
|
||||||
|
|
@ -391,6 +393,7 @@ impl RenamedMOpFilter for () {
|
||||||
pub struct UnitIO {
|
pub struct UnitIO {
|
||||||
pub cd: Option<Expr<ClockDomain>>,
|
pub cd: Option<Expr<ClockDomain>>,
|
||||||
pub from_execute: Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>,
|
pub from_execute: Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>,
|
||||||
|
pub to_d_cache: Option<Expr<LoadStoreToDCacheInterface<PhantomConst<CpuConfig>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UnitTrait:
|
pub trait UnitTrait:
|
||||||
|
|
|
||||||
|
|
@ -1617,6 +1617,7 @@ impl UnitTrait for AluBranch {
|
||||||
UnitIO {
|
UnitIO {
|
||||||
cd: None,
|
cd: None,
|
||||||
from_execute: this.from_execute,
|
from_execute: this.from_execute,
|
||||||
|
to_d_cache: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
237
crates/cpu/src/unit/load_store.rs
Normal file
237
crates/cpu/src/unit/load_store.rs
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::CpuConfig,
|
||||||
|
instruction::LoadStoreMOp,
|
||||||
|
main_memory_and_io::MemoryOperationErrorKind,
|
||||||
|
next_pc::CallStackOp,
|
||||||
|
register::PRegValue,
|
||||||
|
rename_execute_retire::{
|
||||||
|
ExecuteToUnitInterface, NextPcPredictorOp, RenamedMOp, UnitCausedCancel,
|
||||||
|
UnitFinishCauseCancel, UnitInputsReady, UnitOutputReady,
|
||||||
|
},
|
||||||
|
unit::{DynUnit, DynUnitWrapper, RenamedMOpFilter, UnitIO, UnitKind, UnitTrait},
|
||||||
|
};
|
||||||
|
use fayalite::{intern::Interned, prelude::*, util::ready_valid::ReadyValid};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct DCacheOpKindLoad {
|
||||||
|
/// if this is `false`, then if the address refers to a valid cache line, that cache line must have any dirty data
|
||||||
|
/// written back to memory and then be set to invalid before performing the load.
|
||||||
|
pub is_cacheable: Bool,
|
||||||
|
/// if this is `true`, then the load must only look in the cache (must not propagate to the L2/L3 cache or to
|
||||||
|
/// memory), must not cause any earlier operations' timing to change because this load exists, and must not change
|
||||||
|
/// any cache state that remains after all operations finish, including LRU state, random number generators,
|
||||||
|
/// predictive prefetching state, etc.
|
||||||
|
/// Speculative loads that miss the cache must return [`DCacheFinishStatus::NotFound`].
|
||||||
|
pub is_speculative: Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct DCacheOpKindStore {
|
||||||
|
/// if this is `false`, then if the address refers to a valid cache line, that cache line must have any dirty data
|
||||||
|
/// written back to memory and then be set to invalid before performing the store.
|
||||||
|
pub is_cacheable: Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub enum DCacheOpKind {
|
||||||
|
Load(DCacheOpKindLoad),
|
||||||
|
Store(DCacheOpKindStore),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct DCacheStart<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
pub kind: DCacheOpKind,
|
||||||
|
/// unaligned addresses may cause a load/store to cross a cache-line boundary
|
||||||
|
pub address: UInt<64>,
|
||||||
|
pub data_and_mask: Array<HdlOption<UInt<8>>, 8>,
|
||||||
|
pub config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub enum DCacheFinishStatus {
|
||||||
|
/// The operation finished successfully
|
||||||
|
Success,
|
||||||
|
/// A speculative load missed the cache, so data was not returned.
|
||||||
|
NotFound,
|
||||||
|
MemoryError(MemoryOperationErrorKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct DCacheFinish<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
pub status: DCacheFinishStatus,
|
||||||
|
pub data: Array<UInt<8>, 8>,
|
||||||
|
pub config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub struct LoadStoreToDCacheInterface<C: PhantomConstGet<CpuConfig>> {
|
||||||
|
pub start: ReadyValid<DCacheStart<C>>,
|
||||||
|
#[hdl(flip)]
|
||||||
|
pub finish: ReadyValid<DCacheFinish<C>>,
|
||||||
|
pub config: C,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
async fn load_store_impl(
|
||||||
|
config: PhantomConst<CpuConfig>,
|
||||||
|
unit_index: usize,
|
||||||
|
filter: LoadStoreFilter,
|
||||||
|
cd: Expr<ClockDomain>,
|
||||||
|
from_execute: Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>>,
|
||||||
|
to_d_cache: Expr<LoadStoreToDCacheInterface<PhantomConst<CpuConfig>>>,
|
||||||
|
mut sim: ExternModuleSimulationState,
|
||||||
|
) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
struct LoadStoreFilter {
|
||||||
|
load: bool,
|
||||||
|
store: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadStoreFilter {
|
||||||
|
fn new(
|
||||||
|
from_execute: ExecuteToUnitInterface<PhantomConst<CpuConfig>>,
|
||||||
|
filter: &mut impl RenamedMOpFilter,
|
||||||
|
) -> Self {
|
||||||
|
let LoadStoreMOp { Load, Store } = from_execute
|
||||||
|
.inputs_ready
|
||||||
|
.HdlSome
|
||||||
|
.mop
|
||||||
|
.mop
|
||||||
|
.inner_ty()
|
||||||
|
.LoadStore;
|
||||||
|
Self {
|
||||||
|
load: filter.should_include_ty(Load),
|
||||||
|
store: filter.should_include_ty(Store),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module(extern)]
|
||||||
|
pub fn load_store(
|
||||||
|
config: PhantomConst<CpuConfig>,
|
||||||
|
unit_index: usize,
|
||||||
|
filter: &mut impl RenamedMOpFilter,
|
||||||
|
) {
|
||||||
|
#[hdl]
|
||||||
|
let cd: ClockDomain = m.input();
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let from_execute: ExecuteToUnitInterface<PhantomConst<CpuConfig>> =
|
||||||
|
m.input(ExecuteToUnitInterface[config]);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let to_d_cache: LoadStoreToDCacheInterface<PhantomConst<CpuConfig>> =
|
||||||
|
m.output(LoadStoreToDCacheInterface[config]);
|
||||||
|
|
||||||
|
let filter = LoadStoreFilter::new(from_execute.ty(), filter);
|
||||||
|
|
||||||
|
assert_eq!(config.get().units[unit_index].kind, UnitKind::LoadStore);
|
||||||
|
|
||||||
|
m.register_clock_for_past(cd.clk);
|
||||||
|
|
||||||
|
m.extern_module_simulation_fn(
|
||||||
|
(config, unit_index, filter, cd, from_execute, to_d_cache),
|
||||||
|
async |(config, unit_index, filter, cd, from_execute, to_d_cache), mut sim| {
|
||||||
|
sim.resettable(
|
||||||
|
cd,
|
||||||
|
async |mut sim| {
|
||||||
|
#[hdl]
|
||||||
|
let ExecuteToUnitInterface::<_> {
|
||||||
|
global_state: _,
|
||||||
|
enqueue,
|
||||||
|
inputs_ready: _,
|
||||||
|
is_no_longer_speculative: _,
|
||||||
|
cant_cause_cancel,
|
||||||
|
output_ready,
|
||||||
|
finish_cause_cancel,
|
||||||
|
unit_outputs_ready: _,
|
||||||
|
cancel_all,
|
||||||
|
config: _,
|
||||||
|
} = from_execute;
|
||||||
|
sim.write(enqueue.ready, false).await;
|
||||||
|
sim.write(cant_cause_cancel, cant_cause_cancel.ty().HdlNone())
|
||||||
|
.await;
|
||||||
|
sim.write(output_ready, output_ready.ty().HdlNone()).await;
|
||||||
|
sim.write(finish_cause_cancel, finish_cause_cancel.ty().HdlNone())
|
||||||
|
.await;
|
||||||
|
sim.write(cancel_all.ready, false).await;
|
||||||
|
#[hdl]
|
||||||
|
let LoadStoreToDCacheInterface::<_> {
|
||||||
|
start,
|
||||||
|
finish,
|
||||||
|
config: _,
|
||||||
|
} = to_d_cache;
|
||||||
|
sim.write(start.data, start.ty().data.HdlNone()).await;
|
||||||
|
sim.write(finish.ready, false).await;
|
||||||
|
},
|
||||||
|
async |sim, ()| {
|
||||||
|
load_store_impl(
|
||||||
|
config,
|
||||||
|
unit_index,
|
||||||
|
filter,
|
||||||
|
cd,
|
||||||
|
from_execute,
|
||||||
|
to_d_cache,
|
||||||
|
sim,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LoadStore {
|
||||||
|
config: PhantomConst<CpuConfig>,
|
||||||
|
module: Interned<Module<load_store>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadStore {
|
||||||
|
pub fn new(
|
||||||
|
config: PhantomConst<CpuConfig>,
|
||||||
|
unit_index: usize,
|
||||||
|
filter: &mut impl RenamedMOpFilter,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
module: load_store(config, unit_index, filter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnitTrait for LoadStore {
|
||||||
|
type Type = load_store;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
self.module.io_ty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit_kind(&self) -> UnitKind {
|
||||||
|
UnitKind::LoadStore
|
||||||
|
}
|
||||||
|
|
||||||
|
fn module(&self) -> Interned<Module<Self::Type>> {
|
||||||
|
self.module
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io(&self, this: Expr<Self::Type>) -> UnitIO {
|
||||||
|
UnitIO {
|
||||||
|
cd: Some(this.cd),
|
||||||
|
from_execute: this.from_execute,
|
||||||
|
to_d_cache: Some(this.to_d_cache),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_dyn(&self) -> DynUnit {
|
||||||
|
DynUnitWrapper(*self).to_dyn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,13 +94,17 @@ fn formal_harness(
|
||||||
let UnitIO {
|
let UnitIO {
|
||||||
cd: unit_cd,
|
cd: unit_cd,
|
||||||
from_execute: unit_from_execute,
|
from_execute: unit_from_execute,
|
||||||
|
to_d_cache,
|
||||||
} = dyn_unit.io(unit);
|
} = dyn_unit.io(unit);
|
||||||
|
if let Some(unit_cd) = unit_cd {
|
||||||
|
connect(unit_cd, cd);
|
||||||
|
}
|
||||||
connect(
|
connect(
|
||||||
unit_from_execute,
|
unit_from_execute,
|
||||||
ExecuteToUnitInterfaces::unit_fields(decode_and_run.to_units)[unit_index],
|
ExecuteToUnitInterfaces::unit_fields(decode_and_run.to_units)[unit_index],
|
||||||
);
|
);
|
||||||
if let Some(unit_cd) = unit_cd {
|
if let Some(to_d_cache) = to_d_cache {
|
||||||
connect(unit_cd, cd);
|
unimplemented!("to_d_cache for {unit:?}: {to_d_cache:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hdl_assert(cd.clk, !decode_and_run.error, "");
|
hdl_assert(cd.clk, !decode_and_run.error, "");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue