天天看點

riscv gcc中添加custom自定義指令

作者:小寶Q

riscv gcc中添加custom自定義指令

  • 1.概述
  • 2.riscv指令集基礎
  • 3.利用.insn模闆進行程式設計
  • 4.修改`binutils`讓riscv gcc認識到這條指令
    • 4.1 利用riscv-opcodes生成對應的宏
    • 4.2 修改`binutils`
    • 4.3 編譯與測試
  • 5.兩種辦法分析

1.概述

在riscv的處理器開發過程中,各家處理器往往都會涉及到自定義指令功能的添加。在處理器設計上,添加一些特定功能的指令是十分正常的,一般處理辦法本文會講述,讓其識别客戶自定義的指令。從現有的解決辦法上來看,第一種是可以利用Kito Cheng提供的.insn模闆進行開發,第二種則是修改binutils的方法。本文主要介紹這兩種辦法進行riscv custom指令的添加。

2.riscv指令集基礎

要想設計一條自定義的riscv指令,必須了解riscv指令的構成。

從riscv指令集手冊上來說,riscv的指令集被分成了R-type,I-type,S-type,B-type,U-type,J-type。

riscv gcc中添加custom自定義指令

每一種類型的指令的格式都不相同,按照特定的機器碼編排的指令有着特殊的用途。

在進行指令實驗時,可以通過自定義一條基礎整數指令開始。

按照劃分,riscv的子產品化指令集可以分成下列許多類型:

RV32I:整數基礎指令集
RV32M:乘除法
RV32F:單精度浮點
RV32D:雙精度浮點
RV32A:原子指令
RV32V:向量指令
RV32B:位操作
.
.
.
           

riscv基礎指令集中,主要分析R-type,同時可以自定義一條custom指令。

riscv gcc中添加custom自定義指令

custom的指令可以添加一條

比如自定義一條cube指令,該指令的作用是計算算數立方。

*      func7      rs2      rs1    func3           rd        opcode
 * 31---------25--------19------15------12----------------6----------0
 * | 000110    | 00000  | *****  |  110  |    *****       |  1111011 |
 * |------------------------------------------------------|----------|
           

設計完成指令後,就可以實作該指令了。

3.利用.insn模闆進行程式設計

在利用.insn模闆進行程式設計時,不需要修改riscv的gcc任何代碼,隻需要使用者根據指令編碼設計模型。

對于R-type的指令模闆構成,有下面的通用處理辦法:

.insn r opcode, func3, func7, rd, rs1, rs2
           

從c内聯彙編程式設計的程式設計方式,cube指令的實作可以通過下面的指令進行操作。

asm volatile(“.insn r 0x7b, 6, 6, %0, %1, x0” : “=r”(cube) : “r”(addr));
           

當然,也可以裸寫彙編,a0,a1寄存器中存放的是函數調用時的兩個參數。

.insn r 0x7b, 6, 6, a0, a1, x0
           

這樣就完成了一條指令的功能。x0在riscv架構中,始終為0,是以該指令實際上就是講a1的資料通過算數立方乘,将結果存放到a0寄存器。

通過手寫C代碼進行測試

static int custom_cube(int addr)
{
    int cube;
    asm volatile (
       ".insn r 0x7b, 6, 6, %0, %1, x0"
             :"=r"(cube)
             :"r"(addr)
     );
    return cube; 
}
           

反彙編後可以得到

a0002c74 <custom_cube>:
a0002c74: 7179                 addi sp,sp,-48
a0002c76: d622                 sw s0,44(sp)
a0002c78: 1800                 addi s0,sp,48
a0002c7a: fca42e23           sw a0,-36(s0)
a0002c7e: fdc42783           lw a5,-36(s0)
a0002c82: 0c07e7fb           0xc07e7fb
a0002c86: fef42623           sw a5,-20(s0)
a0002c8a: fec42783           lw a5,-20(s0)
a0002c8e: 853e                 mv a0,a5
a0002c90: 5432                 lw s0,44(sp)
a0002c92: 6145                 addi sp,sp,48
a0002c94: 8082                 ret
           

其中的0xc07e7fb,機器碼交給實際的硬體進行解析,隻要硬體設計指令按照指令規範即可。這樣就能夠實作算數立方的功能了。

4.修改binutils讓riscv gcc認識到這條指令

