diff --git a/rust_lib_demo/.cargo/config b/rust_lib_demo/.cargo/config new file mode 100644 index 0000000..d2d46cb --- /dev/null +++ b/rust_lib_demo/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "powerpc64le-unknown-linux-gnu" diff --git a/rust_lib_demo/Cargo.toml b/rust_lib_demo/Cargo.toml new file mode 100644 index 0000000..bbfb8ff --- /dev/null +++ b/rust_lib_demo/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "microwatt" +version = "0.1.4" +authors = ["Tom Vijlbrief "] +edition = "2018" +description = "Running Rust on the microwatt" +license = "GPL-3.0" + +[lib] +name = "mylib" +crate_type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[dependencies] +heapless = "0.5.1" +cty = "0.2" +#embedded-hal = "0.2.3" +#panic-halt = "0.2.0" +linked_list_allocator = "0.6" + +[profile.release] +panic = "abort" +codegen-units = 1 # better optimizations +opt-level = 'z' # Optimize for size. +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations diff --git a/rust_lib_demo/Makefile b/rust_lib_demo/Makefile new file mode 100644 index 0000000..990a865 --- /dev/null +++ b/rust_lib_demo/Makefile @@ -0,0 +1,44 @@ +ARCH = $(shell uname -m) +ifneq ("$(ARCH)", "ppc64") +ifneq ("$(ARCH)", "ppc64le") + CROSS_COMPILE = powerpc64le-linux-gnu- + endif + endif + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS = -Os -g -Wall -std=c99 -msoft-float -mno-string -mno-multiple -mno-vsx -mno-altivec -mlittle-endian -fno-stack-protector -mstrict-align -ffreestanding -fdata-sections -ffunction-sections +ASFLAGS = $(CFLAGS) +LDFLAGS = -T powerpc.lds + +RUSTLIB = target/powerpc64le-unknown-linux-gnu/release/libmylib.a + +all: hello_world.hex + +run: + -ln -sf hello_world.bin main_ram.bin + ../core_tb > /dev/null + +$(RUSTLIB): src/lib.rs + RUSTFLAGS="-C target-feature=-vsx,-altivec,-hard-float" xargo build --release + +size: + size hello_world.elf + +dump: + powerpc64le-linux-gnu-objdump -S hello_world.elf | less + +hello_world.elf: hello_world.o head.o $(RUSTLIB) + $(LD) $(LDFLAGS) -o hello_world.elf hello_world.o head.o $(RUSTLIB) + +hello_world.bin: hello_world.elf + $(OBJCOPY) -O binary hello_world.elf hello_world.bin + +hello_world.hex: hello_world.bin + ./bin2hex.py hello_world.bin > hello_world.hex + +clean: + cargo clean + @rm -f *.o hello_world.elf hello_world.bin hello_world.hex diff --git a/rust_lib_demo/README.md b/rust_lib_demo/README.md new file mode 100644 index 0000000..1b60548 --- /dev/null +++ b/rust_lib_demo/README.md @@ -0,0 +1,33 @@ +I made a minimal Rust demo. + +This needs a rebuild of the `core` library with `xargo` (https://github.com/japaric/xargo) for working soft floating-point support. + +Steps: + +``` +$ rustup default nightly + +$ rustup target add powerpc64le-unknown-linux-gnu +``` +$ rustup component add rust-src + +$ cargo install xargo + +$ make +$ make run + +ln -sf hello_world.bin main_ram.bin +../core_tb > /dev/null +Hello World +Rust +i 2 +5 +5 +i 3 +3.3333333333333335 +3.3333333333333335 +[-9, -6, 2, 3] +!panicked at 'test', src/lib.rs:58:5 + +``` + diff --git a/rust_lib_demo/Xargo.toml b/rust_lib_demo/Xargo.toml new file mode 100644 index 0000000..eb56903 --- /dev/null +++ b/rust_lib_demo/Xargo.toml @@ -0,0 +1,4 @@ +[target.powerpc64le-unknown-linux-gnu.dependencies] + +[dependencies.alloc] +features = ["compiler-builtins-mem"] diff --git a/rust_lib_demo/bin2hex.py b/rust_lib_demo/bin2hex.py new file mode 100755 index 0000000..af278bc --- /dev/null +++ b/rust_lib_demo/bin2hex.py @@ -0,0 +1,17 @@ +#!/usr/bin/python3 + +import sys +import subprocess +import struct + +with open(sys.argv[1], "rb") as f: + while True: + word = f.read(8) + if len(word) == 8: + print("%016x" % struct.unpack('Q', word)); + elif len(word) == 4: + print("00000000%08x" % struct.unpack('I', word)); + elif len(word) == 0: + exit(0); + else: + raise Exception("Bad length") diff --git a/rust_lib_demo/head.S b/rust_lib_demo/head.S new file mode 100644 index 0000000..d6d8351 --- /dev/null +++ b/rust_lib_demo/head.S @@ -0,0 +1,95 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x1f000 + +#define FIXUP_ENDIAN \ + tdi 0,0,0x48; /* Reverse endian of b . + 8 */ \ + b 191f; /* Skip trampoline if endian is good */ \ + .long 0xa600607d; /* mfmsr r11 */ \ + .long 0x01006b69; /* xori r11,r11,1 */ \ + .long 0x05009f42; /* bcl 20,31,$+4 */ \ + .long 0xa602487d; /* mflr r10 */ \ + .long 0x14004a39; /* addi r10,r10,20 */ \ + .long 0xa64b5a7d; /* mthsrr0 r10 */ \ + .long 0xa64b7b7d; /* mthsrr1 r11 */ \ + .long 0x2402004c; /* hrfid */ \ +191: + + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + . = 0 +.global _start +_start: + FIXUP_ENDIAN + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12, + bctrl + b . + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + EXCEPTION(0xe00) + EXCEPTION(0xe20) + EXCEPTION(0xe40) + EXCEPTION(0xe60) + EXCEPTION(0xe80) + EXCEPTION(0xf00) + EXCEPTION(0xf20) + EXCEPTION(0xf40) + EXCEPTION(0xf60) + EXCEPTION(0xf80) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif diff --git a/rust_lib_demo/hello_world.c b/rust_lib_demo/hello_world.c new file mode 100644 index 0000000..95b4d09 --- /dev/null +++ b/rust_lib_demo/hello_world.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +/* + * Core UART functions to implement for a port + */ + +static uint64_t potato_uart_base; + +#define PROC_FREQ 50000000 +#define UART_FREQ 115200 +#define UART_BASE 0xc0002000 + +#define POTATO_CONSOLE_TX 0x00 +#define POTATO_CONSOLE_RX 0x08 +#define POTATO_CONSOLE_STATUS 0x10 +#define POTATO_CONSOLE_STATUS_RX_EMPTY 0x01 +#define POTATO_CONSOLE_STATUS_TX_EMPTY 0x02 +#define POTATO_CONSOLE_STATUS_RX_FULL 0x04 +#define POTATO_CONSOLE_STATUS_TX_FULL 0x08 +#define POTATO_CONSOLE_CLOCK_DIV 0x18 +#define POTATO_CONSOLE_IRQ_EN 0x20 + +static uint64_t potato_uart_reg_read(int offset) +{ + uint64_t addr; + uint64_t val; + + addr = potato_uart_base + offset; + + val = *(volatile uint64_t *)addr; + + return val; +} + +static void potato_uart_reg_write(int offset, uint64_t val) +{ + uint64_t addr; + + addr = potato_uart_base + offset; + + *(volatile uint64_t *)addr = val; +} + +static int potato_uart_rx_empty(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); + + if (val & POTATO_CONSOLE_STATUS_RX_EMPTY) + return 1; + + return 0; +} + +static int potato_uart_tx_full(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_STATUS); + + if (val & POTATO_CONSOLE_STATUS_TX_FULL) + return 1; + + return 0; +} + +static char potato_uart_read(void) +{ + uint64_t val; + + val = potato_uart_reg_read(POTATO_CONSOLE_RX); + + return (char)(val & 0x000000ff); +} + +static void potato_uart_write(char c) +{ + uint64_t val; + + val = c; + + potato_uart_reg_write(POTATO_CONSOLE_TX, val); +} + +static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long uart_freq) +{ + return proc_freq / (uart_freq * 16) - 1; +} + +void potato_uart_init(void) +{ + potato_uart_base = UART_BASE; + + potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ)); +} + +int getchar(void) +{ + while (potato_uart_rx_empty()) + /* Do nothing */ ; + + return potato_uart_read(); +} + +void putchar(unsigned char c) +{ + while (potato_uart_tx_full()) + /* Do Nothing */; + + potato_uart_write(c); +} + +void putstr(const char *str, unsigned long len) +{ + for (unsigned long i = 0; i < len; i++) { + putchar(str[i]); + } +} + +size_t strlen(const char *s) +{ + size_t len = 0; + + while (*s++) + len++; + + return len; +} + +void rust_main(); + +void crash() +{ + void (*fun_ptr)() = (void(*)()) 0xdeadbeef; + (*fun_ptr)(); +} + +void init_bss() +{ + extern int _bss, _ebss; + int *p = &_bss; + while (p < &_ebss) { + *p++ = 0; + } +} + + +#define HELLO_WORLD "Hello World\r\n" + +int main(void) +{ + init_bss(); + potato_uart_init(); + + putstr(HELLO_WORLD, strlen(HELLO_WORLD)); + + rust_main(); + crash(); + + while (1) + ; +} diff --git a/rust_lib_demo/powerpc.lds b/rust_lib_demo/powerpc.lds new file mode 100644 index 0000000..934a7e3 --- /dev/null +++ b/rust_lib_demo/powerpc.lds @@ -0,0 +1,17 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x1f000; + .data : { *(.data) } + . = ALIGN(4); + _bss = .; /* define a global symbol at bss start; used by startup code */ + .bss : { *(.bss) } + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end; used by startup code */ +} diff --git a/rust_lib_demo/src/lib.rs b/rust_lib_demo/src/lib.rs new file mode 100644 index 0000000..863c6f3 --- /dev/null +++ b/rust_lib_demo/src/lib.rs @@ -0,0 +1,78 @@ +#![no_std] +#![feature(alloc_error_handler)] + +use core::fmt::Write; +use core::panic::PanicInfo; + +use heapless::consts::*; +use heapless::String; + +extern crate linked_list_allocator; +use linked_list_allocator::*; +#[global_allocator] +static mut HEAP: LockedHeap = LockedHeap::empty(); + +extern crate alloc; +use alloc::vec::Vec; + +extern crate cty; + +extern "C" { + fn putchar(c: cty::c_char) -> (); + fn crash() -> (); +} + +pub fn print(s: &str) { + for c in s.bytes() { + unsafe { putchar(c) }; + } +} + +#[no_mangle] +pub extern "C" fn rust_main() -> ! { + print("Rust\r\n"); + + const HEAP_SIZE: usize = 2048; + static mut HEAP_AREA: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; + unsafe { HEAP = LockedHeap::new(&HEAP_AREA[0] as *const u8 as usize, HEAP_AREA.len()) }; + + let mut s: String = String::new(); + let mut xs = Vec::new(); + for i in 2..=3 { + xs.push(i); + xs.push(-3 * i); + xs.push(i); + writeln!(s, "i {}\r", i).unwrap(); + print(&s); + s.clear(); + writeln!(s, "{}\r", 10.0 / i as f64).ok(); + print(&s); + if xs.pop().unwrap() != i { + panic!("??"); + } + } + xs.sort(); + writeln!(s, "{:?}\r", xs).unwrap(); + print(&s); + + panic!("test"); +} + +#[panic_handler] +fn panic(panic_info: &PanicInfo) -> ! { + unsafe { + putchar('!' as u8); + } + let mut s: String = String::new(); + writeln!(s, "{}\r", panic_info).ok(); + print(&s); + unsafe { + crash(); + } + loop {} +} + +#[alloc_error_handler] +fn alloc_error(_: core::alloc::Layout) -> ! { + panic!("Heap"); +}