本文记录了我对 TriCore 上下文切换运行机制的理解以及 TriCore 内核移植 API 的注释分析。由于水平有限,再加上阅读的手册都是英文的,所以理解上如果存在偏差是难免的,还请公众号留言指正!(事实上,写完这篇文章后,发现 TriCore 架构还有很多可以发掘的点,等有时间再去验证心中所想吧。。。)
 
简介
TriCore 提供了一种硬件的上下文机制,这种机制是专为嵌入式实时操作系统设计的,他的目的就是为了能提高线程切换的效率。
 
编译环境
我使用的是 windows 平台下的 AURIX-Studio IDE , AURIX-Studio IDE 是英飞凌公司开发的基于 eclipse 的集成开发环境。使用的工具链是 altium 的 TASKING 工具链。在整个移植过程中几乎没有写汇编代码,用的一些函数,都是工具链提供的。
比如:

写一个寄存器

__mtcr(CPU_PSW, 0x00000980);    /* 对应的汇编指令就是:mtcr */

读一个寄存器

icr.U = __mfcr(CPU_ICR);    /* 对应的汇编指令就是:mfcr */

RT-Thread CPU 架构级别的移植 API 列表
需要移植的 API 接口说明,在 RT-Thread 文档中心《内核移植》章节里已经讲的很详细了,需要了解详情的可以去以下链接查看!

[链接]https://www.rt-thread.org/document/site/programming-manual/porting/porting/ 
以下是需要实现的函数和变量的列表:(向????滑动查看全部)

TriCore 移植代码注释分析
一. 关闭全局中断 rt_hw_interrupt_disable()

IFX_INLINE boolean IfxCpu_disableInterrupts(void)
{   boolean enabled;   enabled = IfxCpu_areInterruptsEnabled();   __disable();   __nop();   return enabled;
}/** rt_base_t rt_hw_interrupt_disable(void);*/
rt_base_t rt_hw_interrupt_disable(void)
{rt_base_t level;level = IfxCpu_disableInterrupts();return level;
}

__disable(); 是工具链直接提供的函数。这个函数的作用就是关闭芯片全局中断。

单步调试可以看到这条函数究竟做了什么:

由上图可以看到,当执行完 diable 指令后,TriCore 的 ICR 寄存器的 IE 位由 1 变为 0 了。IE 位的功能说明如下图所示:


IE 位是一个全局的中断使能位,当进入中断时,IE 位会自动的置为 0,当中断服务函数执行 rfe 指令后,会自动的恢复进中断前的值。另外, IE 位的值还可以被  enable; disable; mtcr; bisr; 等指令更新。
二. 打开全局中断 rt_hw_interrupt_enable()

IFX_INLINE void IfxCpu_restoreInterrupts(boolean enabled)
{if (enabled != FALSE){__enable();}
}IFX_INLINE void restoreInterrupts(boolean enabled)
{IfxCpu_restoreInterrupts(enabled);
}/** void rt_hw_interrupt_enable(rt_base_t level);*/
void rt_hw_interrupt_enable(rt_base_t level)
{restoreInterrupts((boolean)level);
}

有了前面关闭全局中断的分析,这里打开全局中断的就不再赘述了。rt_hw_interrupt_enable 最终执行的有效函数就是 __enable(); ,这个函数对应的 CPU 指令就是 enable ,这条指令的作用就是将 TriCore 的 ICR 寄存器的 IE 位置为 1 。

三. 实现线程栈初始化 rt_hw_stack_init()
在理解 rt_hw_stack_init 这个函数前,我需要先向大家介绍下 TriCore 的硬件上下文机制。
1. 上下文类型区分

如上图所示 TriCore 内核的上下文分为两组:
上层上下文:外部中断;陷阱;函数调用;会自动保存恢复上下文。
下层上下文:如果要改变内容,需要显式的使用代码指令去读写寄存器。
下层上下文寄存器类似于全局寄存器。在 外部中断;陷阱;函数调用;中对这些寄存器做修改后,事件返回后值仍然会存在。这一特性意味着:

函数可以通过下层上下文寄存器传递参数,传递返回值。
中断与陷阱处理函数在使用下层上下文寄存器之前必须保存下层上下文寄存器的值,结束处理后,需要恢复下层上下文寄存器。
什么时候会保存恢复上下文:

