├── .cargo └── config ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── backtrace-sys ├── Cargo.toml └── src │ └── lib.rs ├── boards └── hifive-revb │ └── design.dts ├── build.rs ├── mikado_riscv32.json └── src ├── asm.s ├── bitfield ├── Cargo.lock ├── Cargo.toml ├── impl │ ├── Cargo.toml │ └── src │ │ ├── attr.rs │ │ ├── benum.rs │ │ ├── bstruct.rs │ │ ├── builtin.rs │ │ └── lib.rs └── src │ ├── checks.rs │ ├── error.rs │ └── lib.rs ├── clint.rs ├── linker.ld ├── main.rs ├── memory_region.rs ├── plic.rs ├── riscv.rs ├── test_harness.rs ├── trap.rs └── uart ├── mod.rs ├── sifive_uart.rs └── virt_uart.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "mikado_riscv32.json" 3 | 4 | [target.mikado_riscv32] 5 | rustflags = [ 6 | "-C", "opt-level=0", 7 | "-C", "linker=riscv64-unknown-elf-gcc", 8 | "-C", "link-arg=-Wl,-march=rv32imac", 9 | "-C", "link-arg=-Wl,-melf32lriscv", 10 | "-C", "link-arg=-Wl,--no-relax", 11 | "-C", "link-arg=-nostartfiles", 12 | "-C", "link-arg=-Tsrc/linker.ld", 13 | "-C", "code-model=medium", 14 | "-C", "relocation-model=static", 15 | ] 16 | 17 | [target.'cfg(target_os = "none")'] 18 | runner = "qemu-system-riscv32 -nographic -machine virt -kernel" 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "bitfield" 5 | version = "0.0.0" 6 | dependencies = [ 7 | "bitfield-impl 0.0.0", 8 | ] 9 | 10 | [[package]] 11 | name = "bitfield-impl" 12 | version = "0.0.0" 13 | dependencies = [ 14 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "cc" 21 | version = "1.0.37" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "mikado" 26 | version = "0.1.0" 27 | dependencies = [ 28 | "bitfield 0.0.0", 29 | "bitfield-impl 0.0.0", 30 | "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", 31 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 33 | ] 34 | 35 | [[package]] 36 | name = "pin-utils" 37 | version = "0.1.0-alpha.4" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | 40 | [[package]] 41 | name = "proc-macro2" 42 | version = "0.4.30" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "quote" 50 | version = "0.6.13" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 54 | ] 55 | 56 | [[package]] 57 | name = "spin" 58 | version = "0.4.10" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | 61 | [[package]] 62 | name = "syn" 63 | version = "0.15.42" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | dependencies = [ 66 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 67 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "unicode-xid" 73 | version = "0.1.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | 76 | [metadata] 77 | "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" 78 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 79 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 80 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 81 | "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" 82 | "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" 83 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 84 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mikado" 3 | version = "0.1.0" 4 | authors = ["Ben Brittain "] 5 | edition = "2018" 6 | 7 | [profile.dev] 8 | debug-assertions = false 9 | overflow-checks = false 10 | 11 | [build-dependencies] 12 | cc = "1.0.37" 13 | 14 | [dependencies] 15 | bitfield = { path = "src/bitfield" } 16 | bitfield-impl = { path = "src/bitfield/impl" } 17 | spin = "0.4.8" 18 | pin-utils = "0.1.0-alpha" 19 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mikado 2 | 3 | Experimental Risc-V Project 4 | 5 | ## Setup 6 | Needs the riscv64-unknown-elf-gcc toolchain in addition to nightly Rust 7 | 8 | ### Build 9 | ``` 10 | cargo xbuild [--release] 11 | ``` 12 | 13 | ### Run 14 | ``` 15 | cargo xrun [--release] # launches qemu-system-riscv32 16 | ``` 17 | -------------------------------------------------------------------------------- /backtrace-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "backtrace-sys" 3 | version = "0.1.31" 4 | 5 | 6 | -------------------------------------------------------------------------------- /backtrace-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | -------------------------------------------------------------------------------- /boards/hifive-revb/design.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <1>; 5 | #size-cells = <1>; 6 | compatible = "sifive,hifive1-revb"; 7 | model = "sifive,hifive1-revb"; 8 | 9 | chosen { 10 | stdout-path = "/soc/serial@10013000:115200"; 11 | metal,entry = <&spi0 0x10000>; 12 | }; 13 | 14 | cpus { 15 | #address-cells = <1>; 16 | #size-cells = <0>; 17 | compatible = "sifive,fe310-g000"; 18 | L6: cpu@0 { 19 | clocks = <&hfclk>; 20 | compatible = "sifive,rocket0", "riscv"; 21 | device_type = "cpu"; 22 | i-cache-block-size = <64>; 23 | i-cache-sets = <128>; 24 | i-cache-size = <16384>; 25 | next-level-cache = <&spi0>; 26 | reg = <0>; 27 | riscv,isa = "rv32imac"; 28 | riscv,pmpregions = <8>; 29 | sifive,dtim = <&dtim>; 30 | status = "okay"; 31 | timebase-frequency = <1000000>; 32 | hardware-exec-breakpoint-count = <4>; 33 | hlic: interrupt-controller { 34 | #interrupt-cells = <1>; 35 | compatible = "riscv,cpu-intc"; 36 | interrupt-controller; 37 | }; 38 | }; 39 | }; 40 | 41 | soc { 42 | #address-cells = <1>; 43 | #size-cells = <1>; 44 | #clock-cells = <1>; 45 | compatible = "sifive,hifive1"; 46 | ranges; 47 | hfxoscin: clock@0 { 48 | #clock-cells = <0>; 49 | compatible = "fixed-clock"; 50 | clock-frequency = <16000000>; 51 | }; 52 | hfxoscout: clock@1 { 53 | compatible = "sifive,fe310-g000,hfxosc"; 54 | clocks = <&hfxoscin>; 55 | reg = <&prci 0x4>; 56 | reg-names = "config"; 57 | }; 58 | hfroscin: clock@2 { 59 | #clock-cells = <0>; 60 | compatible = "fixed-clock"; 61 | clock-frequency = <72000000>; 62 | }; 63 | hfroscout: clock@3 { 64 | compatible = "sifive,fe310-g000,hfrosc"; 65 | clocks = <&hfroscin>; 66 | reg = <&prci 0x0>; 67 | reg-names = "config"; 68 | }; 69 | hfclk: clock@4 { 70 | compatible = "sifive,fe310-g000,pll"; 71 | clocks = <&hfxoscout &hfroscout>; 72 | clock-names = "pllref", "pllsel0"; 73 | reg = <&prci 0x8 &prci 0xc>; 74 | reg-names = "config", "divider"; 75 | clock-frequency = <16000000>; 76 | }; 77 | 78 | lfroscin: clock@5 { 79 | #clock-cells = <0>; 80 | compatible = "fixed-clock"; 81 | clock-frequency = <32000000>; 82 | }; 83 | lfclk: clock@6 { 84 | compatible = "sifive,fe310-g000,lfrosc"; 85 | clocks = <&lfroscin>; 86 | reg = <&aon 0x70>; 87 | reg-names = "config"; 88 | }; 89 | 90 | aon: aon@10000000 { 91 | compatible = "sifive,aon0"; 92 | reg = <0x10000000 0x8000>; 93 | reg-names = "mem"; 94 | }; 95 | 96 | prci: prci@10008000 { 97 | compatible = "sifive,fe310-g000,prci"; 98 | reg = <0x10008000 0x8000>; 99 | reg-names = "mem"; 100 | }; 101 | 102 | clint: clint@2000000 { 103 | compatible = "riscv,clint0"; 104 | interrupts-extended = <&hlic 3 &hlic 7>; 105 | reg = <0x2000000 0x10000>; 106 | reg-names = "control"; 107 | }; 108 | local-external-interrupts-0 { 109 | compatible = "sifive,local-external-interrupts0"; 110 | interrupt-parent = <&hlic>; 111 | interrupts = <16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31>; 112 | }; 113 | plic: interrupt-controller@c000000 { 114 | #interrupt-cells = <1>; 115 | compatible = "riscv,plic0"; 116 | interrupt-controller; 117 | interrupts-extended = <&hlic 11>; 118 | reg = <0xc000000 0x4000000>; 119 | reg-names = "control"; 120 | riscv,max-priority = <7>; 121 | riscv,ndev = <26>; 122 | }; 123 | global-external-interrupts { 124 | compatile = "sifive,global-external-interrupts0"; 125 | interrupt-parent = <&plic>; 126 | interrupts = <1 2 3 4>; 127 | }; 128 | 129 | debug-controller@0 { 130 | compatible = "sifive,debug-011", "riscv,debug-011"; 131 | interrupts-extended = <&hlic 65535>; 132 | reg = <0x0 0x100>; 133 | reg-names = "control"; 134 | }; 135 | 136 | maskrom@1000 { 137 | reg = <0x1000 0x2000>; 138 | reg-names = "mem"; 139 | }; 140 | otp@20000 { 141 | reg = <0x20000 0x2000 0x10010000 0x1000>; 142 | reg-names = "mem", "control"; 143 | }; 144 | 145 | dtim: dtim@80000000 { 146 | compatible = "sifive,dtim0"; 147 | reg = <0x80000000 0x4000>; 148 | reg-names = "mem"; 149 | }; 150 | 151 | pwm@10015000 { 152 | compatible = "sifive,pwm0"; 153 | interrupt-parent = <&plic>; 154 | interrupts = <23 24 25 26>; 155 | reg = <0x10015000 0x1000>; 156 | reg-names = "control"; 157 | }; 158 | gpio0: gpio@10012000 { 159 | compatible = "sifive,gpio0"; 160 | interrupt-parent = <&plic>; 161 | interrupts = <7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22>; 162 | reg = <0x10012000 0x1000>; 163 | reg-names = "control"; 164 | }; 165 | uart0: serial@10013000 { 166 | compatible = "sifive,uart0"; 167 | interrupt-parent = <&plic>; 168 | interrupts = <5>; 169 | reg = <0x10013000 0x1000>; 170 | reg-names = "control"; 171 | clocks = <&hfclk>; 172 | pinmux = <&gpio0 0x30000 0x30000>; 173 | }; 174 | spi0: spi@10014000 { 175 | compatible = "sifive,spi0"; 176 | interrupt-parent = <&plic>; 177 | interrupts = <6>; 178 | reg = <0x10014000 0x1000 0x20000000 0x7A120>; 179 | reg-names = "control", "mem"; 180 | clocks = <&hfclk>; 181 | pinmux = <&gpio0 0x0003C 0x0003C>; 182 | }; 183 | i2c0: i2c@10016000 { 184 | compatible = "sifive,i2c0"; 185 | interrupt-parent = <&plic>; 186 | interrupts = <52>; 187 | reg = <0x10016000 0x1000>; 188 | reg-names = "control"; 189 | }; 190 | led@0red { 191 | compatible = "sifive,gpio-leds"; 192 | label = "LD0red"; 193 | gpios = <&gpio0 22>; 194 | linux,default-trigger = "none"; 195 | }; 196 | led@0green { 197 | compatible = "sifive,gpio-leds"; 198 | label = "LD0green"; 199 | gpios = <&gpio0 19>; 200 | linux,default-trigger = "none"; 201 | }; 202 | led@0blue { 203 | compatible = "sifive,gpio-leds"; 204 | label = "LD0blue"; 205 | gpios = <&gpio0 21>; 206 | linux,default-trigger = "none"; 207 | }; 208 | }; 209 | }; 210 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use cc::Build; 4 | 5 | fn main() -> Result<(), Box> { 6 | // TODO(bwb): Find a way to make this cleaner 7 | // Assemble `asm.s` file into a .o 8 | Build::new() 9 | .compiler("riscv64-linux-gnu-gcc") 10 | .flag("-march=rv32imac") 11 | .flag("-mabi=ilp32") 12 | .file("src/asm.s") 13 | .compile("asm"); 14 | 15 | // Rebuild if non-rust files are changed 16 | println!("cargo:rerun-if-changed=src/asm.s"); 17 | println!("cargo:rerun-if-changed=src/linker.ld"); 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /mikado_riscv32.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "riscv32", 3 | "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", 4 | "arch": "riscv32", 5 | "target-endian": "little", 6 | "target-pointer-width": "32", 7 | "target-c-int-width": "32", 8 | "os": "none", 9 | "executables": true, 10 | "linker-flavor": "ld.lld", 11 | "linker": "rust-lld", 12 | "cpu": "generic-rv32", 13 | "features": "+m,+a,+c", 14 | "panic-strategy": "abort" 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/asm.s: -------------------------------------------------------------------------------- 1 | .equ STACK_SHIFT, 10 2 | .equ STACK_SIZE, 1024 3 | .equ MAX_HARTS, 4 4 | 5 | # 32bit 6 | .equ REGBYTES, 4 7 | .equ SAVE_REGS, 16 8 | .equ CONTEXT_SIZE, (SAVE_REGS * REGBYTES) 9 | 10 | .macro sxsp a, b 11 | sw \a, ((\b)*REGBYTES)(sp) 12 | .endm 13 | 14 | .macro lxsp a, b 15 | lw \a, ((\b)*REGBYTES)(sp) 16 | .endm 17 | 18 | 19 | .section .text.init,"ax",@progbits 20 | 21 | .globl _start 22 | _start: 23 | # Setup trap functionality 24 | la t0, m_trap_vector 25 | csrw mtvec, t0 26 | 27 | # Setup stack pointer based on hartid 28 | csrr t0, mhartid 29 | slli t0, t0, STACK_SHIFT 30 | la sp, stacks + STACK_SIZE 31 | add sp, sp, t0 32 | 33 | # Park all harts except hart 0 34 | csrr a0, mhartid 35 | bnez a0, loop 36 | 37 | # Jump to _rust_start 38 | j _rust_start 39 | 40 | 41 | .align 2 42 | m_trap_vector: 43 | # Save registers 44 | addi sp, sp, -CONTEXT_SIZE 45 | sxsp ra, 0 46 | sxsp a0, 1 47 | sxsp a1, 2 48 | sxsp a2, 3 49 | sxsp a3, 4 50 | sxsp a4, 5 51 | sxsp a5, 6 52 | sxsp a6, 7 53 | sxsp a7, 8 54 | sxsp t0, 9 55 | sxsp t1, 10 56 | sxsp t2, 11 57 | sxsp t3, 12 58 | sxsp t4, 13 59 | sxsp t5, 14 60 | sxsp t6, 15 61 | 62 | # Invoke the M-Mode handler 63 | mv a0, sp 64 | csrr a1, mcause 65 | csrr a2, mepc 66 | jal m_trap_handler 67 | 68 | # Restore registers 69 | lxsp ra, 0 70 | lxsp a0, 1 71 | lxsp a1, 2 72 | lxsp a2, 3 73 | lxsp a3, 4 74 | lxsp a4, 5 75 | lxsp a5, 6 76 | lxsp a6, 7 77 | lxsp a7, 8 78 | lxsp t0, 9 79 | lxsp t1, 10 80 | lxsp t2, 11 81 | lxsp t3, 12 82 | lxsp t4, 13 83 | lxsp t5, 14 84 | lxsp t6, 15 85 | addi sp, sp, CONTEXT_SIZE 86 | 87 | # Return from M-Mode trap 88 | mret 89 | 90 | # No-op loop 91 | loop: 92 | wfi 93 | j loop 94 | 95 | .bss 96 | .align 4 97 | .global stacks 98 | stacks: 99 | .skip STACK_SIZE * MAX_HARTS 100 | 101 | -------------------------------------------------------------------------------- /src/bitfield/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "bitfield" 5 | version = "0.0.0" 6 | dependencies = [ 7 | "bitfield-impl 0.0.0", 8 | "trybuild 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "bitfield-impl" 13 | version = "0.0.0" 14 | dependencies = [ 15 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 18 | ] 19 | 20 | [[package]] 21 | name = "glob" 22 | version = "0.3.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | 25 | [[package]] 26 | name = "itoa" 27 | version = "0.4.4" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | 30 | [[package]] 31 | name = "lazy_static" 32 | version = "1.3.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | 35 | [[package]] 36 | name = "proc-macro2" 37 | version = "0.4.30" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | dependencies = [ 40 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 41 | ] 42 | 43 | [[package]] 44 | name = "quote" 45 | version = "0.6.13" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 49 | ] 50 | 51 | [[package]] 52 | name = "ryu" 53 | version = "1.0.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | 56 | [[package]] 57 | name = "serde" 58 | version = "1.0.98" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | dependencies = [ 61 | "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 62 | ] 63 | 64 | [[package]] 65 | name = "serde_derive" 66 | version = "1.0.98" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | dependencies = [ 69 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "serde_json" 76 | version = "1.0.40" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "syn" 86 | version = "0.15.42" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "termcolor" 96 | version = "1.0.5" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 100 | ] 101 | 102 | [[package]] 103 | name = "toml" 104 | version = "0.5.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 108 | ] 109 | 110 | [[package]] 111 | name = "trybuild" 112 | version = "1.0.10" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | dependencies = [ 115 | "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 116 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 117 | "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "unicode-xid" 125 | version = "0.1.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | 128 | [[package]] 129 | name = "winapi" 130 | version = "0.3.7" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "winapi-i686-pc-windows-gnu" 139 | version = "0.4.0" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | 142 | [[package]] 143 | name = "winapi-util" 144 | version = "0.1.2" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 148 | ] 149 | 150 | [[package]] 151 | name = "winapi-x86_64-pc-windows-gnu" 152 | version = "0.4.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | 155 | [[package]] 156 | name = "wincolor" 157 | version = "1.0.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 162 | ] 163 | 164 | [metadata] 165 | "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 166 | "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" 167 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 168 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 169 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 170 | "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" 171 | "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" 172 | "checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" 173 | "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" 174 | "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" 175 | "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" 176 | "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" 177 | "checksum trybuild 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "56ffbab6e95708364c767a568d5bb2bc180b1762b8761bc142a8f939eaf81a3c" 178 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 179 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 180 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 181 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 182 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 183 | "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" 184 | -------------------------------------------------------------------------------- /src/bitfield/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitfield" 3 | version = "0.0.0" 4 | edition = "2018" 5 | autotests = false 6 | publish = false 7 | 8 | [dependencies] 9 | bitfield-impl = { path = "impl" } 10 | -------------------------------------------------------------------------------- /src/bitfield/impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitfield-impl" 3 | version = "0.0.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | proc-macro2 = "0.4" 12 | quote = "0.6" 13 | syn = { version = "0.15", features = ["full"] } 14 | -------------------------------------------------------------------------------- /src/bitfield/impl/src/attr.rs: -------------------------------------------------------------------------------- 1 | use syn::{Attribute, Error, Lit, LitInt, Meta, Result}; 2 | 3 | // For example: #[bits = 1] 4 | pub fn parse_bits(attrs: &[Attribute]) -> Result> { 5 | let mut bits = None; 6 | 7 | for attr in attrs { 8 | if attr.path.is_ident("doc") { 9 | continue; 10 | } 11 | 12 | if attr.path.is_ident("bits") { 13 | if let Meta::NameValue(nv) = attr.parse_meta()? { 14 | if let Lit::Int(int) = nv.lit { 15 | bits = Some(int); 16 | continue; 17 | } 18 | } 19 | } 20 | 21 | return Err(Error::new_spanned(attr, "unrecognized attribute")); 22 | } 23 | 24 | Ok(bits) 25 | } 26 | -------------------------------------------------------------------------------- /src/bitfield/impl/src/benum.rs: -------------------------------------------------------------------------------- 1 | use crate::attr; 2 | use proc_macro2::{Span, TokenStream}; 3 | use quote::{quote, quote_spanned}; 4 | use syn::parse::{Error, Result}; 5 | use syn::{ItemEnum, LitInt}; 6 | 7 | pub fn expand(input: &ItemEnum) -> Result { 8 | if !input.generics.params.is_empty() { 9 | return Err(Error::new( 10 | Span::call_site(), 11 | "#[derive(BitfieldSpecifier)] does not support generic parameters", 12 | )); 13 | } 14 | 15 | let width = attr::parse_bits(&input.attrs)?; 16 | match width { 17 | Some(width) => expand_with_width(input, width), 18 | None => expand_without_width(input), 19 | } 20 | } 21 | 22 | fn expand_with_width(input: &ItemEnum, width: LitInt) -> Result { 23 | let bits = width.value() as u8; 24 | if bits > 64 { 25 | return Err(Error::new(width.span(), "max width of bitfield enum is 64")); 26 | } 27 | 28 | let declare_discriminants = declare_discriminants(bits, input); 29 | 30 | let ident = &input.ident; 31 | let typename = ident.to_string(); 32 | let variants = &input.variants; 33 | let match_discriminants = variants.iter().map(|variant| { 34 | let variant = &variant.ident; 35 | quote! { 36 | discriminant::#variant => core::result::Result::Ok(#ident::#variant), 37 | } 38 | }); 39 | 40 | Ok(quote! { 41 | impl bitfield::Specifier for #ident { 42 | const BITS: u8 = #bits; 43 | 44 | type SetterType = Self; 45 | type GetterType = core::result::Result; 46 | 47 | fn from_u64(val: u64) -> Self::GetterType { 48 | struct discriminant; 49 | impl discriminant { 50 | #declare_discriminants 51 | } 52 | match val { 53 | #(#match_discriminants)* 54 | v => core::result::Result::Err(bitfield::Error::new(#typename, v, #bits)), 55 | } 56 | } 57 | 58 | fn into_u64(val: Self::SetterType) -> u64 { 59 | val as u64 60 | } 61 | } 62 | }) 63 | } 64 | 65 | // Expand to an impl of bitfield::Specifier for an enum like: 66 | // 67 | // #[bitfield] 68 | // #[derive(Debug, PartialEq)] 69 | // enum TwoBits { 70 | // Zero = 0b00, 71 | // One = 0b01, 72 | // Two = 0b10, 73 | // Three = 0b11, 74 | // } 75 | // 76 | // Such enums may be used as a field of a bitfield struct. 77 | // 78 | // #[bitfield] 79 | // struct Struct { 80 | // prefix: B1, 81 | // two_bits: TwoBits, 82 | // suffix: B5, 83 | // } 84 | // 85 | fn expand_without_width(input: &ItemEnum) -> Result { 86 | let ident = &input.ident; 87 | let variants = &input.variants; 88 | let len = variants.len(); 89 | if len.count_ones() != 1 { 90 | return Err(Error::new( 91 | Span::call_site(), 92 | "BitfieldSpecifier expected a number of variants which is a power of 2", 93 | )); 94 | } 95 | 96 | let bits = len.trailing_zeros() as u8; 97 | let declare_discriminants = declare_discriminants(bits, input); 98 | 99 | let match_discriminants = variants.iter().map(|variant| { 100 | let variant = &variant.ident; 101 | quote! { 102 | discriminant::#variant => #ident::#variant, 103 | } 104 | }); 105 | 106 | Ok(quote! { 107 | impl bitfield::Specifier for #ident { 108 | const BITS: u8 = #bits; 109 | 110 | type SetterType = Self; 111 | type GetterType = Self; 112 | 113 | fn from_u64(val: u64) -> Self::GetterType { 114 | struct discriminant; 115 | impl discriminant { 116 | #declare_discriminants 117 | } 118 | match val { 119 | #(#match_discriminants)* 120 | _ => unreachable!(), 121 | } 122 | } 123 | 124 | fn into_u64(val: Self::SetterType) -> u64 { 125 | val as u64 126 | } 127 | } 128 | }) 129 | } 130 | 131 | fn declare_discriminants(bits: u8, input: &ItemEnum) -> TokenStream { 132 | let ident = &input.ident; 133 | let variants = &input.variants; 134 | let upper_bound = 2u64.pow(bits as u32); 135 | 136 | variants 137 | .iter() 138 | .map(|variant| { 139 | let variant = &variant.ident; 140 | let span = variant.span(); 141 | 142 | let assertion = quote_spanned! {span=> 143 | let _: bitfield::InRange<[(); IS_IN_BOUNDS as usize]>; 144 | }; 145 | 146 | quote! { 147 | #[allow(non_upper_case_globals)] 148 | const #variant: u64 = { 149 | const IS_IN_BOUNDS: bool = (#ident::#variant as u64) < #upper_bound; 150 | 151 | #assertion 152 | 153 | #ident::#variant as u64 154 | }; 155 | } 156 | }) 157 | .collect() 158 | } 159 | -------------------------------------------------------------------------------- /src/bitfield/impl/src/bstruct.rs: -------------------------------------------------------------------------------- 1 | use crate::attr; 2 | use proc_macro2::{Span, TokenStream}; 3 | use quote::{quote, quote_spanned}; 4 | use syn::parse::{Error, Result}; 5 | use syn::{Fields, FieldsNamed, Ident, ItemStruct, LitInt, Type, Visibility}; 6 | 7 | pub fn expand(input: &mut ItemStruct) -> Result { 8 | if !input.generics.params.is_empty() { 9 | return Err(Error::new( 10 | Span::call_site(), 11 | "#[bitfield] does not support generic parameters", 12 | )); 13 | } 14 | 15 | let fields = match &input.fields { 16 | Fields::Named(fields) => fields, 17 | Fields::Unnamed(_) | Fields::Unit => return Err(Error::new( 18 | Span::call_site(), 19 | "#[bitfield] requires a struct with named fields", 20 | )), 21 | }; 22 | 23 | let ident = &input.ident; 24 | let vis = &input.vis; 25 | let attrs = &input.attrs; 26 | let specs = field_specs(fields)?; 27 | let definition = make_definition(vis, ident, &specs); 28 | let helpers = make_helpers(); 29 | let accessors = make_accessors(&specs); 30 | let impl_debug = impl_debug(ident, &specs); 31 | 32 | Ok(quote! { 33 | #(#attrs)* 34 | #definition 35 | 36 | impl #ident { 37 | #helpers 38 | #accessors 39 | } 40 | 41 | #impl_debug 42 | }) 43 | } 44 | 45 | struct FieldSpec<'a> { 46 | ident: &'a Ident, 47 | ty: &'a Type, 48 | expected_bits: Option, 49 | } 50 | 51 | fn field_specs(fields: &FieldsNamed) -> Result> { 52 | let mut specs = Vec::new(); 53 | 54 | for field in &fields.named { 55 | specs.push(FieldSpec { 56 | ident: field 57 | .ident 58 | .as_ref() 59 | .expect("Fields::Named has named fields"), 60 | ty: &field.ty, 61 | expected_bits: attr::parse_bits(&field.attrs)?, 62 | }); 63 | } 64 | 65 | Ok(specs) 66 | } 67 | 68 | fn make_definition(vis: &Visibility, ident: &Ident, specs: &[FieldSpec]) -> TokenStream { 69 | let types = specs.iter().map(|spec| spec.ty); 70 | 71 | let data_size_in_bits = quote!({ 72 | 0 #(+ <#types as bitfield::Specifier>::BITS as usize)* 73 | }); 74 | 75 | quote! { 76 | #[repr(C)] 77 | #vis struct #ident { 78 | data: [u8; #data_size_in_bits / 8], 79 | } 80 | 81 | impl #ident { 82 | #vis fn new() -> Self { 83 | let _: bitfield::MultipleOfEight<[(); #data_size_in_bits % 8]>; 84 | 85 | #ident { 86 | data: [0; #data_size_in_bits / 8], 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | fn make_helpers() -> TokenStream { 94 | quote! { 95 | fn get(&self, offset: usize, width: u8) -> u64 { 96 | let mut val = 0; 97 | 98 | for i in 0..(width as usize) { 99 | let offset = i + offset; 100 | 101 | let byte_index = offset / 8; 102 | let bit_offset = offset % 8; 103 | 104 | let byte = self.data[byte_index]; 105 | let mask = 1 << bit_offset; 106 | 107 | if byte & mask == mask { 108 | val |= 1 << i; 109 | } 110 | } 111 | 112 | val 113 | } 114 | 115 | fn set(&mut self, offset: usize, width: u8, val: u64) { 116 | for i in 0..(width as usize) { 117 | let mask = 1 << i; 118 | let val_bit_is_set = val & mask == mask; 119 | 120 | let offset = i + offset; 121 | 122 | let byte_index = offset / 8; 123 | let bit_offset = offset % 8; 124 | 125 | let byte = &mut self.data[byte_index]; 126 | let mask = 1 << bit_offset; 127 | 128 | if val_bit_is_set { 129 | *byte |= mask; 130 | } else { 131 | *byte &= !mask; 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | fn make_accessors(specs: &[FieldSpec]) -> TokenStream { 139 | let mut accessors = TokenStream::new(); 140 | let mut offset = quote!(0); 141 | 142 | for spec in specs { 143 | let span = spec.ident.span(); 144 | let getter = Ident::new(&format!("get_{}", spec.ident), span); 145 | let setter = Ident::new(&format!("set_{}", spec.ident), span); 146 | let ty = spec.ty; 147 | 148 | // Optional #[bits = N] attribute to provide compile-time checked 149 | // documentation of how many bits some field covers. 150 | let check_expected_bits = spec.expected_bits.as_ref().map(|expected_bits| { 151 | // If expected_bits does not match the actual number of bits in the 152 | // bitfield specifier, this will fail to compile with an error 153 | // pointing into the #[bits = N] attribute. 154 | let span = expected_bits.span(); 155 | quote_spanned! {span=> 156 | #[allow(dead_code)] 157 | const EXPECTED_BITS: [(); #expected_bits as usize] = 158 | [(); <#ty as bitfield::Specifier>::BITS as usize]; 159 | } 160 | }); 161 | 162 | accessors.extend(quote! { 163 | pub fn #getter(&self) -> <#ty as bitfield::Specifier>::GetterType { 164 | #check_expected_bits 165 | let val = self.get(#offset, <#ty as bitfield::Specifier>::BITS); 166 | <#ty as bitfield::Specifier>::from_u64(val) 167 | } 168 | 169 | pub fn #setter(&mut self, val: <#ty as bitfield::Specifier>::SetterType) { 170 | let val = <#ty as bitfield::Specifier>::into_u64(val); 171 | debug_assert!(val <= bitfield::max::<#ty>()); 172 | self.set(#offset, <#ty as bitfield::Specifier>::BITS, val); 173 | } 174 | }); 175 | 176 | offset.extend(quote!(+ <#ty as bitfield::Specifier>::BITS as usize)); 177 | } 178 | 179 | accessors 180 | } 181 | 182 | fn impl_debug(ident: &Ident, specs: &[FieldSpec]) -> TokenStream { 183 | let struct_name = ident.to_string(); 184 | let field_names = specs.iter().map(|spec| spec.ident.to_string()); 185 | let getters = specs 186 | .iter() 187 | .map(|spec| Ident::new(&format!("get_{}", spec.ident), Span::call_site())); 188 | 189 | quote! { 190 | impl core::fmt::Debug for #ident { 191 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 192 | f.debug_struct(#struct_name) 193 | #( 194 | .field(#field_names, &self.#getters()) 195 | )* 196 | .finish() 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/bitfield/impl/src/builtin.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::quote; 3 | 4 | pub fn define() -> TokenStream { 5 | let mut builtins = TokenStream::new(); 6 | 7 | for width in 0u8..=64 { 8 | let span = Span::call_site(); 9 | let name = Ident::new(&format!("B{}", width), span); 10 | 11 | let default_field_type = if width <= 8 { 12 | quote!(u8) 13 | } else if width <= 16 { 14 | quote!(u16) 15 | } else if width <= 32 { 16 | quote!(u32) 17 | } else { 18 | quote!(u64) 19 | }; 20 | 21 | builtins.extend(quote! { 22 | pub enum #name {} 23 | 24 | impl Specifier for #name { 25 | const BITS: u8 = #width; 26 | 27 | type SetterType = #default_field_type; 28 | type GetterType = #default_field_type; 29 | 30 | #[inline] 31 | fn from_u64(val: u64) -> Self::GetterType { 32 | val as Self::GetterType 33 | } 34 | 35 | #[inline] 36 | fn into_u64(val: Self::SetterType) -> u64 { 37 | val as u64 38 | } 39 | } 40 | }); 41 | } 42 | 43 | builtins 44 | } 45 | -------------------------------------------------------------------------------- /src/bitfield/impl/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "256"] 2 | 3 | extern crate proc_macro; 4 | 5 | mod attr; 6 | mod benum; 7 | mod bstruct; 8 | mod builtin; 9 | 10 | use proc_macro::TokenStream; 11 | use quote::quote; 12 | use syn::{parse_macro_input, ItemEnum, ItemStruct}; 13 | 14 | #[proc_macro_attribute] 15 | pub fn bitfield(_args: TokenStream, input: TokenStream) -> TokenStream { 16 | let mut input = parse_macro_input!(input as ItemStruct); 17 | 18 | let expanded = bstruct::expand(&mut input).unwrap_or_else(|e| { 19 | let compile_error = e.to_compile_error(); 20 | quote! { 21 | #compile_error 22 | 23 | // Include the original input to avoid "use of undeclared type" 24 | // errors elsewhere. 25 | #input 26 | } 27 | }); 28 | 29 | TokenStream::from(expanded) 30 | } 31 | 32 | #[proc_macro_derive(BitfieldSpecifier, attributes(bits))] 33 | pub fn derive(input: TokenStream) -> TokenStream { 34 | let input = parse_macro_input!(input as ItemEnum); 35 | let expanded = benum::expand(&input).unwrap_or_else(|e| e.to_compile_error()); 36 | TokenStream::from(expanded) 37 | } 38 | 39 | // Only intended to be used from the bitfield crate. This macro emits the 40 | // marker types bitfield::B1 through bitfield::B64. 41 | #[proc_macro] 42 | #[doc(hidden)] 43 | pub fn define_builtin_specifiers(_input: TokenStream) -> TokenStream { 44 | TokenStream::from(builtin::define()) 45 | } 46 | -------------------------------------------------------------------------------- /src/bitfield/src/checks.rs: -------------------------------------------------------------------------------- 1 | // Instantiated by the generated code to prove that the total size of fields is 2 | // a multiple of 8 bits. 3 | // 4 | // let _: bitfield::Check<[(); #bits % 8]>; 5 | // 6 | #[doc(hidden)] 7 | pub type MultipleOfEight = <::Marker as TotalSizeIsMultipleOfEightBits>::Check; 8 | 9 | // [(); (discr >= 0 && discr < N) as usize] 10 | #[doc(hidden)] 11 | pub type InRange = <::Marker as DiscriminantInRange>::Check; 12 | 13 | pub enum ZeroMod8 {} 14 | pub enum OneMod8 {} 15 | pub enum TwoMod8 {} 16 | pub enum ThreeMod8 {} 17 | pub enum FourMod8 {} 18 | pub enum FiveMod8 {} 19 | pub enum SixMod8 {} 20 | pub enum SevenMod8 {} 21 | 22 | pub trait Array { 23 | type Marker; 24 | } 25 | 26 | impl Array for [(); 0] { 27 | type Marker = ZeroMod8; 28 | } 29 | 30 | impl Array for [(); 1] { 31 | type Marker = OneMod8; 32 | } 33 | 34 | impl Array for [(); 2] { 35 | type Marker = TwoMod8; 36 | } 37 | 38 | impl Array for [(); 3] { 39 | type Marker = ThreeMod8; 40 | } 41 | 42 | impl Array for [(); 4] { 43 | type Marker = FourMod8; 44 | } 45 | 46 | impl Array for [(); 5] { 47 | type Marker = FiveMod8; 48 | } 49 | 50 | impl Array for [(); 6] { 51 | type Marker = SixMod8; 52 | } 53 | 54 | impl Array for [(); 7] { 55 | type Marker = SevenMod8; 56 | } 57 | 58 | pub trait TotalSizeIsMultipleOfEightBits { 59 | type Check; 60 | } 61 | 62 | impl TotalSizeIsMultipleOfEightBits for ZeroMod8 { 63 | type Check = (); 64 | } 65 | 66 | pub enum False {} 67 | pub enum True {} 68 | 69 | pub trait Boolean { 70 | type Marker; 71 | } 72 | 73 | impl Boolean for [(); 0] { 74 | type Marker = False; 75 | } 76 | 77 | impl Boolean for [(); 1] { 78 | type Marker = True; 79 | } 80 | 81 | pub trait DiscriminantInRange { 82 | type Check; 83 | } 84 | 85 | impl DiscriminantInRange for True { 86 | type Check = (); 87 | } 88 | -------------------------------------------------------------------------------- /src/bitfield/src/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display}; 2 | 3 | #[derive(Debug)] 4 | pub struct Error { 5 | typename: &'static str, 6 | value: u64, 7 | width: u8, 8 | } 9 | 10 | impl Error { 11 | #[doc(hidden)] 12 | pub fn new(typename: &'static str, value: u64, width: u8) -> Error { 13 | Error { 14 | typename, 15 | value, 16 | width, 17 | } 18 | } 19 | 20 | pub fn raw_value(&self) -> u64 { 21 | self.value 22 | } 23 | } 24 | 25 | impl Display for Error { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!( 28 | f, 29 | "unrecognized bit pattern for enum {}: 0b{:02$b}", 30 | self.typename, self.value, self.width as usize, 31 | ) 32 | } 33 | } 34 | 35 | // impl core::error::Error for Error {} 36 | -------------------------------------------------------------------------------- /src/bitfield/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | mod checks; 4 | mod error; 5 | 6 | pub use crate::checks::{InRange, MultipleOfEight}; 7 | pub use crate::error::Error; 8 | pub use bitfield_impl::{bitfield, BitfieldSpecifier}; 9 | 10 | pub trait Specifier { 11 | const BITS: u8; 12 | 13 | type SetterType; 14 | type GetterType; 15 | 16 | fn from_u64(val: u64) -> Self::GetterType; 17 | fn into_u64(val: Self::SetterType) -> u64; 18 | } 19 | 20 | // Largest u64 representable by this bitfield specifier. Used by generated code 21 | // in bitfield_impl. 22 | #[doc(hidden)] 23 | pub fn max() -> u64 { 24 | if T::BITS < 64 { 25 | (1 << T::BITS) - 1 26 | } else { 27 | u64::max_value() 28 | } 29 | } 30 | 31 | // Defines bitfield::B1 through bitfield::B64. 32 | bitfield_impl::define_builtin_specifiers!(); 33 | 34 | impl Specifier for bool { 35 | const BITS: u8 = 1; 36 | 37 | type SetterType = bool; 38 | type GetterType = bool; 39 | 40 | #[inline] 41 | fn from_u64(val: u64) -> Self::GetterType { 42 | val > 0 43 | } 44 | 45 | #[inline] 46 | fn into_u64(val: Self::SetterType) -> u64 { 47 | val as u64 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/clint.rs: -------------------------------------------------------------------------------- 1 | //! Core-Local Interruptor 2 | 3 | use crate::memory_region::MemoryRegion; 4 | use crate::{print, println}; 5 | 6 | pub struct Clint { 7 | memory: MemoryRegion, 8 | } 9 | 10 | impl Clint { 11 | pub fn new() -> Self { 12 | Clint { 13 | memory: unsafe { MemoryRegion::new(0x2000000, 0xc000) }, 14 | } 15 | } 16 | 17 | pub fn get_time_cmp(&self) -> usize { 18 | self.memory[0x4000] 19 | } 20 | 21 | pub fn set_time_cmp(&mut self, time: usize) { 22 | self.memory[0x4000] = time; 23 | } 24 | 25 | pub fn get_time(&self) -> usize { 26 | self.memory[0xbff8] 27 | } 28 | } 29 | 30 | #[test_case] 31 | fn blah() { 32 | print!("Clint creation... "); 33 | let clint = Clint::new(); 34 | println!("[ok]"); 35 | } 36 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | 3 | ENTRY( _start ) 4 | 5 | MEMORY 6 | { 7 | RAM : ORIGIN = 0x80000000, LENGTH = 128M 8 | } 9 | 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | data PT_LOAD; 14 | bss PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | .text : { 20 | PROVIDE(_text_start = .); 21 | *(.text.init) *(.text .text.*) 22 | PROVIDE(_text_end = .); 23 | } >RAM AT>RAM :text 24 | 25 | .rodata : { 26 | PROVIDE(_rodata_start = .); 27 | *(.rodata .rodata.*) 28 | PROVIDE(_rodata_end = .); 29 | } >RAM AT>RAM :text 30 | 31 | .data : { 32 | . = ALIGN(4096); 33 | PROVIDE(_data_start = .); 34 | *(.sdata .sdata.*) *(.data .data.*) 35 | PROVIDE(_data_end = .); 36 | } >RAM AT>RAM :data 37 | 38 | .bss :{ 39 | PROVIDE(_bss_start = .); 40 | *(.sbss .sbss.*) *(.bss .bss.*) 41 | PROVIDE(_bss_end = .); 42 | } >RAM AT>RAM :bss 43 | 44 | PROVIDE(_memory_start = ORIGIN(RAM)); 45 | PROVIDE(_memory_end = ORIGIN(RAM) + LENGTH(RAM)); 46 | } 47 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(asm)] 4 | #![feature(custom_test_frameworks)] 5 | #![test_runner(crate::test_harness::runner)] 6 | #![reexport_test_harness_main = "test_main"] 7 | 8 | mod clint; 9 | mod memory_region; 10 | mod plic; 11 | mod riscv; 12 | mod trap; 13 | mod uart; 14 | 15 | #[cfg(test)] 16 | mod test_harness; 17 | 18 | use clint::Clint; 19 | use core::panic::PanicInfo; 20 | use plic::Plic; 21 | 22 | #[macro_export] 23 | macro_rules! print { 24 | ($($arg:tt)*) => ({ 25 | use core::fmt::Write; 26 | crate::uart::Writer.write_fmt(format_args!($($arg)*)).unwrap(); 27 | }); 28 | } 29 | 30 | #[macro_export] 31 | macro_rules! println { 32 | ($fmt:expr) => ($crate::print!(concat!($fmt, "\n"))); 33 | ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*)); 34 | } 35 | 36 | #[no_mangle] 37 | pub extern "C" fn abort() { 38 | panic!("abort!"); 39 | } 40 | 41 | #[cfg(not(test))] 42 | #[panic_handler] 43 | fn panic(info: &PanicInfo) -> ! { 44 | println!("{}", info); 45 | loop {} 46 | } 47 | 48 | #[no_mangle] 49 | pub extern "C" fn _rust_start() -> ! { 50 | // Anything returned from main that isn't Ok is a system-wide panic 51 | if let Err(error) = main() { 52 | panic!("{}", error); 53 | } 54 | println!("\nfinished main\n"); 55 | loop { 56 | riscv::wfi() 57 | } 58 | } 59 | 60 | pub fn main() -> Result<(), core::fmt::Error> { 61 | uart::initialize(); 62 | println!("Coming back to where you started is not the same as never leaving.\n"); 63 | #[cfg(test)] 64 | { 65 | test_main(); 66 | return Ok(()); 67 | } 68 | 69 | let result = riscv::misa(); 70 | println!("result: {:x}", result); 71 | 72 | unsafe { asm!("ebreak") } 73 | 74 | println!("Passed breakpoint."); 75 | 76 | write_csr!(0xC80, 0x01); 77 | 78 | println!("{:?}", riscv::mstatus()); 79 | 80 | riscv::set_mie(true, true, true); 81 | riscv::enable_mie(); 82 | 83 | println!("{:?}", riscv::mstatus()); 84 | 85 | let mut plic = Plic::new(); 86 | let mut clint = Clint::new(); 87 | println!("time: {:?}", clint.set_time_cmp(200)); 88 | println!("time: {:?}", clint.get_time_cmp()); 89 | println!("time: {:?}", clint.get_time()); 90 | println!("time: {:?}", clint.get_time()); 91 | println!("time: {:?}", clint.get_time()); 92 | 93 | Ok(()) 94 | } 95 | -------------------------------------------------------------------------------- /src/memory_region.rs: -------------------------------------------------------------------------------- 1 | use crate::{print, println}; 2 | use core::mem; 3 | use core::ops::{Index, IndexMut}; 4 | 5 | pub struct MemoryRegion { 6 | ptr: *mut usize, 7 | length_bytes: usize, 8 | } 9 | 10 | impl MemoryRegion { 11 | pub unsafe fn new(address: usize, length: usize) -> Self { 12 | assert_eq!(length % mem::size_of::() as usize, 0); 13 | Self { 14 | ptr: address as *mut usize, 15 | length_bytes: length, 16 | } 17 | } 18 | } 19 | 20 | impl Index for MemoryRegion { 21 | type Output = usize; 22 | fn index(&self, index: u32) -> &usize { 23 | assert_eq!(index as usize % mem::size_of::() as usize, 0); 24 | assert!((index as usize) < self.length_bytes); 25 | unsafe { &*(self.ptr.add(index as usize / mem::size_of::())) } 26 | } 27 | } 28 | 29 | impl IndexMut for MemoryRegion { 30 | fn index_mut(&mut self, index: u32) -> &mut usize { 31 | assert_eq!(index % mem::size_of::() as u32, 0); 32 | assert!((index as usize) < self.length_bytes); 33 | unsafe { &mut *(self.ptr.add(index as usize / mem::size_of::())) } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/plic.rs: -------------------------------------------------------------------------------- 1 | //! Platform-Level Interrupt Controller 2 | 3 | use crate::memory_region::MemoryRegion; 4 | 5 | pub struct Plic { 6 | memory: MemoryRegion, 7 | } 8 | 9 | pub enum Priority { 10 | Disable = 0, 11 | One = 1, // low 12 | Two = 2, 13 | Three = 3, 14 | Four = 4, 15 | Five = 5, 16 | Six = 6, 17 | Seven = 7, // high 18 | } 19 | 20 | impl Plic { 21 | pub fn new() -> Self { 22 | Plic { 23 | memory: unsafe { MemoryRegion::new(0x0C00_0000, (0x1000_0000 - 0x0C00_0000)) }, 24 | } 25 | } 26 | 27 | pub fn get_source_priority(&self, interrupt: u16) -> usize { 28 | self.memory[4 * (interrupt as u32)] 29 | } 30 | 31 | pub fn set_source_priority(&mut self, interrupt: u16, priority: Priority) { 32 | self.memory[4 * (interrupt as u32)] = (priority as usize) 33 | } 34 | 35 | pub fn claim(&self) -> usize { 36 | self.memory[0x0C20_0004 - 0x0C00_0000] 37 | } 38 | 39 | //pub fn set_time_cmp(&mut self, time: usize) { 40 | // self.memory[0x4000] = time; 41 | //} 42 | 43 | //pub fn get_time(&self) -> usize { 44 | // self.memory[0xbff8] 45 | //} 46 | } 47 | 48 | //#[test_case] 49 | //fn blah() { 50 | // print!("Clint creation... "); 51 | // let clint = Clint::new(); 52 | // println!("[ok]"); 53 | //} 54 | -------------------------------------------------------------------------------- /src/riscv.rs: -------------------------------------------------------------------------------- 1 | /// Control and Status Registers 2 | /// 3 | /// Documentation Comments are derivative works of 4 | /// "The RISC-V Instruction Set Manual Volume II: Privileged Architecture 5 | /// Document Version 20190608-Priv-MSU-Ratified" 6 | /// 7 | /// Creative Commons Attribution 4.0 International License. 8 | use core::convert::TryInto; 9 | 10 | /// The Wait for Interrupt instruction (WFI) provides a hint to the implementation that the current 11 | /// hart can be stalled until an interrupt might need servicing. Execution of the WFI instruction 12 | /// can also be used to inform the hardware platform that suitable interrupts should preferentially be 13 | /// routed to this hart. WFI is available in all privileged modes, and optionally available to U-mode. 14 | pub fn wfi() { 15 | unsafe { 16 | asm!("wfi" ::: "memory"); 17 | } 18 | } 19 | 20 | 21 | 22 | #[repr(u16)] 23 | #[allow(dead_code)] 24 | pub enum Register { 25 | Fflags = 0x001, 26 | Frm = 0x002, 27 | Fcsr = 0x003, 28 | Mcycle = 0xB00, 29 | Minstret = 0xB02, 30 | Mcycleh = 0xB80, 31 | Minstreth = 0xB82, 32 | Cycle = 0xC00, 33 | Time = 0xC01, 34 | Instret = 0xC02, 35 | Cycleh = 0xC80, 36 | Timeh = 0xC81, 37 | Instreth = 0xC82, 38 | Mvendorid = 0xF11, 39 | Marchid = 0xF12, 40 | Mimpid = 0xF13, 41 | Mhartid = 0xF14, 42 | Mstatus = 0x300, 43 | Misa = 0x301, 44 | Medeleg = 0x302, 45 | Mideleg = 0x303, 46 | Mie = 0x304, 47 | Mtvec = 0x305, 48 | Mcounteren = 0x306, 49 | Mscratch = 0x340, 50 | Mepc = 0x341, 51 | Mcause = 0x342, 52 | Mtval = 0x343, 53 | Mip = 0x344, 54 | Sstatus = 0x100, 55 | Sedeleg = 0x102, 56 | Sideleg = 0x103, 57 | Sie = 0x104, 58 | Stvec = 0x105, 59 | Scounteren = 0x106, 60 | Sscratch = 0x140, 61 | Sepc = 0x141, 62 | Scause = 0x142, 63 | Stval = 0x143, 64 | Sip = 0x144, 65 | Satp = 0x180, 66 | Pmpcfg0 = 0x3A0, 67 | Pmpcfg1 = 0x3A1, 68 | Pmpcfg2 = 0x3A2, 69 | Pmpcfg3 = 0x3A3, 70 | Pmpaddr0 = 0x3B0, 71 | Pmpaddr1 = 0x3B1, 72 | Pmpaddr2 = 0x3B2, 73 | Pmpaddr3 = 0x3B3, 74 | Pmpaddr4 = 0x3B4, 75 | Pmpaddr5 = 0x3B5, 76 | Pmpaddr6 = 0x3B6, 77 | Pmpaddr7 = 0x3B7, 78 | Pmpaddr8 = 0x3B8, 79 | Pmpaddr9 = 0x3B9, 80 | Pmpaddr10 = 0x3BA, 81 | Pmpaddr11 = 0x3BB, 82 | Pmpaddr12 = 0x3BC, 83 | Pmpaddr13 = 0x3BD, 84 | Pmpaddr14 = 0x3BE, 85 | Pmpaddr15 = 0x3BF, 86 | } 87 | 88 | /// Coerce Register into u16. 89 | /// This is a workaround for https://github.com/rust-lang/rust/issues/42974 90 | /// TODO: remove need for this macro and consume enum directly 91 | macro_rules! reg { 92 | ( $reg:expr ) => { 93 | $reg as u16 94 | } 95 | } 96 | 97 | /// Write to an arbitrary Control and Status Register 98 | /// 99 | /// Warning: 100 | /// This currently does not work due to constant folding and the 101 | /// inline assembly constraints on 'i' 102 | #[macro_export] 103 | macro_rules! write_csr { 104 | ( $reg:expr, $value:expr ) => { 105 | #[allow(unused_unsafe)] 106 | unsafe { asm!("csrw $0, $1" :: "i"($reg), "r"($value)) } 107 | } 108 | } 109 | 110 | /// The mepc CSR is a WARL register that must be able to hold all valid physical and virtual addresses. 111 | /// 112 | /// When a trap is taken into M-mode, mepc is written with the virtual address of 113 | /// the instruction that was interrupted or that encountered the exception. 114 | /// 115 | /// No guarantee the virtual address is valid, so unsafe 116 | pub unsafe fn write_mepc(value: usize) { 117 | write_csr!(reg!(Register::Mepc), value); 118 | } 119 | 120 | /// Read from an arbitrary Control and Status Register 121 | #[macro_export] 122 | macro_rules! read_csr { 123 | ( $r:expr ) => { 124 | { 125 | let result: usize; 126 | unsafe { asm!("csrr $0, $1" : "=r"(result) : "i"($r)) } 127 | result 128 | } 129 | } 130 | } 131 | 132 | /// The misa CSR is a WARL read-write register reporting the ISA supported by the hart. 133 | pub fn misa() -> usize { 134 | let result; 135 | unsafe { asm!("csrr $0, $1" : "=r"(result) : "i"(reg!(Register::Misa))) } 136 | result 137 | } 138 | 139 | /// The mcycle CSR counts the number of clock cycles executed by the processor core on which the hart is running. 140 | pub fn mcycle() -> usize { 141 | let result; 142 | unsafe { asm!("csrr $0, $1" : "=r"(result) : "i"(reg!(Register::Mcycle))) } 143 | result 144 | } 145 | 146 | use bitfield::*; 147 | 148 | pub fn enable_mie() { 149 | let value: usize = 1 << 3; 150 | write_csr!(reg!(Register::Mstatus), value); 151 | } 152 | 153 | pub fn set_mie(msie: bool, mtie: bool, meie: bool) { 154 | let mut value: usize = 0; 155 | if msie { 156 | value += 1 << 3; 157 | } 158 | if mtie { 159 | value += 1 << 7; 160 | } 161 | if meie { 162 | value += 1 << 11; 163 | } 164 | write_csr!(reg!(Register::Mie), value); 165 | } 166 | 167 | pub fn mstatus() -> MStatus { 168 | let result: u32; 169 | unsafe { asm!("csrr $0, $1" : "=r"(result) : "i"(reg!(Register::Mstatus))) } 170 | let mut status = MStatus::new(); 171 | // TODO fill in other parts of mstatus. ONLY MIE implemented currentl 172 | status.set_mie((result == (1 << 3)) as u8); 173 | status 174 | } 175 | 176 | #[bitfield] 177 | pub struct MStatus { 178 | uie: B1, 179 | sie: B1, 180 | wpri_1: B1, 181 | mie: B1, 182 | upie: B1, 183 | spie: B1, 184 | wpri_2: B1, 185 | mpie: B1, 186 | spp: B1, 187 | wpri_3: B2, 188 | mpp: B2, 189 | fs: B2, 190 | xs: B2, 191 | mprv: B1, 192 | sum: B1, 193 | mxr: B1, 194 | tvm: B1, 195 | tw: B1, 196 | tsr: B1, 197 | wpri_4: B8, 198 | sd: B1, 199 | } 200 | -------------------------------------------------------------------------------- /src/test_harness.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use crate::{print, println}; 4 | use core::panic::PanicInfo; 5 | 6 | #[panic_handler] 7 | fn panic(info: &PanicInfo) -> ! { 8 | println!("[failed]\n"); 9 | println!("Error: {}\n", info); 10 | loop {} 11 | } 12 | 13 | pub fn runner(tests: &[&dyn Fn()]) { 14 | println!("Running {} tests", tests.len()); 15 | for test in tests { 16 | test(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/trap.rs: -------------------------------------------------------------------------------- 1 | use crate::println; 2 | use crate::riscv; 3 | 4 | #[no_mangle] 5 | pub fn m_trap_handler(registers: *mut usize, mcause: usize, mepc: usize) { 6 | println!( 7 | "registers: {:?} | mcause: {} | mepc: {:x} ", 8 | registers, mcause, mepc 9 | ); 10 | 11 | // `m_trap_vector` calls this function. It ends with a `mret`. 12 | // `mepc` needs to be incremented so the trap handling can make progress 13 | unsafe { 14 | riscv::write_mepc(mepc + 4); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/uart/mod.rs: -------------------------------------------------------------------------------- 1 | mod virt_uart; 2 | mod sifive_uart; 3 | 4 | pub use virt_uart::{initialize, Writer}; 5 | -------------------------------------------------------------------------------- /src/uart/sifive_uart.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use core::{fmt, ptr}; 4 | use core::convert::TryInto; 5 | use crate::memory_region::MemoryRegion; 6 | 7 | /// SIFIVE UART Constants 8 | 9 | const UART0: usize = 0x10013000; 10 | 11 | struct Uart { 12 | memory: MemoryRegion 13 | } 14 | 15 | impl Uart { 16 | pub fn new() -> Self { 17 | let mut memory = unsafe { MemoryRegion::new(UART0, 10000) }; 18 | memory[0x08] = 1; 19 | memory[0x0C] = 1; 20 | Uart { memory } 21 | } 22 | 23 | // TODO error code 24 | pub fn put_char(&mut self, ch: u8) -> Result<(), ()> { 25 | self.memory[0x00] = ch as usize; 26 | Ok(()) 27 | } 28 | 29 | pub fn get_char(&self) -> u8 { 30 | self.memory[0x04].try_into().unwrap() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/uart/virt_uart.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use core::{fmt, ptr}; 4 | 5 | /// NS16550A UART Constants 6 | 7 | const NS16550A_UART: *mut u8 = 0x10000000 as *mut u8; 8 | const UART0_CLOCK_FREQ: u32 = 1843200; 9 | const UART0_BAUD_RATE: u32 = 115200; 10 | const DIVISOR: u32 = UART0_CLOCK_FREQ / (16 * UART0_BAUD_RATE); 11 | 12 | const UART_RBR: u8 = 0x00; // Receive Buffer Register 13 | const UART_THR: u8 = 0x00; // Transmit Hold Register 14 | const UART_IER: u8 = 0x01; // Interrupt Enable Register 15 | const UART_DLL: u8 = 0x00; // Divisor LSB (LCR_DLAB) 16 | const UART_DLM: u8 = 0x01; // Divisor MSB (LCR_DLAB) 17 | const UART_FCR: u8 = 0x02; // FIFO Control Register 18 | const UART_LCR: u8 = 0x03; // Line Control Register 19 | const UART_MCR: u8 = 0x04; // Modem Control Register 20 | const UART_LSR: u8 = 0x05; // Line Status Register 21 | const UART_MSR: u8 = 0x06; // Modem Status Register 22 | const UART_SCR: u8 = 0x07; // Scratch Register 23 | 24 | const UART_LCR_DLAB: u8 = 0x80; // Divisor Latch Bit 25 | const UART_LCR_8BIT: u8 = 0x03; // 8-bit 26 | const UART_LCR_PODD: u8 = 0x08; // Parity Odd 27 | 28 | const UART_LSR_DA: u8 = 0x01; // Data Available 29 | const UART_LSR_OE: u8 = 0x02; // Overrun Error 30 | const UART_LSR_PE: u8 = 0x04; // Parity Error 31 | const UART_LSR_FE: u8 = 0x08; // Framing Error 32 | const UART_LSR_BI: u8 = 0x10; // Break indicator 33 | const UART_LSR_RE: u8 = 0x20; // THR is empty 34 | const UART_LSR_RI: u8 = 0x40; // THR is empty and line is idle 35 | const UART_LSR_EF: u8 = 0x80; // Erroneous data in FIFO 36 | 37 | /// Setup the memory required to initialize the UART 38 | pub fn initialize() { 39 | unsafe { 40 | ptr::write_volatile(NS16550A_UART.offset(UART_LCR as isize), UART_LCR_DLAB); 41 | ptr::write_volatile( 42 | NS16550A_UART.offset(UART_DLL as isize), 43 | (DIVISOR & 0xff) as u8, 44 | ); 45 | ptr::write_volatile( 46 | NS16550A_UART.offset(UART_DLM as isize), 47 | ((DIVISOR >> 8) & 0xff) as u8, 48 | ); 49 | ptr::write_volatile( 50 | NS16550A_UART.offset(UART_LCR as isize), 51 | UART_LCR_PODD | UART_LCR_8BIT, 52 | ); 53 | } 54 | } 55 | 56 | /// Write a single character 57 | fn putchar(ch: u8) { 58 | unsafe { 59 | while (ptr::read_volatile(NS16550A_UART.offset(UART_LSR as isize)) & UART_LSR_RI) == 0 {} 60 | ptr::write_volatile(NS16550A_UART, ch & 0xff) 61 | } 62 | } 63 | 64 | // TODO(bwb): Writer logic belongs elsewhere 65 | pub struct Writer; 66 | 67 | impl fmt::Write for Writer { 68 | fn write_str(&mut self, s: &str) -> fmt::Result { 69 | for byte in s.bytes() { 70 | putchar(byte); 71 | } 72 | Ok(()) 73 | } 74 | } 75 | --------------------------------------------------------------------------------