精华内容
下载资源
问答
  • 多核异构
    2021-03-25 10:01:06

    1、浅析多核ECU运行流程:

    传送门:https://zhuanlan.zhihu.com/p/66934660

    更多相关内容
  • 多核异构模式下有管理的共享内存设计方法
  • (19)中华人民共和国国家知识产权局 (12)发明专利申请 (10)申请公布号 CN 111399911 A (43)申请公布日 2020.07.10 (21)申请号 202010215082.7 (22)申请日 2020.03.24 (71)申请人 杭州博雅鸿图视频技术有限公司 地址 ...
  • 分享多核异构平台的软件开发经验

    多核异构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设置处理核对各存储空间各部分的访问权限,另一种则是在软件开发之前就定好各区域的读写许可级别,在编写代码时规避对禁止访问的区域的操作。

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

    • 访问顺序控制

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

    展开全文
  • 多核异构并行计算OpenMP---并行控制并行控制指令parallel功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样 并行控制 指令parallel /*File:parallel.cpp*/ #include<stdio.h> #include<omp.h>...

    指令parallel

    /*File:parallel.cpp*/
    #include<stdio.h>
    #include<omp.h>
    
    int main(){
    
            int tid,nthreads;
            nthreads = omp_get_num_threads(); // 获取正在运行的线程数量
            tid = omp_get_thread_num();       // 获取正在运行的线程的id号
            //omp_in_parallel()是用来检测代码是并行执行还是串行执行
            if(omp_in_parallel()){
                    printf("In the parallel region! id=%d  ",tid);
                    printf("Number of threads:%d\n",nthreads);
    
            }else{
                    printf("In the serial region! id=%d  ",tid);
                    printf("Number of threads:%d\n",nthreads);
            }
    
            printf("-----before parallel region");
            printf("\n");
            printf("\n");
    
    		//上面的代码段是串行执行,下面是并行区
            #pragma omp parallel private(tid,nthreads)
            	//private表示变量列表中列出的变量对于每个线程来说均是私有变量
            	//即每个线程都有自己的私有变量副本
            {
                    nthreads = omp_get_num_threads();//线程数量
                    tid = omp_get_thread_num();         //线程id
                    if(omp_in_parallel()){
                            printf("in the parallel region! id=%d  ",tid);
                            printf("number of threads:%d\n",nthreads);
                    }else{
                            printf("in the serial region!id=%d  ",tid);
                            printf("number of threads:%d\n",nthreads);
                    }
            }
    
            printf("\n");
            printf("-----after parallel region\n");
            //并行区域结束,再次执行串行区
            nthreads=omp_get_num_threads();
            tid=omp_get_thread_num();
            if(omp_in_parallel()){
                    printf("in the parallel region! id=%d  ",tid);
                    printf("number of threads:%d\n",nthreads);
            }else{
                    printf("in the serial region!id=%d  ",tid);
                    printf("number of threads:%d\n",nthreads);
            }
            return 0;
    }
    //运行结果
    $ ./perallel.out    
    in the serial region! id=0  number of threads:1
    -----before parallel region
    
    in the parallel region! id=7  in the parallel region! id=2  in the parallel region! id=1  number of threads:8
    in the parallel region! id=0  number of threads:8
    in the parallel region! id=5  number of threads:8
    number of threads:8
    in the parallel region! id=3  number of threads:8
    number of threads:8
    in the parallel region! id=6  number of threads:8
    in the parallel region! id=4  number of threads:8
    // 由于在前面parallel定义的并行区域内线程数没有显式声明,因此并行区域内运行的线程数量等于硬件系统所能提供的最大线程数
    // 线程组内子线程号从0到7,而且执行顺序是随机的
    -----after parallel region
    in the serial region!id=0  number of threads:1
    //退出并行区域后,串行区代码段采用单线程方式执行,线程号为0
    
    

    设定线程数量

    对并行区域设置线程数量是必不可少的关键步骤通常有四种途径:

    1. 默认方式
    2. 调用环境库函数
    3. 使用num_thread指令,实际上是一种静态模式
    4. 使用环境变量OMP_NUM_THREADS,它实际上也是一种静态模式。
      在这几种方法中,比较常用的模式是静态模式和动态模式,嵌套模式比较复杂,普通的编程人员一般不会涉及。

    默认模式

    所谓默认模式,就是在程序中对并行计算的线程数量不作显式声明。此方法的优越性在于程序的扩展性好。

    静态模式

    /* File:snt.cpp */
    #include<stdio.h>
    #include<omp.h>
    
    int main(){
    
            int nthreads_set, nthreads,tid;
            //默认模式下
            #pragma omp parallel private(tid,nthreads)
            {
                    nthreads=omp_get_num_threads();
                    tid = omp_get_thread_num();
                    printf("number of threads=(default)%d  ",nthreads);
                    printf("tid=%d\n",tid);
            }
            printf("------before OMP_SET_NUM_THREADS\n");
            printf("\n");
            
            nthreads_set=3;
            //设置线程数量
            omp_set_num_threads(nthreads_set);
            printf("set_number_threads=%d\n",nthreads_set);
    
            //设置线程数量后
            #pragma omp parallel private(tid,nthreads)
            {
                    nthreads=omp_get_num_threads();
                    tid = omp_get_thread_num();
                    printf("number of threads(default)=%d  ",nthreads);
                    printf("tid=%d\n",tid);
                    printf("------------------------\n");
            }
            return 0;
    }
    //运行结果
    $ ./snt.out 
    number of threads=(default)8  tid=0
    number of threads=(default)8  tid=7
    number of threads=(default)8  tid=6
    number of threads=(default)8  tid=1
    number of threads=(default)8  tid=4
    number of threads=(default)8  tid=3
    number of threads=(default)8  tid=2
    number of threads=(default)8  tid=5
    ------before OMP_SET_NUM_THREADS
    
    set_number_threads=3
    number of threads(default)=3  tid=1
    ------------------------
    number of threads(default)=3  tid=0
    ------------------------
    number of threads(default)=3  tid=2
    ------------------------
    
    

    动态模式

    /* File:sd.cpp */
    #include<stdio.h>
    #include<omp.h>
    
    int main(){
    
            int nthreads_set, nthreads,tid;
            nthreads_set=3;
            omp_set_dynamic(1);
            //这上下两个指令是成对使用的
            omp_set_num_threads(nthreads_set);
            printf("set_number_threads=%d\n",nthreads_set);
            printf("dynamic region(1 or 0):%d",omp_get_dynamic());
            printf("\n");
    
            #pragma omp parallel private(tid,nthreads)
            {
                    nthreads=omp_get_num_threads();
                    tid = omp_get_thread_num();
                    printf("number of threads=%d  ",nthreads);
                    printf("tid=%d\n",tid);
                    printf("------------------------\n");
            }
    
            return 0;
    }
    
    //运行结果
    $ ./sd.out                     
    set_number_threads=3
    dynamic region(1 or 0):1
    number of threads=2  tid=0
    ------------------------
    number of threads=2  tid=1
    ------------------------
    

    嵌套模式与num_threads子句

    // sn.cpp
    #include<stdio.h>
    #include<omp.h>
    
    int main(){
    
            omp_set_nested(1);
            omp_set_dynamic(0);
    
            printf("nested region(1 or 0):%d\n",omp_get_nested());
            printf("\n");
    
            #pragma omp parallel num_threads(2)
            //开启两个线程
            {
                    if(omp_get_thread_num() == 0){
                    //如果由主线程运行则
                            omp_set_num_threads(4);
                    }else{
                            omp_set_num_threads(3);
                    }
    
                    #pragma omp master
                    printf("* * * * outer zone:active_level=%d,  team_size=%d\n",
                    omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                    printf("outer:thread_ID=%d,thread_in_team:%d\n",
                    omp_get_thread_num(), omp_get_num_threads());
    
                    #pragma omp parallel
                    {
                            #pragma omp master
                            printf("-------inner zone:active_level=%d,   team_size=%d\n",
                            omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                            printf("inner:thread_ID=%d,threads_in_team=%d\n",
                            omp_get_thread_num(),omp_get_num_threads());
                    }
            }
    
            return 0;
    }
    
    //运行结果
    $ ./sn.out                     
    nested region(1 or 0):1
    
    * * * * outer zone:active_level=1,  team_size=2
    outer:thread_ID=0,thread_in_team:2
    outer:thread_ID=1,thread_in_team:2
    -------inner zone:active_level=2,   team_size=4
    inner:thread_ID=0,threads_in_team=4
    inner:thread_ID=3,threads_in_team=4
    inner:thread_ID=1,threads_in_team=4
    inner:thread_ID=2,threads_in_team=4
    inner:thread_ID=2,threads_in_team=3
    -------inner zone:active_level=2,   team_size=3
    inner:thread_ID=0,threads_in_team=3
    inner:thread_ID=1,threads_in_team=3
    
    /* nc.cpp */
    #include<stdio.h>
    #include<omp.h>
    
    #define m 5
    #define n 4
    int main(){
            int array1[m],array2[n];
            omp_set_nested(1);
            omp_set_dynamic(0);
            #pragma omp parallel sections shared(array1,array2)num_threads(3)
            {
                    #pragma omp section
                    {
                            printf("* * * * *outer section 1:active_level=%d,team_size=%d",
                            omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                            printf("  id=%d, threads_in_team=%d\n\n",
                            omp_get_thread_num(), omp_get_num_threads());
    
                            #pragma omp parallel for shared(array1)num_threads(3)
                            for(int i=0;i<m;i++){
                                    array1[i]=i;
                                    printf("-----inner section 1:i=%d active_level=%d,team_size=%d",i,
                                    omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                                    printf("    id=%d,threads_in_team=%d \n",
                                    omp_get_thread_num(), omp_get_num_threads());
                            }
                    }
    
                    #pragma omp section
                    {
                            printf("* * * * * outer section 2:active_level=%d,team_size=%d",
                            omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                            printf("   id=%d,threads_in_team=%d\n",
                            omp_get_thread_num(), omp_get_num_threads());
    
                            #pragma omp parallel for shared(array2)num_threads(2)
                            for(int j=0;j<n;j++){
                                    array2[j]=j+10;
                                    printf("----inner section2:j=%d active_level=%d,team_size=%d",j,
                                    omp_get_active_level(), omp_get_team_size(omp_get_active_level()));
                                    printf("   id=%d,threads_in_tean=%d  \n",
                                    omp_get_thread_num(), omp_get_num_threads());
                            }
                    }
            }
            return 0;
    }
    
    //运行结果
    $ ./nc.out
    * * * * *outer section 1:active_level=1,team_size=3  id=0, threads_in_team=3
    
    * * * * * outer section 2:active_level=1,team_size=3   id=1,threads_in_team=3
    -----inner section 1:i=0 active_level=2,team_size=3
    -----inner section 1:i=4 active_level=2,team_size=3    id=2,threads_in_team=3 
    ----inner section2:j=0 active_level=2,team_size=2   id=0,threads_in_tean=2  
    ----inner section2:j=1 active_level=2,team_size=2   id=0,threads_in_tean=2  
    -----inner section 1:i=2 active_level=2,team_size=3    id=1,threads_in_team=3 
    -----inner section 1:i=3 active_level=2,team_size=3    id=1,threads_in_team=3 
        id=0,threads_in_team=3 
    -----inner section 1:i=1 active_level=2,team_size=3    id=0,threads_in_team=3 
    ----inner section2:j=2 active_level=2,team_size=2   id=1,threads_in_tean=2  
    ----inner section2:j=3 active_level=2,team_size=2   id=1,threads_in_tean=2  
    
    

    条件并行子句if

    /* ipp.cpp */
    #include<stdio.h>
    #include<omp.h>
    
    void printnumthreads(int n){
    
            int nthreads;
    
            #pragma omp parallel private(nthreads)if(n>10)num_threads(4)
            {
                    nthreads=omp_get_num_threads();
                    printf("number of threads=%d, n=%d\n",nthreads,n);
            }
            return ;
    }
    
    int main(){
            printnumthreads(2);
            printf("\n");
            printnumthreads(20);
            return 0;
    }
    
    //运行结果
    $ ./ipp.out
    number of threads=1, n=2
    
    number of threads=4, n=20
    number of threads=4, n=20
    number of threads=4, n=20
    number of threads=4, n=20
    
    展开全文
  • 为什么要介绍 chipyard都有啥 如何下载 几个典型的示例 DSP

    1 文章导览

    在这里插入图片描述

    本文是简要性的导览chipyard官方手册内容,以及安装开发环境需要注意的的一些地方,最后运行几个简单的官方Demo,希望能对RISC-V有兴趣的小伙伴有所启发帮助,官方网址为https://chipyard.readthedocs.io/en/latest/

    注:文内大部分代码均复制粘贴整理自官方手册。

    2 chipyard组件

    Chipyard是用于敏捷开发基于Chisel的片上系统的开源框架。它将使您能够利用Chisel HDL,Rocket Chip SoC生成器和其他Berkeley项目来生产RISC-V SoC,该产品具有从MMIO映射的外设到定制加速器的所有功能。Chipyard包含:

    • 处理器内核(Rocket,BOOM,Ariane);
    • 加速器(Hwacha,Gemmini,NVDLA);
    • 内存系统以及其他外围设备和工具,以帮助创建功能齐全的SoC。

    2.1 Rocket

    Rocket-core是标准的5级流水顺序执行标量处理器,支持RV64GC RISC-V 指令集,Chisel实现,下面是一个典型的双核实现
    在这里插入图片描述

    它的流水线结构为
    在这里插入图片描述

    2.2 BOOM

    BOOM全名为Berkeley Out-of-Order Machine,顾名思义是个乱序执行的core,为7级流水,支持RV64GC RISC-V 指令集,Chisel实现,如下是详细的流水线结构
    在这里插入图片描述
    这个是简化的流水线结构
    在这里插入图片描述

    特性汇总如下表在这里插入图片描述

    2.3 Ariane

    Ariane是6级流水顺序执行标量core,SV实现,如下是它的流水线结构
    在这里插入图片描述

    2.4 Gemmini

    Gemmini项目是一种正在开发基于脉动阵列的矩阵乘法单元生成器。利用ROCC接口,用于与RISC-V Rocket / BOOM处理器集成的协处理器。
    在这里插入图片描述

    2.5 NVDLA

    NVDLA是NVIDIA开发的开源深度学习加速器。可以通过TileLink总线挂载搭配Rocket Chip SoC 上。
    在这里插入图片描述

    2.6 SHA3 RoCC 加速器

    利用ROCC接口,用于与RISC-V Rocket / BOOM处理器集成的协处理器,专用于SHA3 Hash加速。
    在这里插入图片描述

    3 搭建环境

    注:仅限于Linux系统!!!

    下面以Ubuntu为例,其他的建议参考官方文档

    首先要先安装必要的依赖环境

    #!/bin/bash
    
    set -ex
    
    sudo apt-get install -y build-essential bison flex
    sudo apt-get install -y libgmp-dev libmpfr-dev libmpc-dev zlib1g-dev vim git default-jdk default-jre
    # install sbt: https://www.scala-sbt.org/release/docs/Installing-sbt-on-Linux.html
    echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
    curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add
    sudo apt-get update
    sudo apt-get install -y sbt
    sudo apt-get install -y texinfo gengetopt
    sudo apt-get install -y libexpat1-dev libusb-dev libncurses5-dev cmake
    # deps for poky
    sudo apt-get install -y python3.6 patch diffstat texi2html texinfo subversion chrpath git wget
    # deps for qemu
    sudo apt-get install -y libgtk-3-dev gettext
    # deps for firemarshal
    sudo apt-get install -y python3-pip python3.6-dev rsync libguestfs-tools expat ctags
    # install DTC
    sudo apt-get install -y device-tree-compiler
    
    # install verilator
    git clone http://git.veripool.org/git/verilator
    cd verilator
    git checkout v4.034
    autoconf && ./configure && make -j30 && sudo make install
    

    下面利用git把chipyard以及包含的所有子模块全部下载下来。

    git clone https://github.com/ucb-bar/chipyard.git
    cd chipyard
    ./scripts/init-submodules-no-riscv-tools.sh
    

    最后构建需要的工具链

    # riscv-tools: if set, builds the riscv toolchain (this is also the default)
    # esp-tools: if set, builds esp-tools toolchain used for the hwacha vector accelerator
    # ec2fast: if set, pulls in a pre-compiled RISC-V toolchain for an EC2 manager instance
    export MAKEFLAGS=-j30
    ./scripts/build-toolchains.sh riscv-tools # for a normal risc-v toolchain
    source ./env.sh
    

    如果上面的步骤经过了大半天也没有完成,甚至因为网络的原因出错,那么你可以有如下两种解决方案,如果还有更好的方案欢迎讨论:

    • 利用代理或者梯子;
    • 利用gitee镜像原仓库,然后后台一个一个下载,最后重复执行./scripts/init-submodules-no-riscv-tools.sh./scripts/build-toolchains.sh riscv-tools,直到最终完成工具链的构建。

    4 几个示例

    4.1 Rocket

    首先进行一个典型的Rocket配置,更多有趣的配置可以直接访问源文件

    //generators/chipyard/src/main/scala/config/RocketConfigs.scala
    class RocketConfig extends Config(
      new chipyard.iobinders.WithUARTAdapter ++                      // display UART with a SimUARTAdapter
      new chipyard.iobinders.WithTieOffInterrupts ++                 // tie off top-level interrupts
      new chipyard.iobinders.WithBlackBoxSimMem ++                   // drive the master AXI4 memory with a blackbox DRAMSim model
      new chipyard.iobinders.WithTiedOffDebug ++                     // tie off debug (since we are using SimSerial for testing)
      new chipyard.iobinders.WithSimSerial ++                        // drive TSI with SimSerial for testing
      new testchipip.WithTSI ++                                      // use testchipip serial offchip link
      new chipyard.config.WithBootROM ++                             // use default bootrom
      new chipyard.config.WithUART ++                                // add a UART
      new chipyard.config.WithL2TLBs(1024) ++                        // use L2 TLBs
      new freechips.rocketchip.subsystem.WithNoMMIOPort ++           // no top-level MMIO master port (overrides default set in rocketchip)
      new freechips.rocketchip.subsystem.WithNoSlavePort ++          // no top-level MMIO slave port (overrides default set in rocketchip)
      new freechips.rocketchip.subsystem.WithInclusiveCache ++       // use Sifive L2 cache
      new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts
      new freechips.rocketchip.subsystem.WithNBigCores(1) ++         // single rocket-core
      new freechips.rocketchip.subsystem.WithCoherentBusTopology ++  // hierarchical buses including mbus+l2
      new freechips.rocketchip.system.BaseConfig)                    // "base" rocketchip system
    

    构建core

    cd sims/verilator
    make CONFIG=RocketConfig -j
    

    如下部分设备树log对应着上述的配置
    在这里插入图片描述

    然后运行个跑分程序看看性能

    cd $RISCV/riscv64-unknown-elf/share/riscv-tests/benchmarks/
    make -j
    cd $RISCV/../sims/verilator
    ./simulator-chipyard-RocketConfig $RISCV/riscv64-unknown-elf/share/riscv-tests/benchmarks/dhrystone.riscv
    

    在这里插入图片描述

    4.2 BOOM

    再来看看一个Small BOOM的配置

    // generators/chipyard/src/main/scala/config/BoomConfigs.scala
    class SmallBoomConfig extends Config(
      new chipyard.iobinders.WithUARTAdapter ++                      // display UART with a SimUARTAdapter
      new chipyard.iobinders.WithTieOffInterrupts ++                 // tie off top-level interrupts
      new chipyard.iobinders.WithBlackBoxSimMem ++                   // drive the master AXI4 memory with a SimAXIMem
      new chipyard.iobinders.WithTiedOffDebug ++                     // tie off debug (since we are using SimSerial for testing)
      new chipyard.iobinders.WithSimSerial ++                        // drive TSI with SimSerial for testing
      new testchipip.WithTSI ++                                      // use testchipip serial offchip link
      new chipyard.config.WithBootROM ++                             // use default bootrom
      new chipyard.config.WithUART ++                                // add a UART
      new chipyard.config.WithL2TLBs(1024) ++                        // use L2 TLBs
      new freechips.rocketchip.subsystem.WithNoMMIOPort ++           // no top-level MMIO master port (overrides default set in rocketchip)
      new freechips.rocketchip.subsystem.WithNoSlavePort ++          // no top-level MMIO slave port (overrides default set in rocketchip)
      new freechips.rocketchip.subsystem.WithInclusiveCache ++       // use Sifive L2 cache
      new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts
      new boom.common.WithSmallBooms ++                              // small boom config
      new boom.common.WithNBoomCores(1) ++                           // single-core boom
      new freechips.rocketchip.subsystem.WithCoherentBusTopology ++  // hierarchical buses including mbus+l2
      new freechips.rocketchip.system.BaseConfig)                    // "base" rocketchip system
    

    运行如下命令进行构建内核

    cd sims/verilator
    make CONFIG=SmallBoomConfig -j
    

    如下部分设备树log对应着上述的配置
    在这里插入图片描述

    然后运行个跑分程序看看性能

    cd $RISCV/riscv64-unknown-elf/share/riscv-tests/benchmarks/
    make -j
    cd $RISCV/../sims/verilator
    ./simulator-chipyard-SmallBoomConfig $RISCV/riscv64-unknown-elf/share/riscv-tests/benchmarks/dhrystone.riscv
    

    在这里插入图片描述
    根据跑分,可以看出Mini Boom内核的乱序执行对比Rocket的顺序执行稍微提升了性能(假设内核频率)。

    再来看看一个Large Boom的跑分,带来了两倍以上的性能提升。
    在这里插入图片描述
    注:更深入的跑分数据对比需要换算为DMIPS/MHz,与其他处理器进行对比,这里就不深入说明了。

    4.3 初探定制硬件加速器SOC

    最后来看一个带FIR硬件加速器的Rocket SOC,它的配置为

    //generators/chipyard/src/main/scala/config/RocketConfigs.scala
    class StreamingFIRRocketConfig extends Config (
      new chipyard.example.WithStreamingFIR ++ // use top with tilelink-controlled streaming FIR
      new chipyard.iobinders.WithUARTAdapter ++
      new chipyard.iobinders.WithTieOffInterrupts ++
      new chipyard.iobinders.WithBlackBoxSimMem ++
      new chipyard.iobinders.WithTiedOffDebug ++
      new chipyard.iobinders.WithSimSerial ++
      new testchipip.WithTSI ++
      new chipyard.config.WithBootROM ++
      new chipyard.config.WithUART ++
      new chipyard.config.WithL2TLBs(1024) ++
      new freechips.rocketchip.subsystem.WithNoMMIOPort ++
      new freechips.rocketchip.subsystem.WithNoSlavePort ++
      new freechips.rocketchip.subsystem.WithInclusiveCache ++
      new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++
      new freechips.rocketchip.subsystem.WithNBigCores(1) ++
      new freechips.rocketchip.subsystem.WithCoherentBusTopology ++
      new freechips.rocketchip.system.BaseConfig)
    

    构建core,运行测试

    cd tests/
    make -j
    cd ../sims/verilator
    make CONFIG=StreamingFIRRocketConfig -j BINARY=../../tests/streaming-fir.riscv run-binary
    

    根据log可以看出内存地址有该硬件加速器的一席之地,后面会利用MMIO进行控制访问
    在这里插入图片描述
    测试代码如下

    #define PASSTHROUGH_WRITE 0x2000
    #define PASSTHROUGH_WRITE_COUNT 0x2008
    #define PASSTHROUGH_READ 0x2100
    #define PASSTHROUGH_READ_COUNT 0x2108
    
    #define BP 3
    #define BP_SCALE ((double)(1 << BP))
    
    #include "mmio.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    
    uint64_t roundi(double x)
    {
      if (x < 0.0) {
        return (uint64_t)(x - 0.5);
      } else {
        return (uint64_t)(x + 0.5);
      }
    }
    
    int main(void)
    {
      double test_vector[15] = {1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.5, 0.25, 0.125, 0.125};
      uint32_t num_tests = sizeof(test_vector) / sizeof(double);
      printf("Starting writing %d inputs\n", num_tests);
    
      for (int i = 0; i < num_tests; i++) {
        reg_write64(PASSTHROUGH_WRITE, roundi(test_vector[i] * BP_SCALE));
      }
    
      printf("Done writing\n");
      uint32_t rcnt = reg_read32(PASSTHROUGH_READ_COUNT);
      printf("Write count: %d\n", reg_read32(PASSTHROUGH_WRITE_COUNT));
      printf("Read count: %d\n", rcnt);
    
      int failed = 0;
      if (rcnt != 0) {
        for (int i = 0; i < num_tests - 3; i++) {
          uint32_t res = reg_read32(PASSTHROUGH_READ);
          // double res = ((double)reg_read32(PASSTHROUGH_READ)) / BP_SCALE;
          double expected_double = 3*test_vector[i] + 2*test_vector[i+1] + test_vector[i+2];
          uint32_t expected = ((uint32_t)(expected_double * BP_SCALE + 0.5)) & 0xFF;
          if (res == expected) {
            printf("\n\nPass: Got %u Expected %u\n\n", res, expected);
          } else {
            failed = 1;
            printf("\n\nFail: Got %u Expected %u\n\n", res, expected);
          }
        }
      } else {
        failed = 1;
      }
    
      if (failed) {
        printf("\n\nSome tests failed\n\n");
      } else {
        printf("\n\nAll tests passed\n\n");
      }
      
      return 0;
    }
    

    测试结果如下
    在这里插入图片描述

    4.4 构建多核异构SOC

    一个典型的配置为单核Boom与单核Rocket以及其他必要的组件构成一个异构SOC

    class LargeBoomAndHwachaRocketConfig extends Config(
      new chipyard.iobinders.WithUARTAdapter ++
      new chipyard.iobinders.WithTieOffInterrupts ++
      new chipyard.iobinders.WithBlackBoxSimMem ++
      new chipyard.iobinders.WithTiedOffDebug ++
      new chipyard.iobinders.WithSimSerial ++
      new testchipip.WithTSI ++
      new chipyard.config.WithBootROM ++
      new chipyard.config.WithUART ++
      new chipyard.config.WithMultiRoCC ++                                  // support heterogeneous rocc
      new chipyard.config.WithMultiRoCCHwacha(1) ++                         // put hwacha on hart-2 (rocket)
      new chipyard.config.WithL2TLBs(1024) ++
      new chipyard.config.WithRenumberHarts ++
      new boom.common.WithLargeBooms ++
      new boom.common.WithNBoomCores(1) ++
      new freechips.rocketchip.subsystem.WithNoMMIOPort ++
      new freechips.rocketchip.subsystem.WithNoSlavePort ++
      new freechips.rocketchip.subsystem.WithInclusiveCache ++
      new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++
      new freechips.rocketchip.subsystem.WithNBigCores(1) ++
      new freechips.rocketchip.subsystem.WithCoherentBusTopology ++
      new freechips.rocketchip.system.BaseConfig)
    

    更多的细节内容建议直接访问官方文档,以及文章的后续(如果有机会的话,看情况会有core移植到FPGA、Linux操作系统移植的相关内容)。



    整理不易,严禁剽窃!

    在这里插入图片描述

    欢迎大家关注我创建的微信公众号——小白仓库
    原创经验资料分享:包含但不仅限于FPGA、ARM、RISC-V、Linux、LabVIEW等软硬件开发,另外分享生活中的趣事以及感悟。目的是建立一个平台记录学习过的知识,并分享出来自认为有用的与感兴趣的道友相互交流进步。

    展开全文
  • 计算机-后端-基于多核异构架构的并行有限元算法研究及应用.pdf
  • 一种CPU GPU的多核异构平台设计方案.pdf
  • 行业分类-设备装置-多核异构平台下信息交互的内存申请方法、装置及设备.zip
  • 多核异构处理器的多类型数据车载记录仪设计.pdf
  • #资源达人分享计划#
  • 基于电力专用多核异构芯片架构的低压保护测控装置设计.pdf
  • 基于多核异构的低功耗语音AIoT芯片GX8008_GX8010.pdf
  • 千呼万唤始出来,飞凌FETMX8MQ-C核心板及其配套开发板OKMX8MQ-C于今日正式发布! 核心板售价658元起,提供10~15年产品长期供货,为企业智能产品稳定性保驾护航。 ----- FETMX8MQ-C核心板具有业界领先的音频、语音...
  • 此模块可用于同构(如DSP到DSP)或异构(如ARM到DSP)多处理器消息传递。MessageQ提供了比其他模块更复杂的消息传递。它通常用于复杂的情况,如多处理器消息传递。 以下是MessageQ模块的关键特性: 消息的writers和...
  • TI Sitara系列AM5718/5728是采用ARM DSP多核异构架构,可以实现图像采集、算法处理、显示、控制等功能,具有实时控制、低功耗、多标准工业控制网络互联、工业人机界面的优化、2D/3D图形处理、1080 HD的高清视频应用...
  • 同构多核异构多核简单介绍

    千次阅读 2021-06-17 13:06:06
    多核处理器的由来   多核出现前,商业化处理器都致力于单核处理器的发展,其性能已经发挥到极致,仅仅提高单核芯片的速度会产生过多热量且无法带来相应性能改善,但CPU性能需求大于CPU发展速度。尽管增加流水线...
  • OMAPL多核异构通信驱动AD9833-Notify组件demo OMAPL多核通信有三个主要机制,Notify,MessageQ,RegionShare;这里主要利用了Notify机制进行通信控制。 要做一个什么实验? 简单的说,ARM跑一个界面上面有一些按钮,...
  • TI AM5728 DSP+ARM+FPGA多核异构工业控制处理器。 DSP用于复杂算法处理,ARM用于通用事务管理,FPGA用于高速信号采集,是个完美的高性能嵌入式工业主板组合。 ...
  • 导读:Mentor Graphics Corporation日前宣布推出嵌入式软件行业针对异构多核芯片(SoC)开发的首个全面解决方案。  异构架构即结合两种或多种不同类型的微处理器或微控制器的架构。这种架构促成了整合功能性和连通...
  • 终于让异构多核各跑了一个Linux发布时间:2008-04-07 01:16:49来源:红联作者:PowercutNEC 的EMMA3P,片内有3个Core,分别为 VR5500(MIPS64),4KEc(MIPS32),还有一个用于audio处理的core,具体体系结构不明。...
  • 随机异构多核处理器中的调度算法
  • 最近几年,人工智能应用日益普及,个人语音助手一类的人工智能应用,为我们带来了令人惊叹的体验。为了让你更好地用上人工智能,我们在今年 MWC 上推出了 Qualcomm...高效运行终端侧人工智能需要多核异构计算。因...
  • 低功耗:多核异构CPU,内置一个主频达400MHz的Cortex-M4内核,可用于低功耗,实时任务处理的应用。 供货稳定:供货稳定,i.MX8M Mini处理器列入NXP产品长期供货计划,至少保证10-15年供货周期。 底板的资源是非常...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,805
精华内容 1,922
关键字:

多核异构