当前位置:网站首页>在多核异构SoC平台上进行软件开发

在多核异构SoC平台上进行软件开发

2022-08-06 08:08:25断水客

多核异构SoC软件开发

1 概要

针对有多个不同架构的处理核心的SoC(System on Chip,片上系统),记录一些基本范畴和开发调试的心得。

2 背景介绍

一般说来,讨论一些非常规的东西,都是要交代一下背景的。一方面是方便读文章的人快速地和写东西的人合上拍子,避免鸡同鸭讲、跨服交流的尴尬局面;另一方面则是为写东西的人梳理写作内容框架预留一些缓冲区域,让写作过程更加流畅,文章的逻辑性更强。


在这里插入图片描述

长久以来,或者说至少从1965年摩尔定律(Moore’s law)被提出以来,软件开发人员通常预设有充足的硬件资源供给软件完成计算任务。好像无论多么复杂的任务,都可以找到性能更好的处理核心去处理,单核处理器算力盈余是司空见惯的事情。

假如算力不够,就换性能更强的处理核。

如今由于能量密度和量子隧穿效应的限制,摩尔定律逐步失效,在相同面积的情况下,单核处理器的性能提升遇到了瓶颈。于是我们需要多个处理核心(即多核),并行地进行运算任务。

但同时,运算任务的复杂度却不舍昼夜地增长着。

既要精准控制、又要高速运算、还要开发便捷、更要通用性好

这就使我们意识到,类似于这种既要、又要、还要、更要的综合问题,往往不能指望单核单类处理器包揽所有的工作。于是我们需要不同类型(通常意味着不同的架构,即异构)的芯片。

3 在多核异构系统上进行开发

将微处理器、模拟IP(Intellectual Property)核、数字IP核和存储器(或片外存储控制接口)集成在单一芯片上,面向特定用途的标准产品,就是所谓的SoC(system on chip)。可以把这种芯片理解为把不同的芯片做到同一块硅片上,使得原本独立的芯片称为联系紧密的片上系统。当前大热的ESP32就是典型的SoC(除了常见的MCU功能,还把WIFI和蓝牙射频模组集成到了同一个硅片上)。

这种方法使得我们能够根据任务的需求选择合适的芯片设计或者IP核,通过片上集成的方式得到一款专用的芯片,目前手机上使用的芯片也多是采用这种方式设计。

1、系统特性

系统架构多元、硬件类型多样。我们不妨大胆地假设自己有一片结构如下图所示的SoC。

在这里插入图片描述
这片SoC的特性如下:

同一个片上系统(SoC)内同时存在不同架构的处理器,包含ARM、MIPS、C-Sky、RSIC-V和Xtensa等多个架构,软件任务分配时可以针对单个架构处理器的特性扬长避短、在各不同处理器之间取长补短,使得计算任务的完成效率大大提高。

各处理器有不同用途,图中左侧三类架构的处理器处理器用作CPU,执行控制和逻辑运算相关的任务,右侧两种架构的处理器用作DSP,执行数值运算相关的任务。在软件设计的过程中,可以把计算量较大的任务单独拿出来,放到DSP类处理器中,用CPU类处理器做任务调度,使任务有条不紊地进行。

各处理器内部有多个核心,如ARM处理器中有四个核心,可以并行执行四个不同的任务,在软件开发过程中对应四个独立的程序(或者说,四个不同的入口函数/main函数)。

2、开发所需要的条件

  • 了解不同指令集特性

包括但不限于架构基本特点和汇编指令,这对应指令集架构(Instruction Set Architecture,ISA)的特性。我们通常把计算机架构中与编程序开发有关的部分泛称为指令集。
这部分信息可以在芯片所使用的架构的指令集参考手册(Isa reference manual)中获取,手册中一般包含了基本数据类型、汇编指令、寄存器、寻址模式 、存储体系 、中断、异常处理以及外部 I/O等内容 。


在这里插入图片描述


  • 熟悉各处理器基本构成,包括处理器自带外设使用和核间通信方式

类似于操作系统中进程的概念,不同处理核上运行的程序是各自独立的。要实现不同核之间的通信,可使用核间中断或者共享内存。

核间中断指的是某个处理核向其他核心发送中断信号,各处理核把其他核心的中断信号视作外部中断处理。

共享内存指的是不同处理核都能够访问的存储空间。SoC上通常有一定的空闲存储区域,可以取一部分用作核间通信。比如把0x88888888这个地址用作处理核甲、处理核乙的共享内存,使两个核心轮流工作,程序可以写成下面这样。

处理核甲:

