forked from libre-chip/cpu
Compare commits
10 commits
ef30d325d5
...
126119301e
| Author | SHA1 | Date | |
|---|---|---|---|
| 126119301e | |||
| fb0b07fbb2 | |||
| b6bbc97990 | |||
| ad9ec46aca | |||
| d38bc786a7 | |||
| 6ee0d4265c | |||
| ebd69089c5 | |||
| 51d71c56f6 | |||
| 999860b507 | |||
| 9d9a755d99 |
21 changed files with 841153 additions and 41088 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -388,7 +388,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
|||
[[package]]
|
||||
name = "fayalite"
|
||||
version = "0.3.0"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#ffca1a279d79507c025a8d969bfff18295760c02"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitvec",
|
||||
|
|
@ -417,7 +417,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "fayalite-proc-macros"
|
||||
version = "0.3.0"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#ffca1a279d79507c025a8d969bfff18295760c02"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
|
||||
dependencies = [
|
||||
"fayalite-proc-macros-impl",
|
||||
]
|
||||
|
|
@ -425,7 +425,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "fayalite-proc-macros-impl"
|
||||
version = "0.3.0"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#ffca1a279d79507c025a8d969bfff18295760c02"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
|
||||
dependencies = [
|
||||
"base16ct 0.2.0",
|
||||
"num-bigint",
|
||||
|
|
@ -440,7 +440,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "fayalite-visit-gen"
|
||||
version = "0.3.0"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#ffca1a279d79507c025a8d969bfff18295760c02"
|
||||
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#bb34aeb7f1881ad03a320f9f792994a88afe5fe2"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
|
|
|
|||
|
|
@ -535,6 +535,8 @@ impl<SrcCount: KnownSize> StaticType for CommonMOpDefaultImm<SrcCount> {
|
|||
}
|
||||
|
||||
impl<SrcCount: KnownSize> HdlPartialEqImpl<Self> for CommonMOpDefaultImm<SrcCount> {
|
||||
const TRY_STRUCTURAL_EQ: bool = true;
|
||||
|
||||
#[track_caller]
|
||||
fn cmp_value_eq(
|
||||
_lhs: Self,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
pub mod alu_branch;
|
||||
pub mod load_store;
|
||||
|
||||
macro_rules! all_units {
|
||||
(
|
||||
|
|
@ -333,7 +334,7 @@ all_units! {
|
|||
#[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)]
|
||||
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)]
|
||||
LoadStore(LoadStoreMOp<DestReg, SrcReg>),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,8 +87,11 @@ fn add_sub<C: Type + PhantomConstCpuConfig, SrcCount: KnownSize>(config: C) {
|
|||
connect(src1, src_values[1].int_fp);
|
||||
}
|
||||
#[hdl]
|
||||
let carry_in = wire();
|
||||
connect(carry_in, carry_in_before_inversion ^ invert_carry_in);
|
||||
let carry_in: UInt<1> = wire();
|
||||
connect(
|
||||
carry_in,
|
||||
(carry_in_before_inversion ^ invert_carry_in).cast_to_static::<UInt<1>>(),
|
||||
);
|
||||
#[hdl]
|
||||
let src0 = wire();
|
||||
connect(src0, src_values[0].int_fp);
|
||||
|
|
@ -104,7 +107,12 @@ fn add_sub<C: Type + PhantomConstCpuConfig, SrcCount: KnownSize>(config: C) {
|
|||
connect(pc_or_zero, pc);
|
||||
}
|
||||
let sum_of_sliced = |slice: RangeTo<usize>| {
|
||||
src0[slice] + src1[slice] + src_values[2].int_fp[slice] + pc_or_zero[slice] + imm[slice]
|
||||
src0[slice]
|
||||
+ src1[slice]
|
||||
+ src_values[2].int_fp[slice]
|
||||
+ pc_or_zero[slice]
|
||||
+ imm[slice]
|
||||
+ carry_in
|
||||
};
|
||||
#[hdl]
|
||||
let sum: UInt<64> = wire();
|
||||
|
|
|
|||
157
crates/cpu/src/unit/load_store.rs
Normal file
157
crates/cpu/src/unit/load_store.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
config::CpuConfig,
|
||||
instruction::LoadStoreMOp,
|
||||
next_pc::CallStackOp,
|
||||
register::PRegValue,
|
||||
rename_execute_retire::{
|
||||
ExecuteToUnitInterface, NextPcPredictorOp, RenamedMOp, UnitCausedCancel,
|
||||
UnitFinishCauseCancel, UnitInputsReady, UnitOutputReady,
|
||||
},
|
||||
unit::{DynUnit, DynUnitWrapper, RenamedMOpFilter, UnitKind, UnitTrait},
|
||||
};
|
||||
use fayalite::{intern::Interned, prelude::*};
|
||||
|
||||
#[hdl]
|
||||
async fn load_store_impl(
|
||||
config: PhantomConst<CpuConfig>,
|
||||
unit_index: usize,
|
||||
filter: LoadStoreFilter,
|
||||
cd: Expr<ClockDomain>,
|
||||
from_execute: Expr<ExecuteToUnitInterface<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]);
|
||||
|
||||
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),
|
||||
async |(config, unit_index, filter, cd, from_execute), 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;
|
||||
},
|
||||
async |sim, ()| {
|
||||
load_store_impl(config, unit_index, filter, cd, from_execute, 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 cd(&self, this: Expr<Self::Type>) -> Option<Expr<ClockDomain>> {
|
||||
Some(this.cd)
|
||||
}
|
||||
|
||||
fn from_execute(
|
||||
&self,
|
||||
this: Expr<Self::Type>,
|
||||
) -> Expr<ExecuteToUnitInterface<PhantomConst<CpuConfig>>> {
|
||||
this.from_execute
|
||||
}
|
||||
|
||||
fn to_dyn(&self) -> DynUnit {
|
||||
DynUnitWrapper(*self).to_dyn()
|
||||
}
|
||||
}
|
||||
|
|
@ -317,152 +317,3 @@ impl LFSR31 {
|
|||
state
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CheckedVcdOutput {
|
||||
writer: Option<RcWriter>,
|
||||
expected_path: PathBuf,
|
||||
expected_contents: Result<String, (Option<PathBuf>, std::io::Error)>,
|
||||
location: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl CheckedVcdOutput {
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn new<T: BundleType>(sim: &mut Simulation<T>, expected_path: PathBuf) -> Self {
|
||||
let writer = RcWriter::default();
|
||||
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||
Self {
|
||||
writer: Some(writer),
|
||||
expected_contents: std::fs::read_to_string(&expected_path).map_err(|e| {
|
||||
eprintln!(
|
||||
"error: failed to read expected VCD from: {}",
|
||||
expected_path.display(),
|
||||
);
|
||||
(std::env::current_dir().ok(), e)
|
||||
}),
|
||||
expected_path,
|
||||
location: Location::caller(),
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
#[doc(hidden)]
|
||||
pub fn __checked_vcd_output_macro_helper<T: BundleType>(
|
||||
sim: &mut Simulation<T>,
|
||||
cargo_manifest_dir: &'static str,
|
||||
path: &'static str,
|
||||
) -> Self {
|
||||
Self::new(sim, Path::new(cargo_manifest_dir).join(path))
|
||||
}
|
||||
pub fn with_vcd_output<R>(&self, f: impl FnOnce(&str) -> R) -> R {
|
||||
let Some(writer) = &self.writer else {
|
||||
unreachable!();
|
||||
};
|
||||
writer.clone().borrow(|output| {
|
||||
let Ok(output) = str::from_utf8(output) else {
|
||||
unreachable!("VcdWriter writes valid UTF-8");
|
||||
};
|
||||
f(output)
|
||||
})
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn finish(mut self) {
|
||||
let Ok(()) = self.finish_impl(|msg| panic!("{msg}"));
|
||||
}
|
||||
fn finish_impl<E>(
|
||||
&mut self,
|
||||
error: impl FnOnce(std::fmt::Arguments<'_>) -> E,
|
||||
) -> Result<(), E> {
|
||||
let Self {
|
||||
writer: Some(writer),
|
||||
expected_path,
|
||||
expected_contents,
|
||||
location,
|
||||
} = self
|
||||
else {
|
||||
// already finished
|
||||
return Ok(());
|
||||
};
|
||||
let Ok(vcd) = String::from_utf8(writer.take()) else {
|
||||
unreachable!("VcdWriter writes valid UTF-8");
|
||||
};
|
||||
let expected_path_d = expected_path.display();
|
||||
if expected_contents
|
||||
.as_ref()
|
||||
.is_ok_and(|expected_contents| *expected_contents == vcd)
|
||||
{
|
||||
// avoid written output from being split from threads interleaving writes to stdout
|
||||
let _stdout = std::io::stderr().lock();
|
||||
// use println to get output captured by tests
|
||||
println!("\n{location}: generated VCD matches the expected VCD in {expected_path_d}");
|
||||
return Ok(());
|
||||
}
|
||||
// avoid written output from being split from threads interleaving writes to stderr
|
||||
let _stderr = std::io::stderr().lock();
|
||||
let error = |msg: std::fmt::Arguments<'_>| {
|
||||
// print msg at both beginning and end so it's easier to find when the vcd is huge
|
||||
Err(error(format_args!(
|
||||
"\n{msg}####### VCD:\n{vcd}\n#######\n{msg}"
|
||||
)))
|
||||
};
|
||||
let error = |msg: std::fmt::Arguments<'_>| match &*expected_contents {
|
||||
Ok(_) => error(format_args!(
|
||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||
{msg}",
|
||||
)),
|
||||
Err((Some(current_dir), e)) => error(format_args!(
|
||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||
error: failed to read: {e}\n\
|
||||
current dir: {current_dir}\n\
|
||||
{msg}",
|
||||
current_dir = current_dir.display(),
|
||||
)),
|
||||
Err((None, e)) => error(format_args!(
|
||||
"{location}: generated VCD doesn't match the expected VCD in {expected_path_d}\n\
|
||||
error: failed to read: {e}\n\
|
||||
{msg}",
|
||||
)),
|
||||
};
|
||||
const OVERWRITE_VAR_NAME: &str = "OVERWRITE_EXPECTED_VCD";
|
||||
const OVERWRITE_VAR_VALUE: &str = "overwrite";
|
||||
match std::env::var_os(OVERWRITE_VAR_NAME) {
|
||||
Some(v) if v == OVERWRITE_VAR_VALUE => match std::fs::write(&expected_path, &vcd) {
|
||||
Ok(()) => error(format_args!(
|
||||
"warning: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- writing the generated VCD to {expected_path_d}\n"
|
||||
)),
|
||||
Err(e) => error(format_args!(
|
||||
"error: since `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}` is set -- tried to write the generated VCD to {expected_path_d}\n\
|
||||
error: failed to write: {e}"
|
||||
)),
|
||||
},
|
||||
_ => error(format_args!(
|
||||
"note: rerun the test with the environment variable `{OVERWRITE_VAR_NAME}={OVERWRITE_VAR_VALUE}`\n\
|
||||
to update the expected output to match the generated output.\n"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CheckedVcdOutput {
|
||||
#[track_caller]
|
||||
fn drop(&mut self) {
|
||||
let _ = self.finish_impl(|msg| {
|
||||
if std::thread::panicking() {
|
||||
eprintln!("{msg}"); // use eprintln to get output captured by tests
|
||||
} else {
|
||||
panic!("{msg}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! checked_vcd_output {
|
||||
($sim:expr, $path_relative_to_manifest_dir:expr $(,)?) => {
|
||||
$crate::util::CheckedVcdOutput::__checked_vcd_output_macro_helper(
|
||||
$sim,
|
||||
::std::env!("CARGO_MANIFEST_DIR"),
|
||||
::std::concat!($path_relative_to_manifest_dir),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
37754
crates/cpu/tests/expected/units_formal_power_isa_add_sim.vcd
generated
37754
crates/cpu/tests/expected/units_formal_power_isa_add_sim.vcd
generated
File diff suppressed because it is too large
Load diff
126030
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_0.vcd
generated
Normal file
126030
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_0.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
171484
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_1.vcd
generated
Normal file
171484
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_1.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
135028
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_2.vcd
generated
Normal file
135028
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_2.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
178767
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_3.vcd
generated
Normal file
178767
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_3.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
96323
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_4.vcd
generated
Normal file
96323
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_4.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
72104
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_5.vcd
generated
Normal file
72104
crates/cpu/tests/expected/units_formal_power_isa_add_sub_sim_5.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
25238
crates/cpu/tests/expected/units_formal_power_isa_paddi_sim.vcd
generated
Normal file
25238
crates/cpu/tests/expected/units_formal_power_isa_paddi_sim.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,6 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use cpu::{
|
||||
checked_vcd_output,
|
||||
config::{
|
||||
CpuConfig, CpuConfigFetchWidth, CpuConfigMaxUnitMaxInFlight, PhantomConstCpuConfig,
|
||||
UnitConfig,
|
||||
|
|
@ -29,6 +28,7 @@ use cpu::{
|
|||
};
|
||||
use fayalite::{
|
||||
bundle::BundleType,
|
||||
checked_vcd_output,
|
||||
module::instance_with_loc,
|
||||
prelude::*,
|
||||
ty::{OpaqueSimValue, StaticType},
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
use crate::test_cases::TestCase;
|
||||
use cpu::{
|
||||
checked_vcd_output, decoder::simple_power_isa::decode_one_insn, instruction::MOp,
|
||||
util::array_vec::ArrayVec,
|
||||
decoder::simple_power_isa::decode_one_insn, instruction::MOp, util::array_vec::ArrayVec,
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
use fayalite::{checked_vcd_output, prelude::*};
|
||||
use std::{fmt::Write as _, io::Write, process::Command};
|
||||
|
||||
mod test_cases;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue