diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt new file mode 100644 index 0000000..be24c21 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +add_library(bsp INTERFACE) + +target_sources(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Source/port_asm_vectors.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/boot.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/xil-crt0.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/gic.c +) + +target_include_directories(bsp + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/Include +) + +target_link_libraries(bsp + INTERFACE + freertos_kernel +) diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h new file mode 100644 index 0000000..11fd676 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h @@ -0,0 +1,62 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#define GICD_BASE ( 0xAF000000UL ) /* Base of GIC Distributor on BaseR FVP */ +#define GICR_BASE_PER_CORE( core ) ( 0xAF100000 + (0x20000 * ( core ) ) ) /* Base of GIC Redistributor per core on BaseR FVP */ +#define SGI_BASE ( 0x10000 ) /* SGI Base */ +#define GICD_CTLR ( 0x000 ) /* Distributor Control Register */ +#define GICR_WAKER ( 0x14 ) /* ReDistributor Wake Register */ +#define GICR_PWRR ( 0x24 ) /* ReDistributor Power Register */ +#define GICR_IGROUPR0 ( SGI_BASE + 0x80 ) /* Interrupt Group Registers */ +#define GICR_ISENABLER0 ( SGI_BASE + 0x100 ) /* Interrupt Set-Enable Registers */ +#define GICR_IPRIORITYR( n ) ( SGI_BASE + ( 0x400 + ( 4 * n ) ) ) /* Interrupt Priority Registers */ +#define GICR_IGRPMODR0 ( SGI_BASE + 0xD00 ) /* Distributor Interrupt group modifier Register */ + +#define GICD_CTLR_ENABLEGRP1NS_BIT ( 1U ) /* GICD_CTRL.EnableGrp1NS bit */ +#define GICD_CTLR_ENABLEGRP1S_BIT ( 2U ) /* GICD_CTRL.EnableGrp1S bit */ +#define GICD_CTLR_ARES_BIT ( 4U ) /* GICD_CTRL.ARE_S bit */ +#define GICD_CTLR_DS_BIT ( 6U ) /* GICD_CTRL.DS bit */ + +#define GICR_PWRR_RDPD_BIT ( 0U ) /* GICR_PWRR.RDPD bit */ + +#define GICR_WAKER_PS_BIT ( 1U ) /* GICR_WAKER.PS bit */ +#define GICR_WAKER_CA_BIT ( 2U ) /* GICR_WAKER.CA bit */ + +#define GIC_MAX_INTERRUPT_ID ( 31UL ) /* Maximum Interrupt ID for PPIs and SGIs */ +#define GIC_WAIT_TIMEOUT ( 1000000U ) /* Timeout for waiting on GIC operations */ + +/** + * Assigns the specified interrupt to Group 1 and enables it + * in the Redistributor for the local core. + */ +void vGIC_EnableIRQ( uint32_t ulInterruptID ); + +/** + * Enables signaling of Group-1 interrupts at EL1 via ICC_IGRPEN1_EL1. + */ +void vGIC_EnableCPUInterface( void ); + +/** + * Initializes the GIC Distributor: + * - Enables Group-1 Non-Secure and Group-1 Secure interrupts + * - Enables Affinity Routing (ARE_S) and Disable Security (DS) bits + */ +void vGIC_InitDist( void ); + +/** + * Powers up and wakes the Redistributor for the current core: + * 1. Clears the Redistributor power-down bit and waits for RDPD=0 + * 2. Clears the Processor-Sleep bit and waits for Children-Asleep=0 + */ +void vGIC_PowerUpRedistributor( void ); + +/** + * Sets the priority of the specified SGI/PPI (INTID 0‑31) in the local + * Redistributor bank via GICR_IPRIORITYR. + * For shared peripheral interrupts (SPI, INTID ≥ 32) use the GICD_IPRIORITYR path. + * + * @param ulInterruptID The ID of the interrupt to set the priority for. + * @param ulPriority The priority value to set. + */ +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ); diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S new file mode 100644 index 0000000..e4aa9a1 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S @@ -0,0 +1,228 @@ +/****************************************************************************** +* Copyright (c) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ +.global secondary_cores_release_flag + + .section .bss +secondary_cores_release_flag: + .skip 8 + +#if defined(__ARMCC_VERSION) + /* Externs needed by the MPU setup code. These are defined in Scatter-Loading + * description file (armclang.sct). */ + .set __el1_stack, Image$$ARM_LIB_STACK$$Base + .set _el1_stack_end, Image$$ARM_LIB_HEAP$$Base +#endif + +#include "FreeRTOSConfig.h" + +.global _prestart +.global _boot + +.global __el1_stack +.global _vector_table + +.set EL1_stack, __el1_stack + +.set EL1_stack_end, _el1_stack_end + +.set vector_base, _vector_table + +/* + * N_CPUS_SHIFT must equal log2(configNUMBER_OF_CORES). It represents the + * number of bits required to index the core that owns a particular slice + * of the shared EL1 stack pool. + * + * To avoid overlapping stack regions, the code assumes + * configNUMBER_OF_CORES is a power‑of‑two. The static check below forces + * a build‑time error if that assumption is broken. + */ +#if ( (configNUMBER_OF_CORES & (configNUMBER_OF_CORES - 1)) != 0 ) + #error "configNUMBER_OF_CORES must be a power‑of‑two" +#endif + +/* Compute log2(configNUMBER_OF_CORES). */ +#if (configNUMBER_OF_CORES == 1) + .set N_CPUS_SHIFT, 0 +#elif (configNUMBER_OF_CORES == 2) + .set N_CPUS_SHIFT, 1 +#elif (configNUMBER_OF_CORES == 4) + .set N_CPUS_SHIFT, 2 +#else + #error "Unsupported configNUMBER_OF_CORES value — must be a power‑of‑two up to 4" +#endif + +.section .boot,"ax" + +_prestart: +_boot: +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cbz x0, start +secondary_cores_hold: + ldr x0, =secondary_cores_release_flag + ldr w1, [x0] /* Has core 0 set the flag? */ + cbnz w1, start /* Non-zero → Secondary cores released */ + wfe /* Sleep until any event */ + b secondary_cores_hold /* Re-test the flag */ +#endif +start: +/* Clear all GP registers (x0–x30) for a known initial state */ + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + mov x18, #0 + mov x19, #0 + mov x20, #0 + mov x21, #0 + mov x22, #0 + mov x23, #0 + mov x24, #0 + mov x25, #0 + mov x26, #0 + mov x27, #0 + mov x28, #0 + mov x29, #0 + mov x30, #0 + + mrs x0, currentEL + cmp x0, #0x4 + beq InitEL1 + + b error /* Check we’ve come from EL1 (currentEL==0x4), otherwise fault */ +InitEL1: + /* Set vector table base address */ + ldr x1, =vector_base + msr VBAR_EL1,x1 + + mrs x0, CPACR_EL1 + orr x0, x0, #(0x1 << 20) + msr CPACR_EL1, x0 /* Enable FP/SIMD access at EL1 */ + isb + + /* Clear FP status flags (FPSR) to avoid spurious exceptions on first use */ + mov x0, 0x0 + msr FPSR, x0 + + /* Define stack pointer for current exception level */ +#if configNUMBER_OF_CORES > 1 + /* Divide the EL1 stack region equally among all cores, then set SP based on MPIDR_EL1[7:0] */ + /* x0 = log2(N_CPUS) is assumed to be a build-time constant */ + mov x0, N_CPUS_SHIFT /* log2(#cores) */ + /* load overall stack limits */ + ldr x2, =EL1_stack /* low address of the shared stack pool */ + ldr x3, =EL1_stack_end /* high address (one past the pool) */ + /* x1 = total size of the pool, x1 >> N_CPUS_SHIFT = size per core */ + sub x1, x3, x2 /* total_stack_size */ + lsr x1, x1, x0 /* slice_size = total/#cores */ + /* x4 = this CPU’s index (Aff0 field of MPIDR_EL1) */ + mrs x4, MPIDR_EL1 + and x4, x4, #0xFF /* core_id ∈ {0 … N_CPUS-1} */ + cmp x4, #configNUMBER_OF_CORES + b.hs error + /* x0 = slice_size * core_id → how far to step back from the top */ + mul x0, x1, x4 + /* sp = top_of_pool – offset (so core 0 gets the very top) */ + sub x3, x3, x0 /* x3 = initial SP for this core */ + bic x3, x3, #0xF /* keep the mandated 16-byte alignment */ + mov sp, x3 +#else + ldr x2, =EL1_stack_end + mov sp, x2 +#endif + + /* Enable ICC system-register interface (SRE=1) and disable FIQ/IRQ bypass (DFB/DIB) */ + mov x0, #0x7 + msr ICC_SRE_EL1, x0 + + /* Invalidate I and D caches */ + ic IALLU + bl invalidate_dcaches + dsb sy + isb + + /* Unmask SError interrupts (clear DAIF.A bit) */ + mrs x1,DAIF + bic x1,x1,#(0x1<<8) + msr DAIF,x1 + + mrs x1, SCTLR_EL1 + orr x1, x1, #(1 << 2) /* Set SCTLR_EL1.C caching enable bit */ + msr SCTLR_EL1, x1 + + /* Branch to C-level startup (zero BSS, init data, etc.) */ + bl _startup + +/* If we ever get here, something went wrong—hang forever */ +error: + b error + +invalidate_dcaches: + + dmb ISH + mrs x0, CLIDR_EL1 /* x0 = CLIDR */ + ubfx w2, w0, #24, #3 /* w2 = CLIDR.LoC */ + cmp w2, #0 /* LoC is 0? */ + b.eq invalidateCaches_end /* No cleaning required */ + mov w1, #0 /* w1 = level iterator */ + +invalidateCaches_flush_level: + add w3, w1, w1, lsl #1 /* w3 = w1 * 3 (right-shift for cache type) */ + lsr w3, w0, w3 /* w3 = w0 >> w3 */ + ubfx w3, w3, #0, #3 /* w3 = cache type of this level */ + cmp w3, #2 /* No cache at this level? */ + b.lt invalidateCaches_next_level + + lsl w4, w1, #1 + msr CSSELR_EL1, x4 /* Select current cache level in CSSELR */ + isb /* ISB required to reflect new CSIDR */ + mrs x4, CCSIDR_EL1 /* w4 = CSIDR */ + + ubfx w3, w4, #0, #3 + add w3, w3, #2 /* w3 = log2(line size) */ + ubfx w5, w4, #13, #15 + ubfx w4, w4, #3, #10 /* w4 = Way number */ + clz w6, w4 /* w6 = 32 - log2(number of ways) */ + +invalidateCaches_flush_set: + mov w8, w4 /* w8 = Way number */ +invalidateCaches_flush_way: + lsl w7, w1, #1 /* Fill level field */ + lsl w9, w5, w3 + orr w7, w7, w9 /* Fill level field */ + lsl w9, w8, w6 + orr w7, w7, w9 /* Fill way field */ + dc CISW, x7 /* Invalidate by set/way to point of coherency */ + subs w8, w8, #1 /* Decrement way */ + b.ge invalidateCaches_flush_way + subs w5, w5, #1 /* Descrement set */ + b.ge invalidateCaches_flush_set + +invalidateCaches_next_level: + add w1, w1, #1 /* Next level */ + cmp w2, w1 + b.gt invalidateCaches_flush_level + +invalidateCaches_end: + ret + +.end diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c new file mode 100644 index 0000000..4514f91 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c @@ -0,0 +1,115 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "gic.h" + +void vGIC_EnableCPUInterface( void ) +{ + /* Enable delivery of Group-1 IRQs to EL1 via ICC_IGRPEN1_EL1 */ + __asm volatile( + "msr ICC_IGRPEN1_EL1, %0\n" + "dsb sy\n" + "isb sy\n" + :: "r"(1UL) + : "memory" + ); +} + +void vGIC_PowerUpRedistributor( void ) +{ + volatile uint32_t *pulPwrr = ( uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_PWRR ); + volatile uint32_t *pulWaker = ( uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_WAKER ); + uint32_t ulTimeout = GIC_WAIT_TIMEOUT; + + /* Clear RDPD (Redistributor Power-Down) to 0 → power on the redistributor */ + *pulPwrr &= ~(1U << GICR_PWRR_RDPD_BIT); + /* Wait until RDPD reads 0 (powered up) */ + while( (*pulPwrr & (1U << GICR_PWRR_RDPD_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_PWRR_RDPD_BIT did not clear in time" ); + return; + } + } + + /* Clear ProcessorSleep to 0 → wake hardware threads */ + *pulWaker &= ~(1U << GICR_WAKER_PS_BIT); + ulTimeout = GIC_WAIT_TIMEOUT; + /* Wait until ChildrenAsleep reads 0 (all subcomponents awake) */ + while( (*pulWaker & (1U << GICR_WAKER_CA_BIT)) != 0U ) + { + if( --ulTimeout == 0U ) + { + configASSERT( "GICR_WAKER_CA_BIT did not clear in time" ); + return; + } + } + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_InitDist( void ) +{ + /* Enable Group-1 Non-Secure (NS) and Secure (S) interrupts, and turn on Affinity-routing (ARE_S) + *plus Disable-Security (DS) for full GICv3 operation + */ + *( volatile uint32_t * )( GICD_BASE + GICD_CTLR ) = ( 1 << GICD_CTLR_ENABLEGRP1NS_BIT ) | + ( 1 << GICD_CTLR_ENABLEGRP1S_BIT ) | + ( 1 << GICD_CTLR_ARES_BIT ) | + ( 1 << GICD_CTLR_DS_BIT ); + + /* Ensure distributor configuration is visible before continuing */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +void vGIC_SetPriority( uint32_t ulInterruptID, uint32_t ulPriority ) +{ + if( ( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) && ( ulPriority <= 0xFF ) ) + { + /* Each GICR_IPRIORITYR holds 4 one‑byte priority fields. */ + uint32_t ulIndex = ulInterruptID / 4U; /* Register number */ + uint32_t ulShift = ( ulInterruptID % 4U ) * 8U; /* Byte lane offset */ + uint32_t ulMask = 0xFFUL << ulShift; /* Field mask */ + + volatile uint32_t * pulPriorityReg = ( volatile uint32_t * ) ( ( uintptr_t ) GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_IPRIORITYR( ulIndex ) ); + uint32_t ulRegValue = *pulPriorityReg; + ulRegValue &= ~( ulMask ); + ulRegValue |= ( ( uint32_t ) ulPriority << ulShift ); + *pulPriorityReg = ulRegValue; + /* Ensure priority write is observed */ + __asm volatile ( "dsb ish" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID or ulPriority passed to vGIC_SetPriority" ); + } +} + +void vGIC_EnableIRQ( uint32_t ulInterruptID ) +{ + if( ulInterruptID <= GIC_MAX_INTERRUPT_ID ) + { + uint32_t ulBitMask = 1U << ulInterruptID; + + /* 1. Assign the interrupt to group 1 */ + *( volatile uint32_t * )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_IGROUPR0 ) |= ( 1U << ulInterruptID ); + + /* 2. Enable the interrupt in GIC */ + *( volatile uint32_t* )( GICR_BASE_PER_CORE( xPortGetCoreID() ) + GICR_ISENABLER0 ) |= ulBitMask; + + /* Ensure interrupt enable is visible */ + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "isb sy" ::: "memory" ); + } + else + { + configASSERT( "Invalid ulInterruptID for vGIC_EnableIRQ: must be < 32" ); + } +} diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S new file mode 100644 index 0000000..e0eb431 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/port_asm_vectors.S @@ -0,0 +1,164 @@ +/****************************************************************************** +* +* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of +* this software and associated documentation files (the "Software"), to deal in +* the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +* the Software, and to permit persons to whom the Software is furnished to do so, +* subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* http://www.FreeRTOS.org +* http://aws.amazon.com/freertos +* +* 1 tab == 4 spaces! +* +******************************************************************************/ + +.org 0 +.text + +/* Entry point symbol for startup; referenced by the linker */ +.globl _boot + +/* Base of the EL1 exception vector table (vbases for EL1t/EL1h) */ +.globl _vector_table + +/* Alternate vector table used once the scheduler is running */ +.globl _freertos_vector_table + +.org 0 + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_vector_table: + +.set VBAR, _vector_table + +.org VBAR + b _boot + +.org (VBAR + 0x80) + b . + +.org (VBAR + 0x100) + b . + +.org (VBAR + 0x180) + b . + + +.org (VBAR + 0x200) + b . + +.org (VBAR + 0x280) + b . + +.org (VBAR + 0x300) + b . + +.org (VBAR + 0x380) + b . + +.org (VBAR + 0x400) + b . + +.org (VBAR + 0x480) + b . + +.org (VBAR + 0x500) + b . + +.org (VBAR + 0x580) + b . + +.org (VBAR + 0x600) + b . + +.org (VBAR + 0x680) + b . + +.org (VBAR + 0x700) + b . + +.org (VBAR + 0x780) + b . + + + +/****************************************************************************** + * Vector table to use when FreeRTOS is running. + *****************************************************************************/ +/* Reserve 0x1000 bytes so we can switch VBAR at runtime without overlap */ +.set FREERTOS_VBAR, (VBAR+0x1000) + +.org(FREERTOS_VBAR) + +/* Map this section to read-only, executable memory as the EL1 vector base */ +.section .vectors, "ax" + +_freertos_vector_table: + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x80) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x100) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x180) + b . + +.org (FREERTOS_VBAR + 0x200) + b FreeRTOS_SWI_Handler + +.org (FREERTOS_VBAR + 0x280) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x300) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x380) + b . + +.org (FREERTOS_VBAR + 0x400) + b . + +.org (FREERTOS_VBAR + 0x480) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x500) + b . + +.org (FREERTOS_VBAR + 0x580) + b . + +.org (FREERTOS_VBAR + 0x600) + b . + +.org (FREERTOS_VBAR + 0x680) + b FreeRTOS_IRQ_Handler + +.org (FREERTOS_VBAR + 0x700) + b . + +.org (FREERTOS_VBAR + 0x780) + b . + +.org (FREERTOS_VBAR + 0x800) + +.end diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S new file mode 100644 index 0000000..0ad6554 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S @@ -0,0 +1,112 @@ +/****************************************************************************** +* Copyright (C) 2014 - 2020 Xilinx, Inc. All rights reserved. +* Copyright 2025 Arm Limited and/or its affiliates +* SPDX-License-Identifier: MIT +******************************************************************************/ + +#if defined(__ARMCC_VERSION) + .set __bss_start__, Image$$ER_BSS$$Base + .set __bss_end__, Image$$ARM_LIB_STACK$$Base + .set __data_start__, Image$$ER_DATA$$Base + .set __data_end__, Image$$ER_DATA$$Limit + .set _sidata, Load$$ER_DATA$$Base +#endif + +#include "FreeRTOSConfig.h" + + .file "xil-crt0.S" + .align 2 +.globl _vector_table +.globl secondary_cores_release_flag + + .text + +/* Store the addresses of .bss start/end so they can be cleared if needed */ +.Lbss_start: + .quad __bss_start__ + +.Lbss_end: + .quad __bss_end__ + +/* + * _startup: early runtime init + * - For CORE0: copy .data, clear .bss + * - For all cores: hand off to C code + */ + .globl _startup +_startup: + +/* Only core0 initializes RAM image; others skip to c_init_end */ +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cmp x0, #0 + bne c_init_end +#endif + + /* Copy .data section from ROM to RAM */ + ldr x0, =_sidata /* Source: ROM load address */ + ldr x1, =__data_start__ /* Destination: RAM */ + ldr x2, =__data_end__ /* End of destination */ + +/* Copy initialised data in 8-byte words for performance/alignment */ +1: cmp x1, x2 + b.ge .Lenclbss /* Done copying */ + ldr x3, [x0], #8 /* Load 64-bit word from ROM */ + str x3, [x1], #8 /* Store to RAM */ + b 1b + +/* Zero `.bss` in 8-byte words; efficient bulk clear of uninitialised data */ +.Lenclbss: + /* clear bss */ + ldr x1,.Lbss_start /* calculate beginning of the BSS */ + ldr x2,.Lbss_end /* calculate end of the BSS */ + +.Lloop_bss: + mov x0, #0 + cmp x1,x2 + bge libc_init /* If no BSS, no clearing required */ + str x0, [x1], #8 + b .Lloop_bss + +libc_init: +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Creates the _reent structure that holds errno, + * the three static FILE objects (stdin, stdout, stderr). + * Runs any functions placed in .preinit_array and calls _init(). + */ + bl __libc_init_array + /* Executes three semihosting SYS_OPEN SWIs to open the host's + * special file :tt three times, obtaining descriptors 0, 1 and 2 + * and writes those into stdin->_file, stdout->_file, stderr->_file. + * It also sets the __SWR (write-enabled) flag in each stream. + */ + bl initialise_monitor_handles +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +c_init_end: +#if configNUMBER_OF_CORES > 1 + /* Get CPU Id */ + mrs x0, MPIDR_EL1 + and x0, x0, #0xFF + cbnz x0, branch_to_main /* Secondary cores jump to main directly */ + ldr x2, =secondary_cores_release_flag + mov w1, #1 + str w1, [x2] /* Set secondary_cores_release_flag to 1 */ + dsb sy /* Make flag visible system-wide */ + sev /* Wake anybody stuck in a WFE */ +#endif + +branch_to_main: + /* No command-line args in this environment – pass (argc=0, argv=NULL) */ + mov x0, #0 + mov x1, #0 + + bl main /* jump to main C code */ + + /* main() should never return: if it does, loop here forever */ + bl exit + +.Lexit: + b .Lexit diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/CMakeLists.txt b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/CMakeLists.txt new file mode 100644 index 0000000..125a5a1 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.15) + +project( + SMP-DEMO + VERSION 0.1 + LANGUAGES C ASM) + +set (CMAKE_BUILD_TYPE Release) + +set(CMAKE_EXECUTABLE_SUFFIX ".axf") + +get_filename_component(FREERTOS_DIR_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../.. REALPATH) +message(DEBUG "FREERTOS_DIR_PATH is ${FREERTOS_DIR_PATH}") + +set(KERNEL_DIR_PATH ${FREERTOS_DIR_PATH}/Source) +message(DEBUG "KERNEL_DIR_PATH is ${KERNEL_DIR_PATH}") + +# Select the native compile PORT +if("${CMAKE_C_COMPILER_ID}" STREQUAL "ARMClang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(FREERTOS_PORT "GCC_ARM_CR82" CACHE STRING "" FORCE) +else() + message(FATAL_ERROR "Unsupported compiler: "${CMAKE_C_COMPILER_ID}"") +endif() + +set(FREERTOS_HEAP "4" CACHE STRING "" FORCE) + +add_library(freertos_config INTERFACE) + +target_include_directories(freertos_config SYSTEM + INTERFACE + config +) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/BSP BSP) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../../../Source freertos_kernel) + +add_executable(cortex_r82_smp_fvp_example) + +target_sources(cortex_r82_smp_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/crt_replacements.c +) + +target_include_directories(cortex_r82_smp_fvp_example + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_options(cortex_r82_smp_fvp_example + PRIVATE + $<$:--map $<$:--list=cortex_r82_smp_fvp_example.map> --scatter=${CMAKE_CURRENT_SOURCE_DIR}/armclang_linker_script.sct> + $<$:-T${CMAKE_CURRENT_SOURCE_DIR}/gnu_linker_script.ld -Wl,--gc-sections,-Map=cortex_r82_smp_fvp_example.map> +) + +target_link_libraries(cortex_r82_smp_fvp_example + PRIVATE + bsp +) diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md new file mode 100644 index 0000000..57fd996 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md @@ -0,0 +1,133 @@ +# Arm Cortex-R82 SMP Example on FVP_BaseR_AEMv8R + +# Introduction + +This example shows how to bring-up **FreeRTOS-SMP** on an **Arm Cortex-R82** multiprocessor system using the **BaseR AEMv8R** Architecture Envelope Model (AEM) Fixed Virtual Platform (FVP). + +Two FreeRTOS tasks, each pinned to a different core, exchange a shared flag and print **Ping / Pong** messages to the console to prove that: + + • Both cores are utilised by the scheduler. + • Inter-core communication through a cache-coherent shared RAM region works. + +The application is intentionally minimal so that it can serve as a starting point for larger SMP applications on Cortex-R82. + +# Prerequisites + +## Downloading and installing AEMv8R Architecture Envelope Model (AEM) Fixed Virtual Platform + +Follow the instructions on the [page](https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms/Arm%20Architecture%20FVPs) to download FVP_Base_AEMv8R based on your operating system. + +Ensure that requirements mentioned in the [page](https://developer.arm.com/documentation/100966/1126/Getting-Started-with-Fixed-Virtual-Platforms/Requirements-for-FVPs?lang=en) are met. + +Then, add the path to `FVP_BaseR_AEMv8R` executable to the environment variable `PATH` (the executable path would be something like `/home//AEMv8R_base_pkg/models/64__GCC-9.3/`). + +Execute the following command to ensure that the FVP was installed successfully +```bash +FVP_BaseR_AEMv8R --version + +Fast Models [11.xx.yy (MMM DD YYYY)] +Copyright 2000-2025 ARM Limited. +All Rights Reserved. +``` + +## Build tools + +* [CMake](https://cmake.org/download/) + * The Arm Cortex-R82 SMP example uses `CMake` as the build system. +* [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) or [Arm Compiler for Embedded](https://developer.arm.com/Tools%20and%20Software/Arm%20Compiler%20for%20Embedded) + * To use Arm Compiler For Embedded, login is required for the download, and you will need a license in order to run the toolchain once installed. + +# Supported toolchains + +The example is supported and tested on the following toolchains: + + * Arm Compiler for Embedded v6.23 (armclang). + * Arm GNU toolchain v14.2. + +# Supported FVPs + +The example is supported and tested on FVP_BaseR_AEMv8R Fast Models [11.28.23 (Feb 17 2025)] + +# Implementation + +| Item | Description | +|------|-------------| +| **Shared flag** | `ulSharedFlag` (64-bit) is placed in a `.shared_ram` section so it is cache-coherent across cores. | +| **Tasks** | `prvTaskCore0` (core 0) prints **Ping**, sets flag to 1; `prvTaskCore1` (core 1) prints **Pong**, sets flag to 0. Each task delays for 1 s (`vTaskDelay( pdMS_TO_TICKS(1000) )`). | +| **Core affinity** | After creation, tasks are pinned via `vTaskCoreAffinitySet()` to ensure deterministic execution. | +| **Scheduler bring-up** | Core 0 creates the tasks and calls `vTaskStartScheduler()`. Secondary cores spin in `wfe` until `uxPortSchedularRunning` becomes `pdTRUE`, then invoke `xPortStartScheduler()`. | +| **Tick timer** | `vConfigureTickInterrupt()` programs `CNTP_EL0` for the FreeRTOS tick and routes `IRQ 30` through the GIC to all cores. | +| **Cache maintenance** | Each write to `ulSharedFlag` is followed by a `DSB SY` to guarantee visibility before the other core wakes. | + +## Building and running examples + +First, run the following command to clone FreeRTOS repository: + +```bash +git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules +``` + +Run the following commands to build the example: + +```bash +cd FreeRTOS/FreeRTOS/Demo/ThirdParty/Partner-Supported-Demos/CORTEX_R82_SMP_FVP_GCC_ARMCLANG +rm -rf build && cmake -B build --toolchain=_toolchain.cmake . && cmake --build build +``` + +### Running the example + +Execute the following script to run the example: +```bash +./run.sh +``` + +### Expected output + +```bash +$ ./run.sh + +Info: FVP_BaseR_AEMv8R: terminal_0: Listening for serial connection on port 5000 + + +Info: FVP_BaseR_AEMv8R: terminal_1: Listening for serial connection on port 5001 + + +Info: FVP_BaseR_AEMv8R: terminal_2: Listening for serial connection on port 5002 + + +Info: FVP_BaseR_AEMv8R: terminal_3: Listening for serial connection on port 5003 + +Ping from Core 0 +Pong from Core 1 +Ping from Core 0 +Pong from Core 1 +Ping from Core 0 +Pong from Core 1 +``` + +## Configuration — Running on up to 4 Cores + +FreeRTOS is built for SMP and the FVP model can start **1 – 4 Cortex-R82 cores**. +To change the core count you must keep the firmware and the model in sync by editing **two** settings: + +| File | Setting to change | Example for 4 cores | +|------|-------------------|---------------------| +| `FreeRTOSConfig.h` | `#define configNUMBER_OF_CORES …` | `#define configNUMBER_OF_CORES 4` | +| `fvp_config.txt` | `cluster0.NUM_CORES=…` | `cluster0.NUM_CORES=4` | + +1. **Edit `FreeRTOSConfig.h`** + Set `configNUMBER_OF_CORES` to the number of logical CPUs you want FreeRTOS to schedule (maximum = 4). + +2. **Edit `fvp_config.txt`** + Set `cluster0.NUM_CORES` to the **same** number so the Arm FVP instantiates the requested CPUs. + +> ⚠️ **Both values must match.** + +3. **Edit `main.c`** + Create new tasks along with their functions as per the number of cores desired (optionally pin the new tasks to the required core). + +Rebuild the example and launch the model as usual. + +## License + +This example is released under the **MIT License**. diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_linker_script.sct b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_linker_script.sct new file mode 100644 index 0000000..25cba98 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_linker_script.sct @@ -0,0 +1,60 @@ +#! armclang --target=aarch64-arm-none-eabi -march=armv8-r -E -x c +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define __ROM_START 0x80000000 +#define __RAM_START 0x00000000 +#define __ROM_SIZE 0x00400000 +#define __RAM_SIZE 0x10000000 +#define __STACK_SIZE 0x00010000 +#define __HEAP_SIZE 0x00020000 + +;=============================================================================== +; LOAD REGION: on-board flash 0x80000000 (4 MB) +;=============================================================================== +LOAD_REGION __ROM_START +{ + ;-- Code + RO data --------------------------------------------------------- + ER_ROM_CODE __ROM_START ALIGN 8 + { + *.o (.vectors +First) + * (+RO) ; All read only + } +} + +LOAD_REGION_1 +0 ALIGN 32 +{ + ;-- Data + Shared Ram between cores ---------------------------------------- + ER_DATA __RAM_START ALIGN 8 + { + *(.data*) + *(.shared_ram*) + } + + ;-- Zero-initialised data -------------------------------------------------- + ER_BSS +0 ALIGN 8 + { + *(.bss*) + } + + ;-- Stack ------------------------------------------------------------------ + ARM_LIB_STACK +0 ALIGN 32 EMPTY (__STACK_SIZE) { + } + + ;-- Heap ------------------------------------------------------------------- + ARM_LIB_HEAP +0 ALIGN 8 EMPTY (__HEAP_SIZE) { + } +} diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake new file mode 100644 index 0000000..1a1513d --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_toolchain.cmake @@ -0,0 +1,22 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_ARCH armv8-r) + +# Use the ARM Compiler 6 front-end +set(CMAKE_C_COMPILER armclang) +set(CMAKE_CXX_COMPILER armclang++) +set(CMAKE_ASM_COMPILER armclang) + +set(CMAKE_C_STANDARD 11) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Common flags for compilation +set(CMAKE_C_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -mlittle-endian -Werror -g -gdwarf-3 -mstrict-align" ) +set(CMAKE_ASM_FLAGS "--target=aarch64-arm-none-eabi -mcpu=cortex-r82 -Werror -g -gdwarf-3 -mstrict-align" ) + +set(CMAKE_EXE_LINKER_FLAGS_INIT "--entry=_boot" ) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h new file mode 100644 index 0000000..7c31f7e --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/config/FreeRTOSConfig.h @@ -0,0 +1,228 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2024-2025 Arm Limited and/or its affiliates + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/****************************************************************************** +* See http://www.freertos.org/a00110.html for an explanation of the +* definitions contained in this file. +******************************************************************************/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- +* Application specific definitions. +* +* These definitions should be adjusted for your particular hardware and +* application requirements. +* +* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE +* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. +* https://www.FreeRTOS.org/a00110.html +*----------------------------------------------------------*/ + +/* Ensure definitions are only used by the compiler, and not by the assembler. */ +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + extern uint32_t SystemCoreClock; + void vAssertCalled( const char * pcFile, unsigned long ulLine ); + #endif +#endif + +/* See https://freertos.org/a00110.html#configPROTECTED_KERNEL_OBJECT_POOL_SIZE for details. */ +#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 150 +/* See https://freertos.org/a00110.html#configSYSTEM_CALL_STACK_SIZE for details. */ +#define configSYSTEM_CALL_STACK_SIZE 128 + +/* Cortex M33 port configuration. */ +#define configENABLE_MPU 0 +#define configENABLE_FPU 1 +#define configUSE_TASK_FPU_SUPPORT 2 +#define configENABLE_TRUSTZONE 0 +#define configENABLE_MVE 0 + +/* Run FreeRTOS on the secure side and never jump to the non-secure side. */ +#define configRUN_FREERTOS_SECURE_ONLY 0 + +/* Constants related to the behaviour or the scheduler. */ +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 1 +#define configMAX_PRIORITIES ( 10 ) +#define configIDLE_SHOULD_YIELD 1 +#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_64_BITS + +/* Constants that describe the hardware and memory usage. */ +#define configCPU_CLOCK_HZ SystemCoreClock +#define configMINIMAL_STACK_SIZE ( ( uint16_t ) 512 ) +#define configMAX_TASK_NAME_LEN ( 12 ) +#define configTOTAL_HEAP_SIZE ( 0x20000 ) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Constants that build features in or out. */ +#define configUSE_MUTEXES 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configNUM_TX_DESCRIPTORS 15 +#define configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN 2 + +/* Constants that define which hook (callback) functions should be used. */ +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 1 +#define configUSE_MALLOC_FAILED_HOOK 1 + +/* Constants provided for debugging and optimisation assistance. */ +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ ); +#define configQUEUE_REGISTRY_SIZE 20 + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 20 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +/* Set the following definitions to 1 to include the API function, or zero + * to exclude the API function. NOTE: Setting an INCLUDE_ parameter to 0 is + * only necessary if the linker does not automatically remove functions that are + * not referenced anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_uxTaskGetStackHighWaterMark2 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskAbortDelay 1 + +/* This demo makes use of one or more example stats formatting functions. These + * format the raw data provided by the uxTaskGetSystemState() function in to + * human readable ASCII form. See the notes in the implementation of vTaskList() + * within FreeRTOS/Source/tasks.c for limitations. */ +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 + +/* Dimensions a buffer that can be used by the FreeRTOS+CLI command interpreter. + * See the FreeRTOS+CLI documentation for more information: + * https://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/ */ +#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048 + +/* Interrupt priority configuration follows...................... */ + +/* Interrupt priorities used by the kernel port layer itself. These are generic +* to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configUNIQUE_INTERRUPT_PRIORITIES - 1 ) + +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! + * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configMAX_API_CALL_INTERRUPT_PRIORITY ) + +/* Constants related to the generation of run time stats. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() 0 + +/* Adjust configTICK_RATE_HZ and pdMS_TO_TICKS to simulate a tick per ms on a fast model */ +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( xTimeInMs * 10 ) ) + +/* Enable dynamic allocation. */ +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + +/* Hardware specific configurations. */ +#define configFPU_D32 0 +#define configUNIQUE_INTERRUPT_PRIORITIES 32 +#define configINTERRUPT_CONTROLLER_BASE_ADDRESS 0xAF000000UL +#define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET 0x1000 + +#define configNUMBER_OF_CORES 2 + +/* SMP specific configurations. */ +#if ( configNUMBER_OF_CORES > 1 ) + #define configUSE_CORE_AFFINITY 1 + #define configRUN_MULTIPLE_PRIORITIES 1 + #define configUSE_PASSIVE_IDLE_HOOK 0 + #define configTIMER_SERVICE_TASK_CORE_AFFINITY 0 +#endif + +#if !defined(__ASSEMBLER__) + #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + /* + * The application must provide a function that configures a peripheral to + * create the FreeRTOS tick interrupt, then define configSETUP_TICK_INTERRUPT() + * in FreeRTOSConfig.h to call the function. FreeRTOS_Tick_Handler() must + * be installed as the peripheral's interrupt handler. + */ + void vConfigureTickInterrupt( void ); + #define configSETUP_TICK_INTERRUPT() vConfigureTickInterrupt() + #endif +#endif + +/* + * Interrupts that are assigned a priority at or below + * configMAX_API_CALL_INTERRUPT_PRIORITY (which counter-intuitively in the ARM + * generic interrupt controller [GIC] means a priority that has a numerical + * value above configMAX_API_CALL_INTERRUPT_PRIORITY) can call FreeRTOS safe API + * functions and will nest. + * + * Interrupts that are assigned a priority above + * configMAX_API_CALL_INTERRUPT_PRIORITY (which in the GIC means a numerical + * value below configMAX_API_CALL_INTERRUPT_PRIORITY) cannot call any FreeRTOS + * API functions, will nest, and will not be masked by FreeRTOS critical + * sections (although it is necessary for interrupts to be globally disabled + * extremely briefly as the interrupt mask is updated in the GIC). + * + * FreeRTOS functions that can be called from an interrupt are those that end in + * "FromISR". FreeRTOS maintains a separate interrupt safe API to enable + * interrupt entry to be shorter, faster, simpler and smaller. + * + * For the purpose of setting configMAX_API_CALL_INTERRUPT_PRIORITY 255 + * represents the lowest priority. + */ +#define configMAX_API_CALL_INTERRUPT_PRIORITY 18ULL + +#ifndef __ASSEMBLER__ + void vClearTickInterrupt( void ); + #define configCLEAR_TICK_INTERRUPT() vClearTickInterrupt() +#endif /* __ASSEMBLER__ */ + +#endif /* FREERTOS_CONFIG_H */ diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/crt_replacements.c b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/crt_replacements.c new file mode 100644 index 0000000..d43aa92 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/crt_replacements.c @@ -0,0 +1,136 @@ +/* Copyright 2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#if defined( __GNUC__ ) && !defined( __clang__ ) + #include + #include +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +int fputc( int character, FILE *pxFile ) +{ + ( void )pxFile; // Unused parameter as required by the C standard + + register uint64_t ulSysWriteCode __asm__( "x0" ) = 0x03; // SYS_WRITEC: semihosting write character command + register const char *pcCharAddress __asm__( "x1" ) = ( const char * )&character; // Address of character to send + __asm volatile ( + "hlt #0xF000\n" // Issue semihosting call using HLT instruction + : "+r"( ulSysWriteCode ) + : "r"( pcCharAddress ) + : "memory" + ); + return character; +} + +int puts( const char *pcString ) +{ + const char *pcChar = pcString; + while ( *pcChar ) { + fputc( *pcChar++, stdout ); + } + fputc( '\n', stdout ); // Append newline to the output string + return 0; // Standard puts() returns non-negative on success +} + +#if defined( __GNUC__ ) && !defined( __clang__ ) + /* Non-re-entrant version provided by RDIMON. */ + extern int _fstat ( int /*fd*/, struct stat * /*buf*/ ); + + /* ------------------------------------------------------------------------- */ + int _fstat_r ( struct _reent *pxReent, int fd, struct stat *pxStatBuffer ) + { + // Create a 16-byte aligned temporary buffer for the stat structure needed by semihosting. + struct stat xAlignedStat __attribute__( ( aligned ( 16 ) ) ); + + int iFstatResult = _fstat( fd, &xAlignedStat ); // Perform semihosting call to get file status + + if ( iFstatResult == 0 ) { // Success: copy stat data safely + memcpy( pxStatBuffer, &xAlignedStat, sizeof( struct stat ) ); + } + else { // Failure: set error code from errno + pxReent->_errno = errno; + } + return iFstatResult; + } + + /* Helper: Returns true if the provided pointer is naturally 8-byte aligned. */ + static inline int prvIsEightByteAligned( const void *pvAddr ) + { + return ( ( ( uintptr_t )pvAddr ) & 0x7U ) == 0U; // Check alignment to an 8-byte boundary + } + + void *memmove( void *pvDestination, const void *pvSource, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + const unsigned char *pucSrc = ( const unsigned char * )pvSource; + + if ( pucDest == pucSrc || xCount == 0 ) + return pvDestination; + + if ( pucDest < pucSrc ) { // -------- forward copy -------- + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + pucDest += 8; + pucSrc += 8; + xCount -= 8; + } + } + while ( xCount-- ) { // Copy remaining bytes forward + *pucDest++ = *pucSrc++; + } + } else { // -------- backward copy -------- + pucDest += xCount; + pucSrc += xCount; + if ( prvIsEightByteAligned( pucDest ) && prvIsEightByteAligned( pucSrc ) ) { + while ( xCount >= 8 ) { + pucDest -= 8; + pucSrc -= 8; + xCount -= 8; + *( uint64_t * )pucDest = *( const uint64_t * )pucSrc; + } + } + while ( xCount-- ) { // Copy remaining bytes in reverse order + *--pucDest = *--pucSrc; + } + } + return pvDestination; + } + + /* memcpy() may assume no overlap, so alias to memmove() */ + void *memcpy( void *pvDestination, const void *pvSource, size_t xCount ) + { + return memmove( pvDestination, pvSource, xCount ); + } + + /* Replacement for memset – linker will rename all calls to this symbol. */ + void *__wrap_memset( void *pvDestination, int value, size_t xCount ) + { + unsigned char *pucDest = ( unsigned char * )pvDestination; + unsigned char ucValue = ( unsigned char )value; + volatile size_t xCountNumber = xCount; + + /* Fast 8-byte stores when destination is naturally aligned. */ + if ( prvIsEightByteAligned( pucDest ) ) + { + uint64_t ullEightBytePattern = 0x0101010101010101ULL * ucValue; // Prepare an 8-byte pattern with all bytes set to ucValue + + while ( xCountNumber >= 8 ) + { + *( uint64_t * )pucDest = ullEightBytePattern; + pucDest += 8; + xCountNumber -= 8; + } + } + + /* Copy any remaining bytes (or if fully unaligned). */ + while ( xCountNumber-- > 0 ) + *pucDest++ = ucValue; + + return pvDestination; + } +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/fvp_config.txt b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/fvp_config.txt new file mode 100644 index 0000000..dc00cce --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/fvp_config.txt @@ -0,0 +1,15 @@ +cluster0.has_aarch64=1 +cluster0.VMSA_supported=0 +cluster0.NUM_CORES=2 +cluster0.gicv3.SRE-enable-action-on-mmap=2 +cluster0.gicv3.cpuintf-mmap-access-level=2 +cluster0.gicv3.extended-interrupt-range-support=1 +cluster0.has_pl2=0 +cluster0.gicv3.SRE-EL2-enable-RAO=1 +gic_distributor.GICD_CTLR-DS-1-means-secure-only=1 +gic_distributor.has-two-security-states=0 +bp.refcounter.non_arch_start_at_default=1 +bp.vis.disable_visualisation=1 +bp.vis.rate_limit-enable=0 +cache_state_modelled=1 +semihosting-enable=1 diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_linker_script.ld b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_linker_script.ld new file mode 100644 index 0000000..435c0d9 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_linker_script.ld @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-2025 Arm Limited. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Memory regions */ +MEMORY +{ + /* ROM is device flash mapped at 0x8000_0000 on FVP; RAM begins at 0x0 */ + ROM (rx) : ORIGIN = 0x80000000, LENGTH = 4M /* Flash memory (ROM) */ + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 256M /* System RAM */ +} + +/* Sections */ +SECTIONS +{ + . = ORIGIN(ROM); /* Place text at ROM base (0x8000_0000) */ + + /* Code section */ + . = ALIGN(8); + .text : ALIGN(8) + { + KEEP(*(.vectors)) /* Vector table */ + *(.text*) /* Code */ + *(.rodata*) /* Read-only data */ + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(8); + } > ROM + + /* Initialized data section */ + .data : + { + __data_start__ = .; + *(.data*) + *(.shared_ram*) + . = ALIGN(8); + __data_end__ = .; + } > RAM AT> ROM /* load in ROM, run in RAM */ + _sidata = LOADADDR(.data); + + /* Uninitialized data (BSS) */ + .bss : + { + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(8); + __bss_end__ = .; + } > RAM + + /* Define the end of allocated memory for `_sbrk()` */ + _end = .; /* Marks the end of static memory */ + PROVIDE(end = .); /* Alias for `_end`, commonly used in `_sbrk()` */ + + /* Stack section */ + .stack (NOLOAD) : ALIGN(16) + { + . += 0x10000; /* 64KB stack */ + } > RAM + + /* Heap section (can be used for dynamic memory allocation) */ + .heap (NOLOAD) : + { + . += 0x20000; /* 128KB heap */ + } > RAM + +} + +/* Provide symbols for startup code */ +PROVIDE(__el1_stack = ADDR(.stack)); +PROVIDE(_el1_stack_end = ADDR(.stack) + SIZEOF(.stack)); diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake new file mode 100644 index 0000000..159df91 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_toolchain.cmake @@ -0,0 +1,21 @@ +# Copyright 2023-2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-r82) + +set(CMAKE_C_COMPILER "aarch64-none-elf-gcc") +set(CMAKE_CXX_COMPILER "aarch64-none-elf-g++") +set(CMAKE_ASM_COMPILER "aarch64-none-elf-gcc") + +set(CMAKE_C_STANDARD 11) + +set(CMAKE_C_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -mlittle-endian -Wall -Wextra -g -gdwarf-3 -mstrict-align") +set(CMAKE_ASM_FLAGS "-mcpu=cortex-r82 -mabi=lp64 -Wall -g -gdwarf-3") + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_EXE_LINKER_FLAGS_INIT + "-specs=rdimon.specs -Wl,-e,_boot,--wrap=memset,--emit-relocs -lc -lrdimon") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}") diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c new file mode 100644 index 0000000..413bc62 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c @@ -0,0 +1,331 @@ +/* Copyright 2023-2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +#if defined( __GNUC__ ) && !defined( __clang__ ) + #include +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "portmacro.h" + +/* GIC includes. */ +#include "gic.h" + +#define GENERIC_TIMER_IRQ ( 30UL ) /* Default IRQ for CNTP_EL0 */ +#define PPI0_IRQ ( 0UL ) /* PPI0 IRQ */ +#define GENERIC_TIMER_IRQ_PRIORITY ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) /* priority for CNTP_EL0 */ +#define GENERIC_TIMER_FREQ ( 24000000 ) /* Frequency for Generic Timer */ +#define TIMER_CTRL_ENABLE ( 1UL << 0 ) /* Timer ENABLE bit */ +#define TIMER_CTRL_IMASK ( 1UL << 1 ) /* Timer IMASK bit */ +#define DELAY_MS ( pdMS_TO_TICKS( 1000 ) ) /* Delay duration in milliseconds */ + +__attribute__((section(".shared_ram"))) +volatile uint64_t ulSharedFlag = 0; +#if defined( __GNUC__ ) && !defined( __clang__ ) + static struct _reent core_reent_array[configNUMBER_OF_CORES]; +#endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + +static uint64_t prvGetTimerClockHz ( void ); +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ); +extern volatile uint8_t ucPortSchedulerRunning; +extern void FreeRTOS_Tick_Handler( void ); +extern void FreeRTOS_SGI_Handler( void ); +static TaskHandle_t prvTaskCore0Handle; +static TaskHandle_t prvTaskCore1Handle; +static SemaphoreHandle_t xSharedFlagMutex = NULL; + +void vAssertCalled( const char * pcFile, + unsigned long ulLine ) +{ + printf( "ASSERT failed! file %s:%lu, \r\n", pcFile, ulLine ); + + taskENTER_CRITICAL(); + { + volatile unsigned long looping = 0; + + /* Use the debugger to set ul to a non-zero value in order to step out + * of this function to determine why it was called. */ + while( looping == 0LU ) + { + portNOP(); + } + } + taskEXIT_CRITICAL(); +} + +static void prvTaskCore0( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + while( 1 ) + { + if ( xSemaphoreTake( xSharedFlagMutex, portMAX_DELAY ) == pdTRUE ) + { + if( ulSharedFlag == 0U ) + { + printf( "Ping from Core %lu\r\n", xPortGetCoreID() ); + ulSharedFlag = 1U; + __asm volatile( "dsb sy"); + } + xSemaphoreGive( xSharedFlagMutex ); + vTaskDelay( DELAY_MS ); + } + } +} + +static void prvTaskCore1( void * arg ) +{ + /* Prevent the compiler warning about the unused parameter. */ + ( void ) arg; + while( 1 ) + { + if ( xSemaphoreTake( xSharedFlagMutex, portMAX_DELAY ) == pdTRUE ) + { + if( ulSharedFlag == 1U ) + { + printf( "Pong from Core %lu\r\n", xPortGetCoreID() ); + ulSharedFlag = 0U; + __asm volatile( "dsb sy"); + } + xSemaphoreGive( xSharedFlagMutex ); + vTaskDelay( DELAY_MS ); + } + } +} + +int main() +{ + if ( xPortGetCoreID() == 0 ) + { + if( xTaskCreate( prvTaskCore0, + NULL, + configMINIMAL_STACK_SIZE, + NULL, + ( tskIDLE_PRIORITY + 1 ), + &prvTaskCore0Handle ) == pdFAIL ) + { + return EXIT_FAILURE; + } + + if( xTaskCreate( prvTaskCore1, + NULL, + configMINIMAL_STACK_SIZE, + NULL, + ( tskIDLE_PRIORITY + 1 ), + &prvTaskCore1Handle ) == pdFAIL ) + { + return EXIT_FAILURE; + } + + vTaskCoreAffinitySet( prvTaskCore0Handle, 1UL << 0 ); /* Pin to Core 0 */ + vTaskCoreAffinitySet( prvTaskCore1Handle, 1UL << 1 ); /* Pin to Core 1 */ + + xSharedFlagMutex = xSemaphoreCreateMutex(); + + if ( xSharedFlagMutex == NULL ) + { + return EXIT_FAILURE; /* Failed to create mutex */ + } + + vTaskStartScheduler(); + } + else + { + while( ucPortSchedulerRunning != pdTRUE ) + { + __asm__ volatile ( "wfe" ); + } + #if defined( __GNUC__ ) && !defined( __clang__ ) + uint32_t ulCoreID = xPortGetCoreID(); /* 1, 2, 3… */ + if ( ulCoreID < configNUMBER_OF_CORES ) + { + struct _reent *pxReent = &core_reent_array[ ulCoreID ]; + _REENT_INIT_PTR( pxReent ); /* zero & set default locale etc. */ + _impure_ptr = pxReent; /* make Newlib use this copy */ + } + else + { + printf( "Invalid core ID: %u\r\n", ulCoreID ); + return EXIT_FAILURE; + } + #endif /* if defined( __GNUC__ ) && !defined( __clang__ ) */ + + xPortStartScheduler(); + } + + /* If all is well, the scheduler will now be running, and the following + * line will never be reached. If the following line does execute, then + * there was insufficient FreeRTOS heap memory available for the idle and/or + * timer tasks to be created. See the memory management section on the + * FreeRTOS web site for more details. NOTE: This demo uses static allocation + * for the idle and timer tasks so this line should never execute. */ + for( ; ; ) + { + } +} + +/** + * Dummy implementation of the callback function vApplicationStackOverflowHook(). + */ +#if ( configCHECK_FOR_STACK_OVERFLOW > 0 ) + void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ) + { + ( void ) xTask; + ( void ) pcTaskName; + + /* Assert when stack overflow is enabled but no application defined function exists */ + configASSERT( 0 ); + } +#endif + +/*---------------------------------------------------------------------------*/ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/* + * vApplicationGetIdleTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + + __WEAK void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + StackType_t * pulIdleTaskStackSize ) + { + /* Idle task control block and stack */ + static StaticTask_t Idle_TCB = { 0 }; + static StackType_t Idle_Stack[ configMINIMAL_STACK_SIZE ] = { 0 }; + + *ppxIdleTaskTCBBuffer = &Idle_TCB; + *ppxIdleTaskStackBuffer = &Idle_Stack[ 0 ]; + *pulIdleTaskStackSize = ( uint32_t ) configMINIMAL_STACK_SIZE; + } + +/* + * vApplicationGetTimerTaskMemory gets called when configSUPPORT_STATIC_ALLOCATION + * equals to 1 and is required for static memory allocation support. + */ + __WEAK void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + StackType_t * pulTimerTaskStackSize ) + { + /* Timer task control block and stack */ + static StaticTask_t Timer_TCB = { 0 }; + static StackType_t Timer_Stack[ configTIMER_TASK_STACK_DEPTH ] = { 0 }; + + *ppxTimerTaskTCBBuffer = &Timer_TCB; + *ppxTimerTaskStackBuffer = &Timer_Stack[ 0 ]; + *pulTimerTaskStackSize = ( uint32_t ) configTIMER_TASK_STACK_DEPTH; + } +#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + +void vApplicationTickHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vApplicationIdleHook( void ) +{ + const TickType_t xKitHitCheckPeriod = pdMS_TO_TICKS( 1000UL ); + static TickType_t xTimeNow, xLastTimeCheck = 0; + + if( ( xTimeNow - xLastTimeCheck ) > xKitHitCheckPeriod ) + { + xLastTimeCheck = xTimeNow; + } + + /* Exit. Just a stub. */ +} + +void vApplicationMallocFailedHook( void ) +{ + /* Provide a stub for this function. */ +} + +void vConfigureTickInterrupt( void ) +{ + uint64_t ulTickInterval; + uint32_t ulControlRegister = 0U; + + __asm volatile ( "dsb sy" ::: "memory" ); + prvSetTimerClockHz( GENERIC_TIMER_FREQ ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + + /* Disable CNTP timer before configuring */ + __asm volatile ( "msr cntp_ctl_el0, xzr" ); + + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer interrupt in the GIC */ + vGIC_InitDist(); + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( GENERIC_TIMER_IRQ, GENERIC_TIMER_IRQ_PRIORITY ); + vGIC_EnableIRQ( GENERIC_TIMER_IRQ ); + vGIC_EnableCPUInterface(); + + /* Enable the timer without masking interrupts */ + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister | TIMER_CTRL_ENABLE ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} + +static uint64_t prvGetTimerClockHz ( void ) +{ + uint64_t ullPhysicalTimerFreq; + __asm volatile ( "mrs %0, cntfrq_el0" : "=r" ( ullPhysicalTimerFreq ) ); + return ullPhysicalTimerFreq; +} + +static void prvSetTimerClockHz ( uint64_t ullPhysicalTimerFreq ) +{ + __asm volatile ( "msr cntfrq_el0, %0" :: "r" ( ullPhysicalTimerFreq ) ); +} + +void vApplicationIRQHandler( uint32_t ulICCIAR ) +{ + /* The ID of the interrupt is obtained by bitwise anding the ICCIAR value + with 0x3FF. */ + uint32_t ulInterruptID = ulICCIAR & 0x3FFUL; + if( ulInterruptID == GENERIC_TIMER_IRQ ) + { + FreeRTOS_Tick_Handler(); + } + else if( ulInterruptID == PPI0_IRQ ) + { + FreeRTOS_SGI_Handler(); + } + else + { + /* Handle other interrupts as needed. */ + printf( "Unhandled interrupt ID: %u\r\n", ulInterruptID ); + } +} + +void vClearTickInterrupt( void ) +{ + /* Disable CNTP timer interrupt before re-configuring */ + uint64_t ulControlRegister = 0U; + ulControlRegister |= TIMER_CTRL_IMASK; + __asm volatile ( "dsb sy" ::: "memory" ); + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + + /* Calculate tick interval based on configured FreeRTOS tick rate */ + uint64_t ulTickInterval = ( prvGetTimerClockHz() / configTICK_RATE_HZ ); + /* Set the timer interval (time until next interrupt) */ + __asm volatile ( "msr cntp_tval_el0, %0" :: "r" ( ulTickInterval ) ); + + /* Enable the timer without masking interrupts */ + ulControlRegister &= ~( TIMER_CTRL_IMASK ); + ulControlRegister |= TIMER_CTRL_ENABLE; + __asm volatile ( "msr cntp_ctl_el0, %0" :: "r" ( ulControlRegister ) ); + __asm volatile ( "isb sy" ::: "memory" ); +} diff --git a/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/run.sh b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/run.sh new file mode 100755 index 0000000..1c6767f --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Copyright 2025 Arm Limited and/or its affiliates +# SPDX-License-Identifier: MIT + +FVP_BaseR_AEMv8R -a build/cortex_r82_smp_fvp_example.axf --config ./fvp_config.txt