From b7b6a02777c573fe761f5f678b7c2df9db76a240 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 14 Jan 2026 21:41:25 -0800 Subject: [PATCH] decodes an addi instruction --- crates/cpu/src/decoder/simple_power_isa.rs | 1185 +++++++++++------ .../tests/expected/decode_one_32bit_insn.vcd | 0 crates/cpu/tests/expected/decode_one_insn.vcd | 667 ++++++++++ crates/cpu/tests/simple_power_isa_decoder.rs | 107 +- 4 files changed, 1560 insertions(+), 399 deletions(-) delete mode 100644 crates/cpu/tests/expected/decode_one_32bit_insn.vcd create mode 100644 crates/cpu/tests/expected/decode_one_insn.vcd diff --git a/crates/cpu/src/decoder/simple_power_isa.rs b/crates/cpu/src/decoder/simple_power_isa.rs index 1b16fc0..c4f3761 100644 --- a/crates/cpu/src/decoder/simple_power_isa.rs +++ b/crates/cpu/src/decoder/simple_power_isa.rs @@ -1,402 +1,829 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use crate::instruction::{AddSubMOp, MOpDestReg, MOpRegNum, OutputIntegerMode}; +use crate::powerisa_instructions_xml::{ + InstructionBitFieldName, InstructionBitFieldsInner, TextLineItem, +}; +use crate::util::array_vec::Length; use crate::{ config::CpuConfig, instruction::MOp, powerisa_instructions_xml::Instructions, util::array_vec::ArrayVec, }; +use fayalite::module::wire_with_loc; use fayalite::prelude::*; +use fayalite::ty::StaticType; +use std::collections::BTreeMap; +use std::ops::RangeInclusive; + +#[rustfmt::skip] +const FP_MNEMONICS: &[&str] = &[ + "lfs", "plfs", "lfsx", "lfsu", "lfsux", "lfd", "plfd", "lfdx", "lfdu", "lfdux", "lfiwax", "lfiwzx", + "stfs", "pstfs", "stfsx", "stfsu", "stfsux", "stfd", "pstfd", "stfdx", "stfdu", "stfdux", "stfiwx", + "lfdp", "lfdpx", "stfdp", "stfdpx", "fmr", "fmr.", "fneg", "fneg.", "fabs", "fabs.", "fnabs", "fnabs.", + "fcpsgn", "fcpsgn.", "fmrgew", "fmrgow", "fadd", "fadd.", "fadds", "fadds.", "fsub", "fsub.", "fsubs", + "fsubs.", "fmul", "fmul.", "fmuls", "fmuls.", "fdiv", "fdiv.", "fdivs", "fdivs.", "fsqrt", "fsqrt.", + "fsqrts", "fsqrts.", "fre", "fre.", "fres", "fres.", "frsqrte", "frsqrte.", "frsqrtes", "frsqrtes.", + "ftdiv", "ftsqrt", "fmadd", "fmadd.", "fmadds", "fmadds.", "fmsub", "fmsub.", "fmsubs", "fmsubs.", + "fnmadd", "fnmadd.", "fnmadds", "fnmadds.", "fnmsub", "fnmsub.", "fnmsubs", "fnmsubs.", "frsp", "frsp.", + "fctid", "fctid.", "fctidz", "fctidz.", "fctidu", "fctidu.", "fctiduz", "fctiduz.", "fctiw", "fctiw.", + "fctiwz", "fctiwz.", "fctiwu", "fctiwu.", "fctiwuz", "fctiwuz.", "fcfid", "fcfid.", "fcfidu", "fcfidu.", + "fcfids", "fcfids.", "fcfidus", "fcfidus.", "frin", "frin.", "friz", "friz.", "frip", "frip.", "frim", + "frim.", "fcmpu", "fcmpo", "fsel", "fsel.", "mffs", "mffs.", "mffsce", "mffscdrn", "mffscdrni", + "mffscrn", "mffscrni", "mffsl", "mcrfs", "mtfsfi", "mtfsfi.", "mtfsf", "mtfsf.", "mtfsb0", "mtfsb0.", + "mtfsb1", "mtfsb1.", +]; + +#[rustfmt::skip] +const DFP_MNEMONICS: &[&str] = &[ + "dadd", "dadd.", "daddq", "daddq.", "dsub", "dsub.", "dsubq", "dsubq.", "dmul", "dmul.", "dmulq", + "dmulq.", "ddiv", "ddiv.", "ddivq", "ddivq.", "dcmpu", "dcmpuq", "dcmpo", "dcmpoq", "dtstdc", "dtstdcq", + "dtstdg", "dtstdgq", "dtstex", "dtstexq", "dtstsf", "dtstsfq", "dtstsfi", "dtstsfiq", "dquai", "dquai.", + "dquaiq", "dquaiq.", "dqua", "dqua.", "dquaq", "dquaq.", "drrnd", "drrnd.", "drrndq", "drrndq.", + "drintx", "drintx.", "drintxq", "drintxq.", "drintn", "drintn.", "drintnq", "drintnq.", "dctdp", + "dctdp.", "dctqpq", "dctqpq.", "drsp", "drsp.", "drdpq", "drdpq.", "dcffix", "dcffix.", "dcffixq", + "dcffixq.", "dcffixqq", "dctfix", "dctfix.", "dctfixq", "dctfixq.", "dctfixqq", "ddedpd", "ddedpd.", + "ddedpdq", "ddedpdq.", "denbcd", "denbcd.", "denbcdq", "denbcdq.", "dxex", "dxex.", "dxexq", "dxexq.", + "diex", "diex.", "diexq", "diexq.", "dscli", "dscli.", "dscliq", "dscliq.", "dscri", "dscri.", "dscriq", + "dscriq.", +]; + +#[rustfmt::skip] +const VMX_MNEMONICS: &[&str] = &[ + "lvebx", "lvehx", "lvewx", "lvx", "lvxl", "stvebx", "stvehx", "stvewx", "stvx", "stvxl", "lvsl", "lvsr", + "vpkpx", "vpkuhum", "vpkuwum", "vpkudum", "vupkhsb", "vupklsb", "vupkhsh", "vupklsh", "vupkhsw", + "vupklsw", "vupkhpx", "vupklpx", "vmrghb", "vmrglb", "vmrghh", "vmrglh", "vmrghw", "vmrglw", "vmrgew", + "vmrgow", "vspltb", "vsplth", "vspltw", "vspltisb", "vspltish", "vspltisw", "vperm", "vpermr", "vsel", + "vsldbi", "vsldoi", "vsrdbi", "vsl", "vsr", "vslo", "vsro", "vslv", "vsrv", "vextractub", "vextractuh", + "vextractuw", "vextractd", "vextublx", "vextubrx", "vextuhlx", "vextuhrx", "vextuwlx", "vextuwrx", + "vextdubvlx", "vextdubvrx", "vextduhvlx", "vextduhvrx", "vextduwvlx", "vextduwvrx", "vextddvlx", + "vextddvrx", "vinsertb", "vinserth", "vinsertw", "vinsertd", "vinsblx", "vinsbrx", "vinshlx", "vinshrx", + "vinswlx", "vinswrx", "vinsdlx", "vinsdrx", "vinsw", "vinsd", "vinsbvlx", "vinsbvrx", "vinshvlx", + "vinshvrx", "vinswvlx", "vinswvrx", "vaddcuw", "vaddubm", "vadduhm", "vadduwm", "vaddudm", "vadduqm", + "vaddeuqm", "vaddcuq", "vaddecuq", "vsubcuw", "vsubsbs", "vsububm", "vsubuhm", "vsubuwm", "vsubudm", + "vsububs", "vsubuhs", "vsubuws", "vsubuqm", "vsubeuqm", "vsubcuq", "vsubecuq", "vmulesb", "vmulosb", + "vmuleub", "vmuloub", "vmulesh", "vmulosh", "vmuleuh", "vmulouh", "vmulesw", "vmulosw", "vmuleuw", + "vmulouw", "vmuleud", "vmuloud", "vmulesd", "vmulosd", "vmuluwm", "vmulhsw", "vmulhuw", "vmulhsd", + "vmulhud", "vmulld", "vmladduhm", "vmsumubm", "vmsummbm", "vmsumshm", "vmsumuhm", "vmsumudm", "vmsumcud", + "vdivsw", "vdivuw", "vdivesw", "vdiveuw", "vdivsd", "vdivud", "vdivesd", "vdiveud", "vdivsq", "vdivuq", + "vdivesq", "vdiveuq", "vmodsw", "vmoduw", "vmodsd", "vmodud", "vmodsq", "vmoduq", "vnegw", "vnegd", + "vextsb2w", "vextsh2w", "vextsb2d", "vextsh2d", "vextsw2d", "vextsd2q", "vavgsb", "vavgub", "vavgsh", + "vavguh", "vavgsw", "vavguw", "vabsdub", "vabsduh", "vabsduw", "vmaxsb", "vmaxub", "vmaxsh", "vmaxuh", + "vmaxsw", "vmaxuw", "vmaxsd", "vmaxud", "vminsb", "vminub", "vminsh", "vminuh", "vminsw", "vminuw", + "vminsd", "vminud", "vcmpequb", "vcmpequb.", "vcmpequh", "vcmpequh.", "vcmpequw", "vcmpequw.", + "vcmpequd", "vcmpequd.", "vcmpequq", "vcmpequq.", "vcmpgtsb", "vcmpgtsb.", "vcmpgtub", "vcmpgtub.", + "vcmpgtsh", "vcmpgtsh.", "vcmpgtuh", "vcmpgtuh.", "vcmpgtsw", "vcmpgtsw.", "vcmpgtuw", "vcmpgtuw.", + "vcmpgtsd", "vcmpgtsd.", "vcmpgtud", "vcmpgtud.", "vcmpgtsq", "vcmpgtsq.", "vcmpgtuq", "vcmpgtuq.", + "vcmpneb", "vcmpneb.", "vcmpnezb", "vcmpnezb.", "vcmpneh", "vcmpneh.", "vcmpnezh", "vcmpnezh.", + "vcmpnew", "vcmpnew.", "vcmpnezw", "vcmpnezw.", "vcmpsq", "vcmpuq", "vand", "vandc", "veqv", "vnand", + "vor", "vorc", "vnor", "vxor", "vrlb", "vrlh", "vrlw", "vrld", "vrlq", "vrlwnm", "vrldnm", "vrlqnm", + "vrlwmi", "vrldmi", "vrlqmi", "vslb", "vslh", "vslw", "vsld", "vslq", "vsrb", "vsrh", "vsrw", "vsrd", + "vsrq", "vsrab", "vsrah", "vsraw", "vsrad", "vsraq", "vaddfp", "vsubfp", "vmaddfp", "vnmsubfp", "vmaxfp", + "vminfp", "vcfsx", "vcfux", "vrfim", "vrfin", "vrfip", "vrfiz", "vcmpeqfp", "vcmpeqfp.", "vcmpgefp", + "vcmpgefp.", "vcmpgtfp", "vcmpgtfp.", "vexptefp", "vrefp", "vrsqrtefp", "vcipher", "vcipherlast", + "vncipher", "vncipherlast", "vsbox", "vpmsumb", "vpmsumh", "vpmsumw", "vpmsumd", "vpermxor", "vgnb", + "vclzb", "vclzh", "vclzw", "vclzd", "vclzdm", "vctzb", "vctzh", "vctzw", "vctzd", "vctzdm", "vclzlsbb", + "vctzlsbb", "vpdepd", "vpextd", "vcfuged", "vpopcntb", "vpopcnth", "vpopcntw", "vpopcntd", "vprtybw", + "vprtybd", "vprtybq", "vbpermd", "vbpermq", "mtvsrbm", "mtvsrhm", "mtvsrwm", "mtvsrdm", "mtvsrqm", + "mtvsrbmi", "vexpandbm", "vexpandhm", "vexpandwm", "vexpanddm", "vexpandqm", "vcntmbb", "vcntmbh", + "vcntmbw", "vcntmbd", "vextractbm", "vextracthm", "vextractwm", "vextractdm", "vextractqm", "vstribr", + "vstribr.", "vstribl", "vstribl.", "vstrihr", "vstrihr.", "vstrihl", "vstrihl.", "vclrlb", "vclrrb", + "bcdadd.", "bcdsub.", "bcdcfz.", "vmul10uq", "vmul10cuq", "vmul10euq", "vmul10ecuq", "bcdcpsgn.", + "bcdsetsgn.", "mtvscr", "mfvscr", +]; + +/// note this list only contains the instructions that are in +/// powerisa-instructions.xml, this is not the complete list of VSX instructions +#[rustfmt::skip] +const VSX_MNEMONICS: &[&str] = &[ + "lxsdx", "lxsibzx", "lxsihzx", "lxsiwax", "lxsiwzx", "lxsspx", "stxsdx", "stxsibx", "stxsihx", "stxsiwx", + "stxsspx", "lxvb16x", "lxvh8x", "lxvx", "lxvdsx", "lxvwsx", "lxvrbx", "lxvrdx", "lxvrhx", "lxvrwx", + "lxvll", "stxvb16x", "stxvd2x", "stxvh8x", "stxvw4x", "stxvx", "stxvrbx", "stxvrdx", "stxvrhx", "stxvrwx", + "stxvll", "lxvp", "plxvp", "xsabsdp", "xsabsqp", "xscpsgndp", "xscpsgnqp", "xsnabsdp", "xsnabsqp", + "xsnegdp", "xsnegqp", "xvabsdp", "xvabssp", "xvcpsgndp", "xvcpsgnsp", "xvnabsdp", "xvnabssp", "xvnegdp", + "xvnegsp", "xsaddqp", "xsaddqpo", "xsaddsp", "xsdivsp", "xsmulqp", "xsmulqpo", "xsmulsp", "xssubdp", + "xssubqp", "xssubqpo", "xssubsp", "xsmaddadp", "xsmaddmdp", "xsmaddasp", "xsmaddmsp", "xsmaddqp", + "xsmaddqpo", "xsmsubasp", "xsmsubmsp", "xsmsubqp", "xsmsubqpo", "xsnmaddadp", "xsnmaddmdp", "xsnmaddasp", + "xsnmaddmsp", "xsnmaddqp", "xsnmaddqpo", "xsnmsubasp", "xsnmsubmsp", "xsnmsubqp", "xsnmsubqpo", + "xstsqrtdp", "xvmaddadp", "xvmaddmdp", "xvmaddasp", "xvmaddmsp", "xvmsubadp", "xvmsubmdp", "xvmsubasp", + "xvmsubmsp", "xvnmaddadp", "xvnmaddmdp", "xvnmaddasp", "xvnmaddmsp", "xvnmsubadp", "xvnmsubmdp", + "xvnmsubasp", "xvnmsubmsp", "xvtsqrtdp", "xvtsqrtsp", "xsmincqp", "xscvdpspn", "xscvdpqp", "xscvspdpn", + "xvcvbf16spn", "xvcvspdp", "xvrdpim", "xvrdpip", "xvrspim", "xvrspip", "xscvqpsqz", "xscvqpuqz", + "xscvqpuwz", "xscvsdqp", "xscvudqp", "xscvsqqp", "xscvuqqp", "xscvsxddp", "xscvuxddp", "xscvsxdsp", + "xscvuxdsp", "xvcvsxddp", "xvcvuxddp", "xvcvsxwdp", "xvcvuxwdp", "xvcvsxdsp", "xvcvuxdsp", "xvcvsxwsp", + "xvcvuxwsp", "xsxexpdp", "xsxexpqp", "xsxsigdp", "xsxsigqp", "xviexpdp", "xviexpsp", "xvxexpdp", + "xvxexpsp", "xvxsigdp", "xvxsigsp", "xxmfacc", "xxmtacc", "xxsetaccz", "xvi16ger2", "pmxvi16ger2", + "xvi16ger2s", "pmxvi16ger2s", "xvi4ger8", "pmxvi4ger8", "xvi8ger4", "pmxvi8ger4", "pmxvbf16ger2np", + "pmxvf16ger2np", "pmxvf32gernp", "xxland", "xxlandc", "xxleqv", "xxlnand", "xxlnor", "xxlor", "xxlorc", + "xxlxor", "xxsel", "xxeval", "xxblendvb", "xxblendvd", "xxblendvh", "xxblendvw", "xxbrh", "xxbrq", + "xxbrw", "xxextractuw", "xxinsertw", "xxmrghw", "xxmrglw", "xxsplti32dx", "xxspltib", "xxspltidp", + "xxspltiw", "xxspltw", "xxperm", "xxpermr", "xxsldwi", "xxgenpcvdm", "xxgenpcvwm", "lxvkq", "xvtlsbb", +]; + +struct DecodeState { + mnemonic: &'static str, + arguments: Option<&'static str>, + conditions: Option<&'static str>, + header: &'static crate::powerisa_instructions_xml::InstructionHeader, + insn: &'static crate::powerisa_instructions_xml::Instruction, + output: Expr>>, + is_illegal: Expr, + first_input: Expr>, + second_input: Expr>>, + second_input_used: Expr, +} + +trait FieldSet { + #[track_caller] + fn get(fields: &mut BTreeMap<&str, Expr>) -> Self; +} + +macro_rules! impl_field_set_tuples { + ($($first_v:ident: $FirstT:ident $(, $v:ident: $T:ident)*)? $(,)?) => { + $(impl_field_set_tuples!($($v: $T,)*);)? + + impl<$($FirstT: FieldSet, $($T: FieldSet),*)?> FieldSet for ($($FirstT, $($T,)*)?) { + #[track_caller] + fn get(fields: &mut BTreeMap<&str, Expr>) -> Self { + let _ = fields; + $(let $first_v = $FirstT::get(fields); + $(let $v = $T::get(fields);)*)? + ($($first_v, $($v,)*)?) + } + } + }; +} + +impl_field_set_tuples!(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8, v9: T9, v10: T10, v11: T11); + +macro_rules! impl_fields { + ( + $(#[name = $name:literal] + $vis:vis struct $Struct:ident($field_vis:vis $field_ty:ty);)* + ) => { + $($vis struct $Struct($field_vis Expr<$field_ty>); + + impl FieldSet for $Struct { + #[track_caller] + fn get(fields: &mut BTreeMap<&str, Expr>) -> Self { + Self(fields[$name].cast_bits_to(StaticType::TYPE)) + } + } + )* + }; +} + +impl_fields! { + #[name = "RA"] + struct FieldRA(UInt<5>); + #[name = "RT"] + struct FieldRT(UInt<5>); + #[name = "SI"] + struct FieldSI(SInt<16>); +} + +#[hdl] +fn translate_gpr(v: Expr>) -> Expr { + #[hdl] + MOpRegNum { + value: (v + MOpRegNum::POWER_ISA_GPR_REG_NUMS.start).cast_to_static::>(), + } +} + +#[hdl] +fn translate_gpr_or_zero(v: Expr>) -> Expr { + #[hdl] + let translate_gpr_or_zero = wire(); + connect(translate_gpr_or_zero, translate_gpr(v)); + #[hdl] + if v.cmp_eq(0u8) { + connect(translate_gpr_or_zero, MOpRegNum::const_zero()); + } + translate_gpr_or_zero +} + +impl DecodeState { + fn form(&self) -> &'static str { + let mut title_words = self + .header + .title() + .lines() + .iter() + .flat_map(|v| v.split_whitespace()); + match title_words.next_back() { + // split across lines + Some("form") => { + if let Some(word) = title_words.next_back() { + if let Some(form) = word.strip_suffix("-") { + return form; + } + } + } + Some(word) => { + if let Some(form) = word.strip_suffix("-form") { + return form; + } + } + None => {} + } + panic!( + "can't extract form from instruction header title: {:#?}", + self.header.title() + ) + } + fn bit_field_name(bit_field_name: &InstructionBitFieldName) -> &str { + match bit_field_name.text_line().items() { + [TextLineItem::Text(text)] => return text, + [TextLineItem::Text(text), TextLineItem::Superscript(sup)] => match (&**text, &**sup) { + ("any value", [TextLineItem::Text(sup)]) if &**sup == "*" => return "any value*", + _ => {} + }, + _ => {} + } + panic!("can't extract bit field name text: {bit_field_name:#?}") + } + fn msb0_bit_range(word: Expr>, bit_range: RangeInclusive) -> Expr { + let (msb0_start, msb0_end) = bit_range.into_inner(); + let max_index = word.ty().width() - 1; + let (Some(lsb0_start), Some(lsb0_end)) = ( + max_index.checked_sub(msb0_end), + max_index.checked_sub(msb0_start), + ) else { + panic!("invalid msb0 bit range {msb0_start}..={msb0_end}"); + }; + word[lsb0_start..=lsb0_end] + } + fn conditions(&self) -> impl Iterator { + let conditions = if let Some(conditions) = self.conditions { + let Some(conditions) = conditions + .strip_prefix("(") + .and_then(|v| v.strip_suffix(")")) + else { + panic!( + "instruction mnemonic conditions must begin and end with parenthesis.\n{:#?}", + self.header + ); + }; + conditions + } else { + "" + }; + conditions.split(" ").filter(|v| !v.is_empty()).map(|v| { + let Some((var, value)) = v.split_once("=") else { + panic!( + "instruction mnemonic condition must be of the form `VAR=number`.\n{:#?}", + self.header + ); + }; + (var.trim(), value.trim()) + }) + } + #[hdl] + fn decode_word( + &mut self, + matches: &mut Expr, + fields: &mut BTreeMap<&'static str, Expr>, + word: Expr>, + fields_inner: &'static InstructionBitFieldsInner, + ) { + let mut last_start = word.ty().width(); + for bit_field in fields_inner.fields().iter().rev() { + let mut bit_number_split = bit_field.bit_number().text().split_whitespace(); + let Some(first_bit_number) = bit_number_split.next() else { + panic!( + "missing first bit number: {fields_inner:#?}\nheader: {:#?}", + self.header, + ); + }; + let first_bit_number = first_bit_number + .parse() + .expect("first bit number should be a valid integer"); + let second_bit_number = bit_number_split.next(); + assert_eq!( + bit_number_split.next(), + None, + "{fields_inner:#?}\nheader: {:#?}", + self.header, + ); + let msb0_bit_range = if let Some(second_bit_number) = second_bit_number { + let second_bit_number = second_bit_number + .parse() + .expect("second bit number should be a valid integer"); + first_bit_number..=second_bit_number + } else if let Some(end) = last_start.checked_sub(1) { + first_bit_number..=end + } else { + panic!("no space for bit field, next bit field starts at bit 0\n{fields_inner:#?}"); + }; + last_start = *msb0_bit_range.start(); + let field = Self::msb0_bit_range(word, msb0_bit_range); + let mut name = Self::bit_field_name(bit_field.name()); + if name.contains(char::is_alphabetic) { + for (cond_name, cond_value) in self.conditions() { + if name == cond_name { + name = cond_value; + break; + } + } + } + if name == "any value*" || name.bytes().all(|b| b == b'/') { + // wildcard + } else if name.contains(char::is_alphabetic) { + let wire = wire_with_loc( + &format!("{}_{name}", self.mnemonic) + .replace(|c: char| !c.is_ascii_alphanumeric(), "_"), + SourceLocation::caller(), + field.ty(), + ); + connect(wire, field); + if fields.insert(name, wire).is_some() { + panic!("duplicate field name: {name:?}\nheader: {:#?}", self.header); + } + } else { + let value: u32 = name.parse().expect("bit field name must have at least one letter, be all `/`, or be a valid decimal number"); + *matches = *matches & field.cmp_eq(value); + } + } + } + #[hdl] + #[track_caller] + fn decode_scope(&mut self, f: F) { + let mut fields = BTreeMap::new(); + let mut matches = true.to_expr(); + let mut f = Some(f); + let mut run = + |this: &mut Self, matches: Expr, fields: &mut BTreeMap<&str, Expr>| { + #[hdl] + if matches { + connect( + this.second_input_used, + this.header.bit_fields().prefix().is_some(), + ); + connect(this.is_illegal, false); + f.take().expect("known to be Some")(this, FS::get(fields)); + } + }; + if let Some(prefix) = self.header.bit_fields().prefix() { + #[hdl] + if let HdlSome(prefix_word) = self.second_input { + self.decode_word( + &mut matches, + &mut fields, + prefix_word, + prefix.fields_inner(), + ); + self.decode_word( + &mut matches, + &mut fields, + self.first_input, + self.header.bit_fields().fields_inner(), + ); + run(self, matches, &mut fields); + } + } else { + self.decode_word( + &mut matches, + &mut fields, + self.first_input, + self.header.bit_fields().fields_inner(), + ); + run(self, matches, &mut fields); + } + } + fn decode_b_ba_bl_bla(&mut self) { + // TODO + } + #[hdl] + fn decode_addi_paddi(&mut self) { + match self.mnemonic { + "addi" => { + self.decode_scope(|this, (FieldRT(rt), FieldRA(ra), FieldSI(si))| { + connect( + ArrayVec::len(this.output), + 1usize.cast_to_static::>(), + ); + connect( + this.output[0], + AddSubMOp::add_sub_i( + #[hdl] + MOpDestReg { + normal_regs: [translate_gpr(rt), MOpRegNum::const_zero()], + flag_regs: [HdlNone(); _], + }, + #[hdl] + [ + translate_gpr_or_zero(ra).value, + MOpRegNum::const_zero().value, + ], + si.cast_to_static(), + OutputIntegerMode.Full64(), + false, + false, + false, + false, + ), + ); + }); + } + "paddi" => { + // TODO + } + _ => unreachable!("{:?}", self.mnemonic), + } + } +} + +type DecodeFn = fn(&mut DecodeState); + +const DECODE_FNS: &[(&[&str], DecodeFn)] = &[ + (&["b", "ba", "bl", "bla"], DecodeState::decode_b_ba_bl_bla), + (&["bc", "bca", "bcl", "bcla"], |_state| { + // TODO + }), + (&["bclr", "bclrl"], |_state| { + // TODO + }), + (&["bcctr", "bcctrl"], |_state| { + // TODO + }), + (&["bctar", "bctarl"], |_state| { + // TODO + }), + ( + &[ + "crand", "crnand", "cror", "crxor", "crnor", "creqv", "crandc", "crorc", + ], + |_state| { + // TODO + }, + ), + (&["mcrf"], |_state| { + // TODO + }), + (&["sc", "scv"], |_state| { + // TODO + }), + ( + &[ + "lbz", "plbz", "lbzx", "lbzu", "lbzux", "lhz", "plhz", "lhzx", "lhzu", "lhzux", "lha", + "plha", "lhax", "lhau", "lhaux", "lwz", "plwz", "lwzx", "lwzu", "lwzux", "lwa", "plwa", + "lwax", "lwaux", "ld", "pld", "ldx", "ldu", "ldux", + ], + |_state| { + // TODO + }, + ), + ( + &[ + "stb", "pstb", "stbx", "stbu", "stbux", "sth", "psth", "sthx", "sthu", "sthux", "stw", + "pstw", "stwx", "stwu", "stwux", "std", "pstd", "stdx", "stdu", "stdux", + ], + |_state| { + // TODO + }, + ), + (&["lq", "plq", "stq", "pstq"], |_state| { + // TODO + }), + ( + &["lhbrx", "sthbrx", "lwbrx", "stwbrx", "ldbrx", "stdbrx"], + |_state| { + // TODO + }, + ), + (&["lmw", "stmw"], |_state| { + // load/store multi-word are intentionally not implemented + }), + (&["lswi", "lswx", "stswi", "stswx"], |_state| { + // load/store string are intentionally not implemented + }), + (&["addi", "paddi"], DecodeState::decode_addi_paddi), + (&["addis"], |_state| { + // TODO + }), + (&["addpcis"], |_state| { + // TODO + }), + (&["add", "add.", "addo", "addo."], |_state| { + // TODO + }), + (&["addic", "addic."], |_state| { + // TODO + }), + (&["subf", "subf.", "subfo", "subfo."], |_state| { + // TODO + }), + (&["subfic"], |_state| { + // TODO + }), + (&["addc", "addc.", "addco", "addco."], |_state| { + // TODO + }), + (&["subfc", "subfc.", "subfco", "subfco."], |_state| { + // TODO + }), + (&["adde", "adde.", "addeo", "addeo."], |_state| { + // TODO + }), + (&["subfe", "subfe.", "subfeo", "subfeo."], |_state| { + // TODO + }), + (&["addme", "addme.", "addmeo", "addmeo."], |_state| { + // TODO + }), + (&["addze", "addze.", "addzeo", "addzeo."], |_state| { + // TODO + }), + (&["subfme", "subfme.", "subfmeo", "subfmeo."], |_state| { + // TODO + }), + (&["subfze", "subfze.", "subfzeo", "subfzeo."], |_state| { + // TODO + }), + (&["addex"], |_state| { + // TODO + }), + (&["neg", "neg.", "nego", "nego."], |_state| { + // TODO + }), + ( + &[ + "mulli", "mullw", "mullw.", "mullwo", "mullwo.", "mulhw", "mulhw.", "mulhwu", "mulhwu.", + ], + |_state| { + // TODO + }, + ), + ( + &[ + "divw", "divw.", "divwo", "divwo.", "divwu", "divwu.", "divwuo", "divwuo.", "divwe", + "divwe.", "divweo", "divweo.", "divweu", "divweu.", "divweuo", "divweuo.", "modsw", + "moduw", + ], + |_state| { + // TODO + }, + ), + (&["darn"], |_state| { + // TODO + }), + ( + &[ + "mulld", "mulld.", "mulldo", "mulldo.", "mulhd", "mulhd.", "mulhdu", "mulhdu.", + "maddhd", "maddhdu", "maddld", + ], + |_state| { + // TODO + }, + ), + ( + &[ + "divd", "divd.", "divdo", "divdo.", "divdu", "divdu.", "divduo", "divduo.", "divde", + "divde.", "divdeo", "divdeo.", "divdeu", "divdeu.", "divdeuo", "divdeuo.", "modsd", + "modud", + ], + |_state| { + // TODO + }, + ), + (&["cmpi", "cmp", "cmpli", "cmpl"], |_state| { + // TODO + }), + (&["cmprb", "cmpeqb"], |_state| { + // TODO + }), + (&["twi", "tw", "tdi", "td"], |_state| { + // TODO + }), + (&["isel"], |_state| { + // TODO + }), + ( + &[ + "andi.", "andis.", "ori", "oris", "xori", "xoris", "and", "and.", "xor", "xor.", + "nand", "nand.", "or", "or.", "orc", "orc.", "nor", "nor.", "eqv", "eqv.", "andc", + "andc.", + ], + |_state| { + // TODO + }, + ), + (&["extsb", "extsb.", "extsh", "extsh."], |_state| { + // TODO + }), + (&["cmpb"], |_state| { + // TODO + }), + ( + &[ + "cntlzw", "cntlzw.", "cnttzw", "cnttzw.", "popcntb", "popcntw", "prtyw", "popcntd", + "prtyd", "cntlzd", "cntlzd.", "cnttzd", "cnttzd.", "cntlzdm", "cnttzdm", "bpermd", + "cfuged", "pextd", "pdepd", + ], + |_state| { + // TODO + }, + ), + (&["extsw", "extsw."], |_state| { + // TODO + }), + ( + &[ + "rlwinm", "rlwinm.", "rlwnm", "rlwnm.", "rlwimi", "rlwimi.", "rldicl", "rldicl.", + "rldicr", "rldicr.", "rldic", "rldic.", "rldcl", "rldcl.", "rldcr", "rldcr.", "rldimi", + "rldimi.", + ], + |_state| { + // TODO + }, + ), + ( + &[ + "slw", "slw.", "srw", "srw.", "srawi", "srawi.", "sraw", "sraw.", "sld", "sld.", + "sradi", "sradi.", "srd", "srd.", "srad", "srad.", + ], + |_state| { + // TODO + }, + ), + (&["extswsli", "extswsli."], |_state| { + // TODO + }), + (&["cdtbcd", "cbcdtd", "addg6s"], |_state| { + // TODO + }), + (&["brh", "brw", "brd"], |_state| { + // TODO + }), + (&["hashst", "hashchk", "hashstp", "hashchkp"], |_state| { + // hash check/store are intentionally not implemented + }), + ( + &[ + "mfvsrd", "mfvsrld", "mfvsrwz", "mtvsrd", "mtvsrwa", "mtvsrwz", "mtvsrdd", "mtvsrws", + ], + |_state| { + // TODO(FP) -- mostly intentionally not implemented + }, + ), + ( + &["mtspr", "mfspr", "mftb", "mtmsr", "mtmsrd", "mfmsr"], + |_state| { + // TODO + }, + ), + ( + &[ + "mcrxrx", "mtocrf", "mtcrf", "mfocrf", "mfcr", "setb", "setbc", "setbcr", "setnbc", + "setnbcr", + ], + |_state| { + // TODO + }, + ), + (&["pnop"], |_state| { + // TODO + }), + (FP_MNEMONICS, |_state| { + // TODO(FP) + }), + (DFP_MNEMONICS, |_state| { + // decimal FP is intentionally not implemented + }), + (VMX_MNEMONICS, |_state| { + // VMX is intentionally not implemented + }), + (VSX_MNEMONICS, |_state| { + // VSX is intentionally not implemented + }), + ( + &["icbi", "icbt", "dcbz", "dcbst", "dcbf", "isync", "sync"], + |_state| { + // TODO + }, + ), + (&["copy", "paste.", "cpabort"], |_state| { + // copy/paste is intentionally not implemented + }), + (&["lwat", "ldat", "stwat", "stdat"], |_state| { + // TODO + }), + ( + &[ + "lbarx", "lharx", "lwarx", "stbcx.", "sthcx.", "stwcx.", "ldarx", "stdcx.", "stqcx.", + ], + |_state| { + // TODO + }, + ), + (&["wait"], |_state| { + // TODO + }), + (&["clrbhrb", "mfbhrbe"], |_state| { + // TODO branch history + }), + (&["rfscv", "rfid", "hrfid", "urfid"], |_state| { + // TODO + }), + (&["stop"], |_state| { + // TODO + }), + (&["lbzcix", "lhzcix", "lwzcix", "ldcix"], |_state| { + // TODO + }), + (&["stbcix", "sthcix", "stwcix", "stdcix"], |_state| { + // TODO + }), + ( + &[ + "slbie", "slbieg", "slbia", "slbiag", "slbmfev", "slbmfee", "slbfee.", "slbsync", + "tlbsync", + ], + |_state| { + // TODO + }, + ), + ( + &[ + "msgsndu", "msgclru", "msgsnd", "msgclr", "msgsndp", "msgclrp", "msgsync", + ], + |_state| { + // TODO + }, + ), +]; #[hdl_module] -pub fn decode_one_32bit_insn() { +pub fn decode_one_insn() { #[hdl] let output: ArrayVec> = m.output(); #[hdl] - let input: UInt<32> = m.input(); + let is_illegal: Bool = m.output(); + #[hdl] + let first_input: UInt<32> = m.input(); + #[hdl] + let second_input: HdlOption> = m.input(); + #[hdl] + let second_input_used: Bool = m.output(); + + connect(output, ArrayVec::TYPE.new()); + connect(second_input_used, false); + connect(is_illegal, true); + + let mut decode_fns = BTreeMap::new(); + for &(mnemonics, decode_fn) in DECODE_FNS { + for &mnemonic in mnemonics { + let duplicate = decode_fns.insert(mnemonic, decode_fn).is_some(); + assert!(!duplicate, "duplicate mnemonic in DECODE_FNS: {mnemonic:?}"); + } + } for insn in Instructions::get().instructions() { for header in insn.header() { for mnemonic_line in header.mnemonics().lines() { - let Some(mnemonic) = mnemonic_line.split_whitespace().next() else { - continue; - }; - match mnemonic { - "b" | "ba" | "bl" | "bla" => { - // TODO - } - "bc" | "bca" | "bcl" | "bcla" => { - // TODO - } - "bclr" | "bclrl" => { - // TODO - } - "bcctr" | "bcctrl" => { - // TODO - } - "bctar" | "bctarl" => { - // TODO - } - "crand" | "crnand" | "cror" | "crxor" | "crnor" | "creqv" | "crandc" - | "crorc" => { - // TODO - } - "mcrf" => { - // TODO - } - "sc" | "scv" => { - // TODO - } - "lbz" | "plbz" | "lbzx" | "lbzu" | "lbzux" | "lhz" | "plhz" | "lhzx" - | "lhzu" | "lhzux" | "lha" | "plha" | "lhax" | "lhau" | "lhaux" | "lwz" - | "plwz" | "lwzx" | "lwzu" | "lwzux" | "lwa" | "plwa" | "lwax" | "lwaux" - | "ld" | "pld" | "ldx" | "ldu" | "ldux" => { - // TODO - } - "stb" | "pstb" | "stbx" | "stbu" | "stbux" | "sth" | "psth" | "sthx" - | "sthu" | "sthux" | "stw" | "pstw" | "stwx" | "stwu" | "stwux" | "std" - | "pstd" | "stdx" | "stdu" | "stdux" => { - // TODO - } - "lq" | "plq" | "stq" | "pstq" => { - // TODO - } - "lhbrx" | "sthbrx" | "lwbrx" | "stwbrx" | "ldbrx" | "stdbrx" => { - // TODO - } - "lmw" | "stmw" => { - // load/store multi-word are intentionally not implemented - } - "lswi" | "lswx" | "stswi" | "stswx" => { - // load/store string are intentionally not implemented - } - "addi" | "paddi" => { - // TODO - } - "addis" => { - // TODO - } - "addpcis" => { - // TODO - } - "add" | "add." | "addo" | "addo." => { - // TODO - } - "addic" | "addic." => { - // TODO - } - "subf" | "subf." | "subfo" | "subfo." => { - // TODO - } - "subfic" => { - // TODO - } - "addc" | "addc." | "addco" | "addco." => { - // TODO - } - "subfc" | "subfc." | "subfco" | "subfco." => { - // TODO - } - "adde" | "adde." | "addeo" | "addeo." => { - // TODO - } - "subfe" | "subfe." | "subfeo" | "subfeo." => { - // TODO - } - "addme" | "addme." | "addmeo" | "addmeo." => { - // TODO - } - "addze" | "addze." | "addzeo" | "addzeo." => { - // TODO - } - "subfme" | "subfme." | "subfmeo" | "subfmeo." => { - // TODO - } - "subfze" | "subfze." | "subfzeo" | "subfzeo." => { - // TODO - } - "addex" => { - // TODO - } - "neg" | "neg." | "nego" | "nego." => { - // TODO - } - "mulli" | "mullw" | "mullw." | "mullwo" | "mullwo." | "mulhw" | "mulhw." - | "mulhwu" | "mulhwu." => { - // TODO - } - "divw" | "divw." | "divwo" | "divwo." | "divwu" | "divwu." | "divwuo" - | "divwuo." | "divwe" | "divwe." | "divweo" | "divweo." | "divweu" - | "divweu." | "divweuo" | "divweuo." | "modsw" | "moduw" => { - // TODO - } - "darn" => { - // TODO - } - "mulld" | "mulld." | "mulldo" | "mulldo." | "mulhd" | "mulhd." | "mulhdu" - | "mulhdu." | "maddhd" | "maddhdu" | "maddld" => { - // TODO - } - "divd" | "divd." | "divdo" | "divdo." | "divdu" | "divdu." | "divduo" - | "divduo." | "divde" | "divde." | "divdeo" | "divdeo." | "divdeu" - | "divdeu." | "divdeuo" | "divdeuo." | "modsd" | "modud" => { - // TODO - } - "cmpi" | "cmp" | "cmpli" | "cmpl" => { - // TODO - } - "cmprb" | "cmpeqb" => { - // TODO - } - "twi" | "tw" | "tdi" | "td" => { - // TODO - } - "isel" => { - // TODO - } - "andi." | "andis." | "ori" | "oris" | "xori" | "xoris" | "and" | "and." - | "xor" | "xor." | "nand" | "nand." | "or" | "or." | "orc" | "orc." | "nor" - | "nor." | "eqv" | "eqv." | "andc" | "andc." => { - // TODO - } - "extsb" | "extsb." | "extsh" | "extsh." => { - // TODO - } - "cmpb" => { - // TODO - } - "cntlzw" | "cntlzw." | "cnttzw" | "cnttzw." | "popcntb" | "popcntw" - | "prtyw" | "popcntd" | "prtyd" | "cntlzd" | "cntlzd." | "cnttzd" - | "cnttzd." | "cntlzdm" | "cnttzdm" | "bpermd" | "cfuged" | "pextd" - | "pdepd" => { - // TODO - } - "extsw" | "extsw." => { - // TODO - } - "rlwinm" | "rlwinm." | "rlwnm" | "rlwnm." | "rlwimi" | "rlwimi." | "rldicl" - | "rldicl." | "rldicr" | "rldicr." | "rldic" | "rldic." | "rldcl" - | "rldcl." | "rldcr" | "rldcr." | "rldimi" | "rldimi." => { - // TODO - } - "slw" | "slw." | "srw" | "srw." | "srawi" | "srawi." | "sraw" | "sraw." - | "sld" | "sld." | "sradi" | "sradi." | "srd" | "srd." | "srad" | "srad." => { - // TODO - } - "extswsli" | "extswsli." => { - // TODO - } - "cdtbcd" | "cbcdtd" | "addg6s" => { - // TODO - } - "brh" | "brw" | "brd" => { - // TODO - } - "hashst" | "hashchk" | "hashstp" | "hashchkp" => { - // hash check/store are intentionally not implemented - } - "mfvsrd" | "mfvsrld" | "mfvsrwz" | "mtvsrd" | "mtvsrwa" | "mtvsrwz" - | "mtvsrdd" | "mtvsrws" => { - // TODO(FP) -- mostly intentionally not implemented - } - "mtspr" | "mfspr" | "mftb" | "mtmsr" | "mtmsrd" | "mfmsr" => { - // TODO - } - "mcrxrx" | "mtocrf" | "mtcrf" | "mfocrf" | "mfcr" | "setb" | "setbc" - | "setbcr" | "setnbc" | "setnbcr" => { - // TODO - } - "pnop" => { - // TODO: not implemented - } - "lfs" | "plfs" | "lfsx" | "lfsu" | "lfsux" | "lfd" | "plfd" | "lfdx" - | "lfdu" | "lfdux" | "lfiwax" | "lfiwzx" | "stfs" | "pstfs" | "stfsx" - | "stfsu" | "stfsux" | "stfd" | "pstfd" | "stfdx" | "stfdu" | "stfdux" - | "stfiwx" | "lfdp" | "lfdpx" | "stfdp" | "stfdpx" | "fmr" | "fmr." - | "fneg" | "fneg." | "fabs" | "fabs." | "fnabs" | "fnabs." | "fcpsgn" - | "fcpsgn." | "fmrgew" | "fmrgow" | "fadd" | "fadd." | "fadds" | "fadds." - | "fsub" | "fsub." | "fsubs" | "fsubs." | "fmul" | "fmul." | "fmuls" - | "fmuls." | "fdiv" | "fdiv." | "fdivs" | "fdivs." | "fsqrt" | "fsqrt." - | "fsqrts" | "fsqrts." | "fre" | "fre." | "fres" | "fres." | "frsqrte" - | "frsqrte." | "frsqrtes" | "frsqrtes." | "ftdiv" | "ftsqrt" | "fmadd" - | "fmadd." | "fmadds" | "fmadds." | "fmsub" | "fmsub." | "fmsubs" - | "fmsubs." | "fnmadd" | "fnmadd." | "fnmadds" | "fnmadds." | "fnmsub" - | "fnmsub." | "fnmsubs" | "fnmsubs." | "frsp" | "frsp." | "fctid" - | "fctid." | "fctidz" | "fctidz." | "fctidu" | "fctidu." | "fctiduz" - | "fctiduz." | "fctiw" | "fctiw." | "fctiwz" | "fctiwz." | "fctiwu" - | "fctiwu." | "fctiwuz" | "fctiwuz." | "fcfid" | "fcfid." | "fcfidu" - | "fcfidu." | "fcfids" | "fcfids." | "fcfidus" | "fcfidus." | "frin" - | "frin." | "friz" | "friz." | "frip" | "frip." | "frim" | "frim." - | "fcmpu" | "fcmpo" | "fsel" | "fsel." | "mffs" | "mffs." | "mffsce" - | "mffscdrn" | "mffscdrni" | "mffscrn" | "mffscrni" | "mffsl" | "mcrfs" - | "mtfsfi" | "mtfsfi." | "mtfsf" | "mtfsf." | "mtfsb0" | "mtfsb0." - | "mtfsb1" | "mtfsb1." => { - // TODO(FP) - } - "dadd" | "dadd." | "daddq" | "daddq." | "dsub" | "dsub." | "dsubq" - | "dsubq." | "dmul" | "dmul." | "dmulq" | "dmulq." | "ddiv" | "ddiv." - | "ddivq" | "ddivq." | "dcmpu" | "dcmpuq" | "dcmpo" | "dcmpoq" | "dtstdc" - | "dtstdcq" | "dtstdg" | "dtstdgq" | "dtstex" | "dtstexq" | "dtstsf" - | "dtstsfq" | "dtstsfi" | "dtstsfiq" | "dquai" | "dquai." | "dquaiq" - | "dquaiq." | "dqua" | "dqua." | "dquaq" | "dquaq." | "drrnd" | "drrnd." - | "drrndq" | "drrndq." | "drintx" | "drintx." | "drintxq" | "drintxq." - | "drintn" | "drintn." | "drintnq" | "drintnq." | "dctdp" | "dctdp." - | "dctqpq" | "dctqpq." | "drsp" | "drsp." | "drdpq" | "drdpq." | "dcffix" - | "dcffix." | "dcffixq" | "dcffixq." | "dcffixqq" | "dctfix" | "dctfix." - | "dctfixq" | "dctfixq." | "dctfixqq" | "ddedpd" | "ddedpd." | "ddedpdq" - | "ddedpdq." | "denbcd" | "denbcd." | "denbcdq" | "denbcdq." | "dxex" - | "dxex." | "dxexq" | "dxexq." | "diex" | "diex." | "diexq" | "diexq." - | "dscli" | "dscli." | "dscliq" | "dscliq." | "dscri" | "dscri." | "dscriq" - | "dscriq." => { - // decimal FP is intentionally not implemented - } - "lvebx" | "lvehx" | "lvewx" | "lvx" | "lvxl" | "stvebx" | "stvehx" - | "stvewx" | "stvx" | "stvxl" | "lvsl" | "lvsr" | "vpkpx" | "vpkuhum" - | "vpkuwum" | "vpkudum" | "vupkhsb" | "vupklsb" | "vupkhsh" | "vupklsh" - | "vupkhsw" | "vupklsw" | "vupkhpx" | "vupklpx" | "vmrghb" | "vmrglb" - | "vmrghh" | "vmrglh" | "vmrghw" | "vmrglw" | "vmrgew" | "vmrgow" - | "vspltb" | "vsplth" | "vspltw" | "vspltisb" | "vspltish" | "vspltisw" - | "vperm" | "vpermr" | "vsel" | "vsldbi" | "vsldoi" | "vsrdbi" | "vsl" - | "vsr" | "vslo" | "vsro" | "vslv" | "vsrv" | "vextractub" | "vextractuh" - | "vextractuw" | "vextractd" | "vextublx" | "vextubrx" | "vextuhlx" - | "vextuhrx" | "vextuwlx" | "vextuwrx" | "vextdubvlx" | "vextdubvrx" - | "vextduhvlx" | "vextduhvrx" | "vextduwvlx" | "vextduwvrx" | "vextddvlx" - | "vextddvrx" | "vinsertb" | "vinserth" | "vinsertw" | "vinsertd" - | "vinsblx" | "vinsbrx" | "vinshlx" | "vinshrx" | "vinswlx" | "vinswrx" - | "vinsdlx" | "vinsdrx" | "vinsw" | "vinsd" | "vinsbvlx" | "vinsbvrx" - | "vinshvlx" | "vinshvrx" | "vinswvlx" | "vinswvrx" | "vaddcuw" | "vaddubm" - | "vadduhm" | "vadduwm" | "vaddudm" | "vadduqm" | "vaddeuqm" | "vaddcuq" - | "vaddecuq" | "vsubcuw" | "vsubsbs" | "vsububm" | "vsubuhm" | "vsubuwm" - | "vsubudm" | "vsububs" | "vsubuhs" | "vsubuws" | "vsubuqm" | "vsubeuqm" - | "vsubcuq" | "vsubecuq" | "vmulesb" | "vmulosb" | "vmuleub" | "vmuloub" - | "vmulesh" | "vmulosh" | "vmuleuh" | "vmulouh" | "vmulesw" | "vmulosw" - | "vmuleuw" | "vmulouw" | "vmuleud" | "vmuloud" | "vmulesd" | "vmulosd" - | "vmuluwm" | "vmulhsw" | "vmulhuw" | "vmulhsd" | "vmulhud" | "vmulld" - | "vmladduhm" | "vmsumubm" | "vmsummbm" | "vmsumshm" | "vmsumuhm" - | "vmsumudm" | "vmsumcud" | "vdivsw" | "vdivuw" | "vdivesw" | "vdiveuw" - | "vdivsd" | "vdivud" | "vdivesd" | "vdiveud" | "vdivsq" | "vdivuq" - | "vdivesq" | "vdiveuq" | "vmodsw" | "vmoduw" | "vmodsd" | "vmodud" - | "vmodsq" | "vmoduq" | "vnegw" | "vnegd" | "vextsb2w" | "vextsh2w" - | "vextsb2d" | "vextsh2d" | "vextsw2d" | "vextsd2q" | "vavgsb" | "vavgub" - | "vavgsh" | "vavguh" | "vavgsw" | "vavguw" | "vabsdub" | "vabsduh" - | "vabsduw" | "vmaxsb" | "vmaxub" | "vmaxsh" | "vmaxuh" | "vmaxsw" - | "vmaxuw" | "vmaxsd" | "vmaxud" | "vminsb" | "vminub" | "vminsh" - | "vminuh" | "vminsw" | "vminuw" | "vminsd" | "vminud" | "vcmpequb" - | "vcmpequb." | "vcmpequh" | "vcmpequh." | "vcmpequw" | "vcmpequw." - | "vcmpequd" | "vcmpequd." | "vcmpequq" | "vcmpequq." | "vcmpgtsb" - | "vcmpgtsb." | "vcmpgtub" | "vcmpgtub." | "vcmpgtsh" | "vcmpgtsh." - | "vcmpgtuh" | "vcmpgtuh." | "vcmpgtsw" | "vcmpgtsw." | "vcmpgtuw" - | "vcmpgtuw." | "vcmpgtsd" | "vcmpgtsd." | "vcmpgtud" | "vcmpgtud." - | "vcmpgtsq" | "vcmpgtsq." | "vcmpgtuq" | "vcmpgtuq." | "vcmpneb" - | "vcmpneb." | "vcmpnezb" | "vcmpnezb." | "vcmpneh" | "vcmpneh." - | "vcmpnezh" | "vcmpnezh." | "vcmpnew" | "vcmpnew." | "vcmpnezw" - | "vcmpnezw." | "vcmpsq" | "vcmpuq" | "vand" | "vandc" | "veqv" | "vnand" - | "vor" | "vorc" | "vnor" | "vxor" | "vrlb" | "vrlh" | "vrlw" | "vrld" - | "vrlq" | "vrlwnm" | "vrldnm" | "vrlqnm" | "vrlwmi" | "vrldmi" | "vrlqmi" - | "vslb" | "vslh" | "vslw" | "vsld" | "vslq" | "vsrb" | "vsrh" | "vsrw" - | "vsrd" | "vsrq" | "vsrab" | "vsrah" | "vsraw" | "vsrad" | "vsraq" - | "vaddfp" | "vsubfp" | "vmaddfp" | "vnmsubfp" | "vmaxfp" | "vminfp" - | "vcfsx" | "vcfux" | "vrfim" | "vrfin" | "vrfip" | "vrfiz" | "vcmpeqfp" - | "vcmpeqfp." | "vcmpgefp" | "vcmpgefp." | "vcmpgtfp" | "vcmpgtfp." - | "vexptefp" | "vrefp" | "vrsqrtefp" | "vcipher" | "vcipherlast" - | "vncipher" | "vncipherlast" | "vsbox" | "vpmsumb" | "vpmsumh" | "vpmsumw" - | "vpmsumd" | "vpermxor" | "vgnb" | "vclzb" | "vclzh" | "vclzw" | "vclzd" - | "vclzdm" | "vctzb" | "vctzh" | "vctzw" | "vctzd" | "vctzdm" | "vclzlsbb" - | "vctzlsbb" | "vpdepd" | "vpextd" | "vcfuged" | "vpopcntb" | "vpopcnth" - | "vpopcntw" | "vpopcntd" | "vprtybw" | "vprtybd" | "vprtybq" | "vbpermd" - | "vbpermq" | "mtvsrbm" | "mtvsrhm" | "mtvsrwm" | "mtvsrdm" | "mtvsrqm" - | "mtvsrbmi" | "vexpandbm" | "vexpandhm" | "vexpandwm" | "vexpanddm" - | "vexpandqm" | "vcntmbb" | "vcntmbh" | "vcntmbw" | "vcntmbd" - | "vextractbm" | "vextracthm" | "vextractwm" | "vextractdm" | "vextractqm" - | "vstribr" | "vstribr." | "vstribl" | "vstribl." | "vstrihr" | "vstrihr." - | "vstrihl" | "vstrihl." | "vclrlb" | "vclrrb" | "bcdadd." | "bcdsub." - | "bcdcfz." | "vmul10uq" | "vmul10cuq" | "vmul10euq" | "vmul10ecuq" - | "bcdcpsgn." | "bcdsetsgn." | "mtvscr" | "mfvscr" => { - // VMX is intentionally not implemented - } - // note this list only contains the instructions that are in - // powerisa-instructions.xml, this is not the complete list of VSX instructions - "lxsdx" | "lxsibzx" | "lxsihzx" | "lxsiwax" | "lxsiwzx" | "lxsspx" - | "stxsdx" | "stxsibx" | "stxsihx" | "stxsiwx" | "stxsspx" | "lxvb16x" - | "lxvh8x" | "lxvx" | "lxvdsx" | "lxvwsx" | "lxvrbx" | "lxvrdx" | "lxvrhx" - | "lxvrwx" | "lxvll" | "stxvb16x" | "stxvd2x" | "stxvh8x" | "stxvw4x" - | "stxvx" | "stxvrbx" | "stxvrdx" | "stxvrhx" | "stxvrwx" | "stxvll" - | "lxvp" | "plxvp" | "xsabsdp" | "xsabsqp" | "xscpsgndp" | "xscpsgnqp" - | "xsnabsdp" | "xsnabsqp" | "xsnegdp" | "xsnegqp" | "xvabsdp" | "xvabssp" - | "xvcpsgndp" | "xvcpsgnsp" | "xvnabsdp" | "xvnabssp" | "xvnegdp" - | "xvnegsp" | "xsaddqp" | "xsaddqpo" | "xsaddsp" | "xsdivsp" | "xsmulqp" - | "xsmulqpo" | "xsmulsp" | "xssubdp" | "xssubqp" | "xssubqpo" | "xssubsp" - | "xsmaddadp" | "xsmaddmdp" | "xsmaddasp" | "xsmaddmsp" | "xsmaddqp" - | "xsmaddqpo" | "xsmsubasp" | "xsmsubmsp" | "xsmsubqp" | "xsmsubqpo" - | "xsnmaddadp" | "xsnmaddmdp" | "xsnmaddasp" | "xsnmaddmsp" | "xsnmaddqp" - | "xsnmaddqpo" | "xsnmsubasp" | "xsnmsubmsp" | "xsnmsubqp" | "xsnmsubqpo" - | "xstsqrtdp" | "xvmaddadp" | "xvmaddmdp" | "xvmaddasp" | "xvmaddmsp" - | "xvmsubadp" | "xvmsubmdp" | "xvmsubasp" | "xvmsubmsp" | "xvnmaddadp" - | "xvnmaddmdp" | "xvnmaddasp" | "xvnmaddmsp" | "xvnmsubadp" | "xvnmsubmdp" - | "xvnmsubasp" | "xvnmsubmsp" | "xvtsqrtdp" | "xvtsqrtsp" | "xsmincqp" - | "xscvdpspn" | "xscvdpqp" | "xscvspdpn" | "xvcvbf16spn" | "xvcvspdp" - | "xvrdpim" | "xvrdpip" | "xvrspim" | "xvrspip" | "xscvqpsqz" | "xscvqpuqz" - | "xscvqpuwz" | "xscvsdqp" | "xscvudqp" | "xscvsqqp" | "xscvuqqp" - | "xscvsxddp" | "xscvuxddp" | "xscvsxdsp" | "xscvuxdsp" | "xvcvsxddp" - | "xvcvuxddp" | "xvcvsxwdp" | "xvcvuxwdp" | "xvcvsxdsp" | "xvcvuxdsp" - | "xvcvsxwsp" | "xvcvuxwsp" | "xsxexpdp" | "xsxexpqp" | "xsxsigdp" - | "xsxsigqp" | "xviexpdp" | "xviexpsp" | "xvxexpdp" | "xvxexpsp" - | "xvxsigdp" | "xvxsigsp" | "xxmfacc" | "xxmtacc" | "xxsetaccz" - | "xvi16ger2" | "pmxvi16ger2" | "xvi16ger2s" | "pmxvi16ger2s" | "xvi4ger8" - | "pmxvi4ger8" | "xvi8ger4" | "pmxvi8ger4" | "pmxvbf16ger2np" - | "pmxvf16ger2np" | "pmxvf32gernp" | "xxland" | "xxlandc" | "xxleqv" - | "xxlnand" | "xxlnor" | "xxlor" | "xxlorc" | "xxlxor" | "xxsel" | "xxeval" - | "xxblendvb" | "xxblendvd" | "xxblendvh" | "xxblendvw" | "xxbrh" | "xxbrq" - | "xxbrw" | "xxextractuw" | "xxinsertw" | "xxmrghw" | "xxmrglw" - | "xxsplti32dx" | "xxspltib" | "xxspltidp" | "xxspltiw" | "xxspltw" - | "xxperm" | "xxpermr" | "xxsldwi" | "xxgenpcvdm" | "xxgenpcvwm" | "lxvkq" - | "xvtlsbb" => { - // VSX is intentionally not implemented - } - "icbi" | "icbt" | "dcbz" | "dcbst" | "dcbf" | "isync" | "sync" => { - // TODO - } - "copy" | "paste." | "cpabort" => { - // copy/paste is intentionally not implemented - } - "lwat" | "ldat" | "stwat" | "stdat" => { - // TODO - } - "lbarx" | "lharx" | "lwarx" | "stbcx." | "sthcx." | "stwcx." | "ldarx" - | "stdcx." | "stqcx." => { - // TODO - } - "wait" => { - // TODO - } - "clrbhrb" | "mfbhrbe" => { - // TODO branch history - } - "rfscv" | "rfid" | "hrfid" | "urfid" => { - // TODO - } - "stop" => { - // TODO - } - "lbzcix" | "lhzcix" | "lwzcix" | "ldcix" => { - // TODO - } - "stbcix" | "sthcix" | "stwcix" | "stdcix" => { - // TODO - } - "slbie" | "slbieg" | "slbia" | "slbiag" | "slbmfev" | "slbmfee" | "slbfee." - | "slbsync" | "tlbsync" => { - // TODO - } - "msgsndu" | "msgclru" | "msgsnd" | "msgclr" | "msgsndp" | "msgclrp" - | "msgsync" => { - // TODO - } - _ => panic!("unhandled mnemonic: {mnemonic:?}"), + let mnemonic_line = mnemonic_line.trim(); + let mnemonic; + let arguments; + let mut conditions; + if let Some((mnemonic_, rest)) = mnemonic_line.split_once(char::is_whitespace) { + mnemonic = mnemonic_; + if let Some((arguments_, rest)) = + rest.trim_start().split_once(char::is_whitespace) + { + arguments = Some(arguments_); + conditions = Some(rest.trim_start()).filter(|v| !v.is_empty()); + } else { + arguments = None; + conditions = None; + } + } else { + mnemonic = mnemonic_line; + arguments = None; + conditions = None; } + if let Some("[Phased-Out]") = conditions { + conditions = None; + } + if let Some(conditions) = conditions { + assert_eq!(conditions.chars().next(), Some('('), "{header:#?}"); + } + let Some(decode_fn) = decode_fns.get(mnemonic) else { + panic!("unhandled mnemonic: {mnemonic:?}"); + }; + decode_fn(&mut DecodeState { + mnemonic, + arguments, + conditions, + header, + insn, + output, + is_illegal, + first_input, + second_input, + second_input_used, + }); } - // TODO: decode instruction fields } } - todo!() } #[hdl_module] diff --git a/crates/cpu/tests/expected/decode_one_32bit_insn.vcd b/crates/cpu/tests/expected/decode_one_32bit_insn.vcd deleted file mode 100644 index e69de29..0000000 diff --git a/crates/cpu/tests/expected/decode_one_insn.vcd b/crates/cpu/tests/expected/decode_one_insn.vcd new file mode 100644 index 0000000..110c3ee --- /dev/null +++ b/crates/cpu/tests/expected/decode_one_insn.vcd @@ -0,0 +1,667 @@ +$timescale 1 ps $end +$scope module decode_one_insn $end +$scope struct output $end +$scope struct elements $end +$scope struct \[0] $end +$var string 1 ! \$tag $end +$scope struct AluBranch $end +$var string 1 " \$tag $end +$scope struct AddSub $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 # prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 $ value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 % value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 & \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 ' \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 ( \[0] $end +$var wire 8 ) \[1] $end +$var wire 8 * \[2] $end +$upscope $end +$var wire 25 + imm_low $end +$var wire 1 , imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 - output_integer_mode $end +$upscope $end +$var wire 1 . invert_src0 $end +$var wire 1 / src1_is_carry_in $end +$var wire 1 0 invert_carry_in $end +$var wire 1 1 add_pc $end +$upscope $end +$scope struct AddSubI $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 2 prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 3 value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 4 value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 5 \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 6 \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 7 \[0] $end +$var wire 8 8 \[1] $end +$var wire 8 9 \[2] $end +$upscope $end +$var wire 25 : imm_low $end +$var wire 1 ; imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 < output_integer_mode $end +$upscope $end +$var wire 1 = invert_src0 $end +$var wire 1 > src1_is_carry_in $end +$var wire 1 ? invert_carry_in $end +$var wire 1 @ add_pc $end +$upscope $end +$scope struct Logical $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 A prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 B value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 C value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 D \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 E \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 F \[0] $end +$var wire 8 G \[1] $end +$var wire 8 H \[2] $end +$upscope $end +$var wire 25 I imm_low $end +$var wire 1 J imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 K output_integer_mode $end +$upscope $end +$var wire 4 L lut $end +$upscope $end +$upscope $end +$scope struct TransformedMove $end +$scope struct common $end +$var wire 2 M prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 N value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 O value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 P \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 Q \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 R \[0] $end +$var wire 8 S \[1] $end +$var wire 8 T \[2] $end +$upscope $end +$var wire 25 U imm_low $end +$var wire 1 V imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$upscope $end +$scope struct LoadStore $end +$var string 1 W \$tag $end +$scope struct Load $end +$var wire 1 X prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 Y value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 Z value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 [ \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 \ \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 ] \[0] $end +$var wire 8 ^ \[1] $end +$var wire 8 _ \[2] $end +$upscope $end +$var wire 25 ` imm_low $end +$var wire 1 a imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$scope struct Store $end +$var wire 1 b prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 c value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 d value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 e \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 f \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 g \[0] $end +$var wire 8 h \[1] $end +$var wire 8 i \[2] $end +$upscope $end +$var wire 25 j imm_low $end +$var wire 1 k imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 l \$tag $end +$scope struct AluBranch $end +$var string 1 m \$tag $end +$scope struct AddSub $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 n prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 o value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 p value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 q \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 r \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 s \[0] $end +$var wire 8 t \[1] $end +$var wire 8 u \[2] $end +$upscope $end +$var wire 25 v imm_low $end +$var wire 1 w imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 x output_integer_mode $end +$upscope $end +$var wire 1 y invert_src0 $end +$var wire 1 z src1_is_carry_in $end +$var wire 1 { invert_carry_in $end +$var wire 1 | add_pc $end +$upscope $end +$scope struct AddSubI $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 } prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 ~ value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 !" value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 "" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 #" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 $" \[0] $end +$var wire 8 %" \[1] $end +$var wire 8 &" \[2] $end +$upscope $end +$var wire 25 '" imm_low $end +$var wire 1 (" imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 )" output_integer_mode $end +$upscope $end +$var wire 1 *" invert_src0 $end +$var wire 1 +" src1_is_carry_in $end +$var wire 1 ," invert_carry_in $end +$var wire 1 -" add_pc $end +$upscope $end +$scope struct Logical $end +$scope struct alu_common $end +$scope struct common $end +$var string 0 ." prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 /" value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 0" value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 1" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 2" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 3" \[0] $end +$var wire 8 4" \[1] $end +$var wire 8 5" \[2] $end +$upscope $end +$var wire 25 6" imm_low $end +$var wire 1 7" imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$var string 1 8" output_integer_mode $end +$upscope $end +$var wire 4 9" lut $end +$upscope $end +$upscope $end +$scope struct TransformedMove $end +$scope struct common $end +$var wire 2 :" prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 ;" value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 <" value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 =" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 >" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 ?" \[0] $end +$var wire 8 @" \[1] $end +$var wire 8 A" \[2] $end +$upscope $end +$var wire 25 B" imm_low $end +$var wire 1 C" imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$upscope $end +$scope struct LoadStore $end +$var string 1 D" \$tag $end +$scope struct Load $end +$var wire 1 E" prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 F" value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 G" value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 H" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 I" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 J" \[0] $end +$var wire 8 K" \[1] $end +$var wire 8 L" \[2] $end +$upscope $end +$var wire 25 M" imm_low $end +$var wire 1 N" imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$scope struct Store $end +$var wire 1 O" prefix_pad $end +$scope struct dest $end +$scope struct normal_regs $end +$scope struct \[0] $end +$var wire 8 P" value $end +$upscope $end +$scope struct \[1] $end +$var wire 8 Q" value $end +$upscope $end +$upscope $end +$scope struct flag_regs $end +$scope struct \[0] $end +$var string 1 R" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$scope struct \[1] $end +$var string 1 S" \$tag $end +$scope struct HdlSome $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct src $end +$var wire 8 T" \[0] $end +$var wire 8 U" \[1] $end +$var wire 8 V" \[2] $end +$upscope $end +$var wire 25 W" imm_low $end +$var wire 1 X" imm_sign $end +$scope struct _phantom $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$scope struct len $end +$var wire 2 Y" value $end +$var string 1 Z" range $end +$upscope $end +$upscope $end +$var wire 1 [" is_illegal $end +$var wire 32 \" first_input $end +$scope struct second_input $end +$var string 1 ]" \$tag $end +$var wire 32 ^" HdlSome $end +$upscope $end +$var wire 1 _" second_input_used $end +$var wire 16 `" addi_SI $end +$var wire 5 a" addi_RA $end +$var wire 5 b" addi_RT $end +$scope struct translate_gpr_or_zero $end +$var wire 8 c" value $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +sAluBranch\x20(0) ! +sAddSubI\x20(1) " +s0 # +b100011 $ +b0 % +sHdlNone\x20(0) & +sHdlNone\x20(0) ' +b100100 ( +b0 ) +b0 * +b1001000110100 + +0, +sFull64\x20(0) - +0. +0/ +00 +01 +s0 2 +b100011 3 +b0 4 +sHdlNone\x20(0) 5 +sHdlNone\x20(0) 6 +b100100 7 +b0 8 +b0 9 +b1001000110100 : +0; +sFull64\x20(0) < +0= +0> +0? +0@ +s0 A +b100011 B +b0 C +sHdlNone\x20(0) D +sHdlNone\x20(0) E +b100100 F +b0 G +b0 H +b1001000110100 I +0J +sFull64\x20(0) K +b0 L +b1 M +b100011 N +b0 O +sHdlNone\x20(0) P +sHdlNone\x20(0) Q +b100100 R +b0 S +b0 T +b1001000110100 U +0V +sStore\x20(1) W +0X +b100011 Y +b0 Z +sHdlNone\x20(0) [ +sHdlNone\x20(0) \ +b100100 ] +b0 ^ +b0 _ +b1001000110100 ` +0a +0b +b100011 c +b0 d +sHdlNone\x20(0) e +sHdlNone\x20(0) f +b100100 g +b0 h +b0 i +b1001000110100 j +0k +sAluBranch\x20(0) l +sAddSub\x20(0) m +s0 n +b0 o +b0 p +sHdlNone\x20(0) q +sHdlNone\x20(0) r +b0 s +b0 t +b0 u +b0 v +0w +sFull64\x20(0) x +0y +0z +0{ +0| +s0 } +b0 ~ +b0 !" +sHdlNone\x20(0) "" +sHdlNone\x20(0) #" +b0 $" +b0 %" +b0 &" +b0 '" +0(" +sFull64\x20(0) )" +0*" +0+" +0," +0-" +s0 ." +b0 /" +b0 0" +sHdlNone\x20(0) 1" +sHdlNone\x20(0) 2" +b0 3" +b0 4" +b0 5" +b0 6" +07" +sFull64\x20(0) 8" +b0 9" +b0 :" +b0 ;" +b0 <" +sHdlNone\x20(0) =" +sHdlNone\x20(0) >" +b0 ?" +b0 @" +b0 A" +b0 B" +0C" +sLoad\x20(0) D" +0E" +b0 F" +b0 G" +sHdlNone\x20(0) H" +sHdlNone\x20(0) I" +b0 J" +b0 K" +b0 L" +b0 M" +0N" +0O" +b0 P" +b0 Q" +sHdlNone\x20(0) R" +sHdlNone\x20(0) S" +b0 T" +b0 U" +b0 V" +b0 W" +0X" +b1 Y" +sPhantomConst(\"0..=2\") Z" +0[" +b111000011001000001001000110100 \" +sHdlNone\x20(0) ]" +b0 ^" +0_" +b1001000110100 `" +b100 a" +b11 b" +b100100 c" +$end +#1000000 diff --git a/crates/cpu/tests/simple_power_isa_decoder.rs b/crates/cpu/tests/simple_power_isa_decoder.rs index 8cd1e11..dd0c5d5 100644 --- a/crates/cpu/tests/simple_power_isa_decoder.rs +++ b/crates/cpu/tests/simple_power_isa_decoder.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use cpu::{ - decoder::simple_power_isa::decode_one_32bit_insn, + decoder::simple_power_isa::decode_one_insn, instruction::{AddSubMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode}, util::array_vec::ArrayVec, }; @@ -15,7 +15,8 @@ use std::{ struct TestCase { mnemonic: &'static str, - input: u32, + first_input: u32, + second_input: Option, output: SimValue>>, loc: &'static std::panic::Location<'static>, } @@ -24,13 +25,21 @@ impl fmt::Debug for TestCase { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { mnemonic, - input, + first_input, + second_input, output, loc, } = self; - f.debug_struct("TestCase") + let mut debug_struct = f.debug_struct("TestCase"); + debug_struct .field("mnemonic", mnemonic) - .field("input", &format_args!("0x{input:08x}")) + .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() @@ -43,7 +52,8 @@ fn test_cases() -> Vec { #[track_caller] fn insn_single( mnemonic: &'static str, - input: u32, + first_input: u32, + second_input: Option, output: impl ToSimValue, ) -> TestCase { let zero_mop = UInt::new_dyn(MOp.canonical().bit_width()) @@ -54,7 +64,8 @@ fn test_cases() -> Vec { ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value(); TestCase { mnemonic, - input, + first_input, + second_input, output: single_storage.clone(), loc: std::panic::Location::caller(), } @@ -62,8 +73,9 @@ fn test_cases() -> Vec { retval.push(insn_single( "addi 3, 4, 0x1234", 0x38641234, + None, AddSubMOp::add_sub_i( - MOpDestReg::new_sim(&[3], &[]), + MOpDestReg::new_sim(&[MOpRegNum::POWER_ISA_GPR_REG_NUMS.start + 3], &[]), [ (MOpRegNum::POWER_ISA_GPR_REG_NUMS.start + 4).cast_to_static::>(), MOpRegNum::CONST_ZERO_REG_NUM.cast_to_static::>(), @@ -91,7 +103,8 @@ fn test_test_cases_assembly() -> std::io::Result<()> { let mut assembly = String::new(); for TestCase { mnemonic, - input: _, + first_input: _, + second_input: _, output: _, loc: _, } in &test_cases @@ -123,15 +136,29 @@ fn test_test_cases_assembly() -> std::io::Result<()> { let mut lines = stdout.lines(); let text_line = lines.next(); assert_eq!(text_line, Some("\t.text")); - for test_case in test_cases { + for test_case @ TestCase { + mnemonic: _, + first_input, + second_input, + output: _, + loc: _, + } in test_cases + { let Some(line) = lines.next() else { panic!("output missing line for: {test_case:?}"); }; let Some((_, comment)) = line.split_once('#') else { panic!("output line missing comment. test_case={test_case:?}\nline:\n{line}"); }; - let [b0, b1, b2, b3] = test_case.input.to_le_bytes(); - let expected_comment = format!(" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x}]"); + let [b0, b1, b2, b3] = first_input.to_le_bytes(); + let expected_comment = if let Some(second_input) = second_input { + let [b4, b5, b6, b7] = second_input.to_le_bytes(); + format!( + " encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x},0x{b4:02x},0x{b5:02x},0x{b6:02x},0x{b7:02x}]" + ) + } else { + format!(" encoding: [0x{b0:02x},0x{b1:02x},0x{b2:02x},0x{b3:02x}]") + }; assert_eq!( comment, expected_comment, "test_case={test_case:?}\nline:\n{line}" @@ -145,9 +172,9 @@ fn test_test_cases_assembly() -> std::io::Result<()> { #[hdl] #[test] -fn test_decode_one_32bit_insn() { +fn test_decode_insn() { let _n = SourceLocation::normalize_files_for_tests(); - let m = decode_one_32bit_insn(); + let m = decode_one_insn(); let mut sim = Simulation::new(m); let writer = RcWriter::default(); sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); @@ -165,20 +192,60 @@ fn test_decode_one_32bit_insn() { let mut writer = DumpVcdOnDrop { writer: Some(writer), }; - for test_case in test_cases() { - sim.write(sim.io().input, test_case.input); + for test_case @ TestCase { + mnemonic: _, + first_input, + second_input, + output: _, + loc: _, + } in test_cases() + { + sim.write(sim.io().first_input, first_input); + sim.write( + sim.io().second_input, + if let Some(v) = second_input { + #[hdl(sim)] + HdlSome(v) + } else { + #[hdl(sim)] + HdlNone() + }, + ); sim.advance_time(SimDuration::from_micros(1)); + let second_input_used = sim.read_bool(sim.io().second_input_used); + let is_illegal = sim.read_bool(sim.io().is_illegal); let output = sim.read(sim.io().output); - let expected = format!("{:?}", ArrayVec::elements_sim_ref(&test_case.output)); - let output = format!("{:?}", ArrayVec::elements_sim_ref(&output)); + #[derive(Debug)] + #[expect(dead_code, reason = "used only for Debug formatting")] + struct FormattedOutput<'a> { + insns: &'a [SimValue], + 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}" + "test_case={test_case:#?}\noutput={output}" ); } let vcd = String::from_utf8(writer.writer.take().unwrap().take()).unwrap(); println!("####### VCD:\n{vcd}\n#######"); - if vcd != include_str!("expected/decode_one_32bit_insn.vcd") { + if vcd != include_str!("expected/decode_one_insn.vcd") { panic!(); } }