From fa7aba4ff67c6cc77fc175c8709702087be93248 Mon Sep 17 00:00:00 2001 From: Chuck Benedict Date: Mon, 29 Jul 2024 16:12:59 -0700 Subject: [PATCH] Add Riscv example --- .../examples/embedded/riscv-qemu/Makefile | 23 +++++++++++++++ .../examples/embedded/riscv-qemu/README.md | 8 ++++++ .../examples/embedded/riscv-qemu/baremetal.ld | 14 ++++++++++ .../examples/embedded/riscv-qemu/hello.c3 | 10 +++++++ .../examples/embedded/riscv-qemu/start.s | 17 +++++++++++ .../examples/embedded/riscv-qemu/uart.c3 | 28 +++++++++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 resources/examples/embedded/riscv-qemu/Makefile create mode 100644 resources/examples/embedded/riscv-qemu/README.md create mode 100644 resources/examples/embedded/riscv-qemu/baremetal.ld create mode 100644 resources/examples/embedded/riscv-qemu/hello.c3 create mode 100644 resources/examples/embedded/riscv-qemu/start.s create mode 100644 resources/examples/embedded/riscv-qemu/uart.c3 diff --git a/resources/examples/embedded/riscv-qemu/Makefile b/resources/examples/embedded/riscv-qemu/Makefile new file mode 100644 index 000000000..312ba1660 --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/Makefile @@ -0,0 +1,23 @@ +SRCS_C3 := $(wildcard *.c3) + +default: hello.elf + +hello.a: $(SRCS_C3) + c3c --use-stdlib=no --no-entry --target elf-riscv32 static-lib $(SRCS_C3) + +start.o: start.s + riscv64-unknown-elf-as -g -march=rv32i -mabi=ilp32 -o start.o start.s + +hello.elf: hello.a start.o baremetal.ld + riscv64-unknown-elf-ld -T baremetal.ld -m elf32lriscv -o hello.elf hello.a start.o + +run: hello.elf + @echo "Ctrl-A C for QEMU console, then quit to exit" + qemu-system-riscv32 -nographic -serial mon:stdio -machine virt -bios hello.elf + +debug: hello.elf + @echo "Ctrl-A C for QEMU console, then quit to exit" + qemu-system-riscv32 -nographic -serial mon:stdio -machine virt -s -S -bios hello.elf + +clean: + rm -f *.o *.a hello.elf diff --git a/resources/examples/embedded/riscv-qemu/README.md b/resources/examples/embedded/riscv-qemu/README.md new file mode 100644 index 000000000..23f12db77 --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/README.md @@ -0,0 +1,8 @@ +# Risc-V 32 Embedded Example With QEMU +## Prereqs +- QEMU +- Risc-V toolchain +- C3C +- Make +## Running +`make run` diff --git a/resources/examples/embedded/riscv-qemu/baremetal.ld b/resources/examples/embedded/riscv-qemu/baremetal.ld new file mode 100644 index 000000000..f37432d4a --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/baremetal.ld @@ -0,0 +1,14 @@ +EXTERN(main) + +SECTIONS +{ + . = 0x80000000; /* QEMU default load address to run bios */ + .text : { + KEEP(*(.text._start)); /* Ensure _start is placed first */ + *(.text*); /* Program code here */ + } + . = ALIGN (CONSTANT (COMMONPAGESIZE)); /* Make sure linker does not jam data into text section, making text writable */ + .data : { + *(.data*) /* Stack goes here */ + } +} \ No newline at end of file diff --git a/resources/examples/embedded/riscv-qemu/hello.c3 b/resources/examples/embedded/riscv-qemu/hello.c3 new file mode 100644 index 000000000..edbc198d0 --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/hello.c3 @@ -0,0 +1,10 @@ +import uart; + +const UART0_BASE = 0x10000000; + +fn void main() @export("main") { + Uart* uart0 = (Uart*)UART0_BASE; // Create pointer to UART 0 + uart0.fcr = uart::UARTFCR_FFENA; // Enable FIFO + uart0.puts("Hello World!\n"); // Write the string to the UART + while (1); // Loop forever to prevent program from ending +} diff --git a/resources/examples/embedded/riscv-qemu/start.s b/resources/examples/embedded/riscv-qemu/start.s new file mode 100644 index 000000000..9064a9112 --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/start.s @@ -0,0 +1,17 @@ +# Simple C runtime startup bootstrap +# Two primary functions: +# - Stack allocation and initializing stack pointer +# - Jumping to main + +.section .text._start +.global _start +_start: + la sp, __stack_top # Load the stack pointer + add s0, sp, zero # Set the frame pointer + jal zero, main # Run main entry point - no argc +loop: j loop # Spin forever in case main returns + +.section .data +.space 1024*8 # allocate 8K of memory to serve as initial stack +.align 16 # Smallest stack allocation is 16 bytes, so align accordingly +__stack_top: # The stack grows downward according the Risc-V ABI diff --git a/resources/examples/embedded/riscv-qemu/uart.c3 b/resources/examples/embedded/riscv-qemu/uart.c3 new file mode 100644 index 000000000..8f05b218f --- /dev/null +++ b/resources/examples/embedded/riscv-qemu/uart.c3 @@ -0,0 +1,28 @@ +module uart; + +const UARTFCR_FFENA = 0x01; // UART FIFO Control Register enable bit +const UARTLSR_THRE = 0x20; // UART Line Status Register Transmit Hold Register Empty bit + +struct Uart { + char dr; // UART Data Register + char filler1; + char fcr; // FIFO Control Register + char filler2; + char filler3; + char lsr; // Line Status Register +} + +fn bool Uart.uart_ff_thr_empty(Uart* this) { + return (bool)($$volatile_load(&this.lsr) & UARTLSR_THRE); +} + +fn void Uart.putc(Uart* this, char c) { + while (!this.uart_ff_thr_empty()); // Wait until the FIFO holding register is empty + $$volatile_store(&this.dr, c); // Write character to transmit register +} + +fn void Uart.puts(Uart* this, char *str) { + while (*str) { // Loop until value at string pointer is zero + this.putc(*str++); // Write the character and increment pointer + } +}