Startup File startup_<Device>.S

The Startup File startup_<device>.S contains:
  • The reset handler which is executed after CPU reset and typically calls the SystemInit() function.

  • The setup values for the stack pointer SP and global pointor GP for small data access.

  • Exception vectors of the Nuclei Processor with weak functions that implement default routines.

  • Interrupt vectors that are device specific with weak functions that implement default routines.

The processer level start flow is implemented in the startup_<Device>.S. Detail description as below picture:

The IAR version of startup code located in startup_<Device>.c.

Stage1: Interrupt and Exception initialization
  • Disable Interrupt

  • Initialize GP, SP for single core or smp core if existed

  • Initialize NMI entry and set default NMI handler

  • Initialize exception entry to early exception entry in startup_<Device>.S

  • Initialize vector table entry and set default interrupt handler

  • Initialize Interrupt mode as ECLIC mode. (ECLIC mode is proposed. Default mode is CLINT mode)

Stage2: Hardware initialization
  • Enable FPU if necessary

  • Enable VPU if necessary

  • Enable Zc if necessary

Stage3: Section initialization
  • Copy section, e.g. data section, text section if necessary.

  • Clear Block Started by Symbol (BSS) section

  • Call user defined SystemInit() for system clock initialization.

  • Call __libc_fini_array and __libc_init_array functions to do C library initialization

  • Call _premain_init function to do initialization steps before main function

  • Initialize exception entry to exception entry in intexc_<Device>.S

  • Enable BPU of Nuclei CPU

  • Jump Main

The file exists for each supported toolchain and is the only toolchain specific NMSIS file.

To adapt the file to a new device only the interrupt vector table needs to be extended with the device-specific interrupt handlers.

The naming convention for the interrupt handler names are eclic_<interrupt_name>_handler.

This table needs to be consistent with IRQn_Type that defines all the IRQ numbers for each interrupt.

The following example shows the extension of the interrupt vector table for the GD32VF103 device family.

 1     .section .text.vtable
 2 
 3     .weak  eclic_msip_handler
 4     .weak  eclic_mtip_handler
 5     .weak  eclic_pmaf_handler
 6     /* Adjusted for GD32VF103 interrupt handlers */
 7     .weak  eclic_wwdgt_handler
 8     .weak  eclic_lvd_handler
 9     .weak  eclic_tamper_handler
10         :    :
11         :    :
12     .weak  eclic_can1_ewmc_handler
13     .weak  eclic_usbfs_handler
14 
15     .globl vector_base
16     .type vector_base, @object
17 vector_base:
18     /* Run in FlashXIP download mode */
19     j _start                                                /* 0: Reserved, Jump to _start when reset for vector table not remapped cases.*/
20     .align LOG_REGBYTES                                     /*    Need to align 4 byte for RV32, 8 Byte for RV64 */
21     DECLARE_INT_HANDLER     default_intexc_handler          /* 1: Reserved */
22     DECLARE_INT_HANDLER     default_intexc_handler          /* 2: Reserved */
23     DECLARE_INT_HANDLER     eclic_msip_handler              /* 3: Machine software interrupt */
24                     :          :
25                     :          :
26     /* Adjusted for Vendor Defined External Interrupts */
27     DECLARE_INT_HANDLER     eclic_wwdgt_handler             /* 19: Window watchDog timer interrupt */
28 
29     DECLARE_INT_HANDLER     eclic_lvd_handler               /* 20: LVD through EXTI line detect interrupt */
30     DECLARE_INT_HANDLER     eclic_tamper_handler            /* 21: tamper through EXTI line detect */
31                     :          :
32                     :          :
33     DECLARE_INT_HANDLER     eclic_can1_ewmc_handler         /* 85: CAN1 EWMC interrupt */
34     DECLARE_INT_HANDLER     eclic_usbfs_handler             /* 86: USBFS global interrupt */

startup_Device.S Template File

