MIPS Linux内核编译构建环境的搭建

AI的端侧应用离不开各种算力平台,目前形成了CPU+NPU,CPU+FPGA以及CPU+GPU的几类算力搭配,他们各有优势,也各有弱点。先看它们的共同点,从组合名字可以看出,那就是他们都依赖CPU,这是因为无论NPU,FPGA还是GPU,它们的架构特点决定了在流水线pipeline,逻辑控制方面要弱于CPU,它们要么作为专用算力加速器,没有完备的指令集系统,比如GPU有简单的流水线,可以完成取指,解码执行的操作,它的流水线适合做大量的SIMD并行数据计算,比如DP4a指令(4元素矢量点积),Intel、AMD、NVIDIA基本所有的GPU都支持它,但是无法完成复杂的执行流以及逻辑控制。另外还有一些没有取指流水等机制,无法自举执行指令,必须依赖CPU打配合,有点类似于FPU或者NEON单元,比如NPU就是这个样子,这类算力无法独立完成取指令执行的操作,运行依赖于CPU不断的喂指令,在设计中,必须配合CPU一起用。

之前对与RISCV Vector指令,MIPS的FPU以及ARM的NEON都有过一些分析,但当时对AI没有太多概念,对AI如何使用这些单元提供的算力缺乏认识,分析完了感觉没有结果。现在对AI有了一些了解,尤其其中的算子实现对异构算力的设计需求有了一些自己的看法,这样反过来从需求方倒推,对ISA指令集的设计理解的会更加深刻。 所以觉得有必要结合AI,对一些主流的CPU ISA指令集特点做一个总结,包括最早接触的MIPS,Xlinux(FPGA)做过验证,之后是ARM,然后RISCV和DSP(Xtensa),ARM和RISCV也都跑过Altera FPGA。这里面RISCV是最精简的,软件上可以认为是MIPS的一个子集(可能Vector向量指令的区别会大一些),可以放在一起分析,感觉这是一个比较大的主题和工作量,万事开头难,先从重温MIPS开始,之后逐渐切入其它ISA,横向对比,纵向深挖,分析它们各自对AI加速提供的各类ISA级的机制以及背后的思考,个人的一些看法,可能不对,期待被打脸。

首先从搭建MIPS开发环境说起

MIPS编译工具链下载

MIPS官网:MIPS

比较滑稽的是,在MIPS官网主页上,一幅标题赫然写着:

We’re taking RISC-V to new heights(我们将RISC-V推向新的高度),以MIPS多年积累的技术实力,相信这个新“高度”不难实现。但是市场是残酷的,只见新人笑,哪闻旧人哭,曾经的产业一哥如今沦落到要为新近晚辈执鞭随镫,不禁让人喟叹。

MIPS不但是这样说的,还是这样做的,MIPS已经发布它的的首颗RISCV ISA CPU IP eVocore

更奇葩的是MIPS官网的副标题竟然是elevating riscv,几年前MIPS就已经宣布放弃MIPS拥抱RISCV,一代旗手沦落至此,令人唏嘘。

顺着导航,来到Developer Tools – MIPS,可以看到开发工具和开发模式。

现在有多没落,曾经就有多辉煌,从高端的64位MIPS服务器,到低端32位MCU级控制器,都可以看到MIPS的身影。

之后来到编译器下载链接:MIPS Compilers – MIPS

MIPS支持LLVM工具开发,不过我们这里用GCC

 我们将bare metal和Linux用户态工具链都下载下来,由于我们要编译的是Linux内核,究竟使用哪种工具链关系不大,任何一个都可以,我们解压:Codescape.GNU.Tools.Package.2019.02-05.for.MIPS.IMG.Linux.CentOS-6.x86_64.tar.gz

之后导出环境变量:

export PATH=$PATH:/home/caozilong/mips/mips-img-linux-gnu/2019.02-05/bin

之后,在任何一个目录,都可以直接输入mips-img-linux-gnu-gcc访问GCC了。

下载最新的Linux内核代码:

从kernel.org下载并解压:

编译

