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 initializationCall
_premain_init
function to do initialization steps before main functionInitialize 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