二進位檔操作說明解碼器教學課程

本教學課程的目標是:

  • 瞭解二進位格式說明檔案的結構和語法。
  • 瞭解二進位格式說明如何與 ISA 說明相符。
  • 為 RiscV RV32I 部分指令撰寫二進位說明。

總覽

RiscV 二進位指令編碼

二進位指令編碼是將操作說明編碼的標準方式 透過微處理器執行這些檔案通常儲存在可執行檔中 通常使用 ELF 格式指示可以是固定寬度或可變動 寬度。

一般而言,指示會使用少量的編碼格式,每種格式都有。 ,根據編碼指示類型自訂。例如註冊 指令可能會使用一種格式,以最大化可用的運算碼 而應立即以其他方式取捨 可用的運算碼,可增加可編碼的立即大小。 分支版本和跳轉指示幾乎一律會使用可將 為了支援具有較大偏移量的分支版本。

我們要在 RiscV 中解碼的操作說明採用的指示格式 模擬工具如下:

註冊操作說明所用的 R-Type 格式:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
func7 rs2 rs1 func3 運算程式碼

I-Type 格式,用於註冊中操作說明、載入指示和 jalr 指令,12 位元立即生效。

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版
12 5 3 5 7
imm12 rs1 func3 運算程式碼

特殊的 I-Type 格式,用於能立即位移,5 位元 立即:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
func7 uimm5 rs1 func3 運算程式碼

U-Type 格式,用於長方即時指示 (luiauipc),20 位元 立即:

12 月 31 日 11..7 6..0 版
20 5 7
uimm20 運算程式碼

B-Type 格式,用於條件分支版本,12 位元立即。

31 30.25 2020 年 24 月 19:15 14 月 12 日 11..8 7 6..0 版
1 6 5 5 3 4 1 7
rs2 rs1 func3 運算程式碼

J-Type 格式,用於 jal 指令,即時 20 位元。

31 2021 年 3 月 30 日 20 19:12 11..7 6..0 版
1 10 1 8 5 7
運算程式碼

S-Type 格式,儲存指示後可使用 12 位元格式。

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
rs2 rs1 func3 運算程式碼

如這些格式所示,所有指示的長度都是 32 位元,且 也就是每個格式的 7 位元是 opcode 欄位同時請注意 幾種格式的尺寸會立即相同,其位元的來源 指示分成不同的部分如畫面所示 就能表達出這一點

二進位編碼說明

指示的二進位編碼以二進位格式表示 (.bin_fmt) 說明檔案。它會說明 操作說明,以便系統擷取二進位格式指示 。產生的解碼器會判斷運算碼,並擷取 運算和即時欄位,以提供 ISA 所需的資訊 先前教學課程中所述的編碼式解碼器

在這個教學課程中,我們會針對子集,編寫二元編碼說明檔案 才能模擬 小型的「Hello World」計畫。如要進一步瞭解 RiscV ISA,請參閱 Risc-V 規格{.external}。

先開啟檔案: riscv_bin_decoder/riscv32i.bin_fmt

檔案內容分為幾個部分。

首先是 decoder 定義。

decoder RiscV32I {
  // The namespace in which code will be generated.
  namespace mpact::sim::codelab;
  // The name (including any namespace qualifiers) of the opcode enum type.
  opcode_enum = "OpcodeEnum";
  // Include files specific to this decoder.
  includes {
    #include "riscv_isa_decoder/solution/riscv32i_decoder.h"
  }
  // Instruction groups for which to generate decode functions.
  RiscVInst32;
};

解碼器定義會指定解碼器 RiscV32I 的名稱,以及 以及四項其他資訊第一個是 namespace,用來定義 要放置產生的程式碼所在的命名空間其次, opcode_enum,可為產生的運算程式碼列舉類型命名 而是在產生的程式碼中參照第三 includes {} 會指定程式碼產生的所需檔案 編碼器與解碼器模型 在這個案例中,這是由 ISA 解碼器 請參閱上一堂教學課程 可在全域範圍的 includes {} 中指定其他 include 檔案 定義如果定義了多個解碼器,而且需要 加入一些相同檔案第四是指引名稱清單 來構成解碼器的產生指示群組在我們 結果只有一個:RiscVInst32

