forked from libre-chip/cpu
WIP
This commit is contained in:
parent
dca59a24ec
commit
b6f3ecfb32
6 changed files with 606 additions and 17 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -211,6 +211,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"fayalite",
|
||||
"serde",
|
||||
"simple-mermaid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -690,6 +691,12 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simple-mermaid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589144a964b4b30fe3a83b4bb1a09e2475aac194ec832a046a23e75bddf9eb29"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ rust-version = "1.89.0"
|
|||
[workspace.dependencies]
|
||||
fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" }
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
simple-mermaid = "0.2.0"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@ version.workspace = true
|
|||
[dependencies]
|
||||
fayalite.workspace = true
|
||||
serde.workspace = true
|
||||
simple-mermaid.workspace = true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
//! after the `decode` stage there's a `post_decode` stage (that may run in the same clock cycle as `decode`)
|
||||
//! that checks that the fetched instructions' kinds match the predicted instruction kinds and that feeds
|
||||
//! information back to the `fetch` stage to cancel fetches that need to be predicted differently.
|
||||
//!
|
||||
#![doc = simple_mermaid::mermaid!("next_pc/next_pc.mermaid")]
|
||||
|
||||
use crate::{
|
||||
config::{CpuConfig, CpuConfigFetchWidth},
|
||||
|
|
@ -17,6 +19,7 @@ use fayalite::{
|
|||
int::{UIntInRange, UIntInRangeInclusive, UIntInRangeType},
|
||||
prelude::*,
|
||||
sim::value::SimOnlyValueTrait,
|
||||
ty::StaticType,
|
||||
util::ready_valid::ReadyValid,
|
||||
};
|
||||
|
||||
|
|
@ -142,6 +145,505 @@ pub struct PostDecodeOutputInterface<C: PhantomConstGet<CpuConfig>> {
|
|||
pub config: C,
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct Cancel<C: PhantomConstGet<CpuConfig>> {
|
||||
call_stack: CallStack,
|
||||
start_pc: UInt<64>,
|
||||
new_btb_entry: HdlOption<BTBEntryWithoutStartPc>,
|
||||
btb_entry_index: HdlOption<UIntInRange<0, { BranchTargetBuffer::SIZE }>>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
/// the output of `Stage::run`.
|
||||
/// when cancelling operations, the returned [`StageOutput.cancel`] should be the state after running all operations returned in [`StageOutput.output`]
|
||||
#[hdl(no_static)]
|
||||
struct StageOutput<Output, MaxOutputCount: Size, C: PhantomConstGet<CpuConfig>> {
|
||||
outputs: ArrayVec<Output, MaxOutputCount>,
|
||||
cancel: HdlOption<Cancel<C>>,
|
||||
}
|
||||
|
||||
trait Stage: Type + SimValueDefault + ResetSteps {
|
||||
type Inputs: Type;
|
||||
type Output: Type;
|
||||
type MaxOutputCount: Size;
|
||||
|
||||
fn output_ty(config: PhantomConst<CpuConfig>) -> Self::Output;
|
||||
fn max_output_count(
|
||||
config: PhantomConst<CpuConfig>,
|
||||
) -> <Self::MaxOutputCount as Size>::SizeType;
|
||||
fn stage_output_ty(
|
||||
config: PhantomConst<CpuConfig>,
|
||||
) -> StageOutput<Self::Output, Self::MaxOutputCount, PhantomConst<CpuConfig>> {
|
||||
StageOutput[Self::output_ty(config)][Self::max_output_count(config)][config]
|
||||
}
|
||||
fn run(
|
||||
state: &mut SimValue<Self>,
|
||||
inputs: &SimValue<Self::Inputs>,
|
||||
) -> SimValue<StageOutput<Self::Output, Self::MaxOutputCount, PhantomConst<CpuConfig>>>;
|
||||
/// changes state to match `cancel`
|
||||
fn cancel(state: &mut SimValue<Self>, cancel: &SimValue<Cancel<PhantomConst<CpuConfig>>>);
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct NextPcStageOutput<C: PhantomConstGet<CpuConfig>> {
|
||||
start_pc: UInt<64>,
|
||||
next_start_pc: UInt<64>,
|
||||
btb_entry: HdlOption<(
|
||||
UIntInRange<0, { BranchTargetBuffer::SIZE }>,
|
||||
BTBEntryWithoutStartPc,
|
||||
)>,
|
||||
fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct NextPcStageState<C: PhantomConstGet<CpuConfig>> {
|
||||
call_stack: CallStack,
|
||||
branch_target_buffer: BranchTargetBuffer,
|
||||
next_pc: UInt<64>,
|
||||
next_fetch_block_id: UInt<{ FETCH_BLOCK_ID_WIDTH }>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl SimValueDefault for NextPcStageState<PhantomConst<CpuConfig>> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
call_stack,
|
||||
branch_target_buffer,
|
||||
next_pc: _,
|
||||
next_fetch_block_id: _,
|
||||
config,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
call_stack: call_stack.sim_value_default(),
|
||||
branch_target_buffer: branch_target_buffer.sim_value_default(),
|
||||
// use something other than the default so you can see the reset progress
|
||||
next_pc: !0u64,
|
||||
// use something other than the default so you can see the reset progress
|
||||
next_fetch_block_id: !0u8,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetSteps for NextPcStageState<PhantomConst<CpuConfig>> {
|
||||
#[hdl]
|
||||
fn reset_step(this: &mut SimValue<Self>, step: usize) -> ResetStatus {
|
||||
#[hdl(sim)]
|
||||
let Self {
|
||||
call_stack,
|
||||
branch_target_buffer,
|
||||
next_pc,
|
||||
next_fetch_block_id,
|
||||
config: _,
|
||||
} = this;
|
||||
**next_pc = 0u64.into(); // match Microwatt's reset PC
|
||||
**next_fetch_block_id = 0u8.into();
|
||||
let call_stack = ResetSteps::reset_step(call_stack, step);
|
||||
let branch_target_buffer = ResetSteps::reset_step(branch_target_buffer, step);
|
||||
call_stack.and(branch_target_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage for NextPcStageState<PhantomConst<CpuConfig>> {
|
||||
type Inputs = ();
|
||||
type Output = NextPcStageOutput<PhantomConst<CpuConfig>>;
|
||||
type MaxOutputCount = ConstUsize<1>;
|
||||
|
||||
fn output_ty(config: PhantomConst<CpuConfig>) -> Self::Output {
|
||||
NextPcStageOutput[config]
|
||||
}
|
||||
|
||||
fn max_output_count(
|
||||
_config: PhantomConst<CpuConfig>,
|
||||
) -> <Self::MaxOutputCount as Size>::SizeType {
|
||||
ConstUsize
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn run(
|
||||
state: &mut SimValue<Self>,
|
||||
_inputs: &SimValue<Self::Inputs>,
|
||||
) -> SimValue<StageOutput<Self::Output, Self::MaxOutputCount, PhantomConst<CpuConfig>>> {
|
||||
let config = state.config.ty();
|
||||
let fetch_block_id = state.next_fetch_block_id.as_int();
|
||||
*state.next_fetch_block_id = state.next_fetch_block_id.as_int().wrapping_add(1).into();
|
||||
let start_pc = state.next_pc.as_int();
|
||||
let fetch_pc = start_pc & (!0u64 << config.get().log2_fetch_width_in_bytes);
|
||||
|
||||
let btb_entry_index = state
|
||||
.branch_target_buffer
|
||||
.branch_pc_to_target_map
|
||||
.iter()
|
||||
.position(|entry| {
|
||||
#[hdl(sim)]
|
||||
match entry {
|
||||
HdlNone => false,
|
||||
HdlSome(entry) => entry.start_pc.as_int() == start_pc,
|
||||
}
|
||||
});
|
||||
let (next_start_pc, btb_entry) = if let Some(btb_entry_index) = btb_entry_index {
|
||||
#[hdl(sim)]
|
||||
let Self {
|
||||
call_stack,
|
||||
branch_target_buffer,
|
||||
..
|
||||
} = state;
|
||||
let entry = #[hdl(sim)]
|
||||
match &branch_target_buffer.branch_pc_to_target_map[btb_entry_index] {
|
||||
HdlSome(entry) => entry,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let next_start_pc = #[hdl(sim)]
|
||||
match &entry.rest.insn_kind {
|
||||
BTBEntryInsnKind::Branch => {
|
||||
if BTBEntryAddrKind::taken(&entry.rest.addr_kind) {
|
||||
BTBEntry::taken_pc(entry)
|
||||
} else {
|
||||
BTBEntry::not_taken_start_pc(entry)
|
||||
}
|
||||
}
|
||||
BTBEntryInsnKind::Call => {
|
||||
if BTBEntryAddrKind::taken(&entry.rest.addr_kind) {
|
||||
CallStack::push(call_stack, BTBEntry::after_call_pc(entry));
|
||||
BTBEntry::taken_pc(entry)
|
||||
} else {
|
||||
BTBEntry::not_taken_start_pc(entry)
|
||||
}
|
||||
}
|
||||
BTBEntryInsnKind::Ret => {
|
||||
if BTBEntryAddrKind::taken(&entry.rest.addr_kind) {
|
||||
CallStack::pop(call_stack).unwrap_or(BTBEntry::taken_pc(entry))
|
||||
} else {
|
||||
BTBEntry::not_taken_start_pc(entry)
|
||||
}
|
||||
}
|
||||
BTBEntryInsnKind::Unknown => unreachable!(),
|
||||
};
|
||||
(
|
||||
next_start_pc,
|
||||
#[hdl(sim)]
|
||||
HdlSome((btb_entry_index, &entry.rest)),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
fetch_pc.wrapping_add(config.get().fetch_width_in_bytes() as u64),
|
||||
#[hdl(sim)]
|
||||
HdlNone(),
|
||||
)
|
||||
};
|
||||
let output = #[hdl(sim)]
|
||||
NextPcStageOutput::<_> {
|
||||
start_pc,
|
||||
next_start_pc,
|
||||
btb_entry,
|
||||
fetch_block_id,
|
||||
config,
|
||||
};
|
||||
#[hdl(sim)]
|
||||
StageOutput::<_, _, _> {
|
||||
outputs: Self::stage_output_ty(config).outputs.new_full_sim([output]),
|
||||
cancel: #[hdl(sim)]
|
||||
(HdlOption[Cancel[config]]).HdlNone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn cancel(state: &mut SimValue<Self>, cancel: &SimValue<Cancel<PhantomConst<CpuConfig>>>) {
|
||||
#[hdl(sim)]
|
||||
let Self {
|
||||
call_stack,
|
||||
branch_target_buffer,
|
||||
next_pc,
|
||||
next_fetch_block_id: _,
|
||||
config: _,
|
||||
} = state;
|
||||
#[hdl(sim)]
|
||||
let Cancel::<_> {
|
||||
call_stack: new_call_stack,
|
||||
start_pc,
|
||||
new_btb_entry,
|
||||
btb_entry_index,
|
||||
config: _,
|
||||
} = cancel;
|
||||
call_stack.clone_from(new_call_stack);
|
||||
next_pc.clone_from(start_pc);
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(new_btb_entry) = new_btb_entry {
|
||||
// add/update btb entry
|
||||
|
||||
// get old entry if it's still there
|
||||
let btb_entry_index = #[hdl(sim)]
|
||||
if let HdlSome(btb_entry_index) = btb_entry_index {
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(entry) =
|
||||
&branch_target_buffer.branch_pc_to_target_map[**btb_entry_index]
|
||||
{
|
||||
if entry.start_pc == *start_pc {
|
||||
// found the old entry
|
||||
Some(**btb_entry_index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let btb_entry_index = btb_entry_index.unwrap_or_else(|| {
|
||||
// old entry isn't there, pick an entry to replace
|
||||
BranchTargetBuffer::next_index_to_replace(branch_target_buffer)
|
||||
});
|
||||
|
||||
// replace with new entry
|
||||
branch_target_buffer.branch_pc_to_target_map[btb_entry_index] = #[hdl(sim)]
|
||||
HdlSome(
|
||||
#[hdl(sim)]
|
||||
BTBEntry {
|
||||
start_pc,
|
||||
rest: new_btb_entry,
|
||||
},
|
||||
);
|
||||
} else if let HdlSome(btb_entry_index) = btb_entry_index {
|
||||
// remove btb entry if it's still there
|
||||
let entry_mut = &mut branch_target_buffer.branch_pc_to_target_map[**btb_entry_index];
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(entry) = &entry_mut {
|
||||
if entry.start_pc == *start_pc {
|
||||
// we found it, remove it
|
||||
*entry_mut = #[hdl(sim)]
|
||||
HdlNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct BrPredStageOutput<C: PhantomConstGet<CpuConfig>> {
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl SimValueDefault for BrPredStageOutput<PhantomConst<CpuConfig>> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
config: self.config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct BrPredStageState<C: PhantomConstGet<CpuConfig>> {
|
||||
branch_history: UInt<6>,
|
||||
branch_predictor: Array<BranchPredictionState, { BRANCH_PREDICTOR_SIZE }>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl BrPredStageState<PhantomConst<CpuConfig>> {
|
||||
fn branch_predictor_index(this: &SimValue<Self>, branch_pc: u64) -> usize {
|
||||
let mut t = this.branch_history.cast_to_static::<UInt<64>>().as_int();
|
||||
t ^= t.rotate_left(5) & !branch_pc.rotate_right(3);
|
||||
t ^= branch_pc;
|
||||
t ^= !t.rotate_left(2) & t.rotate_left(4);
|
||||
let mut retval = 0;
|
||||
for i in (0..BRANCH_PREDICTOR_LOG2_SIZE).step_by(BRANCH_PREDICTOR_LOG2_SIZE) {
|
||||
retval ^= t >> i;
|
||||
}
|
||||
retval as usize % BRANCH_PREDICTOR_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl SimValueDefault for BrPredStageState<PhantomConst<CpuConfig>> {
|
||||
#[hdl]
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
let Self {
|
||||
branch_history: _,
|
||||
branch_predictor: _,
|
||||
config,
|
||||
} = self;
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
// use something other than the default so you can see the reset progress
|
||||
branch_history: (-1i8).cast_to_static::<UInt<_>>(),
|
||||
// use something other than the default so you can see the reset progress
|
||||
branch_predictor: std::array::from_fn(|_| {
|
||||
BranchPredictionState::towards_not_taken(&BranchPredictionState.sim_value_default())
|
||||
}),
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetSteps for BrPredStageState<PhantomConst<CpuConfig>> {
|
||||
#[hdl]
|
||||
fn reset_step(this: &mut SimValue<Self>, step: usize) -> ResetStatus {
|
||||
#[hdl(sim)]
|
||||
let Self {
|
||||
branch_history,
|
||||
branch_predictor,
|
||||
config: _,
|
||||
} = this;
|
||||
**branch_history = 0u8.cast_to_static::<UInt<_>>();
|
||||
ResetSteps::reset_step(branch_predictor, step)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage for BrPredStageState<PhantomConst<CpuConfig>> {
|
||||
type Inputs = NextPcStageOutput<PhantomConst<CpuConfig>>;
|
||||
type Output = BrPredStageOutput<PhantomConst<CpuConfig>>;
|
||||
type MaxOutputCount = ConstUsize<1>;
|
||||
|
||||
fn output_ty(config: PhantomConst<CpuConfig>) -> Self::Output {
|
||||
BrPredStageOutput[config]
|
||||
}
|
||||
|
||||
fn max_output_count(
|
||||
_config: PhantomConst<CpuConfig>,
|
||||
) -> <Self::MaxOutputCount as Size>::SizeType {
|
||||
ConstUsize
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
fn run(
|
||||
state: &mut SimValue<Self>,
|
||||
inputs: &SimValue<Self::Inputs>,
|
||||
) -> SimValue<StageOutput<Self::Output, Self::MaxOutputCount, PhantomConst<CpuConfig>>> {
|
||||
let config = state.config.ty();
|
||||
#[hdl(sim)]
|
||||
let NextPcStageOutput::<_> {
|
||||
start_pc,
|
||||
next_start_pc,
|
||||
btb_entry,
|
||||
fetch_block_id,
|
||||
config: _,
|
||||
} = inputs;
|
||||
#[hdl(sim)]
|
||||
if let HdlSome(btb_entry) = btb_entry {
|
||||
let taken = #[hdl(sim)]
|
||||
match &btb_entry.1.addr_kind {
|
||||
BTBEntryAddrKind::Unconditional | BTBEntryAddrKind::Indirect => None,
|
||||
BTBEntryAddrKind::CondTaken => Some(true),
|
||||
BTBEntryAddrKind::CondNotTaken => Some(false),
|
||||
};
|
||||
if let Some(taken) = taken {
|
||||
let index = Self::branch_predictor_index(
|
||||
state,
|
||||
BTBEntry::branch_pc(
|
||||
&#[hdl(sim)]
|
||||
BTBEntry {
|
||||
start_pc,
|
||||
rest: &btb_entry.1,
|
||||
},
|
||||
),
|
||||
);
|
||||
if taken != BranchPredictionState::is_taken(&state.branch_predictor[index]) {
|
||||
let retval = #[hdl(sim)]
|
||||
StageOutput::<_, _, _> {
|
||||
outputs: Self::stage_output_ty(config).outputs.sim_value_default(),
|
||||
cancel: #[hdl(sim)]
|
||||
(HdlOption[Cancel[config]]).HdlSome(
|
||||
#[hdl(sim)]
|
||||
Cancel::<_> {
|
||||
call_stack: todo!(),
|
||||
start_pc: todo!(),
|
||||
new_btb_entry: todo!(),
|
||||
btb_entry_index: todo!(),
|
||||
config: todo!(),
|
||||
},
|
||||
),
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
let output = #[hdl(sim)]
|
||||
BrPredStageOutput::<_> { config };
|
||||
#[hdl(sim)]
|
||||
StageOutput::<_, _, _> {
|
||||
outputs: Self::stage_output_ty(config).outputs.new_full_sim([output]),
|
||||
cancel: #[hdl(sim)]
|
||||
(HdlOption[Cancel[config]]).HdlNone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(state: &mut SimValue<Self>, cancel: &SimValue<Cancel<PhantomConst<CpuConfig>>>) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct FetchOpInFetchDecode<C: PhantomConstGet<CpuConfig>> {
|
||||
start_pc: UInt<64>,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: Type + PhantomConstGet<CpuConfig>> FetchOpInFetchDecode<C> {
|
||||
#[hdl]
|
||||
fn from_fetch_op_in_next_pc(v: SimValue<FetchOpInNextPc<C>>) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
let FetchOpInNextPc::<_> { start_pc, config } = v;
|
||||
#[hdl(sim)]
|
||||
Self { start_pc, config }
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct InsnInPostDecode<C: PhantomConstGet<CpuConfig>> {
|
||||
insn: WipDecodedInsn,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: Type + PhantomConstGet<CpuConfig>> InsnInPostDecode<C> {
|
||||
#[hdl]
|
||||
fn from_fetch_op_in_fetch_decode(
|
||||
fetch_op: SimValue<FetchOpInFetchDecode<C>>,
|
||||
insn: SimValue<WipDecodedInsn>,
|
||||
) -> SimValue<InsnInPostDecode<C>> {
|
||||
#[hdl(sim)]
|
||||
let FetchOpInFetchDecode::<_> {} = fetch_op;
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct InsnInRenameDispatchExecute<C: PhantomConstGet<CpuConfig>> {
|
||||
insn: WipDecodedInsn,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: Type + PhantomConstGet<CpuConfig>> InsnInRenameDispatchExecute<C> {
|
||||
#[hdl]
|
||||
fn from_insn_in_post_decode(v: SimValue<InsnInPostDecode<C>>) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
let InsnInPostDecode::<_> { insn, config } = v;
|
||||
#[hdl(sim)]
|
||||
Self { insn, config }
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl(no_static)]
|
||||
struct InsnInRetire<C: PhantomConstGet<CpuConfig>> {
|
||||
insn: WipDecodedInsn,
|
||||
config: C,
|
||||
}
|
||||
|
||||
impl<C: Type + PhantomConstGet<CpuConfig>> InsnInRetire<C> {
|
||||
#[hdl]
|
||||
fn from_insn_in_rename_dispatch_execute(
|
||||
v: SimValue<InsnInRenameDispatchExecute<C>>,
|
||||
) -> SimValue<Self> {
|
||||
#[hdl(sim)]
|
||||
let InsnInRenameDispatchExecute::<_> { insn, config } = v;
|
||||
#[hdl(sim)]
|
||||
Self { insn, config }
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
enum BranchPredictionState {
|
||||
StronglyNotTaken,
|
||||
|
|
@ -221,6 +723,12 @@ impl<T: SimOnlyValueTrait> SimValueDefault for SimOnly<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SimValueDefault, N: KnownSize> SimValueDefault for ArrayVec<T, N> {
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
self.new_sim(self.element().sim_value_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> SimValueDefault for HdlOption<T> {
|
||||
fn sim_value_default(self) -> SimValue<Self> {
|
||||
self.HdlNone().to_sim_value_with_type(self)
|
||||
|
|
@ -411,27 +919,51 @@ impl BTBEntryAddrKind {
|
|||
}
|
||||
|
||||
#[hdl]
|
||||
struct BTBEntry {
|
||||
/// address of first instruction to run in this fetch block
|
||||
start_pc: UInt<64>,
|
||||
struct BTBEntryWithoutStartPc {
|
||||
target_pc: UInt<64>,
|
||||
/// when branch is not taken, the next pc to fetch from is `start_pc + fallthrough_offset`.
|
||||
/// needed because there may be more than one branch in a fetch block
|
||||
fallthrough_offset: UInt<8>,
|
||||
/// the pc to use for branch prediction is `start_pc + branch_offset`
|
||||
branch_offset: UInt<8>,
|
||||
/// when a call is made, the return address is `start_pc + after_call_offset`
|
||||
after_call_offset: UInt<8>,
|
||||
insn_kind: BTBEntryInsnKind,
|
||||
addr_kind: BTBEntryAddrKind,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
struct BTBEntry {
|
||||
/// address of first instruction to run in this fetch block
|
||||
start_pc: UInt<64>,
|
||||
rest: BTBEntryWithoutStartPc,
|
||||
}
|
||||
|
||||
impl BTBEntry {
|
||||
fn taken_pc(this: &SimValue<Self>) -> u64 {
|
||||
this.target_pc.as_int()
|
||||
this.rest.target_pc.as_int()
|
||||
}
|
||||
fn not_taken_fetch_pc(this: &SimValue<Self>) -> u64 {
|
||||
fn not_taken_start_pc(this: &SimValue<Self>) -> u64 {
|
||||
Self::fallthrough_pc(this)
|
||||
}
|
||||
/// when branch is not taken, this returns the next pc to fetch from.
|
||||
/// needed because there may be more than one branch in a fetch block
|
||||
fn fallthrough_pc(this: &SimValue<Self>) -> u64 {
|
||||
this.start_pc
|
||||
.as_int()
|
||||
.wrapping_add(this.fallthrough_offset.as_int().into())
|
||||
.wrapping_add(this.rest.fallthrough_offset.as_int().into())
|
||||
}
|
||||
/// the pc to use for branch prediction
|
||||
fn branch_pc(this: &SimValue<Self>) -> u64 {
|
||||
this.start_pc
|
||||
.as_int()
|
||||
.wrapping_add(this.rest.branch_offset.as_int().into())
|
||||
}
|
||||
/// when a call is made, this gives the return address
|
||||
fn after_call_pc(this: &SimValue<Self>) -> u64 {
|
||||
this.start_pc
|
||||
.as_int()
|
||||
.wrapping_add(this.rest.after_call_offset.as_int().into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -489,11 +1021,15 @@ impl SimValueDefault for BranchTargetBuffer {
|
|||
#[hdl(sim)]
|
||||
BTBEntry {
|
||||
start_pc: !0u64,
|
||||
target_pc: !0u64,
|
||||
fallthrough_offset: !0u8,
|
||||
after_call_offset: !0u8,
|
||||
insn_kind: BTBEntryInsnKind.Call(),
|
||||
addr_kind: BTBEntryAddrKind.CondNotTaken(),
|
||||
rest: #[hdl(sim)]
|
||||
BTBEntryWithoutStartPc {
|
||||
target_pc: !0u64,
|
||||
fallthrough_offset: !0u8,
|
||||
branch_offset: !0u8,
|
||||
after_call_offset: !0u8,
|
||||
insn_kind: BTBEntryInsnKind.Call(),
|
||||
addr_kind: BTBEntryAddrKind.CondNotTaken(),
|
||||
},
|
||||
},
|
||||
); Self::SIZE],
|
||||
next_index_to_replace_lfsr: LFSR31.sim_value_default(),
|
||||
|
|
@ -1007,13 +1543,13 @@ impl<C: Type + PhantomConstGet<CpuConfig>> NextPcState<C> {
|
|||
// for now we just truncate the fetch block right before the second ctrl transfer insn.
|
||||
break;
|
||||
}
|
||||
btb_entry_fields = Some((insn_kind, addr_kind, target_pc));
|
||||
fallthrough_offset += size_in_bytes.cast_to_static::<UInt<8>>().as_int();
|
||||
#[hdl(sim)]
|
||||
match insn_kind {
|
||||
match &insn_kind {
|
||||
BTBEntryInsnKind::Call => after_call_offset = fallthrough_offset,
|
||||
BTBEntryInsnKind::Branch | BTBEntryInsnKind::Ret | BTBEntryInsnKind::Unknown => {}
|
||||
}
|
||||
btb_entry_fields = Some((insn_kind, addr_kind, target_pc));
|
||||
fallthrough_offset += size_in_bytes.cast_to_static::<UInt<8>>().as_int();
|
||||
}
|
||||
let new_next_pc = if let Some((insn_kind, addr_kind, mut target_pc)) = btb_entry_fields {
|
||||
// add/update BTBEntry if it doesn't match
|
||||
|
|
@ -1046,15 +1582,22 @@ impl<C: Type + PhantomConstGet<CpuConfig>> NextPcState<C> {
|
|||
BranchTargetBuffer::next_index_to_replace(&mut this.branch_target_buffer)
|
||||
});
|
||||
let new_next_pc = #[hdl(sim)]
|
||||
match insn_kind {
|
||||
BTBEntryInsnKind::Branch => {}
|
||||
match &insn_kind {
|
||||
BTBEntryInsnKind::Branch => {
|
||||
todo!()
|
||||
}
|
||||
BTBEntryInsnKind::Call => {
|
||||
CallStack::push(&mut this.speculative_call_stack, todo!());
|
||||
CallStack::push(
|
||||
&mut this.speculative_call_stack,
|
||||
start_pc + u64::from(after_call_offset),
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
BTBEntryInsnKind::Ret => {
|
||||
target_pc = CallStack::pop(&mut this.speculative_call_stack).or(target_pc);
|
||||
target_pc.unwrap_or(0u64)
|
||||
}
|
||||
BTBEntryInsnKind::Unknown => unreachable!(),
|
||||
};
|
||||
let new_entry = #[hdl(sim)]
|
||||
BTBEntry {
|
||||
|
|
|
|||
25
crates/cpu/src/next_pc/next_pc.mermaid
Normal file
25
crates/cpu/src/next_pc/next_pc.mermaid
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
stateDiagram-v2
|
||||
direction LR
|
||||
|
||||
state "Next PC" as next_pc
|
||||
[*] --> next_pc
|
||||
|
||||
state "Fetch/Decode" as fetch_decode
|
||||
next_pc --> fetch_decode
|
||||
|
||||
state "Branch Predictor" as br_pred
|
||||
next_pc --> br_pred
|
||||
br_pred --> next_pc: cancel following
|
||||
|
||||
state "Post-decode" as post_decode
|
||||
fetch_decode --> post_decode
|
||||
br_pred --> post_decode
|
||||
post_decode --> next_pc: cancel following
|
||||
|
||||
state "Rename\nDispatch\nExecute" as execute
|
||||
post_decode --> execute
|
||||
|
||||
state "Retire" as retire
|
||||
execute --> retire
|
||||
retire --> [*]
|
||||
retire --> next_pc: cancel following
|
||||
|
|
@ -34,6 +34,18 @@ impl<T: Type, N: Size> ArrayVec<T, N> {
|
|||
len: 0u8.cast_to(self.len),
|
||||
}
|
||||
}
|
||||
#[hdl]
|
||||
pub fn new_full_sim(
|
||||
self,
|
||||
elements: impl ToSimValueWithType<ArrayType<T, N>>,
|
||||
) -> SimValue<Self> {
|
||||
let elements = elements.to_sim_value_with_type(self.elements);
|
||||
#[hdl(sim)]
|
||||
Self {
|
||||
elements,
|
||||
len: self.elements.len().to_sim_value_with_type(self.len),
|
||||
}
|
||||
}
|
||||
pub fn element(self) -> T {
|
||||
self.elements.element()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue