Compare commits

...

10 commits

21 changed files with 841153 additions and 41088 deletions

8
Cargo.lock generated
View file

@ -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",

View file

@ -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,

View file

@ -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>),
}

View file

@ -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();

View 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()
}
}

View file

@ -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

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

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

View file

@ -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},

View file

@ -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