A practical guide to RISC-V startup code, linker script memory layout, and common boot-time pitfalls translated from the 21ic community.
Unlike ARM Cortex-M, which expects a vector table at address zero, RISC-V resets to a single fixed entry point defined by the linker script. That entry is typically address 0x00000000 for small MCUs, or the start of Flash/ROM for application processors. The first routine is conventionally named _start and is written in assembly.
A minimal RISC-V startup file performs the following steps before calling main:
If step 3 is forgotten, uninitialized global variables retain whatever values were in RAM at power-on, producing intermittent failures that are extremely difficult to debug.
Modern RISC-V toolchains such as riscv-none-elf-gcc and gcc-riscv64-unknown-elf define memory regions with the MEMORY command. A typical production layout looks like this:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K } SECTIONS { .text : { (.text) } > FLASH .rodata : { (.rodata) } > FLASH .data : AT(_data_lma) { _data_vma = .; (.data) _edata = .; } > RAM .bss : { _bss_start = .; (.bss) *(COMMON) _bss_end = .; } > RAM }Key points:
Before blaming hardware for a runaway program, verify the image layout:
riscv-none-elf-readelf -S firmware.elf riscv-none-elf-objdump -d firmware.elf | lessCompare the VMA and LMA columns with the linker script and the map file. In an OpenOCD plus GDB session, place a breakpoint at _start and confirm that sp is loaded with __stack_top before stepping into the C runtime.
This generic RISC-V boot sequence applies directly to the WCH CH32V003, CH32V307, CH32V317, CH32H417, and CH32L103 families, as well as to the SpacemiT K1 and K3 bootloaders. Whether you are writing a bare-metal motor controller on a CH32V003 or porting a custom RTOS to K1, the linker script is the contract between the hardware memory map and the compiler output. Getting it right early prevents weeks of chasing phantom memory corruption later.
Source: RISC-V内核启动流程详解:从复位入口到链接脚本内存布局调优