Here provided a riscv-gcc template startup assemble code template file as below. The files for other compilers can slightly differ from this version.

  1 /*
  2  * Copyright (c) 2019 Nuclei Limited. All rights reserved.
  3  *
  4  * SPDX-License-Identifier: Apache-2.0
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the License); you may
  7  * not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  * www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing permissions and
 16  * limitations under the License.
 17  */
 18 /******************************************************************************
 19  * \file     startup_<Device>.S
 20  * \brief    NMSIS Nuclei N/NX Class Core based Core Device Startup File for
 21  *           Device <Device>
 22  * \version  V2.1.0
 23  * \date     19. Dec 2023
 24  *
 25  ******************************************************************************/
 26 
 27 #include "riscv_encoding.h"
 28 
 29 /* TODO: If BOOT_HARTID is not defined, default value is 0, change it to your desired default boot hartid */
 30 #ifndef BOOT_HARTID
 31     .equ BOOT_HARTID,    0
 32 #endif
 33 
 34 .macro DECLARE_INT_HANDLER  INT_HDL_NAME
 35 #if defined(__riscv_xlen) && (__riscv_xlen == 32)
 36     .word \INT_HDL_NAME
 37 #else
 38     .dword \INT_HDL_NAME
 39 #endif
 40 .endm
 41 
 42     .section .text.vtable
 43 
 44     /* TODO: Add your interrupt handler in this vector table */
 45     .weak eclic_msip_handler
 46     .weak eclic_mtip_handler
 47     .weak eclic_inter_core_int_handler
 48     .globl vector_base
 49     .type vector_base, @object
 50     .option push
 51     .option norelax
 52 vector_base:
 53 #ifndef VECTOR_TABLE_REMAPPED
 54     j _start                                                /* 0: Reserved, Jump to _start when reset for vector table not remapped cases.*/
 55     .align LOG_REGBYTES                                     /*    Need to align 4 byte for RV32, 8 Byte for RV64 */
 56 #else
 57     DECLARE_INT_HANDLER     default_intexc_handler          /* 0: Reserved, default handler for vector table remapped cases */
 58 #endif
 59     DECLARE_INT_HANDLER     default_intexc_handler          /* 1: Reserved */
 60     DECLARE_INT_HANDLER     default_intexc_handler          /* 2: Reserved */
 61     DECLARE_INT_HANDLER     eclic_msip_handler              /* 3: Machine software interrupt */
 62 
 63     DECLARE_INT_HANDLER     default_intexc_handler          /* 4: Reserved */
 64     DECLARE_INT_HANDLER     default_intexc_handler          /* 5: Reserved */
 65     DECLARE_INT_HANDLER     default_intexc_handler          /* 6: Reserved */
 66     DECLARE_INT_HANDLER     eclic_mtip_handler              /* 7: Machine timer interrupt */
 67 
 68     DECLARE_INT_HANDLER     default_intexc_handler          /* 8: Reserved */
 69     DECLARE_INT_HANDLER     default_intexc_handler          /* 9: Reserved */
 70     DECLARE_INT_HANDLER     default_intexc_handler          /* 10: Reserved */
 71     DECLARE_INT_HANDLER     default_intexc_handler          /* 11: Reserved */
 72 
 73     DECLARE_INT_HANDLER     default_intexc_handler          /* 12: Reserved */
 74     DECLARE_INT_HANDLER     default_intexc_handler          /* 13: Reserved */
 75     DECLARE_INT_HANDLER     default_intexc_handler          /* 14: Reserved */
 76     DECLARE_INT_HANDLER     default_intexc_handler          /* 15: Reserved */
 77 
 78     DECLARE_INT_HANDLER     eclic_inter_core_int_handler    /* 16: Reserved */
 79     DECLARE_INT_HANDLER     default_intexc_handler          /* 17: Reserved */
 80     DECLARE_INT_HANDLER     default_intexc_handler          /* 18: Reserved */
 81     DECLARE_INT_HANDLER     default_intexc_handler          /* 19: Interrupt 19 */
 82 
 83     DECLARE_INT_HANDLER     default_intexc_handler          /* 20: Interrupt 20 */
 84     DECLARE_INT_HANDLER     default_intexc_handler          /* 21: Interrupt 21 */
 85     DECLARE_INT_HANDLER     default_intexc_handler          /* 22: Interrupt 22 */
 86     DECLARE_INT_HANDLER     default_intexc_handler          /* 23: Interrupt 23 */
 87 
 88     DECLARE_INT_HANDLER     default_intexc_handler          /* 24: Interrupt 24 */
 89     DECLARE_INT_HANDLER     default_intexc_handler          /* 25: Interrupt 25 */
 90     DECLARE_INT_HANDLER     default_intexc_handler          /* 26: Interrupt 26 */
 91     DECLARE_INT_HANDLER     default_intexc_handler          /* 27: Interrupt 27 */
 92 
 93     DECLARE_INT_HANDLER     default_intexc_handler          /* 28: Interrupt 28 */
 94     DECLARE_INT_HANDLER     default_intexc_handler          /* 29: Interrupt 29 */
 95     DECLARE_INT_HANDLER     default_intexc_handler          /* 30: Interrupt 30 */
 96     DECLARE_INT_HANDLER     default_intexc_handler          /* 31: Interrupt 31 */
 97 
 98     DECLARE_INT_HANDLER     default_intexc_handler          /* 32: Interrupt 32 */
 99     DECLARE_INT_HANDLER     default_intexc_handler          /* 33: Interrupt 33 */
