├── .github └── workflows │ ├── ci.yml │ └── static.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── app ├── Cargo.toml └── src │ └── main.rs ├── capture.gif ├── core ├── Cargo.toml ├── src │ ├── bus.rs │ ├── bus_interface.rs │ ├── clint.rs │ ├── cpu.rs │ └── lib.rs └── tests │ └── mod.rs ├── device_interfaces ├── Cargo.toml └── src │ ├── lib.rs │ ├── serial.rs │ └── timer.rs ├── devices ├── Cargo.toml └── src │ ├── keyboard.rs │ ├── lib.rs │ ├── timer.rs │ └── uart.rs ├── fixtures ├── baremetal │ ├── Makefile │ ├── baremetal.S │ ├── baremetal.bin │ ├── baremetal.c │ ├── baremetal.debug.txt │ ├── baremetal.elf │ └── flatfile.lds ├── default.dtb └── linux.bin ├── renovate.json ├── rust-toolchain ├── rustfmt.toml ├── wasi ├── Cargo.toml └── src │ └── main.rs └── wasm ├── Cargo.toml ├── index.html ├── out.wasm ├── src └── lib.rs ├── vendor ├── css │ └── xterm.css └── lib │ ├── xterm-addon-fit.js │ ├── xterm.js │ └── xterm.js.map └── worker.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: ci 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: dtolnay/rust-toolchain@stable 11 | with: 12 | toolchain: stable 13 | components: clippy 14 | - run: cargo check 15 | - run: cargo build -p app 16 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v3 32 | - name: Setup Pages 33 | uses: actions/configure-pages@v2 34 | - name: Upload artifact 35 | uses: actions/upload-pages-artifact@v1 36 | with: 37 | # Upload entire repository 38 | path: 'wasm' 39 | - name: Deploy to GitHub Pages 40 | id: deployment 41 | uses: actions/deploy-pages@v1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Generated by gibo (https://github.com/simonwhitaker/gibo) 2 | ### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Node.gitignore 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # Snowpack dependency directory (https://snowpack.dev/) 49 | web_modules/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Optional stylelint cache 61 | .stylelintcache 62 | 63 | # Microbundle cache 64 | .rpt2_cache/ 65 | .rts2_cache_cjs/ 66 | .rts2_cache_es/ 67 | .rts2_cache_umd/ 68 | 69 | # Optional REPL history 70 | .node_repl_history 71 | 72 | # Output of 'npm pack' 73 | *.tgz 74 | 75 | # Yarn Integrity file 76 | .yarn-integrity 77 | 78 | # dotenv environment variable files 79 | .env 80 | .env.development.local 81 | .env.test.local 82 | .env.production.local 83 | .env.local 84 | 85 | # parcel-bundler cache (https://parceljs.org/) 86 | .cache 87 | .parcel-cache 88 | 89 | # Next.js build output 90 | .next 91 | out 92 | 93 | # Nuxt.js build / generate output 94 | .nuxt 95 | dist 96 | 97 | # Gatsby files 98 | .cache/ 99 | # Comment in the public line in if your project uses Gatsby and not Next.js 100 | # https://nextjs.org/blog/next-9-1#public-directory-support 101 | # public 102 | 103 | # vuepress build output 104 | .vuepress/dist 105 | 106 | # vuepress v2.x temp and cache directory 107 | .temp 108 | .cache 109 | 110 | # Docusaurus cache and generated files 111 | .docusaurus 112 | 113 | # Serverless directories 114 | .serverless/ 115 | 116 | # FuseBox cache 117 | .fusebox/ 118 | 119 | # DynamoDB Local files 120 | .dynamodb/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | 136 | ### https://raw.github.com/github/gitignore/e5323759e387ba347a9d50f8b0ddd16502eb71d4/Rust.gitignore 137 | 138 | # Generated by Cargo 139 | # will have compiled files and executables 140 | debug/ 141 | target/ 142 | 143 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 144 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 145 | Cargo.lock 146 | 147 | # These are backup files generated by rustfmt 148 | **/*.rs.bk 149 | 150 | # MSVC Windows builds of rustc generate these, which store debugging information 151 | *.pdb 152 | 153 | 154 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "app", 5 | "core", 6 | "wasi", 7 | "wasm", 8 | "devices", 9 | "device_interfaces" 10 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R2 2 | 3 | [![GitHub Actions Status](https://github.com/bokuweb/r2/workflows/ci/badge.svg)](https://github.com/bokuweb/r2/actions) 4 | 5 | A RISC-V emulator written in Rust :crab:. 6 | Inspired [cnlohr/mini-rv32ima](https://github.com/cnlohr/mini-rv32ima). 7 | 8 | ## Capture 9 | 10 | You can run linux in your browser. 11 | 12 | ![capture](https://github.com/bokuweb/r2/blob/main/capture.gif?raw=true) 13 | 14 | ## Playground 15 | 16 | [https://bokuweb.github.io/r2/](https://bokuweb.github.io/r2/) 17 | 18 | ## Native 19 | 20 | ```sh 21 | $ cargo run -p app -- -i fixtures/linux.bin -d fixtures/default.dtb 22 | ``` 23 | 24 | ## WASI 25 | 26 | ```sh 27 | $ cargo build -p wasi --target wasm32-wasi --release 28 | $ wasmtime ./target/wasm32-wasi/release/wasi.wasm 29 | ``` 30 | 31 | ## Wasm 32 | 33 | ```sh 34 | $ cd wasm 35 | $ cargo build --target wasm32-unknown-unknown --release 36 | $ wasm-opt --asyncify --pass-arg=asyncify-imports@env.keydown ../target/wasm32-unknown-unknown/release/wasm.wasm -o out.wasm 37 | $ npx serve 38 | ``` 39 | 40 | ## Special Thanks 41 | 42 | - [cnlohr/mini-rv32ima](https://github.com/cnlohr/mini-rv32ima) 43 | - [Writing a Really Tiny RISC-V Emulator](https://www.youtube.com/watch?v=YT5vB3UqU_E) 44 | 45 | ## References 46 | 47 | - [https://github.com/torvalds/linux/tree/master/arch/riscv](https://github.com/torvalds/linux/tree/master/arch/riscv) 48 | - [https://www.five-embeddev.com/riscv-isa-manual/latest/machine.html](https://www.five-embeddev.com/riscv-isa-manual/latest/machine.html) 49 | - [https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf](https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf) 50 | - [https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf](https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf) 51 | 52 | ## License 53 | 54 | MIT 55 | -------------------------------------------------------------------------------- /app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.75" 10 | clap = { version = "4.4.11", features = ["derive"] } 11 | core = { path = "../core" } 12 | devices = { path = "../devices" } 13 | device_interfaces = { path = "../device_interfaces" } 14 | 15 | [[bin]] 16 | name = "core" 17 | path = "src/main.rs" 18 | -------------------------------------------------------------------------------- /app/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Read; 3 | use std::path::PathBuf; 4 | 5 | use anyhow::{bail, Result}; 6 | 7 | use core::{ 8 | bus::{Bus, RAM_START}, 9 | clint::Clint, 10 | start, 11 | }; 12 | 13 | use clap::Parser; 14 | 15 | #[derive(Parser, Debug)] 16 | #[command(author, version, about, long_about = None)] 17 | struct Args { 18 | #[arg(short, long)] 19 | /// Path to image file. 20 | image_file_path: PathBuf, 21 | 22 | #[arg(short, long)] 23 | /// Path to dtb file. 24 | dtb_file_path: Option, 25 | 26 | #[arg(short, long, default_value = "67108864")] 27 | /// RAM size. default 64 * 1024 * 1024. 28 | ram_size: usize, 29 | } 30 | 31 | fn main() -> Result<()> { 32 | let args = Args::parse(); 33 | 34 | let ram_size = args.ram_size; 35 | 36 | let mut ram = vec![0u8; ram_size]; 37 | 38 | let mut f = File::open(args.image_file_path)?; 39 | 40 | let len = f.metadata()?.len(); 41 | if len > ram_size as u64 { 42 | bail!("Insufficient RAM capacity. Please increase RAM capacity with `-r` option.") 43 | } 44 | 45 | f.read_exact(&mut ram[..len as usize])?; 46 | 47 | let dtb_ref = if let Some(dtb) = args.dtb_file_path { 48 | let mut f = File::open(dtb)?; 49 | let len = f.metadata()?.len(); 50 | let ptr = ram_size as u64 - len; 51 | f.read_exact(&mut ram[(ptr as usize)..(ptr + len) as usize])?; 52 | ptr as u32 + RAM_START 53 | } else { 54 | 0 55 | }; 56 | 57 | let clint = Clint::new(devices::timer::Timer::default()); 58 | let uart = devices::uart::Uart::new(); 59 | let bus = Bus::new(ram, clint, uart); 60 | 61 | start(bus, RAM_START, dtb_ref, &std::thread::sleep); 62 | 63 | Ok(()) 64 | } 65 | -------------------------------------------------------------------------------- /capture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/capture.gif -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | device_interfaces = { path = "../device_interfaces" } 10 | 11 | [lib] 12 | name = "core" 13 | path = "src/lib.rs" 14 | -------------------------------------------------------------------------------- /core/src/bus.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bus_interface::{BusController, BusException, BusReader, BusWriter}, 3 | clint::Clint, 4 | }; 5 | 6 | pub const RAM_START: u32 = 0x8000_0000; 7 | 8 | pub struct Bus { 9 | pub ram: Vec, 10 | pub clint: Clint, 11 | pub serial: S, 12 | pub power_off: bool, 13 | pub reboot: bool, 14 | } 15 | 16 | impl Bus { 17 | pub fn new(ram: Vec, clint: Clint, serial: S) -> Self { 18 | Self { 19 | ram, 20 | clint, 21 | serial, 22 | power_off: false, 23 | reboot: false, 24 | } 25 | } 26 | 27 | pub fn clint(&self) -> &Clint { 28 | &self.clint 29 | } 30 | 31 | pub fn replace_ram(&mut self, ram: Vec) -> Vec { 32 | std::mem::replace(&mut self.ram, ram) 33 | } 34 | } 35 | 36 | impl BusController for Bus 37 | where 38 | T: device_interfaces::TimerDriver, 39 | S: device_interfaces::SerialInterface, 40 | { 41 | fn step(&mut self, mip: &mut u32) { 42 | self.clint.step(mip); 43 | } 44 | 45 | fn power_off(&self) -> bool { 46 | self.power_off 47 | } 48 | 49 | fn reboot(&self) -> bool { 50 | self.reboot 51 | } 52 | } 53 | 54 | impl BusReader for Bus 55 | where 56 | T: device_interfaces::TimerDriver, 57 | S: device_interfaces::SerialInterface, 58 | { 59 | fn read8(&self, addr: u32) -> Result { 60 | match addr { 61 | 0x1100bffc => Ok(self.clint.read(addr) as u8), 62 | 0x1100bff8 => Ok(self.clint.read(addr) as u8), 63 | 0x10000000..=0x100000ff => { 64 | let addr = addr & 0xffff; 65 | Ok(self.serial.read(addr)) 66 | } 67 | 0x10000100..=0x12000000 => Ok(0), 68 | _ => { 69 | let addr = addr.wrapping_sub(RAM_START); 70 | Ok(self.ram[addr as usize]) 71 | } 72 | } 73 | } 74 | 75 | fn read16(&self, addr: u32) -> Result { 76 | if addr & 1 != 0 { 77 | return Err(BusException::LoadAddressMisaligned); 78 | } 79 | match addr { 80 | 0x1100bffc => Ok(self.clint.read(addr) as u16), 81 | 0x1100bff8 => Ok(self.clint.read(addr) as u16), 82 | 0x10000000..=0x100000ff => { 83 | let addr = addr & 0xffff; 84 | Ok(self.serial.read(addr) as u16) 85 | } 86 | 0x10000100..=0x12000000 => Ok(0), 87 | _ => { 88 | let addr = addr.wrapping_sub(RAM_START); 89 | Ok(u16::from_le_bytes([ 90 | self.ram[addr as usize], 91 | self.ram[addr as usize + 1], 92 | ])) 93 | } 94 | } 95 | } 96 | 97 | fn read32(&self, addr: u32) -> Result { 98 | if addr & 3 != 0 { 99 | return Err(BusException::LoadAddressMisaligned); 100 | } 101 | match addr { 102 | 0x1100bffc => Ok(self.clint.read(addr & 0xffff)), 103 | 0x1100bff8 => Ok(self.clint.read(addr & 0xffff)), 104 | 0x10000000..=0x100000ff => { 105 | let addr = addr & 0xffff; 106 | Ok(self.serial.read(addr) as u32) 107 | } 108 | 0x10000100..=0x12000000 => Ok(0), 109 | _ => { 110 | let addr = addr.wrapping_sub(RAM_START); 111 | Ok(u32::from_le_bytes([ 112 | self.ram[addr as usize], 113 | self.ram[addr as usize + 1], 114 | self.ram[addr as usize + 2], 115 | self.ram[addr as usize + 3], 116 | ])) 117 | } 118 | } 119 | } 120 | } 121 | 122 | impl BusWriter for Bus 123 | where 124 | T: device_interfaces::TimerDriver, 125 | S: device_interfaces::SerialInterface, 126 | { 127 | fn write8(&mut self, addr: u32, v: u8) -> Result<(), BusException> { 128 | match addr { 129 | // msip 130 | 0x11100000 => self.clint.write(addr & 0xffff, v as u32), 131 | // mtime 132 | 0x11004004 => self.clint.write(addr & 0xffff, v as u32), 133 | 0x11004000 => self.clint.write(addr & 0xffff, v as u32), 134 | 0x10000000..=0x100000ff => { 135 | let addr = addr & 0xffff; 136 | self.serial.write(addr, v as u32); 137 | } 138 | 0x10000100..=0x12000000 => {} 139 | _ => { 140 | let addr = addr.wrapping_sub(RAM_START); 141 | self.ram[addr as usize] = v; 142 | } 143 | }; 144 | Ok(()) 145 | } 146 | 147 | fn write16(&mut self, addr: u32, v: u16) -> Result<(), BusException> { 148 | if addr & 1 != 0 { 149 | return Err(BusException::StoreAddressMisaligned); 150 | } 151 | match addr { 152 | // syscon 153 | 0x11100000 if v == 0x5555 => self.power_off = true, 154 | 0x11100000 if v == 0x7777 => self.reboot = true, 155 | // msip 156 | 0x11100000 => self.clint.write(addr & 0xffff, v as u32), 157 | // mtime 158 | 0x11004004 => self.clint.write(addr & 0xffff, v as u32), 159 | 0x11004000 => self.clint.write(addr & 0xffff, v as u32), 160 | 0x10000000..=0x100000ff => { 161 | let addr = addr & 0xffff; 162 | self.serial.write(addr, v as u32); 163 | } 164 | 0x10000100..=0x12000000 => {} 165 | _ => { 166 | let addr = addr.wrapping_sub(RAM_START); 167 | self.ram[addr as usize..addr as usize + 2].copy_from_slice(&v.to_le_bytes()); 168 | } 169 | }; 170 | Ok(()) 171 | } 172 | 173 | fn write32(&mut self, addr: u32, v: u32) -> Result<(), BusException> { 174 | if addr & 3 != 0 { 175 | return Err(BusException::StoreAddressMisaligned); 176 | } 177 | match addr { 178 | // syscon 179 | 0x11100000 if v == 0x5555 => self.power_off = true, 180 | 0x11100000 if v == 0x7777 => self.reboot = true, 181 | // msip 182 | 0x11100000 => self.clint.write(addr & 0xffff, v), 183 | // mtime 184 | 0x11004004 => self.clint.write(addr & 0xffff, v), 185 | 0x11004000 => self.clint.write(addr & 0xffff, v), 186 | 0x10000000..=0x100000ff => { 187 | let addr = addr & 0xffff; 188 | self.serial.write(addr, v); 189 | } 190 | 0x10000100..=0x12000000 => {} 191 | _ => { 192 | let addr = addr.wrapping_sub(RAM_START); 193 | self.ram[addr as usize..addr as usize + 4].copy_from_slice(&v.to_le_bytes()); 194 | } 195 | }; 196 | Ok(()) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /core/src/bus_interface.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub enum BusException { 5 | LoadAddressMisaligned, 6 | LoadAccessFault, 7 | StoreAddressMisaligned, 8 | StoreAccessFault, 9 | } 10 | 11 | impl std::fmt::Display for BusException { 12 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 13 | match self { 14 | Self::LoadAddressMisaligned => write!(f, "LoadAddressMisaligned,"), 15 | Self::LoadAccessFault => write!(f, "LoadAccessFault,"), 16 | Self::StoreAddressMisaligned => write!(f, "StoreAddressMisaligned"), 17 | Self::StoreAccessFault => write!(f, "StoreAccessFault"), 18 | } 19 | } 20 | } 21 | 22 | impl Error for BusException { 23 | fn source(&self) -> Option<&(dyn Error + 'static)> { 24 | None 25 | } 26 | } 27 | 28 | pub trait BusController { 29 | fn step(&mut self, mip: &mut u32); 30 | fn power_off(&self) -> bool; 31 | fn reboot(&self) -> bool; 32 | } 33 | 34 | pub trait BusReader { 35 | fn read8(&self, addr: u32) -> Result; 36 | fn read16(&self, addr: u32) -> Result; 37 | fn read32(&self, addr: u32) -> Result; 38 | } 39 | 40 | pub trait BusWriter { 41 | fn write8(&mut self, addr: u32, v: u8) -> Result<(), BusException>; 42 | fn write16(&mut self, addr: u32, v: u16) -> Result<(), BusException>; 43 | fn write32(&mut self, addr: u32, v: u32) -> Result<(), BusException>; 44 | } 45 | -------------------------------------------------------------------------------- /core/src/clint.rs: -------------------------------------------------------------------------------- 1 | use device_interfaces::TimerDriver; 2 | 3 | /// Core-Local Interruptor (CLINT) 4 | /// https://sifive.cdn.prismic.io/sifive%2Fc89f6e5a-cf9e-44c3-a3db-04420702dcc1_sifive+e31+manual+v19.08.pdf 5 | /// https://chromitem-soc.readthedocs.io/en/latest/clint.html 6 | #[derive(Debug, Default)] 7 | pub struct Clint { 8 | /// Machine-mode software interrupts are generated by writing to the memory-mapped control register msip 9 | /// The msip register is a 32-bit wide WARL register where the upper 31 bits are tied to 0. 10 | /// The least significant bit can be used to drive the MSIP bit of the mip CSR of a RISC-V hart. 11 | /// Other bits in the msip register are hardwired to zero. On reset, the msip register is cleared to zero. 12 | pub msip: u32, 13 | /// This is a read-write register and holds a 64-bit value. 14 | /// A timer interrupt is pending whenever mtime is greater than or equal to the value in the mtimecmp register. 15 | /// The timer interrupt is used to drive the MTIP bit of the mip CSR of a RISC-V core. 16 | pub mtimecmp: u64, 17 | /// mtime is a 64-bit read-write register that keeps track of the number of cycles counted from an arbitrary 18 | /// point in time. It is a free-running counter which is incremented every tick_count number of cycles 19 | pub mtime: u64, 20 | /// timer driver. 21 | timer: T, 22 | } 23 | 24 | impl Clint { 25 | pub fn new(timer: T) -> Self { 26 | Self { 27 | msip: 0, 28 | mtimecmp: 0, 29 | mtime: 0, 30 | timer, 31 | } 32 | } 33 | 34 | pub fn step(&mut self, mip: &mut u32) { 35 | self.mtime += self.timer.as_micros(); 36 | // Handle Elasped interrupt. 37 | if self.mtimecmp != 0 && self.mtime >= self.mtimecmp { 38 | *mip |= 0x80; 39 | } else { 40 | *mip &= !(0x80); 41 | } 42 | } 43 | 44 | /// Read register content. 45 | pub fn read(&self, addr: u32) -> u32 { 46 | match addr { 47 | // MSIP 48 | 0x0000 => self.msip, 49 | // MTIMECMP 50 | 0x4000 => self.mtimecmp as u32, 51 | 0x4004 => self.mtimecmp.wrapping_shr(32) as u32, 52 | 0xbff8 => self.mtime as u32, 53 | 0xbffc => self.mtime.wrapping_shr(32) as u32, 54 | _ => unreachable!(), 55 | } 56 | } 57 | 58 | /// Write. 59 | pub fn write(&mut self, addr: u32, value: u32) { 60 | match addr { 61 | // MSIP 62 | 0x0000 => self.msip = (self.msip & !0x1) | value & 1, 63 | // MTIMECMP 64 | 0x4000 => self.mtimecmp = (self.mtimecmp & !0xffffffff) | (value as u64), 65 | 0x4004 => { 66 | self.mtimecmp = (self.mtimecmp & !(0xffffffff << 32)) | ((value as u64) << 32) 67 | } 68 | // MTIME registers 8 bytes 69 | 0xbff8 => self.mtime = (self.mtime & !0xffffffff) | (value as u64), 70 | 0xbffc => self.mtime = (self.mtime & !(0xffffffff << 32)) | ((value as u64) << 32), 71 | _ => unreachable!(), 72 | }; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use crate::bus_interface::{BusController, BusException, BusReader, BusWriter}; 2 | 3 | #[derive(Debug, Default)] 4 | pub struct Cpu { 5 | /// CPU bus 6 | bus: B, 7 | /// Registers 8 | x: [u32; 32], 9 | /// Program counter 10 | pc: u32, 11 | /// The mstatus register is an MXLEN-bit read/write register formatted as shown in Figure 1.6 12 | /// for RV64 and Figure 1.7 for RV32. The mstatus register keeps track of and controls 13 | /// the hart’s current operating state. 14 | mstatus: u32, 15 | cycle: u64, 16 | // @See. https://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#machine-level-csrs 17 | /// The mscratch register is an MXLEN-bit read/write register dedicated for use by machine mode. 18 | /// Typically, it is used to hold a pointer to a machine-mode hart-local context space and swapped 19 | /// with a user register upon entry to an M-mode trap handler. 20 | mscratch: u32, 21 | /// The mtvec register is an MXLEN-bit WARL read/write register that holds trap vector configuration, 22 | /// consisting of a vector base address (BASE) and a vector mode (MODE). 23 | mtvec: u32, 24 | /// The mie is the corresponding MXLEN-bit read/write register containing interrupt enable bits. 25 | mie: u32, 26 | /// The mip register is an MXLEN-bit read/write register containing information on pending interrupts. 27 | mip: u32, 28 | /// mepc is an MXLEN-bit read/write register formatted as shown in Figure 1.21. The low bit of mepc 29 | /// (mepc[0]) is always zero. On implementations that support only IALIGN=32, the two low bits (mepc[1:0]) 30 | /// are always zero. 31 | mepc: u32, 32 | /// Machine Trap Value Register (mtval) 33 | /// The mtval register is an MXLEN-bit read-write register formatted as shown in Figure 1.23. 34 | /// When a trap is taken into M-mode, mtval is either set to zero or written with 35 | /// exception-specific information to assist software in handling the trap. Otherwise, 36 | /// mtval is never written by the implementation, though it may be explicitly written by software. 37 | /// The hardware platform will specify which exceptions must set mtval informatively and which may 38 | /// unconditionally set it to zero. 39 | mtval: u32, 40 | /// Machine Cause Register (mcause) 41 | /// The mcause register is an MXLEN-bit read-write register formatted as shown in Figure 3.22. When 42 | /// a trap is taken into M-mode, mcause is written with a code indicating the event that caused the 43 | /// trap. Otherwise, mcause is never written by the implementation, though it may be explicitly 44 | /// written by software. 45 | mcause: u32, 46 | /// Exception code recoder. 47 | exception: u32, 48 | /// The Wait for Interrupt instruction (WFI) provides a hint to the implementation that 49 | /// the current hart can be stalled until an interrupt might need servicing. 50 | wait_for_interrupt: bool, 51 | /// Previous privilege mode. 52 | previous_mode: PrivilegeMode, 53 | /// This is used to reserve addresses for LR/SC 54 | reserved_load_addresses: std::collections::HashMap, 55 | /// It is used to record exception reason for mtval 56 | cause: u32, 57 | } 58 | 59 | impl Cpu { 60 | pub fn new(bus: B) -> Self { 61 | Self { 62 | x: [0; 32], 63 | pc: 0, 64 | mstatus: 0, 65 | cycle: 0, 66 | mscratch: 0, 67 | mtvec: 0, 68 | mie: 0, 69 | mip: 0, 70 | mepc: 0, 71 | mtval: 0, 72 | mcause: 0, 73 | bus, 74 | exception: 0, 75 | wait_for_interrupt: false, 76 | previous_mode: PrivilegeMode::Machine, 77 | reserved_load_addresses: std::collections::HashMap::new(), 78 | cause: 0, 79 | } 80 | } 81 | 82 | pub fn a0(&mut self, a0: u32) -> &mut Self { 83 | self.x[10] = a0; 84 | self 85 | } 86 | 87 | pub fn a1(&mut self, a1: u32) -> &mut Self { 88 | self.x[11] = a1; 89 | self 90 | } 91 | 92 | pub fn pc(&mut self, pc: u32) -> &mut Self { 93 | self.pc = pc; 94 | self 95 | } 96 | } 97 | 98 | // @See https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf p39 99 | #[derive(Debug, PartialEq)] 100 | #[allow(dead_code)] 101 | enum Exception { 102 | // 0x0: Instruction address misaligned 103 | InstructionAddressMisaligned = 0x0, 104 | // 0x1: Instruction access fault 105 | InstructionAccessFault = 0x1, 106 | // 0x2: Illegal instruction 107 | IllegalInstruction = 0x2, 108 | // 0x3: Breakpoint 109 | Breakpoint = 0x3, 110 | // 0x4: Load address misaligned 111 | LoadAddressMisaligned = 0x4, 112 | // 0x5: Load access fault 113 | LoadAccessFault = 0x5, 114 | // 0x6: Store/AMO address misaligned 115 | StoreAmoAddressMisaligned = 0x6, 116 | // 0x7: Store/AMO access fault 117 | StoreAmoAccessFault = 0x7, 118 | // 0x8: Environment call from U-mode 119 | EnvironmentCallUmode = 0x8, 120 | // 0x9: Environment call from S-mode 121 | EnvironmentCallSmode = 0x9, 122 | // 0xB: Environment call from M-mode 123 | EnvironmentCallMmode = 0xB, 124 | // 0xC: Instruction page fault 125 | InstructionPageFault = 0xC, 126 | // 0xD: Load page fault 127 | LoadPageFault = 0xD, 128 | // 0xF: Store/AMO page fault 129 | StoreAmoPageFault = 0xF, 130 | } 131 | 132 | impl From for Exception { 133 | fn from(value: BusException) -> Self { 134 | match value { 135 | BusException::LoadAccessFault => Exception::LoadAccessFault, 136 | BusException::LoadAddressMisaligned => Exception::LoadAddressMisaligned, 137 | BusException::StoreAccessFault => Exception::StoreAmoAccessFault, 138 | BusException::StoreAddressMisaligned => Exception::StoreAmoAddressMisaligned, 139 | } 140 | } 141 | } 142 | 143 | impl From for u32 { 144 | fn from(value: Exception) -> Self { 145 | value as u32 146 | } 147 | } 148 | 149 | /// @See https://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause 150 | #[derive(Debug, PartialEq, Clone, Copy)] 151 | #[allow(dead_code)] 152 | enum Interrupt { 153 | MachineTimerInterrupt, 154 | } 155 | 156 | impl From for u32 { 157 | fn from(value: Interrupt) -> Self { 158 | match value { 159 | Interrupt::MachineTimerInterrupt => 0x8000_0007, 160 | } 161 | } 162 | } 163 | 164 | #[derive(Debug, PartialEq, Clone, Copy)] 165 | pub enum PrivilegeMode { 166 | User, 167 | SuperVisor, 168 | Reserved, 169 | Machine, 170 | } 171 | 172 | impl Default for PrivilegeMode { 173 | fn default() -> Self { 174 | Self::Machine 175 | } 176 | } 177 | 178 | impl From for u32 { 179 | fn from(value: PrivilegeMode) -> Self { 180 | match value { 181 | PrivilegeMode::User => 0, 182 | PrivilegeMode::SuperVisor => 1, 183 | PrivilegeMode::Reserved => 2, 184 | PrivilegeMode::Machine => 3, 185 | } 186 | } 187 | } 188 | 189 | impl From for PrivilegeMode { 190 | fn from(value: u32) -> Self { 191 | match value { 192 | 0b00 => PrivilegeMode::User, 193 | 0b01 => PrivilegeMode::SuperVisor, 194 | 0b11 => PrivilegeMode::Machine, 195 | _ => PrivilegeMode::Reserved, 196 | } 197 | } 198 | } 199 | 200 | mod helpers { 201 | pub(crate) fn rd(ir: u32) -> usize { 202 | ((ir >> 7) & 0x1f) as usize 203 | } 204 | 205 | pub(crate) fn rs1(ir: u32) -> usize { 206 | ((ir >> 15) & 0x1f) as usize 207 | } 208 | } 209 | 210 | #[derive(Debug, Copy, Clone)] 211 | pub enum CpuState { 212 | Idle, 213 | Active, 214 | } 215 | 216 | impl Cpu { 217 | pub fn add_cycles(&mut self, count: u32) { 218 | self.cycle = self.cycle.wrapping_add(count as u64); 219 | } 220 | 221 | pub fn bus(&self) -> &B { 222 | &self.bus 223 | } 224 | 225 | pub fn step(&mut self) -> CpuState { 226 | // Drive bus state 227 | self.bus.step(&mut self.mip); 228 | 229 | // If interrupted 230 | // MTIP(bit 7):Machine timer interrupt pending 231 | if self.mip & 0x80 != 0 { 232 | self.wait_for_interrupt = false; 233 | } else if self.wait_for_interrupt { 234 | return CpuState::Idle; 235 | } 236 | 237 | // bit3 in mstatus is MIE: Machine Interrupt Enable 238 | if (self.mip & 0x80 != 0) && (self.mie & 0x80 != 0) && (self.mstatus & 0x8 != 0) { 239 | self.exception = Interrupt::MachineTimerInterrupt.into(); 240 | self.process_exception(); 241 | return CpuState::Active; 242 | } 243 | 244 | self.cycle = self.cycle.wrapping_add(1); 245 | 246 | let Ok(ir) = self.bus.read32(self.pc) else { 247 | self.record_exception(Exception::InstructionAddressMisaligned, self.pc); 248 | self.process_exception(); 249 | return CpuState::Active; 250 | }; 251 | 252 | match ir & 0x7f { 253 | 0b0110111 => self.write_back(helpers::rd(ir), ir & 0xfffff000), // LUI 254 | 0b0010111 => { 255 | self.write_back(helpers::rd(ir), self.pc.wrapping_add(ir & 0xfffff000)) 256 | // AUIPC 257 | } 258 | 0b1101111 => self.jal(ir), // JAL 259 | 0b1100111 => self.jalr(ir), // JALR 260 | 0b1100011 => self.branch(ir), // Branch 261 | 0b0000011 => self.load(ir), // Load 262 | 0b0100011 => self.store(ir), // Store 263 | 0b0110011 if (ir & 0x02000000) != 0 && (ir & 0b100000) != 0 => { 264 | self.multi_or_div(ir) // RV32M 265 | } 266 | 0b0010011 | 0b0110011 => self.op(ir), // Op 267 | 0b0001111 => {} // Fence.i, NOP in this emulator. 268 | 0b1110011 => { 269 | let op = (ir >> 12) & 0b111; 270 | // system 271 | if op == 0 { 272 | self.system(ir); 273 | if self.wait_for_interrupt { 274 | return CpuState::Idle; 275 | } 276 | } else { 277 | // Zicsr 278 | self.zicsr(ir); 279 | } 280 | } 281 | 0b0101111 => self.atomic(ir), // RV32A 282 | _ => { 283 | self.record_exception(Exception::IllegalInstruction, ir); 284 | } 285 | } 286 | 287 | if self.exception != 0 { 288 | self.process_exception(); 289 | return CpuState::Active; 290 | } 291 | 292 | self.pc += 4; 293 | self.process_exception(); 294 | CpuState::Active 295 | } 296 | 297 | fn record_exception(&mut self, e: Exception, cause: u32) { 298 | // When a hardware breakpoint is triggered, or an address-misaligned, access-fault, or page-fault exception 299 | // occurs on an instruction fetch, load, or store, mtval is written with the faulting virtual address. 300 | // On an illegal instruction trap, mtval may be written with the first XLEN or ILEN bits of the faulting instruction 301 | // as described below. For other traps, mtval is set to zero, but a future standard may redefine 302 | // mtval’s setting for other traps. 303 | self.exception = e.into(); 304 | self.cause = cause; 305 | } 306 | 307 | fn process_exception(&mut self) { 308 | if self.exception != 0 { 309 | // Interrupt 310 | if self.exception & 0x80000000 != 0 { 311 | self.mcause = self.exception; 312 | self.mtval = 0; 313 | } else { 314 | // Exception 315 | self.mcause = self.exception; 316 | self.mtval = self.cause; 317 | } 318 | self.mepc = self.pc; 319 | let prev: u32 = self.previous_mode.into(); 320 | self.mstatus = (((self.mstatus) & 0x08) << 4) | (prev << 11); 321 | self.pc = self.mtvec; 322 | self.previous_mode = PrivilegeMode::Machine; 323 | self.exception = 0; 324 | } 325 | } 326 | 327 | fn write_back(&mut self, rd: usize, v: u32) { 328 | if rd != 0 { 329 | self.x[rd] = v 330 | } 331 | } 332 | 333 | fn jal(&mut self, ir: u32) { 334 | let rd = helpers::rd(ir); 335 | let rel = ((ir & 0x80000000) >> 11) 336 | | ((ir & 0x7fe00000) >> 20) 337 | | ((ir & 0x00100000) >> 9) 338 | | (ir & 0x000ff000); 339 | let rel = if rel & 0x00100000 != 0 { rel | 0xffe00000 } else { rel } as i32; 340 | let v = self.pc + 4; 341 | self.pc = (self.pc as i64 + rel as i64 - 4) as u32; 342 | self.write_back(rd, v); 343 | } 344 | 345 | fn jalr(&mut self, ir: u32) { 346 | let rd = helpers::rd(ir); 347 | let imm = ir >> 20; 348 | let imm_s = imm | if (imm & 0x800) != 0 { 0xfffff000 } else { 0 }; 349 | let v = self.pc + 4; 350 | let rs1 = self.x[helpers::rs1(ir)]; 351 | self.pc = (((rs1 as i64 + imm_s as i32 as i64) & !1) - 4) as u32; 352 | self.write_back(rd, v) 353 | } 354 | 355 | fn branch(&mut self, ir: u32) { 356 | // Branch 357 | let immm4 = ((ir & 0xf00) >> 7) 358 | | ((ir & 0x7e000000) >> 20) 359 | | ((ir & 0x80) << 4) 360 | | ((ir >> 31) << 12); 361 | let immm4 = if immm4 & 0x1000 != 0 { immm4 | 0xffffe000 } else { immm4 }; 362 | let immm4 = (self.pc as i64 + immm4 as i32 as i64 - 4) as u32; 363 | let rs1 = self.x[helpers::rs1(ir)] as i32; 364 | let rs2 = self.x[((ir >> 20) & 0x1f) as usize] as i32; 365 | match (ir >> 12) & 0x7 { 366 | // BEQ, BNE, BLT, BGE, BLTU, BGEU 367 | 0b000 => (rs1 == rs2).then(|| self.pc = immm4), 368 | 0b001 => (rs1 != rs2).then(|| self.pc = immm4), 369 | 0b100 => (rs1 < rs2).then(|| self.pc = immm4), 370 | 0b101 => (rs1 >= rs2).then(|| self.pc = immm4), 371 | 0b110 => ((rs1 as u32) < (rs2 as u32)).then(|| self.pc = immm4), 372 | 0b111 => ((rs1 as u32) >= (rs2 as u32)).then(|| self.pc = immm4), 373 | _ => { 374 | self.record_exception(Exception::IllegalInstruction, ir); 375 | None 376 | } 377 | }; 378 | } 379 | 380 | fn load(&mut self, ir: u32) { 381 | let rd = helpers::rd(ir); 382 | let rs1 = self.x[helpers::rs1(ir)]; 383 | let imm = ir >> 20; 384 | let imm_s = (imm | (if (imm & 0x800) != 0 { 0xfffff000 } else { 0 })) as i32; 385 | let rsval = (rs1 as i64 + imm_s as i64) as u32; 386 | 387 | match (ir >> 12) & 0x7 { 388 | // LB, LH, LW, LBU, LHU 389 | 0b000 => match self.bus.read8(rsval) { 390 | Ok(v) => self.x[rd] = (v as i8) as u32, 391 | Err(e) => self.record_exception(e.into(), rsval), 392 | }, 393 | 0b001 => match self.bus.read16(rsval) { 394 | Ok(v) => self.x[rd] = (v as i16) as u32, 395 | Err(e) => self.record_exception(e.into(), rsval), 396 | }, 397 | 0b010 => match self.bus.read32(rsval) { 398 | Ok(v) => self.x[rd] = v, 399 | Err(e) => self.record_exception(e.into(), rsval), 400 | }, 401 | 0b100 => match self.bus.read8(rsval) { 402 | Ok(v) => self.x[rd] = v as u32, 403 | Err(e) => self.record_exception(e.into(), rsval), 404 | }, 405 | 0b101 => match self.bus.read16(rsval) { 406 | Ok(v) => self.x[rd] = v as u32, 407 | Err(e) => self.record_exception(e.into(), rsval), 408 | }, 409 | _ => { 410 | self.record_exception(Exception::IllegalInstruction, ir); 411 | } 412 | } 413 | } 414 | 415 | fn store(&mut self, ir: u32) { 416 | let rs1 = self.x[helpers::rs1(ir)]; 417 | let rs2 = self.x[((ir >> 20) & 0x1f) as usize]; 418 | let mut addr = ((ir >> 7) & 0x1f) | ((ir & 0xfe000000) >> 20); 419 | if addr & 0x800 != 0 { 420 | addr |= 0xfffff000; 421 | } 422 | let addr = addr.wrapping_add(rs1); 423 | 424 | match (ir >> 12) & 0x7 { 425 | // SB, SH, SW 426 | 0b000 => { 427 | self.bus 428 | .write8(addr, rs2 as u8) 429 | .unwrap_or_else(|e| self.record_exception(e.into(), addr)); 430 | } 431 | 0b001 => { 432 | self.bus 433 | .write16(addr, rs2 as u16) 434 | .unwrap_or_else(|e| self.record_exception(e.into(), addr)); 435 | } 436 | 0b010 => { 437 | self.bus 438 | .write32(addr, rs2) 439 | .unwrap_or_else(|e| self.record_exception(e.into(), addr)); 440 | } 441 | _ => { 442 | self.record_exception(Exception::IllegalInstruction, ir); 443 | } 444 | }; 445 | } 446 | 447 | // RV32A 448 | fn atomic(&mut self, ir: u32) { 449 | let rd = helpers::rd(ir); 450 | let rs1 = self.x[helpers::rs1(ir)]; 451 | let mut rs2 = self.x[((ir >> 20) & 0x1f) as usize]; 452 | let f = (ir >> 27) & 0x1f; 453 | 454 | let v = match self.bus.read32(rs1) { 455 | Ok(v) => v, 456 | Err(e) => { 457 | self.record_exception(e.into(), rs1); 458 | return; 459 | } 460 | }; 461 | 462 | match f { 463 | // LR.W 464 | // Load-Reserved Word 465 | 0b00010 => { 466 | self.reserved_load_addresses.insert(rs1, v); 467 | self.write_back(rd, v) 468 | } 469 | // SC.W 470 | // Store-Conditional Word 471 | 0b00011 => { 472 | if let Some(val) = self.reserved_load_addresses.get(&rs1) { 473 | if *val == v { 474 | self.bus 475 | .write32(rs1, rs2) 476 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 477 | self.write_back(rd, 0); 478 | } else { 479 | self.write_back(rd, 1); 480 | } 481 | } else { 482 | self.write_back(rd, 1); 483 | } 484 | } 485 | // AMOSWAP.W 486 | 0b00001 => { 487 | self.bus 488 | .write32(rs1, rs2) 489 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 490 | self.write_back(rd, v); 491 | } 492 | 0b00000 => { 493 | rs2 = rs2.wrapping_add(v); 494 | self.bus 495 | .write32(rs1, rs2) 496 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 497 | self.write_back(rd, v) 498 | } 499 | // AMOXOR.W 500 | 0b00100 => { 501 | rs2 ^= v; 502 | self.bus 503 | .write32(rs1, rs2) 504 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 505 | self.write_back(rd, v) 506 | } 507 | // AMOAND.W 508 | 0b01100 => { 509 | rs2 &= v; 510 | self.bus 511 | .write32(rs1, rs2) 512 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 513 | self.write_back(rd, v) 514 | } 515 | // AMOOR.W 516 | 0b01000 => { 517 | rs2 |= v; 518 | self.bus 519 | .write32(rs1, rs2) 520 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 521 | self.write_back(rd, v) 522 | } 523 | // AMOMIN.W 524 | 0b10000 => { 525 | rs2 = if (rs2 as i32) < (v as i32) { rs2 } else { v }; 526 | self.bus 527 | .write32(rs1, rs2) 528 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 529 | self.write_back(rd, v) 530 | } 531 | // AMOMAX.W 532 | 0b10100 => { 533 | rs2 = if (rs2 as i32) > (v as i32) { rs2 } else { v }; 534 | self.bus 535 | .write32(rs1, rs2) 536 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 537 | self.write_back(rd, v) 538 | } 539 | // AMOMINU.W 540 | 0b11000 => { 541 | rs2 = if rs2 < v { rs2 } else { v }; 542 | self.bus 543 | .write32(rs1, rs2) 544 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 545 | self.write_back(rd, v) 546 | } 547 | // AMOMAXU.W 548 | 0b11100 => { 549 | rs2 = if rs2 > v { rs2 } else { v }; 550 | self.bus 551 | .write32(rs1, rs2) 552 | .unwrap_or_else(|e| self.record_exception(e.into(), rs1)); 553 | self.write_back(rd, v) 554 | } 555 | _ => { 556 | self.record_exception(Exception::IllegalInstruction, ir); 557 | } 558 | } 559 | } 560 | 561 | // RV32M 562 | fn multi_or_div(&mut self, ir: u32) { 563 | let rd = helpers::rd(ir); 564 | let imm = ir >> 20; 565 | let imm = imm | if (imm & 0x800) != 0 { 0xfffff000 } else { 0 }; 566 | let rs1 = self.x[helpers::rs1(ir)]; 567 | let is_reg = (ir & 0b100000) != 0; 568 | let rs2 = if is_reg { self.x[imm as usize & 0x1f] } else { imm }; 569 | 570 | let mut v = 0; 571 | match (ir >> 12) & 7 { 572 | 0b000 => v = rs1.wrapping_mul(rs2), // MUL 573 | 0b001 => v = ((rs1 as i32 as i64).wrapping_mul(rs2 as i32 as i64) >> 32) as u32, // MULH 574 | 0b010 => v = ((rs1 as i32 as i64).wrapping_mul(rs2 as i64) >> 32) as u32, // MULHSU 575 | 0b011 => v = ((rs1 as u64).wrapping_mul(rs2 as u64) >> 32) as u32, // MULHU 576 | 0b100 => v = if rs2 == 0 { !0 } else { (rs1).wrapping_div(rs2) }, // DIV 577 | 0b101 => v = if rs2 == 0 { u32::MAX } else { rs1 / rs2 }, // DIVU 578 | 0b110 if rs2 == 0 => v = 0, // REM 579 | 0b110 => v = (rs1 as i32).wrapping_rem(rs2 as i32) as u32, // REM 580 | 0b111 => v = if rs2 == 0 { rs1 } else { rs1 % rs2 }, // REMU 581 | _ => { 582 | self.record_exception(Exception::IllegalInstruction, ir); 583 | } 584 | } 585 | self.write_back(rd, v); 586 | } 587 | 588 | // Op 589 | fn op(&mut self, ir: u32) { 590 | let rd = helpers::rd(ir); 591 | let imm = ir >> 20; 592 | let imm = imm | if (imm & 0x800) != 0 { 0xfffff000 } else { 0 }; 593 | let rs1 = self.x[helpers::rs1(ir)]; 594 | let reg = (ir & 0b100000) != 0; 595 | let rs2 = if reg { self.x[imm as usize & 0x1f] } else { imm }; 596 | 597 | let v = match (ir >> 12) & 7 { 598 | 0b000 if reg && (ir & 0x4000_0000) != 0 => rs1.wrapping_sub(rs2), 599 | 0b000 => rs1.wrapping_add(rs2), 600 | 0b001 => rs1 << (rs2 & 0x1f), 601 | 0b010 => ((rs1 as i32) < (rs2 as i32)) as u32, 602 | 0b011 => (rs1 < rs2) as u32, 603 | 0b100 => rs1 ^ rs2, 604 | 0b101 if (ir & 0x40000000) != 0 => ((rs1 as i32) >> (rs2 & 0x1f)) as u32, 605 | 0b101 => rs1 >> (rs2 & 0x1f), 606 | 0b110 => rs1 | rs2, 607 | 0b111 => rs1 & rs2, 608 | _ => { 609 | self.record_exception(Exception::IllegalInstruction, ir); 610 | 0 611 | } 612 | }; 613 | self.write_back(rd, v) 614 | } 615 | 616 | fn zicsr(&mut self, ir: u32) { 617 | // Zicsr 618 | let rd = helpers::rd(ir); 619 | let mut v = 0; 620 | let csr = ir >> 20; 621 | let op = (ir >> 12) & 0b111; 622 | if (op & 3) != 0 { 623 | let rs1imm = (ir >> 15) & 0x1f; 624 | let rs1 = self.x[rs1imm as usize]; 625 | let mut val = rs1; 626 | // https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf 627 | // Generally, support for Zicsr 628 | match csr { 629 | 0x340 => v = self.mscratch, 630 | 0x305 => v = self.mtvec, 631 | 0x304 => v = self.mie, 632 | 0xC00 => v = self.cycle as u32, 633 | 0x341 => v = self.mepc, 634 | 0x300 => v = self.mstatus, 635 | 0x342 => v = self.mcause, 636 | 0x343 => v = self.mtval, 637 | 0xf11 => v = 0x00000000, // mvendorid 638 | 0x301 => v = 0x00000000, // misa 639 | _ => {} 640 | } 641 | 642 | match op { 643 | 0b001 => val = rs1, //CSRRW 644 | 0b010 => val = v | rs1, //CSRRS 645 | 0b011 => val = v & !rs1, //CSRRC 646 | 0b101 => val = rs1imm, //CSRRWI 647 | 0b110 => val = v | rs1imm, //CSRRSI 648 | 0b111 => val = v & !rs1imm, //CSRRCI 649 | _ => {} 650 | } 651 | 652 | match csr { 653 | 0x340 => self.mscratch = val, 654 | 0x305 => self.mtvec = val, 655 | 0x304 => self.mie = val, 656 | 0x344 => self.mip = val, 657 | 0x341 => self.mepc = val, 658 | 0x300 => self.mstatus = val, 659 | 0x342 => self.mcause = val, 660 | 0x343 => self.mtval = val, 661 | _ => {} 662 | } 663 | } else { 664 | self.record_exception(Exception::IllegalInstruction, ir); 665 | } 666 | self.write_back(rd, v); 667 | } 668 | 669 | // system 670 | fn system(&mut self, ir: u32) { 671 | let csr = ir >> 20; 672 | if csr == 0x105 { 673 | //WFI 674 | self.mstatus |= 8; 675 | self.wait_for_interrupt = true; //Inform environment we want to go to sleep. 676 | self.pc += 4; 677 | } else if (csr & 0xff) == 0x02 { 678 | // MRET 679 | let prev_mstatus = self.mstatus; 680 | let prev_mode: u32 = self.previous_mode.into(); 681 | self.mstatus = ((prev_mstatus & 0x80) >> 4) | (prev_mode << 11) | 0x80; 682 | self.previous_mode = PrivilegeMode::from(prev_mstatus >> 11); 683 | self.pc = self.mepc - 4; 684 | } else { 685 | match csr { 686 | 0 if self.previous_mode != PrivilegeMode::User => { 687 | self.exception = Exception::EnvironmentCallMmode.into() 688 | } 689 | 0 => self.exception = Exception::EnvironmentCallUmode.into(), 690 | 1 => self.exception = Exception::Breakpoint.into(), 691 | _ => self.record_exception(Exception::IllegalInstruction, ir), 692 | } 693 | } 694 | } 695 | } 696 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bus; 2 | pub mod bus_interface; 3 | pub mod clint; 4 | pub mod cpu; 5 | 6 | use bus_interface::{BusController, BusReader, BusWriter}; 7 | use cpu::{Cpu, CpuState}; 8 | 9 | pub fn start( 10 | bus: B, 11 | pc: u32, 12 | dtb_ref: u32, 13 | sleep: &dyn Fn(std::time::Duration), 14 | ) { 15 | 'reboot: { 16 | let mut core = Cpu::new(bus); 17 | 18 | // https://github.com/torvalds/linux/blob/89d77f71f493a3663b10fa812d17f472935d24be/arch/riscv/kernel/head.S#LL153C1-L153C1 19 | // Pass hart id and ref to dtb. 20 | core.a0(0x00) // hart id 21 | .a1(dtb_ref) // ref to dtb 22 | .pc(pc); 23 | 24 | loop { 25 | match core.step() { 26 | CpuState::Active if core.bus().power_off() => return, 27 | CpuState::Active if core.bus().reboot() => break 'reboot, 28 | CpuState::Idle => { 29 | sleep(core::time::Duration::from_micros(100)); 30 | core.add_cycles(1); 31 | } 32 | _ => {} 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn baremetal() {} 5 | } 6 | -------------------------------------------------------------------------------- /device_interfaces/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "device_interfaces" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /device_interfaces/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod serial; 2 | mod timer; 3 | 4 | pub use serial::*; 5 | pub use timer::*; 6 | -------------------------------------------------------------------------------- /device_interfaces/src/serial.rs: -------------------------------------------------------------------------------- 1 | pub trait SerialInterface { 2 | fn read(&self, addr: u32) -> u8; 3 | 4 | fn write(&self, addr: u32, v: u32); 5 | } 6 | -------------------------------------------------------------------------------- /device_interfaces/src/timer.rs: -------------------------------------------------------------------------------- 1 | pub trait TimerDriver { 2 | fn as_micros(&self) -> u64; 3 | } 4 | -------------------------------------------------------------------------------- /devices/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "devices" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | libc = "0.2" 10 | device_interfaces = { path = "../device_interfaces" } 11 | -------------------------------------------------------------------------------- /devices/src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use libc::{poll, pollfd, read, POLLIN}; 2 | use std::os::fd::AsRawFd; 3 | 4 | pub fn read_kb_byte() -> u32 { 5 | let stdin_fd = std::io::stdin().as_raw_fd(); 6 | let mut buffer: [u8; 1] = [0]; 7 | let result = unsafe { read(stdin_fd, buffer.as_mut_ptr() as *mut libc::c_void, 1) }; 8 | 9 | if result > 0 { 10 | buffer[0] as u32 11 | } else { 12 | 0 13 | } 14 | } 15 | 16 | pub fn is_kb_hit() -> bool { 17 | let stdin_fd = std::io::stdin().as_raw_fd(); 18 | let mut fds = pollfd { 19 | fd: stdin_fd, 20 | events: POLLIN, 21 | revents: 0, 22 | }; 23 | 24 | let timeout = 0; // No timeout, return immediately 25 | let result = unsafe { poll(&mut fds, 1, timeout) }; 26 | 27 | result > 0 && fds.revents & POLLIN != 0 28 | } 29 | -------------------------------------------------------------------------------- /devices/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod keyboard; 2 | pub mod timer; 3 | pub mod uart; 4 | 5 | -------------------------------------------------------------------------------- /devices/src/timer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct Timer { 3 | last: std::cell::RefCell, 4 | } 5 | 6 | impl Default for Timer { 7 | fn default() -> Self { 8 | Self { 9 | last: std::cell::RefCell::new(std::time::Instant::now()), 10 | } 11 | } 12 | } 13 | 14 | impl device_interfaces::TimerDriver for Timer { 15 | fn as_micros(&self) -> u64 { 16 | let now = std::time::Instant::now(); 17 | let duration = { 18 | let last = self.last.borrow(); 19 | now.duration_since(*last) 20 | }; 21 | *self.last.borrow_mut() = now; 22 | duration.as_micros() as u64 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /devices/src/uart.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use crate::keyboard::{is_kb_hit, read_kb_byte}; 4 | 5 | #[derive(Debug, Default)] 6 | pub struct Uart; 7 | 8 | impl Uart { 9 | pub fn new() -> Self { 10 | Self::default() 11 | } 12 | } 13 | 14 | impl device_interfaces::SerialInterface for Uart { 15 | fn read(&self, addr: u32) -> u8 { 16 | match addr { 17 | 0x0005 => 0x60 | if is_kb_hit() { 1 } else { 0 }, 18 | 0x0000 if is_kb_hit() => read_kb_byte() as u8, 19 | _ => 0, 20 | } 21 | } 22 | 23 | fn write(&self, addr: u32, v: u32) { 24 | if addr == 0x000 { 25 | let c = char::from_u32(v).expect("failed to Convert char from u32."); 26 | print!("{}", c); 27 | std::io::stdout().flush().expect("failed to flush stdout."); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fixtures/baremetal/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT:=baremetal 2 | TARGETS:=$(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt 3 | 4 | all : $(TARGETS) 5 | 6 | #PREFIX:=riscv64-unknown-elf- 7 | #CFLAGS:=-I/usr/include 8 | 9 | PREFIX:=../buildroot/output/host/bin/riscv32-buildroot-linux-uclibc- 10 | CFLAGS:=-fno-stack-protector 11 | CFLAGS+=-static-libgcc -fdata-sections -ffunction-sections 12 | CFLAGS+=-g -Os -march=rv32ima -mabi=ilp32 -static 13 | LDFLAGS:= -T flatfile.lds -nostdlib -Wl,--gc-sections 14 | 15 | $(PROJECT).elf : $(PROJECT).c $(PROJECT).S 16 | $(PREFIX)gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) 17 | 18 | $(PROJECT).debug.txt : $(PROJECT).elf 19 | $(PREFIX)objdump -t $^ > $@ 20 | $(PREFIX)objdump -S $^ >> $@ 21 | 22 | $(PROJECT).bin : $(PROJECT).elf 23 | $(PREFIX)objcopy $^ -O binary $@ 24 | 25 | test : $(PROJECT).bin 26 | ../mini-rv32ima/mini-rv32ima -f $< 27 | 28 | clean : 29 | rm -rf $(TARGETS) 30 | 31 | -------------------------------------------------------------------------------- /fixtures/baremetal/baremetal.S: -------------------------------------------------------------------------------- 1 | 2 | .section .initial_jump 3 | .global _start 4 | .global asm_demo_func 5 | 6 | .align 4 7 | _start: 8 | la sp, _sstack 9 | addi sp,sp,-16 10 | sw ra,12(sp) 11 | jal ra, main 12 | 13 | 14 | .section .data 15 | 16 | .align 4 17 | asm_label: 18 | .ascii "I'm an assembly function." 19 | .byte 0 20 | 21 | .section .text 22 | 23 | .align 4 24 | asm_demo_func: 25 | addi sp, sp, -16 26 | sw t0, 12(sp) 27 | la t0, asm_label 28 | csrw 0x138, t0 29 | lw t0, 12(sp) 30 | addi sp, sp, 16 31 | ret 32 | 33 | -------------------------------------------------------------------------------- /fixtures/baremetal/baremetal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/fixtures/baremetal/baremetal.bin -------------------------------------------------------------------------------- /fixtures/baremetal/baremetal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // These are going to be bound to memory addresses in the linker script. 6 | extern uint32_t SYSCON; 7 | extern uint32_t TIMERL; 8 | 9 | // This is just a definition for a symbol found in the .S file. 10 | void asm_demo_func(); 11 | 12 | // These will not turn into function calls, but instead will find a way 13 | // of writing the assembly in-line 14 | static void lprint( const char * s ) 15 | { 16 | asm volatile( ".option norvc\ncsrrw x0, 0x138, %0\n" : : "r" (s)); 17 | } 18 | 19 | static void pprint( intptr_t ptr ) 20 | { 21 | asm volatile( ".option norvc\ncsrrw x0, 0x137, %0\n" : : "r" (ptr)); 22 | } 23 | 24 | static void nprint( intptr_t ptr ) 25 | { 26 | asm volatile( ".option norvc\ncsrrw x0, 0x136, %0\n" : : "r" (ptr)); 27 | } 28 | 29 | static inline uint32_t get_cyc_count() { 30 | uint32_t ccount; 31 | asm volatile(".option norvc\ncsrr %0, 0xC00":"=r" (ccount)); 32 | return ccount; 33 | } 34 | 35 | int main() 36 | { 37 | lprint("\n"); 38 | lprint("Hello world from RV32 land.\n"); 39 | lprint("main is at: "); 40 | pprint( (intptr_t)main ); 41 | lprint("\nAssembly code: "); 42 | asm_demo_func(); 43 | lprint("\n"); 44 | 45 | // Wait a while. 46 | uint32_t cyclecount_initial = get_cyc_count(); 47 | uint32_t timer_initial = TIMERL; 48 | 49 | volatile int i; 50 | for( i = 0; i < 1000; i++ ) 51 | { 52 | asm volatile( "nop" ); 53 | } 54 | 55 | // Gather the wall-clock time and # of cycles 56 | uint32_t cyclecount = get_cyc_count() - cyclecount_initial; 57 | uint32_t timer = TIMERL - timer_initial; 58 | 59 | lprint( "Processor effective speed: "); 60 | nprint( cyclecount / timer ); 61 | lprint( " Mcyc/s\n"); 62 | 63 | lprint("\n"); 64 | SYSCON = 0x5555; // Power off 65 | } 66 | 67 | -------------------------------------------------------------------------------- /fixtures/baremetal/baremetal.debug.txt: -------------------------------------------------------------------------------- 1 | 2 | baremetal.elf: file format elf32-littleriscv 3 | 4 | SYMBOL TABLE: 5 | 80000000 l d .text 00000000 .text 6 | 80000140 l d .data 00000000 .data 7 | 800001d0 l d .heap 00000000 .heap 8 | 800011d0 l d .stack 00000000 .stack 9 | 00000000 l d .debug_info 00000000 .debug_info 10 | 00000000 l d .debug_abbrev 00000000 .debug_abbrev 11 | 00000000 l d .debug_loclists 00000000 .debug_loclists 12 | 00000000 l d .debug_aranges 00000000 .debug_aranges 13 | 00000000 l d .debug_rnglists 00000000 .debug_rnglists 14 | 00000000 l d .debug_line 00000000 .debug_line 15 | 00000000 l d .debug_str 00000000 .debug_str 16 | 00000000 l d .debug_line_str 00000000 .debug_line_str 17 | 00000000 l d .comment 00000000 .comment 18 | 00000000 l d .riscv.attributes 00000000 .riscv.attributes 19 | 00000000 l d .debug_frame 00000000 .debug_frame 20 | 00000000 l df *ABS* 00000000 ccYpwqrc.o 21 | 800001b0 l .data 00000000 asm_label 22 | 00000000 l df *ABS* 00000000 baremetal.c 23 | 80000044 l F .text 00000008 lprint 24 | 800001d0 g .heap 00000000 _sheap 25 | 00001000 g *ABS* 00000000 __stack_size 26 | 800001d0 g .data 00000000 __BSS_BEGIN__ 27 | 80000020 g .text 00000000 asm_demo_func 28 | 00001000 g *ABS* 00000000 __heap_size 29 | 80000000 g .text 00000000 _start 30 | 800001ca g .data 00000000 __DATA_END__ 31 | 1100bff8 g *ABS* 00000000 TIMERL 32 | 800001d0 g .data 00000000 __BSS_END__ 33 | 8000004c g F .text 000000f4 main 34 | 80000140 g .text 00000000 __TEXT_END__ 35 | 800021d0 g .stack 00000000 _sstack 36 | 800011d0 g .stack 00000000 _estack 37 | 80000140 g .data 00000000 __DATA_BEGIN__ 38 | 800011d0 g .heap 00000000 _eheap 39 | 11100000 g *ABS* 00000000 SYSCON 40 | 80000000 g .text 00000000 __TEXT_BEGIN__ 41 | 42 | 43 | 44 | baremetal.elf: file format elf32-littleriscv 45 | 46 | 47 | Disassembly of section .text: 48 | 49 | 80000000 <__TEXT_BEGIN__>: 50 | 80000000: 00002117 auipc sp,0x2 51 | 80000004: 1d010113 addi sp,sp,464 # 800021d0 <_sstack> 52 | 80000008: ff010113 addi sp,sp,-16 53 | 8000000c: 00112623 sw ra,12(sp) 54 | 80000010: 03c000ef jal ra,8000004c
55 | ... 56 | 57 | 80000020 : 58 | 59 | .section .text 60 | 61 | .align 4 62 | asm_demo_func: 63 | addi sp, sp, -16 64 | 80000020: ff010113 addi sp,sp,-16 65 | sw t0, 12(sp) 66 | 80000024: 00512623 sw t0,12(sp) 67 | la t0, asm_label 68 | 80000028: 00000297 auipc t0,0x0 69 | 8000002c: 18828293 addi t0,t0,392 # 800001b0 70 | csrw 0x138, t0 71 | 80000030: 13829073 csrw 0x138,t0 72 | lw t0, 12(sp) 73 | 80000034: 00c12283 lw t0,12(sp) 74 | addi sp, sp, 16 75 | 80000038: 01010113 addi sp,sp,16 76 | ret 77 | 8000003c: 00008067 ret 78 | 80000040: 0000 .2byte 0x0 79 | ... 80 | 81 | 80000044 : 82 | 83 | // These will not turn into function calls, but instead will find a way 84 | // of writing the assembly in-line 85 | static void lprint( const char * s ) 86 | { 87 | asm volatile( ".option norvc\ncsrrw x0, 0x138, %0\n" : : "r" (s)); 88 | 80000044: 13851073 csrw 0x138,a0 89 | } 90 | 80000048: 00008067 ret 91 | 92 | 8000004c
: 93 | asm volatile(".option norvc\ncsrr %0, 0xC00":"=r" (ccount)); 94 | return ccount; 95 | } 96 | 97 | int main() 98 | { 99 | 8000004c: fe010113 addi sp,sp,-32 100 | 80000050: 00912a23 sw s1,20(sp) 101 | lprint("\n"); 102 | 80000054: 800004b7 lui s1,0x80000 103 | 80000058: 14048513 addi a0,s1,320 # 80000140 <_sstack+0xffffdf70> 104 | { 105 | 8000005c: 00112e23 sw ra,28(sp) 106 | 80000060: 00812c23 sw s0,24(sp) 107 | 80000064: 01212823 sw s2,16(sp) 108 | lprint("\n"); 109 | 80000068: fddff0ef jal ra,80000044 110 | lprint("Hello world from RV32 land.\n"); 111 | 8000006c: 80000537 lui a0,0x80000 112 | 80000070: 14450513 addi a0,a0,324 # 80000144 <_sstack+0xffffdf74> 113 | 80000074: fd1ff0ef jal ra,80000044 114 | lprint("main is at: "); 115 | 80000078: 80000537 lui a0,0x80000 116 | 8000007c: 16450513 addi a0,a0,356 # 80000164 <_sstack+0xffffdf94> 117 | 80000080: fc5ff0ef jal ra,80000044 118 | asm volatile( ".option norvc\ncsrrw x0, 0x137, %0\n" : : "r" (ptr)); 119 | 80000084: 800007b7 lui a5,0x80000 120 | 80000088: 04c78793 addi a5,a5,76 # 8000004c <_sstack+0xffffde7c> 121 | 8000008c: 13779073 csrw 0x137,a5 122 | pprint( (intptr_t)main ); 123 | lprint("\nAssembly code: "); 124 | 80000090: 80000537 lui a0,0x80000 125 | 80000094: 17450513 addi a0,a0,372 # 80000174 <_sstack+0xffffdfa4> 126 | 80000098: fadff0ef jal ra,80000044 127 | asm_demo_func(); 128 | 8000009c: f85ff0ef jal ra,80000020 129 | lprint("\n"); 130 | 800000a0: 14048513 addi a0,s1,320 131 | 800000a4: fa1ff0ef jal ra,80000044 132 | asm volatile(".option norvc\ncsrr %0, 0xC00":"=r" (ccount)); 133 | 800000a8: c00025f3 rdcycle a1 134 | 135 | // Wait a while. 136 | uint32_t cyclecount_initial = get_cyc_count(); 137 | uint32_t timer_initial = TIMERL; 138 | 800000ac: 1100c6b7 lui a3,0x1100c 139 | 800000b0: ff86a603 lw a2,-8(a3) # 1100bff8 140 | 141 | volatile int i; 142 | for( i = 0; i < 1000000; i++ ) 143 | 800000b4: 000f47b7 lui a5,0xf4 144 | 800000b8: 00012623 sw zero,12(sp) 145 | 800000bc: 23f78793 addi a5,a5,575 # f423f <__heap_size+0xf323f> 146 | 800000c0: 00c12703 lw a4,12(sp) 147 | 800000c4: 06e7d463 bge a5,a4,8000012c 148 | asm volatile(".option norvc\ncsrr %0, 0xC00":"=r" (ccount)); 149 | 800000c8: c0002473 rdcycle s0 150 | asm volatile( "nop" ); 151 | } 152 | 153 | // Gather the wall-clock time and # of cycles 154 | uint32_t cyclecount = get_cyc_count() - cyclecount_initial; 155 | uint32_t timer = TIMERL - timer_initial; 156 | 800000cc: ff86a903 lw s2,-8(a3) 157 | 158 | lprint( "Processor effective speed: "); 159 | 800000d0: 80000537 lui a0,0x80000 160 | uint32_t cyclecount = get_cyc_count() - cyclecount_initial; 161 | 800000d4: 40b40433 sub s0,s0,a1 162 | uint32_t timer = TIMERL - timer_initial; 163 | 800000d8: 40c90933 sub s2,s2,a2 164 | lprint( "Processor effective speed: "); 165 | 800000dc: 18850513 addi a0,a0,392 # 80000188 <_sstack+0xffffdfb8> 166 | 800000e0: f65ff0ef jal ra,80000044 167 | nprint( cyclecount / timer ); 168 | 800000e4: 03245433 divu s0,s0,s2 169 | asm volatile( ".option norvc\ncsrrw x0, 0x136, %0\n" : : "r" (ptr)); 170 | 800000e8: 13641073 csrw 0x136,s0 171 | lprint( " Mcyc/s\n"); 172 | 800000ec: 80000537 lui a0,0x80000 173 | 800000f0: 1a450513 addi a0,a0,420 # 800001a4 <_sstack+0xffffdfd4> 174 | 800000f4: f51ff0ef jal ra,80000044 175 | 176 | lprint("\n"); 177 | 800000f8: 14048513 addi a0,s1,320 178 | 800000fc: f49ff0ef jal ra,80000044 179 | SYSCON = 0x5555; // Power off 180 | } 181 | 80000100: 01c12083 lw ra,28(sp) 182 | 80000104: 01812403 lw s0,24(sp) 183 | SYSCON = 0x5555; // Power off 184 | 80000108: 000057b7 lui a5,0x5 185 | 8000010c: 11100737 lui a4,0x11100 186 | 80000110: 55578793 addi a5,a5,1365 # 5555 <__heap_size+0x4555> 187 | 80000114: 00f72023 sw a5,0(a4) # 11100000 188 | } 189 | 80000118: 01412483 lw s1,20(sp) 190 | 8000011c: 01012903 lw s2,16(sp) 191 | 80000120: 00000513 li a0,0 192 | 80000124: 02010113 addi sp,sp,32 193 | 80000128: 00008067 ret 194 | asm volatile( "nop" ); 195 | 8000012c: 00000013 nop 196 | for( i = 0; i < 1000000; i++ ) 197 | 80000130: 00c12703 lw a4,12(sp) 198 | 80000134: 00170713 addi a4,a4,1 199 | 80000138: 00e12623 sw a4,12(sp) 200 | 8000013c: f85ff06f j 800000c0 201 | -------------------------------------------------------------------------------- /fixtures/baremetal/baremetal.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/fixtures/baremetal/baremetal.elf -------------------------------------------------------------------------------- /fixtures/baremetal/flatfile.lds: -------------------------------------------------------------------------------- 1 | __heap_size = 0x1000; 2 | __stack_size = 0x1000; 3 | 4 | ENTRY(_start) 5 | 6 | SECTIONS 7 | { 8 | . = 0x80000000; 9 | /* 10 | .header : ALIGN( 16 ) 11 | { 12 | LONG( 0 ) 13 | LONG( 0 ) 14 | } 15 | */ 16 | .text : ALIGN(16) { 17 | __TEXT_BEGIN__ = .; 18 | *(.initial_jump) 19 | *(.entry.text) 20 | *(.init.literal) 21 | *(.init) 22 | *(.text) 23 | *(.literal .text .literal.* .text.* .stub) 24 | *(.out_jump.literal.*) 25 | *(.out_jump.*) 26 | __TEXT_END__ = .; 27 | } 28 | 29 | /* If we're on a newer compiler */ 30 | /DISCARD/ : 31 | { 32 | *(.interp) 33 | *(.dynsym) 34 | *(.dynstr) 35 | *(.header) 36 | } : phdr 37 | 38 | .data : ALIGN(16) { 39 | __DATA_BEGIN__ = .; 40 | *(.rodata) 41 | *(.rodata.*) 42 | *(.gnu.linkonce.r.*) 43 | *(.rodata1) 44 | *(.dynsbss) 45 | *(.gnu.linkonce.sb.*) 46 | *(.scommon) 47 | *(.gnu.linkonce.sb2.*) 48 | *(.sbss) 49 | *(.sbss.*) 50 | *(.sbss2) 51 | *(.sbss2.*) 52 | *(.dynbss) 53 | *(.data) 54 | *(.data.*) 55 | *(.got) 56 | *(.got.*) 57 | __DATA_END__ = .; 58 | } 59 | 60 | .bss : ALIGN( 16 ) { 61 | __BSS_BEGIN__ = .; 62 | *(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */ 63 | *(.bss.*) 64 | __BSS_END__ = .; 65 | } 66 | 67 | .heap : ALIGN( 16 ) { 68 | _sheap = .; 69 | . = . + __heap_size; 70 | _eheap = .; 71 | } 72 | 73 | .stack : ALIGN( 16 ) { 74 | _estack = .; 75 | . = . + __stack_size; 76 | _sstack = .; 77 | } 78 | 79 | PROVIDE( SYSCON = 0x11100000 ); 80 | PROVIDE( TIMERL = 0x1100bff8 ); 81 | PROVIDE( TIMERH = 0x1100bffc ); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /fixtures/default.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/fixtures/default.dtb -------------------------------------------------------------------------------- /fixtures/linux.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/fixtures/linux.bin -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.80.0 -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | single_line_if_else_max_width = 100 -------------------------------------------------------------------------------- /wasi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | core = { path = "../core" } 10 | devices = { path = "../devices" } 11 | -------------------------------------------------------------------------------- /wasi/src/main.rs: -------------------------------------------------------------------------------- 1 | use core::bus::RAM_START; 2 | 3 | fn main() { 4 | let ram_size = 640 * 1024 * 1024; 5 | 6 | let mut ram = vec![0u8; ram_size]; 7 | 8 | let bin = include_bytes!("../../fixtures/linux.bin"); 9 | let dtb = include_bytes!("../../fixtures/default.dtb"); 10 | 11 | ram[0..bin.len()].copy_from_slice(bin); 12 | 13 | let dtb_ref = ram_size - dtb.len(); 14 | ram[dtb_ref..dtb_ref + dtb.len()].copy_from_slice(dtb); 15 | 16 | let clint = core::clint::Clint::new(devices::timer::Timer::default()); 17 | let uart = devices::uart::Uart::new(); 18 | let bus = core::bus::Bus::new(ram, clint, uart); 19 | 20 | core::start( 21 | bus, 22 | RAM_START, 23 | dtb_ref as u32 + RAM_START, 24 | &std::thread::sleep, 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | device_interfaces = { path = "../device_interfaces" } 13 | core = { path = "../core" } 14 | 15 | [profile.release] -------------------------------------------------------------------------------- /wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | R2 7 | 8 | 21 | 22 | 23 | 24 | 25 |
26 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /wasm/out.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/r2/8e6ba851e5e0162a12daaf81012f2dd93fa7b0c5/wasm/out.wasm -------------------------------------------------------------------------------- /wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core::bus::RAM_START; 2 | 3 | extern "C" { 4 | fn elapsed_us() -> usize; 5 | fn tx(s: u32); 6 | fn rx() -> u32; 7 | fn keydown() -> bool; 8 | } 9 | 10 | struct Elapsed; 11 | 12 | impl device_interfaces::TimerDriver for Elapsed { 13 | fn as_micros(&self) -> u64 { 14 | unsafe { elapsed_us() as u64 } 15 | } 16 | } 17 | 18 | struct Term; 19 | 20 | impl device_interfaces::SerialInterface for Term { 21 | fn read(&self, addr: u32) -> u8 { 22 | match addr { 23 | 0x0000 if unsafe { keydown() } => unsafe { rx() as u8 }, 24 | 0x0005 => 0x60 | if unsafe { keydown() } { 1 } else { 0 }, 25 | _ => 0, 26 | } 27 | } 28 | 29 | fn write(&self, addr: u32, v: u32) { 30 | if addr == 0x000 { 31 | unsafe { 32 | tx(v); 33 | } 34 | } 35 | } 36 | } 37 | 38 | #[no_mangle] 39 | pub extern "C" fn start() { 40 | let ram_size = 640 * 1024 * 1024; 41 | 42 | let mut ram = vec![0u8; ram_size]; 43 | 44 | let bin = include_bytes!("../../fixtures/linux.bin"); 45 | let dtb = include_bytes!("../../fixtures/default.dtb"); 46 | 47 | ram[0..bin.len()].copy_from_slice(bin); 48 | 49 | let dtb_ref = ram_size - dtb.len(); 50 | ram[dtb_ref..dtb_ref + dtb.len()].copy_from_slice(dtb); 51 | 52 | let clint = core::clint::Clint::new(Elapsed); 53 | let term = Term; 54 | let bus = core::bus::Bus::new(ram, clint, term); 55 | 56 | let sleep = |_u: std::time::Duration| {}; 57 | 58 | core::start(bus, RAM_START, dtb_ref as u32 + RAM_START, &sleep); 59 | } 60 | -------------------------------------------------------------------------------- /wasm/vendor/css/xterm.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 The xterm.js authors. All rights reserved. 3 | * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 4 | * https://github.com/chjj/term.js 5 | * @license MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | * Originally forked from (with the author's permission): 26 | * Fabrice Bellard's javascript vt100 for jslinux: 27 | * http://bellard.org/jslinux/ 28 | * Copyright (c) 2011 Fabrice Bellard 29 | * The original design remains. The terminal itself 30 | * has been extended to include xterm CSI codes, among 31 | * other features. 32 | */ 33 | 34 | /** 35 | * Default styles for xterm.js 36 | */ 37 | 38 | .xterm { 39 | cursor: text; 40 | position: relative; 41 | user-select: none; 42 | -ms-user-select: none; 43 | -webkit-user-select: none; 44 | } 45 | 46 | .xterm.focus, 47 | .xterm:focus { 48 | outline: none; 49 | } 50 | 51 | .xterm .xterm-helpers { 52 | position: absolute; 53 | top: 0; 54 | /** 55 | * The z-index of the helpers must be higher than the canvases in order for 56 | * IMEs to appear on top. 57 | */ 58 | z-index: 5; 59 | } 60 | 61 | .xterm .xterm-helper-textarea { 62 | padding: 0; 63 | border: 0; 64 | margin: 0; 65 | /* Move textarea out of the screen to the far left, so that the cursor is not visible */ 66 | position: absolute; 67 | opacity: 0; 68 | left: -9999em; 69 | top: 0; 70 | width: 0; 71 | height: 0; 72 | z-index: -5; 73 | /** Prevent wrapping so the IME appears against the textarea at the correct position */ 74 | white-space: nowrap; 75 | overflow: hidden; 76 | resize: none; 77 | } 78 | 79 | .xterm .composition-view { 80 | /* TODO: Composition position got messed up somewhere */ 81 | background: #000; 82 | color: #FFF; 83 | display: none; 84 | position: absolute; 85 | white-space: nowrap; 86 | z-index: 1; 87 | } 88 | 89 | .xterm .composition-view.active { 90 | display: block; 91 | } 92 | 93 | .xterm .xterm-viewport { 94 | /* On OS X this is required in order for the scroll bar to appear fully opaque */ 95 | background-color: #000; 96 | overflow-y: scroll; 97 | cursor: default; 98 | position: absolute; 99 | right: 0; 100 | left: 0; 101 | top: 0; 102 | bottom: 0; 103 | } 104 | 105 | .xterm .xterm-screen { 106 | position: relative; 107 | } 108 | 109 | .xterm .xterm-screen canvas { 110 | position: absolute; 111 | left: 0; 112 | top: 0; 113 | } 114 | 115 | .xterm .xterm-scroll-area { 116 | visibility: hidden; 117 | } 118 | 119 | .xterm-char-measure-element { 120 | display: inline-block; 121 | visibility: hidden; 122 | position: absolute; 123 | top: 0; 124 | left: -9999em; 125 | line-height: normal; 126 | } 127 | 128 | .xterm.enable-mouse-events { 129 | /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ 130 | cursor: default; 131 | } 132 | 133 | .xterm.xterm-cursor-pointer, 134 | .xterm .xterm-cursor-pointer { 135 | cursor: pointer; 136 | } 137 | 138 | .xterm.column-select.focus { 139 | /* Column selection mode */ 140 | cursor: crosshair; 141 | } 142 | 143 | .xterm .xterm-accessibility, 144 | .xterm .xterm-message { 145 | position: absolute; 146 | left: 0; 147 | top: 0; 148 | bottom: 0; 149 | z-index: 10; 150 | color: transparent; 151 | } 152 | 153 | .xterm .live-region { 154 | position: absolute; 155 | left: -9999px; 156 | width: 1px; 157 | height: 1px; 158 | overflow: hidden; 159 | } 160 | 161 | .xterm-dim { 162 | opacity: 0.5; 163 | } 164 | 165 | .xterm-underline-1 { text-decoration: underline; } 166 | .xterm-underline-2 { text-decoration: double underline; } 167 | .xterm-underline-3 { text-decoration: wavy underline; } 168 | .xterm-underline-4 { text-decoration: dotted underline; } 169 | .xterm-underline-5 { text-decoration: dashed underline; } 170 | 171 | .xterm-strikethrough { 172 | text-decoration: line-through; 173 | } 174 | 175 | .xterm-screen .xterm-decoration-container .xterm-decoration { 176 | z-index: 6; 177 | position: absolute; 178 | } 179 | 180 | .xterm-decoration-overview-ruler { 181 | z-index: 7; 182 | position: absolute; 183 | top: 0; 184 | right: 0; 185 | pointer-events: none; 186 | } 187 | 188 | .xterm-decoration-top { 189 | z-index: 2; 190 | position: relative; 191 | } 192 | -------------------------------------------------------------------------------- /wasm/vendor/lib/xterm-addon-fit.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(function(){return(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{constructor(){}activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()})); 2 | //# sourceMappingURL=xterm-addon-fit.js.map -------------------------------------------------------------------------------- /wasm/worker.js: -------------------------------------------------------------------------------- 1 | let last = performance.now(); 2 | const elapsed_us = () => { 3 | const elapsed = (performance.now() - last) * 1000; 4 | last = performance.now(); 5 | return elapsed; 6 | }; 7 | 8 | const tx = (s) => { 9 | postMessage(s); 10 | }; 11 | 12 | const keybuf = []; 13 | const keydown = async () => { 14 | if (!!keybuf.length) return true; 15 | await delay(); 16 | return !!keybuf.length; 17 | }; 18 | 19 | const rx = () => { 20 | const d = keybuf.shift(); 21 | return d.charCodeAt(0); 22 | }; 23 | 24 | self.addEventListener("message", (event) => { 25 | keybuf.push(event.data); 26 | }); 27 | 28 | let wasm; 29 | 30 | const cachedTextDecoder = new TextDecoder("utf-8", { 31 | ignoreBOM: true, 32 | fatal: true, 33 | }); 34 | 35 | async function load(module, imports) { 36 | if (typeof Response === "function" && module instanceof Response) { 37 | try { 38 | return await instantiateStreaming(module, imports); 39 | } catch (e) { 40 | if (module.headers.get("Content-Type") != "application/wasm") { 41 | console.warn( 42 | "`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", 43 | e 44 | ); 45 | } else { 46 | throw e; 47 | } 48 | } 49 | const bytes = await module.arrayBuffer(); 50 | return await WebAssembly.instantiate(bytes, imports); 51 | } else { 52 | const instance = await WebAssembly.instantiate(module, imports); 53 | 54 | if (instance instanceof WebAssembly.Instance) { 55 | return { instance, module }; 56 | } else { 57 | return instance; 58 | } 59 | } 60 | } 61 | 62 | function getImports() { 63 | const imports = {}; 64 | imports.env = {}; 65 | imports.env.elapsed_us = elapsed_us; 66 | imports.env.tx = function (arg0) { 67 | tx(arg0 >>> 0); 68 | }; 69 | imports.env.keydown = keydown; 70 | imports.env.rx = rx; 71 | 72 | return imports; 73 | } 74 | 75 | function finalizeInit(instance, module) { 76 | wasm = instance.exports; 77 | init.__wbindgen_wasm_module = module; 78 | cachedUint8Memory0 = null; 79 | 80 | return wasm; 81 | } 82 | 83 | async function init(input) { 84 | if (typeof input === "undefined") { 85 | input = "out.wasm"; 86 | } 87 | const imports = getImports(); 88 | 89 | if ( 90 | typeof input === "string" || 91 | (typeof Request === "function" && input instanceof Request) || 92 | (typeof URL === "function" && input instanceof URL) 93 | ) { 94 | input = fetch(input); 95 | } 96 | const { instance, module } = await load(await input, imports); 97 | 98 | return finalizeInit(instance, module); 99 | } 100 | 101 | const delay = () => 102 | new Promise((r) => 103 | setTimeout(() => { 104 | r(); 105 | }) 106 | ); 107 | 108 | init().then((w) => { 109 | w.start(); 110 | }); 111 | 112 | // Asyncify 113 | const t = new WeakMap(); 114 | function e(t, e) { 115 | return new Proxy(t, { get: (t, r) => e(t[r]) }); 116 | } 117 | class r { 118 | constructor() { 119 | (this.value = void 0), (this.exports = null); 120 | } 121 | getState() { 122 | return this.exports.asyncify_get_state(); 123 | } 124 | assertNoneState() { 125 | let t = this.getState(); 126 | if (0 !== t) throw new Error(`Invalid async state ${t}, expected 0.`); 127 | } 128 | wrapImportFn(t) { 129 | return (...e) => { 130 | if (2 === this.getState()) 131 | return this.exports.asyncify_stop_rewind(), this.value; 132 | this.assertNoneState(); 133 | let r = t(...e); 134 | if ( 135 | !(s = r) || 136 | ("object" != typeof s && "function" != typeof s) || 137 | "function" != typeof s.then 138 | ) 139 | return r; 140 | var s; 141 | this.exports.asyncify_start_unwind(16), (this.value = r); 142 | }; 143 | } 144 | wrapModuleImports(t) { 145 | return e(t, (t) => ("function" == typeof t ? this.wrapImportFn(t) : t)); 146 | } 147 | wrapImports(t) { 148 | if (void 0 !== t) 149 | return e(t, (t = Object.create(null)) => this.wrapModuleImports(t)); 150 | } 151 | wrapExportFn(e) { 152 | let r = t.get(e); 153 | console.log({ r, e: e.toString() }); 154 | return ( 155 | void 0 !== r || 156 | ((r = async (...t) => { 157 | this.assertNoneState(); 158 | let r = e(...t); 159 | for (; 1 === this.getState(); ) 160 | this.exports.asyncify_stop_unwind(), 161 | (this.value = await this.value), 162 | this.assertNoneState(), 163 | this.exports.asyncify_start_rewind(16), 164 | (r = e()); 165 | return this.assertNoneState(), r; 166 | }), 167 | t.set(e, r)), 168 | r 169 | ); 170 | } 171 | wrapExports(e) { 172 | console.log(e); 173 | let r = Object.create(null); 174 | for (let t in e) { 175 | let s = e[t]; 176 | "function" != typeof s || 177 | t.startsWith("asyncify_") || 178 | (s = this.wrapExportFn(s)), 179 | Object.defineProperty(r, t, { enumerable: !0, value: s }); 180 | } 181 | return t.set(e, r), r; 182 | } 183 | init(t, e) { 184 | const { exports: r } = t, 185 | n = r.memory || (e.env && e.env.memory); 186 | new Int32Array(n.buffer, 16).set([24, 1024]), 187 | (this.exports = this.wrapExports(r)), 188 | Object.setPrototypeOf(t, s.prototype); 189 | } 190 | } 191 | class s extends WebAssembly.Instance { 192 | constructor(t, e) { 193 | let s = new r(); 194 | super(t, s.wrapImports(e)), s.init(this, e); 195 | } 196 | get exports() { 197 | return t.get(super.exports); 198 | } 199 | } 200 | async function n(t, e) { 201 | let s = new r(), 202 | n = await WebAssembly.instantiate(t, s.wrapImports(e)); 203 | return s.init(n instanceof WebAssembly.Instance ? n : n.instance, e), n; 204 | } 205 | async function instantiateStreaming(t, e) { 206 | let s = new r(), 207 | n = await WebAssembly.instantiateStreaming(t, s.wrapImports(e)); 208 | console.log(n); 209 | return s.init(n.instance, e), n; 210 | } 211 | --------------------------------------------------------------------------------