make ARCH=mips CROSS_COMPILE=mips-img-linux-gnu- malta_defconfig 
make ARCH=mips CROSS_COMPILE=mips-img-linux-gnu- menuconfig 
make -j4 ARCH=mips CROSS_COMPILE=mips-img-linux-gnu-

编译结果:

 反编译

mips-img-linux-gnu-objdump -d vmlinux>vmlinux.dis

从运行地址也可以看出,内核虚拟地址从0X80000000地址开始,这也是MIPS ISA地址分布的特征。

32位MIPS处理器将地址空间划分为四段,从低地址到搞地址,各段的特性如下:

1.用户模式,监管模式和核心模式都可以访问,用户模式下只能访问该段空间,需要通过MMU映射,其CACHE算法由页表中相应字段规定,在LINUX操作系统中,该段存放用户程序,动态连接库,程序堆栈等等。

2.kseg0,只能由核心模式访问,该段地址不通过MMU进行地址映射,其CACHE算法由CONFIG控制寄存器的K0域决定,KSEG0直接映射至低512MB物理地址空间,映射方法为抹去最高三位,在LINUX 系统中,该段用来存放内核代码,数据,异常入口。

3.kseg1,只能由和新模式访问,该段地址不通过MMU进行地址映射,也不通过CACHE进行缓存,KSEG1同样映射至低512M物理地址空间,映射方法为抹去高3位,KSEG1和KSEG0映射到相同的物理地址,但KSEG1的访问无需依赖CACHE,因此MIPS处理器程序计数器的复位地址0xbfc00000就落在KSEG1段,完成CACHE初始化等工作后,在跳转到KSEG0执行,可以利用CACHE提高访问速度,在LINUX操作系统中,该段存放启动ROM和IO寄存器等。

4.kseg2,由于核心模式和监管模式访问,通过MMU进行映射,CACHE 算法由页表决定,LINUX操作系统中,该段用于存放动态分配的内核数据。

编译完成,下一步准备用busybox构建一个小的文件系统,之后用qemu模拟器进行启动,就可以愉快的跑MIPS ISA了。

MIPS处理器复位

处理器的第一条指令,实际上是由复位信号控制的,但受限于各种其他因素,复位信号并没有对处理器内部的所有部分进行控制,例如TLB, CACHE等等。

MIPS处理器复位后的第一条指令将固定从地址0xbfc00000位置获取,这个过程是由处理器执行指针寄存器,也就是PC,被硬件复位为0xbfc00000而决定的。

对于MIPS来说,0xbfc00000这个虚拟地址属于直接映射且不经CACHE缓存的地址段,直接对应于物理地址的0x1fc00000,正是因为这个地址是直接映射的,不需要使用TLB转换,也不经过CACHE缓存,不需要使用CACHE结构,所以硬件上没有必要对TLB,CACHE这两个结构进行初始化,只需由硬件提供机制,软件在启动过程中在对其进行初始化即可。

环境搭建写到这里,下面简要分析一下MIPS版的Linux内核,从调度入手:

save_some会调用get_saved_sp从kernelsp结构中获取current对应的内核栈指针,而且是在 判断prev状态为user mode的时候才会这样做,这说明什么? 说明从用户空间通过系统调用进来的时刻,内核栈一定是空的,但在内核中睡眠的用户态线程,当前处于内核态,它们不需要这样的方式获取栈,判断依据是CP0_STATUS寄存器的CU0 BIT。

arch/mips/kernel/r4k_switch.S,相当于arm下的__switch_to,kernelsp维护者当前进程的内核态堆栈指针,铁打的营盘,流水的兵,任何用户态进程需要内核服务的时候,一猛子扎下来都要嫖一下kernelsp,发生点故事后再退回去,不留下一点云彩,所以用户态resume进来的时候,内核栈一定是空的,注意系统调用而非中断。

arch/mips/kernel/scall32-o32.S,相当于arm下的SVC异常入口,STI开中断,是宏定义。

 安装异常处理向量入口:

kernelsp的定义:

后面有时间在更新对ISA 中向量指令分析的一些文章。


结束!


版权声明:本文为tugouxp原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。