|
汽车零部件采购、销售通信录 填写你的培训需求,我们帮你找 招募汽车专业培训老师
前言: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
|
[img=552.010009765625,293.0]https://mmbiz.qpic.cn/sz_mmbiz_png/Vgbm9ibpszia6rNxWeib28VMFh0GicWKgicwCibUw5muiclDu7wJ0ehTHC1IYRR3K2cKzvJZaxALBUFM5LVB1wnLHhmTQ/640?wx_fmt=png[/img]
本文介绍 基于 硬件 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 新建一个项目即可。
[img=94.0,81.0]https://mmbiz.qpic.cn/sz_mmbiz_png/Vgbm9ibpszia6rNxWeib28VMFh0GicWKgicwCOorI1UQ7UeE0emBib3F45tQz8Yick7OiaSvjXtoFTiaia9OfEmw3qjKibIiag/640?wx_fmt=png[/img]
确保 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
这两个文件是需要开发者主要关注的文件。里面包含了大量的针对板子的移植工作。
比如根据芯片手册查到的 寄存器地址
寄存器地址
[img=552.010009765625,415.0100402832031]https://mmbiz.qpic.cn/sz_mmbiz_png/Vgbm9ibpszia6rNxWeib28VMFh0GicWKgicwC33wuBvVbStP0WrKGl60uiaibsNqV4GEeL6dZiczmO98eyadzP76NImgxQ/640?wx_fmt=png[/img]
截图出自 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\",@progbits\n"
" .align 5\n"
" __intvec_entry_" XSTR( configCONTEXT_INTERRUPT_PRIORITY ) ":\n"
" svlcx\n"
" movh.a %a14, hi:vPortSystemContextHandler\n"
" lea %a14, [%a14]lo:vPortSystemContextHandler\n"
" ji %a14\n"
" .org 32\n"
" .popsection\n" );
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\",@progbits\n"
" .align 5\n"
" __intvec_entry_" XSTR( configTIMER_INTERRUPT_PRIORITY ) ":\n"
" svlcx\n"
" movh.a %a14, hi:vPortSystemTickHandler\n"
" lea %a14, [%a14]lo:vPortSystemTickHandler\n"
" ji %a14\n"
" .org 32\n"
" .popsection\n" );
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, 4\n"
"sh %0, %0, 28\n"
"insert %0, %0, %1 6, 16\n" : "+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 output\objs\src\Cpu0_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
|
测试
直接调试器看一下 任务里面的全局变量是不是预想的自增。并且有大概四倍的关系。
测试通过。
[img=552.010009765625,122.0]https://mmbiz.qpic.cn/sz_mmbiz_png/Vgbm9ibpszia6rNxWeib28VMFh0GicWKgicwCnONSlPukeybwJyZG9S9U8duoibv3Potw20YZF8ibT3NJS5aunmSUsq2g/640?wx_fmt=png[/img] |
|