//***************************************************************************** // (C) Automotive Lighting Reutlingen GmbH // Tuebinger Strasse 123, 72762 Reutlingen, Germany // // Automotive Lighting Reutlingen GmbH owns all the rights to this work. // This work shall not be copied, reproduced, used, modified, transferred // or its information shall not be disclosed without the prior written // authorization of Automotive Lighting Reutlingen GmbH. //***************************************************************************** //----------------------------------------------------------------------------- /// \file StatM.h /// /// \brief ECU statistic manager, including CPU load, stack usage... /// /// \author f24301c (walker.lv@marelli.com) /// /// \date 05.17.2023 //----------------------------------------------------------------------------- //============================================================================= // Standard / system header file includes //============================================================================= #include "StatM.h" #include "Rte_ctadCddStatM.h" #include "Os.h" #include "Os_StackInt.h" #include #include #include "Mcu_SmImpl.h" #if (STATM_PFLSH_INTEGRITY_CHECK == STD_ON) #include "CfgId/CfgId.h" // pFlash HASH digest #endif // end of STATM_PFLSH_INTEGRITY_CHECK #if (STATM_MEASURE_CPU_LOAD == STD_ON) #include #endif // end of STATM_MEASURE_CPU_LOAD // Project-specific header file includes //============================================================================= //============================================================================= // static variables //============================================================================= //============================================================================= // start definitions of uninitialized array or structure variables //============================================================================= #define ctadCddStatM_START_SEC_VAR_NOINIT_UNSPECIFIED #include "ctadCddStatM_MemMap.h" // -------------- Start variable definitions. // -------------- Stop variable definitions. #define ctadCddStatM_STOP_SEC_VAR_NOINIT_UNSPECIFIED #include "ctadCddStatM_MemMap.h" //============================================================================= // stop definitions of uninitialized array or structure variables //============================================================================= //============================================================================= // start definitions of uninitialized array or structure variables //============================================================================= #define ctadCddStatM_START_SEC_VAR_INIT_UNSPECIFIED #include "ctadCddStatM_MemMap.h" // -------------- Start variable definitions. #if (OS_CFG_STACKMEASUREMENT == STD_ON) static StatM_Statistic_Stack StatM_Stacks[] = { // core0 kernel stack 0 { StatM_Stack_Type_Core, OS_CORE_ID_0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Kernel_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Kernel_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Kernel_Dyn), 0, 0.0f }, // core0 ISR stack 1 { StatM_Stack_Type_ISR, CounterIsr_OsCounter, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Isr_Core_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Isr_Core_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Isr_Core_Dyn), 0, 0.0f }, // task OsTask_Timing_NT0_10ms_CDD 2 { StatM_Stack_Type_TASK, OsTask_Timing_NT0_10ms_CDD, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio0_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio0_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio0_Dyn), 0, 0.0f }, // task OsTask_Timing_NT0_100ms 3 { StatM_Stack_Type_TASK, OsTask_Timing_NT0_100ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio4_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio4_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio4_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_100ms 4 { StatM_Stack_Type_TASK, OsTask_Timing_T0_100ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio5_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio5_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio5_Dyn), 0, 0.0f }, // task OsTask_Timing_NT0_50ms 5 { StatM_Stack_Type_TASK, OsTask_Timing_NT0_50ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio6_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio6_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio6_Dyn), 0, 0.0f }, // task OsTask_Timing_NT0_20ms 6 { StatM_Stack_Type_TASK, OsTask_Timing_NT0_20ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio8_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio8_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio8_Dyn), 0, 0.0f }, // task OsTask_Async_OsCore0 7 { StatM_Stack_Type_TASK, OsTask_Async_OsCore0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio10_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio10_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio10_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_10ms 8 { StatM_Stack_Type_TASK, OsTask_Timing_T0_10ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio13_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio13_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio13_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_5ms 9 { StatM_Stack_Type_TASK, OsTask_Timing_T0_5ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio15_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio15_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio15_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_1ms 10 { StatM_Stack_Type_TASK, OsTask_Timing_T0_1ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio20_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio20_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio20_Dyn), 0, 0.0f }, // task OsTask_InitEvent_NT0 11 { StatM_Stack_Type_TASK, OsTask_InitEvent_NT0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio22_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio22_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio22_Dyn), 0, 0.0f }, // task OsTask_InitEvent_T0 12 { StatM_Stack_Type_TASK, OsTask_InitEvent_T0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio24_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio24_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio24_Dyn), 0, 0.0f }, // task OsTask_DeInitEvent_NT0 13 { StatM_Stack_Type_TASK, OsTask_DeInitEvent_NT0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio29_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio29_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio29_Dyn), 0, 0.0f }, // task OsTask_DeInitEvent_T0 14 { StatM_Stack_Type_TASK, OsTask_DeInitEvent_T0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio30_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio30_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio30_Dyn), 0, 0.0f }, // task Default_Init_Task, Default_Init_Task_Trusted 15 { StatM_Stack_Type_TASK, Default_Init_Task, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Task_Prio50_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsCore0_Task_Prio50_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Task_Prio50_Dyn), 0, 0.0f }, // task OsTask_DRE_T0 16 { StatM_Stack_Type_TASK, OsTask_DRE_T0, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsTask_DRE_T0_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsTask_DRE_T0_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsTask_DRE_T0_Dyn), 0, 0.0f }, // task OsTask_Timing_NT0_10ms 17 { StatM_Stack_Type_TASK, OsTask_Timing_NT0_10ms, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsTask_Timing_NT0_10ms_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsTask_Timing_NT0_10ms_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsTask_Timing_NT0_10ms_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_10ms_BSW 18 { StatM_Stack_Type_TASK, OsTask_Timing_T0_10ms_BSW, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsTask_Timing_T0_10ms_BSW_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsTask_Timing_T0_10ms_BSW_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsTask_Timing_T0_10ms_BSW_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_2ms_BSW 19 { StatM_Stack_Type_TASK, OsTask_Timing_T0_2ms_BSW, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsTask_Timing_T0_2ms_BSW_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsTask_Timing_T0_2ms_BSW_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsTask_Timing_T0_2ms_BSW_Dyn), 0, 0.0f }, // task OsTask_Timing_T0_5ms_BSW 20 { StatM_Stack_Type_TASK, OsTask_Timing_T0_5ms_BSW, (const uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsTask_Timing_T0_5ms_BSW_Dyn), OS_STACK_GETSIZE(OsCfg_Stack_OsTask_Timing_T0_5ms_BSW_Dyn), (const uint32)OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsTask_Timing_T0_5ms_BSW_Dyn), 0, 0.0f }, // end of array 21 { StatM_Stack_Type_INVALID, 0, 0, 0, 0, 0, 0.0f }, }; #endif static StatM_Stat_System_Interal StatMStistics = { // system up time // offset size { 0 }, // +0x00 4 #if (STATM_MEASURE_CPU_LOAD == STD_ON) #define CPULOAD_TEST_DEFAULT_DURATION_MS (1000 * 10) /* default test period: 10s */ // CPU load { 0, // startFlag // +0x04 4 0, // idleCounter // +0x08 4 0, // endTime // +0x0c 4 0, // runningFlag // +0x10 1 0, // padding // +0x11 1 0, // nonIdleLoad // +0x12 2 CPULOAD_TEST_DEFAULT_DURATION_MS, // duration // +0x14 2 0, // padding // +0x16 2 }, #endif #if (OS_CFG_STACKMEASUREMENT == STD_ON) // stack usage { &StatM_Stacks[0] }, #endif #if (STATM_PFLSH_INTEGRITY_CHECK == STD_ON) // StatM_Statistic_Pflash_Intact { 0, // flag of digest availability, default not ready 0, // flag after integrity check, default false HASH_START_ADDR, // begin address of pFlash to be check, see linker script for detail HASH_LENGHT, // size of data to be check, exclude first 256 byte: CfgId_CfgInfoConfigData {0}, // HASH Digest 0 // HASH execution time in [ms] } #endif // end of STATM_PFLSH_INTEGRITY_CHECK }; // System statistic to be export to RTE static StatM_SysStatistic StatM_SysStatis_Exp = { 0, // EcuUptime 0, // CpuLoad 0, // pFlashIntact }; // -------------- Stop variable definitions. #define ctadCddStatM_STOP_SEC_VAR_INIT_UNSPECIFIED #include "ctadCddStatM_MemMap.h" //============================================================================= // stop definitions of initialized array or structure variables //============================================================================= //============================================================================= // Start declaration or definitions of functions //============================================================================= #define ctadCddStatM_START_SEC_CODE #include "ctadCddStatM_MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */ // -------------- Start function declarations or definitions. //============================================================================= // Function prototypes //============================================================================= static void StatM_Init(void); static void StatM_Cycle100ms(void); //============================================================================= // Macros //============================================================================= //============================================================================= // Function definitions //============================================================================= //----------------------------------------------------------------------------- /// \brief initialization runnable /// /// \descr RTE initialization call out /// /// \param void /// /// \return void //----------------------------------------------------------------------------- void Rte_riStatMInit(void) { StatM_Init(); } //----------------------------------------------------------------------------- /// \brief cyclic runnable /// /// \descr deinitialization, call from Os startup before init, nothing to do /// /// \param void /// /// \return void //----------------------------------------------------------------------------- void Rte_rdStatMDeInit(void) { } //----------------------------------------------------------------------------- /// \brief cyclic runnable /// /// \descr RTE cyclic function /// /// \param void /// /// \return void //----------------------------------------------------------------------------- void Rte_rpStatM100ms(void) { StatM_Cycle100ms(); } //----------------------------------------------------------------------------- /// \brief /// /// \descr StatM_Init() /// /// \param none /// /// \return none //----------------------------------------------------------------------------- static void StatM_Init(void) { StatMStistics.systemUptime = 0; uint32 ret = 0xff; ret = Mtu_Sffd_BatchRun(); ret = SMU_RegMonSFFRun(); // NOTENOTE: if using debugger to flash ELF, possible result in PFlash double // bit error, these errors result in SMU alarm[7][23] counters. if flash // a full mapping HEX, there is not DBE observed. // for this reason I change that alarm reaction to INT and failure // threshold to endure PFlash integrity error count StatM_SmuInit(); (void)ret; } //----------------------------------------------------------------------------- /// \brief perform Code integrity check by execute SHA2-256 HASH of code flash /// (portion) define in StatM_Statistic_Pflash_Intact.hashStartAddr and /// hashLength /// /// \descr pFlashIntegityCheck() SHALL only run once per power cycle /// execution time is about 1200ms in idle loop (lowest priority task) /// /// \param none /// /// \return none, StatM_Statistic_Pflash_Intact internal state updated after execution //----------------------------------------------------------------------------- static void pFlashIntegrityCheck(void) { #if (STATM_PFLSH_INTEGRITY_CHECK == STD_ON) // calculate HASH2_256 during one driving cycle StatM_Statistic_Pflash_Intact* hashCfg = &StatMStistics.pFlashIntegrity; if (hashCfg->hashReady == 0) { TIMER startTime = UTILTIME_TIMER_START(0); SHA256_CTX context = { { 0 }, 0, { 0 } }; sha256_Init(&context); sha256_Update(&context, (const uint8*)hashCfg->hashStartAddr, hashCfg->hashLength); sha256_Final(&context, hashCfg->digest); hashCfg->hashReady = 1; hashCfg->duration = UTILTIME_TIMER_GET_TIME_SINCE(startTime); // PFlash integrity check: if (0 == memcmp(hashCfg->digest, (const void*)CfgId_CfgInfoConfigData.CfgId_aucHashValue, sizeof(hashCfg->digest))) { hashCfg->integrityFlag = 1; } else { // kept 0 as initial value } } #endif // end of STATM_PFLSH_INTEGRITY_CHECK } //----------------------------------------------------------------------------- /// \brief profiling statistic of stack, cpu, uptime /// /// \descr StatM_Cycle100ms() /// \param none /// /// \return none //----------------------------------------------------------------------------- static void StatM_Cycle100ms(void) { static uint32 freq_divider = 0; freq_divider++; if ((freq_divider % 10) == 0) // per second { StatMStistics.systemUptime++; // counter of second #if (OS_CFG_STACKMEASUREMENT == STD_ON) // update ONE stack usage PER second static uint8 stackUsageIdx = 0; uint32 result = 0; switch (StatMStistics.stacks[stackUsageIdx].type) { case StatM_Stack_Type_Core: result = Os_GetKernelStackUsage((CoreIdType)StatMStistics.stacks[stackUsageIdx].parameter); break; case StatM_Stack_Type_TASK: result = Os_GetTaskStackUsage((TaskType)StatMStistics.stacks[stackUsageIdx].parameter); break; case StatM_Stack_Type_ISR: result = Os_GetISRStackUsage((ISRType)StatMStistics.stacks[stackUsageIdx].parameter); break; default: // not support break; } // NOTE: possible BUG is type == StatM_Stack_Type_ISR; StatMStistics.stacks[stackUsageIdx].UsedSize = result; StatMStistics.stacks[stackUsageIdx].UsedPercentage = (float)(result * 100) / StatMStistics.stacks[stackUsageIdx].StatckSize; stackUsageIdx++; // rewind if reach to the end of array... if ((StatMStistics.stacks[stackUsageIdx].type == StatM_Stack_Type_INVALID) || (StatMStistics.stacks[stackUsageIdx].StartAddress == 0)) { stackUsageIdx = 0; } #endif #if (SMU_ALARM_TEST == STD_ON) // trigger a SW alarm 0 every second Smu_Trigger_SW_Alarm(SMU_ALARM_0); #endif } } //----------------------------------------------------------------------------- /// \brief start CPU load testing /// /// \descr StatM_RTE_Cpuload_Start() /// /// \param duration: Cpu load measurement time in millisecond[1..65535] /// /// \return Std return type //----------------------------------------------------------------------------- Std_ReturnType StatM_RTE_Cpuload_Start(uint16 duration) { Std_ReturnType ret = E_NOT_CONFIGURED; #if (STATM_MEASURE_CPU_LOAD == STD_ON) StatM_Statistic_CpuLoad* cpuload = &StatMStistics.cpuLoad; if (duration < 1) { ret = E_INVALID_PARAMETER; } else if (cpuload->runningFlag) { ret = E_REJECT; } else { cpuload->duration = duration; cpuload->startFlag = 1; ret = E_OK; // CPU load measure will start from next idle loop } #endif // end of (STATM_MEASURE_CPU_LOAD == STD_ON) return ret; } //----------------------------------------------------------------------------- /// \brief serializes system statistic data to a buffer /// /// \descr C/S port implementation. serializes system statistic data into a /// general byte buffer, caller shall restore data structure from that /// buffer. notice the buffer size requirement /// /// \param buffer: a buffer to fetch system statistic /// bufLen: buffer size. the size has to be at least sizeof type /// StatM_Stat_System /// /// \return Std return type //----------------------------------------------------------------------------- Std_ReturnType StatM_RTE_GetSystemStat(void* buffer, uint32 bufLen) { Std_ReturnType ret = E_OK; if ((NULL == buffer) || (bufLen < sizeof(StatM_SysStatis_Exp))) { ret = E_INVALID_PARAMETER; } else { // prepare data to export StatM_SysStatis_Exp.EcuUptime = StatMStistics.systemUptime; if (StatMStistics.cpuLoad.runningFlag == 0) { StatM_SysStatis_Exp.CpuLoad = StatMStistics.cpuLoad.nonIdleLoad; } else { StatM_SysStatis_Exp.CpuLoad = 0xFFFF; // invalid } StatM_SysStatis_Exp.pFlashIntact = StatMStistics.pFlashIntegrity.integrityFlag; memcpy(buffer, &StatM_SysStatis_Exp, sizeof(StatM_SysStatis_Exp)); } return ret; } #define ctadCddStatM_STOP_SEC_CODE #include "ctadCddStatM_MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */ // end of StatM private code // do not add StatM private code into this section #define OS_START_SEC_CODE #include "Os_MemMap_OsCode.h" #if (STATM_MEASURE_CPU_LOAD == STD_ON) // CPU load measurement HOW: // 1, CPU load definition: // CPU is aways running at full speed, so from CPU point of view, CPU load is 100% // but from Os perspective, things are different: CPU time including but not limit to: // 1.1 interrupt / exception time, T_int // 1.2 system time: context switch and other system time, T_sys // 1.3 Task time: application time: T_efftive // 1.4 Idle time: Idle task time. T_idle // CPU load is defined as: 100% - T_idle/T_mes, so the key to measurement is to estimate // how much time idle loop spent compare to measurement period(T_mes). // 2, Os assumption: // 2.1 Os must support preemptive // 2.2 Os must support priority based task schedule, task Idle has the lowest priority // 2.3 interrupt preempt (any) task // 2.4 Overall, the theory of CPU load measurement is based on: // 2.4.1, idle task is lowest priority task // 2.4.2, idle task should be preempted by any event: Os timer, interrupts, any other tasks // 2.4.3, idle task execution time is not directly measurable due to 2.4.1 and 2.4.2 // 2.4.4, idle task should resume execution after idle regain execution time (not interrupt, no // higher priority task running...) // 3, The key: T_idle // According to 2.4.3 and 2.4.4, to accumulating T_idle, we can use a counter (also due to idle task // is endlessness loop) to counts the number of idle loop executed during T_mes, with this counter // multiply the time of ONE idle loop execution time, we have T_idle. // so problem transfer to how to precisely measure ONE idle loop under the condition of idle loop runs // without interrupt. There maybe several ways to achieve this goal: for example, disable interrupt // during idle loop. But that would change system behavior, so I another solution: // replace Os Idle loop with StatM_IdleLoop2us, it does NOT matter how much code or complexity of the // code has to be written, the new idle loop has none system impact. Toggling a GPIO pin in every idle loop, // in theory, this will result in a square waveform in oscilloscope, with perfectly 50% duty IF idle loop // was not interrupted. if we found a continue square waveform in oscilloscope, i can assure that during this // measure time, idle task was not interrupted, so we get one idle execution time. // 3.1 calibration idle loop time // for calculation automation, i prefer calibrate idle loop time to a KNOWN value, that is also the // accuracy of the measurement. StatM_IdleLoop2us will calibrate idle loop execution time to 2 micro-sec // by padding enough NOP instruction to the loop. // to start calibration, set startFlag = 1, check GPIO duty and period, make sure it is 50% and 4 micro-sec // and stable. if SW change during calibration, flash to ECU afterward. // 3.2 after calibration, we can formally start CPU load measurement // 3.2.1 set startFlag = 1 via UDS write memory by address // 3.2.2 read nonIdleLoad after 10 seconds via UDS read memory by address // 4 verification // By add some dummy CPU load in rpIoHwAbUser10ms, the load can be roughly calculated as follow: // 500 * 50 * 100 * NOP = 2500000 * 3.3ns = 8.25ms per 200ms = 4.125%+ #define OS_IDLE_10_NOPS \ __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); #define OS_IDLE_25_NOPS \ OS_IDLE_10_NOPS; OS_IDLE_10_NOPS; __nop(); __nop(); __nop(); __nop(); __nop(); \ #define OS_IDLE_50_NOPS \ OS_IDLE_25_NOPS; OS_IDLE_25_NOPS; // time of execute NOPs * 3.3 // compensate idle execution time to 2us #define OS_IDLE_CALIBRATED_TO_2US \ OS_IDLE_50_NOPS; OS_IDLE_50_NOPS; OS_IDLE_50_NOPS; OS_IDLE_50_NOPS \ OS_IDLE_10_NOPS; OS_IDLE_10_NOPS; \ __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); // replacement of Os_CoreIdle if enable CPU load measurement void StatM_IdleLoop2us(void) { StatM_Statistic_CpuLoad* cpuload = &StatMStistics.cpuLoad; // instruction overhead if ((cpuload->runningFlag == 0) && (cpuload->startFlag != 0)) { cpuload->runningFlag = 1; // reset statistics cpuload->endTime = UTILTIME_TIMER_START(0) + UTILTIME_MSEC(cpuload->duration); cpuload->idleCounter = 0; cpuload->nonIdleLoad = 0; } if (cpuload->runningFlag == 1) { IoHwAb_ToggleDebugPin(DEBUG_PIN1); // using oscilloscope to calibrate idle loop execution time to 2 us OS_IDLE_CALIBRATED_TO_2US; cpuload->idleCounter += 2; // every 2us if (UTILTIME_TIMER_TIMEOUT(cpuload->endTime)) { // stop cumulation after cpu load measurement duration cpuload->startFlag = 0; cpuload->runningFlag = 0; cpuload->nonIdleLoad = 1000 - (uint16)(((uint64)cpuload->idleCounter) / cpuload->duration); } } else { //Os_Hal_CoreIdle(); // default idle code pFlashIntegrityCheck(); // run once __nop(); } } #endif //(STATM_MEASURE_CPU_LOAD == STD_ON) #define OS_STOP_SEC_CODE #include "Os_MemMap_OsCode.h" // EOF