100     DECLARE_INT_HANDLER     default_intexc_handler          /* 34: Interrupt 34 */
101     DECLARE_INT_HANDLER     default_intexc_handler          /* 35: Interrupt 35 */
102 
103     DECLARE_INT_HANDLER     default_intexc_handler          /* 36: Interrupt 36 */
104     DECLARE_INT_HANDLER     default_intexc_handler          /* 37: Interrupt 37 */
105     DECLARE_INT_HANDLER     default_intexc_handler          /* 38: Interrupt 38 */
106     DECLARE_INT_HANDLER     default_intexc_handler          /* 39: Interrupt 39 */
107 
108     DECLARE_INT_HANDLER     default_intexc_handler          /* 40: Interrupt 40 */
109     DECLARE_INT_HANDLER     default_intexc_handler          /* 41: Interrupt 41 */
110     DECLARE_INT_HANDLER     default_intexc_handler          /* 42: Interrupt 42 */
111     DECLARE_INT_HANDLER     default_intexc_handler          /* 43: Interrupt 43 */
112 
113     DECLARE_INT_HANDLER     default_intexc_handler          /* 44: Interrupt 44 */
114     DECLARE_INT_HANDLER     default_intexc_handler          /* 45: Interrupt 45 */
115     DECLARE_INT_HANDLER     default_intexc_handler          /* 46: Interrupt 46 */
116     DECLARE_INT_HANDLER     default_intexc_handler          /* 47: Interrupt 47 */
117 
118     DECLARE_INT_HANDLER     default_intexc_handler          /* 48: Interrupt 48 */
119     DECLARE_INT_HANDLER     default_intexc_handler          /* 49: Interrupt 49 */
120     DECLARE_INT_HANDLER     default_intexc_handler          /* 50: Interrupt 50 */
121     DECLARE_INT_HANDLER     default_intexc_handler          /* 51: Interrupt 51 */
122 
123     DECLARE_INT_HANDLER     default_intexc_handler          /* 52: Interrupt 52 */
124     DECLARE_INT_HANDLER     default_intexc_handler          /* 53: Interrupt 53 */
125     DECLARE_INT_HANDLER     default_intexc_handler          /* 54: Interrupt 54 */
126     DECLARE_INT_HANDLER     default_intexc_handler          /* 55: Interrupt 55 */
127 
128     DECLARE_INT_HANDLER     default_intexc_handler          /* 56: Interrupt 56 */
129     DECLARE_INT_HANDLER     default_intexc_handler          /* 57: Interrupt 57 */
130     DECLARE_INT_HANDLER     default_intexc_handler          /* 58: Interrupt 58 */
131     DECLARE_INT_HANDLER     default_intexc_handler          /* 59: Interrupt 59 */
132 
133     DECLARE_INT_HANDLER     default_intexc_handler          /* 60: Interrupt 60 */
134     DECLARE_INT_HANDLER     default_intexc_handler          /* 61: Interrupt 61 */
135     DECLARE_INT_HANDLER     default_intexc_handler          /* 62: Interrupt 62 */
136     DECLARE_INT_HANDLER     default_intexc_handler          /* 63: Interrupt 63 */
137 
138     .option pop
139 
140 
141     .section .text.init
142     .globl _start
143     .type _start, @function
144 
145 /**
146  * Reset Handler called on controller reset
147  */
148 _start:
149     /* ===== Startup Stage 1 ===== */
150     /* Disable Global Interrupt */
151     csrc CSR_MSTATUS, MSTATUS_MIE
152 
153     /* If SMP_CPU_CNT is not defined,
154      * assume that only 1 core is allowed to run,
155      * the core hartid is defined via BOOT_HARTID.
156      * other harts if run to here, just do wfi in __amp_wait
157      */
158 #ifndef SMP_CPU_CNT
159     /* take bit 0-7 for hart id in a local cluster */
160     csrr a0, CSR_MHARTID
161     andi a0, a0, 0xFF
162     /* BOOT_HARTID is configurable in Makefile via BOOT_HARTID variable */
163     li a1, BOOT_HARTID
164     bne a0, a1, __amp_wait
165 #endif
166 
167     /* Initialize GP and TP and jump table base when zcmt enabled */
168     .option push
169     .option norelax
170     la gp, __global_pointer$
171     la tp, __tls_base
172 #if defined(__riscv_zcmt)
173     la t0, __jvt_base$
174     csrw CSR_JVT, t0
175 #endif
176     .option pop
177 
178 #if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
179     /* Set correct sp for each cpu
180      * each stack size is __STACK_SIZE
181      * defined in linker script */
182     lui t0, %hi(__STACK_SIZE)
183     addi t0, t0, %lo(__STACK_SIZE)
184     la sp, _sp
185     csrr a0, CSR_MHARTID
186     andi a0, a0, 0xFF
187     li a1, 0
188 1:
189     beq a0, a1, 2f
190     sub sp, sp, t0
191     addi a1, a1, 1
192     j 1b
193 2:
194 #else
195     /* Set correct sp for current cpu */
196     la sp, _sp
197 #endif
198 
199     /*
200      * Set the the NMI base mnvec to share
201      * with mtvec by setting CSR_MMISC_CTL
202      * bit 9 NMI_CAUSE_FFF to 1
203      */
204     li t0, MMISC_CTL_NMI_CAUSE_FFF
205     csrs CSR_MMISC_CTL, t0
206 
207     /*
208      * Enable Zc feature when compiled zcmp & zcmt
209      */
210 #if defined(__riscv_zcmp) || defined(__riscv_zcmt)
211     li t0, MMISC_CTL_ZC
212     csrs CSR_MMISC_CTL, t0
213 #endif
214 
215     /*
216      * Intialize ECLIC vector interrupt
217      * base address mtvt to vector_base
218      */
219     la t0, vector_base
220     csrw CSR_MTVT, t0
221 
222     /*
223      * Set ECLIC non-vector entry to be controlled
224      * by mtvt2 CSR register.
225      * Intialize ECLIC non-vector interrupt
226      * base address mtvt2 to irq_entry.
227      */
228     la t0, irq_entry
229     csrw CSR_MTVT2, t0
230     csrs CSR_MTVT2, 0x1
231 
232     /*
233      * Set Exception Entry MTVEC to early_exc_entry
234      * Due to settings above, Exception and NMI
235      * will share common entry.
236      * This early_exc_entry is only used during early
237      * boot stage before main
238      */
239     la t0, early_exc_entry
240     csrw CSR_MTVEC, t0
241 
242     /* Set the interrupt processing mode to ECLIC mode */
243     li t0, 0x3f
244     csrc CSR_MTVEC, t0
245     csrs CSR_MTVEC, 0x3
246 
247     /* ===== Startup Stage 2 ===== */
248 
249     /* Enable FPU and Vector Unit if f/d/v exist in march */
250 #if defined(__riscv_flen) && __riscv_flen > 0
251     /* Enable FPU, and set state to initial */
252     li t0, MSTATUS_FS
253     csrc mstatus, t0
254     li t0, MSTATUS_FS_INITIAL
255     csrs mstatus, t0
256 #endif
257 
258 #if defined(__riscv_vector)
259     /* Enable Vector, and set state to initial */
260     li t0, MSTATUS_VS
261     csrc mstatus, t0
262     li t0, MSTATUS_VS_INITIAL
263     csrs mstatus, t0
264 #endif
265 
266     /* Enable mcycle and minstret counter */
267     csrci CSR_MCOUNTINHIBIT, 0x5
268 
269 #if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
270     csrr a0, CSR_MHARTID
271     li a1, BOOT_HARTID
272     bne a0, a1, __skip_init
273 #endif
274 
275 __init_common:
276     /* ===== Startup Stage 3 ===== */
277     /*
278      * Load text section from CODE ROM to CODE RAM
279      * when text LMA is different with VMA
280      */
281     la a0, _text_lma
282     la a1, _text
283     /* If text LMA and VMA are equal
284      * then no need to copy text section */
285     beq a0, a1, 2f
286     la a2, _etext
287     bgeu a1, a2, 2f
288 
289 1:
290     /* Load code section if necessary */
291     lw t0, (a0)
292     sw t0, (a1)
293     addi a0, a0, 4
294     addi a1, a1, 4
295     bltu a1, a2, 1b
296 2:
297     /* Load data section */
298     la a0, _data_lma
299     la a1, _data
300     /* If data vma=lma, no need to copy */
301     beq a0, a1, 2f
302     la a2, _edata
303     bgeu a1, a2, 2f
304 1:
305     lw t0, (a0)
306     sw t0, (a1)
307     addi a0, a0, 4
308     addi a1, a1, 4
309     bltu a1, a2, 1b
310 2:
311     /* Clear bss section */
312     la a0, __bss_start
313     la a1, _end
314     bgeu a0, a1, 2f
315 1:
316     sw zero, (a0)
317     addi a0, a0, 4
318     bltu a0, a1, 1b
319 2:
320 
321 .globl _start_premain
322 .type _start_premain, @function
323 _start_premain:
324     /*
325      * Call vendor defined SystemInit to
326      * initialize the micro-controller system
327      * SystemInit will just be called by boot cpu
328      */
329     call SystemInit
330 
331     /*
332      * Call C/C++ constructor start up code,
333      * __libc_fini is defined in linker script,
334      * so register_fini function will be called
335      * and will run atexit (__libc_fini_array)
336      * to do previous call atexit function
337      */
338     call __libc_init_array
339 
340 __skip_init:
341     /* Sync all harts at this function */
342     call __sync_harts
343 
344     /* do pre-init steps before main */
345     /* _premain_init will be called by each cpu
346      * please make sure the implementation of __premain_int
347      * considered this
348      */
349     call _premain_init
350 
351     /*
352      * When all initialization steps done
353      * set exception entry to correct exception
354      * entry and jump to main.
355      * And set the interrupt processing mode to
356      * ECLIC mode
357      */
358     la t0, exc_entry
359     csrw CSR_MTVEC, t0
360     li t0, 0x3f
361     csrc CSR_MTVEC, t0
362     csrs CSR_MTVEC, 0x3
363 
364     /* BPU cold bringup need time, so enable BPU before enter to main */
365     li t0, MMISC_CTL_BPU
366     csrs CSR_MMISC_CTL, t0
367 
368     /* ===== Call SMP Main Function  ===== */
369     /* argc = argv = 0 */
370     li a0, 0
371     li a1, 0
372 #if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
373     /* The weak implementation of smp_main is in this file */
374     call smp_main
375 #else
376 #ifdef RTOS_RTTHREAD
377     // Call entry function when using RT-Thread
378     call entry
379 #else
380     call main
381 #endif
382 #endif
383     /* do post-main steps after main
384      * this function will be called by each cpu */
385     call _postmain_fini
386 
387 __amp_wait:
388 1:
389     wfi
390     j 1b
391 
392 #if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
393 /*
394  * You can re-implement smp_main function in your code
395  * to do smp boot process and handle multi harts
396  */
397 .weak smp_main
398 .type smp_main, @function
399 smp_main:
400     addi sp, sp, -2*REGBYTES
401     STORE ra, 0*REGBYTES(sp)
402     /* only boot hart goto main, other harts do wfi */
403     csrr t0, CSR_MHARTID
404     li t1, BOOT_HARTID
405     beq t0, t1, 2f
406 1:
407     wfi
408     j 1b
409 2:
410 #ifdef RTOS_RTTHREAD
411     // Call entry function when using RT-Thread
412     call entry
413 #else
414     call main
415 #endif
416     LOAD ra, 0*REGBYTES(sp)
417     addi sp, sp, 2*REGBYTES
418     ret
419 #endif
420 
421 /* Early boot exception entry before main */
422 .align 6
423 .global early_exc_entry
424 .type early_exc_entry, @function
425 early_exc_entry:
426     wfi
427     j early_exc_entry