接下來是三種格式定義分別代表不同的指示 32 位元指示用字的格式 檔案中。

// The generic RiscV 32 bit instruction format.
format Inst32Format[32] {
  fields:
    unsigned bits[25];
    unsigned opcode[7];
};

// RiscV 32 bit instruction format used by a number of instructions
// needing a 12 bit immediate, including CSR instructions.
format IType[32] : Inst32Format {
  fields:
    signed imm12[12];
    unsigned rs1[5];
    unsigned func3[3];
    unsigned rd[5];
    unsigned opcode[7];
};

// RiscV instruction format used by fence instructions.
format Fence[32] : Inst32Format {
  fields:
    unsigned fm[4];
    unsigned pred[4];
    unsigned succ[4];
    unsigned rs1[5];
    unsigned func3[3];
    unsigned rd[5];
    unsigned opcode[7];
};

第一個是定義名為 Inst32Format 的 32 位元寬指示格式,其中包含 bits (寬 25 位元) 和 opcode (寬 7 位元)。每個欄位都是 unsigned:表示在擷取值時,會延伸零 並放在 C++ 整數類型中位元欄位寬度的總和必須 就會等於格式的寬度如果發生問題,這項工具會產生錯誤 。這個格式並不衍生其他格式,因此 視為頂層格式

第二個則定義名為 IType 的 32 位元寬指示格式,衍生自 ,由 Inst32Format 製作,並使這兩種格式相關。格式包含 5 個 欄位:imm12rs1func3rdopcodeimm12 欄位是 signed,這表示當值在 並放在 C++ 整數型別中請注意,IType.opcode 和 相同的帶正負號/無正負號屬性,且是指相同的指令文字位元 使用 Inst32Format.opcode

第三種格式是僅供 fence 使用的自訂格式 指令:預先指定的指示, 因此您不需要擔心本教學課程中的問題

重點提示:以不同相關格式重複使用欄位名稱,只要欄位名稱 代表相同的位元且具有相同的正負號/未簽署屬性。

riscv32i.bin_fmt 的格式定義完成後,就會成為指令群組 定義老師群組中的所有指令都必須相同 並使用取自 頂層操作說明格式針對 ISA 提供操作說明的情況, 則每種長度都使用不同的指示群組此外, 如果目標 ISA 解碼需要仰賴執行模式 (例如 Arm 和 Thumb) 指令,每個模式都必須建立個別的指示群組。 bin_fmt 剖析器會為每個指令群組產生二進位解碼器。

instruction group RiscV32I[32] "OpcodeEnum" : Inst32Format {
  fence   : Fence  : func3 == 0b000, opcode == 0b000'1111;
  csrs    : IType  : func3 == 0b010, rs1 != 0, opcode == 0b111'0011;
  csrw_nr : IType  : func3 == 0b001, rd == 0,  opcode == 0b111'0011;
  csrs_nw : IType  : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
};

指令群組會定義名稱 RiscV32I、寬度 [32]、 使用 "OpcodeEnum" 的 Ocode 列舉類型,以及基本指令 格式。運算碼列舉類型應與 格式獨立指示解碼器 編碼器和解碼器

每項操作說明編碼說明都包含 3 個部分:

  • 運算碼名稱,必須與指示中使用的名稱相同 解碼器說明
  • 運算程式碼所使用的指示格式。這種格式 用來滿足最後部分位元欄位的參照。
  • 以半形逗號分隔的位元欄位限制條件清單:==!=<<=>>= 都必須為運算程式碼才能成功比對 的說明文字

