今天尝试从RTOS内核的角度来看看 TriCore 的 CSA。
CSA的细节信息可以参考前一篇文章 TriCore User Manual 笔记 1-CSDN博客
CSA 的全称是 Context Save Area,顾名思义就是专门用来保存上下文的一块存储区域。
既然是上下文使用,那必然要求低延迟,因此一般将其部署在 fast ram 区域,具体到 TriCore 中,一般将其置于 DSPR 区域。在使用时,在内核启动之前,要将用作 CSA 的内存区域进行初始化,将其串成一个 Free CSA List,并将该区域的首尾地址分别赋值给 FCX 和 LCX 两个 CSA 寄存器,保证程序运行时能正常通过两个寄存器使用 Free CSA List。然后在创建任务时,为每个任务分配初始 CSA,保存任务初始运行状态信息。
下面,结合 Aurix Studio IDE 和 FreeRTOS 来看下上面所说的每一步分别怎么实现。
1. 部署 csa,放置到 DSPR 区域
#define LCF_DSPR0_SIZE 240k
#define LCF_CSA0_OFFSET (LCF_DSPR0_SIZE - 1k - LCF_CSA0_SIZE)memory dsram0 // Data Scratch Pad Ram{mau = 8;size = 240k;type = ram;map (dest=bus:tc0:fpi_bus, dest_offset=0xd0000000, size=240k, priority=8);map (dest=bus:sri, dest_offset=0x70000000, size=240k);}group (ordered){// 将 CORE0 CSA 部署到 DSPR 区域group (align = 64, attributes=rw, run_addr=mem:dsram0[LCF_CSA0_OFFSET]) reserved "csa_tc0" (size = LCF_CSA0_SIZE);"__CSA0":= "_lc_ub_csa_tc0";"__CSA0_END":= "_lc_ue_csa_tc0";}
2. 初始化 CSA, 创建 Free CSA List,并初始化 FCX 和 LCX 寄存器
#define IFX_SSW_INIT_CONTEXT() \{ \/* Load user stack pointer */ \Ifx_Ssw_setAddressReg(a10, __USTACK(0)); \Ifx_Ssw_DSYNC(); \\/*Initialize the context save area for CPU0. Function Calls Possible */ \/* Setup the context save area linked list */ \Ifx_Ssw_initCSA((unsigned int *)__CSA(0), (unsigned int *)__CSA_END(0)); \/* Clears any instruction buffer */ \Ifx_Ssw_ISYNC(); \}IFX_SSW_INLINE void Ifx_Ssw_initCSA(unsigned int *csaBegin, unsigned int *csaEnd)
{unsigned int k;unsigned int nxt_cxi_val = 0U;unsigned int *prvCsa = 0U;unsigned int *nxtCsa = csaBegin;unsigned int numOfCsa = (((unsigned int)csaEnd - (unsigned int)csaBegin) / 64U);for (k = 0U; k < numOfCsa; k++){nxt_cxi_val = ((unsigned int)((unsigned int)nxtCsa & ((unsigned int)0XFU << 28U)) >> 12U) | \((unsigned int)((unsigned int)nxtCsa & ((unsigned int)0XFFFFU << 6U)) >> 6U);if (k == 0U){Ifx_Ssw_MTCR(CPU_FCX, nxt_cxi_val); /* store the new pcxi value to LCX */}else{*prvCsa = nxt_cxi_val;}if (k == (numOfCsa - 3U)){Ifx_Ssw_MTCR(CPU_LCX, nxt_cxi_val); /* Last but 2 context save area is pointed in LCX to know if there is CSA depletion */}prvCsa = (unsigned int *)nxtCsa;nxtCsa += IFX_SSW_CSA_SIZE; /* next CSA */}*prvCsa = 0U; /* Store null pointer in last CSA (= very first time!) */Ifx_Ssw_DSYNC();
}
3. 创建任务时分配csa
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters )
{uint32_t * pulUpperCSA = NULL;uint32_t * pulLowerCSA = NULL;/* 16 Address Registers (4 Address registers are global), 16 Data* Registers, and 3 System Registers.** There are 3 registers that track the CSAs.* FCX points to the head of globally free set of CSAs.* PCX for the task needs to point to Lower->Upper->NULL arrangement.* LCX points to the last free CSA so that corrective action can be taken.** Need two CSAs to store the context of a task.* The upper context contains D8-D15, A10-A15, PSW and PCXI->NULL.* The lower context contains D0-D7, A2-A7, A11 and PCXI->UpperContext.* The pxCurrentTCB->pxTopOfStack points to the Lower Context RSLCX matching the initial BISR.* The Lower Context points to the Upper Context ready for the return from the interrupt handler.** The Real stack pointer for the task is stored in the A10 which is restored* with the upper context. *//* Have to disable interrupts here because the CSAs are going to be* manipulated. */portENTER_CRITICAL();{/* DSync to ensure that buffering is not a problem. */_dsync();/* Consume two free CSAs. */pulLowerCSA = portCSA_TO_ADDRESS( __MFCR( $FCX ) );if( NULL != pulLowerCSA ){/* The Lower Links to the Upper. */pulUpperCSA = portCSA_TO_ADDRESS( pulLowerCSA[ 0 ] );}/* Check that we have successfully reserved two CSAs. */if( ( NULL != pulLowerCSA ) && ( NULL != pulUpperCSA ) ){/* Remove the two consumed CSAs from the free CSA list. */_disable();_dsync();_mtcr( $FCX, pulUpperCSA[ 0 ] );_isync();_enable();}else{/* Simply trigger a context list depletion trap. */_svlcx();}}portEXIT_CRITICAL();/* Clear the upper CSA. */memset( pulUpperCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) );/* Upper Context. */pulUpperCSA[ 2 ] = ( uint32_t ) pxTopOfStack; /* A10; Stack Return aka Stack Pointer */pulUpperCSA[ 1 ] = portSYSTEM_PROGRAM_STATUS_WORD; /* PSW *//* Clear the lower CSA. */memset( pulLowerCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) );/* Lower Context. */pulLowerCSA[ 8 ] = ( uint32_t ) pvParameters; /* A4; Address Type Parameter Register */pulLowerCSA[ 1 ] = ( uint32_t ) pxCode; /* A11; Return Address aka RA *//* PCXI pointing to the Upper context. */pulLowerCSA[ 0 ] = ( portINITIAL_PCXI_UPPER_CONTEXT_WORD | ( uint32_t ) portADDRESS_TO_CSA( pulUpperCSA ) );/* Save the link to the CSA in the top of stack. */pxTopOfStack = ( uint32_t * ) portADDRESS_TO_CSA( pulLowerCSA );/* DSync to ensure that buffering is not a problem. */_dsync();return pxTopOfStack;
}