diff --git a/crush.core b/crush.core
index 1776347..32fe3e6 100644
--- a/crush.core
+++ b/crush.core
@@ -19,7 +19,7 @@ filesets:
- rtl/soc/memory_ice40_spram.v
- rtl/soc/top.v
- rtl/soc/gpio.v
- - fw/binary/crush-blinky.data:
+ - fw/rust/binary/crush-blinky.data:
file_type: user
copyto: fw.data
file_type: verilogSource
diff --git a/fw/.clang-tidy b/fw/c/.clang-tidy
similarity index 100%
rename from fw/.clang-tidy
rename to fw/c/.clang-tidy
diff --git a/fw/.idea/.gitignore b/fw/c/.idea/.gitignore
similarity index 100%
rename from fw/.idea/.gitignore
rename to fw/c/.idea/.gitignore
diff --git a/fw/.idea/.name b/fw/c/.idea/.name
similarity index 100%
rename from fw/.idea/.name
rename to fw/c/.idea/.name
diff --git a/fw/c/.idea/cmake.xml b/fw/c/.idea/cmake.xml
new file mode 100644
index 0000000..e300a94
--- /dev/null
+++ b/fw/c/.idea/cmake.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw/.idea/discord.xml b/fw/c/.idea/discord.xml
similarity index 100%
rename from fw/.idea/discord.xml
rename to fw/c/.idea/discord.xml
diff --git a/fw/.idea/fw.iml b/fw/c/.idea/fw.iml
similarity index 100%
rename from fw/.idea/fw.iml
rename to fw/c/.idea/fw.iml
diff --git a/fw/.idea/inspectionProfiles/Project_Default.xml b/fw/c/.idea/inspectionProfiles/Project_Default.xml
similarity index 100%
rename from fw/.idea/inspectionProfiles/Project_Default.xml
rename to fw/c/.idea/inspectionProfiles/Project_Default.xml
diff --git a/fw/.idea/misc.xml b/fw/c/.idea/misc.xml
similarity index 100%
rename from fw/.idea/misc.xml
rename to fw/c/.idea/misc.xml
diff --git a/fw/.idea/modules.xml b/fw/c/.idea/modules.xml
similarity index 100%
rename from fw/.idea/modules.xml
rename to fw/c/.idea/modules.xml
diff --git a/fw/.idea/vcs.xml b/fw/c/.idea/vcs.xml
similarity index 75%
rename from fw/.idea/vcs.xml
rename to fw/c/.idea/vcs.xml
index 6c0b863..64713b8 100644
--- a/fw/.idea/vcs.xml
+++ b/fw/c/.idea/vcs.xml
@@ -1,6 +1,7 @@
+
\ No newline at end of file
diff --git a/fw/CMakeLists.txt b/fw/c/CMakeLists.txt
similarity index 100%
rename from fw/CMakeLists.txt
rename to fw/c/CMakeLists.txt
diff --git a/fw/TC-crush.cmake b/fw/c/TC-crush.cmake
similarity index 100%
rename from fw/TC-crush.cmake
rename to fw/c/TC-crush.cmake
diff --git a/fw/binary/crush-blinky.data b/fw/c/binary/crush-blinky.data
similarity index 100%
rename from fw/binary/crush-blinky.data
rename to fw/c/binary/crush-blinky.data
diff --git a/fw/crush.ld b/fw/c/crush.ld
similarity index 100%
rename from fw/crush.ld
rename to fw/c/crush.ld
diff --git a/fw/hal.c b/fw/c/hal.c
similarity index 100%
rename from fw/hal.c
rename to fw/c/hal.c
diff --git a/fw/hal.h b/fw/c/hal.h
similarity index 100%
rename from fw/hal.h
rename to fw/c/hal.h
diff --git a/fw/main.c b/fw/c/main.c
similarity index 100%
rename from fw/main.c
rename to fw/c/main.c
diff --git a/fw/start.S b/fw/c/start.S
similarity index 100%
rename from fw/start.S
rename to fw/c/start.S
diff --git a/fw/start.c b/fw/c/start.c
similarity index 100%
rename from fw/start.c
rename to fw/c/start.c
diff --git a/fw/rust/.cargo/config.toml b/fw/rust/.cargo/config.toml
new file mode 100644
index 0000000..2c5d626
--- /dev/null
+++ b/fw/rust/.cargo/config.toml
@@ -0,0 +1,5 @@
+[build]
+target = "riscv32i-unknown-none-elf"
+
+[target.riscv32i-unknown-none-elf]
+rustflags = ["-C", "link-arg=-Tcrush.ld"]
\ No newline at end of file
diff --git a/fw/rust/.gitignore b/fw/rust/.gitignore
new file mode 100644
index 0000000..eb5a316
--- /dev/null
+++ b/fw/rust/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/fw/rust/.idea/.gitignore b/fw/rust/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/fw/rust/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/fw/rust/.idea/discord.xml b/fw/rust/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/fw/rust/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw/rust/.idea/modules.xml b/fw/rust/.idea/modules.xml
new file mode 100644
index 0000000..8e518a5
--- /dev/null
+++ b/fw/rust/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw/rust/.idea/rust.iml b/fw/rust/.idea/rust.iml
new file mode 100644
index 0000000..c254557
--- /dev/null
+++ b/fw/rust/.idea/rust.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw/rust/.idea/vcs.xml b/fw/rust/.idea/vcs.xml
new file mode 100644
index 0000000..b2bdec2
--- /dev/null
+++ b/fw/rust/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fw/rust/Cargo.lock b/fw/rust/Cargo.lock
new file mode 100644
index 0000000..f255837
--- /dev/null
+++ b/fw/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "crush_blinky"
+version = "0.1.0"
diff --git a/fw/rust/Cargo.toml b/fw/rust/Cargo.toml
new file mode 100644
index 0000000..3fe87a0
--- /dev/null
+++ b/fw/rust/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "crush_blinky"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/fw/rust/binary/crush-blinky.bin b/fw/rust/binary/crush-blinky.bin
new file mode 100755
index 0000000..c6101f9
Binary files /dev/null and b/fw/rust/binary/crush-blinky.bin differ
diff --git a/fw/rust/binary/crush-blinky.data b/fw/rust/binary/crush-blinky.data
new file mode 100644
index 0000000..fa7fdce
--- /dev/null
+++ b/fw/rust/binary/crush-blinky.data
@@ -0,0 +1,78 @@
+@00000000
+17210000 13010100 97000000 E780C02B
+130101FE 232E1100 13050000 2322A100
+37250000 13050571 2324A100 03254100
+83258100 97000000 E780001D 2326A100
+2328B100 6F004000 1305C100 97000000
+E7808019 232CB100 232AA100 03254101
+631A0500 6F004000 8320C101 13010102
+67800000 13000000 6FF01FFD 130101FF
+93050500 2324B100 2326B100 1305F001
+636EB500 6F004000 83258100 13051000
+3315B500 13010101 67800000 37050010
+13050542 B7050010 1386C540 93053002
+97000000 E780802F 731000C0 130101FE
+232E1100 37050040 2328A100 232AA100
+03250500 232CA100 03258101 2326A100
+13058000 97000000 E78080F8 93050500
+0325C100 3375B500 3335A000 8320C101
+13010102 67800000 130101FB 23261104
+2328A100 232AA100 A30DB100 639E0504
+6F004000 03250101 B7050040 2324B100
+2328B102 232AB102 83A50500 232CB102
+83258103 2326B100 232EB100 97000000
+E78000F2 83258100 13060500 0325C100
+1346F6FF 3375C500 2322B104 2324A104
+23A0A500 6F004005 03250101 B7050040
+2320B100 2322B102 2324B102 83A50500
+2326B102 8325C102 2322B100 2320B102
+97000000 E780C0EC 83250100 13060500
+03254100 3365C500 232EB102 2320A104
+23A0A500 6F004000 8320C104 13010105
+67800000 130101FF 23261100 2324A100
+97000000 E7808002 8320C100 13010101
+67800000 130101FF 2324A100 2326B100
+13010101 67800000 130101FD 23261102
+93050500 2328B100 232EB100 13854500
+2322B102 2324A102 03A50500 83A54500
+634AB500 6F004000 13050000 232AA100
+6F004004 03250101 03250500 2324A100
+2320A102 93051000 2326B100 97000000
+E780C003 83260101 83258100 13060500
+0325C100 23A0C600 232CB100 232AA100
+6F004000 03254101 83258101 8320C102
+13010103 67800000 130101FF 2322A100
+2324B100 2326B100 3305B500 13010101
+67800000 97000000 E780C001 731000C0
+130101FF 2326A100 6F004000 6F000000
+130101FF 23261100 13050000 2305A100
+6F004000 0345A100 1345F5FF 13751500
+2305A100 97000000 E78080DC 2322A100
+A305A100 8345A100 13050000 97000000
+E780C0DF 83254100 13051000 97000000
+E780C0DE 97000000 E780C0CD 6FF09FFB
+67800000 B7F54DC9 93856570 2326B500
+B7B56D2F 9385A572 2324B500 B7553D82
+9385B532 2322B500 B7555202 9385F5F7
+2320B500 67800000 130101FE 232E1100
+37060010 13064644 2324C100 37060010
+13064644 2326C100 232AA100 2328B100
+13051000 230CA100 13058100 97000000
+E78040F2 731000C0 130101FD 23261102
+2322A102 2324B102 13054102 2326A100
+13051000 2328A100 232E0100 37050010
+13054544 232AA100 232C0100 1305C100
+93050600 97000000 E78040F8 731000C0
+7372632F 68616C2E 72730000 00040010
+0A000000 0E000000 05000000 00000000
+61747465 6D707420 746F2073 68696674
+206C6566 74207769 7468206F 76657266
+6C6F7700 40030010 00000000 01000000
+44030010 10000000 00000000 017A5200
+017C0101 1B0C0200 10000000 18000000
+D0FEFFFF 04000000 00000000 10000000
+2C000000 C0FEFFFF 34000000 00000000
+14000000 40000000 E0FEFFFF 40000000
+00440E20 44810100 14000000 58000000
+08FFFFFF 48000000 00440E30 44810100
+00000000
diff --git a/fw/rust/build.sh b/fw/rust/build.sh
new file mode 100755
index 0000000..fb98303
--- /dev/null
+++ b/fw/rust/build.sh
@@ -0,0 +1,2 @@
+cargo objcopy -- -O binary binary/crush-blinky.bin
+objcopy -I binary --verilog-data-width 4 -O verilog binary/crush-blinky.bin binary/crush-blinky.data
diff --git a/fw/rust/crush.ld b/fw/rust/crush.ld
new file mode 100644
index 0000000..d879ea3
--- /dev/null
+++ b/fw/rust/crush.ld
@@ -0,0 +1,33 @@
+OUTPUT_ARCH( "riscv" )
+ENTRY(start)
+
+MEMORY
+{
+ mem (rwx): ORIGIN = 0x10000000, LENGTH = 0x800 * 4
+}
+
+SECTIONS
+{
+ .mem : {
+ . = ALIGN(4);
+ /* FIXME: must use ".init" due to possible bug in rust:
+ * https://github.com/rust-lang/rust/issues/117323
+ */
+ KEEP(*(.init));
+ *(.text .text.*);
+ *(.rodata .rodata*);
+ . = ALIGN(4);
+ *(.data .data.*);
+ } > mem
+
+ .bss (NOLOAD) : {
+ . = ALIGN(4);
+ bss_start = .;
+ *(.bss .bss.*);
+ . = ALIGN(4);
+ bss_end = .;
+ } > mem
+
+ stack_start = ORIGIN(mem) + LENGTH(mem);
+}
+
diff --git a/fw/rust/src/hal.rs b/fw/rust/src/hal.rs
new file mode 100644
index 0000000..304c7d3
--- /dev/null
+++ b/fw/rust/src/hal.rs
@@ -0,0 +1,35 @@
+use core::arch::asm;
+
+pub(crate) fn delay() {
+ for _ in 0..10_000 {
+ unsafe {
+ asm!("nop");
+ }
+ }
+}
+
+const GPIO_BASE_ADDRESS: *mut u32 = 0x40000000 as *mut u32;
+
+const fn bit_mask(i: u32) -> u32 {
+ 1 << i
+}
+
+pub(crate) fn get_button() -> bool {
+ unsafe {
+ return (GPIO_BASE_ADDRESS.read_volatile() & bit_mask(8)) != 0;
+ }
+}
+
+pub(crate) fn set_led_on(index: u32, led_on: bool) {
+ if led_on {
+ unsafe {
+ let value = GPIO_BASE_ADDRESS.read_volatile();
+ GPIO_BASE_ADDRESS.write_volatile(value | bit_mask(index));
+ }
+ } else {
+ unsafe {
+ let value = GPIO_BASE_ADDRESS.read_volatile();
+ GPIO_BASE_ADDRESS.write_volatile(value & !bit_mask(index));
+ }
+ }
+}
diff --git a/fw/rust/src/main.rs b/fw/rust/src/main.rs
new file mode 100644
index 0000000..6e417ac
--- /dev/null
+++ b/fw/rust/src/main.rs
@@ -0,0 +1,24 @@
+#![no_main]
+#![no_std]
+
+use core::panic::PanicInfo;
+
+mod hal;
+mod start;
+
+#[panic_handler]
+fn panic(_panic: &PanicInfo) -> ! {
+ loop {}
+}
+
+pub fn main() -> ! {
+ let mut blinky_led_on = false;
+ loop {
+ blinky_led_on = !blinky_led_on;
+
+ let is_pressed = hal::get_button();
+ hal::set_led_on(0, blinky_led_on);
+ hal::set_led_on(1, is_pressed);
+ hal::delay();
+ }
+}
diff --git a/fw/rust/src/start.S b/fw/rust/src/start.S
new file mode 100644
index 0000000..a6bf701
--- /dev/null
+++ b/fw/rust/src/start.S
@@ -0,0 +1,7 @@
+.section .init
+.global start
+start:
+ // Load stack_start defined by the linker file to the stack pointer
+ la sp, stack_start
+ // Call c_start to do the rest of the initialisation
+ call c_start
diff --git a/fw/rust/src/start.rs b/fw/rust/src/start.rs
new file mode 100644
index 0000000..ff19b61
--- /dev/null
+++ b/fw/rust/src/start.rs
@@ -0,0 +1,16 @@
+//! Start-up code for hardware initialisation.
+
+// FIXME: import bss addresses from linker script
+
+use crate::main;
+use core::arch::global_asm;
+
+global_asm!(include_str!("start.S"));
+
+#[no_mangle]
+// FIXME: should this be unsafe?
+pub extern "C" fn c_start() -> ! {
+ // FIXME: bss init
+
+ main();
+}
diff --git a/rtl/soc/memory_infer.v b/rtl/soc/memory_infer.v
index 164ec6e..f580531 100644
--- a/rtl/soc/memory_infer.v
+++ b/rtl/soc/memory_infer.v
@@ -29,7 +29,7 @@ module memory_infer #(
reg [31:0] data;
assign dat_o = ack_o ? data : 32'hzzzz_zzzz;
- wire addressed = (adr_i >= BASE_ADDRESS) & (adr_i < BASE_ADDRESS + SIZE);
+ wire addressed = (adr_i >= BASE_ADDRESS) & (adr_i < BASE_ADDRESS + 4 * SIZE);
// depending on the size, some address bits are not going to be used
// verilator lint_off UNUSEDSIGNAL