RISC-V处理器核设计优化与扩展指令集实现(三)

本文探讨了RISC-V架构的扩展指令集,特别是K扩展指令集,用于密码学加速,包括SM3、SHA256和SHA512。文章详细介绍了如何在硬件和软件层面实现定制指令,以及如何通过编译器支持来整合这些扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3.1 扩展指令集实现研究

3.1.1 自定义扩展指令集

RISC-V指令集提供了在32位和64位的基础指令集“I”,任何要实现RISC-V指令集的处理器必须实现这个基本的指令集,其它的指令集都是以扩展指令集的方式实现的。除了基本扩展指令集I,还提供了M、A、F、D、C基本扩展指令集。所有的32位和64位RISC-V指令可以归类为如图3-8所示6种基本类型,每种类型opcode、func、rs和rd字段均有不同。RISC-V指令用opcode字段、func3字段以及func7字段编码,rs1和rs2是源寄存器,imm是立即数,rd是指令的目的寄存器。

图3-8 RISC-V指令格式

RISC-V指令集规范允许用户自定义扩展指令,且并不局限于特定类型,但这些指令与现有基本指令集和扩展指令集兼容。这种可扩展指令集的特性促进了RISC-V架构向定制化和领域专用架构(DSA)加速器的方向发展,为用户实现面向多媒体、AI和安全等领域的自定义指令提供了可能,从而提高了在特定应用场景下的性能和效率。要实现RISC-V的定制或扩展指令,需要从三个方面入手:

(1)定义指令集,确保兼容已有实现的指令编码;

(2)修改软件,主要涉及riscv-gnu-toolchain工具包中的GCC和binutils工具;

(3)修改硬件,主要包括对流水线功能部件的改动和扩充,如添加译码级对扩展指令的译码以及添加执行级对扩展指令执行的支持。

3.1.2 K扩展指令集及其硬件设计

K扩展指令是RISC-V所定义的标量密码学扩展指令,支持对常见的加解密算法,包括SHA2-256、SHA2-512、AES以及国密算法SM3和SM4的加速。对于加解密算法的实现,使用高级语言如C语言编程会因存在大量重复运算而产生更多的指令,执行效率低,特别是在嵌入式设备中。虽然使用FPGA实现加解密加速器速度最快,但其通用性受限,且仅在FPGA上实现加解密算法也浪费资源。相比之下,使用加解密指令实现加解密算法兼顾通用性和硬件加速性能。

由于加解密算法种类众多,在RISC-V密码学指令集定义中,将K指令集扩展拆分成多个指令子集。每个指令子集支持不同的加解密算法。

表3-2 K扩展的子集

功能子集描述
ZkndAES解密指令
ZkneAES加密指令
ZkshSM3哈希函数指令
ZknhSHA2哈希函数指令
ZkseSM4加密指令
ZksdSM4解密指令
Zks商密算法集合,包括Zbkb、Zbkc、Zbkx、Zksed、Zksh
Zk标量加密结合,包括Zkn、Zkr、Zkt

本文在XQ900RV的基础上,扩展了密码学加速指令集K的子集,通过硬件电路实现了K指令集的SM3、SHA256和SHA512部分。K指令集采用两个源操作数和一个目的操作数,同时指令的编码字段包括opcode、func3和func7,每个指令通过这三个编码字段的唯一确定。表3-3详细列出了本文实现的K指令集扩展子集所涉及到的指令编码定义。

表3-3 K扩展指令定义

指令编码(func7_rs2_rs1_func3_rd_opcode)
sm3p0
sm3p1
sha256sum0
sha256sum1
sha256sig0
sha256sig1
sha512sum0r
sha512sum1r
0001000_01000_rs1_001_rd_0010011
0001000_01001_rs1_001_rd_0010011
0001000_00000_rs1_001_rd_0010011
0001000_00001_rs1_001_rd_0010011
0001000_00010_rs1_001_rd_0010011
0001000_00011_rs1_001_rd_0010011
0101000_rs2_rs1_000_rd_0110011
0101001_rs2_rs1_000_rd_0110011

续表3-3 K扩展指令定义

指令编码(func7_rs2_rs1_func3_rd_opcode)
sha512sig0l0101010_rs2_rs1_000_rd_0110011
sha512sig0h0101110_rs2_rs1_000_rd_0110011
sha512sig1l0101011_rs2_rs1_000_rd_0110011
sha512sig1h0101111_rs2_rs1_000_rd_0110011

指令操作如下:

(1)针对SM3算法中的 �0(�) 和 �1(�)

函数,K扩展使用两条指令对其中的运算进行加速:sm3p0和sm3p1。

该RISC-V指令采用I-type编码格式,用于执行SM3算法函数的计算。其中op字段指定所需执行的操作类型;rd字段为目标寄存器,rs1字段为源寄存器。对应的Sail代码实现如图3-9所示:

