开源处理器Rocket的自定义指令研究与测试

2017-05-13 01:09雷思磊
单片机与嵌入式系统应用 2017年5期
关键词:测试程序模拟器寄存器

雷思磊

(酒泉卫星发射中心,酒泉 735000)

开源处理器Rocket的自定义指令研究与测试

雷思磊

(酒泉卫星发射中心,酒泉 735000)

Rocket是基于RISC-V指令集架构的开源处理器,其实现了RISC-V的三条自定义指令custom0、custom1、custom2,在分析Rocket中自定义指令的处理过程后,编写测试程序,验证了自定义指令的实现。

Rocket;RoCC;RISC-V

引 言

RISC-V是加州大学伯克利分校(University of California at Berkeley,以下简称UCB)设计并发布的一种开源精简指令集架构,其目标是成为指令集架构领域的Linux,应用覆盖IoT(Internet of Things)设备、桌面计算机、高性能计算机等领域[1]。RISC-V自2014年正式发布以来,得到了包括谷歌、IBM、Oracle等在内的众多企业,以及包括剑桥大学、苏黎世联邦理工大学、印度理工学院、中国科学院在内的众多知名学府与研究机构的关注和参与,围绕RISC-V的生态环境逐渐完善,并涌现了众多开源处理器及SoC采用RISC-V架构。Rocket就是采用RISC-V指令集的开源处理器,本文研究分析了Rocket处理器对于RISC-V中自定义指令的实现原理,并进行了测试。

1 Rocket处理器简介

Rocket是UCB设计的一款基于RISC-V指令集、5级流水线、单发射顺序执行的64位处理器,主要特点有:

① 支持MMU,支持分页虚拟内存,所以可以移植Linux操作系统;

② 具有兼容IEEE 754-2008标准的FPU;具有分支预测功能,具有BPB(Branch Prediction Buff)、BHT(Branch History Table)、RAS(Return Address Stack)。

③ Rocket是采用Chisel(Constructing Hardware in an Scala Embedded Language)编写的,这也是UCB设计的一种开源硬件编程语言,是Scala语言的领域特定应用,可以充分利用Scala的优势,将面向对象(object orientation)、函数式编程(functional programming)、类型参数化(parameterized types)、类型推断(type inference)等概念引入硬件编程语言,从而提供更加强大的硬件开发能力。Chisel除了开源之外,还有一个优势就是使用Chisel编写的硬件电路可以通过编译得到对应的Verilog设计,还可以得到对应的C++模拟器。Rocket使用Chisel编写,就可以很容易得到对应的软件模拟器[2]。

2 RISCV指令集中的自定义指令

RISC-V指令集架构是一个灵活可扩展的架构,其中定义了4个自定义指令:custom0、custom1、custom2、custom3。其使用方法如下:

customX rd, rs1, rs2, funct

其中rs1、rs2是源操作数寄存器编码,rd是目的寄存器编码,funct是具体的操作类型编码。其二进制指令格式如图1所示。通过opcode的编码来区分custom0、custom1、custom2、custom3。xd、xs1、xs2分别表示rd、rs1、rs2对应的通用寄存器是否需要访问(读或者写)。在Rocket处理器中,默认实现了custom0、custom1、custom2指令,其通过RoCC(Rocket Custom Coprocessor)模块执行这三条自定义指令。

图1 customX指令的二进制格式

3 RoCC模块

RoCC是在Rocket处理器中设计的协处理器,用来执行自定义指令,从而有助于实现特定运算的加速执行。RoCC与其他模块的连接关系如图2所示[3],在实际应用中可以有多个RoCC模块,分别执行不同的自定义指令。

图2 RoCC与Rocket中其余模块的连接关系

从图2中可以发现,RoCC与Core、L1 DCache、FPU、PageTable Walker、L2 Bus都有接口连接,其中与Core、L1 DCache之间的接口是基本接口,可分为如下3组:

① Core Control:用来在RoCC与Rocket Core之间传递状态信息,比如RoCC正在执行指令,处于busy状态,就会通过CC_Busy传递给Rocket Core。图2中以CC开始的信息就是Core Control接口的内容。

② Register Mode:用来在RoCC与Rocket Core之间传递指令信息,比如源寄存器的值、指令内容等。图2中以Core开始的信息就是Register Mode接口的内容。

③ Memory Mode:用来在RoCC与L1 DCache之间传递数据,图2中以MEM开始的信息就是Memory Mode接口的内容。

其余的接口都是可选的扩展接口,可分为如下4组:

① Control Status Register:用来使得运行在Rocket Core上的Linux可以获取RoCC的状态信息,图2中以CSR开始的信息就是Control Status Register接口的内容。

② PageTable Walker:RoCC可以使用该接口进行虚拟地址到物理地址的转换,图2中以PTW开始的信息就是PageTable Walker接口的内容。

③ Float Point Unit:RoCC可以使用该接口向FPU收发数据,图2中以FPU开始的信息就是Float Point Unit接口的内容。

④ Uncached TileLink:RoCC可以使用该接口访问L2 Cache,图2中以UTL开始的信息就是Uncached TileLink接口的内容。

上述7组接口中,最基本的就是Register Mode接口,处理器发送指令给RoCC以及RoCC返回响应信息都是通过该接口实现的,该接口包括两个部分:Rocket Core发送给RoCC的指令信息(即Core Cmd)、RoCC返回给Rocket Core的结果信息(即Core Resp)。其中Core Cmd的定义如下:

//指令域的定义,共32bit

class RoCCInstruction extends Bundle{

val funct = Bits(width = 7)

val rs2 = Bits(width = 5)

val rs1 = Bits(width = 5)

val xd = Bool()

val xs1 = Bool()

val xs2 = Bool()

val rd = Bits(width = 5)

val opcode = Bits(width = 7)

}

class RoCCCommand(implicit p: Parameters) extends CoreBundle()(p) {

val inst = new RoCCInstruction

//在上面定义了RoCCInstruction类

val rs1 = Bits(width = xLen)

val rs2 = Bits(width = xLen)

val status = new MStatus

}

上文定义的类RoCCCommand就是Core Cmd,是Rocket Core发送给RoCC的指令,其中包括源寄存器rs1、rs2的值,以及对应的指令信息,后者通过RoCCInstruction类实现,其内容与图1中的结构是一一对应的。Core Resp的定义如下:

class RoCCResponse(implicit p: Parameters) extends

CoreBundle()(p) {

val rd = Bits(width = 5)

val data = Bits(width = xLen)

}

其中包括要写入的目的寄存器地址、要写入的数据。

4 默认的RoCC功能分析

Rocket处理器默认实现了3个RoCC,分别是AccumulatorExample、TranslatorExample、CharacterCountExample,均在rocc.scala中定义,其作用分别如下:

① AccumulatorExample:是一个累加器的例子,验证了RoCC与Rocket Core、L1 DCache的接口是否正常,用来处理指令custom0。

② TranslatorExample:通过计算一个虚拟地址对应的物理地址,验证了RoCC与PageTable Walker的接口是否正常,用来处理指令custom1。

③ CharacterCountExample:通过计算一个数据块中的特定字符的数量,验证RoCC的Uncache TileLink接口是否正常,用来处理指令custom2。

Rocket Core在译码的时候需要判断是否是自定义指令,如果是,那么会通过Core Cmd送出自定义指令,由RoccCommandRouter这个类分析判断是哪一条自定义指令,从而送入对应的RoCC进行处理并将结果返回给Rocket Core,如图3所示。

图3 RoccCommandRouter类进行RoCC指令与处理结果的分发

限于篇幅,本文重点对AccumulatorExample的实现加以分析和测试。当Rocket Core处理器在译码的时候发现是指令custom0,那么就会将相应的参数通过Core Cmd发送给AccumulatorExample这个RoCC,AccumulatorExample依据发送过的参数中funct的值进行具体处理,funct代表了具体的操作类型, AccumulatorExample支持的操作类型有:

① 将通用寄存器的值写入RoCC。AccumulatorExample在RoCC中定义了4个内部寄存器,可以将Rocket Core中某个通用寄存器的值写到这4个内部寄存器中。指令格式如下:

custom0 rd, rs1, rs2, 0

rd没有使用,可以任意;rs1为要读取的通用寄存器的编号;rs2的值等于0~3,表示RoCC内部寄存器的编号;最后的funct为0。

② 将RoCC内部寄存器的值写入通用寄存器。指令格式如下:

custom0 rd, rs1, rs2, 1

rd为要写入的通用寄存器编号;rs1没有使用,可以任意;rs2的值等于0~3,表示RoCC内部寄存器的编号;最后的funct为1。

③ 从L1 DCache加载数据到RoCC内部寄存器。指令格式如下:

custom0 rd, rs1, rs2, 2

rd没有使用,可以任意;rs1为通用寄存器的编号,该通用寄存器中存储的是数据的物理地址;rs2的值等于0~3,表示要加载到的RoCC内部寄存器的编号;最后的funct为2。

④ 将通用寄存器的值与RoCC内部寄存器的值相加,结果保存到RoCC内部寄存器。指令格式如下:

custom0 rd, rs1, rs2, 3

rd没有使用,可以任意;rs1为通用寄存器的编号;rs2的值等于0~3,表示要执行累加操作的RoCC内部寄存器的编号;最后的funct为3。

5 RoCC测试

本节通过Rocket自带的程序测试RoCC的功能是否正确,试验环境为Ubuntu14.04。

5.1 编译得到对应的模拟器

使用如下指令从Github上下载Rocket对应的代码,并编译得到相应的GCC编译器等工具。

$ git clone https://github.com/ucb-bar/rocket-chip.git

$ cd rocket-chip

$ git submodule update - -init

$ cd riscv-tools

$ git submodule update - -init - -recursive

$ export RISCV=/opt/riscv

$ ./build.sh

