mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
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:
9
.github/workflows/main.yml
vendored
9
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
21
resources/examples/embedded/riscv-qemu/Makefile
Normal file
21
resources/examples/embedded/riscv-qemu/Makefile
Normal 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
|
||||
8
resources/examples/embedded/riscv-qemu/README.md
Normal file
8
resources/examples/embedded/riscv-qemu/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Risc-V 32 Embedded Example With QEMU
|
||||
## Prereqs
|
||||
- QEMU
|
||||
- Risc-V toolchain
|
||||
- C3C
|
||||
- Make
|
||||
## Running
|
||||
`make run`
|
||||
14
resources/examples/embedded/riscv-qemu/baremetal.ld
Normal file
14
resources/examples/embedded/riscv-qemu/baremetal.ld
Normal 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 */
|
||||
}
|
||||
}
|
||||
11
resources/examples/embedded/riscv-qemu/hello.c3
Normal file
11
resources/examples/embedded/riscv-qemu/hello.c3
Normal 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
|
||||
}
|
||||
20
resources/examples/embedded/riscv-qemu/semihost.c3
Normal file
20
resources/examples/embedded/riscv-qemu/semihost.c3
Normal 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);
|
||||
}
|
||||
29
resources/examples/embedded/riscv-qemu/start.s
Normal file
29
resources/examples/embedded/riscv-qemu/start.s
Normal 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
|
||||
28
resources/examples/embedded/riscv-qemu/uart.c3
Normal file
28
resources/examples/embedded/riscv-qemu/uart.c3
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user