图3-9 SM3 K扩展指令Sail描述

(2)为了加速SHA256循环中的 ∑1� 、∑0� 、 ()�0(�) 和 �1

四个函数,K扩展使用四条指指令:sha256sum0、sha256sum1、sha256sig0和sha256sig1。

该指令使用RISC-V的I-type编码,完成∑1� 、∑0� 、 ()�0(�) 和 �1四个函数的计算,其中ror32(a, b)表示将32bit的数a循环右移b个bit;op字段表示操作类型;rd字段为目标寄存器,rs1字段为源寄存器。其Sail如图3-10所示:

图3-10 SHA256 K扩展指令Sail描述

(3)针对SHA512中的四个函数,K扩展使用六条指令进行加速。六条指令为:sha512sum0r、sha512sum1r、sha512sig0l、sha512sig0h、sha512sig1l和sha512sig1h。指令的Sail代码如图3-11所示:

图3-11 SHA512 K扩展指令Sail描述

实现加解密指令集硬件电路的关键在于中对常用算子的实现。这些算子主要由异或、移位和循环移位等操作组成。本文将加解密指令集的Sail语言描述转换成对应的Verilog硬件描述,然后将其集成到一个名为“exu_crypto”的执行单元。

该执行单元接收来自译码级的数据,并根据指令的解码信息确定加解密指令类型。执行单元将解码信息中对应的rs1、rs2以及imm字段送到“exu_crypto”相应指令的计算单元中执行。该执行单元一个周期返回执行结果,并设置输出使能表示输出数据的有效性。

对于加解密指令的译码则只需要在原有处理器流水线的译码单元根据加解密指令opcode以及function字段编码信息译码出对应的加解密指令,以SM3和Sha256指令译码为例,其代码如图3-12所示。

图3-12 部分加解密指令的译码

加解密指令被译码出来以后会封装成一个加解密指令的总线信息,这个总线信息包含加解密指令的类型以及加解密所使用的源操作数和立即数信息。

总之,为了增加XQ900RV处理器流水线对加解密指令集的支持,需要更改流水线的两个主要部分。第一部分是译码级,需要在该级增加对K扩展指令的译码,以判断当前指令是否为加解密指令。第二部分为执行级,若指令为加解密指令,则将指令的操作数和立即数信息送到加解密指令执行单元,并在一个时钟周期内返回计算结果,将加解密指令单元输出信号拉高并将结果写回总线。在硬件修改过程中,流水线中需要修改的部分在图3-13中加深,使用少量的硬件扩展便可实现对加解密算法加速。

图3-13 添加K扩展涉及的流水线部件改动

3.1.3 扩展指令集的编译器支持

RISC-V扩展指令集在处理器中执行依赖于编译器。编译器将高层次语言编译、链接为可执行程序,即将高级语言的程序翻译成二进制或者十六进制的程序流,以编译C语言为例,过程如图3-14所示。

图3-14 软件编译流程图

RISC-V GNU Toolchain是RISC-V平台实现C/C++语言的开源跨平台编译器。官方提供的工具链支持架构rv32i或rv64i以及MAFD的标准扩展。在构建GCC工具的环境中,用“--with-arch”指定GCC的目标指令集架构;用“--with-abi”指定硬件所支持的浮点计算的类型;用“--prefix”指定生成工具链的目标文件夹;用“-j”指定编译时使用的主机硬件线程数目。

为编译器增加扩展指令支持两种方法,方法1是只修改binutils;方法二不仅仅修改binutils,还要修改GCC目录下的文件。下面分别介绍这两种方法。

(1) 方法1:

从Github下载riscv-gnu-toolchain开源代码后,首先要确定指令的格式,以及指令各个字段的值,然后根据指令的这些字段确定指令的编码值和掩码值。编码值的确定方法是将指令中使用的源操作数和目的操作数所在位置零得到;掩码值是将寄存器所在位置0使用的位置1得到。例如在图3-15的sha256sum0指令中,将rs1和rd置零可得到该指令的编码值为0x10001013,将rs1和rd置0,同时将其余使用的位置1就可以得到该指令的掩码值为0xfff0707f。

图3-15 Sha256sum0指令

同理可以得到本文实现的所有加解密扩展指令的编码值和掩码值,如表3-4所示。

表3-4 The encoded and mask values of the K instructions

Tab. 3-4 Training platform configuration

以SHA256系列指令为例,为了让编译器识别到对应的指令,需要将编码值(MH)和掩码值(MK)信息定义在配置文件riscv-opc.h中,如图3-16所示。

图3-16 编码值和掩码值的宏定义

