├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build.sh └── src ├── _start.S ├── board.rs ├── board ├── clocks.rs ├── gpio.rs ├── interrupts.rs ├── platform.rs └── timer.rs ├── boot ├── header.bin └── toc.bin ├── debug.rs ├── kernel.rs ├── lib.rs ├── linker.ld ├── sys.rs └── sys ├── context.rs ├── lists.rs └── mem.rs /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | 3 | # Added by cargo 4 | 5 | /target 6 | Cargo.lock 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "armv7a-rust-os" 3 | version = "0.1.0" 4 | authors = ["Josh Cole "] 5 | edition = "2018" 6 | 7 | [unstable] 8 | asm = 'yes' 9 | 10 | [lib] 11 | name = "kernel" 12 | path = "src/lib.rs" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Joshua Cole 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-kernel 2 | Welcome! This project is designed to run a baremetal rust program on the [beaglebone black](https://beagleboard.org/black). I am actively developing the various modules for this project, and you can expect more support for board features over time. 3 | 4 | ## Technical Articles 5 | My motivation for this project is primarily to act as a forcing function to help me practice writing technical articles. Each module I develop will eventually be turned into a blog post. Here is a list of the documentation I've written so far: 6 | 7 | - Compiling: https://sharpcoder.medium.com/baremetal-rust-for-beaglebone-black-part-1-compilation-6609cdc2545c 8 | - Creating a Stack Datastructure: https://medium.com/baremetal-rust/baremetal-rust-for-beaglebone-black-implementing-a-stack-6da5af43f677 9 | 10 | 11 | ## Compiling 12 | 13 | ### Configuring your environment 14 | You will need the following in order for the `build.sh` script to run successfully. 15 | ``` 16 | # Install gcc tools 17 | sudo apt-get install gcc-arm-none-eabi 18 | 19 | # Configure rust 20 | rustup default nightly 21 | rustup target add arm-unknown-linux-gnueabihf 22 | ``` 23 | 24 | ### Building 25 | 26 | Simply run the `build.sh` script. This will generate `out/` files. The final is `rom.img` which you can flash to an SDCard. From there, you must hold down the "user program" button (labeled S2 on my beaglebone black) in order for the image to load. 27 | 28 | 29 | ### Flashing the rom image 30 | 31 | The `rom.img` which is generated as part of the build process is a valid `.img` file and can be flashed with a variety of tools. On Windows, I use the **Win32 Disk Imager** application. 32 | 33 | ## Technologies 34 | This project is primarily written in [rust](https://www.rust-lang.org). 35 | 36 | 37 | ## Resources 38 | 39 | - (Primary board documentation) [http://www.sal.wisc.edu/st5000/datasheets/tq-systems/spruh73p.pdf](http://www.sal.wisc.edu/st5000/datasheets/tq-systems/spruh73p.pdf) 40 | - (My articles) [https://medium.com/baremetal-rust](https://medium.com/baremetal-rust) 41 | - (Details about Beaglebone) [https://beagleboard.org/black](https://beagleboard.org/black) 42 | 43 | 44 | ## License 45 | [MIT](https://tldrlegal.com/license/mit-license) -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | rm -rf out || true 2 | mkdir out || true 3 | 4 | 5 | # Rust compilation 6 | echo "compiling rust" 7 | rustc -C lto --target arm-unknown-linux-gnueabihf -o out/kernel.o -O --emit=obj src/kernel.rs 8 | echo "compiling assembly" 9 | arm-none-eabi-gcc -c src/_start.S -o out/start.o 10 | echo "linking" 11 | arm-none-eabi-ld -T src/linker.ld out/start.o out/kernel.o -o out/kernel.elf 12 | 13 | echo "generating debug info" 14 | arm-none-eabi-objdump -D out/kernel.o > out/kernel.list 15 | arm-none-eabi-objdump -d -s out/kernel.elf > out/kernel.elf.list 16 | arm-none-eabi-nm out/kernel.o > out/kernel.dump 17 | arm-none-eabi-nm out/kernel.o > out/kernel.elf.dump 18 | 19 | # Final image generation 20 | echo "generating image file" 21 | arm-none-eabi-objcopy out/kernel.elf -O binary out/boot.bin 22 | cat src/boot/toc.bin src/boot/header.bin out/boot.bin > out/rom.img 23 | du -sh out/rom.img 24 | -------------------------------------------------------------------------------- /src/_start.S: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | main: 4 | @ Assign the IRQ interrupt method 5 | LDR r0, base_irq_addr 6 | LDR r1, basic_handler 7 | STR r1,[r0] 8 | 9 | @ Setup sp in IRQ mode 10 | mov r0, #0xD2 11 | msr cpsr_c, r0 12 | ldr r0, stack_base 13 | ADD r0, #1024 14 | mov sp, r0 15 | 16 | @ Enter supervisor mode, irq disabled 17 | mov r0, #0xD3 18 | msr cpsr_c, r0 19 | ldr r0, stack_base 20 | ADD r0, #1024 21 | ADD r0, #1024 22 | mov sp, r0 23 | 24 | @ Enter supervisor mode, irq enabled 25 | mov r0, #0x53 26 | msr cpsr_c, r0 27 | 28 | bl kmain 29 | b hang 30 | 31 | 32 | handle_irq: 33 | SUB lr, lr, #4 34 | STMFD sp!, {R0-R12, lr} 35 | 36 | @ Save SPSR in R11 37 | MRS R11, SPSR 38 | PUSH {r11} 39 | 40 | @ Invoke handler 41 | bl handle_irq_rust 42 | 43 | @ Restore pending program state 44 | POP {r11} 45 | MSR SPSR, R11 46 | 47 | @ Return 48 | LDMFD sp!, {R0-R12, pc}^ 49 | b hang 50 | 51 | 52 | hang: 53 | b hang 54 | 55 | stack_base: .word 0x4030BE30 56 | base_irq_addr: .word 0x4030CE38 57 | basic_handler: .word handle_irq 58 | -------------------------------------------------------------------------------- /src/board.rs: -------------------------------------------------------------------------------- 1 | pub mod clocks; 2 | pub mod gpio; 3 | pub mod interrupts; 4 | pub mod platform; 5 | pub mod timer; -------------------------------------------------------------------------------- /src/board/clocks.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::board::platform::CM_PER; 4 | 5 | pub const CM_PER_L4LS_CLKSTCTRL: u32 = 0x0; 6 | pub const CM_PER_L3S_CLKSTCTRL: u32 = 0x4; 7 | pub const CM_PER_L3_CLKSTCTRL: u32 = 0xC; 8 | pub const CM_PER_EMIF_CLKCTRL: u32 = 0x28; 9 | pub const CM_PER_L4LS_CLKCTRL: u32 = 0x60; 10 | pub const CM_PER_TIMER7_CLKCTRL: u32 = 0x7C; 11 | pub const CM_PER_TIMER2_CLKCTRL: u32 = 0x80; 12 | pub const CM_PER_TIMER3_CLKCTRL: u32 = 0x84; 13 | pub const CM_PER_TIMER4_CLKCTRL: u32 = 0x88; 14 | pub const CM_PER_GPIO1_CLKCTRL: u32 = 0xAC; 15 | pub const CM_PER_GPIO2_CLKCTRL: u32 = 0xB0; 16 | pub const CM_PER_GPIO3_CLKCTRL: u32 = 0xB4; 17 | pub const CM_PER_L3_INSTR_CLKCTRL: u32 = 0xDC; 18 | pub const CM_PER_L3_CLKCTRL: u32 = 0xE0; 19 | pub const CM_PER_TIMER5_CLKCTRL: u32 = 0xEC; 20 | pub const CM_PER_TIMER6_CLKCTRL: u32 = 0xF0; 21 | pub const CM_PER_LCDC_CLKSTCTRL: u32 = 0x148; 22 | pub const CM_PER_L4HS_CLKSTCTRL: u32 = 0x11C; 23 | pub const CM_PER_L4HS_CLKCTRL: u32 = 0x120; 24 | 25 | 26 | pub fn enable_clock_device(address: u32) { 27 | crate::sys::assign(CM_PER + address, 0x2); 28 | } 29 | 30 | pub fn enable_clock_devices(addresses: &[u32]) { 31 | for address in addresses { 32 | enable_clock_device(*address); 33 | } 34 | } -------------------------------------------------------------------------------- /src/board/gpio.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use crate::sys; 3 | use crate::board::platform::{ 4 | CM_PER, GPIO1 5 | }; 6 | 7 | const CM_PER_GPIO1_CLKCTRL: u32 = 0xAC; 8 | const GPIO_OE_OFFSET: u32 = 0x134; 9 | const GPIO_DATAOUT: u32 = 0x13C; 10 | 11 | static mut GPIO_MODE_BITS: u32 = 0xFF; 12 | static mut GPIO_VAL: u32 = 0x00; 13 | 14 | pub enum GpioMode { 15 | Input, 16 | Output, 17 | } 18 | 19 | pub fn init() { 20 | // Enable GPIO1 clock 21 | let clock_ptr = (CM_PER + CM_PER_GPIO1_CLKCTRL) as *mut u32; 22 | unsafe { *clock_ptr = 0x2; } 23 | } 24 | 25 | pub fn configure(pin: u8, mode: GpioMode) { 26 | match mode { 27 | GpioMode::Input => { 28 | unsafe { GPIO_MODE_BITS = sys::set_bit(GPIO_MODE_BITS, pin); } 29 | }, 30 | GpioMode::Output => { 31 | unsafe { GPIO_MODE_BITS = sys::clear_bit(GPIO_MODE_BITS, pin); } 32 | }, 33 | } 34 | 35 | let gpio_oe_ptr = (GPIO1 + GPIO_OE_OFFSET) as *mut u32; 36 | unsafe { *gpio_oe_ptr = GPIO_MODE_BITS; } 37 | } 38 | 39 | pub fn set(pin: u8, val: bool) { 40 | match val { 41 | true => { 42 | unsafe { GPIO_VAL = sys::set_bit(GPIO_VAL, pin); } 43 | }, 44 | false => { 45 | unsafe { GPIO_VAL = sys::clear_bit(GPIO_VAL, pin); } 46 | }, 47 | } 48 | 49 | let gpio_ptr = (GPIO1 + GPIO_DATAOUT) as *mut u32; 50 | unsafe { *gpio_ptr = GPIO_VAL; } 51 | } -------------------------------------------------------------------------------- /src/board/interrupts.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::board::platform; 4 | use crate::sys::{set_bit, read_word, assign}; 5 | 6 | type Ptr = fn(); 7 | 8 | pub const INT_USB0: usize = 18; 9 | pub const INT_USB1: usize = 19; 10 | pub const INT_DMTIMER0: usize = 66; 11 | pub const INT_DMTIMER2: usize = 68; 12 | pub const INT_DMTIMER3: usize = 69; 13 | pub const INT_UART0: usize = 72; 14 | pub const INT_DMTIMER4: usize = 92; 15 | pub const INT_DMTIMER7: usize = 95; 16 | 17 | const INTC_SIR_IRQ_REG: u32 = 0x40; 18 | 19 | static mut VECTOR_HANDLERS: [Ptr; 128] = [noop; 128]; 20 | static MIRN_CLEAR_BANK_ADDRESSES: [u32;4] = [0x88,0xA8,0xC8,0xE8]; 21 | 22 | fn get_address_bank(int_number: usize) -> usize { 23 | let mut int_number = int_number; 24 | let mut bank = 0; 25 | loop { 26 | if int_number > 32 { 27 | int_number -= 32; 28 | bank += 1; 29 | } else { 30 | break; 31 | } 32 | } 33 | 34 | return bank; 35 | } 36 | 37 | pub fn get_active_irq_number() -> usize { 38 | return read_word(platform::INTCPS + INTC_SIR_IRQ_REG) as usize; 39 | } 40 | 41 | pub fn unmask_interrupt(int_number: usize) { 42 | let bank = get_address_bank(int_number); 43 | let mirn_clear_address = platform::INTCPS + MIRN_CLEAR_BANK_ADDRESSES[bank]; 44 | let next_value = set_bit(0x0, int_number as u8); 45 | assign(mirn_clear_address, next_value); 46 | } 47 | 48 | pub fn register_handler(int_number: usize, method: Ptr) { 49 | unsafe { 50 | VECTOR_HANDLERS[int_number] = method; 51 | } 52 | } 53 | 54 | pub fn service_handler(int_number: usize) { 55 | unsafe { 56 | VECTOR_HANDLERS[int_number](); 57 | } 58 | } 59 | 60 | pub fn clear_interrupts() { 61 | crate::sys::assign(platform::INTCPS + 0x48, 0x1); 62 | } 63 | 64 | pub fn noop() { 65 | 66 | } -------------------------------------------------------------------------------- /src/board/platform.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This outlines the ARM CORTEX-A8 memory mapping 3 | as defined in the Technical Reference Manual. Check out 4 | page 177 for the start of the memory mappings. 5 | */ 6 | #![allow(dead_code)] 7 | // Core memory locations 8 | pub const CM_PER: u32 = 0x44E0_0000; // Clock Module Peripheral Registers 9 | pub const CM_DPLL: u32 = 0x44E0_0500; // Clock Module PLL Registers 10 | pub const CM_MPU: u32 = 0x44E0_0600; 11 | pub const CM_DEVICE: u32 = 0x44E0_0700; 12 | pub const CM_WKUP: u32 = 0x44E0_0400; 13 | pub const PRM_PER: u32 = 0x44E0_0C00; 14 | pub const PRM_IRQ: u32 = 0x44E0_0B00; 15 | pub const PRM_MPU: u32 = 0x44E0_0E00; 16 | pub const PRM_WKUP: u32 = 0x44E0_0D00; 17 | pub const PRM_DEVICE: u32 = 0x44E0_0F00; 18 | pub const GPIO1: u32 = 0x4804_C000; // GPIO1 Registers 19 | pub const DMTIMER0: u32 = 0x44E0_5000; // DMTimer0 Registers 20 | pub const DMTIMER1: u32 = 0x44E3_1000; 21 | pub const DMTIMER2: u32 = 0x4804_0000; 22 | pub const DMTIMER3: u32 = 0x4804_2000; 23 | pub const DMTIMER4: u32 = 0x4804_4000; 24 | pub const DMTIMER5: u32 = 0x4804_6000; 25 | pub const DMTIMER6: u32 = 0x4804_8000; 26 | pub const DMTIMER7: u32 = 0x4804_A000; 27 | pub const EMIF0_SDRAM: u32 = 0x8000_0000; // 1GB SDRAM 28 | pub const OCMC0_BASE_PTR: u32 = 0x4030_0000; 29 | pub const LCD_CONTROLLER: u32 = 0x4830_E000; 30 | pub const INTCPS: u32 = 0x4820_0000; // Interrupt controller registers 31 | pub const EDMA3CC: u32 = 0x4900_0000; 32 | 33 | // Some useful constants 34 | pub const GB1: u32 = 0x3FFF_FFFF; 35 | pub const KB64: u32 = 0x1_0000; 36 | 37 | // /// Data Synchronization Barrie 38 | // pub fn dsb() { 39 | // unsafe { 40 | // asm!(" 41 | // MOV R6, #0 42 | // MCR P15, #0, R6, C7, C10, #4 43 | // "); 44 | // } 45 | // } 46 | 47 | // /// Instruction synchronization barrier 48 | // pub fn isb() { 49 | // unsafe { 50 | // asm!(" 51 | // MOV R6, #0 52 | // MCR P15, #0, R6, C7, C5, #4 53 | // "); 54 | // } 55 | // } 56 | 57 | // /// Memory barrier 58 | // pub fn mb() { 59 | // unsafe { 60 | // asm!(" 61 | // MOV R6, #0 62 | // MCR P15, #0, R6, C7, C10, #5 63 | // "); 64 | // } 65 | // } -------------------------------------------------------------------------------- /src/board/timer.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This submodule interfaces with the on-board 32,768-Hz timer to 3 | produce 1ms interrupts which will be utilized for a proper 4 | sleep mechanism. 5 | */ 6 | #![allow(dead_code)] 7 | const TIOCP_CFG: u32 = 0x10; 8 | const IRQ_EOI_REG: u32 = 0x20; 9 | const IRQENABLE_SET_REG: u32 = 0x2C; 10 | const IRQSTATUS_REG: u32 = 0x28; 11 | const IRQENABLE_CLR_REG: u32 = 0x30; 12 | const TCLR_REG: u32 = 0x38; 13 | const TCRR_REG: u32 = 0x3C; 14 | const TLDR_REG: u32 = 0x40; 15 | const TTRG_REG: u32 = 0x44; 16 | 17 | pub const ENABLE_AUTO_RELOAD: u32 = 0x1; 18 | pub const IRQ_MATCH_MODE: u32 = 0x2; 19 | pub const IRQ_OVERFLOW_MODE: u32 = 0x4; 20 | pub const IRQ_CAPTURE_MODE: u32 = 0x8; 21 | 22 | 23 | pub struct Timer { 24 | base_addr: u32, 25 | pub config_mask: u32, 26 | ticks: u32, 27 | } 28 | 29 | impl Timer { 30 | pub const fn new(base_addr: u32) -> Self { 31 | return Timer { 32 | base_addr: base_addr, 33 | config_mask: 0x0, 34 | ticks: 0, 35 | }; 36 | } 37 | 38 | pub fn incr(&mut self) { 39 | self.ticks += 1; 40 | } 41 | 42 | pub fn elapsed(&self) -> u32 { 43 | return self.ticks; 44 | } 45 | 46 | pub fn configure(&mut self, bit_mask: u32) { 47 | self.config_mask = bit_mask; 48 | } 49 | 50 | pub fn start(&self) { 51 | // Initialize 52 | crate::sys::assign(self.base_addr + TIOCP_CFG, 0x0); 53 | crate::sys::assign(self.base_addr + TCLR_REG, 0x1 | ((0x1 & self.config_mask) << 1)); 54 | } 55 | 56 | pub fn stop(&self) { 57 | crate::sys::assign(self.base_addr + TCLR_REG, 0x0); 58 | } 59 | 60 | pub fn set_load_value(&self, value: u32) { 61 | crate::sys::assign(self.base_addr + TLDR_REG, value); 62 | } 63 | 64 | pub fn set_value(&self, value: u32) { 65 | crate::sys::assign(self.base_addr + TTRG_REG, value); 66 | crate::sys::assign(self.base_addr + TCRR_REG, value); 67 | } 68 | 69 | pub fn irq_enable(&self) { 70 | crate::sys::assign(self.base_addr + IRQENABLE_SET_REG, (0xE & self.config_mask) >> 1); 71 | } 72 | 73 | pub fn irq_disable(&self) { 74 | crate::sys::assign(self.base_addr + IRQENABLE_CLR_REG, 0x2); 75 | } 76 | 77 | pub fn irq_clear(&self) { 78 | crate::sys::assign(self.base_addr + IRQSTATUS_REG, 0x7); 79 | } 80 | 81 | pub fn irq_acknowledge(&self) { 82 | crate::sys::assign(self.base_addr + IRQ_EOI_REG, 0x0); 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod test_timer { 88 | 89 | use super::*; 90 | 91 | #[test] 92 | fn test_bit_shift() { 93 | assert_eq!((0xE & IRQ_MATCH_MODE) >> 1, 0x1); 94 | assert_eq!((0xE & IRQ_OVERFLOW_MODE) >> 1, 0x2); 95 | assert_eq!((0xE & (IRQ_MATCH_MODE | IRQ_OVERFLOW_MODE)) >> 1, 0x3); 96 | assert_eq!( 0x1 | ((0x1 & 0x0) << 1), 0x1); 97 | assert_eq!( 0x1 | ((0x1 & (ENABLE_AUTO_RELOAD | IRQ_OVERFLOW_MODE | IRQ_MATCH_MODE)) << 1), 0x3); 98 | } 99 | } -------------------------------------------------------------------------------- /src/boot/header.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharpCoder/rust-kernel/0464e2e52a9817563d87bd136a14b64a31eb80a2/src/boot/header.bin -------------------------------------------------------------------------------- /src/boot/toc.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SharpCoder/rust-kernel/0464e2e52a9817563d87bd136a14b64a31eb80a2/src/boot/toc.bin -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Debug helper 3 | This concept is silly but something I've always wanted to do. 4 | Write a debug helper which outputs morse code, so you don't need a monitor :) 5 | */ 6 | #![allow(dead_code)] 7 | use crate::board::gpio; 8 | use crate::sys::{wait_ms}; 9 | use crate::sys::lists::{Stack}; 10 | 11 | // Configure the gpio pin which is used as data output 12 | const GPIO_PIN: u8 = 21; 13 | const SLEEP_TIME_MS: u32 = 350; 14 | 15 | const DOT_TIME: u32 = 1; 16 | const WHACK_TIME: u32 = DOT_TIME * 3; 17 | const LETTER_REST_TIME: u32 = DOT_TIME * 3; 18 | const WORD_REST_TIME: u32 = (DOT_TIME * 7) - LETTER_REST_TIME; 19 | 20 | pub fn emit(input: &[u8]) { 21 | for c in input { 22 | emit_char(*c as char); 23 | emit_rest(); 24 | } 25 | emit_word_rest(); 26 | } 27 | 28 | pub fn emit_num(input: u32) { 29 | let mut temp = input; 30 | let mut stack = Stack::new(); 31 | 32 | while temp > 0 { 33 | let v: u8 = (temp % 10) as u8; 34 | stack.push(v); 35 | temp /= 10; 36 | } 37 | 38 | loop { 39 | match stack.pop() { 40 | None => break, 41 | Some(element) => { 42 | emit_char(num_to_char(element)); 43 | emit_rest(); 44 | }, 45 | } 46 | } 47 | } 48 | 49 | pub fn emit_char(character: char) { 50 | match character { 51 | 'a' | 'A' => { emit_dot(1); emit_whack(1); }, 52 | 'b' | 'B' => { emit_whack(1); emit_dot(3); }, 53 | 'c' | 'C' => { emit_whack(1); emit_dot(1); emit_whack(1); emit_dot(1); }, 54 | 'd' | 'D' => { emit_whack(1); emit_dot(2); }, 55 | 'e' | 'E' => { emit_dot(1); }, 56 | 'f' | 'F' => { emit_dot(2); emit_whack(1); emit_dot(1); }, 57 | 'g' | 'G' => { emit_whack(2); emit_dot(1); }, 58 | 'h' | 'H' => { emit_dot(4); }, 59 | 'i' | 'I' => { emit_dot(2); }, 60 | 'j' | 'J' => { emit_dot(1); emit_whack(3); }, 61 | 'k' | 'K' => { emit_whack(1); emit_dot(1); emit_whack(1); }, 62 | 'l' | 'L' => { emit_dot(1); emit_whack(1); emit_dot(2); }, 63 | 'm' | 'M' => { emit_whack(2); }, 64 | 'n' | 'N' => { emit_whack(1); emit_dot(1); }, 65 | 'o' | 'O' => { emit_whack(3); }, 66 | 'p' | 'P' => { emit_dot(1); emit_whack(2); emit_dot(1); }, 67 | 'q' | 'Q' => { emit_whack(2); emit_dot(1); emit_whack(1); }, 68 | 'r' | 'R' => { emit_dot(1); emit_whack(1); emit_dot(1); }, 69 | 's' | 'S' => { emit_dot(3); }, 70 | 't' | 'T' => { emit_whack(1); }, 71 | 'u' | 'U' => { emit_dot(2); emit_whack(1); }, 72 | 'v' | 'V' => { emit_dot(3); emit_whack(1); }, 73 | 'w' | 'W' => { emit_dot(1); emit_whack(2); }, 74 | 'x' | 'X' => { emit_whack(1); emit_dot(2); emit_whack(1); }, 75 | 'y' | 'Y' => { emit_whack(1); emit_dot(1); emit_whack(2); }, 76 | 'z' | 'Z' => { emit_whack(2); emit_dot(2); }, 77 | 78 | '1' => { emit_dot(1); emit_whack(4); }, 79 | '2' => { emit_dot(2); emit_whack(3); }, 80 | '3' => { emit_dot(3); emit_whack(2); }, 81 | '4' => { emit_dot(4); emit_whack(1); }, 82 | '5' => { emit_dot(5); }, 83 | '6' => { emit_whack(1); emit_dot(4); } 84 | '7' => { emit_whack(2); emit_dot(3); } 85 | '8' => { emit_whack(3); emit_dot(2); } 86 | '9' => { emit_whack(4); emit_dot(1); } 87 | '0' => { emit_whack(5); } 88 | 89 | ' ' => { emit_word_rest(); } 90 | _ => { }, 91 | } 92 | } 93 | 94 | fn emit_dot(repetition: usize) { 95 | for _ in 0 .. repetition { 96 | gpio::set(GPIO_PIN, true); 97 | wait_ms(SLEEP_TIME_MS * DOT_TIME); 98 | gpio::set(GPIO_PIN, false); 99 | wait_ms(SLEEP_TIME_MS * DOT_TIME); 100 | } 101 | } 102 | 103 | fn emit_whack(repition: usize) { 104 | for _ in 0 .. repition { 105 | gpio::set(GPIO_PIN, true); 106 | wait_ms(SLEEP_TIME_MS * WHACK_TIME); 107 | gpio::set(GPIO_PIN, false); 108 | wait_ms(SLEEP_TIME_MS * DOT_TIME); 109 | } 110 | } 111 | 112 | fn emit_rest() { 113 | gpio::set(GPIO_PIN, false); 114 | wait_ms(SLEEP_TIME_MS * LETTER_REST_TIME); 115 | } 116 | 117 | fn emit_word_rest() { 118 | gpio::set(GPIO_PIN, false); 119 | wait_ms(SLEEP_TIME_MS * WORD_REST_TIME); 120 | } 121 | 122 | fn num_to_char(input: u8) -> char { 123 | if input >= 10 { 124 | return '\0'; 125 | } else { 126 | return (('0' as u8) + input) as char; 127 | } 128 | } 129 | 130 | #[cfg(test)] 131 | mod test { 132 | use super::*; 133 | 134 | #[test] 135 | fn test_number_to_char() { 136 | assert_eq!(num_to_char(0), '0'); 137 | assert_eq!(num_to_char(1), '1'); 138 | } 139 | 140 | #[test] 141 | fn test_emit_num() { 142 | let mut temp = 102; 143 | assert_eq!((temp % 10) as u8, 2); 144 | temp /= 10; 145 | assert_eq!((temp % 10) as u8, 0); 146 | temp /= 10; 147 | assert_eq!((temp % 10) as u8, 1); 148 | } 149 | } -------------------------------------------------------------------------------- /src/kernel.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items, asm)] 2 | #![crate_type = "staticlib"] 3 | #![no_std] 4 | mod sys; 5 | mod board; 6 | mod debug; 7 | 8 | use board::{clocks, timer, platform, interrupts}; 9 | use sys::context::{configure_context, SystemContext, get_context}; 10 | 11 | const CLOCK_RELOAD_VALUE: u32 = 0xFFFF_FFDF; 12 | 13 | #[no_mangle] 14 | pub fn kmain() { 15 | 16 | // Configure the main application context 17 | configure_context(SystemContext { 18 | sysclock: timer::Timer::new(platform::DMTIMER2), 19 | }); 20 | 21 | initialize_platform(); 22 | initialize_interrupts(); 23 | 24 | loop { 25 | debug::emit(b"hello world"); 26 | unsafe { 27 | asm!("nop"); 28 | } 29 | } 30 | } 31 | 32 | fn initialize_platform() { 33 | clocks::enable_clock_devices(&[ 34 | clocks::CM_PER_L4LS_CLKSTCTRL, 35 | clocks::CM_PER_GPIO1_CLKCTRL, 36 | clocks::CM_PER_EMIF_CLKCTRL, 37 | clocks::CM_PER_TIMER2_CLKCTRL, 38 | ]); 39 | 40 | // Set GPIO pins for USR LED's to output 41 | for i in 21 ..= 24 { 42 | board::gpio::configure(i, board::gpio::GpioMode::Output); 43 | } 44 | 45 | // Set the on gpio pin 46 | board::gpio::set(24, true); 47 | } 48 | 49 | fn initialize_interrupts() { 50 | let context = get_context(); 51 | 52 | // Enable DMTimer2 53 | context.sysclock.stop(); 54 | context.sysclock.set_load_value(CLOCK_RELOAD_VALUE); 55 | context.sysclock.set_value(CLOCK_RELOAD_VALUE); 56 | context.sysclock.configure(timer::ENABLE_AUTO_RELOAD | timer::IRQ_OVERFLOW_MODE); 57 | context.sysclock.irq_enable(); 58 | 59 | // Wire up register 60 | interrupts::register_handler(interrupts::INT_DMTIMER2, handle_timer_irq); 61 | interrupts::unmask_interrupt(interrupts::INT_DMTIMER2); 62 | 63 | // Start the clock 64 | context.sysclock.start(); 65 | } 66 | 67 | fn handle_timer_irq() { 68 | let context = get_context(); 69 | 70 | context.sysclock.irq_disable(); 71 | context.sysclock.stop(); 72 | context.sysclock.irq_acknowledge(); 73 | context.sysclock.irq_clear(); 74 | context.sysclock.set_value(CLOCK_RELOAD_VALUE); 75 | context.sysclock.incr(); 76 | context.sysclock.irq_enable(); 77 | context.sysclock.start(); 78 | } 79 | 80 | #[no_mangle] 81 | fn handle_irq_rust() { 82 | let int_number = interrupts::get_active_irq_number(); 83 | interrupts::service_handler(int_number); 84 | interrupts::clear_interrupts(); 85 | } 86 | 87 | #[lang = "eh_personality"] 88 | #[no_mangle] 89 | pub extern fn eh_personality() { } 90 | 91 | #[panic_handler] 92 | #[no_mangle] 93 | pub extern fn my_panic(_info: &core::panic::PanicInfo) -> ! { 94 | loop { } 95 | } 96 | 97 | #[no_mangle] 98 | pub extern "C" fn __aeabi_unwind_cpp_pr0() { } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(asm)] 2 | 3 | pub mod sys; 4 | pub mod debug; 5 | pub mod board; 6 | 7 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | sram is defined as position 402F0400 per 2.1 in the documentation. 3 | It contains a length of 64kb 4 | */ 5 | MEMORY { 6 | sram (rwx) : ORIGIN = 0x402F0400, LENGTH = 0xFFFF 7 | } 8 | 9 | /* Section variables */ 10 | ENTRY(main) 11 | SECTIONS { 12 | .text : { *(.text*) } > sram 13 | .bss : { *(.bss*) } > sram 14 | .data : { *(.data*) } > sram 15 | _end = . ; 16 | } -------------------------------------------------------------------------------- /src/sys.rs: -------------------------------------------------------------------------------- 1 | pub mod lists; 2 | pub mod mem; 3 | pub mod context; 4 | 5 | pub fn assign(address: u32, value: u32) { 6 | unsafe { 7 | *(address as *mut u32) = value; 8 | } 9 | } 10 | 11 | pub fn read_word(address: u32) -> u32 { 12 | unsafe { 13 | return *(address as *mut u32); 14 | } 15 | } 16 | 17 | pub fn clear_bit(number: u32, bit: u8) -> u32 { 18 | return number & !(0x01 << bit); 19 | } 20 | 21 | pub fn set_bit(number: u32, bit: u8) -> u32 { 22 | return number | (0x01 << bit); 23 | } 24 | 25 | pub fn millis() -> u32 { 26 | let context = context::get_context(); 27 | return context.sysclock.elapsed(); 28 | } 29 | 30 | pub fn wait_ms(ms: u32) { 31 | let target = millis() + ms; 32 | loop { 33 | if millis() > target { 34 | break; 35 | } else { 36 | unsafe { 37 | asm!("nop"); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/sys/context.rs: -------------------------------------------------------------------------------- 1 | use crate::board::{timer, platform}; 2 | 3 | pub struct SystemContext { 4 | pub sysclock: timer::Timer, 5 | } 6 | 7 | static mut CONTEXT: SystemContext = SystemContext { 8 | sysclock: timer::Timer::new(platform::DMTIMER3), 9 | }; 10 | 11 | pub fn configure_context(context: SystemContext) { 12 | unsafe { 13 | CONTEXT = context; 14 | } 15 | } 16 | 17 | pub fn get_context() -> &'static mut SystemContext { 18 | unsafe { return &mut CONTEXT }; 19 | } -------------------------------------------------------------------------------- /src/sys/lists.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Josh Cole 3 | 4 | This is a rust implementation of a system level linked list. It supports 5 | standard operations including pop, and get. 6 | */ 7 | #![allow(dead_code)] 8 | use crate::sys::mem::{ kalloc, free }; 9 | 10 | #[derive(Copy, Clone)] 11 | struct Node { 12 | item: T, 13 | next: Option<*mut Node>, 14 | } 15 | 16 | pub struct Stack { 17 | head: Option<*mut Node>, 18 | size: usize, 19 | } 20 | 21 | impl Stack { 22 | pub fn new() -> Self { 23 | return Stack { head: None, size: 0 }; 24 | } 25 | 26 | pub fn push(&mut self, item: T) { 27 | let ptr = kalloc(); 28 | unsafe { 29 | (*ptr) = Node { 30 | item: item, 31 | next: self.head, 32 | }; 33 | } 34 | 35 | self.head = Some(ptr); 36 | self.size = self.size + 1; 37 | } 38 | 39 | pub fn pop(&mut self) -> Option { 40 | match self.head { 41 | None => { 42 | return None; 43 | }, 44 | Some(node) => { 45 | // Copy the reference 46 | let indirect = node.clone(); 47 | let node_item = unsafe { *indirect }; 48 | 49 | // Free the actual node. 50 | free(node); 51 | 52 | let result = node_item.item; 53 | self.head = node_item.next; 54 | self.size = self.size - 1; 55 | return Some(result); 56 | }, 57 | }; 58 | } 59 | 60 | pub fn size(&self) -> usize { 61 | return self.size; 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod test { 67 | 68 | use super::*; 69 | 70 | #[test] 71 | fn stack() { 72 | let mut list = Stack::new(); 73 | list.push(32); 74 | list.push(64); 75 | list.push(128); 76 | list.push(256); 77 | 78 | assert_eq!(list.size(), 4); 79 | assert_eq!(list.pop(), Some(256)); 80 | assert_eq!(list.size(), 3); 81 | assert_eq!(list.pop(), Some(128)); 82 | assert_eq!(list.size(), 2); 83 | assert_eq!(list.pop(), Some(64)); 84 | assert_eq!(list.size(), 1); 85 | assert_eq!(list.pop(), Some(32)); 86 | assert_eq!(list.size(), 0); 87 | assert_eq!(list.pop(), None); 88 | 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/sys/mem.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Josh Cole 3 | This library is dedicated to onboard OCMC RAM. 64kb 4 | */ 5 | #![allow(dead_code, unused_imports)] 6 | #[cfg(test)] 7 | use std::alloc::{alloc, Layout}; 8 | use core::mem::{size_of}; 9 | use crate::board::platform::{ 10 | EMIF0_SDRAM, 11 | KB64, 12 | GB1, 13 | }; 14 | 15 | // Per 2.1 in the AM335x Technical Reference Manual 16 | const MEMORY_MAXIMUM: u32 = GB1; 17 | const MEMORY_ADDRESS: u32 = EMIF0_SDRAM; 18 | static mut MEMORY_OFFSET: u32 = 0; 19 | 20 | #[cfg(not(test))] 21 | pub fn memtest() { 22 | // Write one to every byte of memory and check for overflow. 23 | let ptr = MEMORY_ADDRESS as *mut u32; 24 | for idx in 0 .. GB1 / 4 { 25 | unsafe { 26 | *ptr.offset(idx as isize) = 0xFFFF_FFFE; 27 | } 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | pub fn kalloc() -> *mut T { 33 | return unsafe { alloc(Layout::new::()) as *mut T }; 34 | } 35 | 36 | #[cfg(not(test))] 37 | /// This is kernal alloc and it implements what I call a "cyclical mempage strategy". 38 | /// Memory is constantly allocated in RAM and eventually will loop back around 39 | /// if all memory is used up. Clearly, this is a bad idea. Will be improved over time. 40 | pub fn kalloc() -> *mut T { 41 | let bytes = size_of::(); 42 | // Check for boundaries and reset if applicable. 43 | // TODO: this is definitely USR LED worthy. Maybe even panic worthy. 44 | unsafe { 45 | if MEMORY_OFFSET + bytes as u32 > MEMORY_MAXIMUM { 46 | MEMORY_OFFSET = 0; 47 | } 48 | 49 | let ptr = (MEMORY_ADDRESS + MEMORY_OFFSET) as *mut T; 50 | MEMORY_OFFSET += bytes as u32; 51 | return ptr; 52 | } 53 | } 54 | 55 | /// Free a pointer by updating the pagefile 56 | pub fn free(ptr: *mut T) { 57 | let bytes = size_of::(); 58 | let zero_ptr = ptr as *mut u32; 59 | for i in 0 .. bytes / 4 { 60 | unsafe { 61 | *(zero_ptr.offset(i as isize)) = 0; 62 | } 63 | } 64 | } --------------------------------------------------------------------------------