An academic project for mastering RISC-V assembly language (RV32I/M instruction set) through progressive exercises. This project pairs C implementations with hand-written RISC-V assembly, targeting a custom embedded platform (CEP - Custom Embedded Platform).
The project is organized into 5 progressive lab assignments (tp1-tp5), each building on fundamental assembly concepts:
- GCD (PGCD) using Euclid's algorithm
- Array summation with different strategies
- Multiplication algorithms (simple, Egyptian, native)
- Conditional statements and branching
- Recursive functions (factorial, Fibonacci)
- String manipulation basics
- String operations (length, reversal)
- Linked list manipulation
- Array sorting algorithms
- Structure handling in assembly
- Binary search trees (BST)
- Tail-call optimization
- Performance comparison: naive → optimized → super-optimized
- Loop unrolling and register optimization
- Timer-based interrupts (CLINT)
- External interrupts (PLIC)
- LED and push-button I/O
- HDMI frame buffer manipulation
- RISC-V Toolchain:
riscv32-unknown-elf-gcc(GCC cross-compiler) - QEMU:
qemu-system-riscv32(for emulation) - Build Tools:
make,flex,bison - Debugger:
riscv32-unknown-elf-gdb
Set the toolchain path (if not in default location):
export RVDIR=/path/to/riscvcd tp1 # Start with tp1, or choose tp2-tp5makeThis will:
- Compile all
.cfiles to.oobject files - Assemble all
fct_*.sfiles to.oobject files - Validate assembly structure (label ordering, context blocks)
- Link C and assembly objects into executable binaries
- Generate
.ctxt,.fun, and.stxetdvalidation files
Expected output: Binary files (pgcd, somme, mult_egypt, etc.) appear in the directory.
make pgcdThis builds only the pgcd binary from pgcd.c + fct_pgcd.s.
qemu-system-riscv32 -machine cep -nographic -bios none -kernel pgcdExpected output:
PGCD calculé
en C : 5
en assembleur: 5
Press Ctrl+A then X to exit QEMU.
../common/verif_etud.shThis script:
- Builds all exercises in the current lab
- Runs each binary in QEMU
- Compares output with expected results in
test/<name>.sortie - Reports pass/fail status with colored output
Expected output:
pgcd................OK
somme...............OK
mult_egypt..........OK
Terminal 1 - Start QEMU in debug mode:
qemu-system-riscv32 -machine cep -nographic -bios none -s -S -kernel pgcd(waits for debugger connection on port 1234)
Terminal 2 - Connect GDB:
riscv32-unknown-elf-gdb -ex "target remote :1234" pgcd
# Inside GDB:
(gdb) break pgcd # Set breakpoint at function
(gdb) continue # Start execution
(gdb) info registers # View register contents
(gdb) stepi # Step one assembly instruction
(gdb) x/10i $pc # Examine next 10 instructionsOr use the provided script:
cd ../exam
./gdb-script.sh ../tp1/pgcdmake cleanRemoves: .o files, binaries, .ctxt, .fun, .stxetd, and .sortie files.
| Error | Cause | Solution |
|---|---|---|
Étiquette de la fonction X manquante |
Missing function label | Add function_name: label |
Attention à l'étiquette X_fin_prologue |
Missing prologue label | Add function_name_fin_prologue: label |
Les étiquettes dans votre fonction doivent être, dans l'ordre |
Wrong label order | Order: function: → _fin_prologue: → _debut_epilogue: → ret |
access fault (runtime) |
Invalid memory access | Check memory alignment, verify addresses |
Illegal instruction (runtime) |
Invalid opcode or unimplemented instruction | Verify RISC-V syntax, check ISA extension requirements |
Each exercise follows this structure:
<name>.c # C driver with main() and test harness
fct_<name>.s # RISC-V assembly implementation
<name> # Compiled binary (created by make)
test/<name>.sortie # Expected output for validation
Example: pgcd.c + fct_pgcd.s → pgcd binary
Every assembly function must follow this structure:
/* DEBUT DU CONTEXTE
Fonction :
function_name : feuille # or non-feuille
Contexte :
param1 : registre a0
temp1 : registre t0
local1 : pile *(sp+0)
FIN DU CONTEXTE */
function_name:
function_name_fin_prologue:
# Function body here
function_name_debut_epilogue:
retImportant: The label order is strictly enforced by build scripts.
| Register | Usage | Preserved? |
|---|---|---|
a0-a7 |
Arguments & return values | No |
t0-t6 |
Temporary registers | No |
s0-s11 |
Saved registers | Yes (by callee) |
sp |
Stack pointer | Yes |
ra |
Return address | Yes (save before call) |
- ABI:
-mabi=ilp32(32-bit integers, longs, pointers) - ISA:
-march=rv32im(M extension for multiply/divide) - Standard:
-std=c99with-Wall -Wextra - Linking:
-nostartfiles -nostdlib -static -T cep.ld
Memory-mapped I/O devices:
| Device | Base Address | Description |
|---|---|---|
| LEDs | 0x30000000 |
Output display |
| Push Buttons | 0x30000008 |
Input polling/interrupts |
| PLIC | 0x0c000000 |
External interrupt controller |
| CLINT | 0x02000000 |
Timer and software interrupts |
| Frame Buffer | 0x70000000 |
HDMI video output (720p/1080p) |
The verif_etud.sh script automates testing:
- Compiles each exercise
- Runs binary in QEMU emulator
- Compares output with
test/<name>.sortie - Reports errors: "access fault", "Illegal instruction"