2. 上下文保存空间 CSA ( Context Save Area )
TriCore 架构使用固定大小的上下文保存区域的链表。一个 CSA 存储 16 个 word ,按 16 个 word 的边界对齐,每个 CSA 正好可以完整的存储一个上层上下文或者下层上下文。CSAs 通过一个 LinkWord 链接在一起。LinkWord 的内容通过如下图所示的关系可以转换成一个有效的 CSA 地址。


3. 如何访问 CSA
TriCore 内核有三个寄存器用来操作访问 CSA 的信息:
FCX:这个寄存器的值用来指向全局的空闲的 CSA 列表头指针地址信息。(总是指向可用的 CSA 列表头指针)
PCX:这个寄存器的值用来保存前一个任务的 CSA 列表头指针地址信息。PCX 同时也是 PCXI 寄存器的一部分。
LCX:如果 LCX 寄存器的值与 PCX 的值一致,那么就会触发一个  FCD 陷阱。(FCD:空闲上下文空间消耗殆尽的异常)
4. rt_hw_stack_init() 的实现
rt_hw_stack_init 的实现如下:

rt_uint8_t *rt_hw_stack_init(void *tentry,void *parameter,rt_uint8_t *stack_addr,  void*texit)
{unsigned long *lower_csa = NULL;unsigned long *upper_csa = NULL;rt_hw_interrupt_disable();{/* DSync to ensure that buffering is not a problem. */__dsync();/* 通过 FCX 寄存器获取一个空闲的 CSA 作为下层上下文。*/lower_csa = LINKWORD_TO_ADDRESS( __mfcr( CPU_FCX ) );if( NULL != lower_csa ){/* 通过下层上下文的保存的 LinkWord 获取到上层上下文。*/upper_csa = LINKWORD_TO_ADDRESS( lower_csa[ 0 ] );}/* Check that we have successfully reserved two CSAs. */if( ( NULL != lower_csa ) && ( NULL != upper_csa ) ){__disable();__dsync();/* 由于已经使用掉了两个 CSA ,这里需要手动更新 FCX 寄存器的值为下一个空闲的 CSA 。*/__mtcr( CPU_FCX, upper_csa[ 0 ] );__isync();__enable();}else{/* Simply trigger a context list depletion trap. */__svlcx();}}rt_hw_interrupt_enable((rt_base_t)RT_NULL);/* 将获取到的上层 CSA 空间全部清零。*/memset( upper_csa, 0, TRICORE_NUM_WORDS_IN_CSA * sizeof( unsigned long ) );/* Upper Context. */upper_csa[ 2 ] = ( unsigned long )stack_addr;/* 对应 A10 寄存器; 这个寄存器用于保存栈指针 */upper_csa[ 1 ] = TRICORE_SYSTEM_PROGRAM_STATUS_WORD;/* 对应 PSW 寄存器;这个寄存器用于保存当前线程的初始状态*//* 将获取到的下层 CSA 空间全部清零。*/memset( lower_csa, 0, TRICORE_NUM_WORDS_IN_CSA * sizeof( unsigned long ) );/* Lower Context. */lower_csa[ 8 ] = ( unsigned long ) parameter;/* 对应 A4 寄存器; 用于保存函数的入参。*/lower_csa[ 1 ] = ( unsigned long ) tentry;/* 对应 A11 寄存器; 用于保存 PC 指针。*//* 将上层上下文的地址保存到下层上下文的 LinkWord 中。*/lower_csa[ 0 ] = ( TRICORE_INITIAL_PCXI_UPPER_CONTEXT_WORD | ( unsigned long ) ADDRESS_TO_LINKWORD( upper_csa ) );/* Save the link to the CSA in the top of stack. */stack_addr = (unsigned long * ) ADDRESS_TO_LINKWORD( lower_csa );/* DSync to ensure that buffering is not a problem. */__dsync();/* !!!注意:这里返回的其实已经不是栈地址了,而是下层 CSA 的 LinkWord 值。*/return stack_addr;
}

四. 实现上下文切换
RT-Thread 的 libcpu 抽象层需要实现以下三个线程切换相关的函数:1) rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候被调用。2) rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。3) rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。
实现 rt_hw_context_switch_to()

