Compare commits

...

10 commits

14 changed files with 299378 additions and 17935 deletions

483
Cargo.lock generated
View file

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
@ -81,12 +96,36 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base16ct"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "2.6.0"
@ -130,20 +169,47 @@ dependencies = [
]
[[package]]
name = "cc"
version = "1.1.28"
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "4.5.20"
@ -209,9 +275,17 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
name = "cpu"
version = "0.1.0"
dependencies = [
"base16ct 1.0.0",
"fayalite",
"hex-literal",
"parse_powerisa_pdf",
"regex",
"roxmltree",
"serde",
"sha2",
"simple-mermaid",
"ureq",
"which",
]
[[package]]
@ -223,6 +297,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -278,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]]
@ -342,7 +425,7 @@ name = "fayalite-proc-macros-impl"
version = "0.3.0"
source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#c97b44d9d646a4aa64fcc046538fc2354bb708ee"
dependencies = [
"base16ct",
"base16ct 0.2.0",
"num-bigint",
"prettyplease",
"proc-macro2",
@ -367,12 +450,28 @@ dependencies = [
"thiserror",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "foldhash"
version = "0.1.5"
@ -395,6 +494,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
@ -408,10 +518,10 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.14.5"
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
@ -424,12 +534,24 @@ dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex-literal"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
[[package]]
name = "home"
version = "0.5.9"
@ -439,6 +561,22 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "indenter"
version = "0.3.3"
@ -447,13 +585,14 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.5.0"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
@ -462,6 +601,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -476,7 +624,7 @@ checksum = "58715c67c327da7f1558708348d68c207fd54900c4ae0529e29305d04d795b8c"
dependencies = [
"cfg-if",
"derive_destructure2",
"getrandom",
"getrandom 0.3.4",
"libc",
"scopeguard",
"windows-sys 0.61.2",
@ -484,9 +632,25 @@ 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"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
dependencies = [
"cfg-if",
"windows-link",
]
[[package]]
name = "libm"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "linux-raw-sys"
@ -494,12 +658,57 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "mupdf-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e9a0d4e844ab50315d43312f3d62f72c77205b07c8ee21cbd4b52bdc2a9910"
dependencies = [
"bindgen",
"cc",
"pkg-config",
"regex",
"zerocopy",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
@ -545,6 +754,23 @@ dependencies = [
"serde",
]
[[package]]
name = "parse_powerisa_pdf"
version = "0.1.0"
source = "git+https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git?branch=master#38a1fb328bd44f26389c28fbf66716154f4113dc"
dependencies = [
"indexmap",
"libm",
"mupdf-sys",
"quick-xml",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "petgraph"
version = "0.8.3"
@ -557,6 +783,12 @@ dependencies = [
"serde",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "prettyplease"
version = "0.2.22"
@ -576,6 +808,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.37"
@ -616,6 +857,64 @@ dependencies = [
"serde",
]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roxmltree"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb"
dependencies = [
"memchr",
]
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
version = "0.38.37"
@ -629,6 +928,41 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.23.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.18"
@ -643,18 +977,28 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@ -676,9 +1020,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.8"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
@ -691,6 +1035,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "simple-mermaid"
version = "0.2.0"
@ -703,6 +1053,12 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.96"
@ -765,6 +1121,47 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a"
dependencies = [
"base64",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"ureq-proto",
"utf-8",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.2"
@ -783,6 +1180,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
@ -792,6 +1195,15 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "webpki-roots"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "which"
version = "6.0.3"
@ -800,6 +1212,7 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
dependencies = [
"either",
"home",
"regex",
"rustix",
"winsafe",
]
@ -921,3 +1334,29 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "zerocopy"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

View file

@ -14,9 +14,16 @@ categories = []
rust-version = "1.89.0"
[workspace.dependencies]
base16ct = "1.0.0"
fayalite = { git = "https://git.libre-chip.org/libre-chip/fayalite.git", version = "0.3.0", branch = "master" }
hex-literal = "1.1.0"
parse_powerisa_pdf = { git = "https://git.libre-chip.org/libre-chip/parse_powerisa_pdf.git", version = "0.1.0", branch = "master" }
roxmltree = "0.21.1"
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

View file

@ -16,5 +16,20 @@ version.workspace = true
[dependencies]
fayalite.workspace = true
roxmltree.workspace = true
serde.workspace = true
simple-mermaid.workspace = true
[build-dependencies]
base16ct.workspace = true
hex-literal.workspace = true
parse_powerisa_pdf.workspace = true
sha2.workspace = true
ureq.workspace = true
[dev-dependencies]
base16ct.workspace = true
hex-literal.workspace = true
regex = "1.12.2"
sha2.workspace = true
which.workspace = true

96
crates/cpu/build.rs Normal file
View file

@ -0,0 +1,96 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use parse_powerisa_pdf::parse_powerisa_pdf_and_generate_xml;
use sha2::{Digest, Sha256};
use std::{
error::Error,
io::{ErrorKind, Read},
path::Path,
};
const EXPECTED_FILE_SIZE: u64 = 6425593;
const EXPECTED_FILE_HASH: &[u8; 32] =
&hex_literal::hex!("56372d23ece7e9e2c1b381a639443982a3e16e38109df1c141d655b779b61fdb");
fn verify_powerisa_file(file: impl std::io::Read) -> Result<Vec<u8>, Box<dyn Error>> {
let mut buf = Vec::with_capacity(usize::try_from(EXPECTED_FILE_SIZE)? + 1);
file.take(EXPECTED_FILE_SIZE + 1).read_to_end(&mut buf)?;
if buf.len() > EXPECTED_FILE_SIZE as usize {
Err(format!("file is bigger than the expected length {EXPECTED_FILE_SIZE}").into())
} else if buf.len() < EXPECTED_FILE_SIZE as usize {
Err(format!(
"file is smaller than the expected length {EXPECTED_FILE_SIZE}: actual length {}",
buf.len()
)
.into())
} else {
let hash = Sha256::digest(&buf);
let hash = &*hash;
if hash != EXPECTED_FILE_HASH {
Err(format!(
"file's SHA256 hash doesn't match the expected hash: expected: {:x} got: {:x}",
base16ct::HexDisplay(EXPECTED_FILE_HASH),
base16ct::HexDisplay(hash)
)
.into())
} else {
Ok(buf)
}
}
}
fn is_powerisa_pdf(path: impl AsRef<Path>) -> Result<bool, Box<dyn Error>> {
let path = path.as_ref();
let metadata = match std::fs::metadata(path) {
Err(e) if e.kind() == ErrorKind::NotFound => return Ok(false),
v => v?,
};
if !metadata.is_file() {
return Ok(false);
}
let file = std::fs::File::open(path)?;
verify_powerisa_file(file)?;
Ok(true)
}
fn out_dir() -> String {
std::env::var("OUT_DIR").expect("OUT_DIR env var is not set or invalid")
}
fn get_powerisa_pdf_name_and_dir<'a>(
out_dir: &'a str,
) -> Result<(&'static str, &'a Path), Box<dyn Error>> {
const FILE_NAME: &str = "OPF_PowerISA_v3.1C.pdf";
let out_dir = Path::new(out_dir);
if is_powerisa_pdf(FILE_NAME)? {
println!("cargo::rerun-if-changed={FILE_NAME}");
return Ok((FILE_NAME, Path::new(".")));
}
let full_path = out_dir.join(FILE_NAME);
let full_path = full_path
.into_os_string()
.into_string()
.expect("should be valid UTF-8");
if is_powerisa_pdf(&full_path)? {
println!("cargo::rerun-if-changed={full_path}");
return Ok((FILE_NAME, out_dir));
}
const URL: &str = "https://libre-chip.org/OPF_PowerISA_v3.1C.pdf";
println!("cargo::warning={FILE_NAME} not found locally, downloading from {URL}");
let buf = verify_powerisa_file(ureq::get(URL).call()?.into_body().into_reader())?;
std::fs::write(&full_path, buf)?;
println!("cargo::rerun-if-changed={full_path}");
Ok((FILE_NAME, out_dir))
}
fn main() -> Result<(), Box<dyn Error>> {
let out_dir = out_dir();
let (pdf_name, pdf_dir) = get_powerisa_pdf_name_and_dir(&out_dir)?;
let old_dir = std::env::current_dir()?;
std::env::set_current_dir(pdf_dir)?;
let xml = parse_powerisa_pdf_and_generate_xml(pdf_name, None, false)?;
std::env::set_current_dir(old_dir)?;
std::fs::write(Path::new(&out_dir).join("powerisa-instructions.xml"), xml)?;
Ok(())
}

View file

@ -0,0 +1,4 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub mod simple_power_isa;

View file

@ -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<MOp, ConstUsize<2>> = 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<CpuConfig>) {
todo!()
}

View file

@ -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<HdlOption<()>, { range_u32_len(&MOpRegNum::FLAG_REG_NUMS) }>,
}
impl MOpDestReg {
#[hdl]
#[track_caller]
pub fn new_sim(normal_regs: &[u32], flag_regs: &[u32]) -> SimValue<Self> {
let zero_reg = MOpRegNum::const_zero().to_sim_value();
let mut normal_regs_sim = std::array::from_fn(|_| zero_reg.clone());
for (i, reg) in normal_regs.iter().copied().enumerate() {
let Some(normal_reg_sim) = normal_regs_sim.get_mut(i) else {
panic!("too many normal regs");
};
if reg >= 1 << MOpRegNum::WIDTH {
panic!("normal reg number out of range");
}
*normal_reg_sim.value = reg.cast_to_static::<UInt<_>>();
}
let mut flag_regs_sim = std::array::from_fn(|_| {
#[hdl(sim)]
HdlNone()
});
for &flag_reg in flag_regs {
let Some(index) = { MOpRegNum::FLAG_REG_NUMS }.position(|v| flag_reg == v) else {
panic!(
"flag reg number {flag_reg} is out of range, supported range is: {:?}",
MOpRegNum::FLAG_REG_NUMS
);
};
flag_regs_sim[index] = #[hdl(sim)]
HdlSome(());
}
#[hdl(sim)]
Self {
normal_regs: normal_regs_sim,
flag_regs: flag_regs_sim,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum RenameTableName {
/// the large rename table for normal registers (has less read/write ports)

View file

@ -1,8 +1,10 @@
// 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;
pub mod reg_alloc;
pub mod register;
pub mod unit;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -326,7 +326,9 @@ impl MockInsn {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct MockMachineState {
reset_at_exit: bool,
call_stack: Vec<u64>,
orig_input: &'static [u8],
input: &'static [u8],
output: Vec<u8>,
regs: MockRegs,
@ -340,16 +342,36 @@ const RESET_PC: u64 = 0;
const EXIT_PC: u64 = u64::from_be_bytes(*b"ExitExit");
impl MockMachineState {
fn new(insns: MockInsns, input: impl AsRef<[u8]>) -> Self {
fn new(insns: MockInsns, input: impl AsRef<[u8]>, reset_at_exit: bool) -> Self {
let input = Interned::into_inner(input.as_ref().intern());
Self {
reset_at_exit,
call_stack: Vec::with_capacity(16),
input: Interned::into_inner(input.as_ref().intern()),
orig_input: input,
input,
output: Vec::with_capacity(16),
regs: MockRegs::new(),
pc: RESET_PC,
insns,
}
}
fn reset(&mut self) {
let Self {
reset_at_exit: _,
call_stack,
orig_input,
input,
output,
regs,
pc,
insns: _,
} = self;
call_stack.clear();
*input = *orig_input;
output.clear();
*regs = MockRegs::new();
*pc = RESET_PC;
}
fn run_one(&mut self, trace: bool) -> RetireSeqEntry {
let orig_pc = self.pc;
let insn = self
@ -420,7 +442,18 @@ impl MockMachineState {
next_pc = target;
}
MockInsn::Ret => next_pc = self.call_stack.pop().unwrap_or(DEMO_ILLEGAL_INSN_TRAP),
MockInsn::ExitSysCall => next_pc = EXIT_PC,
MockInsn::ExitSysCall => {
if self.reset_at_exit {
self.reset();
return RetireSeqEntry {
pc: orig_pc,
cond_br_taken: None,
insn,
};
} else {
next_pc = EXIT_PC;
}
}
MockInsn::Illegal => next_pc = DEMO_ILLEGAL_INSN_TRAP,
}
self.pc = next_pc;
@ -721,7 +754,7 @@ fn test_program_expr_parser() {
#[track_caller]
fn test(expected_output: &str, expected_exit_code: u64, input: &str) {
println!("starting new test case: input={input:?}\n\n");
let mut state = MockMachineState::new(mock_program_expr_parser(), input);
let mut state = MockMachineState::new(mock_program_expr_parser(), input, false);
let exit_code = state.run_until_exit(10000, true).unwrap();
println!("output: {:?}", str::from_utf8(&state.output));
println!("exit code: {exit_code}");
@ -771,7 +804,7 @@ impl FetchPipeQueueEntry {
.rotate_left(32)
.wrapping_mul(0x92B38C197608A6B) // random prime
.rotate_right(60);
(random % 8) as u8
if random % 32 == 0 { 30 } else { 3 }
}
}
@ -799,6 +832,12 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
async |mut sim| {
sim.write(from_fetch.fetch.ready, false).await;
sim.write(from_fetch.cancel.ready, false).await;
sim.write(
from_fetch.next_fetch_block_ids,
#[hdl(sim)]
(from_fetch.next_fetch_block_ids.ty()).HdlNone(),
)
.await;
sim.write(
to_post_decode.inner.data,
to_post_decode.ty().inner.data.HdlNone(),
@ -840,12 +879,21 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
let mut next_id = 0u32;
loop {
let mut sim_queue = queue_debug.ty().new_sim(FetchPipeQueueEntry.default_sim());
let mut next_fetch_block_ids =
from_fetch.next_fetch_block_ids.ty().HdlSome.new_sim(0u8);
for entry in &queue {
ArrayVec::try_push_sim(&mut sim_queue, entry)
.ok()
.expect("queue is known to be small enough");
let _ = ArrayVec::try_push_sim(&mut next_fetch_block_ids, &entry.fetch_block_id);
}
sim.write(queue_debug, sim_queue).await;
sim.write(
from_fetch.next_fetch_block_ids,
#[hdl(sim)]
(from_fetch.next_fetch_block_ids.ty()).HdlSome(next_fetch_block_ids),
)
.await;
if let Some(front) = queue.front().filter(|v| v.cycles_left.as_int() == 0) {
#[hdl(sim)]
let FetchPipeQueueEntry {
@ -874,6 +922,7 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
fetch_block_id,
id: next_id.cast_to_static::<UInt<_>>(),
pc,
predicted_next_pc: 0u64,
size_in_bytes: insn.byte_len().cast_to_static::<UInt<_>>(),
kind: insn.wip_decoded_insn_kind(),
};
@ -890,6 +939,7 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
fetch_block_id,
id: next_id.cast_to_static::<UInt<_>>(),
pc: start_pc,
predicted_next_pc: 0u64,
size_in_bytes: 0u8.cast_to_static::<UInt<_>>(),
kind: WipDecodedInsnKind.Interrupt(DEMO_ILLEGAL_INSN_TRAP),
},
@ -917,10 +967,27 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
.await;
sim.write(from_fetch.cancel.ready, true).await;
sim.wait_for_clock_edge(cd.clk).await;
println!(
"Dump mock fetch decode pipe queue: {:#?}",
Vec::from_iter(queue.iter().map(|v| {
DebugAsDisplay(format!(
"fid={:#x} pc={:#x}",
v.fetch_block_id.as_int(),
v.start_pc.as_int(),
))
}))
);
if sim.read_past_bool(to_post_decode.inner.ready, cd.clk).await {
#[hdl(sim)]
if let HdlSome(_) = sim.read_past(to_post_decode.inner.data, cd.clk).await {
queue.pop_front();
let Some(v) = queue.pop_front() else {
unreachable!();
};
println!(
"mock fetch decode pipe queue pop: fid={:#x} pc={:#x}",
v.fetch_block_id.as_int(),
v.start_pc.as_int(),
);
}
}
for entry in &mut queue {
@ -935,7 +1002,14 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
{
// cancel in-progress fetches from newest to oldest
for _ in 0..*in_progress_fetches_to_cancel {
let _ = queue.pop_back();
let Some(v) = queue.pop_back() else {
unreachable!();
};
println!(
"mock fetch decode pipe queue cancel unpush: fid={:#x} pc={:#x}",
v.fetch_block_id.as_int(),
v.start_pc.as_int(),
);
}
}
if !sim.read_past_bool(from_fetch.fetch.ready, cd.clk).await {
@ -949,6 +1023,11 @@ fn mock_fetch_pipe(config: PhantomConst<CpuConfig>, insns: MockInsns) {
start_pc,
fetch_block_id,
} = &inner;
println!(
"mock fetch decode pipe queue push: fid={:#x} pc={:#x}",
fetch_block_id.as_int(),
start_pc.as_int(),
);
queue.push_back(
#[hdl(sim)]
FetchPipeQueueEntry {
@ -979,7 +1058,8 @@ impl ExecuteRetirePipeQueueEntry {
WipDecodedInsn {
fetch_block_id: 0u8,
id: 0_hdl_u12,
pc: 0xEEEE_EEEE_EEEE_EEEEu64,
pc: 0u64,
predicted_next_pc: 0u64,
size_in_bytes: 0_hdl_u4,
kind: WipDecodedInsnKind.NonBranch(),
},
@ -996,7 +1076,13 @@ impl ExecuteRetirePipeQueueEntry {
.rotate_left(32)
.wrapping_mul(0x73161B54984B1C21) // random prime
.rotate_right(60);
(random % 16) as u8
const DELAYS: &[u8; 0x20] = &[
0, 0, 0, 0, 0, 0, 0, 0, //
1, 1, 1, 1, 1, 1, 1, 1, //
2, 2, 2, 2, 2, 2, 2, 2, //
3, 3, 3, 3, 4, 5, 6, 50, // 50 for simulating a cache miss or something
];
DELAYS[(random & 0x1F) as usize]
}
}
@ -1052,6 +1138,7 @@ impl MockExecuteState {
fetch_block_id: &insn.fetch_block_id,
id: &insn.id,
pc,
predicted_next_pc: &insn.predicted_next_pc,
size_in_bytes: mock_insn.byte_len().cast_to_static::<UInt<4>>(),
kind: mock_insn.wip_decoded_insn_kind(),
};
@ -1060,16 +1147,14 @@ impl MockExecuteState {
"insn doesn't match expected:\ninsn: {insn:?}\nexpected insn: {expected_insn:?}"
));
}
if let Some(next_insn) = self.queue.front() {
if next_pc != next_insn.insn.pc.as_int() {
self.canceling = true;
if !passive {
println!(
"MockExecuteState: starting canceling {} instruction(s): next_pc={next_pc:#x}, mis-predicted next_pc={next_insn_pc}",
self.queue.len(),
next_insn_pc = next_insn.insn.pc
);
}
if next_pc != insn.predicted_next_pc.as_int() {
self.canceling = true;
if !passive {
println!(
"MockExecuteState: starting canceling {} instruction(s): next_pc={next_pc:#x}, mis-predicted next_pc={}",
self.queue.len(),
insn.predicted_next_pc
);
}
}
Ok(
@ -1130,6 +1215,10 @@ impl MockExecuteState {
if !self.used_ids.insert(insn.id.clone()) {
panic!("next_pc gave a duplicate insn id: {insn:?}");
}
println!(
"MockExecutionState::start fid={} id={} pc={}",
insn.fetch_block_id, insn.id, insn.pc
);
self.queue.push_back(
#[hdl(sim)]
ExecuteRetirePipeQueueEntry {
@ -1187,14 +1276,15 @@ fn mock_execute_retire_pipe(
cd,
async |mut sim| {
sim.write(from_post_decode.ready, 0usize).await;
sim.write(from_post_decode.cancel.ready, false).await;
sim.write(
retire_output.inner.data,
retire_output.ty().inner.data.HdlNone(),
)
.await;
sim.write(
retire_output.next_insn_ids,
retire_output.next_insn_ids.ty().HdlNone(),
retire_output.next_insns,
retire_output.next_insns.ty().HdlNone(),
)
.await;
sim.write(
@ -1237,7 +1327,7 @@ fn mock_execute_retire_pipe(
let config = from_post_decode.config.ty();
let mut state = MockExecuteState::new(
config,
RetireSeq::new(MockMachineState::new(mock_insns, mock_input)),
RetireSeq::new(MockMachineState::new(mock_insns, mock_input, true)),
);
let empty_retire_insn = #[hdl(sim)]
RetireToNextPcInterfacePerInsn::<_> {
@ -1255,22 +1345,32 @@ fn mock_execute_retire_pipe(
let mut sim_queue = queue_debug
.ty()
.new_sim(ExecuteRetirePipeQueueEntry.default_sim());
let mut next_insn_ids = retire_output.next_insn_ids.ty().HdlSome.new_sim(0_hdl_u12);
let mut next_insns = retire_output.next_insns.ty().HdlSome.new_sim(
#[hdl(sim)]
WipDecodedInsn {
fetch_block_id: 0u8,
id: 0_hdl_u12,
pc: 0u64,
predicted_next_pc: 0u64,
size_in_bytes: 0_hdl_u4,
kind: WipDecodedInsnKind.NonBranch(),
},
);
for entry in &state.queue {
ArrayVec::try_push_sim(&mut sim_queue, entry)
.ok()
.expect("queue is known to be small enough");
let _ = ArrayVec::try_push_sim(&mut next_insn_ids, &entry.insn.id);
let _ = ArrayVec::try_push_sim(&mut next_insns, &entry.insn);
}
sim.write(queue_debug, sim_queue).await;
sim.write(
retire_output.next_insn_ids,
retire_output.next_insns,
if state.canceling {
#[hdl(sim)]
(retire_output.next_insn_ids.ty()).HdlNone()
(retire_output.next_insns.ty()).HdlNone()
} else {
#[hdl(sim)]
(retire_output.next_insn_ids.ty()).HdlSome(next_insn_ids)
(retire_output.next_insns.ty()).HdlSome(next_insns)
},
)
.await;
@ -1310,6 +1410,8 @@ fn mock_execute_retire_pipe(
},
)
.await;
sim.write(from_post_decode.cancel.ready, state.canceling)
.await;
sim.wait_for_clock_edge(cd.clk).await;
println!(
"Dump mock execute retire pipe queue: {:#?}",
@ -1322,8 +1424,17 @@ fn mock_execute_retire_pipe(
))
}))
);
if state.canceling {
state.finish_cancel();
#[hdl(sim)]
if let HdlSome(v) = sim.read_past(from_post_decode.cancel.data, cd.clk).await {
#[hdl(sim)]
let () = v;
if sim
.read_past_bool(from_post_decode.cancel.ready, cd.clk)
.await
{
assert!(state.canceling);
state.finish_cancel();
}
}
if sim.read_past_bool(retire_output.inner.ready, cd.clk).await {
for _ in 0..**ArrayVec::len_sim(&retiring) {
@ -1339,7 +1450,7 @@ fn mock_execute_retire_pipe(
&mut new_insns,
*sim.read_past(from_post_decode.ready, cd.clk).await,
);
for insn in dbg!(ArrayVec::elements_sim_ref(&new_insns)) {
for insn in ArrayVec::elements_sim_ref(&new_insns) {
state.start(insn, delay_sequence_index);
}
}
@ -1430,9 +1541,9 @@ fn test_next_pc() {
};
sim.write_clock(sim.io().cd.clk, false);
sim.write_reset(sim.io().cd.rst, true);
for _cycle in 0..500 {
for cycle in 0..2000 {
sim.advance_time(SimDuration::from_nanos(500));
println!("clock tick");
println!("clock tick: {cycle}");
sim.write_clock(sim.io().cd.clk, true);
sim.advance_time(SimDuration::from_nanos(500));
sim.write_clock(sim.io().cd.clk, false);

View file

@ -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<ArrayVec<MOp, ConstUsize<2>>>,
loc: &'static std::panic::Location<'static>,
}
impl fmt::Debug for TestCase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
mnemonic,
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<TestCase> {
let mut retval = Vec::new();
#[track_caller]
fn insn_single(
mnemonic: &'static str,
input: u32,
output: impl ToSimValue<Type = MOp>,
) -> TestCase {
let zero_mop = UInt::new_dyn(MOp.canonical().bit_width())
.zero()
.cast_bits_to(MOp);
let mut single_storage = ArrayVec::new_sim(ArrayVec[MOp][ConstUsize], &zero_mop);
ArrayVec::try_push_sim(&mut single_storage, zero_mop).expect("known to have space");
ArrayVec::elements_sim_mut(&mut single_storage)[0] = output.to_sim_value();
TestCase {
mnemonic,
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::<UInt<_>>(),
MOpRegNum::CONST_ZERO_REG_NUM.cast_to_static::<UInt<_>>(),
],
0x1234.cast_to_static::<SInt<_>>(),
#[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-<num> in path")
.next()
.expect("can't find llvm-mc or llvm-mc-<num> in path");
let test_cases = test_cases();
let mut assembly = String::new();
for TestCase {
mnemonic,
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<RcWriter>,
}
impl Drop for DumpVcdOnDrop {
fn drop(&mut self) {
if let Some(mut writer) = self.writer.take() {
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
}
}
}
let mut writer = DumpVcdOnDrop {
writer: Some(writer),
};
for test_case 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
}