diff --git a/Cargo.lock b/Cargo.lock index f33bbae..29086a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,11 +279,13 @@ dependencies = [ "fayalite", "hex-literal", "parse_powerisa_pdf", + "regex", "roxmltree", "serde", "sha2", "simple-mermaid", "ureq", + "which", ] [[package]] @@ -359,12 +361,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -630,9 +632,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" @@ -1210,6 +1212,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", + "regex", "rustix", "winsafe", ] diff --git a/Cargo.toml b/Cargo.toml index b7dc4d3..a3a9787 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0.202", features = ["derive"] } sha2 = "0.10.9" simple-mermaid = "0.2.0" ureq = "3.1.4" +which = { version = "6.0.3", features = ["regex"] } [profile.dev] opt-level = 1 diff --git a/crates/cpu/Cargo.toml b/crates/cpu/Cargo.toml index 0450556..f346e88 100644 --- a/crates/cpu/Cargo.toml +++ b/crates/cpu/Cargo.toml @@ -30,4 +30,6 @@ ureq.workspace = true [dev-dependencies] base16ct.workspace = true hex-literal.workspace = true +regex = "1.12.2" sha2.workspace = true +which.workspace = true diff --git a/crates/cpu/src/decoder.rs b/crates/cpu/src/decoder.rs new file mode 100644 index 0000000..661877c --- /dev/null +++ b/crates/cpu/src/decoder.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +pub mod simple_power_isa; diff --git a/crates/cpu/src/decoder/simple_power_isa.rs b/crates/cpu/src/decoder/simple_power_isa.rs new file mode 100644 index 0000000..1b16fc0 --- /dev/null +++ b/crates/cpu/src/decoder/simple_power_isa.rs @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + config::CpuConfig, instruction::MOp, powerisa_instructions_xml::Instructions, + util::array_vec::ArrayVec, +}; +use fayalite::prelude::*; + +#[hdl_module] +pub fn decode_one_32bit_insn() { + #[hdl] + let output: ArrayVec> = m.output(); + #[hdl] + let input: UInt<32> = m.input(); + 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:?}"), + } + } + // TODO: decode instruction fields + } + } + todo!() +} + +#[hdl_module] +pub fn simple_power_isa_decoder(config: PhantomConst) { + todo!() +} diff --git a/crates/cpu/src/instruction.rs b/crates/cpu/src/instruction.rs index 1610750..f70fe28 100644 --- a/crates/cpu/src/instruction.rs +++ b/crates/cpu/src/instruction.rs @@ -5,6 +5,7 @@ use fayalite::{ expr::{HdlPartialEqImpl, ops::ArrayLiteral}, intern::Interned, prelude::*, + ty::StaticType, }; use std::{borrow::Cow, fmt, marker::PhantomData, ops::Range}; @@ -947,6 +948,43 @@ pub struct MOpDestReg { pub flag_regs: Array, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>, } +impl MOpDestReg { + #[hdl] + #[track_caller] + pub fn new_sim(normal_regs: &[u32], flag_regs: &[u32]) -> SimValue { + let zero_reg = MOpRegNum::const_zero().to_sim_value(); + let mut normal_regs_sim = std::array::from_fn(|_| zero_reg.clone()); + for (i, reg) in normal_regs.iter().copied().enumerate() { + let Some(normal_reg_sim) = normal_regs_sim.get_mut(i) else { + panic!("too many normal regs"); + }; + if reg >= 1 << MOpRegNum::WIDTH { + panic!("normal reg number out of range"); + } + *normal_reg_sim.value = reg.cast_to_static::>(); + } + let mut flag_regs_sim = std::array::from_fn(|_| { + #[hdl(sim)] + HdlNone() + }); + for &flag_reg in flag_regs { + let Some(index) = { MOpRegNum::FLAG_REG_NUMS }.position(|v| flag_reg == v) else { + panic!( + "flag reg number {flag_reg} is out of range, supported range is: {:?}", + MOpRegNum::FLAG_REG_NUMS + ); + }; + flag_regs_sim[index] = #[hdl(sim)] + HdlSome(()); + } + #[hdl(sim)] + Self { + normal_regs: normal_regs_sim, + flag_regs: flag_regs_sim, + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum RenameTableName { /// the large rename table for normal registers (has less read/write ports) diff --git a/crates/cpu/src/lib.rs b/crates/cpu/src/lib.rs index e66e693..7992ec5 100644 --- a/crates/cpu/src/lib.rs +++ b/crates/cpu/src/lib.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information pub mod config; +pub mod decoder; pub mod instruction; pub mod next_pc; pub mod powerisa_instructions_xml; diff --git a/crates/cpu/tests/expected/decode_one_32bit_insn.vcd b/crates/cpu/tests/expected/decode_one_32bit_insn.vcd new file mode 100644 index 0000000..e69de29 diff --git a/crates/cpu/tests/simple_power_isa_decoder.rs b/crates/cpu/tests/simple_power_isa_decoder.rs new file mode 100644 index 0000000..8cd1e11 --- /dev/null +++ b/crates/cpu/tests/simple_power_isa_decoder.rs @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use cpu::{ + decoder::simple_power_isa::decode_one_32bit_insn, + instruction::{AddSubMOp, MOp, MOpDestReg, MOpRegNum, OutputIntegerMode}, + util::array_vec::ArrayVec, +}; +use fayalite::{prelude::*, sim::vcd::VcdWriterDecls, util::RcWriter}; +use std::{ + fmt::{self, Write as _}, + io::Write, + process::Command, +}; + +struct TestCase { + mnemonic: &'static str, + input: u32, + output: SimValue>>, + loc: &'static std::panic::Location<'static>, +} + +impl fmt::Debug for TestCase { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + mnemonic, + input, + output, + loc, + } = self; + f.debug_struct("TestCase") + .field("mnemonic", mnemonic) + .field("input", &format_args!("0x{input:08x}")) + .field("output", &ArrayVec::elements_sim_ref(output)) + .field("loc", &format_args!("{loc}")) + .finish() + } +} + +#[hdl] +fn test_cases() -> Vec { + let mut retval = Vec::new(); + #[track_caller] + fn insn_single( + mnemonic: &'static str, + input: u32, + output: impl ToSimValue, + ) -> 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, + input, + output: single_storage.clone(), + loc: std::panic::Location::caller(), + } + } + retval.push(insn_single( + "addi 3, 4, 0x1234", + 0x38641234, + AddSubMOp::add_sub_i( + MOpDestReg::new_sim(&[3], &[]), + [ + (MOpRegNum::POWER_ISA_GPR_REG_NUMS.start + 4).cast_to_static::>(), + MOpRegNum::CONST_ZERO_REG_NUM.cast_to_static::>(), + ], + 0x1234.cast_to_static::>(), + #[hdl(sim)] + OutputIntegerMode::Full64(), + false, + false, + false, + false, + ), + )); + retval +} + +#[test] +fn test_test_cases_assembly() -> std::io::Result<()> { + let llvm_mc_regex = regex::Regex::new(r"llvm-mc(-\d+)?$").expect("known to be a valid regex"); + let llvm_mc = which::which_re(llvm_mc_regex) + .expect("can't find llvm-mc or llvm-mc- in path") + .next() + .expect("can't find llvm-mc or llvm-mc- in path"); + let test_cases = test_cases(); + let mut assembly = String::new(); + for TestCase { + mnemonic, + input: _, + output: _, + loc: _, + } in &test_cases + { + writeln!(assembly, "{mnemonic}").unwrap(); + } + let (reader, mut writer) = std::io::pipe()?; + let thread = std::thread::spawn(move || writer.write_all(assembly.as_bytes())); + let std::process::Output { + status, + stdout, + stderr, + } = Command::new(&llvm_mc) + .arg("--triple=powerpc64le-linux-gnu") + .arg("--assemble") + .arg("--filetype=asm") + .arg("--show-encoding") + .arg("-") + .stdin(reader) + .output()?; + let _ = thread.join(); + let stderr = String::from_utf8_lossy(&stderr); + eprint!("{stderr}"); + if !status.success() { + panic!("{} failed: {status}", llvm_mc.display()); + } + let stdout = String::from_utf8_lossy(&stdout); + print!("{stdout}"); + let mut lines = stdout.lines(); + let text_line = lines.next(); + assert_eq!(text_line, Some("\t.text")); + for test_case 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}]"); + assert_eq!( + comment, expected_comment, + "test_case={test_case:?}\nline:\n{line}" + ); + } + for line in lines { + assert!(line.trim().is_empty(), "bad trailing output line: {line:?}"); + } + Ok(()) +} + +#[hdl] +#[test] +fn test_decode_one_32bit_insn() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = decode_one_32bit_insn(); + let mut sim = Simulation::new(m); + let writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + struct DumpVcdOnDrop { + writer: Option, + } + 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 in test_cases() { + sim.write(sim.io().input, test_case.input); + sim.advance_time(SimDuration::from_micros(1)); + 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)); + assert!( + expected == 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") { + panic!(); + } +} + +#[hdl] +#[test] +fn test_simple_power_isa_decoder() { + // TODO +}