嵌入式并行多线程处理器,了解一下!

27 篇文章 52 订阅
5 篇文章 0 订阅

大家好,我是杂烩君。

最近,朋友送了块小板子,板子上的MCU是个很有意思的东西——并行多线程处理器MC3172 。

通俗地说,这颗MCU的内部实现了类似RTOS多线程的功能。但是MC3172 编程与RTOS编程的最大区别就是:

  • MC3172多线程绝对并行运行,没有切换抖动及开销。

  • MC3172无线程优先级、优先级反转、死锁等概念。

  • MC3172所有中断都可以安排专门线程处理,没有中断嵌套和延迟。

  • MC3172各线程同步并行运行,互不阻塞,互不干扰。

  • MC3172线程响应的确定性相对于RTOS更为精确。

MC3172简介

MC3172 是厦门感芯科技的一款32 位 RISC并行多线程实时处理器。基于RISC-V RV32IMC 指令集, 100%单周期指令, 最高200MHz主频, 3.37coremark/MHz。可以代替实时操作系统, 实现程序的模块化与复用性。

相关资料可在感芯官网下载。链接:

http://www.gxchip.cn/

MC3172 特性:

920ccfe9c086be117e0730e42812118b.png

MC3172实践

MC3172的开发环境使用的是国产软件——MounRiver Studio。

9a6d6c843cecdd3266f3ecd127401352.png

MounRiver Studio下载链接:

http://www.mounriver.com/download

我们简单看一下MC3172的demo工程:

7efb45069293934978c48f2db6ecfe9d.png

1、MC3172文件夹

MC3172存放MC3172编程核心文件。

线程配置工具可对各线程进行配置:

19dc38d0fc6ebee8cff8808e6902f2dc.png

可以配置线程时钟源、频率、栈空间、存储器分配等信息。

MC3172支持64路线程同步并行运行,  其中分为4个线程组,每个线程组16线程,每个线程组里的线程编号如上图所示。其中,不使用的线程可以设置为空闲线程,空闲线程完全不运行,不产生功耗。

每个线程都有自己独立的栈空间 ,在数据空间允许范围内可随意分配,但需要确保所有非空闲线程所占的数据空间不超过数据空间的大小。

MC3172.h存放外设地址相关宏定义及其配置宏,如:

ebc49b045f14d1cd8f3757f376f7897c.png

类似于ST的stm32fxxx.h。

thread_config.h为线程配置文件,由线程配置工具生成:

834bbfff1fa1425c835788b2321d866b.png

MC3172.lds为链接脚本,由线程配置工具生成

5e95e13a5810be03f1ade3b6b67736fc.png

thread_start.c为启动线程相关的源文件:

#ifndef THREAD_START_C
#define THREAD_START_C
#include "./MC3172.h"
#include "./thread_config.h"

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
}
void thread2_initial(void)
{
#ifdef ROTHD_THREAD2_VALID
extern void thread2_main(void);
    rothd_set_sp_const(ROTHD_THREAD2_STACKCFG_VALUE|0x20000000);
    thread2_main();
#endif
}

// 省略部分代码......
void (*thread_initial_pointer[64]) (void)={
                                               &thread0_initial,
                                               &thread1_initial,
                                               &thread2_initial
// 省略部分代码......
}

void thread_start(void)
{
    (*thread_initial_pointer[THREAD_ID])();
}

程序运行的入口函数为:thread_start ,从链接脚本里可以知道:

edba4f0512752802c57154b03de8099d.png

thread_start里的THREAD_ID为线程ID值,直接从0x50000000地址中读出:

#define THREAD_ID (*(volatile u8*)(0x50000000))

猜测:0x50000000地址里的ID值会不断变化,通过某种机制跳转,遍历执行thread_initial_pointer函数指针数组里的各个线程函数。

threadx_initial里初始化线程栈,并执行线程主体,如

void thread_end(void)
{
    while(1);
}

void thread1_main(void)
{
    while(1){
        //user code section
    }
    thread_end();
}

这是用户代码,我们可以在各个线程主体函数里边编写我们的应用代码。

2、Release文件夹

Release文件夹里存放的是编译生成的固件程序,通过 开发板程序下载工具 可进行下载:

b0071b74eca8916f77da502eeb7904bb.png

3、USER_CODE文件夹

USER_CODE文件夹存放用户代码:

28c1087bdc4f8940a899494e102f7237.png

MC3172 是一颗并行并行多线程实时处理器,我们下面来看看其多线程并行执行的特性。

我们编写两个线程,线程进行相同的配置,两个线程分别对两个IO进行翻转,测试代码如:

void LED0_GPIOA_PIN0_TEST(void)
{
 // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN0引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN0, GPIO_SET_ENABLE);

    while(1)
    {
     // GPIOA PIN0输出1
     GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN0);

     // 延时
        for (u32 var = 0; var < 5000; ++var)
        {
            NOP();
        }

        // GPIOA PIN0输出0
     GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN0);

     // 延时
        for (u32 var = 0; var < 5000; ++var)
        {
            NOP();
        }
    }
}

void LED1_GPIOA_PIN1_TEST(void)
{
 // 启动GPIOA并设置特权组及时钟频率
    INTDEV_SET_CLK_RST(GPIOA_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2));

    // 使能GPIOA PIN1引脚
    GPIO_SET_OUTPUT_EN_VALUE(GPIOA_BASE_ADDR, GPIO_PIN1, GPIO_SET_ENABLE);

    while(1)
    {
     // GPIOA PIN1输出1
     GPIO_SET_OUTPUT_PIN_TO_1(GPIOA_BASE_ADDR, GPIO_PIN1);

     // 延时
        for (u32 var = 0; var < 5000; ++var)
        {
            NOP();
        }

        // GPIOA PIN1输出0
     GPIO_SET_OUTPUT_PIN_TO_0(GPIOA_BASE_ADDR, GPIO_PIN1);

     // 延时
        for (u32 var = 0; var < 5000; ++var)
        {
            NOP();
        }
    }
}




void thread_end(void)
{
    while(1);
}



void thread0_main(void)
{
    while(1){
        //user code section
    }
    thread_end();
}



void thread1_main(void)
{
    while(1){
        //user code section
     LED0_GPIOA_PIN0_TEST();
    }
    thread_end();
}



void thread2_main(void)
{
    while(1){
        //user code section
     LED1_GPIOA_PIN1_TEST();
    }
    thread_end();
}

烧录程序,使用逻辑分析仪抓取GPIOA_PIN0及GPIOA_PIN1引脚电平变化如:

07fb627fc1671b8c848ce6a485e9d054.png

可见,这两个波形是完全同步的,CPU同时在干两件事情,实现了与RTOS多线程同样的效果。

心得与总结

嵌入式开发,是软件+硬件结合,两者互补。如果硬件功能很强大,则软件可能可以设计得比较简单;如果硬件功能有限,则软件方面可能得考虑比较多的方面。

比如:

  • 一些软件算法,需要多传感器数据输入进行融合,则功能实现可能比较简单,但实际可能为了降成本,减少一些传感器,这时候需要实现稳定可靠的功能,则软件算法上得下更大的功夫。

  • 对于一些不太复杂的数字信号处理,在通用的MCU上就可以处理,但对于一些比较复杂的数字信号处理,则可能使用一些带有DSP处理器的MCU。

特别的,对于芯片内部IC电路来说,如果内部有相关模块可以实现某些功能的话,则对应的软件编程会简单很多,而且硬件实现的比软件实现的效率要高。

硬件实现的多线程编程确实优于RTOS编程,但实际开发中产品软硬件架构需要考虑多个方面,比如芯片的稳定性以及软件生态等方面。

并行多线程实时处理器是个好东西,但目前并行多线程实时处理器还处于起步阶段,还有很多东西需要完善,需要我们多支持与传播,只有生态起来了,将来我们才有机会用得上。

以上就是本次的分享,如果觉得文章有帮助,麻烦帮忙转发,谢谢!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
/************************************************ * * The classic producer-consumer example. * Illustrates mutexes and conditions. * by Zou jian guo * 2003-12-22 * *************************************************/ #include #include #include #include "pthread.h" #define BUFFER_SIZE 16 /* Circular buffer of integers. */ struct prodcons { int buffer[BUFFER_SIZE]; /* the actual data */ pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */ int readpos, writepos; /* positions for reading and writing */ pthread_cond_t notempty; /* signaled when buffer is not empty */ pthread_cond_t notfull; /* signaled when buffer is not full */ }; /*--------------------------------------------------------*/ /* Initialize a buffer */ void init(struct prodcons * b) { pthread_mutex_init(&b->lock, NULL); pthread_cond_init(&b->notempty, NULL); pthread_cond_init(&b->notfull, NULL); b->readpos = 0; b->writepos = 0; } /*--------------------------------------------------------*/ /* Store an integer in the buffer */ void put(struct prodcons * b, int data) { pthread_mutex_lock(&b->lock); /* Wait until buffer is not full */ while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) { printf("wait for not full\n"); pthread_cond_wait(&b->notfull, &b->lock); } /* Write the data and advance write pointer */ b->buffer[b->writepos] = data; b->writepos++; if (b->writepos >= BUFFER_SIZE) b->writepos = 0; /* Signal that the buffer is now not empty */ pthread_cond_signal(&b->notempty); pthread_mutex_unlock(&b->lock); } /*--------------------------------------------------------*/ /* Read and remove an integer from the buffer */ int get(struct prodcons * b) { int data; pthread_mutex_lock(&b->lock); /* Wait until buffer is not empty */ while (b->writepos == b->readpos) { printf("wait for not empty\n"); pthread_cond_wait(&b->notempty, &b->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式大杂烩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值