RISC-V: A Baremetal Introduction using C++. Startup.

Booting a RISC-V Core

From here https://commons.wikimedia.org/wiki/File:Linux_boot_screen_compact.png
Booting an OS is much more complex, but the entry point relies on the same principles.
extern "C" void _enter(void)  
__attribute__((naked,
section(".text.metal.init.enter")));
extern "C" void _start(void)
__attribute__ ((noreturn));
void _enter(void) {
// Setup SP and GP
// The locations are defined in the linker script
__asm__ volatile (
".option push;"
".option norelax;"
"la gp, __global_pointer$;"
".option pop;"
"la sp, _sp;"
"jal zero, _start;"
: /* output: none %0 */
: /* input: none */
: /* clobbers: none */);
// This point will not be executed,
// _start() will be called with no return.
}
  • The sp and gp are aliases to general-purpose registers and these are defined by the ABI and code generation conventions.
  • The zero register is an alias to r0, however, this is defined as constant 0 by the hardware specification, not just by convention.
  • The la instruction is a pseudo instruction for loading addresses. RISC-V defines many such pseudo instructions

Initializing the C++ World

extern "C" std::uintptr_t metal_segment_bss_target_start, 
metal_segment_bss_target_end,
metal_segment_data_source_start ..
metal_segment_itim_target_end;
extern "C" function_t *__init_array_start, *__init_array_end;
// At this point we have a stack and global pointer,
// but no access to global variables.
void _start(void) {
// Init memory regions
// Clear the .bss section
// (global variables with no initial values)
std::fill(&metal_segment_bss_target_start,
&metal_segment_bss_target_end,
0U);
// Initialize the .data section
//(global variables with initial values)
std::copy(&metal_segment_data_source_start,
&metal_segment_data_source_start +
(&metal_segment_data_target_end -
&metal_segment_data_target_start),
&metal_segment_data_target_start);
// Initialize the .itim section
//(code moved from flash to SRAM to improve performance)
std::copy(&metal_segment_itim_source_start,
&metal_segment_itim_source_start +
(&metal_segment_itim_target_end -
&metal_segment_itim_target_start),
&metal_segment_itim_target_start);
// Call constructors
std::for_each( __init_array_start,
__init_array_end,
[](const function_t pf) {pf();});
// Jump to main
auto rc = main();
// Don't expect to return, if so busy loop in the exit function.
_Exit(rc);
}
  • The linker script used here is from the Freedom E-SDK. Any variable named metal_* or __init_*is defined in the linker script.
  • The bss region contains global variables with no initial value. The SRAM allocated to these variables is cleared to 0.
  • The data section contains global variables with initial values. These values are copied from the program image in read-only memory (FLASH/ROM) to SRAM.
  • The itim section is a code section that is to be moved to SRAM to improve performance.
  • The init array is a table of constructor function pointers to construct global variables.

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Phil Mulholland

Phil Mulholland

Experienced in Distributed Systems, Event-Driven Systems, Firmware for SoC/MCU, Systems Simulation, Network Monitoring and Analysis, Automated Testing and RTL.