Startup File startup_<Device>.S

Caution

Please be informed that the NMSIS-Core Device Templates may not be updated in a timely manner and thus could become outdated. We suggest referring to the specific implementation of evalsoc in the Nuclei SDK for the latest reference template. This reference template may not be actively maintained in the future.

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 *  Nuclei Eval SoC which support Nuclei N/NX class cores
 22 * \version  V1.00
 23 * \date     17. Dec 2019
 24 *
 25 ******************************************************************************/
 26
 27/* Please check the TODO items */
 28
 29#include "riscv_encoding.h"
 30
 31/* TODO: Require Nuclei SDK >= 0.6.0, which introduced this cpufeature.h */
 32#include "cpufeature.h"
 33
 34/* If BOOT_HARTID is not defined, default value is 0 */
 35#ifndef BOOT_HARTID
 36    .equ BOOT_HARTID,    0
 37#endif
 38
 39.macro DECLARE_INT_HANDLER  INT_HDL_NAME
 40#if defined(__riscv_xlen) && (__riscv_xlen == 32)
 41    .word \INT_HDL_NAME
 42#else
 43    .dword \INT_HDL_NAME
 44#endif
 45.endm
 46
 47#ifdef CFG_HAS_CLIC
 48    .section .text.vtable
 49
 50    .weak eclic_msip_handler
 51    .weak eclic_mtip_handler
 52    .weak eclic_uart0_int_handler
 53    .weak eclic_inter_core_int_handler
 54    .globl vector_base
 55    .type vector_base, @object
 56    .option push
 57    .option norelax
 58vector_base:
 59#ifndef VECTOR_TABLE_REMAPPED
 60    j _start                                                /* 0: Reserved, Jump to _start when reset for vector table not remapped cases.*/
 61    .align LOG_REGBYTES                                     /*    Need to align 4 byte for RV32, 8 Byte for RV64 */
 62#else
 63    DECLARE_INT_HANDLER     default_intexc_handler          /* 0: Reserved, default handler for vector table remapped cases */
 64#endif
 65    DECLARE_INT_HANDLER     default_intexc_handler          /* 1: Reserved */
 66    DECLARE_INT_HANDLER     default_intexc_handler          /* 2: Reserved */
 67    DECLARE_INT_HANDLER     eclic_msip_handler              /* 3: Machine software interrupt */
 68
 69    DECLARE_INT_HANDLER     default_intexc_handler          /* 4: Reserved */
 70    DECLARE_INT_HANDLER     default_intexc_handler          /* 5: Reserved */
 71    DECLARE_INT_HANDLER     default_intexc_handler          /* 6: Reserved */
 72    DECLARE_INT_HANDLER     eclic_mtip_handler              /* 7: Machine timer interrupt */
 73
 74    DECLARE_INT_HANDLER     default_intexc_handler          /* 8: Reserved */
 75    DECLARE_INT_HANDLER     default_intexc_handler          /* 9: Reserved */
 76    DECLARE_INT_HANDLER     default_intexc_handler          /* 10: Reserved */
 77    DECLARE_INT_HANDLER     default_intexc_handler          /* 11: Reserved */
 78
 79    DECLARE_INT_HANDLER     default_intexc_handler          /* 12: Reserved */
 80    DECLARE_INT_HANDLER     default_intexc_handler          /* 13: Reserved */
 81    DECLARE_INT_HANDLER     default_intexc_handler          /* 14: Reserved */
 82    DECLARE_INT_HANDLER     default_intexc_handler          /* 15: Reserved */
 83
 84    DECLARE_INT_HANDLER     eclic_inter_core_int_handler    /* 16: Reserved */
 85    DECLARE_INT_HANDLER     default_intexc_handler          /* 17: Reserved */
 86    DECLARE_INT_HANDLER     default_intexc_handler          /* 18: Reserved */
 87    /* TODO Below are external interrupt handlers, please define them as your requirements, you need to increase or decrease it, and define correct interrupt handler name */
 88    DECLARE_INT_HANDLER     default_intexc_handler          /* 19: Interrupt 19 */
 89
 90    DECLARE_INT_HANDLER     default_intexc_handler          /* 20: Interrupt 20 */
 91    DECLARE_INT_HANDLER     default_intexc_handler          /* 21: Interrupt 21 */
 92    DECLARE_INT_HANDLER     default_intexc_handler          /* 22: Interrupt 22 */
 93    DECLARE_INT_HANDLER     default_intexc_handler          /* 23: Interrupt 23 */
 94
 95    DECLARE_INT_HANDLER     default_intexc_handler          /* 24: Interrupt 24 */
 96    DECLARE_INT_HANDLER     default_intexc_handler          /* 25: Interrupt 25 */
 97    DECLARE_INT_HANDLER     default_intexc_handler          /* 26: Interrupt 26 */
 98    DECLARE_INT_HANDLER     default_intexc_handler          /* 27: Interrupt 27 */
 99
100    DECLARE_INT_HANDLER     default_intexc_handler          /* 28: Interrupt 28 */
101    DECLARE_INT_HANDLER     default_intexc_handler          /* 29: Interrupt 29 */
102    DECLARE_INT_HANDLER     default_intexc_handler          /* 30: Interrupt 30 */
103    DECLARE_INT_HANDLER     default_intexc_handler          /* 31: Interrupt 31 */
104
105    DECLARE_INT_HANDLER     default_intexc_handler          /* 32: Interrupt 32 */
106    DECLARE_INT_HANDLER     default_intexc_handler          /* 33: Interrupt 33 */
107    DECLARE_INT_HANDLER     default_intexc_handler          /* 34: Interrupt 34 */
108    DECLARE_INT_HANDLER     default_intexc_handler          /* 35: Interrupt 35 */
109
110    DECLARE_INT_HANDLER     default_intexc_handler          /* 36: Interrupt 36 */
111    DECLARE_INT_HANDLER     default_intexc_handler          /* 37: Interrupt 37 */
112    DECLARE_INT_HANDLER     default_intexc_handler          /* 38: Interrupt 38 */
113    DECLARE_INT_HANDLER     default_intexc_handler          /* 39: Interrupt 39 */
114
115    DECLARE_INT_HANDLER     default_intexc_handler          /* 40: Interrupt 40 */
116    DECLARE_INT_HANDLER     default_intexc_handler          /* 41: Interrupt 41 */
117    DECLARE_INT_HANDLER     default_intexc_handler          /* 42: Interrupt 42 */
118    DECLARE_INT_HANDLER     default_intexc_handler          /* 43: Interrupt 43 */
119
120    DECLARE_INT_HANDLER     default_intexc_handler          /* 44: Interrupt 44 */
121    DECLARE_INT_HANDLER     default_intexc_handler          /* 45: Interrupt 45 */
122    DECLARE_INT_HANDLER     default_intexc_handler          /* 46: Interrupt 46 */
123    DECLARE_INT_HANDLER     default_intexc_handler          /* 47: Interrupt 47 */
124
125    DECLARE_INT_HANDLER     default_intexc_handler          /* 48: Interrupt 48 */
126    DECLARE_INT_HANDLER     default_intexc_handler          /* 49: Interrupt 49 */
127    DECLARE_INT_HANDLER     default_intexc_handler          /* 50: Interrupt 50 */
128    DECLARE_INT_HANDLER     eclic_uart0_int_handler         /* 51: Interrupt 51 */
129
130    DECLARE_INT_HANDLER     default_intexc_handler          /* 52: Interrupt 52 */
131    DECLARE_INT_HANDLER     default_intexc_handler          /* 53: Interrupt 53 */
132    DECLARE_INT_HANDLER     default_intexc_handler          /* 54: Interrupt 54 */
133    DECLARE_INT_HANDLER     default_intexc_handler          /* 55: Interrupt 55 */
134
135    DECLARE_INT_HANDLER     default_intexc_handler          /* 56: Interrupt 56 */
136    DECLARE_INT_HANDLER     default_intexc_handler          /* 57: Interrupt 57 */
137    DECLARE_INT_HANDLER     default_intexc_handler          /* 58: Interrupt 58 */
138    DECLARE_INT_HANDLER     default_intexc_handler          /* 59: Interrupt 59 */
139
140    DECLARE_INT_HANDLER     default_intexc_handler          /* 60: Interrupt 60 */
141    DECLARE_INT_HANDLER     default_intexc_handler          /* 61: Interrupt 61 */
142    DECLARE_INT_HANDLER     default_intexc_handler          /* 62: Interrupt 62 */
143    DECLARE_INT_HANDLER     default_intexc_handler          /* 63: Interrupt 63 */
144
145    .option pop
146
147    .size vector_base, . - vector_base
148#endif
149
150    .section .text.init
151    .globl _start
152    .type _start, @function
153
154/**
155 * Reset Handler called on controller reset
156 */
157_start:
158    /* ===== Startup Stage 1 ===== */
159    /* Disable Global Interrupt */
160    csrc CSR_MSTATUS, MSTATUS_MIE
161
162    /* If SMP_CPU_CNT is not defined,
163     * assume that only 1 core is allowed to run,
164     * the core hartid is defined via BOOT_HARTID.
165     * other harts if run to here, just do wfi in __amp_wait
166     */
167#ifndef SMP_CPU_CNT
168    /* take bit 0-7 for hart id in a local cluster */
169    csrr a0, CSR_MHARTID
170    andi a0, a0, 0xFF
171    /* BOOT_HARTID is configurable in Makefile via BOOT_HARTID variable */
172    li a1, BOOT_HARTID
173    bne a0, a1, __amp_wait
174#endif
175
176    /* Initialize GP and TP and jump table base when zcmt enabled */
177    .option push
178    .option norelax
179    la gp, __global_pointer$
180    la tp, __tls_base
181#if defined(__riscv_zcmt)
182    la t0, __jvt_base$
183    csrw CSR_JVT, t0
184#endif
185    .option pop
186
187/* TODO if don't have SMP, you can remove the SMP_CPU_CNT related code */
188#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
189    /* Set correct sp for each cpu
190     * each stack size is __STACK_SIZE
191     * defined in linker script */
192    lui t0, %hi(__STACK_SIZE)
193    addi t0, t0, %lo(__STACK_SIZE)
194    la sp, _sp
195    csrr a0, CSR_MHARTID
196    andi a0, a0, 0xFF
197    li a1, 0
1981:
199    beq a0, a1, 2f
200    sub sp, sp, t0
201    addi a1, a1, 1
202    j 1b
2032:
204#else
205    /* Set correct sp for current cpu */
206    la sp, _sp
207#endif
208
209    /*
210     * Set the the NMI base mnvec to share
211     * with mtvec by setting CSR_MMISC_CTL
212     * bit 9 NMI_CAUSE_FFF to 1
213     */
214    li t0, MMISC_CTL_NMI_CAUSE_FFF
215    csrs CSR_MMISC_CTL, t0
216
217    /*
218     * Enable Zc feature when compiled zcmp & zcmt
219     */
220    li t0, MMISC_CTL_ZC
221#if defined(__riscv_zcmp) || defined(__riscv_zcmt)
222    csrs CSR_MMISC_CTL, t0
223#else
224    csrc CSR_MMISC_CTL, t0
225#endif
226
227    /*
228     * Set Exception Entry MTVEC to early_exc_entry
229     * Due to settings above, Exception and NMI
230     * will share common entry.
231     * This early_exc_entry is only used during early
232     * boot stage before main
233     * Set default interrupt mode to CLINT interrupt mode
234     */
235    la t0, early_exc_entry
236    csrw CSR_MTVEC, t0
237
238    /* ===== Startup Stage 2 ===== */
239
240    /* Enable FPU and Vector Unit if f/d/v exist in march */
241#if defined(__riscv_flen) && __riscv_flen > 0
242    /* Enable FPU, and set state to initial */
243    li t0, MSTATUS_FS
244    csrc mstatus, t0
245    li t0, MSTATUS_FS_INITIAL
246    csrs mstatus, t0
247#endif
248
249#if defined(__riscv_vector)
250    /* Enable Vector, and set state to initial */
251    li t0, MSTATUS_VS
252    csrc mstatus, t0
253    li t0, MSTATUS_VS_INITIAL
254    csrs mstatus, t0
255#endif
256
257    /* TODO: Enable I/D Cache if present determined by cpufeature.h */
258    /* This should be only used by nuclei_gen which generate a correct cpufeature.h */
259    /* We use CPU_ISA macro to determine whether this cpufeature.h is generated or hand written */
260    /* This is used to speedup data loading */
261#ifdef CPU_ISA
262#ifndef CFG_HAS_ECC
263    /* Only enable i/dcache when ecc not present */
264#ifdef CFG_HAS_ICACHE
265    csrsi CSR_MCACHE_CTL, MCACHE_CTL_IC_EN
266#endif
267#ifdef CFG_HAS_DCACHE
268    li t0, MCACHE_CTL_DC_EN
269    csrs CSR_MCACHE_CTL, t0
270#endif
271#endif
272#endif
273
274    /* Enable mcycle and minstret counter */
275    csrci CSR_MCOUNTINHIBIT, 0x5
276
277#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
278    csrr a0, CSR_MHARTID
279    li a1, BOOT_HARTID
280    bne a0, a1, __skip_init
281#endif
282
283    .size _start, . - _start
284
285.type __init_common, @function
286__init_common:
287    /* ===== Startup Stage 3 ===== */
288    /*
289     * Load text section from CODE ROM to CODE RAM
290     * when text LMA is different with VMA
291     */
292    la a0, _text_lma
293    la a1, _text
294    /* If text LMA and VMA are equal
295     * then no need to copy text section */
296    beq a0, a1, 2f
297    la a2, _etext
298    bgeu a1, a2, 2f
299
3001:
301    /* Load code section if necessary */
302    lw t0, (a0)
303    sw t0, (a1)
304    addi a0, a0, 4
305    addi a1, a1, 4
306    bltu a1, a2, 1b
3072:
308    /* Load data section */
309    la a0, _data_lma
310    la a1, _data
311    /* If data vma=lma, no need to copy */
312    beq a0, a1, 2f
313    la a2, _edata
314    bgeu a1, a2, 2f
3151:
316    lw t0, (a0)
317    sw t0, (a1)
318    addi a0, a0, 4
319    addi a1, a1, 4
320    bltu a1, a2, 1b
3212:
322    /* Clear bss section */
323    la a0, __bss_start
324    la a1, _end
325    bgeu a0, a1, 2f
3261:
327    sw zero, (a0)
328    addi a0, a0, 4
329    bltu a0, a1, 1b
3302:
331
332    .size __init_common, . - __init_common
333
334.globl _start_premain
335.type _start_premain, @function
336_start_premain:
337    /*
338     * Call vendor defined SystemInit to
339     * initialize the micro-controller system
340     * SystemInit will just be called by boot cpu
341     */
342    call SystemInit
343
344    /*
345     * Call C/C++ constructor start up code,
346     * __libc_fini is defined in linker script,
347     * so register_fini function will be called
348     * and will run atexit (__libc_fini_array)
349     * to do previous call atexit function
350     */
351    call __libc_init_array
352
353    .size _start_premain, . - _start_premain
354
355.type __skip_init, @function
356__skip_init:
357    /* Sync all harts at this function */
358    call __sync_harts
359
360    /* do pre-init steps before main */
361    /* _premain_init will be called by each cpu
362     * please make sure the implementation of __premain_int
363     * considered this
364     * it will update mtvec according to eclic present or not
365     * mtvec will set to exc_entry
366     */
367    call _premain_init
368
369    /* BPU cold bringup need time, so enable BPU before enter to main */
370    li t0, MMISC_CTL_BPU
371    csrs CSR_MMISC_CTL, t0
372
373    // Interrupt is still disabled here
374    /* ===== Call SMP Main Function  ===== */
375    /* argc = argv = 0 */
376    li a0, 0
377    li a1, 0
378#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
379    /* The weak implementation of smp_main is in this file */
380    call smp_main
381#else
382#ifdef RTOS_RTTHREAD
383    // Call entry function when using RT-Thread
384#ifdef SMODE_RTOS
385    call main_entry
386#else
387    call entry
388#endif
389#else
390    call main
391#endif
392#endif
393    /* do post-main steps after main
394     * this function will be called by each cpu */
395    call _postmain_fini
396
397    .size __skip_init, . - __skip_init
398
399.type __amp_wait, @function
400__amp_wait:
4011:
402    wfi
403    j 1b
404
405    .size __amp_wait, . - __amp_wait
406
407#if defined(SMP_CPU_CNT) && (SMP_CPU_CNT > 1)
408/*
409 * You can re-implement smp_main function in your code
410 * to do smp boot process and handle multi harts
411 */
412.weak smp_main
413.type smp_main, @function
414smp_main:
415    addi sp, sp, -2*REGBYTES
416    STORE ra, 0*REGBYTES(sp)
417    /* only boot hart goto main, other harts do wfi */
418    csrr t0, CSR_MHARTID
419    li t1, BOOT_HARTID
420    beq t0, t1, 2f
4211:
422    wfi
423    j 1b
4242:
425#ifdef RTOS_RTTHREAD
426    // Call entry function when using RT-Thread
427#ifdef SMODE_RTOS
428    call main_entry
429#else
430    call entry
431#endif
432#else
433    call main
434#endif
435    LOAD ra, 0*REGBYTES(sp)
436    addi sp, sp, 2*REGBYTES
437    ret
438
439    .size smp_main, . - smp_main
440#endif
441
442/* Early boot exception entry before main */
443.align 6
444.global early_exc_entry
445.type early_exc_entry, @function
446early_exc_entry:
447    wfi
448    j early_exc_entry
449
450    .size early_exc_entry, . - early_exc_entry