前言:autosar os 确实很强,但是我们在学习一些汽车软件的协议栈的时候,比如学习以太网协议栈,CAN 协议栈, 诊断等 相关知识的时候,需要os, 但是也不至于把os 搞一遍,这时候我们的开发环境就需要一个简单好用的操作系统。
于是我网上找了各种资源,也没找到找到tc397 + hightec 编译器的 freertos 说明。于是 自己做一个吧。
目录
需要完整环境,可以在最下方打赏领取。谢谢,制作不易
背景
硬件编译环境
芯片:Aurix Tc39x
编译器:Hightec
移植 freertos 操作系统。
freertos git 地址
JSON https://github.com/FreeRTOS/FreeRTOS-Kernel.git
|
Rtos 源码地址
也可以访问官网去下载,学习。
JSON https://www.FreeRTOS.org https://github.com/FreeRTOS
|
本文介绍 基于 硬件 aurix tc397 编译器 hightec 的环境 去移植 freertos. 内核版本
JSON FreeRTOS Kernel V10.5.1
|
移植freertos项目
工程 代码目录结构
JSON ├─asw ├─Configurations │ └─Debug ├─Libraries │ ├─iLLD │ │ └─TC39B │ │ └─Tricore │ │ ├─Asclin │ │ │ ├─Asc │ │ │ ├─Lin │ │ │ ├─Spi │ │ │ └─Std │ │ ├─Can │ │ │ ├─Can │ │ │ └─Std │ │ ├─Ccu6 │ │ │ ├─Icu │ │ │ ├─PwmBc │ │ │ ├─PwmHl │ │ │ ├─Std │ │ │ ├─Timer │ │ │ ├─TimerWithTrigger │ │ │ └─TPwm │ │ ├─Convctrl │ │ │ └─Std │ │ ├─Cpu │ │ │ ├─Irq │ │ │ ├─Std │ │ │ └─Trap │ │ ├─Dma │ │ │ ├─Dma │ │ │ └─Std │ │ ├─Dts │ │ │ ├─Dts │ │ │ └─Std │ │ ├─Ebu │ │ │ ├─BFlashSpansion │ │ │ ├─BFlashSt │ │ │ ├─Dram │ │ │ ├─Sram │ │ │ └─Std │ │ ├─Edsadc │ │ │ ├─Edsadc │ │ │ └─Std │ │ ├─Emem │ │ │ └─Std │ │ ├─Eray │ │ │ ├─Eray │ │ │ └─Std │ │ ├─Evadc │ │ │ ├─Adc │ │ │ └─Std │ │ ├─Fce │ │ │ ├─Crc │ │ │ └─Std │ │ ├─Flash │ │ │ └─Std │ │ ├─Geth │ │ │ ├─Eth │ │ │ └─Std │ │ ├─Gpt12 │ │ │ ├─IncrEnc │ │ │ └─Std │ │ ├─Gtm │ │ │ ├─Atom │ │ │ │ ├─Dtm_PwmHl │ │ │ │ ├─Pwm │ │ │ │ ├─PwmHl │ │ │ │ └─Timer │ │ │ ├─Std │ │ │ ├─Tim │ │ │ │ ├─In │ │ │ │ └─Timer │ │ │ ├─Tom │ │ │ │ ├─Dtm_PwmHl │ │ │ │ ├─Pwm │ │ │ │ ├─PwmHl │ │ │ │ └─Timer │ │ │ └─Trig │ │ ├─Hspdm │ │ │ └─Std │ │ ├─Hssl │ │ │ ├─Hssl │ │ │ └─Std │ │ ├─I2c │ │ │ ├─I2c │ │ │ └─Std │ │ ├─Iom │ │ │ ├─Driver │ │ │ ├─Iom │ │ │ └─Std │ │ ├─Msc │ │ │ ├─Msc │ │ │ └─Std │ │ ├─Mtu │ │ │ └─Std │ │ ├─Pms │ │ │ └─Std │ │ ├─Port │ │ │ ├─Io │ │ │ └─Std │ │ ├─Psi5 │ │ │ ├─Psi5 │ │ │ └─Std │ │ ├─Psi5s │ │ │ ├─Psi5s │ │ │ └─Std │ │ ├─Qspi │ │ │ ├─SpiMaster │ │ │ ├─SpiSlave │ │ │ └─Std │ │ ├─Rif │ │ │ ├─Rif │ │ │ └─Std │ │ ├─Scu │ │ │ └─Std │ │ ├─Sdmmc │ │ │ ├─Emmc │ │ │ ├─Sd │ │ │ └─Std │ │ ├─Sent │ │ │ ├─Sent │ │ │ └─Std │ │ ├─Smu │ │ │ ├─Smu │ │ │ └─Std │ │ ├─Spu │ │ │ └─Std │ │ ├─Src │ │ │ └─Std │ │ ├─Stm │ │ │ ├─Std │ │ │ └─Timer │ │ ├─_Build │ │ ├─_Impl │ │ ├─_Lib │ │ │ ├─DataHandling │ │ │ └─InternalMux │ │ └─_PinMap │ ├─Infra │ │ ├─Platform │ │ │ └─Tricore │ │ │ └─Compilers │ │ ├─Sfr │ │ │ └─TC39B │ │ │ └─_Reg │ │ └─Ssw │ │ └─TC39B │ │ └─Tricore │ └─Service │ └─CpuGeneric │ ├─If │ │ └─Ccu6If │ ├─StdIf │ ├─SysSe │ │ ├─Bsp │ │ ├─Comm │ │ ├─General │ │ ├─Math │ │ └─Time │ └─_Utilities └─os └─FreeRTOS ├─include └─portable ├─GCC │ └─TC3 ├─MemMang │ └─reserve └─Tasking └─TC3
|
初始环境
通过上面的文件夹结构,大家也能发现,base 实际上是aurix 的 iLLD环境。没错,我们一开始需要一个能跑到 main 的 无操作系统环境。
初始项目环境
这个环境怎么获取呢。可以通过aurix 的IDE 新建一个项目即可。
确保 main 可以跑到。这个怎么验证呢。
JSON /*Call main function of Cpu0 */ Ifx_Ssw_jumpToFunction(core0_main);
|
在main 里面写好标识,调试一下即可。默认的环境应该都是可以的。
链接文件
注意 IDE 使用的默认编译器是tasking. 如果我们想换成 hightec 的编译器,需要修改 连接文件。
这里我们使用默认的地址分配。
JSON MEMORY { dsram5_local (w!xp): org = 0xd0000000, len = 96K dsram5 (w!xp): org = 0x10000000, len = 96K psram5 (w!xp): org = 0x10100000, len = 64K dsram4_local (w!xp): org = 0xd0000000, len = 96K dsram4 (w!xp): org = 0x30000000, len = 96K psram4 (w!xp): org = 0x30100000, len = 64K dsram3_local (w!xp): org = 0xd0000000, len = 96K dsram3 (w!xp): org = 0x40000000, len = 96K psram3 (w!xp): org = 0x40100000, len = 64K dsram2_local (w!xp): org = 0xd0000000, len = 96K dsram2 (w!xp): org = 0x50000000, len = 96K psram2 (w!xp): org = 0x50100000, len = 64K dsram1_local (w!xp): org = 0xd0000000, len = 240K dsram1 (w!xp): org = 0x60000000, len = 240K psram1 (w!xp): org = 0x60100000, len = 64K dsram0_local (w!xp): org = 0xd0000000, len = 240K dsram0 (w!xp): org = 0x70000000, len = 240K psram0 (w!xp): org = 0x70100000, len = 64K psram_local (w!xp): org = 0xc0000000, len = 64K pfls0 (rx!p): org = 0x80000000, len = 3M pfls0_nc (rx!p): org = 0xa0000000, len = 3M pfls1 (rx!p): org = 0x80300000, len = 3M pfls1_nc (rx!p): org = 0xa0300000, len = 3M pfls2 (rx!p): org = 0x80600000, len = 3M pfls2_nc (rx!p): org = 0xa0600000, len = 3M pfls3 (rx!p): org = 0x80900000, len = 3M pfls3_nc (rx!p): org = 0xa0900000, len = 3M pfls4 (rx!p): org = 0x80c00000, len = 3M pfls4_nc (rx!p): org = 0xa0c00000, len = 3M pfls5 (rx!p): org = 0x80f00000, len = 1M pfls5_nc (rx!p): org = 0xa0f00000, len = 1M dfls0 (rx!p): org = 0xaf000000, len = 1M ucb (rx!p): org = 0xaf400000, len = 24K cpu0_dlmu (w!xp): org = 0x90000000, len = 64K cpu0_dlmu_nc (w!xp): org = 0xb0000000, len = 64K cpu1_dlmu (w!xp): org = 0x90010000, len = 64K cpu1_dlmu_nc (w!xp): org = 0xb0010000, len = 64K cpu2_dlmu (w!xp): org = 0x90020000, len = 64K cpu2_dlmu_nc (w!xp): org = 0xb0020000, len = 64K cpu3_dlmu (w!xp): org = 0x90030000, len = 64K cpu3_dlmu_nc (w!xp): org = 0xb0030000, len = 64K lmuram (w!xp): org = 0x90040000, len = 768K lmuram_nc (w!xp): org = 0xb0040000, len = 768K cpu4_dlmu (w!xp): org = 0x90100000, len = 64K cpu4_dlmu_nc (w!xp): org = 0xb0100000, len = 64K cpu5_dlmu (w!xp): org = 0x90110000, len = 64K cpu5_dlmu_nc (w!xp): org = 0xb0110000, len = 64K edmem (w!xp): org = 0x99000000, len = 4M edmem_nc (w!xp): org = 0xb9000000, len = 4M }
|
起始地址函数
JSON void cstart(void) { Ifx_Ssw_jumpToFunction(__StartUpSoftware); }
|
需要根据实际的编译选项来修改。
到这里初始项目环境已经完毕。
添加os源码到工程
在这个基础上我们添加了 os的源码
JSON └─os └─FreeRTOS ├─include └─portable ├─GCC │ └─TC3 ├─MemMang │ └─reserve └─Tasking └─TC3
|
移植
堆使用
Rtos 对象是动态创建的所以需要用到 C 库 malloc() 和 free() 函数。但是这里不是直接使用 标准C 接口,而是进行了 丰富,优化,约束。
当 RTOS 内核需要 RAM 时,它不调用 malloc(),而是调用 pvPortMalloc()。释放 RAM 时, RTOS 内核调用 vPortFree(),而不是 free()。
在 rtos 源码中提供了下面五种 堆内存使用方式。在我们移植的时候,只需要选择其中一种。
1heap_1 —— 最简单,不允许释放内存。
1heap_2—— 允许释放内存,但不会合并相邻的空闲块。
1heap_3 —— 简单包装了标准 malloc() 和 free(),以保证线程安全。
1heap_4 —— 合并相邻的空闲块以避免碎片化。包含绝对地址放置选项。
1heap_5 —— 如同 heap_4,能够跨越多个不相邻内存区域的堆。
为了简单。本文使用了heap_1.c ( heap_1 不太有用,因为 FreeRTOS 添加了静态分配支持)
所以在移植过程 文件夹MemMang 文件夹下面的五个文件
JSON heap_1.c heap_2.c heap_3.c heap_4.c heap_5.c
|
只需要保留 heap_1.c
FreeRTOSConfig.h
这里需要根据tc397的芯片手册进行修改。
比如 300M的主频
JSON #define configCPU_CLOCK_HZ ( ( unsigned long ) 300000000UL )
|
下面这些定时器,中断相关的参数
比如下面的STM 的clc 控制寄存器,寄存器地址就需要去芯片手册里找。我这里找一个
JSON /** brief 0, Clock Control Register */ #define STM0_CLC /*lint –e(923, 9078)*/ (*(volatile Ifx_STM_CLC*)0xF0001000u) /** brief Clock Control Register */ typedef struct _Ifx_STM_CLC_Bits { Ifx_UReg_32Bit DISR:1; /**< brief [0:0] Module Disable Request Bit – DISR (rw) */ Ifx_UReg_32Bit DISS:1; /**< brief [1:1] Module Disable Status Bit – DISS (r) */ Ifx_UReg_32Bit reserved_2:1; /**< brief [2:2] internal Reserved */ Ifx_UReg_32Bit EDIS:1; /**< brief [3:3] Sleep Mode Enable Control – EDIS (rw) */ Ifx_UReg_32Bit reserved_4:28; /**< brief [31:4] internal Reserved */ } Ifx_STM_CLC_Bits;
|
下面给出全部的 该文件对应 tc397 配置。
JSON #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configCPU_CLOCK_HZ ( ( unsigned long ) 300000000UL ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000UL ) #define configMAX_PRIORITIES ( 10 ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 48U * 1024U ) ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configENABLE_BACKWARD_COMPATIBILITY 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 1 #define configUSE_TICK_HOOK 0 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_MUTEXES 1 #define configRECORD_STACK_HIGH_ADDRESS 1 #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 /* Software timer configuration. */ #define configUSE_TIMERS ( 1 ) #define configTIMER_TASK_PRIORITY ( 9 ) #define configTIMER_QUEUE_LENGTH ( 5 ) #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE /* Set the following definitions to 1 to include the API function, or zero * to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 /* Interrupt above priority 31 are not effected by critical sections, but cannot call interrupt safe FreeRTOS functions. */ #define configMAX_API_CALL_INTERRUPT_PRIORITY 31 /* Default definition of configASSERT(). */ #ifdef DEBUG #ifdef __TASKING__ #define configASSERT( x ) if( ( x ) == 0 ) { __disable(); __debug(); } #endif #ifdef __clang__ #define configASSERT( x ) if( ( x ) == 0 ) { __builtin_tricore_disable(); __builtin_tricore_debug(); } #endif #else #define configASSERT( x ) ((void)(x)) /* Empty macro to remove compiler warning(s) about unused variables */ #endif /* AURIX TCxxx definitions */ #define configCONTEXT_INTERRUPT_PRIORITY 1 #define configTIMER_INTERRUPT_PRIORITY 200 /* This value must not be bigger then context priority */ #define configCPU_NR 0 #define configPROVIDE_SYSCALL_TRAP 0 #define configSYSCALL_CALL_DEPTH 2 #define configSTM ( ( uint32_t * ) (0xF0001000 + configCPU_NR*0x100 ) ) #define configSTM_SRC ( ( uint32_t * ) (0xF0038300 + configCPU_NR*0x8) ) #define configSTM_CLOCK_HZ ( 100000000 ) #define configSTM_DEBUG ( 1 ) #define configCONTEXT_SRC ( ( uint32_t * ) 0xF0038990 ) #endif /* FREERTOS_CONFIG_H */
|
port.c 与 portmacro.h
这两个文件是需要开发者主要关注的文件。里面包含了大量的针对板子的移植工作。
比如根据芯片手册查到的 寄存器地址
寄存器地址
截图出自 TC3xx Architecture vol1 V1.2.2.pdf
对应的移植就是
JSON #define portCPU_PSW 0xFE04 #define portCPU_PSW_IS_OFF ( 9 ) #define portCPU_PSW_CSC_MSK ( 0x7F ) #define portCPU_ICR 0xFE2C #define portCPU_ICR_CCPN_OFF ( 0 ) #define portCPU_ICR_CCPN_MSK ( 0x000000FFUL ) #define portCPU_FCX 0xFE38 #define portCPU_PCXI 0xFE00 #define portCPU_CORE_ID 0xFE1C
|
数据类型定义
JSON /* Type definitions. */ #define portCHAR char #define portSHORT short #define portLONG long #define portFLOAT float #define portDOUBLE double #define portSTACK_TYPE unsigned int #define portBASE_TYPE long #define portPOINTER_SIZE_TYPE uintptr_t
|
编译器指令
JSON /* Instructions */ #define portNOP() _nop() #define portMEMORY_BARRIER() _dsync()
|
话不多说,直接给出 portmacro.h
JSON #ifndef PORTMACRO_H #define PORTMACRO_H #ifdef __cplusplus extern “C” #endif #include “FreeRTOSConfig.h” #include #define portCPU_PSW 0xFE04 #define portCPU_PSW_IS_OFF ( 9 ) #define portCPU_PSW_CSC_MSK ( 0x7F ) #define portCPU_ICR 0xFE2C #define portCPU_ICR_CCPN_OFF ( 0 ) #define portCPU_ICR_CCPN_MSK ( 0x000000FFUL ) #define portCPU_FCX 0xFE38 #define portCPU_PCXI 0xFE00 #define portCPU_CORE_ID 0xFE1C /* Register defintions */ #define portSRC_SRCR_SRPN_OFF 0 #define portSRC_SRCR_SRE_OFF 10 #define portSRC_SRCR_TOS_OFF 11 #define portSRC_SRCR_SRR_OFF 24 #define portSRC_SRCR_SETR_OFF 26 /* Type definitions. */ #define portCHAR char #define portSHORT short #define portLONG long #define portFLOAT float #define portDOUBLE double #define portSTACK_TYPE unsigned int #define portBASE_TYPE long #define portPOINTER_SIZE_TYPE uintptr_t typedef portSTACK_TYPE StackType_t; typedef long BaseType_t; typedef unsigned long UBaseType_t; #if ( configUSE_16_BIT_TICKS == 1 ) typedef unsigned short TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffff #else typedef unsigned int TickType_t __attribute__( ( aligned( 4 ) ) ); #define portMAX_DELAY ( TickType_t ) 0xffffffffUL #endif /* FreeRTOS parameters */ #define portTICK_TYPE_IS_ATOMIC 1 #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 #define portCRITICAL_NESTING_IN_TCB 0 /* Attributes */ #define portDONT_DISCARD __attribute__( ( used ) ) #define portNORETURN __attribute__( ( noreturn ) ) /* Instructions */ #define portNOP() _nop() #define portMEMORY_BARRIER() _dsync() /* Critical section management */ extern void vPortEnterCritical( void ); extern void vPortExitCritical( void ); #define portENTER_CRITICAL() vPortEnterCritical() #define portEXIT_CRITICAL() vPortExitCritical() #define portENABLE_INTERRUPTS() vPortSetCCPN( 0 ); #define portDISABLE_INTERRUPTS() vPortSetCCPN( configMAX_API_CALL_INTERRUPT_PRIORITY ) #define portASSERT_IF_IN_ISR() configASSERT( ( _mfcr( portCPU_PSW ) & ( 1 << portCPU_PSW_IS_OFF ) ) == 0 ) #define portCLEAR_INTERRUPT_MASK_FROM_ISR( ulSavedMaskValue ) vPortSetICR( ulSavedMaskValue ) #define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetCCPN( configMAX_API_CALL_INTERRUPT_PRIORITY ) #ifndef configYIELD_SYSCALL_ID #define configYIELD_SYSCALL_ID 0 #endif #define portYIELD() _syscall( configYIELD_SYSCALL_ID ) #define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) { const uint32_t xTrigger = ( ( *configCONTEXT_SRC >> portSRC_SRCR_SRR_OFF ) & 0x1 ) != 1 && ( xHigherPriorityTaskWoken != 0 ); *configCONTEXT_SRC |= ( xTrigger << portSRC_SRCR_SETR_OFF ); /* Wait until write request completes to trigger IRQ */ _dsync(); _isync(); } #ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 #endif #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 ) /* Check the configuration. */ #if ( configMAX_PRIORITIES > 32 ) #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. #endif /* Store/clear the ready priorities in a bit map. */ #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL – ( ( uint32_t ) __builtin_clz( ( uxReadyPriorities ) ) ) ) #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ /* Function prototypes */ #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters ) #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters ) /* TCB handling */ extern void vPortReclaimCSA( unsigned long ** pxTCB ); #define portCLEAN_UP_TCB( pxTCB ) vPortReclaimCSA( ( unsigned long ** ) ( pxTCB ) ) /* ICR & CCPN modifying functions to enable and disable interrupts. * Only interrupts with a priority lower than */ static void __attribute__( ( used, always_inline ) ) vPortSetCCPN( unsigned char ucCCPN ) { _disable(); _mtcr( portCPU_ICR, ( _mfcr( portCPU_ICR ) & ~portCPU_ICR_CCPN_MSK ) | ( ucCCPN & portCPU_ICR_CCPN_MSK ) ); _isync(); _enable(); } static void __attribute__( ( used, always_inline ) ) vPortSetICR( portBASE_TYPE ulICR ) { _disable(); _mtcr( portCPU_ICR, ( unsigned int ) ulICR ); _isync(); _enable(); } static portBASE_TYPE __attribute__( ( used, always_inline ) ) xPortSetCCPN( unsigned char ucCCPN ) { unsigned int xICR; _disable(); xICR = _mfcr( portCPU_ICR ); _mtcr( portCPU_ICR, ( xICR & ~portCPU_ICR_CCPN_MSK ) | ( ucCCPN & portCPU_ICR_CCPN_MSK ) ); _isync(); _enable(); return ( portBASE_TYPE ) xICR; } #ifdef __cplusplus } #endif #endif /* PORTMACRO_H */
|
话不多说,直接给出 port.c
JSON #include #include #include #include “FreeRTOS.h” #include “task.h” /* Prgoram status word macros */ #define portINITIAL_SYSTEM_PSW ( 0x000008FFUL ) /* Supervisor Mode, MPU Register Set 0 and Call Depth Counting disabled. */ /* Context save area macros */ #define portCSA_FCX_MASK ( 0x000FFFFFUL ) #define portINITIAL_LOWER_PCXI ( 0x00300000UL ) /* Set UL to upper and PIE to 1 */ #define portINITIAL_UPPER_PCXI ( 0x00200000UL ) /* Set UL to lower and PIE to 1 */ #define portNUM_WORDS_IN_CSA ( 16 ) extern volatile unsigned long * pxCurrentTCB; /* Tick and context switch config */ #define portTICK_COUNT ( configSTM_CLOCK_HZ / configTICK_RATE_HZ ) /* Register defines */ static volatile uint32_t *const pxStm = configSTM; static volatile uint32_t *const pxStmSrc = configSTM_SRC; static volatile uint32_t *const pxContextSrc = configCONTEXT_SRC; #define portSTM_TIM0 0x10 #define portSTM_CMP0 0x30 #define portSTM_COMCON 0x38 #define portSTM_ICR 0x3C #define portSTM_ISCR 0x40 #define portSTM_OCS 0xE8 #define portSTM_CMCON_MSTART0_OFF 8 #define portSTM_CMCON_MSIZE0_OFF 0 #define portSTM_ICR_CMP0EN_OFF 0 #define portSTM_ICR_CMP0OS_OFF 2 #define portSTM_ISCR_CMP0IRR_OFF 0 static inline void vPortStartFirstTask( void ); static inline void vPortInitContextSrc( void ); static inline void vPortInitTickTimer( void ); static inline void __attribute__( ( always_inline ) ) vPortLoadContext( unsigned char ucCallDepth ); static inline void __attribute__( ( always_inline ) ) vPortSaveContext( unsigned char ucCallDepth ); static inline uint32_t * __attribute__( ( always_inline ) ) pxPortCsaToAddress( uint32_t xCsa ); static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; /* FreeRTOS required functions */ BaseType_t xPortStartScheduler( void ) { vPortInitTickTimer(); vPortInitContextSrc(); vPortStartFirstTask(); return 0; } void vPortEndScheduler() { pxStmSrc[ 0 ] &= ~( 1 << portSRC_SRCR_SRE_OFF ); pxContextSrc[ 0 ] &= ~( 1 << portSRC_SRCR_SRE_OFF ); } StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack, TaskFunction_t pxCode, void * pvParameters ) { uint32_t xLowerCsa = 0, xUpperCsa = 0; uint32_t * pxUpperCSA = NULL; uint32_t * pxLowerCSA = NULL; /* Have to disable interrupts here because the CSAs are going to be * manipulated. */ _disable(); { /* DSync to ensure that buffering is not a problem. */ _dsync(); /* Consume two free CSAs. */ xLowerCsa = _mfcr( portCPU_FCX ); pxLowerCSA = pxPortCsaToAddress( xLowerCsa ); if( pxLowerCSA != NULL ) { /* The Lower Links to the Upper. */ xUpperCsa = pxLowerCSA[ 0 ]; pxUpperCSA = pxPortCsaToAddress( pxLowerCSA[ 0 ] ); } /* Check that we have successfully reserved two CSAs. */ if( ( pxLowerCSA != NULL ) && ( pxUpperCSA != NULL ) ) { /* Remove the two consumed CSAs from the free CSA list. */ _mtcr( portCPU_FCX, pxUpperCSA[ 0 ] ); _isync(); } else { /* Simply trigger a context list depletion trap. */ __asm( “tsvlcx” ); } } _enable(); /* Upper Context. */ memset( pxUpperCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) ); pxUpperCSA[ 2 ] = ( uint32_t ) pxTopOfStack; /* A10; Stack Return aka Stack Pointer */ pxUpperCSA[ 1 ] = portINITIAL_SYSTEM_PSW; /* PSW */ pxUpperCSA[ 0 ] = portINITIAL_UPPER_PCXI; /* Lower Context. */ memset( pxLowerCSA, 0, portNUM_WORDS_IN_CSA * sizeof( uint32_t ) ); pxLowerCSA[ 8 ] = ( uint32_t ) pvParameters; /* A4; Address Type Parameter Register */ pxLowerCSA[ 1 ] = ( uint32_t ) pxCode; /* A11; Return Address aka RA */ pxLowerCSA[ 0 ] = portINITIAL_LOWER_PCXI | xUpperCsa; /* PCXI pointing to the Upper context. */ /* Initialize the uxCriticalNesting. */ pxTopOfStack–; *pxTopOfStack = 0; /* Save the link to the CSA to the top of stack. */ pxTopOfStack–; *pxTopOfStack = xLowerCsa; return pxTopOfStack; } void __attribute__( ( interrupt_handler ) ) vPortSystemContextHandler() { /* Disable interrupts to protect section*/ _disable(); /* Do a save, switch, execute */ vPortSaveContext( 0 ); vTaskSwitchContext(); vPortLoadContext( 0 ); _enable(); } #define STR( x ) # x #define XSTR( s ) STR( s ) __asm__ ( ” .pushsection .intvec_tc” XSTR( configCPU_NR ) “_” XSTR( configCONTEXT_INTERRUPT_PRIORITY ) “,”ax”,@progbitsn” ” .align 5n” ” __intvec_entry_” XSTR( configCONTEXT_INTERRUPT_PRIORITY ) “:n” ” svlcxn” ” movh.a %a14, hi:vPortSystemContextHandlern” ” lea %a14, [%a14]lo:vPortSystemContextHandlern” ” ji %a14n” ” .org 32n” ” .popsectionn” ); void __attribute__( ( interrupt_handler ) ) vPortSystemTickHandler() { unsigned long ulSavedInterruptMask; BaseType_t xYieldRequired; /* Increment compare value by tick count */ pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_CMP0 >> 2 ] + portTICK_COUNT; pxStm[ portSTM_ISCR >> 2 ] |= ( 1 << portSTM_ISCR_CMP0IRR_OFF ); /* Check for possible tick drop. * If the time is beyond the compare value, the next tick will need a complete * wrap around. The tick count isn’t accruate any more. Increase the tick count * or adapt to execute xTaskIncrementTick multiple times depending on the * counts missed. */ #if configCPU_STM_DEBUG != 0 configASSERT( ( pxStm[ portSTM_CMP0 >> 2 ] – pxStm[ portSTM_TIM0 >> 2 ] ) <= portTICK_COUNT ); #endif /* Kernel API calls require Critical Sections. */ ulSavedInterruptMask = portSET_INTERRUPT_MASK_FROM_ISR(); { /* Increment the Tick. */ xYieldRequired = xTaskIncrementTick(); } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulSavedInterruptMask ); portYIELD_FROM_ISR( xYieldRequired ); } __asm__ ( ” .pushsection .intvec_tc” XSTR( configCPU_NR ) “_” XSTR( configTIMER_INTERRUPT_PRIORITY ) “,”ax”,@progbitsn” ” .align 5n” ” __intvec_entry_” XSTR( configTIMER_INTERRUPT_PRIORITY ) “:n” ” svlcxn” ” movh.a %a14, hi:vPortSystemTickHandlern” ” lea %a14, [%a14]lo:vPortSystemTickHandlern” ” ji %a14n” ” .org 32n” ” .popsectionn” ); void __attribute__( ( noinline ) ) vPortSyscallYield( void ); int vPortSyscallHandler( unsigned char id ) { switch( id ) { case 0: vPortSyscallYield(); break; default: break; } return 0; } void vPortInitTickTimer() { pxStm[ portSTM_COMCON >> 2 ] = ( 0 << portSTM_CMCON_MSTART0_OFF ) | ( 31 << portSTM_CMCON_MSIZE0_OFF ); pxStm[ portSTM_ICR >> 2 ] &= ~( 1 << portSTM_ICR_CMP0OS_OFF ); pxStmSrc[ 0 ] = ( ( configCPU_NR > 0 ? configCPU_NR + 1 : configCPU_NR ) << portSRC_SRCR_TOS_OFF ) | ( ( configTIMER_INTERRUPT_PRIORITY ) << portSRC_SRCR_SRPN_OFF ); pxStmSrc[ 0 ] |= ( 1 << portSRC_SRCR_SRE_OFF ); pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_TIM0 >> 2 ]; pxStm[ portSTM_ISCR >> 2 ] |= ( 1 << portSTM_ISCR_CMP0IRR_OFF ); pxStm[ portSTM_ICR >> 2 ] |= ( 1 << portSTM_ICR_CMP0EN_OFF ); pxStm[ portSTM_CMP0 >> 2 ] = pxStm[ portSTM_TIM0 >> 2 ] + portTICK_COUNT; #if configTICK_STM_DEBUG != 0 pxStm[ portSTM_OCS >> 2 ] = 0x12000000; #endif } void vPortInitContextSrc() { pxContextSrc[ 0 ] = ( ( configCPU_NR > 0 ? configCPU_NR + 1 : configCPU_NR ) << portSRC_SRCR_TOS_OFF ) | ( ( configCONTEXT_INTERRUPT_PRIORITY ) << portSRC_SRCR_SRPN_OFF ); pxContextSrc[ 0 ] |= ( 1 << portSRC_SRCR_SRE_OFF ); } void vPortStartFirstTask() { /* Disable interrupts */ _disable(); vPortLoadContext( 0 ); /* Reset the call stack counting, to avoid trap on rfe */ unsigned long ulPsw = _mfcr( portCPU_PSW ); ulPsw &= ~( portCPU_PSW_CSC_MSK ); _mtcr( portCPU_PSW, ulPsw ); _isync(); /* Load the lower context and upper context through rfe to enable irqs */ __asm( “trslcx” ); __asm( “trfe” ); _nop(); _nop(); _nop(); } void vPortLoadContext( unsigned char ucCallDepth ) { uint32_t ** ppxTopOfStack; uint32_t uxLowerCSA; /* Dsync is required for save CSA access */ _dsync(); /* Load the new CSA id from the stack and update the stack pointer */ ppxTopOfStack = ( uint32_t ** ) pxCurrentTCB; uxLowerCSA = **ppxTopOfStack; ( *ppxTopOfStack )++; uxCriticalNesting = **ppxTopOfStack; ( *ppxTopOfStack )++; /* Store the lower context directly if inside the syscall or interrupt, * else replace the lower context in the call stack. */ if( !ucCallDepth ) { /* Update the link register */ _mtcr( portCPU_PCXI, uxLowerCSA ); _isync(); } else { /* Update previous lower context */ uint32_t * pxCSA = pxPortCsaToAddress( _mfcr( portCPU_PCXI ) ); int i; for(i = 0; i < ucCallDepth – 1; i++) { pxCSA = pxPortCsaToAddress( pxCSA[ 0 ] ); } pxCSA[ 0 ] = uxLowerCSA; } } void vPortSaveContext( unsigned char ucCallDepth ) { uint32_t ** ppxTopOfStack; uint32_t * pxLowerCSA, * pxUpperCSA; uint32_t uxLowerCSA; /* Dsync is required for save CSA access */ _dsync(); /* Get the current context information. */ uxLowerCSA = _mfcr( portCPU_PCXI ); /* If this function is used inside a function from the syscall or interrupt, * load the correct context from the call stack */ if( ucCallDepth ) { uint32_t * pxCSA = pxPortCsaToAddress( uxLowerCSA ); int i; for(i = 0; i < ucCallDepth – 1; i++) { pxCSA = pxPortCsaToAddress( pxCSA[ 0 ] ); } uxLowerCSA = pxCSA[ 0 ]; } pxLowerCSA = pxPortCsaToAddress( uxLowerCSA ); pxUpperCSA = pxPortCsaToAddress( pxLowerCSA[ 0 ] ); /* Load the stack pointer */ ppxTopOfStack = ( uint32_t ** ) pxCurrentTCB; /* Update the stack info in the TCB */ *ppxTopOfStack = ( uint32_t * ) pxUpperCSA[ 2 ]; /* Place ucNestedContext */ ( *ppxTopOfStack )–; **ppxTopOfStack = uxCriticalNesting; /* Place the lower CSA id on the stack */ ( *ppxTopOfStack )–; **ppxTopOfStack = uxLowerCSA; } void vPortSyscallYield() { /* Do a save, switch, execute */ vPortSaveContext( configSYSCALL_CALL_DEPTH ); vTaskSwitchContext(); vPortLoadContext( configSYSCALL_CALL_DEPTH ); } uint32_t * pxPortCsaToAddress( uint32_t xCsa ) { uint32_t pxCsa; __asm ( “extr.u %0, %1, 16, 4n” “sh %0, %0, 28n” “insert %0, %0, %1 6, 16n” : “+d” ( pxCsa ) : “d” ( xCsa ) ); /*pxCsa = (_extr_u(xCsa, 16, 4) << 28); */ /*pxCsa = _insert(pxCsa, xCsa, 6, 16); */ return ( uint32_t * ) pxCsa; } void vPortEnterCritical( void ) { portDISABLE_INTERRUPTS(); uxCriticalNesting++; /* This is not the interrupt safe version of the enter critical function so * assert() if it is being called from an interrupt context. Only API * functions that end in “FromISR” can be used in an interrupt. Only assert if * the critical nesting count is 1 to protect against recursive calls if the * assert function also uses a critical section. */ if( uxCriticalNesting == 1 ) { portASSERT_IF_IN_ISR(); } } void vPortExitCritical( void ) { configASSERT( uxCriticalNesting ); uxCriticalNesting–; if( uxCriticalNesting == 0 ) { portENABLE_INTERRUPTS(); } } void vPortReclaimCSA( unsigned long ** pxTCB ) { uint32_t ulHeadCSA, ulFreeCSA; uint32_t * pulNextCSA; /* The lower context (PCXI value) to return to the task is stored as the * current element on the stack. Mask off everything in the PCXI register * other than the address. */ ulHeadCSA = ( **pxTCB ) & portCSA_FCX_MASK; /* Iterate over the CSAs that were consumed as part of the task. */ for(pulNextCSA = pxPortCsaToAddress( ulHeadCSA ); ( pulNextCSA[ 0 ] & portCSA_FCX_MASK ) != 0; pulNextCSA = pxPortCsaToAddress( pulNextCSA[ 0 ] ) ) { /* Mask off everything in the PCXI value other than the address. */ pulNextCSA[ 0 ] &= portCSA_FCX_MASK; } _disable(); { /* Look up the current free CSA head. */ _dsync(); ulFreeCSA = _mfcr( portCPU_FCX ); /* Join the current free onto the tail of what is being reclaimed. */ pulNextCSA[ 0 ] = ulFreeCSA; /* Move the head of the reclaimed into the Free. */ _mtcr( portCPU_FCX, ulHeadCSA ); _isync(); } _enable(); } void __attribute__( ( noreturn ) ) vPortLoopForever( void ) { while( 1 ) { } }
|
测试代码
我们在main 函数里面创建了两个任务。
创建任务
1task_app1
1task_app2
JSON void core0_main(void) { IfxCpu_enableInterrupts(); /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!! * Enable the watchdogs and service them periodically if it is required */ IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword()); IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword()); /* Wait for CPU sync event */ IfxCpu_emitEvent(&g_cpuSyncEvent); IfxCpu_waitEvent(&g_cpuSyncEvent, 1); /* Initialize a time variable */ // Ifx_TickTime ticksFor1s = IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, WAIT_TIME); /* Create LED1 app task */ app1_status = xTaskCreate(task_app1, “APP1”, configMINIMAL_STACK_SIZE, NULL, 3, NULL); /* Create LED2 app task */ app2_status = xTaskCreate(task_app2, “APP2”, configMINIMAL_STACK_SIZE, NULL, 5, NULL); /* Start the scheduler */ vTaskStartScheduler(); while(1) { } }
|
任务的内容很简单,就是周期任务,里面有counter 在自增。
任务实现
JSON void task_app1(void) { app1_cnt++; TickType_t xLastWakeTime; xLastWakeTime = xTaskGetTickCount(); while(1) { app1_cnt++; vTaskDelayUntil(&xLastWakeTime, 1000); } } void task_app2(void) { app2_cnt++; while(1) { app2_cnt++; /* Delay 250ms */ vTaskDelay(pdMS_TO_TICKS(250)); } }
|
编译测试
编译生成 elf, 和 map 文件。
检查map文件
我们来读取一下map 文件中的两个任务。实际上就是两个函数。和我们预期的一样。
JSON 0x800394cc 0x8003950d 66 g task_app1 pfls0 .CPU0.text .text.task_app1 outputobjssrcCpu0_Main.o 0x8003950e 0x8003953f 50 g task_app2 pfls0 .CPU0.text .text.task_app2
|
我们也通过hightec 编译器提供的工具来读一下elf文件,看一下两个 任务的 符号
检查elf文件
JSON tricore-readelf.exe .tc397_rtos.elf -s | grep task_* 369: 00000000 0 FILE LOCAL DEFAULT ABS tasks.c 794: 800394cc 66 FUNC GLOBAL DEFAULT 45 task_app1 5511: 8003950e 50 FUNC GLOBAL DEFAULT 45 task_app2
|
测试
直接调试器看一下 任务里面的全局变量是不是预想的自增。并且有大概四倍的关系。
测试通过。
原文始发于微信公众号(汽车与基础软件):Tc397 + hightec 编译器 移植 freertos