采用.insn模闆進行程式設計的缺點非常明顯,就是非常的複雜難懂,程式設計人員還需要知道每條指令的機器碼,這樣不利于riscv程式設計使用者的開發體驗。為了解決這樣的問題,可以通過修改binutils來解決。

4.1 利用riscv-opcodes生成對應的宏

首先定義好cube指令的格式後。

*      func7      rs2      rs1    func3           rd        opcode
 * 31---------25--------19------15------12----------------6----------0
 * | 000110    | 00000  | *****  |  110  |    *****       |  1111011 |
 * |------------------------------------------------------|----------|
           

下載下傳riscv-opcodes

https://github.com/riscv/riscv-opcodes
           

可生成對應的指令模闆。

首先建立一個opcodes-custom檔案。

添加如下的内容

cube rd rs1 rs2 31..25=0x0c 14..12=0x6 6..2=0x1e 1..0=3
           

其中的格式是按照定義好的指令序列進行排布。

接着輸入

cat opcodes-custom | python3 parse_opcodes -c > encoding.h
           

可看到encoding.h生成對應的檔案

DECLARE_INSN(cube, MATCH_CUBE, MASK_CUBE)
           

還生成下面的宏定義

#define MATCH_CUBE 0x1800607b
#define MASK_CUBE  0xfe00707f
           

4.2 修改binutils

在riscv-gnu-toolchain/riscv-binutils中,修改

include/opcode/riscv-opc.h
           

上述riscv-opcodes生成的三條宏定義放到該檔案中。

然後修改opcodes/riscv-opc.c中的指令定義。

{"cube",       0, INSN_CLASS_I, "d,s,t",  MATCH_CUBE, MASK_CUBE, match_opcode, 0 },
           

修改完成後,這樣就添加完成了。

4.3 編譯與測試

對于單獨編譯binutils,可以直接進入到build-binutils-newlib。

輸入make -j8 && make install。不用全部重新編譯riscv gcc效率比較高。

如果是第一次編譯riscv-gnu-toolchain,則沒有build-binutils-newlib,需要全部重新編譯:

./configure --prefix=$RISCV --enable-multilib --with-cmodel=medany
make -j8
           

測試時,可以寫内聯彙編

static int custom_cube(int addr)
{
    int cube;
    asm volatile (
       "cube %0, %1, x0"
             :"=r"(cube)
             :"r"(addr)
     );
    return cube; 
}
           

很容易,也可以在彙編檔案中寫

cube a0,a1,zero
           

因為x0寄存器表示zero,是以這樣寫是等價的。

通過反彙編後可以看到解析代碼如下:

a0002c74 <custom_cube>:
a0002c74: 7179                 addi sp,sp,-48
a0002c76: d622                 sw s0,44(sp)
a0002c78: 1800                 addi s0,sp,48
a0002c7a: fca42e23           sw a0,-36(s0)
a0002c7e: fdc42783           lw a5,-36(s0)
a0002c82: 1807e7fb           cube a5,a5,zero
a0002c86: fef42623           sw a5,-20(s0)
a0002c8a: fec42783           lw a5,-20(s0)
a0002c8e: 853e                 mv a0,a5
a0002c90: 5432                 lw s0,44(sp)
a0002c92: 6145                 addi sp,sp,48
a0002c94: 8082                 ret
           

直接寫彙編,gcc已經可以識别到cube指令了。

嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和内容,導緻工資要不上去!無償分享大家一個資料包,差不多150多G。裡面學習内容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。

點選這裡找小助理0元領取:加微信領取資料

riscv gcc中添加custom自定義指令
riscv gcc中添加custom自定義指令

5.兩種辦法分析

riscv添加新的自定義指令,利用.insn的好處是不用修改riscv gcc的代碼,所有的riscv gcc均可進行編譯,但是需要了解指令的操作碼,對于應用程式程式設計來說比較複雜,更加适合硬體指令的功能驗證。而采用修改binutils則需要單獨維護一個與riscv gcc主線分離的版本,單獨釋出,更适合晶片方案廠商。雖然修改riscv gcc并不是一件很容易的事情,但是對使用者來說,操作體驗更好。

原文連結:https://mp.weixin.qq.com/s/K-5RJcplqflbjLCOt6VGWQ

轉載自:嵌入式IoT

文章來源于bigmagic,嵌入式IoT

版權聲明:本文來源網絡,免費傳達知識,版權歸原作者所有,如涉及作品版權問題,請聯系我進行删除。

繼續閱讀