.bin_fmt 剖析器會使用所有這些資訊來建構具有以下功能的解碼器:

  • 提供適合每個位元的擷取函式 (帶正負號/未簽署) 欄位。擷取器函式位於命名空間中 使用 Sake-case 版本的格式名稱命名舉例來說, 格式 IType 的擷取器函式位於命名空間 i_type 中。 每個擷取器函式都會宣告為 inline,採用最小 uint_t 表示格式寬度的 類型,然後傳回最窄的 int_t (適用於未簽署的)、uint_t (適用於未簽署欄位) 類型,保存擷取的欄位 寬度。例如:
inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}
  • 每個指示群組的解碼函式。該函式會傳回類型 OpcodeEnum,並採用最窄的 uint_t 類型,該類型包含寬度的 指示群組格式

執行初始建構作業

將目錄變更為 riscv_bin_decoder,並使用 以下指令:

$ cd riscv_bin_decoder
$ bazel build :all

現在將目錄改回存放區根目錄,接著來看看 所產生的來源為此,請將目錄變更為 bazel-out/k8-fastbuild/bin/riscv_bin_decoder (假設您使用 x86 格式) host - 如果是其他主機,k8-fastbuild 會是另一個字串)。

$ cd ..
$ cd bazel-out/k8-fastbuild/bin/riscv_bin_decoder
  • riscv32i_bin_decoder.h
  • riscv32i_bin_decoder.cc

產生的標頭檔案 (.h)

開啟 riscv32i_bin_decoder.h。檔案的第一個部分包含標準檔案 樣板防護、包含檔案、命名空間宣告。然後 命名空間「internal」中有範本輔助函式。這個函式 用於擷取格式太長而無法容納 64 位元 C++ 的位元欄位 整數值。

#ifndef RISCV32I_BIN_DECODER_H
#define RISCV32I_BIN_DECODER_H

#include <iostream>
#include <cstdint>

#include "third_party/absl/functional/any_invocable.h"


#include "learning/brain/research/mpact/sim/codelab/riscv_isa_decoder/solution/riscv32i_decoder.h"

namespace mpact {
namespace sim {
namespace codelab {


namespace internal {

template <typename T>
static inline T ExtractBits(const uint8_t *data, int data_size,
                            int bit_index, int width) {
  if (width == 0) return 0;

  int byte_pos = bit_index >> 3;
  int end_byte = (bit_index + width - 1) >> 3;
  int start_bit = bit_index & 0x7;

  // If it is only from one byte, extract and return.
  if (byte_pos == end_byte) {
    uint8_t mask = 0xff >> start_bit;
    return (mask & data[byte_pos]) >> (8 - start_bit - width);
  }

  // Extract from the first byte.
  T val = 0;
  val = data[byte_pos++] & 0xff >> start_bit;
  int remainder = width - (8 - start_bit);
  while (remainder >= 8) {
    val = (val << 8) | data[byte_pos++];
    remainder -= 8;
  }

  // Extract any remaining bits.
  if (remainder > 0) {
    val <<= remainder;
    int shift = 8 - remainder;
    uint8_t mask = 0b1111'1111 << shift;
    val |= (data[byte_pos] & mask) >> shift;
  }
  return val;
}

}  // namespace internal

初始部分之後有三個命名空間 riscv32i.bin_fmt 檔案中 format 宣告的結構:


namespace fence {

...

}  // namespace fence

namespace i_type {

...

}  // namespace i_type

namespace inst32_format {

...

}  // namespace inst32_format

在每個命名空間中,inline位元欄位擷取函式 每個位元欄位都會由該格式定義此外,基礎格式 從子系格式擷取重複函式 1) 欄位名稱只會出現在單一欄位名稱中,或是 2) 欄位名稱參照相同的類型欄位 (帶正負號/無正負號和位元位置) 都能提供不同的內容即可啟用描述相同值的位元欄位 要透過頂層格式命名空間中的函式擷取的位元。

i_type 命名空間中的函式如下所示:

namespace i_type {

inline uint8_t ExtractFunc3(uint32_t value) {
  return  (value >> 12) & 0x7;
}

inline int16_t ExtractImm12(uint32_t value) {
  int16_t result = ( (value >> 20) & 0xfff) << 4;
  result = result >> 4;
  return result;
}

inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}

inline uint8_t ExtractRd(uint32_t value) {
  return  (value >> 7) & 0x1f;
}

inline uint8_t ExtractRs1(uint32_t value) {
  return  (value >> 15) & 0x1f;
}

}  // namespace i_type

