1 /******************************************************************************************
2 * Copyright 2019-2021 Microchip FPGA Embedded Systems Solutions.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24 /***************************************************************************
25 * @file system_startup.c
26 * @author Microchip-FPGA Embedded Systems Solutions
27 * @brief first C code called on startup. Will call user code created outside
28 * the HAL.
29 */
30 #include <stddef.h>
31 #include <stdbool.h>
32 #include "mpfs_hal/mss_hal.h"
33 #ifdef MPFS_HAL_HW_CONFIG
34 #include "../common/nwc/mss_nwc_init.h"
35 #include "system_startup_defs.h"
36 #endif
37
38
39 /*==============================================================================
40 * This function is called by the lowest enabled hart (MPFS_HAL_FIRST_HART) in
41 * the configuration file (platform/config/software/mpfs_hal/mss_sw_config.h )
42 *
43 * The other harts up to MPFS_HAL_LAST_HART are placed in wfi at this point.
44 * This function brings them out of wfi in sequence.
45 * If you need to modify this function, create your own one in a user directory
46 * space
47 * e.g. /hart0/e51.c
48 */
main_first_hart(HLS_DATA * hls)49 __attribute__((weak)) int main_first_hart(HLS_DATA* hls)
50 {
51 uint64_t hartid = read_csr(mhartid);
52
53 if(hartid == MPFS_HAL_FIRST_HART)
54 {
55 uint8_t hart_id;
56 ptrdiff_t stack_top;
57
58 /*
59 * We only use code within the conditional compile
60 * #ifdef MPFS_HAL_HW_CONFIG
61 * if this program is used as part of the initial board bring-up
62 * Please comment/uncomment MPFS_HAL_HW_CONFIG define in
63 * platform/config/software/mpfs_hal/sw_config.h
64 * as required.
65 */
66 #ifdef MPFS_HAL_HW_CONFIG
67 config_l2_cache();
68 #endif /* MPFS_HAL_HW_CONFIG */
69
70 init_memory();
71 #ifndef MPFS_HAL_HW_CONFIG
72 hls->my_hart_id = MPFS_HAL_FIRST_HART;
73 #endif
74 #ifdef MPFS_HAL_HW_CONFIG
75 load_virtual_rom();
76 (void)init_bus_error_unit();
77 (void)init_mem_protection_unit();
78 (void)init_pmp((uint8_t)MPFS_HAL_FIRST_HART);
79 (void)mss_set_apb_bus_cr((uint32_t)LIBERO_SETTING_APBBUS_CR);
80 #endif /* MPFS_HAL_HW_CONFIG */
81 /*
82 * Initialise NWC
83 * Clocks
84 * SGMII
85 * DDR
86 * IOMUX
87 */
88 #ifdef MPFS_HAL_HW_CONFIG
89 (void)mss_nwc_init();
90
91 /* main hart init's the PLIC */
92 PLIC_init_on_reset();
93 /*
94 * Start the other harts. They are put in wfi in entry.S
95 * When debugging, harts are released from reset separately,
96 * so we need to make sure hart is in wfi before we try and release.
97 */
98 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h0$);
99 hls = (HLS_DATA*)(stack_top - HLS_DEBUG_AREA_SIZE);
100 hls->in_wfi_indicator = HLS_MAIN_HART_STARTED;
101 hls->my_hart_id = MPFS_HAL_FIRST_HART;
102 WFI_SM sm_check_thread = INIT_THREAD_PR;
103 hart_id = MPFS_HAL_FIRST_HART + 1U;
104 while( hart_id <= MPFS_HAL_LAST_HART)
105 {
106 uint32_t wait_count = 0U;
107
108 switch(sm_check_thread)
109 {
110 default:
111 case INIT_THREAD_PR:
112
113 switch (hart_id)
114 {
115 case 1:
116 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h1$);
117 break;
118 case 2:
119 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h2$);
120 break;
121 case 3:
122 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h3$);
123 break;
124 case 4:
125 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h4$);
126 break;
127 }
128 hls = (HLS_DATA*)(stack_top - HLS_DEBUG_AREA_SIZE);
129 sm_check_thread = CHECK_WFI;
130 wait_count = 0U;
131 break;
132
133 case CHECK_WFI:
134 if( hls->in_wfi_indicator == HLS_OTHER_HART_IN_WFI )
135 {
136 /* Separate state- to add a little delay */
137 sm_check_thread = SEND_WFI;
138 }
139 break;
140
141 case SEND_WFI:
142 hls->my_hart_id = hart_id; /* record hartid locally */
143 raise_soft_interrupt(hart_id);
144 sm_check_thread = CHECK_WAKE;
145 wait_count = 0UL;
146 break;
147
148 case CHECK_WAKE:
149 if( hls->in_wfi_indicator == HLS_OTHER_HART_PASSED_WFI )
150 {
151 sm_check_thread = INIT_THREAD_PR;
152 hart_id++;
153 wait_count = 0UL;
154 }
155 else
156 {
157 wait_count++;
158 if(wait_count > 0x10U)
159 {
160 if( hls->in_wfi_indicator == HLS_OTHER_HART_IN_WFI )
161 {
162 hls->my_hart_id = hart_id; /* record hartid locally */
163 raise_soft_interrupt(hart_id);
164 wait_count = 0UL;
165 }
166 }
167 }
168 break;
169 }
170 }
171 stack_top = (ptrdiff_t)((uint8_t*)&__stack_top_h0$);
172 hls = (HLS_DATA*)(stack_top - HLS_DEBUG_AREA_SIZE);
173 hls->in_wfi_indicator = HLS_MAIN_HART_FIN_INIT;
174
175 /*
176 * Turn on fic interfaces by default. Drivers will turn on/off other MSS
177 * peripherals as required.
178 */
179 (void)mss_config_clk_rst(MSS_PERIPH_FIC0, (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
180 (void)mss_config_clk_rst(MSS_PERIPH_FIC1, (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
181 (void)mss_config_clk_rst(MSS_PERIPH_FIC2, (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
182 (void)mss_config_clk_rst(MSS_PERIPH_FIC3, (uint8_t)MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
183
184 #endif /* MPFS_HAL_HW_CONFIG */
185 (void)main_other_hart(hls);
186 }
187
188 /* should never get here */
189 while(true)
190 {
191 static volatile uint64_t counter = 0U;
192 /* Added some code as debugger hangs if in loop doing nothing */
193 counter = counter + 1U;
194 }
195
196 return (0);
197 }
198
199 /*==============================================================================
200 * u54_single_hart startup.
201 * This is called when a bootloader has loaded a single hart program.
202 * A pointer to the hart Local Storage (HLS) is passed here which has been
203 * passed by the boot program in the a1 register.
204 * The HLS contains the hartID.
205 * It also contains a pointer to shared memory
206 * Information on the HLS used in the bare metal examples.
207 * The HLS is a small amount of memory dedicated to each hart.
208 * The HLS also contains a pointer to shared memory.
209 * The shared memory is accessible by all harts if used. It is
210 * allocated by the boot-loader if the MPFS_HAL_SHARED_MEM_ENABLED
211 * is defined in the mss_sw_config.h file project configuration file.
212 * Please see the project mpfs-hal-run-from-ddr-u54-1 located in the Bare Metal
213 * library under examples/mpfs-hal for an example of it use.
214 *
215 * https://github.com/polarfire-soc/polarfire-soc-bare-metal-library
216 *
217 * If you need to modify this function, create your own one in a user directory
218 * space
219 * e.g. /hart1/x.c
220 */
u54_single_hart(HLS_DATA * hls)221 __attribute__((weak)) int u54_single_hart(HLS_DATA* hls)
222 {
223 init_memory();
224 hls->my_hart_id = MPFS_HAL_FIRST_HART;
225 #ifdef MPFS_HAL_SHARED_MEM_ENABLED
226 /* if shared memory enabled, pointer from Boot-loader in the HLS should be
227 * non-zero */
228 ASSERT(hls->shared_mem != NULL);
229 #endif
230 switch(hls->my_hart_id)
231 {
232 case 0U:
233 e51();
234 break;
235
236 case 1U:
237 u54_1();
238 break;
239
240 case 2U:
241 u54_2();
242 break;
243
244 case 3U:
245 u54_3();
246 break;
247
248 case 4U:
249 u54_4();
250 break;
251
252 default:
253 /* no more harts */
254 break;
255 }
256
257 /* should never get here */
258 while(true)
259 {
260 static volatile uint64_t counter = 0U;
261 /* Added some code as debugger hangs if in loop doing nothing */
262 counter = counter + 1U;
263 }
264
265 return (0);
266 }
267
268 /*==============================================================================
269 * U54s startup.
270 * This is called from entry.S
271 * If you need to modify this function, create your own one in a user directory
272 * space.
273 *
274 * Please note: harts MPFS_HAL_FIRST_FIRST + 1 to MPFS_HAL_FIRST_LAST will wait
275 * in startup code in entry.S as they run the wfi (wait for interrupt)
276 * instruction.
277 * They are woken up as required by the the MPFS_HAL_FIRST_HART, in the function
278 * main_first_hart().
279 * ( It triggers a software interrupt on the particular hart to be woken up )
280 */
main_other_hart(HLS_DATA * hls)281 __attribute__((weak)) int main_other_hart(HLS_DATA* hls)
282 {
283 #ifdef MPFS_HAL_HW_CONFIG
284 extern char __app_stack_top_h0;
285 extern char __app_stack_top_h1;
286 extern char __app_stack_top_h2;
287 extern char __app_stack_top_h3;
288 extern char __app_stack_top_h4;
289
290 const uint64_t app_stack_top_h0 = (const uint64_t)&__app_stack_top_h0 - (HLS_DEBUG_AREA_SIZE);
291 const uint64_t app_stack_top_h1 = (const uint64_t)&__app_stack_top_h1 - (HLS_DEBUG_AREA_SIZE);
292 const uint64_t app_stack_top_h2 = (const uint64_t)&__app_stack_top_h2 - (HLS_DEBUG_AREA_SIZE);
293 const uint64_t app_stack_top_h3 = (const uint64_t)&__app_stack_top_h3 - (HLS_DEBUG_AREA_SIZE);
294 const uint64_t app_stack_top_h4 = (const uint64_t)&__app_stack_top_h4 - (HLS_DEBUG_AREA_SIZE);
295
296 #ifdef MPFS_HAL_HW_CONFIG
297 #ifdef MPFS_HAL_SHARED_MEM_ENABLED
298 /*
299 * If we are a boot-loader, and shared memory enabled (MPFS_HAL_SHARED_MEM_ENABLED)
300 * sets the pointer in each harts HLS to the shared memory.
301 * This allows access to this shared memory across all harts.
302 */
303 const uint64_t app_hart_common_start = (const uint64_t)&__app_hart_common_start;
304 hls->shared_mem = (uint64_t *)app_hart_common_start;
305 hls->shared_mem_marker = SHARED_MEM_INITALISED_MARKER;
306 hls->shared_mem_status = SHARED_MEM_DEFAULT_STATUS;
307 #endif
308 #else
309 #ifdef MPFS_HAL_SHARED_MEM_ENABLED
310 /* make sure each harts Harts Local Storage has pointer to common memory if enabled */
311 /* store the value here received from boot-loader */
312 /* a1 will contain pointer to the start of shared memory */
313 //hls->shared_mem = (uint64_t *)__uninit_bottom$;
314 #else
315 /*
316 * We are not using shared memory
317 */
318 hls->shared_mem = (uint64_t *)NULL;
319 #endif
320 #endif
321
322 volatile uint64_t dummy;
323
324 switch(hls->my_hart_id)
325 {
326
327 case 0U:
328 __asm volatile ("add sp, x0, %1" : "=r"(dummy) : "r"(app_stack_top_h0));
329 e51();
330 break;
331
332 case 1U:
333 (void)init_pmp((uint8_t)1);
334 __asm volatile ("add sp, x0, %1" : "=r"(dummy) : "r"(app_stack_top_h1));
335 u54_1();
336 break;
337
338 case 2U:
339 (void)init_pmp((uint8_t)2);
340 __asm volatile ("add sp, x0, %1" : "=r"(dummy) : "r"(app_stack_top_h2));
341 u54_2();
342 break;
343
344 case 3U:
345 (void)init_pmp((uint8_t)3);
346 __asm volatile ("add sp, x0, %1" : "=r"(dummy) : "r"(app_stack_top_h3));
347 u54_3();
348 break;
349
350 case 4U:
351 (void)init_pmp((uint8_t)4);
352 __asm volatile ("add sp, x0, %1" : "=r"(dummy) : "r"(app_stack_top_h4));
353 u54_4();
354 break;
355
356 default:
357 /* no more harts */
358 break;
359 }
360
361 /* should never get here */
362 while(true)
363 {
364 static volatile uint64_t counter = 0U;
365 /* Added some code as debugger hangs if in loop doing nothing */
366 counter = counter + 1U;
367 }
368 #endif
369 return (0);
370
371 }
372
373 /*==============================================================================
374 * Load the virtual ROM located at address 0x20003120 within the SCB system
375 * registers with an executable allowing to park a hart in an infinite loop.
376 */
377 #ifdef MPFS_HAL_HW_CONFIG
378 #define VIRTUAL_BOOTROM_BASE_ADDR 0x20003120UL
379 #define NB_BOOT_ROM_WORDS 8U
380 const uint32_t rom[NB_BOOT_ROM_WORDS] =
381 {
382 0x00000513U, /* li a0, 0 */
383 0x34451073U, /* csrw mip, a0 */
384 0x10500073U, /* wfi */
385 0xFF5FF06FU, /* j 0x20003120 */
386 0xFF1FF06FU, /* j 0x20003120 */
387 0xFEDFF06FU, /* j 0x20003120 */
388 0xFE9FF06FU, /* j 0x20003120 */
389 0xFE5FF06FU /* j 0x20003120 */
390 };
391
load_virtual_rom(void)392 void load_virtual_rom(void)
393 {
394 volatile uint32_t * p_virtual_bootrom = (uint32_t *)VIRTUAL_BOOTROM_BASE_ADDR;
395 config_copy( (void *)p_virtual_bootrom, (void *)rom,sizeof(rom[NB_BOOT_ROM_WORDS]));
396 }
397 #endif /* MPFS_HAL_HW_CONFIG */
398
399 /*==============================================================================
400 * Put the hart executing this code into an infinite loop executing from the
401 * SCB system register memory space.
402 * This allows preventing a hart from accessing memory regardless of memory
403 * hierarchy configuration or compiler/linker settings.
404 * This function relies on load_virtual_rom() having been called previously to
405 * populate the virtual ROM with a suitable executable.
406 */
park_hart(void)407 static void park_hart(void)
408 {
409 clear_csr(mstatus, MSTATUS_MIE);
410 __asm volatile("fence.i");
411 __asm volatile("li ra,0x20003120");
412 __asm volatile("ret");
413 }
414
415 /*==============================================================================
416 * E51 code executing after system startup.
417 * In absence of an application function of this name with strong linkage, this
418 * function will get linked.
419 * This default implementation is for illustration purpose only. If you need to
420 * modify this function, create your own one in an application directory space.
421 */
e51(void)422 __attribute__((weak)) void e51(void)
423 {
424 /* Put hart in safe infinite WFI loop. */
425 park_hart();
426 }
427
428 /*==============================================================================
429 * First U54.
430 * In absence of an application function of this name with strong linkage, this
431 * function will get linked.
432 * This default implementation is for illustration purpose only. If you need to
433 * modify this function, create your own one in an application directory space.
434 */
u54_1(void)435 __attribute__((weak)) void u54_1(void)
436 {
437 /* Put hart in safe infinite WFI loop. */
438 park_hart();
439 }
440
441
442 /*==============================================================================
443 * Second U54.
444 * In absence of an application function of this name with strong linkage, this
445 * function will get linked.
446 * This default implementation is for illustration purpose only. If you need to
447 * modify this function, create your own one in an application directory space.
448 */
u54_2(void)449 __attribute__((weak)) void u54_2(void)
450 {
451 /* Put hart in safe infinite WFI loop. */
452 park_hart();
453 }
454
455 /*==============================================================================
456 * Third U54.
457 * In absence of an application function of this name with strong linkage, this
458 * function will get linked.
459 * This default implementation is for illustration purpose only. If you need to
460 * modify this function, create your own one in an application directory space.
461 */
u54_3(void)462 __attribute__((weak)) void u54_3(void)
463 {
464 /* Put hart in safe infinite WFI loop. */
465 park_hart();
466 }
467
468 /*==============================================================================
469 * Fourth U54.
470 * In absence of an application function of this name with strong linkage, this
471 * function will get linked.
472 * This default implementation is for illustration purpose only. If you need to
473 * modify this function, create your own one in an application directory space.
474 */
u54_4(void)475 __attribute__((weak)) void u54_4(void)
476 {
477 /* Put hart in safe infinite WFI loop. */
478 park_hart();
479 }
480
481 /*-----------------------------------------------------------------------------
482 * _start() function called invoked
483 * This function is called on power up and warm reset.
484 */
init_memory(void)485 __attribute__((weak)) void init_memory( void)
486 {
487 copy_section(&__text_load, &__text_start, &__text_end);
488 copy_section(&__sdata_load, &__sdata_start, &__sdata_end);
489 copy_section(&__data_load, &__data_start, &__data_end);
490
491 zero_section(&__sbss_start, &__sbss_end);
492 zero_section(&__bss_start, &__bss_end);
493
494 __disable_all_irqs(); /* disables local and global interrupt enable */
495 }
496
497 /*-----------------------------------------------------------------------------
498 * _start() function called invoked
499 * This function is called on power up and warm reset.
500 */
init_ddr(void)501 __attribute__((weak)) void init_ddr(void)
502 {
503 if ((LIBERO_SETTING_DDRPHY_MODE & DDRPHY_MODE_MASK) != DDR_OFF_MODE) {
504 #ifdef DDR_SUPPORT
505 uint64_t end_address;
506
507 #if 0 /* enable to init cache to zero using 64 bit writes */
508 end_address = LIBERO_SETTING_DDR_64_NON_CACHE + LIBERO_SETTING_CFG_AXI_END_ADDRESS_AXI2_0 + LIBERO_SETTING_CFG_AXI_END_ADDRESS_AXI2_1;
509 zero_section((uint64_t *)LIBERO_SETTING_DDR_64_NON_CACHE, (uint64_t *)end_address);
510 #endif
511
512 end_address = LIBERO_SETTING_DDR_64_CACHE + LIBERO_SETTING_CFG_AXI_END_ADDRESS_AXI1_0 + LIBERO_SETTING_CFG_AXI_END_ADDRESS_AXI1_1;
513 zero_section((uint64_t *)LIBERO_SETTING_DDR_64_CACHE, (uint64_t *)end_address);
514 #endif
515 }
516 }
517
518 /**
519 * This function is configured by editing parameters in
520 * mss_sw_config.h as required.
521 * @return
522 */
523
init_bus_error_unit(void)524 __attribute__((weak)) uint8_t init_bus_error_unit(void)
525 {
526 #ifndef SIFIVE_HIFIVE_UNLEASHED
527 uint8_t hart_id;
528 /* Init BEU in all harts - enable local interrupt */
529 for(hart_id = MPFS_HAL_FIRST_HART; hart_id <= MPFS_HAL_LAST_HART; hart_id++)
530 {
531 BEU->regs[hart_id].ENABLE = (uint64_t)BEU_ENABLE;
532 BEU->regs[hart_id].PLIC_INT = (uint64_t)BEU_PLIC_INT;
533 BEU->regs[hart_id].LOCAL_INT = (uint64_t)BEU_LOCAL_INT;
534 BEU->regs[hart_id].CAUSE = 0ULL;
535 BEU->regs[hart_id].ACCRUED = 0ULL;
536 BEU->regs[hart_id].VALUE = 0ULL;
537 }
538 #endif
539 return (0U);
540 }
541
542 /**
543 * init_mem_protection_unit(void)
544 * add this function to you code and configure as required
545 * @return
546 */
init_mem_protection_unit(void)547 __attribute__((weak)) uint8_t init_mem_protection_unit(void)
548 {
549 #ifndef SIFIVE_HIFIVE_UNLEASHED
550 mpu_configure();
551 #endif
552 return (0U);
553 }
554
555 /**
556 * init_pmp(void)
557 * add this function to you code and configure as required
558 * @return
559 */
init_pmp(uint8_t hart_id)560 __attribute__((weak)) uint8_t init_pmp(uint8_t hart_id)
561 {
562 pmp_configure(hart_id);
563 return (0U);
564 }
565
566 /**
567 * set_apb_bus_cr(void)
568 * todo: add check to see if value valid re. mss configurator
569 * @return
570 */
mss_set_apb_bus_cr(uint32_t reg_value)571 __attribute__((weak)) uint8_t mss_set_apb_bus_cr(uint32_t reg_value)
572 {
573 SYSREG->APBBUS_CR = reg_value;
574 return (0U);
575 }
576
577 /**
578 * get_apb_bus_cr(void)
579 * @return
580 */
mss_get_apb_bus_cr(void)581 __attribute__((weak)) uint8_t mss_get_apb_bus_cr(void)
582 {
583 return (SYSREG->APBBUS_CR);
584 }
585
586