created a throughly commented startup file.

It's based on the OG factory startup code data.
This commit is contained in:
Jake Goodwin 2026-01-05 20:46:11 -08:00
parent 1ab0edaf24
commit 6be8210eae

245
src/startup_ch32v003.S Normal file
View file

@ -0,0 +1,245 @@
#; Filename: startup_ch32v003.S
#; Author: Jake G
#; Date: 2026-01-05
#; Version: 0.1.0
#; Description: Vectortable and startup code.
#; Mostly it's just built from the QingKeV2 reference manual combined with the
#; comments and definitions I've found online and from the RISC-V ISA for the
#; ISA: RV32I with Ec extension, missing the multiplacation hardware from the
#; CH32V002/4/5/6/7 series.
#; First some info about the terms:
#; handler: Actual code that "handles" events that causes the intterput.
#; Vector table entry: address that points to the code/handler.
#; Example: For SPI1_IRQHandler the order would go:
#; vector_table --> SPI1_IRQHandler --> code or orverride.
#; Create the init section,
#; .init, places it at the reset address.
#; a --> allocate(goes into memory)
#; x --> executable.
#; @progbits --> contains actual code/data not bss
.section .init, "ax", @progbits
.globl _start /* Export entry point, aka where cpu begins after reset.*/
.align 2 /* We pad until we align on 2byte boundry. */
_start: /* The start label */
.option norvc; /* Disables generation of compressed instructions*/
j handle_reset /* Starts from QingKeV2 Table 3-1 Exeception & interrupt vector table.*/
.word 0 /* All of these jump to zero/reset vector if hit, they are reserved / unused. */
.word NMI_Handler /* NMI Handler */
.word HardFault_Handler /* Hard Fault Handler */
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word SysTick_Handler /* SysTick Handler */
.word 0
.word SW_Handler /* SW Handler */
.word 0
/* External Interrupts */
.word WWDG_IRQHandler /* Window Watchdog */
.word PVD_IRQHandler /* PVD through EXTI Line detect */
.word FLASH_IRQHandler /* Flash */
.word RCC_IRQHandler /* RCC */
.word EXTI7_0_IRQHandler /* EXTI Line 7..0 */
.word AWU_IRQHandler /* AWU */
.word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
.word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */
.word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */
.word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */
.word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */
.word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */
.word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */
.word ADC1_IRQHandler /* ADC1 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word USART1_IRQHandler /* USART1 */
.word SPI1_IRQHandler /* SPI1 */
.word TIM1_BRK_IRQHandler /* TIM1 Break */
.word TIM1_UP_IRQHandler /* TIM1 Update */
.word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
/* Re-enable the usage of compressed instructions. */
/* The following aren't posision-sensative so compressed is okay.*/
.option rvc
/* Section where we start code stuff (text), allocated and executable. */
/* The seperate section allows us to relocate it wherever we need. */
.section .text.vector_handler, "ax", @progbits
/*Weak symbol declaractions, can be overriden without modifying startupcode. */
.weak NMI_Handler
.weak HardFault_Handler
.weak SysTick_Handler
.weak SW_Handler
.weak WWDG_IRQHandler
.weak PVD_IRQHandler
.weak FLASH_IRQHandler
.weak RCC_IRQHandler
.weak EXTI7_0_IRQHandler
.weak AWU_IRQHandler
.weak DMA1_Channel1_IRQHandler
.weak DMA1_Channel2_IRQHandler
.weak DMA1_Channel3_IRQHandler
.weak DMA1_Channel4_IRQHandler
.weak DMA1_Channel5_IRQHandler
.weak DMA1_Channel6_IRQHandler
.weak DMA1_Channel7_IRQHandler
.weak ADC1_IRQHandler
.weak I2C1_EV_IRQHandler
.weak I2C1_ER_IRQHandler
.weak USART1_IRQHandler
.weak SPI1_IRQHandler
.weak TIM1_BRK_IRQHandler
.weak TIM1_UP_IRQHandler
.weak TIM1_TRG_COM_IRQHandler
.weak TIM1_CC_IRQHandler
.weak TIM2_IRQHandler
/* Define te default handler's*/
/* These handlers preform infinite loops if entered without an override.*/
/* ASM Instructions: `1` Local label, `j` jump, `1b` nearest label 1 backwards. */
/* From what I can see it's a single line way to impliment a inf loop.*/
/* These using the compressed instructions only take 2Bytes per line. */
NMI_Handler: 1: j 1b
HardFault_Handler: 1: j 1b
SysTick_Handler: 1: j 1b
SW_Handler: 1: j 1b
WWDG_IRQHandler: 1: j 1b
PVD_IRQHandler: 1: j 1b
FLASH_IRQHandler: 1: j 1b
RCC_IRQHandler: 1: j 1b
EXTI7_0_IRQHandler: 1: j 1b
AWU_IRQHandler: 1: j 1b
DMA1_Channel1_IRQHandler: 1: j 1b
DMA1_Channel2_IRQHandler: 1: j 1b
DMA1_Channel3_IRQHandler: 1: j 1b
DMA1_Channel4_IRQHandler: 1: j 1b
DMA1_Channel5_IRQHandler: 1: j 1b
DMA1_Channel6_IRQHandler: 1: j 1b
DMA1_Channel7_IRQHandler: 1: j 1b
ADC1_IRQHandler: 1: j 1b
I2C1_EV_IRQHandler: 1: j 1b
I2C1_ER_IRQHandler: 1: j 1b
USART1_IRQHandler: 1: j 1b
SPI1_IRQHandler: 1: j 1b
TIM1_BRK_IRQHandler: 1: j 1b
TIM1_UP_IRQHandler: 1: j 1b
TIM1_TRG_COM_IRQHandler: 1: j 1b
TIM1_CC_IRQHandler: 1: j 1b
TIM2_IRQHandler: 1: j 1b
/* Another section that holds the text section, aka code for handle_reset*/
/* Same as previous sections, marked for allocation and execution. */
.section .text.handle_reset, "ax", @progbits
/* We weakly define the handle_reset label */
.weak handle_reset
/* Align 1 because of RVC(compressed instructions).*/
.align 1
handle_reset:
/* Global Pointer(GP) register init.*/
.option push /* What does this mean? */
.option norelax /* Prevents optimizing the la(load address) instruction. */
la gp, __global_pointer$ /* __global_pointer$ is liker defined */
.option pop /* What does this mean? */
/* We need to do the stack setup because the MCU has no MMU or stack checking */
/* GNUC stuff needs some more inspection.*/
1:
la sp, _eusrstack /*Sets stack pointer to the top of user stack.*/
/* MAX: Addition needed here */
#if __GNUC__ > 10
.option arch, +zicsr /*Enables CSR(control and status registers) needed for newer GCC toolchains. */
#endif
2:
/* Load data section from flash to RAM */
/* LMA (Load Memory Address) is a term used in embedded systems to */
/* indicate the address in memory where a program or data is loaded */
/* VMA: Virtual Memory Address. */
la a0, _data_lma /* SRC: Load arg/address a0 with (flash/LMA) */
la a1, _data_vma /* DST: load return/address a1 with (RAM/VMA) */
la a2, _edata /* End of data */
bgeu a1, a2, 2f /* If `.data` is empty then skip. */
/* The copy loop: local label*/
1:
lw t0, (a0) /*Load word into temp 0 reg, from a0 `()` from pointed loc? */
sw t0, (a1) /*Stores word from tmp0 into a1 reg pointed addr */
addi a0, a0, 4 /*Add immediate, */
addi a1, a1, 4 /*Add immediate, */
bltu a1, a2, 1b /*Unsigned comparision, with jump/loop back.*/
/* Clear the .bss: local label */
/* The .bss section is all for the zero initialized globals. */
2:
/* clear bss section */
la a0, _sbss /* Load address reg a0 with linker defined _sbss */
la a1, _ebss /* Load address reg a1 with linker defined _ebss */
bgeu a0, a1, 2f /* Compare and jump 2 forward */
/* */
1:
sw zero, (a0) /* Store Word into a0, aka zero out a0 */
addi a0, a0, 4 /* Add immediate*/
bltu a0, a1, 1b /* Compare and loop this local label */
/* This is jumped to if X */
/* */
2:
/* Sets the MIE, machine interrupt enable bit, enables interrupts globally */
/* Needed for the QingKeV2 otherwise all the interrupts are masked forever. */
li t0, 0x80 /* Load immediate to temp 0, with 0x80 */
csrw mstatus, t0 /* Control and status register write: t0 to mstatus. */
/* Sets/configures the QingKeV2 interrupt controller */
/* Enables: vectored interrupts, nested interrupts. */
li t0, 0x3 /* Load immediate to temp 0 with `0x3` */
csrw 0x804, t0 /* Control and status register write: t0. */
/* Load address into tmp 0 with address of _start code. */
la t0, _start
ori t0, t0, 3 /* OR immediate, */
csrw mtvec, t0 /* Control status register write: _start to vector base table*/
/* mtvec: 00 --> direct mode, 11 --> vectored mode. */
/* Logical OR with 3(0b11) means we selected vector mode. */
/* Saves some space if we don't use it.*/
/* Optional C++ runtime support:*/
/* Run Constructors __libc_fini_array */
/* Destructors registered at `atexit` */
#if defined(__PIO_CPP_SUPPORT__)
/* register fini (destructor array) call at exit if wanted (bloats up RAM+Flash) */
#if defined(__PIO_CPP_CALL_FINI__)
la a0,__libc_fini_array
call atexit
#endif
/* call into C++ constructors now */
call __libc_init_array
#endif
/* SystemInit: does stuff like clock setup, PLL cnofig, Periph reset. */
/* This mcu and microprocessor only has "M-Mode" or machine mode */
/* The other modes, U(user) and S(???) aren't availble. */
/* MEPC: Machine Exception Program Counter.*/
/* Jump's and links(ra --> return address) to SystemInit */
jal SystemInit
la t0, main /* Load address of main into tmp 0 */
csrw mepc, t0 /* Control status register write t0 to the */
mret