Add Riscv Example (#1268)

Add Riscv example. Risc-V CI. Install baremetal toolchain. Prevent imported crt file from messing up linker search.
This commit is contained in:
Chuck Benedict
2024-07-31 05:43:47 -07:00
committed by GitHub
parent 7664d0568e
commit 563e677b08
9 changed files with 172 additions and 28 deletions

View File

@@ -319,6 +319,15 @@ jobs:
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
- name: Install QEMU and Risc-V toolchain
run: |
sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
- name: Compile and run Baremetal Risc-V Example
run: |
cd resources/examples/embedded/riscv-qemu
make C3C_PATH=../../../../build/ run
- name: Build testproject direct linker
run: |
cd resources/testproject

View File

@@ -0,0 +1,21 @@
SRCS_C3 := $(wildcard *.c3)
default: hello.elf
hello.a: $(SRCS_C3)
$(C3C_PATH)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 -misa-spec=20191213 -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
qemu-system-riscv32 -nographic -serial mon:stdio -machine virt -semihosting -bios hello.elf
debug: hello.elf
qemu-system-riscv32 -nographic -serial mon:stdio -machine virt -semihosting -s -S -bios hello.elf
clean:
rm -f *.o *.a hello.elf

View File

@@ -0,0 +1,8 @@
# Risc-V 32 Embedded Example With QEMU
## Prereqs
- QEMU
- Risc-V toolchain
- C3C
- Make
## Running
`make run`

View File

@@ -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 */
}
}

View File

@@ -0,0 +1,11 @@
import uart;
import semihost;
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
semihost::exit(0); // Semihosting call to exit host
}

View File

@@ -0,0 +1,20 @@
module semihost;
// See: https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst#sys-exit-extended-0x20
extern fn int sys_semihost(int operation, SemihostParameters* parms);
struct SemihostParameters {
int field1;
int field2;
}
const int SYS_EXIT_EXTENDED = 0x20;
const int ADP_STOPPED_APPLICATIONEXIT = 0x20026;
fn void exit(int status) {
SemihostParameters parms;
parms.field1 = ADP_STOPPED_APPLICATIONEXIT;
parms.field2 = status;
sys_semihost(SYS_EXIT_EXTENDED, &parms);
}

View File

@@ -0,0 +1,29 @@
# 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
# Support semi-hosting calls
.option norvc
.text
.balign 16
.global sys_semihost
.type sys_semihost @function
sys_semihost: # https://github.com/riscv-non-isa/riscv-semihosting/blob/main/src/binary-interface.adoc
slli zero, zero, 0x1f
ebreak
srai zero, zero, 0x7
ret
.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

View File

@@ -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
}
}

View File

@@ -247,50 +247,54 @@ static const char *find_freebsd_crt(void)
return NULL;
}
static const char *find_linux_crt(void)
static const char *find_arch_glob_path(const char *glob_path, int file_len)
{
if (active_target.linuxpaths.crt) return active_target.linuxpaths.crt;
#if PLATFORM_POSIX
glob_t globbuf;
if (!glob("/usr/lib/*/crt1.o", 0, NULL, &globbuf) && globbuf.gl_pathc)
if (!glob(glob_path, 0, NULL, &globbuf))
{
const char *path = globbuf.gl_pathv[0];
INFO_LOG("Found crt at %s", path);
size_t len = strlen(path);
assert(len > 6);
const char *res = str_copy(path, len - 6);
for (int i = 0; i < globbuf.gl_pathc; i++)
{
const char *path = globbuf.gl_pathv[i];
// Avoid qemu problems
if (platform_target.arch != ARCH_TYPE_RISCV64
&& platform_target.arch != ARCH_TYPE_RISCV32
&& strstr(path, "riscv")) continue;
size_t len = strlen(path);
assert(len > file_len);
const char *res = str_copy(path, len - file_len);
globfree(&globbuf);
return res;
}
globfree(&globbuf);
return res;
}
else
{
INFO_LOG("No crt in /usr/lib/*/");
}
#endif
return NULL;
}
static const char *find_linux_crt(void)
{
if (active_target.linuxpaths.crt) return active_target.linuxpaths.crt;
const char *path = find_arch_glob_path("/usr/lib/*/crt1.o", 6);
if (!path)
{
INFO_LOG("No crt in /usr/lib/*/");
return NULL;
}
INFO_LOG("Found crt at %s", path);
return path;
}
static const char *find_linux_crt_begin(void)
{
if (active_target.linuxpaths.crtbegin) return active_target.linuxpaths.crtbegin;
#if PLATFORM_POSIX
glob_t globbuf;
if (!glob("/usr/lib/gcc/*/*/crtbegin.o", 0, NULL, &globbuf) && globbuf.gl_pathc)
{
const char *path = globbuf.gl_pathv[0];
INFO_LOG("Found crtbegin at %s", path);
size_t len = strlen(path);
assert(len > 10);
const char *res = str_copy(path, len - 10);
globfree(&globbuf);
return res;
}
else
const char *path = find_arch_glob_path("/usr/lib/gcc/*/*/crtbegin.o", 10);
if (!path)
{
INFO_LOG("No crtbegin in /usr/lib/gcc/*/*/");
return NULL;
}
#endif
return NULL;
INFO_LOG("Found crtbegin at %s", path);
return path;
}
static void linker_setup_linux(const char ***args_ref, Linker linker_type)