对于RTOS开发我们知道,线程调度不能太频繁,因为软件进行上下文切换需要时间,调度太频繁则调度时间占的比例会越来越大,实际运行用户代码的时间就会很少效率降低。可不可以减少这个调度时间呢,可以,感芯的MC3172就用硬件去实现了这个上下文切换个调度,使得实时性更高,用户也不需要考虑这个软件调度切换花掉的时间了。
MC3172这个芯片是一个非常大的创新,我们下面实际来体验下。
http://www.gxchip.cn/down/show-70.html
https://whycan.com/f_57.html
CPU:MC3172,32 位RISC 处理器,个64线程同步并行运行。最高200MHz主频,3.37coremark/MHz。
数据段与代码段共享128K字节SRAM可配置为
96KCODE+32KDATA
64KCODE+64KDATA
32KCODE+96KDATA
在http://www.mounriver.com/download
下载MounRiver_Studio_Setup_V181.zip
安装没有什么特别的,过程略。
在http://www.gxchip.cn/down/show-70.html下下载
MC3172 拥有4 个时钟源,
线程配置工具_V1.exe工具中如下
对应的代码位于thread_start.c
**#if** ROTHD_COLCK_SOURCE_SEL==0
(*(**volatile** u32*)(0x50050108))=0x00200003;
(*(**volatile** u32*)(0x50050108))=0x00200007;
(*(**volatile** u32*)(0x50050108))=0x0020000f;
(*(**volatile** u32*)(0x50050108))=0x0020010f;
**#endif**
**#if** ROTHD_COLCK_SOURCE_SEL==1
(*(**volatile** u32*)(0x50050108))=0x00300003;
(*(**volatile** u32*)(0x50050108))=0x00300007;
(*(**volatile** u32*)(0x50050108))=0x0030000f;
(*(**volatile** u32*)(0x50050108))=0x0030010f;
**#endif**
**#if** ROTHD_COLCK_SOURCE_SEL==2
(*(**volatile** u32*)(0x50050088))=0x1d00;
(*(**volatile** u32*)(0x50050090))=0xff000000;
(*(**volatile** u32*)(0x50050098))=0x00000000;
(*(**volatile** u32*)(0x50050108))=0x00000003;
(*(**volatile** u32*)(0x50050108))=0x00000007;
(*(**volatile** u32*)(0x50050108))=0x0000000f;
(*(**volatile** u32*)(0x50050108))=0x0000010f;
**for** (u8 var = 0; var < 64; ++ var)
{
(*(**volatile** u32*)(0x50050090))=0xff000000+var;
(*(**volatile** u32*)(0x50050098))=0x00000001;
(*(**volatile** u32*)(0x50050098))=0x00000203;
**while** ((((*(**volatile** u8*)(0x500500ca)))&0x80)==0);
**while** ((((*(**volatile** u8*)(0x500500ca)))&0x80)!=0);
**if** ((((*(**volatile** u16*)(0x500500c8))))>16380){ **break** ;}
}
**#endif**
**#if** ROTHD_COLCK_SOURCE_SEL==3
(*(**volatile** u32*)(0x50050108))=0x00100003;
(*(**volatile** u32*)(0x50050108))=0x00100007;
(*(**volatile** u32*)(0x50050108))=0x0010000f;
(*(**volatile** u32*)(0x50050108))=0x0010010f;
**#endif**
64 个线程每个线程最高可以运行在主频的1/4,最低是主频的1/1024,
不使用的线程可设置为空闲,空闲线程完全不运行,也不产生功耗。64 个线程分属4 个线
程组,每个线程组的最高主频份额不能超过主频的1/4。
实际上可以理解为64个线程去共享主频分时执行。
线程配置工具_V1.exe工具中如下
对应的代码位于thread_start.c
**#ifdef** ROTHD_THREAD1_VALID
*( **int** *)(THD_TS_ADDR+8)=ROTHD_THREAD1_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD2_VALID
*( **int** *)(THD_TS_ADDR+16)=ROTHD_THREAD2_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD3_VALID
*( **int** *)(THD_TS_ADDR+24)=ROTHD_THREAD3_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD5_VALID
*( **int** *)(THD_TS_ADDR+40)=ROTHD_THREAD5_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD6_VALID
*( **int** *)(THD_TS_ADDR+48)=ROTHD_THREAD6_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD7_VALID
*( **int** *)(THD_TS_ADDR+56)=ROTHD_THREAD7_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD9_VALID
*( **int** *)(THD_TS_ADDR+72)=ROTHD_THREAD9_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD10_VALID
*( **int** *)(THD_TS_ADDR+80)=ROTHD_THREAD10_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD11_VALID
*( **int** *)(THD_TS_ADDR+88)=ROTHD_THREAD11_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD13_VALID
*( **int** *)(THD_TS_ADDR+104)=ROTHD_THREAD13_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD14_VALID
*( **int** *)(THD_TS_ADDR+112)=ROTHD_THREAD14_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD15_VALID
*( **int** *)(THD_TS_ADDR+120)=ROTHD_THREAD15_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD17_VALID
*( **int** *)(THD_TS_ADDR+136)=ROTHD_THREAD17_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD18_VALID
*( **int** *)(THD_TS_ADDR+144)=ROTHD_THREAD18_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD19_VALID
*( **int** *)(THD_TS_ADDR+152)=ROTHD_THREAD19_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD21_VALID
*( **int** *)(THD_TS_ADDR+168)=ROTHD_THREAD21_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD22_VALID
*( **int** *)(THD_TS_ADDR+176)=ROTHD_THREAD22_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD23_VALID
*( **int** *)(THD_TS_ADDR+184)=ROTHD_THREAD23_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD25_VALID
*( **int** *)(THD_TS_ADDR+200)=ROTHD_THREAD25_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD26_VALID
*( **int** *)(THD_TS_ADDR+208)=ROTHD_THREAD26_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD27_VALID
*( **int** *)(THD_TS_ADDR+216)=ROTHD_THREAD27_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD29_VALID
*( **int** *)(THD_TS_ADDR+232)=ROTHD_THREAD29_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD30_VALID
*( **int** *)(THD_TS_ADDR+240)=ROTHD_THREAD30_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD31_VALID
*( **int** *)(THD_TS_ADDR+248)=ROTHD_THREAD31_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD33_VALID
*( **int** *)(THD_TS_ADDR+264)=ROTHD_THREAD33_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD34_VALID
*( **int** *)(THD_TS_ADDR+272)=ROTHD_THREAD34_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD35_VALID
*( **int** *)(THD_TS_ADDR+280)=ROTHD_THREAD35_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD37_VALID
*( **int** *)(THD_TS_ADDR+296)=ROTHD_THREAD37_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD38_VALID
*( **int** *)(THD_TS_ADDR+304)=ROTHD_THREAD38_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD39_VALID
*( **int** *)(THD_TS_ADDR+312)=ROTHD_THREAD39_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD41_VALID
*( **int** *)(THD_TS_ADDR+328)=ROTHD_THREAD41_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD42_VALID
*( **int** *)(THD_TS_ADDR+336)=ROTHD_THREAD42_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD43_VALID
*( **int** *)(THD_TS_ADDR+344)=ROTHD_THREAD43_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD45_VALID
*( **int** *)(THD_TS_ADDR+360)=ROTHD_THREAD45_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD46_VALID
*( **int** *)(THD_TS_ADDR+368)=ROTHD_THREAD46_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD47_VALID
*( **int** *)(THD_TS_ADDR+376)=ROTHD_THREAD47_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD49_VALID
*( **int** *)(THD_TS_ADDR+392)=ROTHD_THREAD49_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD50_VALID
*( **int** *)(THD_TS_ADDR+400)=ROTHD_THREAD50_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD51_VALID
*( **int** *)(THD_TS_ADDR+408)=ROTHD_THREAD51_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD53_VALID
*( **int** *)(THD_TS_ADDR+424)=ROTHD_THREAD53_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD54_VALID
*( **int** *)(THD_TS_ADDR+432)=ROTHD_THREAD54_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD55_VALID
*( **int** *)(THD_TS_ADDR+440)=ROTHD_THREAD55_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD57_VALID
*( **int** *)(THD_TS_ADDR+456)=ROTHD_THREAD57_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD58_VALID
*( **int** *)(THD_TS_ADDR+464)=ROTHD_THREAD58_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD59_VALID
*( **int** *)(THD_TS_ADDR+472)=ROTHD_THREAD59_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD61_VALID
*( **int** *)(THD_TS_ADDR+488)=ROTHD_THREAD61_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD62_VALID
*( **int** *)(THD_TS_ADDR+496)=ROTHD_THREAD62_FREQCFG_VALUE;
**#endif**
**#ifdef** ROTHD_THREAD63_VALID
*( **int** *)(THD_TS_ADDR+504)=ROTHD_THREAD63_FREQCFG_VALUE;
**#endif**
128KB RAM可以划分3种配置
线程配置工具_V1.exe工具中如下
对应的代码位于thread_start.c
实际就是写DATA_RAM_RATIO寄存器,用于指定DATA的大小(32KBx1,32KBx2,32KBx3),
剩下的就是CODE区域。
**#if** ROTHD_DATA_RAM_VALUE==32768*3
DATA_RAM_RATIO=0x60;
**#endif**
**#if** ROTHD_DATA_RAM_VALUE==32768*2
DATA_RAM_RATIO=0x40;
**#endif**
**#if** ROTHD_DATA_RAM_VALUE==32768
DATA_RAM_RATIO=0x20;
**#endif**
程序/DATA空间分配好后,再从DATA空间中分配栈空间
64 个线程,每个线程都有自己独立的栈空间,且在数据空间允许的范
围内随意分配,只要所有非空闲线程的栈空间总和不超过数据空间的大小即可(数据空间有
192 字节的保留区不可使用),栈空间大小需要是4 字节的整数倍
线程配置工具_V1.exe工具中如下
对应的代码位于thread_start.c
比如线程1,通过rothd_set_sp_const设置SP寄存器
**void** **thread1_initial** ( **void** )
{
**#ifdef** ROTHD_THREAD1_VALID
**extern** **void** **thread1_main** ( **void** );
rothd_set_sp_const(ROTHD_THREAD1_STACKCFG_VALUE|0x20000000);
thread1_main();
**#endif**
}
其他线程类似
各个线程之间共享全局变量实现通讯
看程序的运行过程一般都是从连接脚本入手,先找入口点
再看MEMMAP等。
MC3172.lds
可以看到入口点
ENTRY(thread_start)
空间分配
MEMORY
{
CODE_SPACE (x) : ORIGIN = 0x00000010, LENGTH = 65520
DATA_SPACE (rw) : ORIGIN = 0x20000010, LENGTH = 48672
}
以下可以看到.text.thread_start放在了CODE_SPACE的开始处。后续就是DATA区域分配。所以最开始就是执行thread_start函数。
. = 0x00000010;
.text :
{
*(.text.thread_start)
*(.text)
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
}
thread_start ->
(*thread_initial_pointer[THREAD_ID])()
其中THREAD_ID寄存器指定一个运行的线程,一般是0。
则执行
thread0_initial->
进行时钟源配置,CODE/DATA区域配置,线程频率分配。
thread0_main->
其他线程运行
通过thread_initial_pointer查找到初始化函数
threadx_initial
进行栈配置然后
threadx_main运行
双击打开
MC3172资料合集_v1.12MC3172_TemplateMC3172.wvproj
打开工程浏览器
右键点击
GPIO_GPCOM_TIMER_Example.c->Properties
按如下操作,将该文件添加到编译。
取消
thread0_main~thread4_main相关代码注释
菜单栏
Project->Build ALL
生成进行镜像位于
MC3172资料合集_v1.12MC3172_TemplateRelease
双击该目录下开发板程序下载_v1.1.exe
按如下单次下载运行
上述下载到RAM中掉电不保存,也可以点击烧录固件到这样可以掉电保存。
从以下可以看出GPCOM8 P2:RXD PC2 GPCOM8 P3:TXD PC3 波特率115200
GPCOM_UART_EXAMPLE(GPCOM8_BASE_ADDR);
GPCOM_SET_IN_PORT(gpcom_sel,(GPCOM_RXD_IS_P2));
GPCOM_SET_OUT_PORT(gpcom_sel,(
GPCOM_P0_OUTPUT_DISABLE|GPCOM_P3_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_DISABLE|GPCOM_P1_OUTPUT_DISABLE|
GPCOM_P0_IS_HIGH |GPCOM_P3_IS_TXD |GPCOM_P2_IS_HIGH |GPCOM_P1_IS_HIGH
));
按如下接上串口线,使用串口调试助手设置为115200-8-n-1, 上位机发送数据开发板收到后原样返回。
对应代码如下
u8 rx_data_rp=0;
u8 rx_data=0;
rx_data_rp=GPCOM_GET_RX_WP(gpcom_sel);
while (1) {
if (rx_data_rp!=(GPCOM_GET_RX_WP(gpcom_sel))){
rx_data=GPCOM_GET_RX_DATA(gpcom_sel,rx_data_rp);
GPCOM_PUSH_TX_DATA(gpcom_sel,rx_data);
rx_data_rp++;
rx_data_rp&=0x0f;
}
}
打开线程配置工具_V1.exe时能自动加载当前设置,这样方便做修改,而不需要全部从头配置。
64个线程共享主频,所以实际并不是并行运行,而是硬件进行分时轮流执行,也就是并没有64个运行环境(寄存器组等),实际是硬件完成了RTOS线程切换的上下文切换等动作,各线程执行的时间占比根据线程主频控制器设置的共享频率进行分配。硬件完成上下文切换时间损失很小,这样在高调度频率的情况就避免了上下文切换时间占比较大导致的效率低的问题,这样比RTOS软件实现实时性效率更高。
外设模块能提供更详细的编程手册更好。
总结下从开发环境,配置工具等来看还是比较容易入手的,尤其是硬件实现线程切换调度,减少了RTOS移植,上下文调度切换的时间考虑等问题,编程更简单,使得开发板都效率都更高,运行的实时性也更高。
全部0条评论
快来发表一下你的评论吧 !