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