本教學課程的目標是:
- 瞭解 MPACT-Sim 模擬器如何表示指示。
- 瞭解 ISA 說明檔案的結構和語法。
- 為 RiscV RV32I 部分指示撰寫 ISA 說明
總覽
在 MPACT-Sim 中,目標指示會進行解碼並儲存在內部 表示法以便獲得更多資訊 執行這些指示執行個體會在操作說明中進行快取 快取,以便減少經常執行指示的次數 執行狀態
教學課程
開始前,先瞭解 指示
是以 MPACT-Sim 代表的Instruction
類別已定義於
mpact-sim/mpact/sim/generic/instruction.h.
指令類別執行個體包含 模擬「執行」時的指令,例如:
- 操作說明地址、模擬指示大小,即 .text 中的大小。
- 命令 Ocode。
- 述詞運算元介面指標 (如適用)。
- 來源運算元介面指標的向量。
- 目的地運算元介面指標的向量。
- 語意函式可呼叫。
- 架構狀態物件的指標。
- 結構定義物件的指標。
- 指向子項和下一個指示例項的指標。
- 拆解字串。
這些執行個體通常會儲存在指示 (執行個體) 快取中。 都會重複使用。這麼做可以提高效能 執行任務
除了結構定義物件的指標以外,所有都會填入 根據 ISA 說明產生的指示解碼器。為此 教學課程 不需要瞭解這些項目的詳細資料,因為我們不會 直接使用這些功能而是會概略說明這些容器的使用方式 而負責任的 AI 技術做法 有助於達成這項目標
語意函式可呼叫為 C++ 函式/方法/函式物件
(包括 lambda) 實作指令語意。適用對象
舉例來說,對於 add
指令,它會載入每個來源運算元,並將兩個
運算結果,並將結果寫入單一目的地運算元。主題
語意函式教學課程將詳細說明語意函式。
指令運算元
指令類別包含三種運算元介面的指標: 述詞、來源和目的地。這些介面可讓語意函式 而不受實際指令類型影響 運算元。例如,存取暫存器的值與立即存取的過程 可透過相同的介面這意味著在執行 執行不同運算元 (例如暫存器與動畫) 之間的運算 使用相同的語意函式實作
述詞運算元介面,適用於支援指定參數的 ISAs。 指令執行 (對其他 ISA 為空值) 可用於判斷 指定的指示應根據述詞的布林值執行。
// The predicte operand interface is intended primarily as the interface to
// read the value of instruction predicates. It is separated from source
// predicates to avoid mixing it in with the source operands needed for modeling
// the instruction semantics.
class PredicateOperandInterface {
public:
virtual bool Value() = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
virtual ~PredicateOperandInterface() = default;
};
來源運算元介面可允許指令語意函式讀取 值,不會與基礎運算元無關 類型。介面方法可同時支援純量和向量值運算元。
// The source operand interface provides an interface to access input values
// to instructions in a way that is agnostic about the underlying implementation
// of those values (eg., register, fifo, immediate, predicate, etc).
class SourceOperandInterface {
public:
// Methods for accessing the nth value element.
virtual bool AsBool(int index) = 0;
virtual int8_t AsInt8(int index) = 0;
virtual uint8_t AsUint8(int index) = 0;
virtual int16_t AsInt16(int index) = 0;
virtual uint16_t AsUint16(int) = 0;
virtual int32_t AsInt32(int index) = 0;
virtual uint32_t AsUint32(int index) = 0;
virtual int64_t AsInt64(int index) = 0;
virtual uint64_t AsUint64(int index) = 0;
// Return a pointer to the object instance that implements the state in
// question (or nullptr) if no such object "makes sense". This is used if
// the object requires additional manipulation - such as a fifo that needs
// to be pop'ed. If no such manipulation is required, nullptr should be
// returned.
virtual std::any GetObject() const = 0;
// Return the shape of the operand (the number of elements in each dimension).
// For instance {1} indicates a scalar quantity, whereas {128} indicates an
// 128 element vector quantity.
virtual std::vector<int> shape() const = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
virtual ~SourceOperandInterface() = default;
};
目的地運算元介面提供配置和處理的方法
DataBuffer
例項 (用來儲存註冊值的內部資料類型)。A 罩杯
目的地運算元也有延遲時間,也就是
等待指示所分配到的資料緩衝區執行個體
語意函式可用來更新目標暫存器的值。適用對象
舉例來說,add
指令的延遲時間可能是 1,而 mpy
則可能是 4。有關這點,請參閱
語意函式的教學課程
// The destination operand interface is used by instruction semantic functions
// to get a writable DataBuffer associated with a piece of simulated state to
// which the new value can be written, and then used to update the value of
// the piece of state with a given latency.
class DestinationOperandInterface {
public:
virtual ~DestinationOperandInterface() = default;
// Allocates a data buffer with ownership, latency and delay line set up.
virtual DataBuffer *AllocateDataBuffer() = 0;
// Takes an existing data buffer, and initializes it for the destination
// as if AllocateDataBuffer had been called.
virtual void InitializeDataBuffer(DataBuffer *db) = 0;
// Allocates and initializes data buffer as if AllocateDataBuffer had been
// called, but also copies in the value from the current value of the
// destination.
virtual DataBuffer *CopyDataBuffer() = 0;
// Returns the latency associated with the destination operand.
virtual int latency() const = 0;
// Return a pointer to the object instance that implmements the state in
// question (or nullptr if no such object "makes sense").
virtual std::any GetObject() const = 0;
// Returns the order of the destination operand (size in each dimension).
virtual std::vector<int> shape() const = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
};
ISA 說明
處理器的 ISA (指令集架構) 會定義抽像模型 軟體與硬體互動的方式這會定義 可用的指令、資料類型、暫存器及其他機器 指令的運作以及其行為 (語意)。目的 但 ISA 不包含操作說明的實際編碼。 將另行處理。
處理器 ISA 會在說明檔案中提供 指令位於各編碼通用的抽象層級。說明檔案 列舉一組可用指示。針對每個指示 必須列出名稱、運算元的數量和名稱,以及 繫結至實作語意的 C++ 函式/可呼叫項目。此外, 就可以指定反組譯碼格式字串,以及指示使用 硬體資源名稱。前者有助於產生 偵錯、追蹤或互動用途的指示表示法。 後者可用於模擬模擬中的週期準確度。
Isa 剖析器會剖析 ISA 說明檔案,進而產生 通用表示法的解碼器解碼器會負責 填入指示物件的欄位。具體值包括 目的地暫存器號碼,從特定格式的特定指示中取得 編碼器和解碼器其中一個解碼器是二進位解碼器 下個單元將深入探討 活化函數等類神經網路
這個教學課程說明如何編寫簡單的純量 ISA 說明檔案 這個架構的簡短總覽我們會使用一組 RiscV RV32I 指令 說明上述概念並和其他教學課程 建構能使用的模擬器 模擬「Hello World」計畫。如要進一步瞭解 RiscV ISA,請參閱 Risc-V 規格。
先開啟檔案:
riscv_isa_decoder/riscv32i.isa
檔案內容分成多個部分。首先是 ISA 宣告:
isa RiscV32I {
namespace mpact::sim::codelab;
slots { riscv32; }
}
這宣告 RiscV32I
為 ISA 名稱,程式碼產生器會
建立名為 RiscV32IEncodingBase
的類別,定義要接收的介面
所產生的解碼器會使用 來取得運算碼和運算元資訊名稱
這個類別是由 ISA 名稱轉換成 Pascal 命名法,
將此函式與 EncodingBase
串連。宣告 slots { riscv32; }
指定 RiscV32I 中只有一個指令版位 riscv32
ISA (與 VLIW 指示中的多個插槽不同),
有效的指令是在 riscv32
中定義的執行。
// First disasm fragment is 15 char wide and left justified.
disasm widths = {-15};
這表示任何故意首次拆解的片段 規格 (詳情請見下方) 15 個半形字元, 欄位系統會將任何後續片段附加至這個欄位,但不含 所有額外間距
這個下方有三個版位宣告:riscv32i
、zicsr
和 riscv32
。
根據上述 isa
定義,只有為 riscv32
定義的指示
版位將是 RiscV32I
區域的一部分另外兩個時段分別適合使用哪些功能?
運算單元可用來將操作說明分成不同的群組,
合併為一個版位請注意,: riscv32i, zicsr
標記
導入 riscv32
版位宣告中。這會指定 riscv32
運算單元沿用
版位 zicsr
和 riscv32i
中定義的所有指示。RiscV 32 位元 ISA
包含一組名為「RV32I」的 ISA 基準,
。運算單元機制可讓這些擴充功能中的指示採用
然後視需要合併為最後部分
。在這個範例中, RiscV「I」是否已定義
與「zicsr」不同的值群組。您可以定義其他群組
「M」的結果(乘除/除號),「F」(單精度浮點)、「D」
(雙精度浮點)、「C」(16 位元精簡指示) 等。
。
// The RiscV 'I' instructions.
slot riscv32i {
...
}
// RiscV32 CSR manipulation instructions.
slot zicsr {
...
}
// The final instruction set combines riscv32i and zicsr.
slot riscv32 : riscv32i, zicsr {
...
}
您不需要變更 zicsr
和 riscv32
版位定義。不過
本教學課程的重點是將必要的定義新增至 riscv32i
。讓我們進一步瞭解這個版位目前的定義:
// The RiscV 'I' instructions.
slot riscv32i {
// Include file that contains the declarations of the semantic functions for
// the 'I' instructions.
includes {
#include "learning/brain/research/mpact/sim/codelab/riscv_semantic_functions/solution/rv32i_instructions.h"
}
// These are all 32 bit instructions, so set default size to 4.
default size = 4;
// Model these with 0 latency to avoid buffering the result. Since RiscV
// instructions have sequential semantics this is fine.
default latency = 0;
// The opcodes.
opcodes {
fence{: imm12 : },
semfunc: "&RV32IFence"c
disasm: "fence";
ebreak{},
semfunc: "&RV32IEbreak",
disasm: "ebreak";
}
}
首先是 includes {}
區段,列出需要的標頭檔案
加到產生的程式碼中 (直接參照或
採用最終的 ISA 程序也可以將檔案列於全域
範圍為 includes {}
區段,此時一律會納入。這可以
如果需要將相同的納入檔案加到每個版位
定義
default size
和 default latency
宣告會定義其定義,除非
否則,指示的大小是 4,而延遲載入為
目的地運算元寫入為 0 個週期。請注意,指示大小
是由程式計數器遞增的大小,
下一個連續指示在模擬中執行
處理器這個大小不一定與
輸入執行檔中的指示表示法。
運算單元定義的中心就是「opcode」區段。如您所見
opcode (指示) fence
和 ebreak
目前已經在
riscv32i
。定義 fence
運算碼的方式是指定名稱 (fence
) 並
運算元規格 ({: imm12 : }
),後面則是可選用的反組譯碼
格式 ("fence"
),以及要繫結為語意的可呼叫函式
函式 ("&RV32IFence"
)。
指示運算元被指定為三元組,每個元件各有
中間以半形分號分隔,述詞「:」來源運算元清單「:」
目的地運算元清單。來源和目的地運算元清單為半形逗號
運算元名稱清單。如畫面所示
fence
指令包含無述詞運算元,但僅限單一來源
運算元名稱 imm12
,而且沒有目的地運算元。RiscV RV32I 子集
不支援預先指定的執行作業,因此述詞運算元一律為空白
其他範例
語意函式指定為指定 C++ 的必要字串
函式或可呼叫的項目,用於呼叫語意函式。簽署的
語意函式/呼叫為 void(Instruction *)
。
反組譯碼規格由逗號分隔的字串清單組成。
通常只會使用兩個字串,一個用於運算碼,另一個用於
運算元。格式化後 (在「操作說明」中使用 AsString()
呼叫),每個
字串在欄位中採用以 disasm widths
為準的格式
規格。
下列練習可協助您在 riscv32i.isa
檔案中加入操作說明
足以模擬「Hello World」計畫。適合趕時間的話
您可前往
riscv32i.isa
和
rv32i_instructions.h。
執行初始建構
如果您尚未將目錄變更為 riscv_isa_decoder
,請立即進行。接著
請按照以下方式建構專案 - 這項建構作業應會成功
$ cd riscv_isa_decoder
$ bazel build :all
現在將目錄改回存放區根目錄,接著來看看
所產生的來源為此,請將目錄變更為
bazel-out/k8-fastbuild/bin/riscv_isa_decoder
(假設您使用 x86 格式)
host - 如果是其他主機,k8-fastbuild 會是另一個字串)。
$ cd ..
$ cd bazel-out/k8-fastbuild/bin/riscv_isa_decoder
在這個目錄中,其他檔案會包含以下內容 產生的 C++ 檔案:
riscv32i_decoder.h
riscv32i_decoder.cc
riscv32i_enums.h
riscv32i_enums.cc
首先,請在瀏覽器中按一下riscv32i_enums.h
。請
您就會發現其中包含類似下方的內容:
#ifndef RISCV32I_ENUMS_H
#define RISCV32I_ENUMS_H
namespace mpact {
namespace sim {
namespace codelab {
enum class SlotEnum {
kNone = 0,
kRiscv32,
};
enum class PredOpEnum {
kNone = 0,
kPastMaxValue = 1,
};
enum class SourceOpEnum {
kNone = 0,
kCsr = 1,
kImm12 = 2,
kRs1 = 3,
kPastMaxValue = 4,
};
enum class DestOpEnum {
kNone = 0,
kCsr = 1,
kRd = 2,
kPastMaxValue = 3,
};
enum class OpcodeEnum {
kNone = 0,
kCsrs = 1,
kCsrsNw = 2,
kCsrwNr = 3,
kEbreak = 4,
kFence = 5,
kPastMaxValue = 6
};
constexpr char kNoneName[] = "none";
constexpr char kCsrsName[] = "Csrs";
constexpr char kCsrsNwName[] = "CsrsNw";
constexpr char kCsrwNrName[] = "CsrwNr";
constexpr char kEbreakName[] = "Ebreak";
constexpr char kFenceName[] = "Fence";
extern const char *kOpcodeNames[static_cast<int>(
OpcodeEnum::kPastMaxValue)];
enum class SimpleResourceEnum {
kNone = 0,
kPastMaxValue = 1
};
enum class ComplexResourceEnum {
kNone = 0,
kPastMaxValue = 1
};
enum class AttributeEnum {
kPastMaxValue = 0
};
} // namespace codelab
} // namespace sim
} // namespace mpact
#endif // RISCV32I_ENUMS_H
如您所見,在
riscv32i.isa
檔案是在其中一種列舉類型中定義。此外,
有一個 OpcodeNames
陣列儲存了運算法的所有名稱 (這是
定義於 riscv32i_enums.cc
)。另一個檔案包含產生的解碼器
我們會在其他教學課程中詳細說明
Bazel 建構規則
Bazel 的 ISA 解碼器目標是透過名為
mpact_isa_decoder
,是從 mpact/sim/decoder/mpact_sim_isa.bzl
載入
在 mpact-sim
存放區中在本教學課程中,可以看到
riscv_isa_decoder/BUILD
是:
mpact_isa_decoder(
name = "riscv32i_isa",
src = "riscv32i.isa",
includes = [],
isa_name = "RiscV32I",
deps = [
"//riscv_semantic_functions:riscv32i",
],
)
這項規則會呼叫 ISA 剖析器工具和產生器產生 C++ 程式碼。
接著,將產生的程式碼編譯成其他規則可依附於
「//riscv_isa_decoder:riscv32i_isa
」標籤。使用「includes
」部分
指定來源檔案可能包含的其他 .isa
檔案。
isa_name
是用來指定哪一個特定 isa (如果有多個)
的檔案名稱。
新增註冊 ALU 操作說明
接下來,請在 riscv32i.isa
檔案中新增一些指示。第一個
操作說明群組是註冊 ALU 操作說明,例如 add
and
等。在 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 | 日 | 運算程式碼 |
雖然 .isa
檔案是用來產生跨格式解碼器,但檔案仍不會
考慮二進位格式及其版面配置來引導項目。如同你
有三個欄位與填入
指令物件:rs2
、rs1
和 rd
。目前我們選擇使用
這些註冊方式的整數暫存器名稱採用相同的編碼方式 (位元序列),
產生的指示值。
我們稍後要新增的操作說明如下:
add
- 加入整數,and
:位元和。or
:位元或。sll
- 將邏輯往左移。sltu
- 設定小於或未簽署的字元。sub
- 減去整數。xor
- 位元 xor。
這些操作說明都會加到opcodes
riscv32i
個運算單元定義。如前所述,我們必須指定名稱、運算碼
每個指示的反組譯碼和語意函式名稱很簡單
我們只用上面的 opcode 名稱此外,它們都使用相同的運算元
我們可以使用 { : rs1, rs2 : rd}
做為運算元規格。也就是說
rs1 指定的註冊來源運算元在來源中的索引為 0
指令物件中的運算元向量,是指定的註冊來源運算元
的 rs2 將索引 1,而 rs2 指定的註冊目的地運算元
會是目的地運算元向量中唯一的元素 (位於索引 0)。
接下來說明語意函式規格方法是使用關鍵字
semfunc
和 C++ 字串,指定可用來指派可呼叫的呼叫項目
至 std::function
。在本教學課程中,我們將使用函式,因此可以呼叫的
字串將是 "&MyFunctionName"
。使用
fence
指示,應為 "&RV32IAdd"
、"&RV32IAnd"
等。
最後是拆解規格開頭為 disasm
這個關鍵字,
後面接著以半形逗號分隔的字串清單,用於指定
指示應以字串的形式顯示。在%
運算元名稱代表使用
該運算元如果是 add
指令,則應為:disasm: "add", "%rd,
%rs1,%rs2"
。這表示 add
指令的項目應該看起來
例如:
add{ : rs1, rs2 : rd},
semfunc: "&RV32IAdd",
disasm: "add", "%rd, %rs1, %rs2";
請直接編輯 riscv32i.isa
檔案,然後將這些指示加入
.isa
說明。如果你需要協助 (或要查看作業),請參閱
說明檔案為
請按這裡。
將操作說明新增至 riscv32i.isa
檔案後,就需要
為每個新的語意函式新增函式宣告
已參照這個位置:rv32i_instructions.h
`../semantic_functions/.如果您需要協助 (或想查看作業)
答案是
請按這裡。
完成上述操作後,請返回 riscv_isa_decoder
目錄及重新建構您可以檢查產生的來源檔案。
新增 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 | 日 | 運算程式碼 |
如您所見,運算元名稱 rs1
和 rd
與
是用來代表整數暫存器,因此這些名稱可以是
續用折扣即時值欄位的長度和位置不同,且
兩組 (uimm5
和 uimm20
) 為未簽署,但 imm12
則是簽署。每個
這些名稱會使用自己的名稱
因此,I-Type 指令的運算元應為 { : rs1, imm12 :rd
}
。如需特殊的 I-Type 指示,則應為 { : rs1, uimm5 : rd}
。
U-Type 指令運算元規格應為 { : uimm20 : rd }
。
需要新增的 I-Type 操作說明如下:
addi
- 立即新增。andi
- 位元和立即。ori
:位元或立即。xori
- 具有立即的位元 xor。
需要新增的特殊 I-Type 指示如下:
slli
- 立即將左移邏輯列。srai
- 立即將右算法調整。srli
- 立即將右移邏輯運算。
需要新增的 U-Type 操作說明如下:
auipc
- 立即將上限新增至電腦。lui
- 立即載入上方。
運算程式碼要使用的名稱自然取決於指示名稱
以上 (你們不必想到新的帳戶,因為它們都是獨一無二的)。這張投影片的內容
指定語意函式,
請回想一下,指示物件經過編碼
通用運算元通用的來源運算元介面
類型。也就是說,適用於含有相同作業的指令,
可能不同,且可以共用相同的語意函式。舉例來說:
如果發生以下情況,addi
指令會執行與 add
指令相同的作業:
第一,會忽略運算元類型,因此可以使用相同的語意函式
"&RV32IAdd"
規格。andi
、ori
、xori
和 slli
也是一樣。
其他操作說明會使用新的語意函式,但應為這些函式命名
取決於作業,而非運算元,因此 srai
會使用 "&RV32ISra"
。
U-Type 指令 auipc
和 lui
沒有註冊對等項目,因此可以正常運作
以便使用 "&RV32IAuipc"
和 "&RV32ILui"
。
拆解字串與先前練習中的字串非常相似,但
一如預期,參照 %rs2
的參照已替換為 %imm12
、%uimm5
、
或 %uimm20
(如適用)。
請繼續進行變更並建構。檢查產生的輸出內容。就像 您可以先檢查自己的實驗 riscv32i.isa 和 rv32i_instructions.h。
新增分支版本和快速連結指示
我們需要加入分支版本和跳轉連結的操作說明,均使用目的地
僅於指示中隱含的運算元,即下一個電腦
值。在這個階段,我們會把這個註解當成名稱的適當運算元
next_pc
。後續教學課程會進一步定義這項設定。
分支版本操作說明
我們新增的分支全都使用 B-Type 編碼。
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 | 嗯 | 嗯 | 運算程式碼 |
不同的直接欄位會串連為 12 位元即時簽署
值。由於格式不相關,我們會立即呼叫此參數
bimm12
,適用於 12 位元分支版本。片段處理的解決方式為
下個單元將示範如何建立二進位解碼器所有
分支版本操作說明會比較 rs1 和 rs2 指定的整數暫存器 (如果有的話)
條件為 true,則立即值會加入目前的 PC 值,
產生下一個要執行指示的位址。的運算元
因此分支版本操作說明應為 { : rs1, rs2, bimm12 : next_pc }
。
需要新增的分支版本指示如下:
beq
- 分支版本 (如果相等)。bge
- 大於或等於分支版本。bgeu
- 大於或等於未簽署的分支版本。blt
- 所屬的分支版本,bltu
- 分支版本 (如果小於未簽署文件)。bne
- 否則為分支版本。
這些運算代碼名稱不得重複,因此可以在 .isa
中重複使用
生成 3D 物件當然,必須新增語意函式名稱,例如
"&RV32IBeq"
等
由於
指令會用於計算目的地,但實際上並不包含
指令運算元。但這是儲存在 Google 伺服器的資訊中
此為指示物件可用的物件時,就視為可用。解決方法是使用
運算式語法。不使用「%」後面接著
運算元名稱,您可以輸入 %(expression: print format)。只相當單純
支援運算式,但地址加上偏移量是其中一個運算式,其中 @
符號,用於目前的指示地址。列印格式與
C 樣式 printf 格式,但沒有開頭的 %
。解譯碼格式
beq
指令就會變為:
disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"
快速連結說明
只需要新增兩段跳轉與連結的操作說明,jal
(跳至連結及連結) 和
jalr
(間接跳連結)。
jal
指令使用 J-Type 編碼:
31 | 2021 年 3 月 30 日 | 20 | 19:12 | 11..7 | 6..0 版 |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
嗯 | 嗯 | 嗯 | 嗯 | 日 | 運算程式碼 |
如同分支指令,20 位元會立即分散在
多個欄位,因此我們將其命名為 jimm20
。分割不重要
但會在下次
這個教學課程到此告一段落運算元
但會成為 { : jimm20 : next_pc, rd }
請注意
目的地運算元、下一個 PC 值,以及
指示
與上述分支版本操作說明類似,拆解格式會變為:
disasm: "jal", "%rd, %(@+jimm20:08x)"
間接的跳連結使用 I-Type 格式與 12 位元立即可用。這項服務
會將正負號即時值新增到
rs1
可產生目標指示地址。連結的作用是
由 rd
指定的整數暫存器。
2020 年 3 月 31 日 | 19:15 | 14 月 12 日 | 11..7 | 6..0 版 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | 日 | 運算程式碼 |
如果您已看過這個模式,您現在應該推斷運算元規格
jalr
應為 { : rs1, imm12 : next_pc, rd }
,
規格:
disasm: "jalr", "%rd, %rs1, %imm12"
請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.isa 和 rv32i_instructions.h。
新增商店操作說明
商店指示非常簡單。它們都使用 S-Type 格式:
2025 年 31 月 31 日 | 2020 年 24 月 | 19:15 | 14 月 12 日 | 11..7 | 6..0 版 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
嗯 | rs2 | rs1 | func3 | 嗯 | 運算程式碼 |
如畫面所示,這是 12 位元立即分割的另一個情況
命名為 simm12
。商店指示所有儲存整數的值
透過新增 rs2 取得記憶體的有效位址
將 rs1 指定的整數暫存器值設為
12 位元會立即生效運算元格式應為 { : rs1, simm12, rs2 }
。
所有店面操作說明
需要實作的商店指示如下:
sb
- 儲存位元組。sh
- 儲存半字。sw
- 儲存字詞。
sb
的反組譯碼規格如下:
disasm: "sb", "%rs2, %simm12(%rs1)"
語意函式規格也應如此:"&RV32ISb"
、
依此類推
請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.isa 和 rv32i_instructions.h。
新增載入指示
載入操作說明的模型,與 模擬工具如要模擬延遲載入的情況, 不確定,載入指示會分為兩個不同的動作:1) 有效 地址運算和記憶體存取,以及 2) 結果寫回。在 將負載的語意動作分割成 兩個獨立的操作說明、主要指令和子項指示。此外 當我們指定運算元時,需要同時指定 main 和 child 指示。方法是將運算元規格 三連串清單The syntax is:
{(predicate : sources : destinations),
(predicate : sources : destinations), ... }
和先前一樣,載入指示均使用 I-Type 格式 說明:
2020 年 3 月 31 日 | 19:15 | 14 月 12 日 | 11..7 | 6..0 版 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | 日 | 運算程式碼 |
運算元規格會分割計算位址所需的運算元
然後從載入資料的註冊目的地啟動記憶體存取:
{( : rs1, imm12 : ), ( : : rd) }
。
由於語意動作會分割在兩個指令中,因此語意函式
也同樣需要指定兩個可呼叫元件如果是 lw
(載入字詞),這會是
寫入:
semfunc: "&RV32ILw", "&RV32ILwChild"
反組譯碼規格較為傳統。不會提及
子項指示。如果是 lw
,則應為:
disasm: "lw", "%rd, %imm12(%rs1)"
需要實作的載入指示如下:
lb
- 載入位元組。lbu
- 載入未簽署的位元組。lh
- 載入半字。lhu
- 載入未簽署的半字。lw
- 載入字詞。
請繼續進行變更,然後開始建構。檢查產生的輸出內容。只要 您可以先檢查 riscv32i.isa 和 rv32i_instructions.h。
感謝你的耐心配合。希望這對您有所幫助。