最後,用於指示的解碼器函式的函式宣告 已宣告 RiscVInst32 群組。這個值需要 32 位元的未簽署值做為 並傳回 OpcodeEnum 列舉類別成員 不相符或 OpcodeEnum::kNone (沒有相符項目)。

OpcodeEnum DecodeRiscVInst32(uint32_t inst_word);

產生的來源檔案 (.cc)

現在開啟 riscv32i_bin_decoder.cc。檔案的第一個部分包含 #include 和命名空間宣告,然後是解碼器函式 宣告:

#include "riscv32i_bin_decoder.h"

namespace mpact {
namespace sim {
namespace codelab {

OpcodeEnum DecodeRiscVInst32None(uint32_t);
OpcodeEnum DecodeRiscVInst32_0(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_3(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_3c(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_5c(uint32_t inst_word);

DecodeRiscVInst32None 用於空白解碼動作,例如: 傳回 OpcodeEnum::kNone。其他三個函式會構成 產生的解碼器整體解碼器會以階層方式運作。一組 是為了區分指令文字中的位元數 或一組指示群組。元件 以連續線來說位元數量會決定對照表的大小 會填入第二層解碼器函式您將在 的區段:

absl::AnyInvocable<OpcodeEnum(uint32_t)> parse_group_RiscVInst32_0[kParseGroupRiscVInst32_0_Size] = {
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32None, &DecodeRiscVInst32_0_3,
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,

    ...

    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32_0_3c, &DecodeRiscVInst32None,

    ...
};

最後,解碼器函式的定義如下:

OpcodeEnum DecodeRiscVInst32None(uint32_t) {
  return OpcodeEnum::kNone;
}

OpcodeEnum DecodeRiscVInst32_0(uint32_t inst_word) {
  if ((inst_word & 0x4003) != 0x3) return OpcodeEnum::kNone;
  uint32_t index;
  index = (inst_word >> 2) & 0x1f;
  index |= (inst_word >> 7) & 0x60;
  return parse_group_RiscVInst32_0[index](inst_word);
}

OpcodeEnum DecodeRiscVInst32_0_3(uint32_t inst_word) {
  return OpcodeEnum::kFence;
}

OpcodeEnum DecodeRiscVInst32_0_3c(uint32_t inst_word) {
  if ((inst_word & 0xf80) != 0x0) return OpcodeEnum::kNone;
  return OpcodeEnum::kCsrwNr;
}

OpcodeEnum DecodeRiscVInst32_0_5c(uint32_t inst_word) {
  uint32_t rs1_value = (inst_word >> 15) & 0x1f;
  if (rs1_value != 0x0)
    return OpcodeEnum::kCsrs;
  if (rs1_value == 0x0)
    return OpcodeEnum::kCsrsNw;
  return OpcodeEnum::kNone;
}

OpcodeEnum DecodeRiscVInst32(uint32_t inst_word) {
  OpcodeEnum opcode;
  opcode = DecodeRiscVInst32_0(inst_word);
  return opcode;
}

在本例中,如果只定義了 4 個指令,則 以及非常稀疏的對照表如 解碼器的結構將會改變 可能會增加


新增註冊註冊 ALU 操作說明

接下來,請在 riscv32i.bin_fmt 檔案中新增一些指示。 第一組操作說明是註冊 ALU 操作說明,例如 addand 等。在 RiscV32 中,這些權限都會使用 R 型二進位檔指示 格式:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
func7 rs2 rs1 func3 運算程式碼

首先,我們要新增格式。直接開啟 riscv32i.bin_fmt。在 Inst32Format 允許 新增名為 RType 的格式,該格式衍生自 Inst32Format。所有位元欄位 RType的價格為 unsigned。使用名稱、位元寬度和順序 (從左到右) 定義格式如果您需要提示 完整解決方案 請按這裡

接下來,我們要新增指示。操作步驟如下:

  • add - 加入整數,
  • and:位元和。
  • or:位元或。
  • sll - 將邏輯往左移。
  • sltu - 設定小於或未簽署的字元。
  • sub - 減去整數。
  • xor - 位元 xor。

其編碼如下:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版 opcode 名稱
000,0000 rs2 rs1 000 011 0011 add
000,0000 rs2 rs1 111 011 0011
000,0000 rs2 rs1 110 011 0011
000,0000 rs2 rs1 001 011 0011 斯爾
000,0000 rs2 rs1 011 011 0011 史詩
010,0000 人 rs2 rs1 000 011 0011 替補球員
000,0000 rs2 rs1 100 011 0011 Xor
func7 func3 運算程式碼

請將這些指令定義放在 RiscVInst32 個指示群組。二進位字串的指定 0b 前置字元 (類似於十六進位數字的 0x)。為了方便起見 讀取二進位數字的長字串,您也可以插入單引號 ', 視需要調整數字分隔符

每項指令定義都會有三個限制,也就是 func7func3opcode。除了 sub 以外,func7 限制將 品牌:

func7 == 0b000'0000

func3 限制會因大多數操作說明而異。適用於 addsub,它是:

func3 == 0b000

以下每個操作說明的 opcode 限制都相同:

opcode == 0b011'0011

請記得以半形分號 ; 終止每一行。

已完成的解決方案是 請按這裡

現在,繼續按照之前的方式建構專案,並開啟 riscv32i_bin_decoder.cc 檔案。您會看到其他解碼器函式 以便處理新的指令。大多數情況下 與先前產生的範本類似 DecodeRiscVInst32_0_c 用於 add/sub 解碼:

OpcodeEnum DecodeRiscVInst32_0_c(uint32_t inst_word) {
  static constexpr OpcodeEnum opcodes[2] = {
    OpcodeEnum::kAdd,
    OpcodeEnum::kSub,
  };
  if ((inst_word & 0xbe000000) != 0x0) return OpcodeEnum::kNone;
  uint32_t index;
  index = (inst_word >> 30) & 0x1;
  return opcodes[index];
}

這個函式會產生一個靜態解碼表,查詢值是 ,選取適當的索引。這麼做會新增 是指示解碼器階層的第二層 直接在表格中查詢,沒有進一步比較,系統會以這個方式內嵌 函式而非需要其他函式呼叫


立即新增 ALU 指示

下一組操作說明為 ALU 指示,使用 即時值,而不是其中一個暫存器。我們有三個群組 指令 (根據即時欄位):I-Type 立即指示 以及 12 位元立即簽署的特殊 I-Type 立即指示 以及 U-Type 會立即使用 20 位元無正負號的即時值。 格式如下:

I-Type 立即格式:

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版
12 5 3 5 7
imm12 rs1 func3 運算程式碼

專用的 I-Type 立即格式:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
func7 uimm5 rs1 func3 運算程式碼

U-Type 立即格式:

12 月 31 日 11..7 6..0 版
20 5 7
uimm20 運算程式碼

riscv32i.bin_fmt 中已有 I-Type 格式,因此不需要 新增該格式

如果我們比較專門的 I-Type 格式和先前在 在上一個練習中,我們發現唯一的差別在於 rs2 欄位 已重新命名為 uimm5。與其新增特殊格式 R-Type 格式。我們無法新增另一個欄位, 但我們可以加入疊加層。「疊加層」是一組別名的別名 編碼中,還可以用來合併多個值的子序列 轉換為獨立的具名實體最終效果是產生的程式碼 現在,除了疊加層外, 檢視畫面也會加入擷取函式 是給欄位的看法在本範例中,rs2uimm5 皆未簽署 並沒有太大的差別,而是明確指出欄位使用了該欄位 立即取得如要在 R-Type 格式中加入名為 uimm5 的疊加層,請在 在最後一個欄位後面:

  overlays:
    unsigned uimm5[5] = rs2;

我們需要新增的唯一新格式是 U-Type 格式。加上 格式,以下是使用該格式的兩個操作說明:auipclui。兩者會在使用前,先將 20 位元的直接值移往 12 ,即可將其新增至暫存器 (auipc),或直接將電腦寫入暫存器中 (lui)。透過重疊,我們可提供原始的 將一些運算作業從指示執行轉為指導 解碼器。首先,根據表格中指定的欄位新增格式 。接著,我們可以加入下列疊加層:

  overlays:
    unsigned uimm32[32] = uimm20, 0b0000'0000'0000;

疊加層語法不僅可讓我們串連欄位,還能將常值參照 在本例中,我們將該變數與 12 個零串連 前 12 名。

需要新增的 I-Type 操作說明如下:

  • addi - 立即新增。
  • andi - 位元和立即。
  • ori:位元或立即。
  • xori - 具有立即的位元 xor。

其編碼如下:

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版 opcode_name
imm12 rs1 000 001 0011 Addi
imm12 rs1 111 001 0011 安迪
imm12 rs1 110 001 0011 Ori
imm12 rs1 100 001 0011 Xori
func3 運算程式碼

我們需要新增的 R-Type (專屬 I-Type) 說明如下:

  • slli - 立即將左移邏輯列。
  • srai - 立即將右算法調整。
  • srli - 立即將右移邏輯運算。

其編碼如下:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版 opcode 名稱
000,0000 uimm5 rs1 001 001 0011 Solli
010,0000 人 uimm5 rs1 101 001 0011 斯拉伊
000,0000 uimm5 rs1 101 001 0011 斯里
func7 func3 運算程式碼

需要新增的 U-Type 操作說明如下:

  • auipc - 立即將上限新增至電腦。
  • lui - 立即載入上方。

其編碼如下:

12 月 31 日 11..7 6..0 版 opcode 名稱
uimm20 001 0111 auipc
uimm20 011 0111 盧伊
運算程式碼

請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.bin_fmt


下一組需要定義的指令是條件分支版本 說明、跳轉連結說明和快速連結註冊 指示

要新增的條件分支全都使用 B-Type 編碼。

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 運算程式碼

雖然版面配置中的 B-Type 編碼與 R-Type 編碼相同,但 配合 RiscV 說明文件,選擇使用新的格式類型。 但您也可以新增疊加層,以便取得正確的分支版本 使用 R-Type 的 func7rd 欄位,立即取代位移 編碼。

BType」格式必須含有上述欄位,但「不」需要 而負責任的 AI 技術做法 有助於達成這項目標如您所見,立即分成兩個指示欄位。 此外,分支指示不算是將其視為 這兩個欄位的值而是將每個欄位進一步分區,然後是這些分區 會以不同順序串連最後,將該值往左移 以取得 16 位元對齊的位移。

用於形成直接形成指示詞中的位元序列為:31、 7、30..25、11..8。這對應下列子欄位參照,其中 索引或範圍會指定欄位中的位元,也就是從右到左編號,也就是 imm7[6] 是指 imm7 的 msb,imm5[0] 則是 lsb 的 lsb。 imm5

imm7[6], imm5[0], imm7[5..0], imm5[4..1]

將位元操控部分設定為分支指示本身,有兩個 也有許多缺點首先,將語意函式的實作結果 二進位檔指示表示法中的詳細資料。其次,這樣做會增加執行階段 同時免除不必要的負擔答案是將疊加層加到 BType 格式,包括 結尾「0」所以務必要考量左移

  overlays:
    signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;

請注意,疊加層已經簽署,因此會自動展開 所擷取的指令

跳連結 (立即) 指示使用 J-Type 編碼:

12 月 31 日 11..7 6..0 版
20 5 7
imm20 運算程式碼

這個格式也容易新增,同樣地, 指導不如預期那麼簡單編碼器-解碼器架構 如下圖所示:31、19..12、20、30..21,最終為立即 目前移到左移 1,表示半文字對齊。解決方法是 依照左移 (21 位元為左側偏移) 調整格式:

  overlays:
    signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;

如您所見,疊加層的語法支援在 欄位。此外,如果未使用欄位名稱,則 編號是指指令文字本身,因此上述指令也可以是 寫成:

    signed j_imm[21] = [31, 19..12, 20, 30..21], 0b0;

最後,跳轉與連結 (註冊) 會使用以前的 I 型格式 像是剛才說過,即便 VM 正在運作 您還是能變更 VM 可用性政策

I-Type 立即格式:

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版
12 5 3 5 7
imm12 rs1 func3 運算程式碼

這次不用變更格式。

需要新增的分支版本指示如下:

  • beq - 分支版本 (如果相等)。
  • bge - 大於或等於分支版本。
  • bgeu - 大於或等於未簽署的分支版本。
  • blt - 所屬的分支版本,
  • bltu - 分支版本 (如果小於未簽署文件)。
  • bne - 否則為分支版本。

這些程式碼的編碼方式如下:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版 opcode 名稱
imm7 rs2 rs1 000 imm5 110 0011 Beq
imm7 rs2 rs1 101 imm5 110 0011 Bge
imm7 rs2 rs1 111 imm5 110 0011 Bgeu
imm7 rs2 rs1 100 imm5 110 0011 blt
imm7 rs2 rs1 110 imm5 110 0011 bltu
imm7 rs2 rs1 001 imm5 110 0011 胸部
func3 運算程式碼

jal 指令的編碼方式如下:

12 月 31 日 11..7 6..0 版 opcode 名稱
imm20 110 1111 Jal
運算程式碼

jalr 指令的編碼方式如下:

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版 opcode_name
imm12 rs1 000 110 0111 jalr
func3 運算程式碼

請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.bin_fmt


新增商店操作說明

商店指示使用 S-Type 編碼,與 B-Type 相同 分支指令所使用的編碼 (除了 可立即上手我們選擇加入 SType 格式,以便與 RiscV 保持一致 說明文件。

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 運算程式碼

使用 SType 格式時,最立即是直觀的 前向串連兩個直接欄位,因此疊加層規格 就是:

  overlays:
    signed s_imm[12] = imm7, imm5;

請注意,串連整個欄位時,不需要位元範圍指定碼。

商店指示的編碼方式如下:

2025 年 31 月 31 日 2020 年 24 月 19:15 14 月 12 日 11..7 6..0 版 opcode 名稱
imm7 rs2 rs1 000 imm5 010 0011 sb
imm7 rs2 rs1 001 imm5 010 0011 淺睡
imm7 rs2 rs1 010 imm5 010 0011 sw
func3 運算程式碼

請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.bin_fmt


新增負載指示

載入指示使用 I-Type 格式。因此不需要在該處進行任何變更。

編碼方式如下:

2020 年 3 月 31 日 19:15 14 月 12 日 11..7 6..0 版 opcode_name
imm12 rs1 000 000 0011 英磅
imm12 rs1 100 000 0011
imm12 rs1 001 000 0011
imm12 rs1 101 000 0011 lhu
imm12 rs1 010 000 0011 lw
func3 運算程式碼

請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.bin_fmt

教學課程到此結束,希望對您有所幫助。