11# From C to Raven
22
3- A bare-metal C project that compiles to a RISC-V ELF binary ready to run in the [ Raven] ( https://github.com/Gaok1/Raven ) simulator.
3+ A bare-metal C project that compiles to a RISC-V ELF binary ready to run in the [ Raven] ( https://github.com/Gaok1/Raven-RiscV ) simulator.
44
5- No OS, no libc. ` raven.h ` is the only runtime you need.
5+ No OS. No libc. ` raven.h ` is the only runtime you need — it gives you syscalls, I/O, strings, memory utilities, a heap allocator, and simulator control, all as ` static inline ` functions .
66
77---
88
99## Files
1010
1111| File | Purpose |
1212| ------| ---------|
13- | ` raven.h ` | Syscall wrappers , I/O helpers, ` raven_pause() ` |
14- | ` crt0.S ` | Minimal startup: calls ` main() ` , then ` exit(return_value) ` |
15- | ` main.c ` | Example: fill array with random numbers, bubble-sort, print result |
16- | ` float_demo.c ` | Example: sum, dot product and basic arithmetic using RV32F hardware floats |
17- | ` Makefile ` | Builds both demos with Clang/LLD targeting ` rv32im ` and ` rv32imf ` |
13+ | ` raven.h ` | The entire runtime: syscalls , I/O, strings, memory, malloc, assert |
14+ | ` crt0.S ` | Minimal startup: calls ` main() ` , forwards return value to ` exit ` |
15+ | ` main.c ` | Example: malloc/free, string ops, sorting, raven_pause |
16+ | ` float_demo.c ` | Example: RV32F hardware float — sum, dot product, basic arithmetic |
17+ | ` Makefile ` | Builds both demos; auto-detects clang or riscv64-unknown-elf-gcc |
1818
1919---
2020
@@ -23,94 +23,141 @@ No OS, no libc. `raven.h` is the only runtime you need.
2323``` sh
2424make # → c-to-raven.elf (RV32IM, integer only)
2525make float-demo # → float-demo.elf (RV32IMF, hardware float)
26- make clean # remove .elf files
26+ make clean
2727```
2828
29- Load the resulting ELF into Raven: Editor tab → ** [ BIN] ** button → select the file.
29+ Load the ELF into Raven: Editor tab → ** [ BIN] ** → select the file.
3030
3131---
3232
3333## Requirements
3434
35- ### Linux (Ubuntu / Debian)
35+ The Makefile auto-detects the compiler. Install one of:
3636
37- The Makefile uses ** Clang 18 ** + ** LLD ** . Both are available from the standard LLVM packages:
37+ ### Linux (Ubuntu / Debian) — recommended: Clang + LLD
3838
3939``` sh
40- sudo apt install clang-18 lld
40+ sudo apt install clang lld
4141```
4242
43- If you prefer GCC instead, change the first two lines of the Makefile:
43+ ### Linux — alternative: GCC cross-compiler
4444
45- ``` makefile
46- CC = riscv64-unknown-elf-gcc
47- CFLAGS = -march=rv32im -mabi=ilp32 -nostdlib -O2 -Wall -Wextra
45+ ``` sh
46+ sudo apt install gcc-riscv64-unknown-elf
4847```
4948
50- Install GCC with:
49+ ### Windows — WSL (recommended)
5150
5251``` sh
53- sudo apt install gcc-riscv64-unknown-elf
52+ wsl --install # then follow Linux instructions inside WSL
5453```
5554
56- ### Windows — Option 1: WSL (recommended)
57-
58- 1 . Install WSL with Ubuntu:
59- ``` sh
60- wsl --install
61- ```
62- 2 . Inside WSL, follow the Linux instructions above.
55+ ### Windows — MSYS2
6356
64- ### Windows — Option 2: MSYS2
57+ Open the ** UCRT64 ** terminal:
6558
66- 1 . Install [ MSYS2] ( https://www.msys2.org/ ) .
67- 2 . Open the ** UCRT64** terminal and run:
68- ``` sh
69- pacman -S mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-lld
70- ```
71- 3 . Build from the UCRT64 terminal:
72- ``` sh
73- make
74- ```
59+ ``` sh
60+ pacman -S mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-lld
61+ ```
7562
7663---
7764
7865## ` raven.h ` API reference
7966
8067### Syscall wrappers
8168
82- | Function | Signature | Description |
83- | ----------| -----------| ----------- --|
84- | ` sys_write ` | ` int sys_write(int fd, const void * buf, int len)` | Write ` len ` bytes to file descriptor (use ` STDOUT ` = 1) |
85- | ` sys_read ` | ` int sys_read(int fd, void * buf, int len)` | Read up to ` len ` bytes from file descriptor (use ` STDIN ` = 0) |
86- | ` sys_exit ` | ` void sys_exit(int code)` | Terminate with exit code (no return) |
87- | ` sys_getrandom ` | ` int sys_getrandom(void * buf, int len, unsigned flags)` | Fill buffer with random bytes ( flags = 0) |
69+ | Function | Description |
70+ | ----------| -------------|
71+ | ` sys_write( fd, buf, len) ` | Write ` len ` bytes to file descriptor (` STDOUT ` = 1, ` STDERR ` = 2 ) |
72+ | ` sys_read( fd, buf, len) ` | Read up to ` len ` bytes from file descriptor (` STDIN ` = 0) |
73+ | ` sys_exit( code) ` | Terminate (no return) |
74+ | ` sys_getrandom( buf, len, flags) ` | Fill buffer with random bytes; flags = 0 |
8875
89- All wrappers use the Linux RISC-V ABI (` a7 ` = syscall number, ` a0 ` –` a2 ` = args, ` a0 ` = return value).
76+ ### Simulator control
77+
78+ | Function | Description |
79+ | ----------| -------------|
80+ | ` raven_pause() ` | Emit ` ebreak ` — Raven pauses so you can inspect registers and memory |
9081
9182### I/O helpers
9283
9384| Function | Description |
9485| ----------| -------------|
95- | ` print_char(char c) ` | Print a single character to stdout |
96- | ` print_str(const char *s) ` | Print a null-terminated string to stdout |
97- | ` print_int(int n) ` | Print a signed integer (decimal) to stdout |
98- | ` print_uint(unsigned int n) ` | Print an unsigned integer (decimal) to stdout |
86+ | ` print_char(c) ` | Print a single character |
87+ | ` print_str(s) ` | Print a null-terminated string |
88+ | ` print_int(n) ` | Print a signed decimal integer |
89+ | ` print_uint(n) ` | Print an unsigned decimal integer |
90+ | ` print_hex(n) ` | Print unsigned int as ` 0x00000000 ` |
91+ | ` print_ptr(p) ` | Print a pointer as hex address |
92+ | ` print_float(v, decimals) ` | Print a float with the given number of decimal places |
93+ | ` print_bool(v) ` | Print ` "true" ` or ` "false" ` |
9994| ` print_ln() ` | Print a newline |
100- | ` read_line(char *buf, int max) ` | Read a line from stdin; null-terminates; returns bytes read |
95+ | ` read_line(buf, max) ` | Read a line from stdin; null-terminates; returns byte count |
96+ | ` read_int() ` | Read a decimal integer from stdin |
10197
102- ### Simulator control
98+ ### Memory utilities
99+
100+ | Function | Description |
101+ | ----------| -------------|
102+ | ` memset(dst, c, n) ` | Fill ` n ` bytes with value ` c ` ; returns ` dst ` |
103+ | ` memcpy(dst, src, n) ` | Copy ` n ` bytes from ` src ` to ` dst ` ; returns ` dst ` |
104+ | ` memmove(dst, src, n) ` | Copy ` n ` bytes, safe for overlapping regions; returns ` dst ` |
105+ | ` memcmp(a, b, n) ` | Compare ` n ` bytes; returns 0 if equal |
106+
107+ ### String utilities
108+
109+ | Function | Description |
110+ | ----------| -------------|
111+ | ` strlen(s) ` | Length of string (not counting ` '\0' ` ) |
112+ | ` strcmp(a, b) ` | Lexicographic comparison; returns 0 if equal |
113+ | ` strncmp(a, b, n) ` | Comparison limited to ` n ` characters |
114+ | ` strcpy(dst, src) ` | Copy string; returns ` dst ` |
115+ | ` strncpy(dst, src, n) ` | Copy at most ` n ` characters, zero-pad; returns ` dst ` |
116+ | ` strcat(dst, src) ` | Append ` src ` to ` dst ` ; returns ` dst ` |
117+ | ` strchr(s, c) ` | First occurrence of ` c ` in ` s ` , or ` NULL ` |
118+ | ` strrchr(s, c) ` | Last occurrence of ` c ` in ` s ` , or ` NULL ` |
119+
120+ ### Math utilities
103121
104122| Function | Description |
105123| ----------| -------------|
106- | ` raven_pause() ` | Emit ` ebreak ` — Raven pauses execution so you can inspect registers and memory |
124+ | ` abs(n) ` | Absolute value of signed int |
125+ | ` min(a, b) ` / ` max(a, b) ` | Integer min/max |
126+ | ` umin(a, b) ` / ` umax(a, b) ` | Unsigned int min/max |
127+ | ` ipow(base, exp) ` | Integer power: ` base^exp ` |
128+
129+ ### Assert / panic
130+
131+ | Function / Macro | Description |
132+ | ----------| -------------|
133+ | ` raven_assert(expr) ` | If ` expr ` is false: print message, pause, exit(1) |
134+ | ` raven_panic(msg) ` | Print ` msg ` to stderr, pause for inspection, exit(1) |
135+
136+ ### Heap allocator
137+
138+ A first-fit free-list allocator backed by a static ` 64 KB ` heap.
139+
140+ | Function | Description |
141+ | ----------| -------------|
142+ | ` malloc(size) ` | Allocate ` size ` bytes; returns ` NULL ` on out-of-memory |
143+ | ` calloc(nmemb, size) ` | Allocate ` nmemb * size ` bytes, zero-initialised |
144+ | ` realloc(ptr, new_size) ` | Resize allocation; copies existing data |
145+ | ` free(ptr) ` | Release allocation; coalesces adjacent free blocks |
146+ | ` raven_heap_free() ` | Returns approximate bytes remaining in the heap |
147+
148+ Change the heap size before including the header:
149+
150+ ``` c
151+ #define RAVEN_HEAP_SIZE (128 * 1024) // 128 KB
152+ #include "raven.h"
153+ ```
154+
155+ > **Tip:** single-step with Raven's **[Dyn]** view active (`v` until `DYN` shows in the status bar). Every `sw` that writes a malloc header will flip the sidebar to show exactly what was written in RAM and where.
107156
108157---
109158
110159## Adding more source files
111160
112- Edit the ` SRCS ` variable in the Makefile:
113-
114161```makefile
115162SRCS = crt0.S main.c mylib.c another.c
116163```
@@ -119,28 +166,21 @@ SRCS = crt0.S main.c mylib.c another.c
119166
120167## Float support
121168
122- ` float_demo.c ` is compiled with ` -march=rv32imf -mabi=ilp32f ` , enabling hardware ` f ` -registers.
123- Open the Run tab in Raven and press ` Tab ` on the register sidebar to switch from integer to float registers and watch ` f0 ` –` f31 ` update in real time as the program runs.
124-
125- To add float support to your own program, change the Makefile target:
169+ `float_demo.c` is compiled with `-march=rv32imf -mabi=ilp32f` for hardware `f`-registers. In Raven' s Run tab, press `Tab` on the register sidebar to switch between integer and float register banks.
126170
127- ``` makefile
128- CC = clang-18
129- CFLAGS = --target=riscv32-unknown-elf -march=rv32imf -mabi=ilp32f \
130- -nostdlib -fuse-ld=lld -O2
131- ```
171+ To enable float in your own program, change `make float-demo` or add the float target to the Makefile pointing at your files.
132172
133173---
134174
135175## How it works
136176
137- ` crt0.S ` sets up the bare minimum before ` main() ` :
177+ `crt0.S` is the only startup code :
138178
139179```asm
140180_start:
141- call main # sp is set by the ELF loader (Raven initialises it)
142- li a7, 93 # exit(return value of main )
181+ call main # Raven initialises sp; main ' s return value lands in a0
182+ li a7, 93 # exit(a0 )
143183 ecall
144184```
145185
146- Raven zeroes BSS automatically when loading the ELF, so no explicit BSS-clear loop is needed in the startup code .
186+ Raven zeroes BSS automatically when loading the ELF, so no explicit BSS-clear loop is needed.
0 commit comments