Commit 71f61863 authored by Oliver Horst's avatar Oliver Horst
Browse files

[chg] Restructured memguard and perfmon code

- Splitted the code into an  architecture specific and a generic part
- Changed naming and formatting to follow the FreeRTOS code style guides
parent ced48adb
/*******************************************************************************
* Created by Dorel Coman on 23.10.17.
*
* This is the implementation of the Memguard framework for ARM64 on FreeRTOS.
* MemGuard is meant to manage the access to the Main Memory, allowing the user
* to set defined limit to the bandwidth available to each task. The current
* implementation allows to run an instance of FreeRTOS on each core and it
* monitors each task running on the different instances.
*
* Yun, H., Yao, G., Pellizzoni, R., Caccamo, M., & Sha, L. (2016). Memory
* bandwidth management for efficient performance isolation in multi-core
* platforms. IEEE Transactions on Computers, 65(2), 562-576.
* https://doi.org/10.1109/TC.2015.2425889
******************************************************************************/
#ifndef FREERTOS_EXTRA_MEMGUARD_H
#define FREERTOS_EXTRA_MEMGUARD_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* Remove any unused trace macros. */
#ifndef traceMEMGUARD_START
/* */
#define traceMEMGUARD_START()
#endif
#ifndef traceMEMGUARD_STOP
/* */
#define traceMEMGUARD_STOP()
#endif
#ifndef traceMEMGUARD_RECLAIM_TRIAL
/* */
#define traceMEMGUARD_RECLAIM_TRIAL()
#endif
#ifndef traceMEMGUARD_RECLAIM_SUCCESSFULL
/* */
#define traceMEMGUARD_RECLAIM_SUCCESSFULL( x )
#endif
#ifndef traceMEMGUARD_SUSPEND_TASK
/* */
#define traceMEMGUARD_SUSPEND_TASK( x )
#endif
#ifndef traceMEMGUARD_OVERFLOW_HANDLER
/* */
#define traceMEMGUARD_OVERFLOW_HANDLER()
#endif
#ifndef traceMEMGUARD_GLOBAL_BUDGET
/* */
#define traceMEMGUARD_GLOBAL_BUDGET( x )
#endif
#ifndef traceMEMGUARD_TASK_BUDGET
/* */
#define traceMEMGUARD_TASK_BUDGET( a, b )
#endif
/**
* This function sets up the MemGuard framework and it should be called at
* the beginning of the main() function, before any task is created. It
* initializes the variables used by MemGuard and creates the task which will
* set up the interrupts and the timer used by the framework.
*/
void memguard_init();
/**
* This function communicates to MemGuard the creation of a new task and
* informs the framework of which is the bandwidth allowed to the respective
* task. If this function is not called for the created task, the
* latter will not be scheduled (i.e., added to the ready list) and it will
* stay blocked until its creation is notified to memguard which will unlock it.
*
* @param task_handle of the task which was created
*
* @param bandwidth : expressed in MB/s - it is the statically assigned
* bandwidth. More details, about what the statically assigned bandwidth, is
* can be found at the memguard_task_info struct definition inside memguard.c
*
* @oaram prediction_allowed : boolean - if set to pdTRUE allows the
* bandwidth of the task to be dynamically predicted. At the beginning of
* each MemGuard window the bandwidth assigned is recomputed as follows: if
* the task is predicted not to use the full amount of assigned bandwidth,
* the surplus of bandwidth is donated to the global budget. During the time
* window, any task which finishes its bandwidth can reclaim some from the
* global budget. This choice will not guarantee to have always the needed
* bandwidth as the prediction may not be totally correct.
* If the parameter is set to pdFALSE it is guaranteed that the task will have
* always allocated its assigned bandwidth, without donating anything. This
* allows to have a predictable memory bandwidth and should always be set to
* pdFALSE in case of hard real-time tasks.
* For a better description of the mechanism please read the chapter "4.3
* Memory Bandwidth Reclaiming" in the paper linked at the beginning of the
* page.
*/
void vMemguardMonitorTask(TaskHandle_t pxTaskToBind,
uint32_t ulBandwidth,
BaseType_t xPredictionAllowed );
/**
* This function notifies MemGuard that a task was resumed through the
* vTaskSuspend() function. It should not be used anywhere except inside
* vTaskSuspend().
* @param xTask task handle which was suspended
*/
void vMemguardNotifyTaskSuspended( TaskHandle_t pxHandle ) PRIVILEGED_FUNCTION;
/**
* This function notifies MemGuard that a task was resumed through the taks
* management APIs. It should be used only inside the functions vTaskResume()
* and xTaskResumeFromISR().
* @param task_handle of the task which was resumed
*/
void vMemguardNotifyTaskResumed( TaskHandle_t pxHandle ) PRIVILEGED_FUNCTION;
/**
* This function notifies MemGuard that a task was deleted. MemGuard will,
* therefore, free the memory occupied by the related memguard_task_info
* structure. This function should be called only inside vTaskDelete().
*
* @param task_handle of the task which is being deleted.
*/
void vMemguardNotifyTaskDeleted( TaskHandle_t pxHandle ) PRIVILEGED_FUNCTION;
/**
* This function returns pdTRUE if the task was suspended by MemGuard and
* pdFALSE otherwise. It is supposed to be used only inside the functions
* vTaskResume() and xTaskResumeFromISR. It is used in order to know if a
* task can be programmatically resumed or it has to be kept suspended until
* MemGuard starts a new time window.
*
* @param xTask which it is wanted to resume
* @return pdFALSE if it should not be rusmed, pdTRUE if it can be resumed
*/
BaseType_t xMemguardIsTaskBlocked(TaskHandle_t pxHandle ) PRIVILEGED_FUNCTION;
/**
* This function notifies MemGuard that a task has to be reset meaning that its
* assigned budget has to be re-computed has to be recomputed and assigned to
* the task. This function is called for each task when the time window is
* being reset, and it is called only inside the function:
* vTaskResetMemguardWindowForTaskList()
*
* @param task_handle of the task which has to be resetted.
*/
void vMemguardResetWindowForTask(TaskHandle_t task_handle) PRIVILEGED_FUNCTION;
/**
* This function is called during a context switch, before a task is scheduled.
* It restores the value of the BUS_ACCESS event counter, previously saved in
* the variable curr_budget of the struct memguard_task_info. The struct is
* referenced by the TCB of the task with the name memguardTaskInfo.
*/
void vMemguardTaskSwitchIn();
/**
* This function is called during a context switch before a task is descheduled.
* It saves the value of the BUS_ACCESS event counter in a task's variable
* keeping track of the bandwidth usage. The value is saved inside the variable
* curr_budget of the struct memguard_task_info which is referenced by the
* TCB of the task with the name memguardTaskInfo
*/
void vMemguardTaskSwitchOut();
/**
*
* @param task_handle
* @param task_info
*/
void vMemguardSuspendTask( TaskHandle_t pxHandle, void *pvTaskInfo );
/**
* This function is called by the MemGuard timer tick or by a slave in order
* to start a new window. It uses a Software Generated Interrupt for
* communicating to each core to execute their relative
* vMemguardSlaveTickHandler() function.
*
* @param callback_ref not used
*/
void vMemguardMasterPeriodTickHandler( void *callback_ref );
/**
* This function calls the reset_window_routine() which is executed inside a
* task context, the task being the Timer task of FreeRTOS. It executes the
* proper window reset for the respective core. It is called by the master
* core on each slave core.
*
* @param callback_ref not used
*/
void vMemguardSlavePeriodTickHandler( void *callback_ref );
/**
* This functions handles the PMU overflow interrupt and it's called by the
* GIC interrupt when a counter overflows.
*
* The case have to be excuted in their current order, never re-arrange them.
* @param callback_ref not used
*/
void vMemguardOverflowHandler( void *callback_ref );
#endif /* FREERTOS_EXTRA_MEMGUARD_H */
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
add_subdirectory(memguard)
......@@ -5,5 +5,4 @@ target_sources(
#
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/memguard.c"
"${CMAKE_CURRENT_LIST_DIR}/perfmon.c"
)
This diff is collapsed.
/*******************************************************************************
* Created by Dorel Coman on 21.10.17.
*
* This API provides functionalities for accessing the Performance Monitor
* Counters (PMU) registers of the ARM64 architectures
******************************************************************************/
#include "memguard/perfmon.h"
#include "freertos/FreeRTOSConfig.h"
#include <xil/xpseudo_asm.h>
/* Macros used for accessing the register in read and write mode */
#define read_register(REG, VAL) __asm__ __volatile__("mrs %0, "#REG : "=r" (VAL))
#define write_register(REG, VAL) __asm__ __volatile__("msr " #REG ",%0" : : "r" (VAL))
/* Mask used for extracting the CPU_ID from the MPIDR_EL1 register */
#define CPU_ID_MASK (uint32_t) (0xFF)
/* Macros used for accessing and setting up the PMU registers */
#define PMCR_MASK 0x3f
#define PMCR_E (1 << 0) /* Enable all counters */
#define PMCR_P (1 << 1) /* Reset all counters */
#define PMCR_C (1 << 2) /* Cycle counter reset */
#define PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */
#define PMCR_N_SHIFT 11 /* Mask used for getting Number of counters supported */
#define PMCR_N_MASK 0x1f
#define PMU_USERENR_MASK 0xf /* Mask for writable bits */
#define PMUSERENR_EN_EL0 (1 << 0) /* EL0 access enable */
#define PMUSERENR_CR (1 << 2) /* Cycle counter read enable */
#define PMUSERENR_ER (1 << 3) /* Event counter read enable */
#define PMUSERENR_ENABLE_ALL 0xD
/*********************************************************
* Registers of the Performance Monitor Unit (PMU)
*********************************************************/
/* Performance Monitors Event Counter Selection Register */
#define PMSELR_EL0 "PMSELR_EL0"
/* Performance Monitors User Enable Register */
#define PMUSERENR_EL0 "PMUSERENR_EL0"
/* Performance Monitors Count Enable Set register */
#define PMCNTENSET_EL0 "PMCNTENSET_EL0"
/* Performance Monitors Control Register */
#define PMCR_EL0 "PMCR_EL0"
/* Performance Monitors Cycle Count Register - 64-bit register */
#define PMCCNTR_EL0 "PMCCNTR_EL0"
/* Performance Monitors Selected Event Count Register */
#define PMXEVCNTR_EL0 "PMXEVCNTR_EL0"
/* Performance Monitors Selected Event Type Register */
#define PMXEVTYPER_EL0 "PMXEVTYPER_EL0"
/* Performance Monitors Interrupt Enable Clear register */
#define PMINTENCLR_EL1 "PMINTENCLR_EL1"
/* Performance Monitors Interrupt Enable Set register */
#define PMINTENSET_EL1 "PMINTENSET_EL1"
/* Performance Monitors Overflow Flag Status Clear Register */
#define PMOVSCLR_EL0 "PMOVSCLR_EL0"
/* Performance Monitors Overflow Flag Status Set register */
#define PMOVSSET_EL0 "PMOVSSET_EL0"
/* Multiprocessor Affinity Register - used to retrieve the CPU ID */
#define MPIDR_EL1 "MPIDR_EL1"
/*******************************************************************************
* Function definitions
******************************************************************************/
#if(configUSE_PMU_IN_USER_MODE == 1)
/**
* It enables user-mode access (EL0: applications) to counters. It is
* important in order to be able to use the counters in FreeRTOS.
*/
static inline void enable_user_access_to_counters()
{
uint32_t reg_val;
read_register(PMUSERENR_EL0, reg_val);
reg_val = reg_val | PMUSERENR_ENABLE_ALL;
write_register(PMUSERENR_EL0, reg_val);
}
#endif
/**
* This function enables the selected counter in order to make it count. If
* the counter is not enabled, it will not count the occurencies of its
* selected event type
* @idx: The counter n index
*/
static inline void pmu_enable_counter(uint32_t idx)
{
write_register(PMCNTENSET_EL0, (1 << idx));
}
/**
* This function disables the selected counter.
* @idx: The counter n index
*/
static inline void pmu_disable_counter(uint32_t idx)
{
write_register(PMCNTENSET_EL0, (0 << idx));
}
/**
* This function writes the PMCR register of the PMU
* @param val to be written inside the PMCR register
*/
static inline void pmu_control_write(uint32_t val)
{
val &= PMCR_MASK;
isb();
write_register(PMCR_EL0, val);
}
/**
* This function returns the value of the PMCR_EL0 register
* @return PMCR_EL0 value
*/
static inline uint32_t pmu_control_read(void)
{
unsigned int val;
read_register(PMCR_EL0, val);
return val;
}
/**
* This function reads the PMCR_EL0 register and writes it back with the value
* needed in order to activate the PMU counters. The PMCR_E mask enables all
* the counters
*/
static inline void set_pmcr_el0()
{
uint32_t val = pmu_control_read();
pmu_control_write(val | PMCR_E);
}
/**
* This function selects the counter on which it is wanted to executed some
* PMU operations
* @param idx of the counter to be selecte
*/
static inline void pmu_counter_select(uint32_t idx)
{
write_register(PMSELR_EL0, idx);
isb();
}
/**
* This function sets, for the given counter, the type of event to be
* monitored and counted
* @param idx of the counter of which it is being selected the event
* @param type of the event to be monitored. Events that can be monitored are
* contained in the file events.h
*/
static inline void pmu_counter_set_event_type(uint32_t idx, uint32_t type)
{
pmu_counter_select(idx);
write_register(PMXEVTYPER_EL0, type);
}
void pmu_init_counters()
{
#if(configUSE_PMU_IN_USER_MODE == 1)
{
enable_user_access_to_counters();
}
#endif
set_pmcr_el0();
uint32_t number_of_counters = pmu_get_number_counters();
for (uint32_t i = 0; i < number_of_counters; ++i) {
pmu_clear_interrupt(i);
}
}
void pmu_enable_counter_for_event(uint32_t idx, uint32_t event_type)
{
pmu_disable_counter(idx);
pmu_counter_set_event_type(idx, event_type);
pmu_enable_counter(idx);
}
uint32_t pmu_get_core_id()
{
uint32_t value;
read_register(MPIDR_EL1, value);
value = value & CPU_ID_MASK;
return value;
}
uint32_t pmu_counter_has_overflowed(uint32_t idx)
{
uint32_t value;
read_register(PMOVSSET_EL0, value);
return ((value) & (1 << idx));
}
void pmu_enable_intr(uint32_t idx)
{
write_register(PMINTENSET_EL1, (1 << idx));
}
void pmu_disable_intr(uint32_t idx)
{
/* Disabling the interrupt for the selected counter */
write_register(PMINTENCLR_EL1, (1 << idx));
isb();
/* Clearing the interrupt line */
write_register(PMOVSCLR_EL0, (1 << idx));
isb();
}
void pmu_clear_interrupt(uint32_t idx)
{
write_register(PMOVSCLR_EL0, (1 << idx));
isb();
}
void pmu_start_counter(uint32_t idx, uint32_t count_val)
{
/* Writing the value on the selected counter */
pmu_write_counter(idx, count_val);
/* Clear the interrupt register before enabling it again to be sure it
wasn't already triggered */
pmu_clear_interrupt(idx);
pmu_enable_intr(idx);
}
uint32_t mask_value(uint32_t value)
{
uint32_t MAX = 0xFFFFFFFF;
uint32_t val = MAX - value;
return val;
}
uint32_t pmu_read_counter(uint32_t idx)
{
uint32_t value;
if (idx < pmu_get_number_counters()) {
pmu_counter_select(idx);
read_register(PMXEVCNTR_EL0, value);
return value;
} else {
return 0;
}
}
void pmu_write_counter(uint32_t idx, uint32_t value)
{
if (idx < pmu_get_number_counters()) {
pmu_counter_select(idx);
write_register(PMXEVCNTR_EL0, value);
}
}
uint64_t pmu_read_cycle_counter(void)
{
uint64_t value;
read_register(PMCCNTR_EL0, value);
return value;
}
uint32_t pmu_get_number_counters(void)
{
uint32_t count = pmu_control_read();
/* Extracting the bits[15:11] - These bits store the number of counters
available for the respective CPU. The description of the register can be
found in the A53 CPU Technical Reference Manual at the chapter 12.4.1
Performance Monitors Control Register */
count = ((count >> PMCR_N_SHIFT) & PMCR_N_MASK);
return count;
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment