add PowerISA decoder #7
31 changed files with 215042 additions and 36165 deletions
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# See Notices.txt for copyright information
|
||||||
|
*.vcd linguist-generated=true
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
# 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
|
||||||
/target
|
/target
|
||||||
|
OPF_PowerISA_v3.1C.pdf
|
||||||
|
|
|
||||||
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;
|
||||||
2847
crates/cpu/src/decoder/simple_power_isa.rs
Normal file
2847
crates/cpu/src/decoder/simple_power_isa.rs
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -27,26 +27,432 @@ pub struct PowerIsaCrBitNum {
|
||||||
|
|
||||||
impl MOpRegNum {
|
impl MOpRegNum {
|
||||||
pub const POWER_ISA_LR_REG_NUM: u32 = 1;
|
pub const POWER_ISA_LR_REG_NUM: u32 = 1;
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_lr_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_LR_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub const POWER_ISA_CTR_REG_NUM: u32 = 2;
|
pub const POWER_ISA_CTR_REG_NUM: u32 = 2;
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_ctr_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_CTR_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
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`]
|
#[hdl]
|
||||||
|
pub fn power_isa_tar_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_TAR_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
/// used as a temporary for things like computing the effective address before loading/storing memory
|
||||||
|
pub const POWER_ISA_TEMP_REG_NUM: u32 = 8;
|
||||||
|
#[hdl]
|
||||||
|
pub fn power_isa_temp_reg() -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::POWER_ISA_TEMP_REG_NUM.cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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_imm(index: usize) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::power_isa_cr_reg_num(index).cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[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_imm(index: usize) -> Expr<Self> {
|
||||||
|
#[hdl]
|
||||||
|
Self {
|
||||||
|
value: Self::power_isa_gpr_reg_num(index).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<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl(cmp_eq)]
|
||||||
|
pub struct PowerIsaSpr {
|
||||||
|
pub num: UInt<10>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_spr_enum {
|
||||||
|
(
|
||||||
|
$(#[$enum_meta:meta])*
|
||||||
|
$enum_vis:vis enum $PowerIsaSprEnum:ident {
|
||||||
|
$($enum_body:tt)*
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
$(#[$enum_meta])*
|
||||||
|
$enum_vis enum $PowerIsaSprEnum {
|
||||||
|
$($enum_body)*
|
||||||
|
Unknown(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
make_spr_enum! {
|
||||||
|
@impl
|
||||||
|
$enum_vis enum $PowerIsaSprEnum {
|
||||||
|
$($enum_body)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
@impl
|
||||||
|
$enum_vis:vis enum $PowerIsaSprEnum:ident {
|
||||||
|
$(
|
||||||
|
$(#[$variant_meta:meta])*
|
||||||
|
$Variant:ident = $value:literal,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
impl $PowerIsaSprEnum {
|
||||||
|
pub const VARIANTS: &[Self; 1 << 10] = &{
|
||||||
|
let mut retval = [Self::Unknown(0); 1 << 10];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < retval.len() {
|
||||||
|
retval[i] = Self::Unknown(i as u16);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let mut last_value = None;
|
||||||
|
#[track_caller]
|
||||||
|
const fn add_variant(
|
||||||
|
values: &mut [$PowerIsaSprEnum; 1 << 10],
|
||||||
|
last_value: &mut Option<u16>,
|
||||||
|
variant: $PowerIsaSprEnum,
|
||||||
|
value: u16,
|
||||||
|
) {
|
||||||
|
assert!(value < 1 << 10, "variant value out of range");
|
||||||
|
if let Some(last_value) = *last_value {
|
||||||
|
assert!(last_value < value, "variants must be in ascending order with no duplicates");
|
||||||
|
}
|
||||||
|
*last_value = Some(value);
|
||||||
|
values[value as usize] = variant;
|
||||||
|
}
|
||||||
|
$(add_variant(&mut retval, &mut last_value, Self::$Variant, $value);)+
|
||||||
|
retval
|
||||||
|
};
|
||||||
|
pub const fn value(self) -> u16 {
|
||||||
|
match self {
|
||||||
|
$(Self::$Variant => $value,)+
|
||||||
|
Self::Unknown(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_spr_enum! {
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum PowerIsaSprEnum {
|
||||||
|
Xer = 1,
|
||||||
|
UserDscr = 3,
|
||||||
|
Lr = 8,
|
||||||
|
Ctr = 9,
|
||||||
|
UserAmr = 13,
|
||||||
|
Dscr = 17,
|
||||||
|
Dsisr = 18,
|
||||||
|
Dar = 19,
|
||||||
|
Dec = 22,
|
||||||
|
Srr0 = 26,
|
||||||
|
Srr1 = 27,
|
||||||
|
Cfar = 28,
|
||||||
|
Amr = 29,
|
||||||
|
Pidr = 48,
|
||||||
|
Iamr = 61,
|
||||||
|
ReadCtrl = 136,
|
||||||
|
WriteCtrl = 152,
|
||||||
|
Fscr = 153,
|
||||||
|
Uamor = 157,
|
||||||
|
Pspb = 159,
|
||||||
|
Dpdes = 176,
|
||||||
|
Dawr0 = 180,
|
||||||
|
Dawr1 = 181,
|
||||||
|
Rpr = 186,
|
||||||
|
Ciabr = 187,
|
||||||
|
Dawrx0 = 188,
|
||||||
|
Dawrx1 = 189,
|
||||||
|
Hfscr = 190,
|
||||||
|
Vrsave = 256,
|
||||||
|
UserSprg3 = 259,
|
||||||
|
Tb = 268,
|
||||||
|
Tbu = 269,
|
||||||
|
Sprg0 = 272,
|
||||||
|
Sprg1 = 273,
|
||||||
|
Sprg2 = 274,
|
||||||
|
Sprg3 = 275,
|
||||||
|
Tbl = 284,
|
||||||
|
WriteTbu = 285,
|
||||||
|
Tbu40 = 286,
|
||||||
|
Pvr = 287,
|
||||||
|
Hsprg0 = 304,
|
||||||
|
Hsprg1 = 305,
|
||||||
|
Hdsisr = 306,
|
||||||
|
Hdar = 307,
|
||||||
|
Spurr = 308,
|
||||||
|
Purr = 309,
|
||||||
|
Hdec = 310,
|
||||||
|
Hrmor = 313,
|
||||||
|
Hsrr0 = 314,
|
||||||
|
Hsrr1 = 315,
|
||||||
|
Lpcr = 318,
|
||||||
|
Lpidr = 319,
|
||||||
|
Hmer = 336,
|
||||||
|
Hmeer = 337,
|
||||||
|
Pcr = 338,
|
||||||
|
Heir = 339,
|
||||||
|
Amor = 349,
|
||||||
|
Tir = 446,
|
||||||
|
UserHdexcr = 455,
|
||||||
|
Ptcr = 464,
|
||||||
|
Hashkeyr = 468,
|
||||||
|
Hashpkeyr = 469,
|
||||||
|
Hdexcr = 471,
|
||||||
|
Usprg0 = 496,
|
||||||
|
Usprg1 = 497,
|
||||||
|
Urmor = 505,
|
||||||
|
Usrr0 = 506,
|
||||||
|
Usrr1 = 507,
|
||||||
|
Smfctrl = 511,
|
||||||
|
UserSier2 = 736,
|
||||||
|
UserSier3 = 737,
|
||||||
|
UserMmcr3 = 738,
|
||||||
|
Sier2 = 752,
|
||||||
|
Sier3 = 753,
|
||||||
|
Mmcr3 = 754,
|
||||||
|
UserSier = 768,
|
||||||
|
Mmcr2 = 769,
|
||||||
|
Mmcra = 770,
|
||||||
|
Pmc1 = 771,
|
||||||
|
Pmc2 = 772,
|
||||||
|
Pmc3 = 773,
|
||||||
|
Pmc4 = 774,
|
||||||
|
Pmc5 = 775,
|
||||||
|
Pmc6 = 776,
|
||||||
|
Mmcr0 = 779,
|
||||||
|
Siar = 780,
|
||||||
|
Sdar = 781,
|
||||||
|
Mmcr1 = 782,
|
||||||
|
Sier = 784,
|
||||||
|
PrivMmcr2 = 785,
|
||||||
|
PrivMmcra = 786,
|
||||||
|
PrivPmc1 = 787,
|
||||||
|
PrivPmc2 = 788,
|
||||||
|
PrivPmc3 = 789,
|
||||||
|
PrivPmc4 = 790,
|
||||||
|
PrivPmc5 = 791,
|
||||||
|
PrivPmc6 = 792,
|
||||||
|
PrivMmcr0 = 795,
|
||||||
|
PrivSiar = 796,
|
||||||
|
PrivSdar = 797,
|
||||||
|
PrivMmcr1 = 798,
|
||||||
|
Bescrs15 = 800,
|
||||||
|
Bescrsu16 = 801,
|
||||||
|
Bescrr15 = 802,
|
||||||
|
Bescrru16 = 803,
|
||||||
|
Ebbhr = 804,
|
||||||
|
Ebbrr = 805,
|
||||||
|
Bescr = 806,
|
||||||
|
Reserved808 = 808,
|
||||||
|
Reserved809 = 809,
|
||||||
|
Reserved810 = 810,
|
||||||
|
Reserved811 = 811,
|
||||||
|
UserDexcr = 812,
|
||||||
|
Tar = 815,
|
||||||
|
Asdr = 816,
|
||||||
|
Psscr = 823,
|
||||||
|
Dexcr = 828,
|
||||||
|
Ic = 848,
|
||||||
|
Vtb = 849,
|
||||||
|
HyperPsscr = 855,
|
||||||
|
Ppr = 896,
|
||||||
|
Ppr32 = 898,
|
||||||
|
Pir = 1023,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueType for PowerIsaSprEnum {
|
||||||
|
type Type = PowerIsaSpr;
|
||||||
|
type ValueCategory = fayalite::expr::value_category::ValueCategoryValue;
|
||||||
|
|
||||||
|
fn ty(&self) -> Self::Type {
|
||||||
|
PowerIsaSpr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToExpr for PowerIsaSprEnum {
|
||||||
|
#[hdl]
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
#[hdl]
|
||||||
|
PowerIsaSpr {
|
||||||
|
num: self.value().cast_to_static::<UInt<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSimValueWithType<PowerIsaSpr> for PowerIsaSprEnum {
|
||||||
|
fn to_sim_value_with_type(&self, _ty: PowerIsaSpr) -> SimValue<PowerIsaSpr> {
|
||||||
|
self.to_sim_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSimValue for PowerIsaSprEnum {
|
||||||
|
#[hdl]
|
||||||
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
PowerIsaSpr {
|
||||||
|
num: self.value().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;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
// 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
|
||||||
use fayalite::prelude::*;
|
|
||||||
|
use crate::instruction::ConditionMode;
|
||||||
|
use fayalite::{
|
||||||
|
expr::CastToImpl,
|
||||||
|
int::{BoolOrIntType, UIntInRange},
|
||||||
|
prelude::*,
|
||||||
|
ty::StaticType,
|
||||||
|
};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[hdl]
|
#[hdl]
|
||||||
pub enum FlagsMode {
|
pub enum FlagsMode {
|
||||||
|
|
@ -8,131 +16,750 @@ pub enum FlagsMode {
|
||||||
X86(PRegFlagsX86),
|
X86(PRegFlagsX86),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
trait PRegFlagsViewTraitSealed {
|
||||||
pub struct PRegFlagsPowerISA {}
|
type UnusedInner<T>: AsRef<[T]>
|
||||||
|
+ AsMut<[T]>
|
||||||
|
+ IntoIterator<
|
||||||
|
Item = T,
|
||||||
|
IntoIter: DoubleEndedIterator<Item = T>
|
||||||
|
+ ExactSizeIterator
|
||||||
|
+ std::iter::FusedIterator
|
||||||
|
+ Default,
|
||||||
|
>;
|
||||||
|
const UNUSED_INNER_LEN: usize;
|
||||||
|
fn unused_inner_map<T, R>(
|
||||||
|
v: Self::UnusedInner<T>,
|
||||||
|
f: impl FnMut(T) -> R,
|
||||||
|
) -> Self::UnusedInner<R>;
|
||||||
|
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T>;
|
||||||
|
fn unused_inner_each_ref<T>(v: &Self::UnusedInner<T>) -> Self::UnusedInner<&T>;
|
||||||
|
fn unused_inner_each_mut<T>(v: &mut Self::UnusedInner<T>) -> Self::UnusedInner<&mut T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(private_bounds)]
|
||||||
|
pub trait PRegFlagsViewTrait: Type + PRegFlagsViewTraitSealed {
|
||||||
|
type View<T>;
|
||||||
|
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>>;
|
||||||
|
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>>;
|
||||||
|
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>>;
|
||||||
|
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>>;
|
||||||
|
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>>;
|
||||||
|
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>>;
|
||||||
|
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T>;
|
||||||
|
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ViewUnused<T, V: PRegFlagsViewTrait>(V::UnusedInner<T>);
|
||||||
|
|
||||||
|
pub struct ViewUnusedIntoIter<T, V: PRegFlagsViewTrait>(
|
||||||
|
<V::UnusedInner<T> as IntoIterator>::IntoIter,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> Iterator for ViewUnusedIntoIter<T, V> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.0.size_hint()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold<B, F>(self, init: B, f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.0.fold(init, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(self) -> usize {
|
||||||
|
self.0.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last(self) -> Option<Self::Item> {
|
||||||
|
self.0.last()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> DoubleEndedIterator for ViewUnusedIntoIter<T, V> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next_back()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rfold<B, F>(self, init: B, f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.0.rfold(init, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> ExactSizeIterator for ViewUnusedIntoIter<T, V> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> std::iter::FusedIterator for ViewUnusedIntoIter<T, V> {}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> Default for ViewUnusedIntoIter<T, V> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnusedIntoIter<T, V>
|
||||||
|
where
|
||||||
|
<V::UnusedInner<T> as IntoIterator>::IntoIter: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> IntoIterator for ViewUnused<T, V> {
|
||||||
|
type Item = T;
|
||||||
|
type IntoIter = ViewUnusedIntoIter<T, V>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
ViewUnusedIntoIter(self.0.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default, V: PRegFlagsViewTrait> Default for ViewUnused<T, V> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::from_fn(|_| T::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V: PRegFlagsViewTrait> ViewUnused<T, V> {
|
||||||
|
pub fn iter(&self) -> std::slice::Iter<'_, T> {
|
||||||
|
self.into_iter()
|
||||||
|
}
|
||||||
|
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
|
||||||
|
self.into_iter()
|
||||||
|
}
|
||||||
|
pub fn from_fn(f: impl FnMut(usize) -> T) -> Self {
|
||||||
|
ViewUnused(V::unused_inner_from_fn(f))
|
||||||
|
}
|
||||||
|
pub fn each_ref(&self) -> ViewUnused<&T, V> {
|
||||||
|
ViewUnused(V::unused_inner_each_ref(&self.0))
|
||||||
|
}
|
||||||
|
pub fn each_mut(&mut self) -> ViewUnused<&mut T, V> {
|
||||||
|
ViewUnused(V::unused_inner_each_mut(&mut self.0))
|
||||||
|
}
|
||||||
|
pub fn map<R>(self, f: impl FnMut(T) -> R) -> ViewUnused<R, V> {
|
||||||
|
ViewUnused(V::unused_inner_map(self.0, f))
|
||||||
|
}
|
||||||
|
pub fn zip<U>(self, other: ViewUnused<U, V>) -> ViewUnused<(T, U), V> {
|
||||||
|
let mut iter = self.into_iter().zip(other);
|
||||||
|
ViewUnused::from_fn(|_| iter.next().expect("known to be Some"))
|
||||||
|
}
|
||||||
|
pub fn splat(v: T) -> Self
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let mut v = Some(v);
|
||||||
|
Self::from_fn(|i| {
|
||||||
|
let v = if i == V::UNUSED_INNER_LEN - 1 {
|
||||||
|
v.take()
|
||||||
|
} else {
|
||||||
|
v.clone()
|
||||||
|
};
|
||||||
|
let Some(v) = v else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn splat_copied(v: T) -> Self
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
Self::from_fn(|_| v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<SimValue<T>, V> {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
for i in self.iter_mut() {
|
||||||
|
SimValue::bits_mut(i).bits_mut().fill(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<&'_ mut SimValue<T>, V> {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
for i in self.iter_mut() {
|
||||||
|
SimValue::bits_mut(i).bits_mut().fill(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BoolOrIntType, V: PRegFlagsViewTrait> ViewUnused<Expr<T>, V>
|
||||||
|
where
|
||||||
|
UInt: CastToImpl<T, ValueOutput: ToExpr>,
|
||||||
|
{
|
||||||
|
pub fn clear(self) {
|
||||||
|
for i in self {
|
||||||
|
connect(i, UInt::new_dyn(i.ty().width()).zero().cast_to(i.ty()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a ViewUnused<T, V> {
|
||||||
|
type Item = &'a T;
|
||||||
|
type IntoIter = std::slice::Iter<'a, T>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.as_ref().iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, V: PRegFlagsViewTrait> IntoIterator for &'a mut ViewUnused<T, V> {
|
||||||
|
type Item = &'a mut T;
|
||||||
|
type IntoIter = std::slice::IterMut<'a, T>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.as_mut().iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, V: PRegFlagsViewTrait> Clone for ViewUnused<T, V>
|
||||||
|
where
|
||||||
|
V::UnusedInner<T>: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy, V: PRegFlagsViewTrait> Copy for ViewUnused<T, V> where V::UnusedInner<T>: Copy {}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug, V: PRegFlagsViewTrait> fmt::Debug for ViewUnused<T, V>
|
||||||
|
where
|
||||||
|
V::UnusedInner<T>: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("ViewUnused").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_view_trait {
|
||||||
|
(
|
||||||
|
$(#[$flags_mode_meta:meta])*
|
||||||
|
$flags_mode_vis:vis struct $FlagsMode:ident {}
|
||||||
|
|
||||||
|
$(#[$view_meta:meta])*
|
||||||
|
$view_vis:vis struct $View:ident {
|
||||||
|
$(#[$unused_field_meta:meta])*
|
||||||
|
$unused_vis:vis $unused:ident: ViewUnused([$($unused_field:ident),* $(,)?]),
|
||||||
|
$($(#[$view_field_meta:meta])*
|
||||||
|
$view_field_vis:vis $view_field:ident: $flags_field:ident,)*
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
$(#[$flags_mode_meta])*
|
||||||
|
$flags_mode_vis struct $FlagsMode {}
|
||||||
|
|
||||||
|
$(#[$view_meta])*
|
||||||
|
$view_vis struct $View<T> {
|
||||||
|
$(#[$unused_field_meta])*
|
||||||
|
$unused_vis $unused: ViewUnused<T, $FlagsMode>,
|
||||||
|
$($(#[$view_field_meta])*
|
||||||
|
$view_field_vis $view_field: T,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> $View<&'_ mut T> {
|
||||||
|
$view_vis const fn reborrow<'a>(&'a mut self) -> $View<&'a mut T> {
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = self;
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$(&mut **$unused_field,)*]),
|
||||||
|
$($view_field: &mut **$flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> $View<T> {
|
||||||
|
$view_vis fn splat(v: T) -> Self
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
$View {
|
||||||
|
$($view_field: v.clone(),)*
|
||||||
|
$unused: ViewUnused::splat(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$view_vis const fn splat_copied(v: T) -> Self
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
$View {
|
||||||
|
$($view_field: v,)*
|
||||||
|
$unused: ViewUnused([v; _]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$view_vis fn map<R>(self, mut f: impl FnMut(T) -> R) -> $View<R> {
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
let $View {
|
||||||
|
$unused,
|
||||||
|
$($view_field,)*
|
||||||
|
} = self;
|
||||||
|
$View {
|
||||||
|
$($view_field: f($view_field),)*
|
||||||
|
$unused: $unused.map(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$view_vis fn zip<U>(self, other: $View<U>) -> $View<(T, U)> {
|
||||||
|
struct Fields<T> {
|
||||||
|
$($unused_field: T,)*
|
||||||
|
$($flags_field: T,)*
|
||||||
|
}
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = self;
|
||||||
|
let this = Fields {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
};
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = other;
|
||||||
|
let other = Fields {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
};
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$((this.$unused_field, other.$unused_field),)*]),
|
||||||
|
$($view_field: (this.$flags_field, other.$flags_field),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlagsViewTraitSealed for $FlagsMode {
|
||||||
|
type UnusedInner<T> = [T; Self::UNUSED_INNER_LEN];
|
||||||
|
const UNUSED_INNER_LEN: usize = {
|
||||||
|
let v: &[&str] = &[$(stringify!($unused_field),)*];
|
||||||
|
v.len()
|
||||||
|
};
|
||||||
|
fn unused_inner_map<T, R>(
|
||||||
|
v: Self::UnusedInner<T>,
|
||||||
|
f: impl FnMut(T) -> R,
|
||||||
|
) -> Self::UnusedInner<R> {
|
||||||
|
v.map(f)
|
||||||
|
}
|
||||||
|
fn unused_inner_from_fn<T>(f: impl FnMut(usize) -> T) -> Self::UnusedInner<T> {
|
||||||
|
std::array::from_fn(f)
|
||||||
|
}
|
||||||
|
fn unused_inner_each_ref<T>(
|
||||||
|
v: &Self::UnusedInner<T>,
|
||||||
|
) -> Self::UnusedInner<&T> {
|
||||||
|
v.each_ref()
|
||||||
|
}
|
||||||
|
fn unused_inner_each_mut<T>(
|
||||||
|
v: &mut Self::UnusedInner<T>,
|
||||||
|
) -> Self::UnusedInner<&mut T> {
|
||||||
|
v.each_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlagsViewTrait for $FlagsMode {
|
||||||
|
type View<T> = $View<T>;
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn view<T: Type>(flags: impl ToExpr<Type = PRegFlags<T>>) -> Self::View<Expr<T>> {
|
||||||
|
#[hdl]
|
||||||
|
let PRegFlags::<T> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
} = flags.to_expr();
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn view_sim<T: Type>(flags: impl ToSimValue<Type = PRegFlags<T>>) -> Self::View<SimValue<T>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let PRegFlags::<T> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
} = flags.into_sim_value();
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn view_sim_ref<T: Type>(flags: &SimValue<PRegFlags<T>>) -> Self::View<&SimValue<T>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let PRegFlags::<T> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
} = flags;
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn view_sim_mut<T: Type>(flags: &mut SimValue<PRegFlags<T>>) -> Self::View<&mut SimValue<T>> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
let PRegFlags::<T> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
} = flags;
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn from_view<T: ToExpr>(view: Self::View<T>) -> Expr<PRegFlags<T::Type>> {
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = view;
|
||||||
|
#[hdl]
|
||||||
|
PRegFlags::<_> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn from_view_sim<T: ToSimValue>(view: Self::View<T>) -> SimValue<PRegFlags<T::Type>> {
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = view;
|
||||||
|
#[hdl(sim)]
|
||||||
|
PRegFlags::<_> {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_unused_into_view<T>(unused: ViewUnused<T, PRegFlagsAllUnused>) -> Self::View<T> {
|
||||||
|
let fields = Fields::from_view_unused(unused);
|
||||||
|
$View {
|
||||||
|
$unused: ViewUnused([$(fields.$unused_field,)*]),
|
||||||
|
$($view_field: fields.$flags_field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_into_view_unused<T>(view: Self::View<T>) -> ViewUnused<T, PRegFlagsAllUnused> {
|
||||||
|
let $View {
|
||||||
|
$unused: ViewUnused([$($unused_field,)*]),
|
||||||
|
$($view_field: $flags_field,)*
|
||||||
|
} = view;
|
||||||
|
let fields = Fields {
|
||||||
|
$($unused_field,)*
|
||||||
|
$($flags_field,)*
|
||||||
|
};
|
||||||
|
fields.into_view_unused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_view_trait! {
|
||||||
|
#[hdl(cmp_eq)]
|
||||||
|
pub struct PRegFlagsPowerISA {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct PRegFlagsPowerISAView {
|
||||||
|
pub unused: ViewUnused([]),
|
||||||
|
pub xer_ca: pwr_ca_x86_cf,
|
||||||
|
pub xer_ca32: pwr_ca32_x86_af,
|
||||||
|
pub xer_ov: pwr_ov_x86_of,
|
||||||
|
pub xer_ov32: pwr_ov32_x86_df,
|
||||||
|
pub cr_lt: pwr_cr_lt_x86_sf,
|
||||||
|
pub cr_gt: pwr_cr_gt_x86_pf,
|
||||||
|
pub cr_eq: pwr_cr_eq_x86_zf,
|
||||||
|
/// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
|
||||||
|
pub so: pwr_so,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlagsPowerISAView<Option<usize>> {
|
||||||
|
pub const CR_BIT_LE_INDEXES: Self = {
|
||||||
|
let mut v = Self::splat_copied(None);
|
||||||
|
let bits = v.cr_bits_lsb0_mut();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bits.len() {
|
||||||
|
*bits[i] = Some(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
pub const CR_BIT_BE_INDEXES: Self = {
|
||||||
|
let mut v = Self::splat_copied(None);
|
||||||
|
let bits = v.cr_bits_msb0_mut();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bits.len() {
|
||||||
|
*bits[i] = Some(i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlagsPowerISAView<Option<SimValue<ConditionMode>>> {
|
||||||
|
pub fn cr_condition_modes_sim() -> Self {
|
||||||
|
PRegFlagsPowerISAView::cr_condition_modes().map(|v| v.map(ToSimValue::into_sim_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlagsPowerISAView<Option<Expr<ConditionMode>>> {
|
||||||
|
pub fn cr_condition_modes() -> Self {
|
||||||
|
Self {
|
||||||
|
unused: ViewUnused([]),
|
||||||
|
xer_ca: None,
|
||||||
|
xer_ca32: None,
|
||||||
|
xer_ov: None,
|
||||||
|
xer_ov32: None,
|
||||||
|
cr_lt: Some(ConditionMode.SLt()),
|
||||||
|
cr_gt: Some(ConditionMode.SGt()),
|
||||||
|
cr_eq: Some(ConditionMode.Eq()),
|
||||||
|
so: Some(ConditionMode.Overflow()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PRegFlagsPowerISAView<T> {
|
||||||
|
pub fn into_cr_bits_msb0(self) -> [T; 4] {
|
||||||
|
[self.cr_lt, self.cr_gt, self.cr_eq, self.so]
|
||||||
|
}
|
||||||
|
pub const fn cr_bits_msb0_ref(&self) -> [&T; 4] {
|
||||||
|
[&self.cr_lt, &self.cr_gt, &self.cr_eq, &self.so]
|
||||||
|
}
|
||||||
|
pub const fn cr_bits_msb0_mut(&mut self) -> [&mut T; 4] {
|
||||||
|
[
|
||||||
|
&mut self.cr_lt,
|
||||||
|
&mut self.cr_gt,
|
||||||
|
&mut self.cr_eq,
|
||||||
|
&mut self.so,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
pub fn into_cr_bits_lsb0(self) -> [T; 4] {
|
||||||
|
let mut retval = self.into_cr_bits_msb0();
|
||||||
|
retval.reverse();
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
pub const fn cr_bits_lsb0_ref(&self) -> [&T; 4] {
|
||||||
|
let [b0, b1, b2, b3] = self.cr_bits_msb0_ref();
|
||||||
|
[b3, b2, b1, b0]
|
||||||
|
}
|
||||||
|
pub const fn cr_bits_lsb0_mut(&mut self) -> [&mut T; 4] {
|
||||||
|
let [b0, b1, b2, b3] = self.cr_bits_msb0_mut();
|
||||||
|
[b3, b2, b1, b0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PRegFlagsPowerISA {
|
impl PRegFlagsPowerISA {
|
||||||
pub fn xer_ca(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub fn cr_condition_modes_msb0() -> [Expr<ConditionMode>; 4] {
|
||||||
flags.to_expr().pwr_ca_x86_cf
|
PRegFlagsPowerISAView::cr_condition_modes()
|
||||||
|
.into_cr_bits_msb0()
|
||||||
|
.map(|v| v.expect("known to be Some"))
|
||||||
}
|
}
|
||||||
pub fn xer_ca32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub fn cr_condition_modes_lsb0() -> [Expr<ConditionMode>; 4] {
|
||||||
flags.to_expr().pwr_ca32_x86_af
|
PRegFlagsPowerISAView::cr_condition_modes()
|
||||||
|
.into_cr_bits_lsb0()
|
||||||
|
.map(|v| v.expect("known to be Some"))
|
||||||
}
|
}
|
||||||
pub fn xer_ov(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub fn cr_condition_modes_msb0_sim() -> [SimValue<ConditionMode>; 4] {
|
||||||
flags.to_expr().pwr_ov_x86_of
|
PRegFlagsPowerISAView::cr_condition_modes_sim()
|
||||||
|
.into_cr_bits_msb0()
|
||||||
|
.map(|v| v.expect("known to be Some"))
|
||||||
}
|
}
|
||||||
pub fn xer_ov32(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub fn cr_condition_modes_lsb0_sim() -> [SimValue<ConditionMode>; 4] {
|
||||||
flags.to_expr().pwr_ov32_x86_df
|
PRegFlagsPowerISAView::cr_condition_modes_sim()
|
||||||
}
|
.into_cr_bits_lsb0()
|
||||||
/// both `CR<N>.SO` and `XER.SO` since instructions that write to both always write the same value
|
.map(|v| v.expect("known to be Some"))
|
||||||
pub fn so(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_so
|
|
||||||
}
|
|
||||||
pub fn cr_lt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_cr_lt_x86_sf
|
|
||||||
}
|
|
||||||
pub fn cr_gt(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_cr_gt_x86_pf
|
|
||||||
}
|
|
||||||
pub fn cr_eq(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_cr_eq_x86_zf
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
|
|
||||||
// list all flags explicitly so we don't miss handling any new flags
|
|
||||||
#[hdl]
|
|
||||||
let PRegFlags {
|
|
||||||
pwr_ca_x86_cf: _,
|
|
||||||
pwr_ca32_x86_af: _,
|
|
||||||
pwr_ov_x86_of: _,
|
|
||||||
pwr_ov32_x86_df: _,
|
|
||||||
pwr_cr_lt_x86_sf: _,
|
|
||||||
pwr_cr_gt_x86_pf: _,
|
|
||||||
pwr_cr_eq_x86_zf: _,
|
|
||||||
pwr_so: _,
|
|
||||||
} = flags;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
impl_view_trait! {
|
||||||
pub struct PRegFlagsX86 {}
|
#[hdl(cmp_eq)]
|
||||||
|
pub struct PRegFlagsX86 {}
|
||||||
|
|
||||||
impl PRegFlagsX86 {
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub fn cf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
#[non_exhaustive]
|
||||||
flags.to_expr().pwr_ca_x86_cf
|
pub struct PRegFlagsX86View {
|
||||||
}
|
pub unused: ViewUnused([pwr_so]),
|
||||||
pub fn zf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub cf: pwr_ca_x86_cf,
|
||||||
flags.to_expr().pwr_cr_eq_x86_zf
|
pub zf: pwr_cr_eq_x86_zf,
|
||||||
}
|
pub sf: pwr_cr_lt_x86_sf,
|
||||||
pub fn sf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub of: pwr_ov_x86_of,
|
||||||
flags.to_expr().pwr_cr_lt_x86_sf
|
pub af: pwr_ca32_x86_af,
|
||||||
}
|
pub pf: pwr_cr_gt_x86_pf,
|
||||||
pub fn of(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
pub df: pwr_ov32_x86_df,
|
||||||
flags.to_expr().pwr_ov_x86_of
|
|
||||||
}
|
|
||||||
pub fn af(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_ca32_x86_af
|
|
||||||
}
|
|
||||||
pub fn pf(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_cr_gt_x86_pf
|
|
||||||
}
|
|
||||||
pub fn df(flags: impl ToExpr<Type = PRegFlags>) -> Expr<Bool> {
|
|
||||||
flags.to_expr().pwr_ov32_x86_df
|
|
||||||
}
|
|
||||||
#[hdl]
|
|
||||||
pub fn clear_unused(flags: impl ToExpr<Type = PRegFlags>) {
|
|
||||||
// list all flags explicitly so we don't miss handling any new flags
|
|
||||||
#[hdl]
|
|
||||||
let PRegFlags {
|
|
||||||
pwr_ca_x86_cf: _,
|
|
||||||
pwr_ca32_x86_af: _,
|
|
||||||
pwr_ov_x86_of: _,
|
|
||||||
pwr_ov32_x86_df: _,
|
|
||||||
pwr_cr_lt_x86_sf: _,
|
|
||||||
pwr_cr_gt_x86_pf: _,
|
|
||||||
pwr_cr_eq_x86_zf: _,
|
|
||||||
pwr_so: unused1,
|
|
||||||
} = flags;
|
|
||||||
connect(unused1, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
macro_rules! impl_flags {
|
||||||
/// this is *not* the same as any particular ISA's flags register,
|
(
|
||||||
/// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
|
$(#[$struct_meta:meta])*
|
||||||
///
|
$struct_vis:vis struct $PRegFlags:ident<$T:ident: Type = Bool> {
|
||||||
/// Accessor functions depend on the ISA:
|
$($field:ident: T,)*
|
||||||
///
|
}
|
||||||
/// * PowerISA: [`struct@PRegFlagsPowerISA`]
|
) => {
|
||||||
/// * x86: [`struct@PRegFlagsX86`]
|
$(#[$struct_meta])*
|
||||||
pub struct PRegFlags {
|
$struct_vis struct $PRegFlags<$T: Type = Bool> {
|
||||||
pwr_ca_x86_cf: Bool,
|
$($field: $T,)*
|
||||||
pwr_ca32_x86_af: Bool,
|
}
|
||||||
pwr_ov_x86_of: Bool,
|
|
||||||
pwr_ov32_x86_df: Bool,
|
struct Fields<$T> {
|
||||||
pwr_cr_lt_x86_sf: Bool,
|
$($field: $T,)*
|
||||||
pwr_cr_gt_x86_pf: Bool,
|
}
|
||||||
pwr_cr_eq_x86_zf: Bool,
|
|
||||||
pwr_so: Bool,
|
impl<$T> Fields<$T> {
|
||||||
|
fn from_view_unused(unused: ViewUnused<$T, PRegFlagsAllUnused>) -> Self {
|
||||||
|
let ViewUnused([
|
||||||
|
$($field,)*
|
||||||
|
]) = unused;
|
||||||
|
Self {
|
||||||
|
$($field,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn into_view_unused(self) -> ViewUnused<$T, PRegFlagsAllUnused> {
|
||||||
|
ViewUnused([
|
||||||
|
$(self.$field,)*
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_view_trait! {
|
||||||
|
#[hdl(cmp_eq)]
|
||||||
|
pub struct PRegFlagsAllUnused {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct PRegFlagsAllUnusedView {
|
||||||
|
pub unused: ViewUnused([
|
||||||
|
$($field,)*
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_flags! {
|
||||||
|
#[hdl(cmp_eq)]
|
||||||
|
/// this is *not* the same as any particular ISA's flags register,
|
||||||
|
/// on PowerISA it is a combination of some bits from XER with a single 4-bit CR field.
|
||||||
|
///
|
||||||
|
/// Accessor functions depend on the ISA:
|
||||||
|
///
|
||||||
|
/// * PowerISA: [`struct@PRegFlagsPowerISA`]
|
||||||
|
/// * x86: [`struct@PRegFlagsX86`]
|
||||||
|
pub struct PRegFlags<T: Type = Bool> {
|
||||||
|
pwr_ca32_x86_af: T,
|
||||||
|
pwr_ca_x86_cf: T,
|
||||||
|
pwr_ov32_x86_df: T,
|
||||||
|
pwr_ov_x86_of: T,
|
||||||
|
pwr_so: T,
|
||||||
|
pwr_cr_eq_x86_zf: T,
|
||||||
|
pwr_cr_gt_x86_pf: T,
|
||||||
|
pwr_cr_lt_x86_sf: T,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> PRegFlags<T> {
|
||||||
|
pub const fn field_ty(self) -> T {
|
||||||
|
self.pwr_so
|
||||||
|
}
|
||||||
|
pub fn view<V: PRegFlagsViewTrait>(flags: impl ToExpr<Type = Self>) -> V::View<Expr<T>> {
|
||||||
|
V::view(flags)
|
||||||
|
}
|
||||||
|
pub fn view_sim<V: PRegFlagsViewTrait>(
|
||||||
|
flags: impl ToSimValue<Type = Self>,
|
||||||
|
) -> V::View<SimValue<T>> {
|
||||||
|
V::view_sim(flags)
|
||||||
|
}
|
||||||
|
pub fn view_sim_ref<V: PRegFlagsViewTrait>(flags: &SimValue<Self>) -> V::View<&SimValue<T>> {
|
||||||
|
V::view_sim_ref(flags)
|
||||||
|
}
|
||||||
|
pub fn view_sim_mut<V: PRegFlagsViewTrait>(
|
||||||
|
flags: &mut SimValue<Self>,
|
||||||
|
) -> V::View<&mut SimValue<T>> {
|
||||||
|
V::view_sim_mut(flags)
|
||||||
|
}
|
||||||
|
pub fn from_view<V: PRegFlagsViewTrait>(view: V::View<impl ToExpr<Type = T>>) -> Expr<Self> {
|
||||||
|
V::from_view(view)
|
||||||
|
}
|
||||||
|
pub fn from_view_sim<V: PRegFlagsViewTrait>(
|
||||||
|
view: V::View<impl ToSimValue<Type = T>>,
|
||||||
|
) -> SimValue<Self> {
|
||||||
|
V::from_view_sim(view)
|
||||||
|
}
|
||||||
|
pub fn fields(flags: impl ToExpr<Type = Self>) -> ViewUnused<Expr<T>, PRegFlagsAllUnused> {
|
||||||
|
Self::view::<PRegFlagsAllUnused>(flags).unused
|
||||||
|
}
|
||||||
|
pub fn fields_sim(
|
||||||
|
flags: impl ToSimValue<Type = Self>,
|
||||||
|
) -> ViewUnused<SimValue<T>, PRegFlagsAllUnused> {
|
||||||
|
Self::view_sim::<PRegFlagsAllUnused>(flags).unused
|
||||||
|
}
|
||||||
|
pub fn fields_sim_ref(flags: &SimValue<Self>) -> ViewUnused<&SimValue<T>, PRegFlagsAllUnused> {
|
||||||
|
Self::view_sim_ref::<PRegFlagsAllUnused>(flags).unused
|
||||||
|
}
|
||||||
|
pub fn fields_sim_mut(
|
||||||
|
flags: &mut SimValue<Self>,
|
||||||
|
) -> ViewUnused<&mut SimValue<T>, PRegFlagsAllUnused> {
|
||||||
|
Self::view_sim_mut::<PRegFlagsAllUnused>(flags).unused
|
||||||
|
}
|
||||||
|
pub fn from_fields(
|
||||||
|
fields: ViewUnused<impl ToExpr<Type = T>, PRegFlagsAllUnused>,
|
||||||
|
) -> Expr<Self> {
|
||||||
|
Self::from_view::<PRegFlagsAllUnused>(PRegFlagsAllUnusedView { unused: fields })
|
||||||
|
}
|
||||||
|
pub fn from_fields_sim(
|
||||||
|
fields: ViewUnused<impl ToSimValue<Type = T>, PRegFlagsAllUnused>,
|
||||||
|
) -> SimValue<Self> {
|
||||||
|
Self::from_view_sim::<PRegFlagsAllUnused>(PRegFlagsAllUnusedView { unused: fields })
|
||||||
|
}
|
||||||
|
/// if trying to set all fields individually, prefer using the individual accessor
|
||||||
|
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
|
||||||
|
pub fn splat(v: impl ToExpr<Type = T>) -> Expr<Self> {
|
||||||
|
Self::from_fields(ViewUnused::splat(v.to_expr()))
|
||||||
|
}
|
||||||
|
/// if trying to set all fields individually, prefer using the individual accessor
|
||||||
|
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
|
||||||
|
pub fn splat_sim(v: impl ToSimValue<Type = T>) -> SimValue<Self> {
|
||||||
|
Self::from_fields_sim(ViewUnused::splat(v.into_sim_value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRegFlags<UIntInRange<0, { PRegFlags::FLAG_COUNT }>> {
|
||||||
|
pub fn flag_indexes() -> SimValue<Self> {
|
||||||
|
let ty = <Self as StaticType>::TYPE.field_ty();
|
||||||
|
Self::from_fields_sim(ViewUnused::from_fn(|i| i.to_sim_value_with_type(ty)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PRegFlags {
|
impl PRegFlags {
|
||||||
/// if trying to set all fields individually, prefer using the individual accessor
|
/// if trying to set all fields individually, prefer using the individual accessor
|
||||||
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
|
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
|
||||||
#[hdl]
|
|
||||||
pub fn zeroed() -> Expr<PRegFlags> {
|
pub fn zeroed() -> Expr<PRegFlags> {
|
||||||
#[hdl]
|
Self::splat(false)
|
||||||
PRegFlags {
|
|
||||||
pwr_ca_x86_cf: false,
|
|
||||||
pwr_ca32_x86_af: false,
|
|
||||||
pwr_ov_x86_of: false,
|
|
||||||
pwr_ov32_x86_df: false,
|
|
||||||
pwr_cr_lt_x86_sf: false,
|
|
||||||
pwr_cr_gt_x86_pf: false,
|
|
||||||
pwr_cr_eq_x86_zf: false,
|
|
||||||
pwr_so: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/// if trying to set all fields individually, prefer using the individual accessor
|
||||||
|
/// functions and [`PRegFlagsPowerISA::clear_unused()`]/[`PRegFlagsX86::clear_unused()`]/etc.
|
||||||
|
pub fn zeroed_sim() -> SimValue<PRegFlags> {
|
||||||
|
Self::splat_sim(false)
|
||||||
|
}
|
||||||
|
pub const FLAG_COUNT: usize = PRegFlagsAllUnused::UNUSED_INNER_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(cmp_eq)]
|
#[hdl(cmp_eq)]
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::CpuConfig,
|
config::CpuConfig,
|
||||||
instruction::{
|
instruction::{
|
||||||
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait, RenamedMOp,
|
AluBranchMOp, LoadStoreMOp, MOp, MOpDestReg, MOpInto, MOpRegNum, MOpTrait,
|
||||||
UnitOutRegNum, mop_enum,
|
MOpVariantVisitOps, MOpVariantVisitor, MOpVisitVariants, RenamedMOp, UnitOutRegNum,
|
||||||
|
mop_enum,
|
||||||
},
|
},
|
||||||
register::{FlagsMode, PRegValue},
|
register::{FlagsMode, PRegValue},
|
||||||
unit::unit_base::UnitToRegAlloc,
|
unit::unit_base::UnitToRegAlloc,
|
||||||
|
|
@ -16,6 +17,7 @@ use fayalite::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub mod alu_branch;
|
pub mod alu_branch;
|
||||||
pub mod unit_base;
|
pub mod unit_base;
|
||||||
|
|
@ -83,7 +85,15 @@ macro_rules! all_units {
|
||||||
#[impl_mop_into = false]
|
#[impl_mop_into = false]
|
||||||
#[hdl]
|
#[hdl]
|
||||||
$(#[$enum_meta])*
|
$(#[$enum_meta])*
|
||||||
$vis enum $UnitMOpEnum<$DestReg: Type, $SrcRegWidth: Size, #[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type> {
|
$vis enum $UnitMOpEnum<
|
||||||
|
$DestReg: Type,
|
||||||
|
$SrcRegWidth: Size,
|
||||||
|
#[MOp(get_ty = $transformed_move_op_get_ty)] $TransformedMoveOp: Type,
|
||||||
|
#[MOpVisitVariants] [
|
||||||
|
$TransformedMoveOp: MOpVisitVariants<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,
|
||||||
|
$($Op: MOpVisitVariants<DestReg = $DestReg, SrcRegWidth = $SrcRegWidth>,)*
|
||||||
|
]
|
||||||
|
> {
|
||||||
$(
|
$(
|
||||||
$(#[$variant_meta])*
|
$(#[$variant_meta])*
|
||||||
$Unit($Op),
|
$Unit($Op),
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
config::CpuConfig,
|
config::CpuConfig,
|
||||||
instruction::{
|
instruction::{
|
||||||
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, LogicalMOp, MOpTrait,
|
AddSubMOp, AluBranchMOp, AluCommonMOp, BranchMOp, COMMON_MOP_SRC_LEN, CommonMOpDefaultImm,
|
||||||
OutputIntegerMode, RenamedMOp, UnitOutRegNum,
|
CompareMOp, LogicalFlagsMOp, LogicalMOp, MOpTrait, OutputIntegerMode, ReadSpecialMOp,
|
||||||
|
RenamedMOp, ShiftRotateMOp, UnitOutRegNum,
|
||||||
|
},
|
||||||
|
register::{
|
||||||
|
FlagsMode, PRegFlagsPowerISA, PRegFlagsPowerISAView, PRegFlagsViewTrait, PRegFlagsX86,
|
||||||
|
PRegFlagsX86View, PRegValue, ViewUnused,
|
||||||
},
|
},
|
||||||
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
|
|
||||||
unit::{
|
unit::{
|
||||||
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
|
DynUnit, DynUnitWrapper, GlobalState, UnitKind, UnitMOp, UnitOutput, UnitResult,
|
||||||
UnitResultCompleted, UnitTrait,
|
UnitResultCompleted, UnitTrait,
|
||||||
|
|
@ -38,11 +42,11 @@ fn add_sub<SrcCount: KnownSize>(
|
||||||
add_pc,
|
add_pc,
|
||||||
} = mop;
|
} = mop;
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let AluCommonMOp::<_, _, _> {
|
let AluCommonMOp::<_, _, _, _> {
|
||||||
common,
|
common,
|
||||||
output_integer_mode,
|
output_integer_mode,
|
||||||
} = alu_common;
|
} = alu_common;
|
||||||
let imm: Expr<UInt<64>> = CommonMOp::imm(common).cast_to_static();
|
let imm = CommonMOpDefaultImm::as_sint_dyn(common.imm).cast_to_static::<UInt<64>>();
|
||||||
#[hdl]
|
#[hdl]
|
||||||
let carry_in_before_inversion = wire();
|
let carry_in_before_inversion = wire();
|
||||||
connect(carry_in_before_inversion, false);
|
connect(carry_in_before_inversion, false);
|
||||||
|
|
@ -56,13 +60,13 @@ fn add_sub<SrcCount: KnownSize>(
|
||||||
FlagsMode::PowerISA(_) => {
|
FlagsMode::PowerISA(_) => {
|
||||||
connect(
|
connect(
|
||||||
carry_in_before_inversion,
|
carry_in_before_inversion,
|
||||||
PRegFlagsPowerISA::xer_ca(src_values[1].flags),
|
PRegFlagsPowerISA::view(src_values[1].flags).xer_ca,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
FlagsMode::X86(_) => {
|
FlagsMode::X86(_) => {
|
||||||
connect(
|
connect(
|
||||||
carry_in_before_inversion,
|
carry_in_before_inversion,
|
||||||
PRegFlagsX86::cf(src_values[1].flags),
|
PRegFlagsX86::view(src_values[1].flags).cf,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,27 +203,36 @@ fn add_sub<SrcCount: KnownSize>(
|
||||||
#[hdl]
|
#[hdl]
|
||||||
match flags_mode {
|
match flags_mode {
|
||||||
FlagsMode::PowerISA(_) => {
|
FlagsMode::PowerISA(_) => {
|
||||||
PRegFlagsPowerISA::clear_unused(flags);
|
connect(
|
||||||
connect(PRegFlagsPowerISA::xer_ca(flags), pwr_ca);
|
flags,
|
||||||
connect(PRegFlagsPowerISA::xer_ca32(flags), pwr_ca32);
|
PRegFlagsPowerISA::from_view(PRegFlagsPowerISAView {
|
||||||
connect(PRegFlagsPowerISA::xer_ov(flags), pwr_ov);
|
unused: ViewUnused::splat(false.to_expr()),
|
||||||
connect(PRegFlagsPowerISA::xer_ov32(flags), pwr_ov32);
|
xer_ca: pwr_ca,
|
||||||
connect(PRegFlagsPowerISA::cr_lt(flags), pwr_cr_lt);
|
xer_ca32: pwr_ca32,
|
||||||
connect(PRegFlagsPowerISA::cr_gt(flags), pwr_cr_gt);
|
xer_ov: pwr_ov,
|
||||||
connect(PRegFlagsPowerISA::cr_eq(flags), pwr_cr_eq);
|
xer_ov32: pwr_ov32,
|
||||||
connect(PRegFlagsPowerISA::so(flags), pwr_so);
|
so: pwr_so,
|
||||||
|
cr_lt: pwr_cr_lt,
|
||||||
|
cr_gt: pwr_cr_gt,
|
||||||
|
cr_eq: pwr_cr_eq,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
FlagsMode::X86(_) => {
|
FlagsMode::X86(_) => {
|
||||||
PRegFlagsX86::clear_unused(flags);
|
connect(
|
||||||
connect(PRegFlagsX86::cf(flags), x86_cf);
|
flags,
|
||||||
connect(PRegFlagsX86::af(flags), x86_af);
|
PRegFlagsX86::from_view(PRegFlagsX86View {
|
||||||
connect(PRegFlagsX86::of(flags), x86_of);
|
unused: ViewUnused::splat(false.to_expr()),
|
||||||
connect(PRegFlagsX86::sf(flags), x86_sf);
|
cf: x86_cf,
|
||||||
connect(PRegFlagsX86::pf(flags), x86_pf);
|
zf: x86_zf,
|
||||||
connect(PRegFlagsX86::zf(flags), x86_zf);
|
sf: x86_sf,
|
||||||
|
of: x86_of,
|
||||||
// this insn doesn't write DF, so it's output isn't used for reading DF
|
af: x86_af,
|
||||||
connect(PRegFlagsX86::df(flags), false);
|
pf: x86_pf,
|
||||||
|
// this insn doesn't write DF, so it's output isn't used for reading DF
|
||||||
|
df: false.to_expr(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[hdl]
|
#[hdl]
|
||||||
|
|
@ -230,9 +243,95 @@ fn add_sub<SrcCount: KnownSize>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn logical_flags(
|
||||||
|
mop: Expr<LogicalFlagsMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||||
|
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]
|
#[hdl]
|
||||||
fn logical(
|
fn logical(
|
||||||
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<2>>>,
|
||||||
|
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]
|
||||||
|
fn logical_i(
|
||||||
|
mop: Expr<LogicalMOp<UnitOutRegNum<DynSize>, DynSize, ConstUsize<1>>>,
|
||||||
|
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]
|
||||||
|
fn shift_rotate(
|
||||||
|
mop: Expr<ShiftRotateMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||||
|
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]
|
||||||
|
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]
|
||||||
|
fn branch<SrcCount: KnownSize>(
|
||||||
|
mop: Expr<BranchMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
|
||||||
|
pc: Expr<UInt<64>>,
|
||||||
|
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]
|
||||||
|
fn read_special(
|
||||||
|
mop: Expr<ReadSpecialMOp<UnitOutRegNum<DynSize>, DynSize>>,
|
||||||
|
pc: Expr<UInt<64>>,
|
||||||
flags_mode: Expr<FlagsMode>,
|
flags_mode: Expr<FlagsMode>,
|
||||||
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
|
||||||
) -> Expr<UnitResultCompleted<()>> {
|
) -> Expr<UnitResultCompleted<()>> {
|
||||||
|
|
@ -319,6 +418,23 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
AluBranchMOp::<_, _>::LogicalFlags(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(logical_flags(
|
||||||
|
mop,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
AluBranchMOp::<_, _>::Logical(mop) => connect(
|
AluBranchMOp::<_, _>::Logical(mop) => connect(
|
||||||
unit_base.execute_end,
|
unit_base.execute_end,
|
||||||
HdlSome(
|
HdlSome(
|
||||||
|
|
@ -336,6 +452,128 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
AluBranchMOp::<_, _>::LogicalI(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(logical_i(
|
||||||
|
mop,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AluBranchMOp::<_, _>::ShiftRotate(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(shift_rotate(
|
||||||
|
mop,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AluBranchMOp::<_, _>::Branch(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(branch(
|
||||||
|
mop,
|
||||||
|
pc,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AluBranchMOp::<_, _>::BranchI(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(branch(
|
||||||
|
mop,
|
||||||
|
pc,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AluBranchMOp::<_, _>::ReadSpecial(mop) => connect(
|
||||||
|
unit_base.execute_end,
|
||||||
|
HdlSome(
|
||||||
|
#[hdl]
|
||||||
|
ExecuteEnd::<_, _> {
|
||||||
|
unit_output: #[hdl]
|
||||||
|
UnitOutput::<_, _> {
|
||||||
|
which: MOpTrait::dest_reg(mop),
|
||||||
|
result: UnitResult[()].Completed(read_special(
|
||||||
|
mop,
|
||||||
|
pc,
|
||||||
|
global_state.flags_mode,
|
||||||
|
src_values,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
use fayalite::{expr::ops::ArrayLiteral, module::wire_with_loc, prelude::*};
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
pub mod array_vec;
|
pub mod array_vec;
|
||||||
pub mod tree_reduce;
|
pub mod tree_reduce;
|
||||||
|
|
||||||
|
|
@ -25,3 +28,255 @@ pub(crate) const fn range_u32_nth_or_panic(range: &std::ops::Range<u32>, index:
|
||||||
panic!("index out of range")
|
panic!("index out of range")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move to fayalite
|
||||||
|
pub trait Rotate<Amount> {
|
||||||
|
type Output;
|
||||||
|
/// like [`usize::rotate_left`] or [`<[T]>::rotate_left`](slice::rotate_left) depending on `Self` -- note that in lsb0 those rotate in opposite directions
|
||||||
|
fn rotate_left(&self, amount: Amount) -> Self::Output;
|
||||||
|
/// like [`usize::rotate_right`] or [`<[T]>::rotate_right`](slice::rotate_right) depending on `Self` -- note that in lsb0 those rotate in opposite directions
|
||||||
|
fn rotate_right(&self, amount: Amount) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VSz: Size> Rotate<usize> for Expr<UIntType<VSz>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`usize::rotate_left`]
|
||||||
|
fn rotate_left(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = *self << amount;
|
||||||
|
let r = *self >> (self.ty().width() - amount);
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
/// like [`usize::rotate_right`]
|
||||||
|
fn rotate_right(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = *self << (self.ty().width() - amount);
|
||||||
|
let r = *self >> amount;
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VSz: Size> Rotate<usize> for SimValue<UIntType<VSz>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`usize::rotate_left`]
|
||||||
|
fn rotate_left(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = self << amount;
|
||||||
|
let r = self >> (self.ty().width() - amount);
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
/// like [`usize::rotate_right`]
|
||||||
|
fn rotate_right(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = self << (self.ty().width() - amount);
|
||||||
|
let r = self >> amount;
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VSz: Size, ASz: Size> Rotate<Expr<UIntType<ASz>>> for Expr<UIntType<VSz>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`usize::rotate_left`]
|
||||||
|
fn rotate_left(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = *self << amount;
|
||||||
|
let r = *self >> (self.ty().width() - amount);
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
/// like [`usize::rotate_right`]
|
||||||
|
fn rotate_right(&self, amount: Expr<UIntType<ASz>>) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = *self << (self.ty().width() - amount).cast_to(amount.ty());
|
||||||
|
let r = *self >> amount;
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VSz: Size, ASz: Size> Rotate<SimValue<UIntType<ASz>>> for SimValue<UIntType<VSz>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`usize::rotate_left`]
|
||||||
|
fn rotate_left(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = self << &amount;
|
||||||
|
let r = self >> (self.ty().width() - amount);
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
/// like [`usize::rotate_right`]
|
||||||
|
fn rotate_right(&self, amount: SimValue<UIntType<ASz>>) -> Self::Output {
|
||||||
|
if self.ty().width() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().width();
|
||||||
|
let l = self << (self.ty().width() - &amount).cast_to(amount.ty());
|
||||||
|
let r = self >> amount;
|
||||||
|
(l | r).cast_to(self.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_rotate_helper<T: Type, N: Size, AmountSize: Size>(
|
||||||
|
mut array: Expr<ArrayType<T, N>>,
|
||||||
|
amount: Expr<UIntType<AmountSize>>,
|
||||||
|
rotate_fn: impl Fn(&mut [Expr<T>], usize),
|
||||||
|
rotate_fn_name: &str,
|
||||||
|
) -> Expr<ArrayType<T, N>> {
|
||||||
|
let Some(mut prev_step_size) = NonZero::new(array.ty().len()) else {
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
fn named<T: Type>(v: Expr<T>, name: impl AsRef<str>) -> Expr<T> {
|
||||||
|
let w = wire_with_loc(name.as_ref(), SourceLocation::caller(), v.ty());
|
||||||
|
connect(w, v);
|
||||||
|
w
|
||||||
|
}
|
||||||
|
fn non_empty_array_to_expr<T: Type, Len: Size>(
|
||||||
|
v: impl AsRef<[Expr<T>]>,
|
||||||
|
) -> Expr<ArrayType<T, Len>> {
|
||||||
|
let v = v.as_ref();
|
||||||
|
ArrayLiteral::new(v[0].ty(), v.iter().map(|v| Expr::canonical(*v)).collect()).to_expr()
|
||||||
|
}
|
||||||
|
fn mux<T: Type>(b: Expr<Bool>, true_v: Expr<T>, false_v: Expr<T>) -> Expr<T> {
|
||||||
|
let a: Expr<Array<T, 2>> = non_empty_array_to_expr([false_v, true_v]);
|
||||||
|
a[b.cast_to_static::<UInt<1>>()]
|
||||||
|
}
|
||||||
|
let amount_ty = amount.ty();
|
||||||
|
let mut amount = (amount % prev_step_size).cast_to(amount_ty);
|
||||||
|
loop {
|
||||||
|
(prev_step_size, amount, array) =
|
||||||
|
if let Some(step_size) = NonZero::new(prev_step_size.get() / 2) {
|
||||||
|
let amount = named(amount, format!("{rotate_fn_name}_amount_{prev_step_size}"));
|
||||||
|
let do_rotate = amount.cmp_ge(step_size);
|
||||||
|
let mut rotated_array = (*array).clone();
|
||||||
|
rotate_fn(rotated_array.as_mut(), step_size.get());
|
||||||
|
let rotated_array = named(
|
||||||
|
non_empty_array_to_expr(rotated_array),
|
||||||
|
format!("{rotate_fn_name}_rotated_array_{step_size}"),
|
||||||
|
);
|
||||||
|
let array = mux(do_rotate, rotated_array, array);
|
||||||
|
let array = named(array, format!("{rotate_fn_name}_array_{step_size}"));
|
||||||
|
let amount = mux(do_rotate, (amount - step_size).cast_to(amount_ty), amount);
|
||||||
|
(step_size, amount, array)
|
||||||
|
} else {
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, N: Size, AmountSize: Size> Rotate<Expr<UIntType<AmountSize>>>
|
||||||
|
for Expr<ArrayType<T, N>>
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`<[T]>::rotate_left`](slice::rotate_left)
|
||||||
|
fn rotate_left(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
|
||||||
|
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_left, "rotate_left")
|
||||||
|
}
|
||||||
|
/// like [`<[T]>::rotate_right`](slice::rotate_right)
|
||||||
|
fn rotate_right(&self, amount: Expr<UIntType<AmountSize>>) -> Self::Output {
|
||||||
|
array_rotate_helper(*self, amount, <[Expr<T>]>::rotate_right, "rotate_right")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, N: Size, AmountSize: Size> Rotate<SimValue<UIntType<AmountSize>>>
|
||||||
|
for SimValue<ArrayType<T, N>>
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`<[T]>::rotate_left`](slice::rotate_left)
|
||||||
|
fn rotate_left(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let mut retval = self.clone();
|
||||||
|
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
/// like [`<[T]>::rotate_right`](slice::rotate_right)
|
||||||
|
fn rotate_right(&self, amount: SimValue<UIntType<AmountSize>>) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let Ok(amount) = usize::try_from(amount.to_bigint() % self.ty().len()) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let mut retval = self.clone();
|
||||||
|
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, N: Size> Rotate<usize> for Expr<ArrayType<T, N>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`<[T]>::rotate_left`](slice::rotate_left)
|
||||||
|
fn rotate_left(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().len();
|
||||||
|
let mut retval = Vec::from_iter(*self);
|
||||||
|
retval.rotate_left(amount);
|
||||||
|
ArrayLiteral::new(
|
||||||
|
self.ty().element(),
|
||||||
|
retval.into_iter().map(Expr::canonical).collect(),
|
||||||
|
)
|
||||||
|
.to_expr()
|
||||||
|
}
|
||||||
|
/// like [`<[T]>::rotate_right`](slice::rotate_right)
|
||||||
|
fn rotate_right(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().len();
|
||||||
|
let mut retval = Vec::from_iter(*self);
|
||||||
|
retval.rotate_right(amount);
|
||||||
|
ArrayLiteral::new(
|
||||||
|
self.ty().element(),
|
||||||
|
retval.into_iter().map(Expr::canonical).collect(),
|
||||||
|
)
|
||||||
|
.to_expr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, N: Size> Rotate<usize> for SimValue<ArrayType<T, N>> {
|
||||||
|
type Output = Self;
|
||||||
|
/// like [`<[T]>::rotate_left`](slice::rotate_left)
|
||||||
|
fn rotate_left(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().len();
|
||||||
|
let mut retval = self.clone();
|
||||||
|
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_left(amount);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
/// like [`<[T]>::rotate_right`](slice::rotate_right)
|
||||||
|
fn rotate_right(&self, amount: usize) -> Self::Output {
|
||||||
|
if self.ty().len() == 0 {
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let amount = amount % self.ty().len();
|
||||||
|
let mut retval = self.clone();
|
||||||
|
AsMut::<[SimValue<T>]>::as_mut(&mut SimValue::value_mut(&mut retval)).rotate_right(amount);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
108351
crates/cpu/tests/expected/reg_alloc.vcd
generated
108351
crates/cpu/tests/expected/reg_alloc.vcd
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use cpu::{
|
use cpu::{
|
||||||
config::{CpuConfig, UnitConfig},
|
config::{CpuConfig, UnitConfig},
|
||||||
instruction::{AddSubMOp, LogicalMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
|
instruction::{AddSubMOp, LogicalMOp, Lut4, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
|
||||||
reg_alloc::{FetchedDecodedMOp, reg_alloc},
|
reg_alloc::{FetchedDecodedMOp, reg_alloc},
|
||||||
register::{FlagsMode, PRegFlagsPowerISA},
|
register::{FlagsMode, PRegFlagsPowerISA},
|
||||||
unit::{GlobalState, UnitKind},
|
unit::{GlobalState, UnitKind},
|
||||||
|
|
@ -101,7 +101,7 @@ fn test_reg_alloc() {
|
||||||
[2u8, 4u8],
|
[2u8, 4u8],
|
||||||
0.cast_to_static::<SInt<_>>(),
|
0.cast_to_static::<SInt<_>>(),
|
||||||
OutputIntegerMode.Full64(),
|
OutputIntegerMode.Full64(),
|
||||||
0b0110_hdl_u4,
|
Lut4::from_fn(|a, b| a ^ b),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
let insns = insns_init.into_iter().chain(insns_loop.into_iter().cycle());
|
let insns = insns_init.into_iter().chain(insns_loop.into_iter().cycle());
|
||||||
|
|
|
||||||
131637
crates/cpu/tests/simple_power_isa_decoder/expected/decode_one_insn.vcd
generated
Normal file
131637
crates/cpu/tests/simple_power_isa_decoder/expected/decode_one_insn.vcd
generated
Normal file
File diff suppressed because it is too large
Load diff
208
crates/cpu/tests/simple_power_isa_decoder/main.rs
Normal file
208
crates/cpu/tests/simple_power_isa_decoder/main.rs
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::TestCase;
|
||||||
|
use cpu::{
|
||||||
|
decoder::simple_power_isa::decode_one_insn, instruction::MOp, util::array_vec::ArrayVec,
|
||||||
|
};
|
||||||
|
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
|
||||||
|
use std::{fmt::Write as _, io::Write, process::Command};
|
||||||
|
|
||||||
|
mod test_cases;
|
||||||
|
|
||||||
|
#[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::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"));
|
||||||
|
let mut any_error = false;
|
||||||
|
macro_rules! assert_eq_cont {
|
||||||
|
($l:expr, $r:expr, $($msg:tt)+) => {
|
||||||
|
match (&$l, &$r) {
|
||||||
|
(l, r) => if l != r {
|
||||||
|
eprintln!("assertion failed: {}\nl={l:#?}\nr={r:#?}", format_args!($($msg)+));
|
||||||
|
any_error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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:?}");
|
||||||
|
};
|
||||||
|
if line.starts_with("\t.long") {
|
||||||
|
assert_eq!(
|
||||||
|
line,
|
||||||
|
format!("\t.long\t{first_input}"),
|
||||||
|
"test_case={test_case:?}\nline:\n{line}"
|
||||||
|
);
|
||||||
|
if let Some(second_input) = second_input {
|
||||||
|
let Some(line) = lines.next() else {
|
||||||
|
panic!("output missing line for: {test_case:?}");
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
line,
|
||||||
|
format!("\t.long\t{second_input}"),
|
||||||
|
"test_case={test_case:?}\nline:\n{line}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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_cont!(
|
||||||
|
comment,
|
||||||
|
expected_comment,
|
||||||
|
"test_case={test_case:?}\nline:\n{line}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for line in lines {
|
||||||
|
assert!(line.trim().is_empty(), "bad trailing output line: {line:?}");
|
||||||
|
}
|
||||||
|
if any_error {
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
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::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
|
||||||
|
}
|
||||||
158
crates/cpu/tests/simple_power_isa_decoder/test_cases.rs
Normal file
158
crates/cpu/tests/simple_power_isa_decoder/test_cases.rs
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use cpu::{instruction::MOp, util::array_vec::ArrayVec};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
mod branch;
|
||||||
|
mod condition_register;
|
||||||
|
mod fixed_point_arithmetic;
|
||||||
|
mod fixed_point_compare;
|
||||||
|
mod fixed_point_load;
|
||||||
|
mod fixed_point_logical;
|
||||||
|
mod fixed_point_rotate_and_shift;
|
||||||
|
mod fixed_point_store;
|
||||||
|
mod move_to_from_system_register;
|
||||||
|
mod prefixed_no_operation;
|
||||||
|
|
||||||
|
pub struct TestCase {
|
||||||
|
pub mnemonic: &'static str,
|
||||||
|
pub first_input: u32,
|
||||||
|
pub second_input: Option<u32>,
|
||||||
|
pub output: SimValue<ArrayVec<MOp, ConstUsize<3>>>,
|
||||||
|
pub 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn insn_empty(mnemonic: &'static str, first_input: u32, second_input: Option<u32>) -> TestCase {
|
||||||
|
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
|
||||||
|
.zero()
|
||||||
|
.cast_bits_to(MOp);
|
||||||
|
TestCase {
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop),
|
||||||
|
loc: std::panic::Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
loc: std::panic::Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn insn_double(
|
||||||
|
mnemonic: &'static str,
|
||||||
|
first_input: u32,
|
||||||
|
second_input: Option<u32>,
|
||||||
|
insns: [impl ToSimValue<Type = MOp>; 2],
|
||||||
|
) -> 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::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
|
||||||
|
TestCase {
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: single_storage,
|
||||||
|
loc: std::panic::Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn insn_triple(
|
||||||
|
mnemonic: &'static str,
|
||||||
|
first_input: u32,
|
||||||
|
second_input: Option<u32>,
|
||||||
|
insns: [impl ToSimValue<Type = MOp>; 3],
|
||||||
|
) -> 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::try_push_sim(&mut single_storage, &zero_mop).expect("known to have space");
|
||||||
|
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[0] = insns[0].to_sim_value();
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[1] = insns[1].to_sim_value();
|
||||||
|
ArrayVec::elements_sim_mut(&mut single_storage)[2] = insns[2].to_sim_value();
|
||||||
|
TestCase {
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
output: single_storage,
|
||||||
|
loc: std::panic::Location::caller(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_cases() -> Vec<TestCase> {
|
||||||
|
let mut retval = Vec::new();
|
||||||
|
branch::test_cases_book_i_2_4_branch(&mut retval);
|
||||||
|
condition_register::test_cases_book_i_2_5_condition_register(&mut retval);
|
||||||
|
fixed_point_load::test_cases_book_i_3_3_2_fixed_point_load(&mut retval);
|
||||||
|
fixed_point_store::test_cases_book_i_3_3_3_fixed_point_store(&mut retval);
|
||||||
|
fixed_point_arithmetic::test_cases_book_i_3_3_9_fixed_point_arithmetic(&mut retval);
|
||||||
|
fixed_point_compare::test_cases_book_i_3_3_10_fixed_point_compare(&mut retval);
|
||||||
|
fixed_point_logical::test_cases_book_i_3_3_13_fixed_point_logical(&mut retval);
|
||||||
|
fixed_point_rotate_and_shift::test_cases_book_i_3_3_14_fixed_point_rotate_and_shift(
|
||||||
|
&mut retval,
|
||||||
|
);
|
||||||
|
move_to_from_system_register::test_cases_book_i_3_3_19_move_to_from_system_register(
|
||||||
|
&mut retval,
|
||||||
|
);
|
||||||
|
prefixed_no_operation::test_cases_book_i_3_3_20_prefixed_no_operation(&mut retval);
|
||||||
|
move_to_from_system_register::test_cases_book_iii_5_4_4_move_to_from_system_register(
|
||||||
|
&mut retval,
|
||||||
|
);
|
||||||
|
retval
|
||||||
|
}
|
||||||
446
crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs
Normal file
446
crates/cpu/tests/simple_power_isa_decoder/test_cases/branch.rs
Normal file
|
|
@ -0,0 +1,446 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_double, insn_single};
|
||||||
|
use cpu::instruction::{
|
||||||
|
AddSubMOp, BranchMOp, ConditionMode, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode,
|
||||||
|
};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 2.4 Branch Instructions
|
||||||
|
pub fn test_cases_book_i_2_4_branch(retval: &mut Vec<TestCase>) {
|
||||||
|
retval.push(insn_single(
|
||||||
|
"b 0x345678",
|
||||||
|
0x48345678,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_i(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
0x345678.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"ba 0x345678",
|
||||||
|
0x4834567a,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_i(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
0x345678.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"bl 0x345678",
|
||||||
|
0x48345679,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
0x345678.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"bla 0x345678",
|
||||||
|
0x4834567b,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
0x345678.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
fn insn_dec_ctr_and(
|
||||||
|
mnemonic: &'static str,
|
||||||
|
first_input: u32,
|
||||||
|
second_input: Option<u32>,
|
||||||
|
second_insn: impl ToSimValue<Type = MOp>,
|
||||||
|
) -> TestCase {
|
||||||
|
insn_double(
|
||||||
|
mnemonic,
|
||||||
|
first_input,
|
||||||
|
second_input,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i::<MOp>(
|
||||||
|
MOpDestReg::new([MOpRegNum::power_isa_ctr_reg()], []),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
(-1).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.into_sim_value(),
|
||||||
|
second_insn.into_sim_value(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
macro_rules! insn_branch_conds {
|
||||||
|
(
|
||||||
|
mnemonic = $mnemonic:literal;
|
||||||
|
mnemonic_l = $mnemonic_l:literal;
|
||||||
|
asm_last_arg = $asm_last_arg:literal;
|
||||||
|
imm = $imm:literal;
|
||||||
|
encoding = $encoding:literal;
|
||||||
|
src1 = $src1:expr;
|
||||||
|
pc_relative = $pc_relative:expr;
|
||||||
|
is_ret = $is_ret:expr;
|
||||||
|
) => {
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = $mnemonic;
|
||||||
|
asm_last_arg = $asm_last_arg;
|
||||||
|
imm = $imm;
|
||||||
|
encoding = $encoding;
|
||||||
|
dest = MOpDestReg::new_sim(&[], &[]);
|
||||||
|
src1 = $src1;
|
||||||
|
pc_relative = $pc_relative;
|
||||||
|
lk = false;
|
||||||
|
is_ret = $is_ret;
|
||||||
|
}
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = $mnemonic_l;
|
||||||
|
asm_last_arg = $asm_last_arg;
|
||||||
|
imm = $imm;
|
||||||
|
encoding = $encoding | 1;
|
||||||
|
dest = MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]);
|
||||||
|
src1 = $src1;
|
||||||
|
pc_relative = $pc_relative;
|
||||||
|
lk = true;
|
||||||
|
is_ret = $is_ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
mnemonic = $mnemonic:literal;
|
||||||
|
asm_last_arg = $asm_last_arg:literal;
|
||||||
|
imm = $imm:literal;
|
||||||
|
encoding = $encoding:expr;
|
||||||
|
dest = $dest:expr;
|
||||||
|
src1 = $src1:expr;
|
||||||
|
pc_relative = $pc_relative:expr;
|
||||||
|
lk = $lk:expr;
|
||||||
|
is_ret = $is_ret:expr;
|
||||||
|
) => {
|
||||||
|
if !$mnemonic.starts_with("bcctr") {
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 0, 0, ", $asm_last_arg),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 0, 1, ", $asm_last_arg),
|
||||||
|
$encoding | 0x010000,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.SGt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 0, 2, ", $asm_last_arg),
|
||||||
|
$encoding | 0x020000,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.Eq(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 0, 3, ", $asm_last_arg),
|
||||||
|
$encoding | 0x030000,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.Overflow(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 0, 9, ", $asm_last_arg),
|
||||||
|
$encoding | 0x090000,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(2).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.SGt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 2, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (2 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
false,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " 4, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (4 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
if !$mnemonic.starts_with("bcctr") {
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 8, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (8 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 10, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (10 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
false,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " 12, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (12 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_cond_ctr(
|
||||||
|
$dest,
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
ConditionMode.SLt(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
if !$mnemonic.starts_with("bcctr") {
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 16, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (16 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_ctr(
|
||||||
|
$dest,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
true,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
concat!($mnemonic, " 18, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (18 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_ctr(
|
||||||
|
$dest,
|
||||||
|
$src1,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " 20, 0, ", $asm_last_arg),
|
||||||
|
$encoding | (20 << 21),
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_i(
|
||||||
|
$dest,
|
||||||
|
$src1,
|
||||||
|
$imm.cast_to_static::<SInt<_>>(),
|
||||||
|
$pc_relative,
|
||||||
|
$lk,
|
||||||
|
$is_ret,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = "bc";
|
||||||
|
mnemonic_l = "bcl";
|
||||||
|
asm_last_arg = "0x1234";
|
||||||
|
imm = 0x1234;
|
||||||
|
encoding = 0x40001234;
|
||||||
|
src1 = MOpRegNum::const_zero().value;
|
||||||
|
pc_relative = true;
|
||||||
|
is_ret = false;
|
||||||
|
}
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = "bca";
|
||||||
|
mnemonic_l = "bcla";
|
||||||
|
asm_last_arg = "0x1234";
|
||||||
|
imm = 0x1234;
|
||||||
|
encoding = 0x40001236;
|
||||||
|
src1 = MOpRegNum::const_zero().value;
|
||||||
|
pc_relative = false;
|
||||||
|
is_ret = false;
|
||||||
|
}
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = "bclr";
|
||||||
|
mnemonic_l = "bclrl";
|
||||||
|
asm_last_arg = "0";
|
||||||
|
imm = 0;
|
||||||
|
encoding = 0x4c000020;
|
||||||
|
src1 = MOpRegNum::power_isa_lr_reg().value;
|
||||||
|
pc_relative = false;
|
||||||
|
is_ret = true;
|
||||||
|
}
|
||||||
|
insn_branch_conds! {
|
||||||
|
mnemonic = "bcctr";
|
||||||
|
mnemonic_l = "bcctrl";
|
||||||
|
asm_last_arg = "0";
|
||||||
|
imm = 0;
|
||||||
|
encoding = 0x4c000420;
|
||||||
|
src1 = MOpRegNum::power_isa_ctr_reg().value;
|
||||||
|
pc_relative = false;
|
||||||
|
is_ret = false;
|
||||||
|
}
|
||||||
|
retval.push(insn_dec_ctr_and(
|
||||||
|
// LLVM doesn't support the bctar[l] instructions:
|
||||||
|
// https://github.com/llvm/llvm-project/issues/176864
|
||||||
|
".long 0x4e400461 # bctarl 18, 0, 0",
|
||||||
|
0x4e400461,
|
||||||
|
None,
|
||||||
|
BranchMOp::branch_ctr(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
|
||||||
|
MOpRegNum::power_isa_tar_reg().value,
|
||||||
|
MOpRegNum::power_isa_ctr_reg().value,
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_single};
|
||||||
|
use cpu::{
|
||||||
|
instruction::{LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp},
|
||||||
|
register::PRegFlagsPowerISA,
|
||||||
|
};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 2.5 Condition Register Instructions
|
||||||
|
pub fn test_cases_book_i_2_5_condition_register(retval: &mut Vec<TestCase>) {
|
||||||
|
macro_rules! cr_bit_logical_op {
|
||||||
|
(
|
||||||
|
$mnemonic:literal,
|
||||||
|
$encoding:literal,
|
||||||
|
$lut:expr
|
||||||
|
) => {{
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " 4*cr3+so, 4*cr1+gt, 4*cr5+lt"),
|
||||||
|
$encoding | 0x01e5a000,
|
||||||
|
None,
|
||||||
|
LogicalFlagsMOp::logical_flags(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(1).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(5).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(3).value,
|
||||||
|
],
|
||||||
|
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
|
||||||
|
let mut dest = src2.map(|v| Some(v.into()));
|
||||||
|
dest.so = Some((src0.cr_gt, src1.cr_lt).into());
|
||||||
|
dest
|
||||||
|
}),
|
||||||
|
$lut,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " lt, gt, eq"),
|
||||||
|
$encoding | 0x00011000,
|
||||||
|
None,
|
||||||
|
LogicalFlagsMOp::logical_flags(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
],
|
||||||
|
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
|
||||||
|
let mut dest = src2.map(|v| Some(v.into()));
|
||||||
|
dest.cr_lt = Some((src0.cr_gt, src1.cr_eq).into());
|
||||||
|
dest
|
||||||
|
}),
|
||||||
|
$lut,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " gt, gt, eq"),
|
||||||
|
$encoding | 0x00211000,
|
||||||
|
None,
|
||||||
|
LogicalFlagsMOp::logical_flags(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(0)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
MOpRegNum::power_isa_cr_reg_imm(0).value,
|
||||||
|
],
|
||||||
|
LogicalFlagsMOpImm::from_swizzle_fn::<PRegFlagsPowerISA>(|src0, src1, src2| {
|
||||||
|
let mut dest = src2.map(|v| Some(v.into()));
|
||||||
|
dest.cr_gt = Some((src0.cr_gt, src1.cr_eq).into());
|
||||||
|
dest
|
||||||
|
}),
|
||||||
|
$lut,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
cr_bit_logical_op!("crand", 0x4c000202, Lut4::from_fn(|a, b| a & b));
|
||||||
|
cr_bit_logical_op!("crnand", 0x4c0001c2, Lut4::from_fn(|a, b| !(a & b)));
|
||||||
|
cr_bit_logical_op!("cror", 0x4c000382, Lut4::from_fn(|a, b| a | b));
|
||||||
|
cr_bit_logical_op!("crxor", 0x4c000182, Lut4::from_fn(|a, b| a ^ b));
|
||||||
|
cr_bit_logical_op!("crnor", 0x4c000042, Lut4::from_fn(|a, b| !(a | b)));
|
||||||
|
cr_bit_logical_op!("creqv", 0x4c000242, Lut4::from_fn(|a, b| a == b));
|
||||||
|
cr_bit_logical_op!("crandc", 0x4c000102, Lut4::from_fn(|a, b| a & !b));
|
||||||
|
cr_bit_logical_op!("crorc", 0x4c000342, Lut4::from_fn(|a, b| a | !b));
|
||||||
|
macro_rules! mcrf {
|
||||||
|
($dest:literal, $src:literal; $encoding:literal) => {
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!("mcrf ", $dest, ", ", $src),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_cr_reg_imm($src).value],
|
||||||
|
0i8.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mcrf!(0, 0; 0x4c000000);
|
||||||
|
mcrf!(5, 7; 0x4e9c0000);
|
||||||
|
mcrf!(5, 0; 0x4e800000);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,408 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_single};
|
||||||
|
use cpu::instruction::{AddSubMOp, MOpDestReg, MOpRegNum, OutputIntegerMode};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.9 Fixed-Point Arithmetic Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_9_fixed_point_arithmetic(retval: &mut Vec<TestCase>) {
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x123456789i64.cast_to_static::<SInt<_>>(),
|
||||||
|
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<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x12340000.cast_to_static::<SInt<_>>(),
|
||||||
|
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<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
(-1i8).cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
(-1i8).cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_single};
|
||||||
|
use cpu::instruction::{CompareMOp, CompareMode, MOpDestReg, MOpRegNum, OutputIntegerMode};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.10 Fixed-Point Compare Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_10_fixed_point_compare(retval: &mut Vec<TestCase>) {
|
||||||
|
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_imm(4).value],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value],
|
||||||
|
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value],
|
||||||
|
0x1234.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value],
|
||||||
|
0x89ab.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
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_imm(4).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm(5).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
CompareMode.CmpEqB(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,525 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_double};
|
||||||
|
use cpu::instruction::{
|
||||||
|
AddSubMOp, LoadMOp, LoadStoreConversion, LoadStoreWidth, MOpDestReg, MOpRegNum,
|
||||||
|
OutputIntegerMode,
|
||||||
|
};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.2 Fixed-Point Load Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_2_fixed_point_load(retval: &mut Vec<TestCase>) {
|
||||||
|
macro_rules! load_prefixed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $disp:literal($ra:literal), $r:literal;
|
||||||
|
$prefix:literal, $suffix:literal;
|
||||||
|
$width:ident;
|
||||||
|
$conversion:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, "), ", $r),
|
||||||
|
$prefix,
|
||||||
|
Some($suffix),
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $r != 0 || $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
$r != 0,
|
||||||
|
),
|
||||||
|
LoadMOp::load(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_temp_reg().value],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.$conversion(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! load {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $disp:literal($ra:literal);
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
$conversion:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
LoadMOp::load(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_temp_reg().value],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.$conversion(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! load_update {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $disp:literal($ra:literal);
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
$conversion:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $dest, ", ", $disp, "(", $ra, ")"),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
LoadMOp::load(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm($ra).value],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.$conversion(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! load_indexed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $ra:literal, $rb:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
$conversion:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
LoadMOp::load(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_temp_reg().value],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.$conversion(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! load_update_indexed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $ra:literal, $rb:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
$conversion:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $dest, ", ", $ra, ", ", $rb),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
LoadMOp::load(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($dest)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm($ra).value],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.$conversion(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"lbz" 3, 0x1234(4);
|
||||||
|
0x88641234;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"lbz" 3, 0x1234(0);
|
||||||
|
0x88601234;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plbz" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0x88646789;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plbz" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0x88606789;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plbz" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0x88606789;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lbzx" 3, 4, 5;
|
||||||
|
0x7c6428ae;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lbzx" 3, 0, 5;
|
||||||
|
0x7c6028ae;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update! {
|
||||||
|
"lbzu" 3, 0x1234(4);
|
||||||
|
0x8c641234;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update_indexed! {
|
||||||
|
"lbzux" 3, 4, 5;
|
||||||
|
0x7c6428ee;
|
||||||
|
Width8Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"lhz" 3, 0x1234(4);
|
||||||
|
0xa0641234;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"lhz" 3, 0x1234(0);
|
||||||
|
0xa0601234;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plhz" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0xa0646789;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plhz" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0xa0606789;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plhz" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0xa0606789;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lhzx" 3, 4, 5;
|
||||||
|
0x7c642a2e;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lhzx" 3, 0, 5;
|
||||||
|
0x7c602a2e;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update! {
|
||||||
|
"lhzu" 3, 0x1234(4);
|
||||||
|
0xa4641234;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update_indexed! {
|
||||||
|
"lhzux" 3, 4, 5;
|
||||||
|
0x7c642a6e;
|
||||||
|
Width16Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"lha" 3, 0x1234(4);
|
||||||
|
0xa8641234;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"lha" 3, 0x1234(0);
|
||||||
|
0xa8601234;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plha" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0xa8646789;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plha" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0xa8606789;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plha" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0xa8606789;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lhax" 3, 4, 5;
|
||||||
|
0x7c642aae;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lhax" 3, 0, 5;
|
||||||
|
0x7c602aae;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_update! {
|
||||||
|
"lhau" 3, 0x1234(4);
|
||||||
|
0xac641234;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_update_indexed! {
|
||||||
|
"lhaux" 3, 4, 5;
|
||||||
|
0x7c642aee;
|
||||||
|
Width16Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"lwz" 3, 0x1234(4);
|
||||||
|
0x80641234;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"lwz" 3, 0x1234(0);
|
||||||
|
0x80601234;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwz" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0x80646789;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwz" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0x80606789;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwz" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0x80606789;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lwzx" 3, 4, 5;
|
||||||
|
0x7c64282e;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lwzx" 3, 0, 5;
|
||||||
|
0x7c60282e;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update! {
|
||||||
|
"lwzu" 3, 0x1234(4);
|
||||||
|
0x84641234;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update_indexed! {
|
||||||
|
"lwzux" 3, 4, 5;
|
||||||
|
0x7c64286e;
|
||||||
|
Width32Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"lwa" 3, 0x1234(4);
|
||||||
|
0xe8641236;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"lwa" 3, 0x1234(0);
|
||||||
|
0xe8601236;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwa" 3, 0x123456789(4), 0;
|
||||||
|
0x04012345, 0xa4646789;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwa" 3, 0x123456789(0), 0;
|
||||||
|
0x04012345, 0xa4606789;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"plwa" 3, 0x123456789(0), 1;
|
||||||
|
0x04112345, 0xa4606789;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lwax" 3, 4, 5;
|
||||||
|
0x7c642aaa;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"lwax" 3, 0, 5;
|
||||||
|
0x7c602aaa;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
// there is no `lwau`
|
||||||
|
load_update_indexed! {
|
||||||
|
"lwaux" 3, 4, 5;
|
||||||
|
0x7c642aea;
|
||||||
|
Width32Bit;
|
||||||
|
SignExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
load! {
|
||||||
|
"ld" 3, 0x1234(4);
|
||||||
|
0xe8641234;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load! {
|
||||||
|
"ld" 3, 0x1234(0);
|
||||||
|
0xe8601234;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"pld" 3, 0x123456789(4), 0;
|
||||||
|
0x04012345, 0xe4646789;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"pld" 3, 0x123456789(0), 0;
|
||||||
|
0x04012345, 0xe4606789;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_prefixed! {
|
||||||
|
"pld" 3, 0x123456789(0), 1;
|
||||||
|
0x04112345, 0xe4606789;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"ldx" 3, 4, 5;
|
||||||
|
0x7c64282a;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_indexed! {
|
||||||
|
"ldx" 3, 0, 5;
|
||||||
|
0x7c60282a;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update! {
|
||||||
|
"ldu" 3, 0x1234(4);
|
||||||
|
0xe8641235;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
load_update_indexed! {
|
||||||
|
"ldux" 3, 4, 5;
|
||||||
|
0x7c64286a;
|
||||||
|
Width64Bit;
|
||||||
|
ZeroExt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,273 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_empty, insn_single};
|
||||||
|
use cpu::instruction::{LogicalMOp, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, OutputIntegerMode};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.13 Fixed-Point Logical Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_13_fixed_point_logical(retval: &mut Vec<TestCase>) {
|
||||||
|
macro_rules! insn_logic_i {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $src:literal, $imm:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
|$a:ident, $b:ident| $lut_fn:expr;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!(
|
||||||
|
$mnemonic,
|
||||||
|
" ",
|
||||||
|
stringify!($dest),
|
||||||
|
", ",
|
||||||
|
stringify!($src),
|
||||||
|
", ",
|
||||||
|
stringify!($imm)
|
||||||
|
),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
LogicalMOp::logical_i(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
|
||||||
|
if $mnemonic.contains('.') {
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm($src).value],
|
||||||
|
(($imm as u32) << if $mnemonic.contains('s') { 16 } else { 0 })
|
||||||
|
.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
Lut4::from_fn(|$a, $b| $lut_fn),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"andi." 3, 4, 0x89ab;
|
||||||
|
0x708389ab;
|
||||||
|
|a, b| a & b;
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"andis." 3, 4, 0x89ab;
|
||||||
|
0x748389ab;
|
||||||
|
|a, b| a & b;
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"ori" 3, 4, 0x89ab;
|
||||||
|
0x608389ab;
|
||||||
|
|a, b| a | b;
|
||||||
|
}
|
||||||
|
// ensure nop decodes to zero instructions
|
||||||
|
retval.push(insn_empty("ori 0, 0, 0", 0x60000000, None));
|
||||||
|
insn_logic_i! {
|
||||||
|
"oris" 3, 4, 0x89ab;
|
||||||
|
0x648389ab;
|
||||||
|
|a, b| a | b;
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"xori" 3, 4, 0x89ab;
|
||||||
|
0x688389ab;
|
||||||
|
|a, b| a ^ b;
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"xori" 0, 0, 0; // ensure xnop actually decodes to a normal ALU instruction
|
||||||
|
0x68000000;
|
||||||
|
|a, b| a ^ b;
|
||||||
|
}
|
||||||
|
insn_logic_i! {
|
||||||
|
"xoris" 3, 4, 0x89ab;
|
||||||
|
0x6c8389ab;
|
||||||
|
|a, b| a ^ b;
|
||||||
|
}
|
||||||
|
macro_rules! insn_logic {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $src0:literal, $src1:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
|$a:ident, $b:ident| $lut_fn:expr;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!(
|
||||||
|
$mnemonic,
|
||||||
|
" ",
|
||||||
|
stringify!($dest),
|
||||||
|
", ",
|
||||||
|
stringify!($src0),
|
||||||
|
", ",
|
||||||
|
stringify!($src1)
|
||||||
|
),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
LogicalMOp::logical(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
|
||||||
|
if $mnemonic.contains('.') {
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($src0).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($src1).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
Lut4::from_fn(|$a, $b| $lut_fn),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"and" 3, 4, 5;
|
||||||
|
0x7c832838;
|
||||||
|
|a, b| a & b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"and." 3, 4, 5;
|
||||||
|
0x7c832839;
|
||||||
|
|a, b| a & b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"xor" 3, 4, 5;
|
||||||
|
0x7c832a78;
|
||||||
|
|a, b| a ^ b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"xor." 3, 4, 5;
|
||||||
|
0x7c832a79;
|
||||||
|
|a, b| a ^ b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"nand" 3, 4, 5;
|
||||||
|
0x7c832bb8;
|
||||||
|
|a, b| !(a & b);
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"nand." 3, 4, 5;
|
||||||
|
0x7c832bb9;
|
||||||
|
|a, b| !(a & b);
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"or" 3, 4, 5;
|
||||||
|
0x7c832b78;
|
||||||
|
|a, b| a | b;
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
"or 3, 4, 4", // mr 3, 4
|
||||||
|
0x7c832378,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm(4).value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
insn_logic! {
|
||||||
|
"or." 3, 4, 5;
|
||||||
|
0x7c832b79;
|
||||||
|
|a, b| a | b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"or." 3, 4, 4; // mr. 3, 4
|
||||||
|
0x7c832379;
|
||||||
|
|a, b| a | b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"orc" 3, 4, 5;
|
||||||
|
0x7c832b38;
|
||||||
|
|a, b| a | !b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"orc." 3, 4, 5;
|
||||||
|
0x7c832b39;
|
||||||
|
|a, b| a | !b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"nor" 3, 4, 5;
|
||||||
|
0x7c8328f8;
|
||||||
|
|a, b| !(a | b);
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"nor." 3, 4, 5;
|
||||||
|
0x7c8328f9;
|
||||||
|
|a, b| !(a | b);
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"eqv" 3, 4, 5;
|
||||||
|
0x7c832a38;
|
||||||
|
|a, b| a == b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"eqv." 3, 4, 5;
|
||||||
|
0x7c832a39;
|
||||||
|
|a, b| a == b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"andc" 3, 4, 5;
|
||||||
|
0x7c832878;
|
||||||
|
|a, b| a & !b;
|
||||||
|
}
|
||||||
|
insn_logic! {
|
||||||
|
"andc." 3, 4, 5;
|
||||||
|
0x7c832879;
|
||||||
|
|a, b| a & !b;
|
||||||
|
}
|
||||||
|
macro_rules! insn_exts {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $dest:literal, $src:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
$OutputIntegerMode:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_single(
|
||||||
|
concat!($mnemonic, " ", stringify!($dest), ", ", stringify!($src)),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
LogicalMOp::logical_i(
|
||||||
|
MOpDestReg::new_sim(
|
||||||
|
&[MOpRegNum::power_isa_gpr_reg_num($dest)],
|
||||||
|
if $mnemonic.contains('.') {
|
||||||
|
&[MOpRegNum::POWER_ISA_CR_0_REG_NUM]
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm($src).value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.$OutputIntegerMode(),
|
||||||
|
Lut4::from_fn(|a, b| a | b),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsb" 3, 4;
|
||||||
|
0x7c830774;
|
||||||
|
SignExt8;
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsb." 3, 4;
|
||||||
|
0x7c830775;
|
||||||
|
SignExt8;
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsh" 3, 4;
|
||||||
|
0x7c830734;
|
||||||
|
SignExt16;
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsh." 3, 4;
|
||||||
|
0x7c830735;
|
||||||
|
SignExt16;
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsw" 3, 4;
|
||||||
|
0x7c8307b4;
|
||||||
|
SignExt32;
|
||||||
|
}
|
||||||
|
insn_exts! {
|
||||||
|
"extsw." 3, 4;
|
||||||
|
0x7c8307b5;
|
||||||
|
SignExt32;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,512 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_double, insn_triple};
|
||||||
|
use cpu::instruction::{
|
||||||
|
AddSubMOp, LoadStoreConversion, LoadStoreWidth, MOpDestReg, MOpRegNum, MoveRegMOp,
|
||||||
|
OutputIntegerMode, StoreMOp,
|
||||||
|
};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.3 Fixed-Point Store Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_3_fixed_point_store(retval: &mut Vec<TestCase>) {
|
||||||
|
macro_rules! store_prefixed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $rs:literal, $disp:literal($ra:literal), $r:literal;
|
||||||
|
$prefix:literal, $suffix:literal;
|
||||||
|
$width:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, "), ", $r),
|
||||||
|
$prefix,
|
||||||
|
Some($suffix),
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $r != 0 || $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
$r != 0,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_temp_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! store {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_temp_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! store_update {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $rs:literal, $disp:literal($ra:literal);
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
) => {
|
||||||
|
if $ra == $rs {
|
||||||
|
retval.push(insn_triple(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_temp_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[MOpRegNum::power_isa_temp_reg().value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $disp, "(", $ra, ")"),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
],
|
||||||
|
($disp as i64).cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! store_indexed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
) => {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
if $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_temp_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! store_update_indexed {
|
||||||
|
(
|
||||||
|
$mnemonic:literal $rs:literal, $ra:literal, $rb:literal;
|
||||||
|
$encoding:literal;
|
||||||
|
$width:ident;
|
||||||
|
) => {
|
||||||
|
if $ra == $rs {
|
||||||
|
retval.push(insn_triple(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TEMP_REG_NUM], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_temp_reg().value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[MOpRegNum::power_isa_temp_reg().value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
retval.push(insn_double(
|
||||||
|
concat!($mnemonic, " ", $rs, ", ", $ra, ", ", $rb),
|
||||||
|
$encoding,
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddSubMOp::add_sub_i(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num($ra)], &[]),
|
||||||
|
[
|
||||||
|
if $ra == 0 {
|
||||||
|
MOpRegNum::const_zero().value
|
||||||
|
} else {
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value
|
||||||
|
},
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rb).value,
|
||||||
|
],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
OutputIntegerMode.Full64(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
StoreMOp::store(
|
||||||
|
MOpDestReg::new_sim(&[], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($ra).value,
|
||||||
|
MOpRegNum::power_isa_gpr_reg_imm($rs).value,
|
||||||
|
],
|
||||||
|
LoadStoreWidth.$width(),
|
||||||
|
LoadStoreConversion.ZeroExt(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
store! {
|
||||||
|
"stb" 3, 0x1234(4);
|
||||||
|
0x98641234;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store! {
|
||||||
|
"stb" 3, 0x1234(0);
|
||||||
|
0x98601234;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstb" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0x98646789;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstb" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0x98606789;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstb" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0x98606789;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stbx" 3, 4, 5;
|
||||||
|
0x7c6429ae;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stbx" 3, 0, 5;
|
||||||
|
0x7c6029ae;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stbu" 3, 0x1234(4);
|
||||||
|
0x9c641234;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stbu" 3, 0x1234(3);
|
||||||
|
0x9c631234;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stbux" 3, 4, 5;
|
||||||
|
0x7c6429ee;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stbux" 3, 3, 5;
|
||||||
|
0x7c6329ee;
|
||||||
|
Width8Bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
store! {
|
||||||
|
"sth" 3, 0x1234(4);
|
||||||
|
0xb0641234;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store! {
|
||||||
|
"sth" 3, 0x1234(0);
|
||||||
|
0xb0601234;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"psth" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0xb0646789;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"psth" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0xb0606789;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"psth" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0xb0606789;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"sthx" 3, 4, 5;
|
||||||
|
0x7c642b2e;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"sthx" 3, 0, 5;
|
||||||
|
0x7c602b2e;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"sthu" 3, 0x1234(4);
|
||||||
|
0xb4641234;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"sthu" 3, 0x1234(3);
|
||||||
|
0xb4631234;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"sthux" 3, 4, 5;
|
||||||
|
0x7c642b6e;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"sthux" 3, 3, 5;
|
||||||
|
0x7c632b6e;
|
||||||
|
Width16Bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
store! {
|
||||||
|
"stw" 3, 0x1234(4);
|
||||||
|
0x90641234;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store! {
|
||||||
|
"stw" 3, 0x1234(0);
|
||||||
|
0x90601234;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstw" 3, 0x123456789(4), 0;
|
||||||
|
0x06012345, 0x90646789;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstw" 3, 0x123456789(0), 0;
|
||||||
|
0x06012345, 0x90606789;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstw" 3, 0x123456789(0), 1;
|
||||||
|
0x06112345, 0x90606789;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stwx" 3, 4, 5;
|
||||||
|
0x7c64292e;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stwx" 3, 0, 5;
|
||||||
|
0x7c60292e;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stwu" 3, 0x1234(4);
|
||||||
|
0x94641234;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stwu" 3, 0x1234(3);
|
||||||
|
0x94631234;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stwux" 3, 4, 5;
|
||||||
|
0x7c64296e;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stwux" 3, 3, 5;
|
||||||
|
0x7c63296e;
|
||||||
|
Width32Bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
store! {
|
||||||
|
"std" 3, 0x1234(4);
|
||||||
|
0xf8641234;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store! {
|
||||||
|
"std" 3, 0x1234(0);
|
||||||
|
0xf8601234;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstd" 3, 0x123456789(4), 0;
|
||||||
|
0x04012345, 0xf4646789;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstd" 3, 0x123456789(0), 0;
|
||||||
|
0x04012345, 0xf4606789;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_prefixed! {
|
||||||
|
"pstd" 3, 0x123456789(0), 1;
|
||||||
|
0x04112345, 0xf4606789;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stdx" 3, 4, 5;
|
||||||
|
0x7c64292a;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_indexed! {
|
||||||
|
"stdx" 3, 0, 5;
|
||||||
|
0x7c60292a;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stdu" 3, 0x1234(4);
|
||||||
|
0xf8641235;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_update! {
|
||||||
|
"stdu" 3, 0x1234(3);
|
||||||
|
0xf8631235;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stdux" 3, 4, 5;
|
||||||
|
0x7c64296a;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
store_update_indexed! {
|
||||||
|
"stdux" 3, 3, 5;
|
||||||
|
0x7c63296a;
|
||||||
|
Width64Bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_single};
|
||||||
|
use cpu::instruction::{
|
||||||
|
LogicalFlagsMOp, LogicalFlagsMOpImm, Lut4, MOpDestReg, MOpRegNum, MoveRegMOp, ReadSpecialMOp,
|
||||||
|
ReadSpecialMOpImm,
|
||||||
|
};
|
||||||
|
use fayalite::prelude::*;
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.19 Move To/From System Register Instructions
|
||||||
|
pub fn test_cases_book_i_3_3_19_move_to_from_system_register(retval: &mut Vec<TestCase>) {
|
||||||
|
// mfspr/mtspr are covered by test_cases_book_iii_5_4_4_move_to_from_system_register
|
||||||
|
#[hdl]
|
||||||
|
fn mcrxrx_imm() -> SimValue<LogicalFlagsMOpImm> {
|
||||||
|
#[hdl(sim)]
|
||||||
|
LogicalFlagsMOpImm {
|
||||||
|
// if the order of flags in PRegFlags changes, this will need to be updated
|
||||||
|
src0_start: 4usize.cast_to(LogicalFlagsMOpImm.src0_start),
|
||||||
|
src1_start: 4usize.cast_to(LogicalFlagsMOpImm.src1_start),
|
||||||
|
src2_start: 4usize.cast_to(LogicalFlagsMOpImm.src2_start),
|
||||||
|
dest_start: 0usize.cast_to(LogicalFlagsMOpImm.dest_start),
|
||||||
|
dest_count: 6usize.cast_to(LogicalFlagsMOpImm.dest_count),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mcrxrx 3",
|
||||||
|
0x7d800480,
|
||||||
|
None,
|
||||||
|
LogicalFlagsMOp::logical_flags(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
|
||||||
|
[
|
||||||
|
MOpRegNum::power_isa_xer_ca_ca32_reg().value,
|
||||||
|
MOpRegNum::const_zero().value,
|
||||||
|
MOpRegNum::power_isa_xer_so_ov_ov32_reg().value,
|
||||||
|
],
|
||||||
|
mcrxrx_imm(),
|
||||||
|
Lut4::from_fn(|a, b| a | b),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book III 5.4.4 Move To/From System Register Instructions
|
||||||
|
pub fn test_cases_book_iii_5_4_4_move_to_from_system_register(retval: &mut Vec<TestCase>) {
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mflr 3",
|
||||||
|
0x7c6802a6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_lr_reg().value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mtlr 3",
|
||||||
|
0x7c6803a6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_LR_REG_NUM], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mfctr 3",
|
||||||
|
0x7c6902a6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_ctr_reg().value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mtctr 3",
|
||||||
|
0x7c6903a6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_CTR_REG_NUM], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mfspr 3, 815 # mftar 3",
|
||||||
|
0x7c6fcaa6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::power_isa_tar_reg().value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mtspr 815, 3 # mttar 3",
|
||||||
|
0x7c6fcba6,
|
||||||
|
None,
|
||||||
|
MoveRegMOp::move_reg(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_TAR_REG_NUM], &[]),
|
||||||
|
[MOpRegNum::power_isa_gpr_reg_imm(3).value],
|
||||||
|
0.cast_to_static::<SInt<_>>(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
// make sure we generate mfspr and not the phased-out mftb
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mfspr 3, 268 # mftb 3",
|
||||||
|
0x7c6c42a6,
|
||||||
|
None,
|
||||||
|
ReadSpecialMOp::read_special(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value; 0],
|
||||||
|
ReadSpecialMOpImm.PowerIsaTimeBase(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
// make sure we generate mfspr and not the phased-out mftb
|
||||||
|
retval.push(insn_single(
|
||||||
|
"mfspr 3, 269 # mftbu 3",
|
||||||
|
0x7c6d42a6,
|
||||||
|
None,
|
||||||
|
ReadSpecialMOp::read_special(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value; 0],
|
||||||
|
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
// phased-out mftb -- not actually generated by the assembler so we have to use .long
|
||||||
|
retval.push(insn_single(
|
||||||
|
".long 0x7c6c42e6 # mftb 3, 268",
|
||||||
|
0x7c6c42e6,
|
||||||
|
None,
|
||||||
|
ReadSpecialMOp::read_special(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value; 0],
|
||||||
|
ReadSpecialMOpImm.PowerIsaTimeBase(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
// phased-out mftb -- not actually generated by the assembler so we have to use .long
|
||||||
|
retval.push(insn_single(
|
||||||
|
".long 0x7c6d42e6 # mftb 3, 269",
|
||||||
|
0x7c6d42e6,
|
||||||
|
None,
|
||||||
|
ReadSpecialMOp::read_special(
|
||||||
|
MOpDestReg::new_sim(&[MOpRegNum::power_isa_gpr_reg_num(3)], &[]),
|
||||||
|
[MOpRegNum::const_zero().value; 0],
|
||||||
|
ReadSpecialMOpImm.PowerIsaTimeBaseU(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::test_cases::{TestCase, insn_empty};
|
||||||
|
|
||||||
|
/// covers instructions in PowerISA v3.1C Book I 3.3.20 Prefixed No-Operation Instruction
|
||||||
|
pub fn test_cases_book_i_3_3_20_prefixed_no_operation(retval: &mut Vec<TestCase>) {
|
||||||
|
// ensure pnop decodes to zero instructions
|
||||||
|
retval.push(insn_empty(
|
||||||
|
// LLVM doesn't support the pnop instruction:
|
||||||
|
// https://github.com/llvm/llvm-project/issues/176831
|
||||||
|
".long 0x07000000, 0 # pnop",
|
||||||
|
0x07000000,
|
||||||
|
Some(0),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
@ -46,10 +46,10 @@ function main()
|
||||||
*/LICENSE.md|*/Notices.txt)
|
*/LICENSE.md|*/Notices.txt)
|
||||||
# copyright file
|
# copyright file
|
||||||
;;
|
;;
|
||||||
/crates/cpu/tests/expected/*.vcd|/crates/cpu/tests/expected/*.txt)
|
/crates/cpu/tests*/expected/*.vcd|/crates/cpu/tests*/expected/*.txt)
|
||||||
# file that can't contain copyright header
|
# file that can't contain copyright header
|
||||||
;;
|
;;
|
||||||
/.forgejo/workflows/*.yml|*/.gitignore|*.toml)
|
/.forgejo/workflows/*.yml|*/.gitignore|*/.gitattributes|*.toml)
|
||||||
check_file "$file" "${POUND_HEADER[@]}"
|
check_file "$file" "${POUND_HEADER[@]}"
|
||||||
;;
|
;;
|
||||||
*.mermaid)
|
*.mermaid)
|
||||||
|
|
@ -71,4 +71,4 @@ function main()
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue