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..9da5b78 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Include/gic.h @@ -0,0 +1,68 @@ +/* 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 ); + +/** + * Powers up the GIC Redistributor, Sets up the priority for SGI0, + * sets SGI0 to be a Group 1 interrupt, and enables delivery of Group-1 IRQs to EL1. + */ +void vGIC_SetupSgi0( void ); 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..5d87233 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/boot.S @@ -0,0 +1,218 @@ +/****************************************************************************** +* 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) + /* 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: +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 + /* Allow FP/SIMD at both EL1 and EL0: CPACR_EL1.FPEN[21:20] = 0b11 */ + orr x0, x0, #(0x3 << 20) + msr CPACR_EL1, x0 /* Enable FP/SIMD access at EL1 and EL0 */ + 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 + + /* Configure SCTLR_EL1: + * - Enable data cache (C=1) + * - Allow EL0 to execute WFE/WFI (Set nTWE/nTWI so they don't trap) + */ + mrs x1, SCTLR_EL1 + orr x1, x1, #(1 << 18) /* nTWE = 1 → WFE at EL0 does not trap */ + orr x1, x1, #(1 << 16) /* nTWI = 1 → WFI at EL0 does not trap */ + orr x1, x1, #(1 << 2) /* C = 1 → enable data cache */ + msr SCTLR_EL1, x1 + isb + + /* 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..7171d4f --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/gic.c @@ -0,0 +1,124 @@ +/* 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" ); + } +} + + +void vGIC_SetupSgi0( void ) +{ + vGIC_PowerUpRedistributor(); + vGIC_SetPriority( portYIELD_CORE_INT_ID, ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); + vGIC_EnableIRQ( portYIELD_CORE_INT_ID ); + vGIC_EnableCPUInterface(); +} 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..1380570 --- /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 FreeRTOS_SWI_Handler + +.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..a136aca --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/BSP/Source/xil-crt0.S @@ -0,0 +1,134 @@ +/****************************************************************************** +* 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 + +.extern _vector_table +.extern ucSecondaryCoresReadyFlags +.extern ucPrimaryCoreInitDoneFlag +.extern _freertos_vector_table + +.set freertos_vector_base, _freertos_vector_table + + .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, secondary_cores_hold +#endif + b jump_to_main + +secondary_cores_hold: + ldr x0, =ucPrimaryCoreInitDoneFlag + ldr w1, [x0] /* Has the primary core set the flag? */ + cmp w1, #1 + b.eq make_secondary_cores_ready /* One? → Secondary cores released */ + wfe /* Sleep until any event */ + b secondary_cores_hold /* Re-test the flag */ + +make_secondary_cores_ready: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 + DSB SY + ISB SY + bl vGIC_SetupSgi0 + ldr x0, =ucSecondaryCoresReadyFlags + mrs x1, MPIDR_EL1 + and x1, x1, #0xFF + sub x1, x1, #1 /* Core 1 is index 0 in the array */ + add x0, x0, x1 + mov w1, #1 + strb w1, [x0] /* Mark this core as ready */ + SVC 106 /* Start the first task on the secondary core */ + +jump_to_main: + ldr X1, = freertos_vector_base + msr VBAR_EL1, X1 /* Set VBAR_EL1 to Freertos vector table */ + DSB SY + ISB SY + mov x0, #0 + mov x1, #0 + bl main + +.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..87d0498 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/README.md @@ -0,0 +1,137 @@ +# 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 fully cache-coherent RAM 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)] + +## Cache Coherency + +This example relies on the `FVP_BaseR_AEMv8R` implementing fully coherent caches. Shared-memory communication (for example, the `ulSharedFlag`) does not require explicit cache clean/invalidate operations; only barrier instructions are used to enforce ordering and visibility. If run on hardware or models without full cache coherency, additional software cache maintenance or non-cacheable mappings would be required for correctness. + +# Implementation + +| Item | Description | +|------|-------------| +| **Shared flag** | `ulSharedFlag` (64-bit) 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** | Only primary Core (i.e., Core 0) jumps to main, creates the user tasks, and calls `vTaskStartScheduler()`. Each secondary core starts and does all the its core specific initialisation and spin in `wfe` until `ucPrimaryCoreInitDoneFlag` is set to `1`, initialize the GIC redistributor and enable SGIs so interrupts from the primary core are receivable, signal the primary core that this secondary core is online and ready by setting the its flag in the `ucSecondaryCoresReadyFlags` array, finally issues an SVC with immediate value `106` to enter `FreeRTOS_SWI_Handler`, which will call `vPortRestoreContext()` based on the SVC number to start scheduling on the secondary core. | +| **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..6ef89d8 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/armclang_linker_script.sct @@ -0,0 +1,59 @@ +#! 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*) + } + + ;-- 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..872e6c2 --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/gnu_linker_script.ld @@ -0,0 +1,83 @@ +/* + * 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*) + . = 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..2a1223a --- /dev/null +++ b/CORTEX_R82_SMP_FVP_GCC_ARMCLANG/main.c @@ -0,0 +1,298 @@ +/* Copyright 2023-2025 Arm Limited and/or its affiliates + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include + +/* 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 SGI0_IRQ ( 0UL ) /* SGI0 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 */ + +volatile uint64_t ulSharedFlag = 0; + +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( 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(); + + /* 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 == SGI0_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