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
17vector_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
 52vector_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
1881:
189    beq a0, a1, 2f
190    sub sp, sp, t0
191    addi a1, a1, 1
192    j 1b
1932:
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
2891:
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
2962:
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
3041:
305    lw t0, (a0)
306    sw t0, (a1)
307    addi a0, a0, 4
308    addi a1, a1, 4
309    bltu a1, a2, 1b
3102:
311    /* Clear bss section */
312    la a0, __bss_start
313    la a1, _end
314    bgeu a0, a1, 2f
3151:
316    sw zero, (a0)
317    addi a0, a0, 4
318    bltu a0, a1, 1b
3192:
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:
3881:
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
399smp_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
4061:
407    wfi
408    j 1b
4092:
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
425early_exc_entry:
426    wfi
427    j early_exc_entry