From 6be8210eae64a4854a05bed73f7a1a13640f7095 Mon Sep 17 00:00:00 2001 From: jake-g00dwin Date: Mon, 5 Jan 2026 20:46:11 -0800 Subject: [PATCH] created a throughly commented startup file. It's based on the OG factory startup code data. --- src/startup_ch32v003.S | 245 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 src/startup_ch32v003.S diff --git a/src/startup_ch32v003.S b/src/startup_ch32v003.S new file mode 100644 index 0000000..b5d9f7e --- /dev/null +++ b/src/startup_ch32v003.S @@ -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 + + +