main_unit_1()
{
    
	
	while(1){
    
		//在这里做一些处理
		//处理完成
		*(unsigned int)(0x88888888) = 2;
		while(*(unsigned int*)(0x88888888) != 1);
	}
}

处理核乙:

main_unit_2()
{
    
	while(1){
    
		while(*(unsigned int*)(0x88888888) != 2);
		//在这里做一些处理
		//处理完成
		*(unsigned int)(0x88888888) = 1;
	}
}
  • 明晰系统内部资源用途划分

包括处理器内部资源和系统共享资源(如片内存储空间的边界划分)。由于SoC的复杂性,开发人员很难像使用单片机时那样对片上设计完全把握。

但至少哈,需要了解地址空间的划分情况,包括内部和外部存储空间地址边界、两个存储空间中指令空间数据空间的划分情况、以及DMA的地址空间(确认DMA能够访问的空间范围对后续开发极为重要)。


在这里插入图片描述


除此之外,还需要了解各个处理核心的地址映射情况,即每个核心的地址访问边界。以esp32-s3为例,单片SoC由两哈佛结构的Xtensa LX7 CPU构成,两个CPU能访问的地址空间范围完全一致。

如果不一致的话,在为不同处理器分配处理任务时应当考虑到访问能力的限制。包括但不限于考虑CPU能否通过数据总线直接访问内部存储器、能否通过cache直接访问映射到地址空间的外部存储器、能否通过数据总线直接访问模块/外设、能否通过多个地址访问同一目标。下面是这几类访问方式的示意图。


在这里插入图片描述


3、开发内容

  • 启动程序

根据各处理器内部资源确定启动位置。

假设SoC上各处理核的程序烧写到了外部的EMMC上,那么启动的时候就需要把EMMC里面的程序和数据搬移到片内各处理核的指令空间(一般是RAM的某个位置i)里。由于硅片是实在的寸土寸金,各处理器的内部的部分甚至全部RAM就会在集成设计时被裁剪掉。没有RAM的话,只能在SoC上的存储器里找个位置做RAM。对于这些没有RAM的处理器,就要搬移到预定的存储器。

这些搬移工作以及一些硬件初始化工作,一般是交给启动程序去完成的。系统上电以后,各处理器执行默认地址开始的指令(即ROM启动程序),根据外部引脚确定boot模式(包括程序存储位置和数据搬移方式),完成搬移数据搬移任务和硬件初始化任务(配置一些寄存器)。

上述工作完成以后,将程序计数器(PC)的值写为指令空间的起始地址,开始运行应用软件(即我们根据实际任务需求编写的程序),进入工作状态。

  • 应用软件

根据系统任务需求,合理借助硬件加速器完成软件开发。注意规范核间对共享资源的访问策略,协调各处理器内部核心和不同处理器之间、处理器和片上各设备之间的通信。

4、软件调试注意事项

  • 存储空间的越界行为

包括普通越界操作(如软件层面的数组越界)和高危越界操作(某一核覆盖写入另一核的指令空间)。

越界写入是未定义的行为,会造成不确定的改变。加入有一个长度16的char型数组array,起始地址为0x90000,结束地址为0x900f,有一个十六位整型变量var,对应地址为090010。对array[16]赋值,即向地址0x90010写入一个字节,将导致var的高8位被意外改写。如果var恰好是某个行为决策用到的标志位的话,将造成更严重的后果。
在这里插入图片描述
前面提到,启动程序负责把外部存储器中的程序搬移到RAM上的指令空间中,而没有RAM的处理就需要把程序核数据搬移到规定的存储空间上。

这就涉及到指令空间的读写权限问题。假如启动时是把程序搬移到处理器自己的RAM上,那么其他处理核没有读写读写这部分空间的权限,自然不需要考虑某个处理核的指令空间被其他处理核写入覆盖的问题。而如果是第二种情况,就需要注意规避对指令空间的写入行为。

一般有两种方式,一种是通过MPU设置处理核对各存储空间各部分的访问权限,另一种则是在软件开发之前就定好各区域的读写许可级别,在编写代码时规避对禁止访问的区域的操作。

下图是读写许可级别的划分样例。
在这里插入图片描述

  • 访问顺序控制

共享内存既是核间通信的实现途径,也是多核协同易错点。以某一地址(单字节或几个字节)内容为协同标志,一般不会有访问顺序问题。而多核共享/同时读写某块内存时则需要控制访问权限(可以借助锁避免多核之间的读写冲突,也可以严格划分读写边界避免同时读写)。

原网站

版权声明
本文为[断水客]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_33904382/article/details/125826701

随机推荐