然后修改riscv-opc.c文件中的“const struct riscv-opcode riscv-opcodes[]”数组,数组中的每一项都是一个riscv_opcode结构体类型的变量。在结构体数组中添加K扩展指令的描述,如图3-17所示。

图3-17 K扩展指令的描述

从左到右依次是:指令名,指令的长度(32表示是32位指令,64表示是64位指令,0表示32或64均可)、指令的类型、寄存器参数、编码、掩码、编码方式以及宏定义。

至此为止可以直接重新编译工具链,然后就可以使用扩展指令了。由于只修改了binutils,这样编译器无法实现指令自动编译,仅能将C语言中的内联汇编直接翻译过来。因此C语言调用该指令时只能使用内联汇编的方式,以sha256sum0指令为例,可以包装成如下函数来使用,如图3-18所示。

图3-18 内联汇编构成的函数

(2) 方法2:

这种方法不仅仅修改binutils,还要修改GCC工具,在GCC中声明指令集架构、版本号以及指令的机器描述,最后声明指令的builtin函数。主要修改文件及内容如表3-5所示。

表3-5 riscv-gcc修改内容

Tab. 3-5 Modifications of riscv-gcc

文件修改内容
riscv-common.c定义的扩展指令flag、扩展信息与版本号
riscv.opt同步定义的flag更新到的MASK以及TargetVariable变量
multilib-generator.pyriscv-opt.hcrypto.mdrvkintrin.hriscv-builtins-crypto.def同步更新指令扩展信息,将参数正确传递给binutils同步更新关于MSAK和TargetVariable变量的信息定义指令机器描述文件描述寄存器行为,并导入到riscv.md文件中添加加解密指令宏汇编生成与寄存器行为内联函数定义built-in函数,声明指令信息

本文使用Ubuntu21.4系统,CPU为Intel i7 12700H,修改完成后重新编译,首先执行“./configure”命令,成功配置GCC编译选项,如图3-19所示。

图3-19 配置GCC编译选项

然后执行命令“./configure --prefix="$PWD/rv32k" --with-arch=rv32gc_zknh_zksh --with-abi=ilp32d”。其中,使用“--prefix”选项指令生成工具链的文件夹;使用“-with-arch=rv32gc_zknh_zksh”选项指明GCC目标指令集支持IMAFDC并且支持本文增加的SHA2-256、SHA2-512以及SM3的指令集扩展;使用“--with-abi=ilp32d”选项指明编译器目标平台支持硬件双精度浮点。编译器此时可以识别到zknh和zksh指令扩展标志。

执行“make -j12”命令进行编译,“-j12”选项指定使用12个CPU的硬件线程编译。编译时长约为40分钟,编译成功没有报错,如图3-20所示。

图3-20 GCC编译完成

编译完成后,在用“--prefix="$PWD/rv32k"”指定的文件夹中可以看到生成的工具链。子文件夹“bin”中就是编译生成的工具。如图3-21所示,编译完成后生成GCC、g++、ld、objcopy以及objdump等常用的工具,工具链前缀为“riscv32-sdu-elf-”。

图3-21 生成的GCC工具链

为了测试编译器能否正确调用修改后的builtin函数,实现加解密指令的编译,编写如下测试用例“rvk_ext_test.c”,如图3-22所示。

图3-22 加解密指令测试程序

使用构建好的工具链“riscv32-sdu-elf-gcc”和“riscv32-sdu-elf-objdump”先后执行编译和反汇编。编译将以上测试用例编译成可执行文件,然后使用可执行文件进行反汇编,观察反汇编以后的代码是否存在已经定义好的加解密指令,如果编译不成功或者没有生成对应的加解密指令,则编译器修改有误,需要重新修改。

执行编译命令“./riscv32-sdu-elf-gcc -o rvk_ext_test rvk_ext_test.c”,将测试文件“rvk_ext_test.c”编译生成可执行文件“rvk_ext_test”;执行反汇编命令“./riscv32-sdu-elf-objdump -S -D rvk_ext_test > rvk_ext_test.dump”,使用上一步生成的可执行文件生成反汇编代码。

在生成的反汇编代码文件“rvk_ext_test.dump”中搜索加解密指令,可以发现编译器已经正确生成了加解密指令的汇编代码,部分反汇编代码如图3-23所示。

图3-23 反汇编后生成加解密指令

本节介绍了两种编译器修改方法:第一种方法仅修改了binutils,需要在C语言中嵌入内联函数。虽然这种方法简单,但使用不够便利。第二种方法不仅修改了工具链中的binutils,还更新了编译器中的GCC扩展版本号、定义机器描述、构建builtin函数接口等。本节在第一种方法的基础上,继续实现第二种方法,在编译器中构建了builtin函数,并编写了简单的测试函数来验证它们的功能。测试结果表明,编译器已能够成功识别内建函数并将其编译成对应的加解密指令。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值