void rt_hw_context_switch_to(rt_ubase_t to)
{unsigned long ulMFCR = 0UL;unsigned long *lower_csa = NULL; unsigned long *upper_csa = NULL;/* Set-up the timer interrupt. */rt_hw_systick_init();/* 初始化通用目的服务请求寄存器 */IfxSrc_init(GPSR[TRICORE_CPU_ID], (IfxSrc_Tos)TRICORE_CPU_ID, 1);IfxSrc_enable(GPSR[TRICORE_CPU_ID]);__disable();/* 初始化系统配置寄存器为初值 */__mtcr( CPU_SYSCON, TRICORE_INITIAL_SYSCON );__isync();/* 确保 PSW 寄存器的值是正确的 */ulMFCR = __mfcr( CPU_PSW );ulMFCR &= TRICORE_RESTORE_PSW_MASK;__dsync();__mtcr( CPU_PSW, ulMFCR );__isync();__dsync();/* 将 to 线程的 LinkWord 赋值给 PCXI __mtcr(CPU_PCXI, (unsigned long)( *( (unsigned long *)to ) ) );__isync();__nop();__rslcx();__nop();/* 当执行 fre 指令后,CPU 的线程会恢复成 PCXI 寄存器指向的 CSA 内容 */__asm volatile( "rfe" );/* Will not get here. */
}

实现 rt_hw_context_switch()
在 TriCore 的移植实现中,这个函数 rt_hw_context_switch 并不会直接切换线程而是将当前线程的 CSA LinkWord 也就是 from 先保存到一个全局变量当中;要切换的线程的 CSA LinkWord 也就是 to 同样保存到一个全局变量中。

void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to)
{rt_base_t level;level = rt_hw_interrupt_disable();current_thread = from;to_thread = to;rt_hw_interrupt_enable(level);/* 在保存完线程 CSA LinkWord 后,判断这次线程切换是否是 systick 触发的线程切换。*/if(systick_flag == 0){/* 如果不是 systick 引发的线程切换,那么就触发一个线程切换的异常函数,这个异常函数里会真正做线程切换的事情。*/__syscall( 0 );}
}

这个函数不会直接切换线程,真正切换线程的函数是在 trigger_scheduling() 中。

实现 rt_hw_context_switch_interrupt()
同样,在 TriCore 的移植实现中,这个函数 rt_hw_context_switch_interrupt 并不会直接切换线程而是将当前线程的 CSA LinkWord 也就是 from 先保存到一个全局变量当中;要切换的线程的 CSA LinkWord 也就是 to 同样保存到一个全局变量中。

void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to)
{rt_base_t level;level = rt_hw_interrupt_disable();current_thread = from;to_thread = to;rt_hw_interrupt_enable(level);/* 在保存完线程 CSA LinkWord 后,会给 SETR 位置 1,这样会触发一个异常陷阱函数。在这个陷阱函数里才会去真正的切换线程。*/GPSR[TRICORE_CPU_ID]->B.SETR = 1;__isync();
}

统一的触发线程切换的函数 trigger_scheduling()

inline void trigger_scheduling(void)
{rt_ubase_t from, to;rt_base_t level;unsigned long *current_upper_csa = NULL;/* 获取 from 和 to 线程的 CSA 信息。*/level = rt_hw_interrupt_disable();from = current_thread;to = to_thread;to_thread = RT_NULL;rt_hw_interrupt_enable(level);/* 判断是否需要切换到 to 线程 */if( to ){__disable(); __dsync();/* 获取当前线程的 CSA LinkWord */current_upper_csa = LINKWORD_TO_ADDRESS( __mfcr( CPU_PCXI ) );/* 保存当前线程的 CSA 上下文 */*( (unsigned long *)from ) = current_upper_csa[ 0 ];/* 将 to 线程的 CSA 地址赋值给当前线程的上层上下文的 LinkWord ,用于 TriCore 自动切换线程。*/current_upper_csa[ 0 ] = *( (unsigned long *)to );GPSR[TRICORE_CPU_ID]->B.SETR = 0;__isync();}
}

trigger_scheduling()  分别在三个地方使用,而且必须使用内联的方式!

首先系统滴答时钟:

__attribute__((noinline)) static void tricore_systick_handler( void )
{rt_base_t level;IfxStm_Timer_acknowledgeTimerIrq(&tricore_timers[TRICORE_CPU_ID]);level = rt_hw_interrupt_disable();systick_flag = 1;rt_tick_increase();systick_flag = 0;rt_hw_interrupt_enable(level);trigger_scheduling();
}

其次是 __syscall( 0 ); 触发的陷阱异常函数里:

void tricore_trap_yield_for_task( int iTrapIdentification )
{switch( iTrapIdentification ){case 0: trigger_scheduling();break;default:/* Unimplemented trap called. */break;}
}

最后是 GPSR[TRICORE_CPU_ID]->B.SETR = 1; 触发的中断环境下的线程切换:

__attribute__((noinline)) static void tricore_yield_for_interrupt( void )
{trigger_scheduling();
}IFX_INTERRUPT(KERNEL_YIELD, 0, 2)
{tricore_yield_for_interrupt();
}

五. 最后就是实现系统时钟节拍

void rt_hw_systick_init( void )
{IfxStm_Timer_Config timer_config;IfxStm_Timer_initConfig(&timer_config, STMs[TRICORE_CPU_ID]);timer_config.base.frequency = RT_TICK_PER_SECOND;timer_config.base.isrPriority = 1;IfxStm_Timer_init(&tricore_timers[TRICORE_CPU_ID], &timer_config);IfxStm_Timer_run(&tricore_timers[TRICORE_CPU_ID]);}

系统时钟节拍没啥好说的,就不在赘述了。
写在最后:
我的实现并不是最优解,这里还有很多值得探讨的。但是业余对 TriCore 的研究学习就暂时告一段落了,后面我还是会把业余精力放回 RISC-V 的学习上。毕竟我觉得 RISC-V 架构才是国产物联网芯片的未来。TriCore 架构的生态有点封闭,目前只适合汽车电子芯片上。
-----------------------------------
TriCore 与 RT-Thread(TC264 移植)
https://blog.51cto.com/u_15288275/2969837

TriCore 与 RT-Thread(TC264 移植)相关推荐

  1. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

  2. 正点原子delay函数移植到rt thread操作系统(HAL库)

    正点原子教程中涉及到的操作系统只涉及了UCOS的教程,其中例程的system文件夹中的delay.c函数只是适配了UCOS. 下面将delay.c函数移植到rt thread中,使用的bsp是rt t ...

  3. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  4. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  5. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  6. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  7. RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程

    (1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...

  8. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  9. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

最新文章

  1. 利用CMake编译内核模块
  2. Java程序员必看!java值类型和引用类型的区别
  3. hadoop: hdfs API示例
  4. hadoop 计数器
  5. 【Python学习】 - TensorFlow.keras 不显示epochs进度条的方法
  6. 操作系统—基本知识(细致版)
  7. 计算机字体库位置,电脑cad软件字体库的路径怎么添加
  8. 聚类算法-k均值聚类(K-means)
  9. win10锁屏时间太短就关闭屏幕
  10. java使用反射生成JDK动态代理
  11. img图片加载失败的处理
  12. 鸡啄米:C++编程入门系列之目录和总结http://www.jizhuomi.com/software/129.html
  13. 用12星座看人的性格准吗?
  14. [画板]画PCB和玩植物大战僵尸
  15. 地图服务 WMS WFS WCS TMS
  16. 如何清洁你脏兮兮的笔记本电脑?
  17. custom的短语_custom到底是什么意思?
  18. position:absolute与position:relative的区别
  19. du命令 – 查看空间
  20. 【Educoder离散数学实训】关系基础

热门文章

  1. 烧洋芋、苞谷、饵块和昭通酱
  2. 【转】《仙剑奇侠传6》将更换全新引擎
  3. PDF文件旋转最好用的方法
  4. 论文笔记:3D-CVF(ECCV 2020)
  5. Android APP在线自动更新安装
  6. python星空画法教程,PS后期打造一张惊艳的星空风景照片后期调色教程
  7. matlab 分水岭法,分水岭算法Matlab实现——三种方法 | 学步园
  8. 【强烈推荐一款吊炸天的 Kafka 图形化工具 Eagle】
  9. 嵌入式linux 使用ntpdate命令同步时间
  10. 在linux上安装微信