感芯科技
直播中

jf_1137202360

7年用户 1340经验值
擅长:嵌入式技术
私信 关注
[经验]

【感芯科技64线程MC3172开发板免费试用体验】这个芯片不一样,硬件多线程MCU体验

前言

对于RTOS开发我们知道,线程调度不能太频繁,因为软件进行上下文切换需要时间,调度太频繁则调度时间占的比例会越来越大,实际运行用户代码的时间就会很少效率降低。可不可以减少这个调度时间呢,可以,感芯的MC3172就用硬件去实现了这个上下文切换个调度,使得实时性更高,用户也不需要考虑这个软件调度切换花掉的时间了。

MC3172这个芯片是一个非常大的创新,我们下面实际来体验下。

资料

http://www.gxchip.cn/down/show-70.html

https://whycan.com/f_57.html

https://gitee.com/gxchip

概述

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资料合集_v1.12.zip

分析

配置

时钟源

MC3172 拥有4 个时钟源,

  • 0 内嵌 200MHz 高速RC 振荡器 默认配置。
  • 1 内嵌 8MHz 低速RC 振荡器
  • 2 外部支持 4MHz~40MHz 高速振荡器(无源)
  • 3 外部支持最高133MHz 输入时钟(有源)

线程配置工具_V1.exe工具中如下
image.png

对应的代码位于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工具中如下
image.png

对应的代码位于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**

程序/DATA空间

128KB RAM可以划分3种配置

线程配置工具_V1.exe工具中如下
image.png

对应的代码位于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工具中如下
image.png

对应的代码位于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.12\MC3172_Template\MC3172.wvproj

打开工程浏览器
image.png

右键点击

GPIO_GPCOM_TIMER_Example.c->Properties

按如下操作,将该文件添加到编译。
image.png

取消

thread0_main~thread4_main相关代码注释

菜单栏

Project->Build ALL

生成进行镜像位于

\MC3172资料合集_v1.12\MC3172_Template\Release\

双击该目录下开发板程序下载_v1.1.exe

按如下单次下载运行
image.png

image.png

上述下载到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 \

                      ));

image.png

image.png

按如下接上串口线,使用串口调试助手设置为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;

     }

}

image.png

image.png

总结

  1. 打开线程配置工具_V1.exe时能自动加载当前设置,这样方便做修改,而不需要全部从头配置。

  2. 64个线程共享主频,所以实际并不是并行运行,而是硬件进行分时轮流执行,也就是并没有64个运行环境(寄存器组等),实际是硬件完成了RTOS线程切换的上下文切换等动作,各线程执行的时间占比根据线程主频控制器设置的共享频率进行分配。硬件完成上下文切换时间损失很小,这样在高调度频率的情况就避免了上下文切换时间占比较大导致的效率低的问题,这样比RTOS软件实现实时性效率更高。

  3. 外设模块能提供更详细的编程手册更好。

  4. 总结下从开发环境,配置工具等来看还是比较容易入手的,尤其是硬件实现线程切换调度,减少了RTOS移植,上下文调度切换的时间考虑等问题,编程更简单,使得开发板都效率都更高,运行的实时性也更高。

回帖(1)

华仔stm32

2022-9-19 07:38:49
从开发环境,配置工具等来看还是比较容易入手的,尤其是硬件实现线程切换调度,减少了RTOS移植,上下文调度切换的时间考虑等问题,编程更简单,使得开发板都效率都更高,运行的实时性也更高。
点评非常到位!
举报

更多回帖

发帖
×
20
完善资料,
赚取积分