然后进入emulator目录,编译得到带RoCC功能的C++模拟器,如下:

$ cd emulator

$ make CONFIG=RoCCExampleConfig

在emulator目录下得到C++模拟器emulator-rocketchip-RoccExampleConfig。

5.2 修改Proxy Kernel

在Rocket处理器中有一个寄存器mstatus,其中有一个XS域,该域的值只有为非零的时候,才可以执行自定义指令,否则会出现无效指令异常。默认情况下该域的值为零,需要通过程序修改该值。

Proxy Kernel(简称pk)是一个轻量级的应用程序执行环境,能够在其上执行ELF程序[4]。对于使用C编写的应用程序,可以在pk中执行,此时处理器首先执行pk,准备好硬件环境,然后pk加载应用程序。

本文的测试程序为C代码,使用pk作为其执行环境,从而可以直接修改pk的代码,使得pk在准备硬件环境的时候就设置mstatus寄存器的XS域为一个非零值。具体过程就是修改pk目录下minit.c中的mstatus_init函数,在其中添加如下语句:

ms = INSERT_FIELD(ms, MSTATUS_XS, 3);

然后重新编译得到新的pk。

5.3 测试程序

使用C语言编写测试程序如下,该程序是在Rocket-chip提供的测试程序之上修改的,原测试程序经过测试有一定问题,第三个测试没有通过。

#include

#include

#include

int main() {

uint64_t x = 123, y = 456, z = 0;

//************** 测试一**************

//加载x的值到RoCC的内部寄存器2

asm volatile ("custom0 x0, %0, 2, 0" : : "r"(x));

//读取RoCC内部寄存器2的值,保存到变量z

asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));

//验证z是否等于x

assert(z == x);

//************** 测试二**************

//将变量y的值与RoCC内部寄存器2的值相加,结果存储 //到RoCC内部寄存器2

asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));

//读出RoCC内部寄存器2的值,保存到变量z

asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));

//验证z是否等于x+y

assert(z == x+y);

//************** 测试三**************

//测试三与测试二的过程是一致的,但是在测试三中是从 //L1 DCache中获取变量x的值

//使用custom1指令,获取变量x的物理地址,保存到通用 //寄存器x0

asm volatile ("custom1 x0, %0, 2, 0" : : "r"(&x));

//从L1 DCache中加载地址为x0的数据,保存到RoCC内 //部寄存器2

asm volatile ("custom0 x0, x0, 2, 2");

//将变量y的值与RoCC内部寄存器2的值相加,结果存储 //到RoCC内部寄存器2

asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));

//读出RoCC内部寄存器2的值,保存到变量z

asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));

//验证z是否等于x+y

assert(z == x+y);

printf("success! ");

}

上述测试程序可以分为三个部分,测试了AccumulatorExample、TranslatorExample两个RoCC,custom0、custom1两条自定义指令。使用gcc编译得到对应的ELF程序,使用前文得到的C++模拟器进行仿真测试,如下:

./emulator-rocketchip-RoccExampleConfig +max-cycles=10000000 +dramsim pk ../../test/rocctest/a.out

最后输出success,表示测试成功。

结 语

[1] Waterman A.The RISC-V Instruction Set Manual,Volume I:User-Level ISA,Version 2.1,2016.

[2] Chisel 2.2 Tutorial[EB/OL].[2016-12].https://chisel.eecs.berkeley.edu/2.2.0/chisel-tutorial.

[3] Anuj Rao.The RoCC Doc V2:An Introduction to the Rocket Custom Coprocessor Interface[EB/OL].[2016-12].https://docs.google.com/document/d/1CH2ep4YcL_ojsa3BVHEW-uwcKh1FlFTjH_kg5v8bxVw/edit.

[4] RISC-V Proxy Kernel[EB/OL].[2016-12].https://github.com/riscv/riscv-pk/tree/f892b43a2bb1c2405b9941aaefdb25 e3b4efe1f1.

雷思磊(工程师),主要研究方向为处理器架构、嵌入式处理器应用等。

Custom Instruction Research and Verification of Open Source Processor Rocket

Lei Silei

(Jiuquan Satellite Launch Center,Jiuquan 735000,China)

The Rocket is an open source processor based on RISC-V instruction set architecture,which implements the RISC-V three custom instructions including custom0,custom1 and custom2.After analyzing the principle of Rocket,the program is written to verify the implementation of the custom instructions.

Rocket;RoCC;RISC-V

TP368.1

A

士然

2016-12-12)

猜你喜欢
测试程序模拟器寄存器
了不起的安检模拟器
盲盒模拟器
划船模拟器
Lite寄存器模型的设计与实现
基于Castle型机械手的三温量产测试平台实现
分簇结构向量寄存器分配策略研究*
手机APP交互界面人因适合性测试程序的设计与实现
中心主导制订的《VHF/UHF频率范围内测向系统测向灵敏度的测试程序》等两项国际标准在ITU官网正式发布
电气自动化控制设备可靠性测试探讨
动态飞行模拟器及其发展概述