Compare commits
6 commits
master
...
add-poweri
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a35a698e2 | |||
| a4b052f5f3 | |||
| 62512960c3 | |||
| b7b6a02777 | |||
| 6d40eaadb3 | |||
| 305d7b0ae6 |
12 changed files with 6798 additions and 13 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -279,11 +279,13 @@ dependencies = [
|
||||||
"fayalite",
|
"fayalite",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"parse_powerisa_pdf",
|
"parse_powerisa_pdf",
|
||||||
|
"regex",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"simple-mermaid",
|
"simple-mermaid",
|
||||||
"ureq",
|
"ureq",
|
||||||
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -359,12 +361,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.9"
|
version = "0.3.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -630,9 +632,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.159"
|
version = "0.2.180"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
|
@ -1210,6 +1212,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"home",
|
"home",
|
||||||
|
"regex",
|
||||||
"rustix",
|
"rustix",
|
||||||
"winsafe",
|
"winsafe",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ serde = { version = "1.0.202", features = ["derive"] }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
simple-mermaid = "0.2.0"
|
simple-mermaid = "0.2.0"
|
||||||
ureq = "3.1.4"
|
ureq = "3.1.4"
|
||||||
|
which = { version = "6.0.3", features = ["regex"] }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,6 @@ ureq.workspace = true
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
base16ct.workspace = true
|
base16ct.workspace = true
|
||||||
hex-literal.workspace = true
|
hex-literal.workspace = true
|
||||||
|
regex = "1.12.2"
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
|
which.workspace = true
|
||||||
|
|
|
||||||
4
crates/cpu/src/decoder.rs
Normal file
4
crates/cpu/src/decoder.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
pub mod simple_power_isa;
|
||||||
1413
crates/cpu/src/decoder/simple_power_isa.rs
Normal file
1413
crates/cpu/src/decoder/simple_power_isa.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,9 @@ use crate::{unit::UnitMOp, util::range_u32_len};
|
||||||
use fayalite::{
|
use fayalite::{
|
||||||
expr::{HdlPartialEqImpl, ops::ArrayLiteral},
|
expr::{HdlPartialEqImpl, ops::ArrayLiteral},
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
|
module::wire_with_loc,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
ty::StaticType,
|
||||||
};
|
};
|
||||||
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Range};
|
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Range};
|
||||||
|
|
||||||
|
|
@ -734,6 +736,120 @@ impl<DestReg: Type, SrcRegWidth: Size> LogicalMOp<DestReg, SrcRegWidth> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
pub enum CompareMode {
|
||||||
|
U64,
|
||||||
|
S64,
|
||||||
|
U32,
|
||||||
|
S32,
|
||||||
|
U16,
|
||||||
|
S16,
|
||||||
|
U8,
|
||||||
|
S8,
|
||||||
|
/// compare one ranged byte -- like the PowerISA `cmprb _, 0, ..` instruction
|
||||||
|
CmpRBOne,
|
||||||
|
/// compare two ranged bytes -- like the PowerISA `cmprb _, 1, ..` instruction
|
||||||
|
CmpRBTwo,
|
||||||
|
/// like the PowerISA `cmpeqb` instruction
|
||||||
|
CmpEqB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HdlPartialEqImpl<Self> for CompareMode {
|
||||||
|
#[track_caller]
|
||||||
|
fn cmp_value_eq(
|
||||||
|
lhs: Self,
|
||||||
|
lhs_value: Cow<'_, Self::SimValue>,
|
||||||
|
rhs: Self,
|
||||||
|
rhs_value: Cow<'_, Self::SimValue>,
|
||||||
|
) -> bool {
|
||||||
|
SimValue::opaque(&SimValue::from_value(lhs, lhs_value.into_owned()))
|
||||||
|
== SimValue::opaque(&SimValue::from_value(rhs, rhs_value.into_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn cmp_sim_value_eq(
|
||||||
|
lhs: Cow<'_, SimValue<Self>>,
|
||||||
|
rhs: Cow<'_, SimValue<Self>>,
|
||||||
|
) -> SimValue<Bool> {
|
||||||
|
(SimValue::opaque(&lhs) == SimValue::opaque(&rhs)).to_sim_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn cmp_sim_value_ne(
|
||||||
|
lhs: Cow<'_, SimValue<Self>>,
|
||||||
|
rhs: Cow<'_, SimValue<Self>>,
|
||||||
|
) -> SimValue<Bool> {
|
||||||
|
(SimValue::opaque(&lhs) != SimValue::opaque(&rhs)).to_sim_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
|
||||||
|
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
common_mop_struct! {
|
||||||
|
#[mapped(<NewDestReg, NewSrcRegWidth> CompareMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
|
||||||
|
#[hdl(cmp_eq)]
|
||||||
|
pub struct CompareMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
|
||||||
|
#[common]
|
||||||
|
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>,
|
||||||
|
pub compare_mode: CompareMode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DestReg: Type, SrcRegWidth: Size> CompareMOp<DestReg, SrcRegWidth, ConstUsize<2>> {
|
||||||
|
#[hdl]
|
||||||
|
pub fn compare<Target: MOpTrait>(
|
||||||
|
dest: impl ToExpr<Type = DestReg>,
|
||||||
|
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 2>>,
|
||||||
|
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_2_IMM_WIDTH }>>,
|
||||||
|
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
|
||||||
|
compare_mode: impl ToExpr<Type = CompareMode>,
|
||||||
|
) -> Expr<Target>
|
||||||
|
where
|
||||||
|
Self: MOpInto<Target>,
|
||||||
|
{
|
||||||
|
MOpInto::mop_into(
|
||||||
|
#[hdl]
|
||||||
|
CompareMOp {
|
||||||
|
alu_common: #[hdl]
|
||||||
|
AluCommonMOp {
|
||||||
|
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
|
||||||
|
output_integer_mode,
|
||||||
|
},
|
||||||
|
compare_mode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DestReg: Type, SrcRegWidth: Size> CompareMOp<DestReg, SrcRegWidth, ConstUsize<1>> {
|
||||||
|
#[hdl]
|
||||||
|
pub fn compare_i<Target: MOpTrait>(
|
||||||
|
dest: impl ToExpr<Type = DestReg>,
|
||||||
|
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 1>>,
|
||||||
|
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_1_IMM_WIDTH }>>,
|
||||||
|
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
|
||||||
|
compare_mode: impl ToExpr<Type = CompareMode>,
|
||||||
|
) -> Expr<Target>
|
||||||
|
where
|
||||||
|
Self: MOpInto<Target>,
|
||||||
|
{
|
||||||
|
MOpInto::mop_into(
|
||||||
|
#[hdl]
|
||||||
|
CompareMOp {
|
||||||
|
alu_common: #[hdl]
|
||||||
|
AluCommonMOp {
|
||||||
|
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
|
||||||
|
output_integer_mode,
|
||||||
|
},
|
||||||
|
compare_mode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
common_mop_struct! {
|
common_mop_struct! {
|
||||||
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
|
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
|
||||||
#[hdl(cmp_eq)]
|
#[hdl(cmp_eq)]
|
||||||
|
|
@ -751,6 +867,8 @@ mop_enum! {
|
||||||
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
|
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
|
||||||
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
|
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
|
||||||
Logical(LogicalMOp<DestReg, SrcRegWidth>),
|
Logical(LogicalMOp<DestReg, SrcRegWidth>),
|
||||||
|
Compare(CompareMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
|
||||||
|
CompareI(CompareMOp<DestReg, SrcRegWidth, ConstUsize<1>>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -947,6 +1065,87 @@ pub struct MOpDestReg {
|
||||||
pub flag_regs: Array<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>,
|
pub flag_regs: Array<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MOpDestReg {
|
||||||
|
#[hdl]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn new_sim(normal_regs: &[u32], flag_regs: &[u32]) -> SimValue<Self> {
|
||||||
|
let zero_reg = MOpRegNum::const_zero().to_sim_value();
|
||||||
|
let mut normal_regs_sim = std::array::from_fn(|_| zero_reg.clone());
|
||||||
|
for (i, reg) in normal_regs.iter().copied().enumerate() {
|
||||||
|
let Some(normal_reg_sim) = normal_regs_sim.get_mut(i) else {
|
||||||
|
panic!("too many normal regs");
|
||||||
|
};
|
||||||
|
if reg >= 1 << MOpRegNum::WIDTH {
|
||||||
|
panic!("normal reg number out of range");
|
||||||
|
}
|
||||||
|
*normal_reg_sim.value = reg.cast_to_static::<UInt<_>>();
|
||||||
|
}
|
||||||
|
let mut flag_regs_sim = std::array::from_fn(|_| {
|
||||||
|
#[hdl(sim)]
|
||||||
|
HdlNone()
|
||||||
|
});
|
||||||
|
for &flag_reg in flag_regs {
|
||||||
|
let Some(index) = { MOpRegNum::FLAG_REG_NUMS }.position(|v| flag_reg == v) else {
|
||||||
|
panic!(
|
||||||
|
"flag reg number {flag_reg} is out of range, supported range is: {:?}",
|
||||||
|
MOpRegNum::FLAG_REG_NUMS
|
||||||
|
);
|
||||||
|
};
|
||||||
|
flag_regs_sim[index] = #[hdl(sim)]
|
||||||
|
HdlSome(());
|
||||||
|
}
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
normal_regs: normal_regs_sim,
|
||||||
|
flag_regs: flag_regs_sim,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn new(
|
||||||
|
normal_regs: impl IntoIterator<Item = Expr<MOpRegNum>>,
|
||||||
|
flag_regs: impl IntoIterator<Item = (u32, Expr<Bool>)>,
|
||||||
|
) -> Expr<Self> {
|
||||||
|
let mut normal_regs_array = [MOpRegNum::const_zero(); Self::NORMAL_REG_COUNT];
|
||||||
|
const FLAG_REG_COUNT: usize = range_u32_len(&MOpRegNum::FLAG_REG_NUMS);
|
||||||
|
let mut used_flag_regs = [false; FLAG_REG_COUNT];
|
||||||
|
let mut flag_regs_array = [HdlNone(); FLAG_REG_COUNT];
|
||||||
|
for (i, normal_reg) in normal_regs.into_iter().enumerate() {
|
||||||
|
assert!(i < Self::NORMAL_REG_COUNT, "too many normal regs");
|
||||||
|
normal_regs_array[i] = normal_reg;
|
||||||
|
}
|
||||||
|
for (flag_reg_num, flag_reg_enabled) in flag_regs {
|
||||||
|
let Some(index) = { MOpRegNum::FLAG_REG_NUMS }.position(|v| flag_reg_num == v) else {
|
||||||
|
panic!(
|
||||||
|
"flag reg number {flag_reg_num} is out of range, supported range is: {:?}",
|
||||||
|
MOpRegNum::FLAG_REG_NUMS
|
||||||
|
);
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
!used_flag_regs[index],
|
||||||
|
"duplicate flag reg number {flag_reg_num}"
|
||||||
|
);
|
||||||
|
used_flag_regs[index] = true;
|
||||||
|
let wire = wire_with_loc(
|
||||||
|
&format!("flag_reg_{index}"),
|
||||||
|
SourceLocation::caller(),
|
||||||
|
StaticType::TYPE,
|
||||||
|
);
|
||||||
|
connect(wire, HdlNone());
|
||||||
|
#[hdl]
|
||||||
|
if flag_reg_enabled {
|
||||||
|
connect(wire, HdlSome(()));
|
||||||
|
}
|
||||||
|
flag_regs_array[index] = wire;
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
normal_regs: normal_regs_array,
|
||||||
|
flag_regs: flag_regs_array,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
pub enum RenameTableName {
|
pub enum RenameTableName {
|
||||||
/// the large rename table for normal registers (has less read/write ports)
|
/// the large rename table for normal registers (has less read/write ports)
|
||||||
|
|
|
||||||
|
|
@ -29,24 +29,150 @@ impl MOpRegNum {
|
||||||
pub const POWER_ISA_LR_REG_NUM: u32 = 1;
|
pub const POWER_ISA_LR_REG_NUM: u32 = 1;
|
||||||
pub const POWER_ISA_CTR_REG_NUM: u32 = 2;
|
pub const POWER_ISA_CTR_REG_NUM: u32 = 2;
|
||||||
pub const POWER_ISA_TAR_REG_NUM: u32 = 3;
|
pub const POWER_ISA_TAR_REG_NUM: u32 = 3;
|
||||||
/// XER bits are stored in [`PRegValue.flags`], bits that don't exist in [`PRegValue.flags`] are stored in [`PRegValue.int_fp`]
|
|
||||||
|
/// SO, OV, and OV32 XER bits -- in [`PRegValue.flags`]
|
||||||
///
|
///
|
||||||
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
/// [`PRegValue.int_fp`]: struct@crate::register::PRegValue
|
pub const POWER_ISA_XER_SO_OV_OV32_REG_NUM: u32 =
|
||||||
pub const POWER_ISA_XER_REG_NUM: u32 = 4;
|
range_u32_nth_or_panic(&Self::FLAG_REG_NUMS, 0);
|
||||||
|
/// CA and CA32 XER bits -- in [`PRegValue.flags`]
|
||||||
|
///
|
||||||
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
|
pub const POWER_ISA_XER_CA_CA32_REG_NUM: u32 = 4;
|
||||||
|
/// only the XER bits that don't exist in [`PRegValue.flags`]
|
||||||
|
///
|
||||||
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
|
pub const POWER_ISA_XER_OTHER_REG_NUM: u32 = 5;
|
||||||
|
|
||||||
pub const POWER_ISA_CR_REG_NUMS: Range<u32> = 8..16;
|
/// SO, OV, and OV32 XER bits -- in [`PRegValue.flags`]
|
||||||
|
///
|
||||||
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_xer_so_ov_ov32_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_XER_SO_OV_OV32_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// CA and CA32 XER bits -- in [`PRegValue.flags`]
|
||||||
|
///
|
||||||
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_xer_ca_ca32_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_XER_CA_CA32_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// only the XER bits that don't exist in [`PRegValue.flags`]
|
||||||
|
///
|
||||||
|
/// [`PRegValue.flags`]: struct@crate::register::PRegValue
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_xer_other_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_XER_OTHER_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const POWER_ISA_CR_0_REG_NUM: u32 = range_u32_nth_or_panic(&Self::FLAG_REG_NUMS, 1);
|
||||||
|
pub const POWER_ISA_CR_1_THRU_7_REG_NUMS: Range<u32> = 9..16;
|
||||||
pub const fn power_isa_cr_reg_num(index: usize) -> u32 {
|
pub const fn power_isa_cr_reg_num(index: usize) -> u32 {
|
||||||
range_u32_nth_or_panic(&Self::POWER_ISA_CR_REG_NUMS, index)
|
if index == 0 {
|
||||||
|
Self::POWER_ISA_CR_0_REG_NUM
|
||||||
|
} else {
|
||||||
|
range_u32_nth_or_panic(&Self::POWER_ISA_CR_1_THRU_7_REG_NUMS, index - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_cr_reg(field_num: Expr<UInt<3>>) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
let power_isa_cr_reg: Self = wire();
|
||||||
|
#[hdl]
|
||||||
|
if field_num.cmp_eq(0u8) {
|
||||||
|
connect_any(power_isa_cr_reg.value, Self::POWER_ISA_CR_0_REG_NUM);
|
||||||
|
} else {
|
||||||
|
connect_any(
|
||||||
|
power_isa_cr_reg.value,
|
||||||
|
Self::POWER_ISA_CR_1_THRU_7_REG_NUMS.start - 1 + field_num,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
power_isa_cr_reg
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_cr_reg_sim(field_num: &SimValue<UInt<3>>) -> SimValue<Self> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
value: Self::power_isa_cr_reg_num(
|
||||||
|
field_num.cast_to_static::<UInt<8>>().as_int() as usize
|
||||||
|
)
|
||||||
|
.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const POWER_ISA_GPR_REG_NUMS: Range<u32> = 32..64;
|
pub const POWER_ISA_GPR_REG_NUMS: Range<u32> = 32..64;
|
||||||
pub const fn power_isa_gpr_reg_num(index: usize) -> u32 {
|
pub const fn power_isa_gpr_reg_num(index: usize) -> u32 {
|
||||||
range_u32_nth_or_panic(&Self::POWER_ISA_GPR_REG_NUMS, index)
|
range_u32_nth_or_panic(&Self::POWER_ISA_GPR_REG_NUMS, index)
|
||||||
}
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_gpr_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: (Self::POWER_ISA_GPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_gpr_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
value: (Self::POWER_ISA_GPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn power_isa_gpr_or_zero_reg_num(index: usize) -> u32 {
|
||||||
|
if index == 0 {
|
||||||
|
Self::CONST_ZERO_REG_NUM
|
||||||
|
} else {
|
||||||
|
Self::power_isa_gpr_reg_num(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_gpr_or_zero_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
let power_isa_gpr_or_zero_reg: Self = wire();
|
||||||
|
connect(power_isa_gpr_or_zero_reg, Self::power_isa_gpr_reg(reg_num));
|
||||||
|
#[hdl]
|
||||||
|
if reg_num.cmp_eq(0u8) {
|
||||||
|
connect(power_isa_gpr_or_zero_reg, Self::const_zero());
|
||||||
|
}
|
||||||
|
power_isa_gpr_or_zero_reg
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_gpr_or_zero_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
value: Self::power_isa_gpr_or_zero_reg_num(
|
||||||
|
reg_num.cast_to_static::<UInt<8>>().as_int() as usize,
|
||||||
|
)
|
||||||
|
.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const POWER_ISA_FPR_REG_NUMS: Range<u32> = 64..96;
|
pub const POWER_ISA_FPR_REG_NUMS: Range<u32> = 64..96;
|
||||||
pub const fn power_isa_fpr_reg_num(index: usize) -> u32 {
|
pub const fn power_isa_fpr_reg_num(index: usize) -> u32 {
|
||||||
range_u32_nth_or_panic(&Self::POWER_ISA_FPR_REG_NUMS, index)
|
range_u32_nth_or_panic(&Self::POWER_ISA_FPR_REG_NUMS, index)
|
||||||
}
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_fpr_reg(reg_num: Expr<UInt<5>>) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: (Self::POWER_ISA_FPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_fpr_reg_sim(reg_num: &SimValue<UInt<5>>) -> SimValue<Self> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
Self {
|
||||||
|
value: (Self::POWER_ISA_FPR_REG_NUMS.start + reg_num).cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod decoder;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod next_pc;
|
pub mod next_pc;
|
||||||
pub mod powerisa;
|
pub mod powerisa_instructions_xml;
|
||||||
pub mod reg_alloc;
|
pub mod reg_alloc;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
pub mod unit;
|
pub mod unit;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::CpuConfig,
|
config::CpuConfig,
|
||||||
instruction::{
|
instruction::{
|
||||||
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, LogicalMOp, MOpTrait,
|
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, CompareMOp,
|
||||||
OutputIntegerMode, RenamedMOp, UnitOutRegNum,
|
LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp, UnitOutRegNum,
|
||||||
},
|
},
|
||||||
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
|
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
|
||||||
unit::{
|
unit::{
|
||||||
|
|
@ -244,6 +244,20 @@ fn logical(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn compare<SrcCount: KnownSize>(
|
||||||
|
mop: Expr<CompareMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
|
||||||
|
flags_mode: Expr<FlagsMode>,
|
||||||
|
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||||
|
) -> Expr<UnitResultCompleted<()>> {
|
||||||
|
// TODO: finish
|
||||||
|
#[hdl]
|
||||||
|
UnitResultCompleted::<_> {
|
||||||
|
value: PRegValue::zeroed(),
|
||||||
|
extra_out: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[hdl_module]
|
#[hdl_module]
|
||||||
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -336,6 +350,40 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
AluBranchMOp::<_, _>::Compare(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(compare(
|
||||||
|
mop,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AluBranchMOp::<_, _>::CompareI(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(compare(
|
||||||
|
mop,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4157
crates/cpu/tests/expected/decode_one_insn.vcd
Normal file
4157
crates/cpu/tests/expected/decode_one_insn.vcd
Normal file
File diff suppressed because it is too large
Load diff
831
crates/cpu/tests/simple_power_isa_decoder.rs
Normal file
831
crates/cpu/tests/simple_power_isa_decoder.rs
Normal file
|
|
@ -0,0 +1,831 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use cpu::{
|
||||||
|
decoder::simple_power_isa::decode_one_insn,
|
||||||
|
instruction::{
|
||||||
|
AddSubMOp, CompareMOp, CompareMode, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode,
|
||||||
|
},
|
||||||
|
util::array_vec::ArrayVec,
|
||||||
|
};
|
||||||
|
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
|
||||||
|
use std::{
|
||||||
|
fmt::{self, Write as _},
|
||||||
|
io::Write,
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
mnemonic: &'static str,
|
||||||
|
first_input: u32,
|
||||||
|
second_input: Option<u32>,
|
||||||
|
output: SimValue<ArrayVec<MOp, ConstUsize<2>>>,
|
||||||
|
loc: &'static std::panic::Location<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for TestCase {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let Self {
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output,
|
||||||
|
loc,
|
||||||
|
} = self;
|
||||||
|
let mut debug_struct = f.debug_struct("TestCase");
|
||||||
|
debug_struct
|
||||||
|
.field("mnemonic", mnemonic)
|
||||||
|
.field("first_input", &format_args!("0x{first_input:08x}"));
|
||||||
|
if let Some(second_input) = second_input {
|
||||||
|
debug_struct.field("second_input", &format_args!("0x{second_input:08x}"));
|
||||||
|
} else {
|
||||||
|
debug_struct.field("second_input", &format_args!("None"));
|
||||||
|
}
|
||||||
|
debug_struct
|
||||||
|
.field("output", &ArrayVec::elements_sim_ref(output))
|
||||||
|
.field("loc", &format_args!("{loc}"))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn test_cases() -> Vec<TestCase> {
|
||||||
|
let mut retval = Vec::new();
|
||||||
|
#[track_caller]
|
||||||
|
fn insn_single(
|
||||||
|
mnemonic: &'static str,
|
||||||
|
first_input: u32,
|
||||||
|
second_input: Option<u32>,
|
||||||
|
output: impl ToSimValue<Type = MOp>,
|
||||||
|
) -> TestCase {
|
||||||
|
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
|
||||||
|
.zero()
|
||||||
|
.cast_bits_to(MOp);
|
||||||
|
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
|
||||||
|
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value();
|
||||||
|
TestCase {
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: single_storage.clone(),
|
||||||
|
loc: std::panic::Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addi 3, 4, 0x1234",
|
||||||
|
0x38641234,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"paddi 3, 4, 0x123456789, 0",
|
||||||
|
0x06012345,
|
||||||
|
Some(0x38646789),
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x123456789i64.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"paddi 3, 0, 0x123456789, 1",
|
||||||
|
0x06112345,
|
||||||
|
Some(0x38606789),
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value, MOpRegNum::const_zero().value],
|
||||||
|
0x123456789i64.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addis 3, 4, 0x1234",
|
||||||
|
0x3C641234,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x12340000.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addpcis 3, 0x1234",
|
||||||
|
0x4c7a1204,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value; _],
|
||||||
|
0x12340004.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"add. 3, 4, 5",
|
||||||
|
0x7c642a15,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num(3)],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addic. 3, 4, 0x1234",
|
||||||
|
0x34641234,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subf. 3, 4, 5",
|
||||||
|
0x7c642851,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num(3)],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subfic 3, 4, 0x1234",
|
||||||
|
0x20641234,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addc. 3, 4, 5",
|
||||||
|
0x7c642815,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subfc. 3, 4, 5",
|
||||||
|
0x7c642811,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"adde. 3, 4, 5",
|
||||||
|
0x7c642915,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subfe. 3, 4, 5",
|
||||||
|
0x7c642911,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addme. 3, 4",
|
||||||
|
0x7c6401d5,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
(-1i8).cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subfme. 3, 4",
|
||||||
|
0x7c6401d1,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
(-1i8).cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"addze. 3, 4",
|
||||||
|
0x7c640195,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"subfze. 3, 4",
|
||||||
|
0x7c640191,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_num(3),
|
||||||
|
MOpRegNum::POWER_ISA_XER_CA_CA32_REG_NUM,
|
||||||
|
],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"neg. 3, 4",
|
||||||
|
0x7c6400d1,
|
||||||
|
None,
|
||||||
|
AddSubMOp::add_sub(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num(3)],
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpi 3, 0, 4, 0x1234",
|
||||||
|
0x2d841234,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::S32(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpi 3, 1, 4, -0x7655",
|
||||||
|
0x2da489ab,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
|
||||||
|
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::S64(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmp 3, 0, 4, 5",
|
||||||
|
0x7d842800,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::S32(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmp 3, 1, 4, 5",
|
||||||
|
0x7da42800,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::S64(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpli 3, 0, 4, 0x1234",
|
||||||
|
0x29841234,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::U32(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpli 3, 1, 4, 0x89ab",
|
||||||
|
0x29a489ab,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
|
||||||
|
0x89ab.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::U64(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpl 3, 0, 4, 5",
|
||||||
|
0x7d842840,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::U32(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpl 3, 1, 4, 5",
|
||||||
|
0x7da42840,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::U64(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmprb 3, 0, 4, 5",
|
||||||
|
0x7d842980,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::CmpRBOne(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmprb 3, 1, 4, 5",
|
||||||
|
0x7da42980,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::CmpRBTwo(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"cmpeqb 3, 4, 5",
|
||||||
|
0x7d8429c0,
|
||||||
|
None,
|
||||||
|
CompareMOp::compare(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
OutputIntegerMode::Full64(),
|
||||||
|
#[hdl(sim)]
|
||||||
|
CompareMode::CmpEqB(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_test_cases_assembly() -> std::io::Result<()> {
|
||||||
|
let llvm_mc_regex = regex::Regex::new(r"llvm-mc(-\d+)?$").expect("known to be a valid regex");
|
||||||
|
let llvm_mc = which::which_re(llvm_mc_regex)
|
||||||
|
.expect("can't find llvm-mc or llvm-mc-<num> in path")
|
||||||
|
.next()
|
||||||
|
.expect("can't find llvm-mc or llvm-mc-<num> in path");
|
||||||
|
let test_cases = test_cases();
|
||||||
|
let mut assembly = String::new();
|
||||||
|
for TestCase {
|
||||||
|
mnemonic,
|
||||||
|
first_input: _,
|
||||||
|
second_input: _,
|
||||||
|
output: _,
|
||||||
|
loc: _,
|
||||||
|
} in &test_cases
|
||||||
|
{
|
||||||
|
writeln!(assembly, "{mnemonic}").unwrap();
|
||||||
|
}
|
||||||
|
let (reader, mut writer) = std::io::pipe()?;
|
||||||
|
let thread = std::thread::spawn(move || writer.write_all(assembly.as_bytes()));
|
||||||
|
let std::process::Output {
|
||||||
|
status,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
} = Command::new(&llvm_mc)
|
||||||
|
.arg("--triple=powerpc64le-linux-gnu")
|
||||||
|
.arg("--assemble")
|
||||||
|
.arg("--filetype=asm")
|
||||||
|
.arg("--show-encoding")
|
||||||
|
.arg("-")
|
||||||
|
.stdin(reader)
|
||||||
|
.output()?;
|
||||||
|
let _ = thread.join();
|
||||||
|
let stderr = String::from_utf8_lossy(&stderr);
|
||||||
|
eprint!("{stderr}");
|
||||||
|
if !status.success() {
|
||||||
|
panic!("{} failed: {status}", llvm_mc.display());
|
||||||
|
}
|
||||||
|
let stdout = String::from_utf8_lossy(&stdout);
|
||||||
|
print!("{stdout}");
|
||||||
|
let mut lines = stdout.lines();
|
||||||
|
let text_line = lines.next();
|
||||||
|
assert_eq!(text_line, Some("\t.text"));
|
||||||
|
for test_case @ TestCase {
|
||||||
|
mnemonic: _,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: _,
|
||||||
|
loc: _,
|
||||||
|
} in test_cases
|
||||||
|
{
|
||||||
|
let Some(line) = lines.next() else {
|
||||||
|
panic!("output missing line for: {test_case:?}");
|
||||||
|
};
|
||||||
|
let Some((_, comment)) = line.split_once('#') else {
|
||||||
|
panic!("output line missing comment. test_case={test_case:?}\nline:\n{line}");
|
||||||
|
};
|
||||||
|
let [b0, b1, b2, b3] = first_input.to_le_bytes();
|
||||||
|
let expected_comment = if let Some(second_input) = second_input {
|
||||||
|
let [b4, b5, b6, b7] = second_input.to_le_bytes();
|
||||||
|
format!(
|
||||||
|
" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x},0x{b4:02x},0x{b5:02x},0x{b6:02x},0x{b7:02x}]"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x}]")
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
comment, expected_comment,
|
||||||
|
"test_case={test_case:?}\nline:\n{line}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for line in lines {
|
||||||
|
assert!(line.trim().is_empty(), "bad trailing output line: {line:?}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
#[test]
|
||||||
|
fn test_decode_insn() {
|
||||||
|
let _n = SourceLocation::normalize_files_for_tests();
|
||||||
|
let m = decode_one_insn();
|
||||||
|
let mut sim = Simulation::new(m);
|
||||||
|
let writer = RcWriter::default();
|
||||||
|
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
|
||||||
|
struct DumpVcdOnDrop {
|
||||||
|
writer: Option<RcWriter>,
|
||||||
|
}
|
||||||
|
impl Drop for DumpVcdOnDrop {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(mut writer) = self.writer.take() {
|
||||||
|
let vcd = String::from_utf8(writer.take()).unwrap();
|
||||||
|
println!("####### VCD:\n{vcd}\n#######");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut writer = DumpVcdOnDrop {
|
||||||
|
writer: Some(writer),
|
||||||
|
};
|
||||||
|
for test_case @ TestCase {
|
||||||
|
mnemonic: _,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: _,
|
||||||
|
loc: _,
|
||||||
|
} in test_cases()
|
||||||
|
{
|
||||||
|
sim.write(sim.io().first_input, first_input);
|
||||||
|
sim.write(
|
||||||
|
sim.io().second_input,
|
||||||
|
if let Some(v) = second_input {
|
||||||
|
#[hdl(sim)]
|
||||||
|
HdlSome(v)
|
||||||
|
} else {
|
||||||
|
#[hdl(sim)]
|
||||||
|
HdlNone()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
sim.advance_time(SimDuration::from_micros(1));
|
||||||
|
let second_input_used = sim.read_bool(sim.io().second_input_used);
|
||||||
|
let is_illegal = sim.read_bool(sim.io().is_illegal);
|
||||||
|
let output = sim.read(sim.io().output);
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[expect(dead_code, reason = "used only for Debug formatting")]
|
||||||
|
struct FormattedOutput<'a> {
|
||||||
|
insns: &'a [SimValue<MOp>],
|
||||||
|
second_input_used: bool,
|
||||||
|
is_illegal: bool,
|
||||||
|
}
|
||||||
|
let expected = format!(
|
||||||
|
"{:#?}",
|
||||||
|
FormattedOutput {
|
||||||
|
insns: ArrayVec::elements_sim_ref(&test_case.output),
|
||||||
|
second_input_used: second_input.is_some(),
|
||||||
|
is_illegal: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let output = format!(
|
||||||
|
"{:#?}",
|
||||||
|
FormattedOutput {
|
||||||
|
insns: ArrayVec::elements_sim_ref(&output),
|
||||||
|
second_input_used,
|
||||||
|
is_illegal,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
expected == output,
|
||||||
|
"test_case={test_case:#?}\noutput={output}\nexpected={expected}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap();
|
||||||
|
println!("####### VCD:\n{vcd}\n#######");
|
||||||
|
if vcd != include_str!("expected/decode_one_insn.vcd") {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
#[test]
|
||||||
|
fn test_simple_power_isa_decoder() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue