decode fixed-point compare instructions

This commit is contained in:
Jacob Lifshay 2026-01-18 16:37:00 -08:00
parent a4b052f5f3
commit 3a35a698e2
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
6 changed files with 3730 additions and 1532 deletions

View file

@ -1,7 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::instruction::{AddSubMOp, MOpDestReg, MOpRegNum, OutputIntegerMode};
use crate::instruction::{
AddSubMOp, CompareMOp, CompareMode, MOpDestReg, MOpRegNum, OutputIntegerMode,
};
use crate::powerisa_instructions_xml::{
InstructionBitFieldName, InstructionBitFieldsInner, TextLineItem,
};
@ -177,6 +179,8 @@ macro_rules! impl_fields {
}
impl_fields! {
#[name = "BF"]
struct FieldBF(FieldCrf);
#[name = "RA"]
struct FieldRA(FieldGpr);
#[name = "RB"]
@ -189,12 +193,16 @@ impl_fields! {
struct FieldSi0(SInt<18>);
#[name = "si1"]
struct FieldSi1(UInt<16>);
#[name = "UI"]
struct FieldUI(UInt<16>);
#[name = "d0"]
struct FieldAddPcISD0(SInt<10>);
#[name = "d1"]
struct FieldAddPcISD1(UInt<5>);
#[name = "d2"]
struct FieldAddPcISD2(UInt<1>);
#[name = "L"]
struct FieldL(Bool);
#[name = "OE"]
struct FieldOE(Bool);
#[name = "R"]
@ -203,11 +211,18 @@ impl_fields! {
struct FieldRc(Bool);
}
/// general-purpose register
#[hdl]
struct FieldGpr {
reg_num: UInt<5>,
}
/// condition register field -- a 4-bit field of the condition register
#[hdl]
struct FieldCrf {
reg_num: UInt<3>,
}
fn gpr(this: impl ToExpr<Type = FieldGpr>) -> Expr<MOpRegNum> {
MOpRegNum::power_isa_gpr_reg(this.to_expr().reg_num)
}
@ -216,6 +231,10 @@ fn gpr_or_zero(this: impl ToExpr<Type = FieldGpr>) -> Expr<MOpRegNum> {
MOpRegNum::power_isa_gpr_or_zero_reg(this.to_expr().reg_num)
}
fn crf(this: impl ToExpr<Type = FieldCrf>) -> Expr<MOpRegNum> {
MOpRegNum::power_isa_cr_reg(this.to_expr().reg_num)
}
impl DecodeState {
fn form(&self) -> &'static str {
let mut title_words = self
@ -839,6 +858,172 @@ impl DecodeState {
},
);
}
/// for `cmpi`
#[hdl]
fn decode_cmpi(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldL(l), FieldRA(ra), FieldSI(si))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
#[hdl]
let compare_mode = wire();
#[hdl]
if l {
connect(compare_mode, CompareMode.S64());
} else {
connect(compare_mode, CompareMode.S32());
}
connect(
this.output[0],
CompareMOp::compare_i(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value],
si.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
compare_mode,
),
);
});
}
/// for `cmp`
#[hdl]
fn decode_cmp(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldL(l), FieldRA(ra), FieldRB(rb))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
#[hdl]
let compare_mode = wire();
#[hdl]
if l {
connect(compare_mode, CompareMode.S64());
} else {
connect(compare_mode, CompareMode.S32());
}
connect(
this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value, gpr(rb).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
compare_mode,
),
);
});
}
/// for `cmpli`
#[hdl]
fn decode_cmpli(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldL(l), FieldRA(ra), FieldUI(ui))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
#[hdl]
let compare_mode = wire();
#[hdl]
if l {
connect(compare_mode, CompareMode.U64());
} else {
connect(compare_mode, CompareMode.U32());
}
connect(
this.output[0],
CompareMOp::compare_i(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value],
ui.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
compare_mode,
),
);
});
}
/// for `cmpl`
#[hdl]
fn decode_cmpl(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldL(l), FieldRA(ra), FieldRB(rb))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
#[hdl]
let compare_mode = wire();
#[hdl]
if l {
connect(compare_mode, CompareMode.U64());
} else {
connect(compare_mode, CompareMode.U32());
}
connect(
this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value, gpr(rb).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
compare_mode,
),
);
});
}
/// for `cmprb`
#[hdl]
fn decode_cmprb(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldL(l), FieldRA(ra), FieldRB(rb))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
#[hdl]
let compare_mode = wire();
#[hdl]
if l {
connect(compare_mode, CompareMode.CmpRBTwo());
} else {
connect(compare_mode, CompareMode.CmpRBOne());
}
connect(
this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value, gpr(rb).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
compare_mode,
),
);
});
}
/// for `cmpeqb`
#[hdl]
fn decode_cmpeqb(&mut self) {
self.decode_scope(|this, (FieldBF(bf), FieldRA(ra), FieldRB(rb))| {
// TODO: handle SO propagation
connect(
ArrayVec::len(this.output),
1usize.cast_to_static::<Length<_>>(),
);
connect(
this.output[0],
CompareMOp::compare(
MOpDestReg::new([crf(bf)], []),
[gpr(ra).value, gpr(rb).value],
0.cast_to_static::<SInt<_>>(),
OutputIntegerMode.Full64(),
CompareMode.CmpEqB(),
),
);
});
}
}
type DecodeFn = fn(&mut DecodeState);
@ -982,12 +1167,12 @@ const DECODE_FNS: &[(&[&str], DecodeFn)] = &[
// TODO
},
),
(&["cmpi", "cmp", "cmpli", "cmpl"], |_state| {
// TODO
}),
(&["cmprb", "cmpeqb"], |_state| {
// TODO
}),
(&["cmpi"], DecodeState::decode_cmpi),
(&["cmp"], DecodeState::decode_cmp),
(&["cmpli"], DecodeState::decode_cmpli),
(&["cmpl"], DecodeState::decode_cmpl),
(&["cmprb"], DecodeState::decode_cmprb),
(&["cmpeqb"], DecodeState::decode_cmpeqb),
(&["twi", "tw", "tdi", "td"], |_state| {
// TODO
}),

View file

@ -736,6 +736,120 @@ impl<DestReg: Type, SrcRegWidth: Size> LogicalMOp<DestReg, SrcRegWidth> {
}
}
#[hdl]
pub enum CompareMode {
U64,
S64,
U32,
S32,
U16,
S16,
U8,
S8,
/// compare one ranged byte -- like the PowerISA `cmprb _, 0, ..` instruction
CmpRBOne,
/// compare two ranged bytes -- like the PowerISA `cmprb _, 1, ..` instruction
CmpRBTwo,
/// like the PowerISA `cmpeqb` instruction
CmpEqB,
}
impl HdlPartialEqImpl<Self> for CompareMode {
#[track_caller]
fn cmp_value_eq(
lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
rhs_value: Cow<'_, Self::SimValue>,
) -> bool {
SimValue::opaque(&SimValue::from_value(lhs, lhs_value.into_owned()))
== SimValue::opaque(&SimValue::from_value(rhs, rhs_value.into_owned()))
}
#[track_caller]
fn cmp_sim_value_eq(
lhs: Cow<'_, SimValue<Self>>,
rhs: Cow<'_, SimValue<Self>>,
) -> SimValue<Bool> {
(SimValue::opaque(&lhs) == SimValue::opaque(&rhs)).to_sim_value()
}
#[track_caller]
fn cmp_sim_value_ne(
lhs: Cow<'_, SimValue<Self>>,
rhs: Cow<'_, SimValue<Self>>,
) -> SimValue<Bool> {
(SimValue::opaque(&lhs) != SimValue::opaque(&rhs)).to_sim_value()
}
#[track_caller]
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
}
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> CompareMOp<NewDestReg, NewSrcRegWidth, SrcCount>)]
#[hdl(cmp_eq)]
pub struct CompareMOp<DestReg: Type, SrcRegWidth: Size, SrcCount: KnownSize> {
#[common]
pub alu_common: AluCommonMOp<DestReg, SrcRegWidth, SrcCount>,
pub compare_mode: CompareMode,
}
}
impl<DestReg: Type, SrcRegWidth: Size> CompareMOp<DestReg, SrcRegWidth, ConstUsize<2>> {
#[hdl]
pub fn compare<Target: MOpTrait>(
dest: impl ToExpr<Type = DestReg>,
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 2>>,
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_2_IMM_WIDTH }>>,
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
compare_mode: impl ToExpr<Type = CompareMode>,
) -> Expr<Target>
where
Self: MOpInto<Target>,
{
MOpInto::mop_into(
#[hdl]
CompareMOp {
alu_common: #[hdl]
AluCommonMOp {
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
output_integer_mode,
},
compare_mode,
},
)
}
}
impl<DestReg: Type, SrcRegWidth: Size> CompareMOp<DestReg, SrcRegWidth, ConstUsize<1>> {
#[hdl]
pub fn compare_i<Target: MOpTrait>(
dest: impl ToExpr<Type = DestReg>,
src: impl ToExpr<Type = Array<UIntType<SrcRegWidth>, 1>>,
imm: impl ToExpr<Type = SInt<{ COMMON_MOP_1_IMM_WIDTH }>>,
output_integer_mode: impl ToExpr<Type = OutputIntegerMode>,
compare_mode: impl ToExpr<Type = CompareMode>,
) -> Expr<Target>
where
Self: MOpInto<Target>,
{
MOpInto::mop_into(
#[hdl]
CompareMOp {
alu_common: #[hdl]
AluCommonMOp {
common: CommonMOp::new(0_hdl_u0, dest, src, Expr::as_dyn_int(imm.to_expr())),
output_integer_mode,
},
compare_mode,
},
)
}
}
common_mop_struct! {
#[mapped(<NewDestReg, NewSrcRegWidth> BranchMOp<NewDestReg, NewSrcRegWidth>)]
#[hdl(cmp_eq)]
@ -753,6 +867,8 @@ mop_enum! {
AddSub(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<3>>),
AddSubI(AddSubMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
Logical(LogicalMOp<DestReg, SrcRegWidth>),
Compare(CompareMOp<DestReg, SrcRegWidth, ConstUsize<2>>),
CompareI(CompareMOp<DestReg, SrcRegWidth, ConstUsize<1>>),
}
}

View file

@ -94,7 +94,7 @@ impl MOpRegNum {
} else {
connect_any(
power_isa_cr_reg.value,
Self::POWER_ISA_CR_1_THRU_7_REG_NUMS.start + field_num,
Self::POWER_ISA_CR_1_THRU_7_REG_NUMS.start - 1 + field_num,
);
}
power_isa_cr_reg

View file

@ -4,8 +4,8 @@
use crate::{
config::CpuConfig,
instruction::{
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, LogicalMOp, MOpTrait,
OutputIntegerMode, RenamedMOp, UnitOutRegNum,
AddSubMOp, AluBranchMOp, AluCommonMOp, COMMON_MOP_SRC_LEN, CommonMOp, CompareMOp,
LogicalMOp, MOpTrait, OutputIntegerMode, RenamedMOp, UnitOutRegNum,
},
register::{FlagsMode, PRegFlagsPowerISA, PRegFlagsX86, PRegValue},
unit::{
@ -244,6 +244,20 @@ fn logical(
}
}
#[hdl]
fn compare<SrcCount: KnownSize>(
mop: Expr<CompareMOp<UnitOutRegNum<DynSize>, DynSize, SrcCount>>,
flags_mode: Expr<FlagsMode>,
src_values: Expr<Array<PRegValue, { COMMON_MOP_SRC_LEN }>>,
) -> Expr<UnitResultCompleted<()>> {
// TODO: finish
#[hdl]
UnitResultCompleted::<_> {
value: PRegValue::zeroed(),
extra_out: (),
}
}
#[hdl_module]
pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
#[hdl]
@ -336,6 +350,40 @@ pub fn alu_branch(config: &CpuConfig, unit_index: usize) {
},
),
),
AluBranchMOp::<_, _>::Compare(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
AluBranchMOp::<_, _>::CompareI(mop) => connect(
unit_base.execute_end,
HdlSome(
#[hdl]
ExecuteEnd::<_, _> {
unit_output: #[hdl]
UnitOutput::<_, _> {
which: MOpTrait::dest_reg(mop),
result: UnitResult[()].Completed(compare(
mop,
global_state.flags_mode,
src_values,
)),
},
},
),
),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,9 @@
use cpu::{
decoder::simple_power_isa::decode_one_insn,
instruction::{AddSubMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode},
instruction::{
AddSubMOp, CompareMOp, CompareMode, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode,
},
util::array_vec::ArrayVec,
};
use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter};
@ -486,6 +488,181 @@ fn test_cases() -> Vec<TestCase> {
false,
),
));
retval.push(insn_single(
"cmpi 3, 0, 4, 0x1234",
0x2d841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
0x1234.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::S32(),
),
));
retval.push(insn_single(
"cmpi 3, 1, 4, -0x7655",
0x2da489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
(0x89abu16 as i16).cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::S64(),
),
));
retval.push(insn_single(
"cmp 3, 0, 4, 5",
0x7d842800,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::S32(),
),
));
retval.push(insn_single(
"cmp 3, 1, 4, 5",
0x7da42800,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::S64(),
),
));
retval.push(insn_single(
"cmpli 3, 0, 4, 0x1234",
0x29841234,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
0x1234.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::U32(),
),
));
retval.push(insn_single(
"cmpli 3, 1, 4, 0x89ab",
0x29a489ab,
None,
CompareMOp::compare_i(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value],
0x89ab.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::U64(),
),
));
retval.push(insn_single(
"cmpl 3, 0, 4, 5",
0x7d842840,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::U32(),
),
));
retval.push(insn_single(
"cmpl 3, 1, 4, 5",
0x7da42840,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::U64(),
),
));
retval.push(insn_single(
"cmprb 3, 0, 4, 5",
0x7d842980,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::CmpRBOne(),
),
));
retval.push(insn_single(
"cmprb 3, 1, 4, 5",
0x7da42980,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::CmpRBTwo(),
),
));
retval.push(insn_single(
"cmpeqb 3, 4, 5",
0x7d8429c0,
None,
CompareMOp::compare(
MOpDestReg::new_sim(&[MOpRegNum::power_isa_cr_reg_num(3)], &[]),
[
MOpRegNum::power_isa_gpr_reg(4_hdl_u5).value,
MOpRegNum::power_isa_gpr_reg(5_hdl_u5).value,
],
0.cast_to_static::<SInt<_>>(),
#[hdl(sim)]
OutputIntegerMode::Full64(),
#[hdl(sim)]
CompareMode::CmpEqB(),
),
));
retval
}