วัตถุประสงค์ของบทแนะนำนี้ได้แก่
- ดูวิธีการแสดงคำสั่งในเครื่องจำลอง MPACT-Sim
- เรียนรู้โครงสร้างและไวยากรณ์ของไฟล์คำอธิบาย ISA
- เขียนคำอธิบาย ISA สำหรับชุดย่อยของคำแนะนำ RiscV RV32I
ภาพรวม
ใน MPACT-ซิม คำสั่งเป้าหมายจะถูกถอดรหัสและจัดเก็บไว้ใน การนำเสนอข้อมูล ซึ่งทำให้ข้อมูลของพวกเขามีมากขึ้นและมีความหมาย ดำเนินการได้รวดเร็วยิ่งขึ้น อินสแตนซ์คำสั่งเหล่านี้จะแคชตามคำสั่ง แคชเพื่อลดจำนวนคำสั่งที่ดำเนินการตามคำสั่งเป็นประจำ ดำเนินการแล้ว
ชั้นเรียนการสอน
ก่อนที่เราจะเริ่มต้น คุณควรดูวิธีการสักเล็กน้อย
แสดงเป็น MPACT-Sim คลาส Instruction
ได้รับการกำหนดไว้ใน
mpact-sim/mpact/sim/generic/instruction.h.
อินสแตนซ์คลาสวิธีการมีข้อมูลทั้งหมดที่จำเป็นในการ จำลองคำสั่งเมื่อ "ดำเนินการ" เช่น
- ที่อยู่ที่ใช้สอน ขนาดคำสั่งจำลอง เช่น ขนาดในรูปแบบ .text
- รหัสการดำเนินการสำหรับวิธีการ
- ตัวชี้อินเทอร์เฟซตัวถูกดำเนินการแบบคาดการณ์ (หากมี)
- เวกเตอร์ของตัวชี้อินเทอร์เฟซตัวถูกดำเนินการต้นทาง
- เวกเตอร์ของตัวชี้อินเทอร์เฟซตัวถูกดำเนินการปลายทาง
- ฟังก์ชันความหมายที่เรียกใช้ได้
- ชี้ไปที่ออบเจ็กต์สถานะทางสถาปัตยกรรม
- ชี้ไปที่ออบเจ็กต์บริบท
- ตัวชี้ไปยังอินสแตนซ์ย่อยและอินสแตนซ์คำสั่งถัดไป
- สตริงการแยกส่วน
โดยทั่วไปแล้ว อินสแตนซ์เหล่านี้จะจัดเก็บไว้ในแคชคำสั่ง (อินสแตนซ์) และ นำมาใช้ซ้ำเมื่อใดก็ตามที่มีการดำเนินการตามคำแนะนำซ้ำ ซึ่งจะช่วยปรับปรุงประสิทธิภาพ ระหว่างรันไทม์
ยกเว้นตัวชี้ไปยังออบเจ็กต์บริบท ระบบจะเติมค่าทั้งหมดด้วยฟังก์ชัน เครื่องมือถอดรหัสคำสั่งที่สร้างขึ้นจากคำอธิบาย ISA สำหรับกรณีนี้ ในบทแนะนำ คุณไม่จำเป็นต้องรู้รายละเอียดของรายการเหล่านี้ เนื่องจากเราจะไม่ โดยใช้ตัวกรองโดยตรง แต่การทำความเข้าใจวิธีใช้งานในระดับสูง เพียงพอ
ฟังก์ชันความหมายที่เรียกใช้ได้คือออบเจ็กต์ฟังก์ชัน/เมธอด/ฟังก์ชัน C++
(รวมถึงแลมบ์ดา) ที่นำความหมายของการสอนมาใช้ สำหรับ
ตัวอย่างเช่น สำหรับคำสั่ง add
นั้นจะโหลดตัวถูกดำเนินการต้นทางแต่ละรายการ ให้เพิ่ม
ตัวถูกดำเนินการ และเขียนผลลัพธ์ไปยังตัวถูกดำเนินการปลายทางเดี่ยว หัวข้อของ
เราจะกล่าวถึงฟังก์ชันความหมายอย่างละเอียดในบทแนะนำฟังก์ชันความหมาย
ตัวถูกดำเนินการด้านวิธีการ
คลาสการสอนมีตัวชี้ไปยังอินเทอร์เฟซตัวถูกดำเนินการ 3 ประเภทดังนี้ คำกริยา ต้นทาง และปลายทาง อินเทอร์เฟซเหล่านี้ช่วยให้ฟังก์ชันเชิงความหมาย เขียนโดยไม่ขึ้นอยู่กับประเภทการสอนพื้นฐานที่แท้จริง ตัวถูกดำเนินการ ตัวอย่างเช่น การเข้าถึงค่าของการลงทะเบียนและข้อมูลในทันทีนั้น ผ่านอินเทอร์เฟซเดียวกัน ซึ่งหมายความว่าคำแนะนำจะ ทำงานในลักษณะเดียวกัน แต่ใช้ตัวถูกดำเนินการที่แตกต่างกัน (เช่น รีจิสเตอร์กับอิมพีท) ได้ โดยใช้ฟังก์ชันความหมายเดียวกัน
อินเทอร์เฟซตัวถูกดำเนินการแบบภาคแสดงสำหรับ ISA ที่รองรับ การดำเนินการตามคำแนะนำ (สำหรับ 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
รายการ (ประเภทข้อมูลภายในที่ใช้เก็บค่าการลงทะเบียน) ต
ตัวถูกดำเนินการปลายทางยังมีเวลาในการตอบสนองที่เชื่อมโยงด้วย ซึ่งก็คือตัวเลข
ของรอบเพื่อรอจนกว่าอินสแตนซ์บัฟเฟอร์ข้อมูลที่คำสั่งจัดสรรไว้ให้
ระบบใช้ฟังก์ชันความหมายเพื่ออัปเดตค่าของการบันทึกเป้าหมาย สำหรับ
เช่น เวลาในการตอบสนองของคำสั่ง 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 (สถาปัตยกรรมชุดคำสั่ง) ของผู้ประมวลผลข้อมูลกำหนดโมเดลนามธรรม ที่ซอฟต์แวร์จะโต้ตอบกับฮาร์ดแวร์ ซึ่งอธิบายกลุ่มของ วิธีการที่ใช้ได้ ประเภทข้อมูล รีจิสเตอร์ และสถานะแมชชีนอื่นๆ ดำเนินการอย่างไร รวมถึงลักษณะการทำงาน (ความหมาย) สำหรับวัตถุประสงค์ ของ MPACT-Sim นั้น ISA ไม่ได้รวมการเข้ารหัสจริงของคำสั่ง แต่จะดำเนินการแยกกัน
ISA โปรเซสเซอร์จะแสดงในไฟล์คำอธิบายที่อธิบาย ที่ตั้งค่าในระดับแอบสแตรกต์ของการเข้ารหัสได้โดยไม่จำเป็นต้องเข้าใจเนื้อหาใดได้ ไฟล์คำอธิบาย แจกแจงชุดคำสั่งที่ใช้ได้ สำหรับแต่ละวิธีการ จำเป็นต้องแสดงชื่อตัวถูกดำเนินการ จำนวนและชื่อของตัวถูกดำเนินการ และ เชื่อมโยงกับฟังก์ชัน/การเรียกใช้ C++ ที่นำความหมายของมันมาใช้ นอกจากนี้ สามารถระบุสตริงการจัดรูปแบบแยกส่วน และการใช้งานคำสั่ง ชื่อทรัพยากรฮาร์ดแวร์ ซึ่งมีประโยชน์ในการสร้างข้อความ การแสดงคำสั่งสำหรับการแก้ไขข้อบกพร่อง การติดตาม หรือการใช้งานแบบอินเทอร์แอกทีฟ สามารถใช้หลังเพื่อสร้างความแม่นยำของรอบในการจำลองมากยิ่งขึ้น
ไฟล์คำอธิบาย ISA ถูกแยกวิเคราะห์โดยโปรแกรมแยกวิเคราะห์ที่สร้างโค้ดสำหรับ เครื่องมือถอดรหัสคำสั่งแบบแนบเนียน ตัวถอดรหัสนี้จะทำหน้าที่ เติมข้อมูลในช่องของออบเจ็กต์คำสั่ง ค่าที่เจาะจง เช่น หมายเลขทะเบียนปลายทางได้มาจากคำแนะนำเฉพาะของรูปแบบ ตัวถอดรหัส เครื่องมือถอดรหัสดังกล่าวอย่างหนึ่งคือเครื่องมือถอดรหัสแบบไบนารี ซึ่งเป็นจุดโฟกัสของ บทแนะนำถัดไป
บทแนะนำนี้ครอบคลุมวิธีเขียนไฟล์คำอธิบาย ISA สำหรับสเกลาร์แบบง่าย สถาปัตยกรรม เราจะใช้ชุดคำสั่งบางส่วนของ RiscV RV32I เป็น อธิบายสิ่งนี้ให้เห็นภาพ และเมื่อใช้ร่วมกับบทแนะนำอื่นๆ ก็ช่วยสร้างเครื่องมือจำลองที่มีความสามารถ ของการจำลอง "สวัสดีโลก" ของโปรแกรม สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ RiscV ISA ดูที่ ข้อกำหนดของ Risc-V
เริ่มต้นด้วยการเปิดไฟล์
riscv_isa_decoder/riscv32i.isa
เนื้อหาของไฟล์แบ่งออกเป็นหลายส่วน ข้อแรกคือ ISA การประกาศ:
isa RiscV32I {
namespace mpact::sim::codelab;
slots { riscv32; }
}
วิธีนี้ประกาศว่า RiscV32I
เป็นชื่อของ ISA และโปรแกรมสร้างโค้ดจะ
สร้างคลาสชื่อ RiscV32IEncodingBase
ซึ่งกำหนดอินเทอร์เฟซ
ตัวถอดรหัสที่สร้างขึ้นจะใช้เพื่อรับข้อมูล opcode และตัวถูกดำเนินการ ชื่อของ
คลาสนี้สร้างขึ้นโดยการแปลงชื่อ ISA เป็น Pascal-case แล้ว
เชื่อมต่อกับ EncodingBase
การประกาศ slots { riscv32; }
ระบุว่ามีเพียงช่องคำสั่งเดียว riscv32
ใน RiscV32I
ISA (ซึ่งตรงข้ามกับสล็อตหลายสล็อตในคำสั่งของ VLIW) และมีเพียงช่องเดียว
คำสั่งที่ถูกต้องคือคำสั่งที่ถูกต้องในการดำเนินการใน riscv32
// First disasm fragment is 15 char wide and left justified.
disasm widths = {-15};
นี่เป็นการระบุให้แยกชิ้นส่วนแรกของการถอดแยกชิ้นส่วน (ดูข้อมูลเพิ่มเติมด้านล่าง) จะชิดขอบเป็นอักขระ 15 ตัว ที่มีพื้นที่กว้าง ส่วนย่อยที่ตามมาจะต่อท้ายฟิลด์นี้โดยไม่มี การเว้นวรรคเพิ่มเติม
ด้านล่างมีการประกาศสล็อต 3 รายการ ได้แก่ riscv32i
, zicsr
และ riscv32
ตามคำจำกัดความของ isa
ข้างต้น มีเฉพาะวิธีการที่กำหนดไว้สำหรับ riscv32
ช่องดังกล่าวจะเป็นส่วนหนึ่งของ RiscV32I
isa สล็อตอีก 2 ช่องมีไว้เพื่ออะไร
สล็อตใช้ในการแยกคำสั่งออกเป็นกลุ่มต่างๆ ได้ ซึ่งจะนำไปพิจารณา
รวมกันเป็นช่องเดียวในตอนท้าย สังเกตเครื่องหมาย : riscv32i, zicsr
ในการประกาศสล็อต riscv32
คอลัมน์นี้ระบุว่าช่องโฆษณา riscv32
จะรับค่าเดิม
วิธีการทั้งหมดที่กำหนดไว้ในสล็อต zicsr
และ riscv32i
ISA 32 บิต RiscV
ประกอบด้วย ISA พื้นฐานที่เรียกว่า RV32I ซึ่งส่วนขยายที่ไม่บังคับชุดหนึ่งอาจ
เพิ่มได้ กลไกของสล็อตช่วยให้คำแนะนำในส่วนขยายเหล่านี้สามารถ
ที่ระบุแยกต่างหาก และรวมเข้าด้วยกันตามที่จำเป็นในตอนท้ายเพื่อกำหนด
ISA โดยรวม ในกรณีนี้ จะมีวิธีการใน RiscV 'I' กำหนดกลุ่มแล้ว
แยกต่างหากจากโฆษณาใน 'zicsr' กลุ่ม สามารถกำหนดกลุ่มเพิ่มเติม
สำหรับ "M" (คูณ/หาร), 'F' (จุดลอยตัวความแม่นยําเดียว), 'D'
(จุดลอยตัวความเที่ยงสองเท่า), 'C' (วิธีการแบบกะทัดรัด 16 บิต) เป็นต้น
ที่จำเป็นสำหรับ RiscV ISA ขั้นสุดท้ายที่ต้องการ
// 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 จะเห็นว่ามี 2 รายการเท่านั้น
ก่อนหน้านี้มีการกำหนดรหัส opcode (วิธีการ) fence
และ ebreak
riscv32i
ระบบกำหนดรหัสดำเนินการ fence
โดยการระบุชื่อ (fence
) และ
ข้อมูลจำเพาะของตัวถูกดำเนินการ ({: imm12 : }
) ตามด้วยการถอดประกอบ (ไม่บังคับ)
("fence"
) และ Callable ที่จะถูกเชื่อมโยงในฐานะความหมาย
("&RV32IFence"
)
ตัวถูกดำเนินการคำสั่งระบุเป็น 3 ตัว โดยแต่ละองค์ประกอบ
คั่นด้วยเครื่องหมายอัฒภาค ให้ predicate ':' รายการตัวถูกดำเนินการแหล่งที่มา ':'
รายการตัวถูกดำเนินการปลายทาง รายการตัวถูกดำเนินการต้นทางและปลายทางเป็นเครื่องหมายคอมมา
รายการชื่อตัวถูกดำเนินการที่คั่นด้วยอักขระ ดังที่คุณเห็น ตัวถูกดำเนินการของคำสั่งสำหรับ
คำสั่ง fence
ไม่มีตัวถูกดำเนินการแบบภาคแสดง มีแหล่งที่มาเพียงแหล่งเดียว
ชื่อตัวถูกดำเนินการ imm12
และไม่มีตัวถูกดำเนินการปลายทาง เซ็ตย่อยของ RiscV RV32I ได้
ไม่รองรับการดำเนินการแบบกำหนดล่วงหน้า ดังนั้นตัวถูกดำเนินการภาคแสดงจะว่างเปล่าเสมอ
ในบทแนะนำนี้
ระบุฟังก์ชันความหมายเป็นสตริงที่จำเป็นในการระบุ C++
หรือ Callable เพื่อใช้เรียกฟังก์ชันอรรถศาสตร์ ลายเซ็นของ
ฟังก์ชันความหมาย/การเรียกได้คือ void(Instruction *)
ข้อกำหนดในการถอดแยกชิ้นส่วนประกอบด้วยรายการสตริงที่คั่นด้วยเครื่องหมายจุลภาค
โดยปกติแล้วจะใช้เพียง 2 สตริง คือ สตริงหนึ่งสำหรับ opcode และอีกเรื่องสำหรับ
ตัวถูกดำเนินการ เมื่อจัดรูปแบบ (โดยใช้การเรียก AsString()
ในวิธีการ) แต่ละรายการ
ระบบจะจัดรูปแบบสตริงภายในช่องตาม disasm widths
ตามที่อธิบายไว้ข้างต้น
แบบฝึกหัดต่อไปนี้จะช่วยคุณเพิ่มคำสั่งในไฟล์ riscv32i.isa
ที่เพียงพอสำหรับการจำลอง "สวัสดีโลก" ของโปรแกรม สำหรับผู้ที่ไม่ค่อยมีเวลา
จะดูโซลูชันได้ใน
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
ตามที่คุณเห็น ช่องเสียบ opcode และตัวถูกดำเนินการที่กำหนดใน
มีการกำหนดไฟล์ riscv32i.isa
ในประเภทการแจกแจงแบบใดแบบหนึ่ง นอกจากนี้
มีอาร์เรย์ OpcodeNames
ที่จัดเก็บชื่อของ opcode ทั้งหมด (คือ
มีคำจำกัดความใน riscv32i_enums.cc
) ไฟล์อื่นๆ จะมีตัวถอดรหัสที่สร้างขึ้น
ซึ่งจะกล่าวถึงเพิ่มเติมในบทแนะนำอื่น
กฎการสร้าง Bazel
เป้าหมายตัวถอดรหัส ISA ใน Bazel จะกำหนดโดยใช้มาโครกฎที่กำหนดเองที่ชื่อ
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 ที่เฉพาะเจาะจง ซึ่งต้องระบุหากมีมากกว่า 1 รายการ
ที่ระบุในไฟล์ต้นฉบับที่จะสร้างตัวถอดรหัส
เพิ่มการลงทะเบียน-ลงทะเบียนคำแนะนำ ALU
ตอนนี้ได้เวลาเพิ่มวิธีการใหม่ลงในไฟล์ riscv32i.isa
แล้ว องค์ประกอบ
กลุ่มวิธีการคือคำสั่งสำหรับการลงทะเบียน ALU เช่น add
and
และอื่นๆ ใน RiscV32 เครื่องมือเหล่านี้จะใช้รูปแบบการสอนไบนารีประเภท R
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | rs2 | rs1 | func3 | ปุ่มเลือก | รหัสดำเนินการ |
แม้ว่าจะมีการใช้ไฟล์ .isa
เพื่อสร้างตัวถอดรหัสที่เข้าใจได้โดยไม่จำเป็นต้องเข้าใจรูปแบบ แต่โมเดลก็ยังคง
มีประโยชน์สำหรับการพิจารณารูปแบบไบนารีและการจัดวางเพื่อเป็นแนวทางในการใส่ข้อความ ขณะที่คุณ
จะเห็นว่ามีฟิลด์ 3 ฟิลด์ที่เกี่ยวข้องกับเครื่องมือถอดรหัสที่ป้อนข้อมูล
ออบเจ็กต์คำสั่ง: rs2
, rs1
และ rd
ถึงตอนนี้เราจะเลือกใช้
ชื่อเหล่านี้สำหรับการลงทะเบียนจำนวนเต็มที่เข้ารหัสด้วยวิธีเดียวกัน (ลำดับบิต)
ในฟิลด์คำสั่งเดียวกัน และในทุกคำสั่ง
วิธีการที่เราจะเพิ่มมีดังต่อไปนี้
add
- เพิ่มจำนวนเต็มand
- บิตไวและor
- บิตไวหรือsll
- เลื่อนไปทางซ้ายเชิงตรรกะsltu
- ตั้งค่าน้อยกว่า ไม่ได้ลงชื่อsub
- การลบจำนวนเต็มxor
- บิตไวส์ xor.
วิธีการเหล่านี้จะเพิ่มลงในส่วน opcodes
ของ
คำจำกัดความของช่อง riscv32i
อย่างเช่นเราต้องระบุชื่อ รหัสการดำเนินการ
การแยกส่วนและฟังก์ชันทางอรรถศาสตร์สำหรับแต่ละการสอน ชื่อนั้นง่ายมาก
ให้ใช้ชื่อ opcode ด้านบน นอกจากนั้น ทุกตัวใช้ตัวถูกดำเนินการเดียวกัน ดังนั้น
เราจะใช้ { : rs1, rs2 : rd}
สำหรับข้อมูลจำเพาะของตัวถูกดำเนินการ ซึ่งหมายความว่า
ตัวถูกดำเนินการของแหล่งที่มาการลงทะเบียนที่ระบุโดย rs1 จะมีดัชนี 0 ในแหล่งที่มา
เวกเตอร์ตัวถูกดำเนินการในออบเจ็กต์คำสั่ง ตัวถูกดำเนินการของแหล่งที่มาการลงทะเบียนที่ระบุ
โดย rs2 จะมีดัชนี 1 และตัวถูกดำเนินการปลายทางการลงทะเบียนที่ระบุโดย rd
จะเป็นองค์ประกอบเดียวในเวกเตอร์ตัวถูกดำเนินการปลายทาง (ที่ดัชนี 0)
ต่อมาคือข้อมูลจำเพาะของฟังก์ชันความหมาย โดยใช้คีย์เวิร์ด
semfunc
และสตริง C++ ที่ระบุ Callable ซึ่งใช้เพื่อกำหนดได้
เป็น 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 ที่ใช้องค์ประกอบ แทนการจดทะเบียนอย่างใดอย่างหนึ่ง ซึ่งจะมีอยู่ 3 กลุ่ม คำสั่ง (ขึ้นอยู่กับฟิลด์ที่อยู่ติดกัน): คำสั่ง I-Type แบบทันที ด้วยการลงชื่อ 12 บิตทันที คำสั่ง I-Type เฉพาะในทันที สำหรับกะ และ U-Type ทันที โดยมีค่าทันทีแบบไม่ลงชื่อแบบ 20 บิต รูปแบบมีดังนี้
รูปแบบ I-Type ทันที:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | ปุ่มเลือก | รหัสดำเนินการ |
รูปแบบ I-Type เฉพาะทันที:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | uimm5 | rs1 | func3 | ปุ่มเลือก | รหัสดำเนินการ |
รูปแบบ U-Type ทันทีมีดังนี้
31..12 | 11..7 | 6..0 |
---|---|---|
20 | 5 | 7 |
uimm20 | ปุ่มเลือก | รหัสดำเนินการ |
ดังที่คุณเห็น ชื่อตัวถูกดำเนินการ rs1
และ rd
หมายถึงฟิลด์บิตเดียวกันกับ
ก่อนหน้านี้และใช้เพื่อแทนการจดทะเบียนจำนวนเต็ม ชื่อเหล่านี้จึงอาจเป็น
ไว้ ฟิลด์ค่าทันทีจะมีความยาวและตำแหน่งที่แตกต่างกัน และ
2 รายการ (uimm5
และ uimm20
) ไม่ได้ลงนาม ขณะที่ imm12
ลงนามอยู่ แต่ละ
สิ่งเหล่านี้จะใช้ชื่อของตัวเอง
ดังนั้น ตัวถูกดำเนินการของคำสั่งประเภท I จึงควรเป็น { : rs1, imm12 :rd
}
สำหรับคำสั่งประเภท I โดยเฉพาะ คำสั่งควรเป็น { : rs1, uimm5 : rd}
ข้อกำหนดตัวถูกดำเนินการของคำสั่ง U-Type ควรเป็น { : uimm20 : rd }
คำสั่ง I-Type ที่เราต้องเพิ่ม ได้แก่
addi
- เพิ่มทันทีandi
- Bitwise และทันทีori
- Bitwise หรือในทันทีxori
- Bitwise xor แบบทันที
คำแนะนำ I-Type เฉพาะทางที่เราต้องเพิ่ม ได้แก่
slli
- เลื่อนไปทางซ้ายอย่างสมเหตุสมผลทันทีsrai
- เลื่อนเลขคณิตด้านขวาทันทีsrli
- เลื่อนไปทางขวาโดยใช้ตรรกะทันที
คำสั่ง U-Type ที่เราต้องเพิ่ม ได้แก่
auipc
- เพิ่มส่วนบนทันทีลงใน PClui
- โหลดด้านบนทันที
ชื่อที่ใช้สำหรับ opcode จะตามมาจากชื่อวิธีทำอยู่แล้ว
ข้างต้น (ไม่จำเป็นต้องสร้าง URL ขึ้นมา - ล้วนไม่ซ้ำกันเลย) เมื่อพูดถึง
ระบุฟังก์ชันความหมาย โปรดทราบว่าออบเจ็กต์การสอนจะเข้ารหัส
เชื่อมต่อกับตัวถูกดำเนินการต้นทางซึ่งไม่แน่นอนกับตัวถูกดำเนินการเบื้องหลัง
ประเภท ซึ่งหมายความว่าสำหรับคำสั่งที่มีการดำเนินการเหมือนกัน แต่
อาจแตกต่างกันไปในประเภทตัวถูกดำเนินการ สามารถใช้ฟังก์ชันความหมายเหมือนกันได้ ตัวอย่างเช่น
คำสั่ง addi
จะดำเนินการเหมือนกับคำสั่ง add
หาก
ตัวถูกละเว้นประเภทตัวถูกดำเนินการ เพื่อที่จะใช้ฟังก์ชันทางอรรถศาสตร์เดียวกันได้
ข้อกำหนดเฉพาะ "&RV32IAdd"
ในทำนองเดียวกันสำหรับ andi
, ori
, xori
และ slli
คำสั่งอื่นๆ ใช้ฟังก์ชันทางความหมายแบบใหม่ แต่ควรตั้งชื่อ
ขึ้นอยู่กับการดำเนินการ ไม่ใช่ตัวถูกดำเนินการ ดังนั้นสำหรับ srai
ให้ใช้ "&RV32ISra"
คำสั่ง U-Type auipc
และ lui
ไม่มีค่าเทียบเท่าในการลงทะเบียน ดังนั้นจึงใช้ได้
เพื่อใช้ "&RV32IAuipc"
และ "&RV32ILui"
สตริงที่ถอดแยกชิ้นส่วนนั้นคล้ายกับสตริงในแบบฝึกหัดก่อนหน้ามาก
ตามที่คาดไว้ การอ้างอิงไปยัง %rs2
จะถูกแทนที่ด้วย %imm12
, %uimm5
หรือ %uimm20
ได้ตามความเหมาะสม
ทำการเปลี่ยนแปลงและสร้าง ตรวจสอบเอาต์พุตที่สร้างขึ้น เหมือนกับ ก่อนหน้านี้ คุณสามารถตรวจสอบงานกับ riscv32i.isa และ rv32i_instructions.h
เพิ่มคำแนะนำสาขาและ Jump-And-Link
คำสั่ง Branch และวิธีการข้ามลิงก์ เราต้องเพิ่มทั้งคู่ให้ใช้ปลายทาง
ตัวถูกดำเนินการที่ระบุเป็นนัยในคำสั่งเท่านั้น เช่น พีซีถัดไป
ในขั้นตอนนี้ เราจะถือว่าสิ่งนี้เป็นตัวถูกดำเนินการที่เหมาะสมที่มีชื่อ
next_pc
ซึ่งจะระบุเพิ่มเติมในบทแนะนำหลังจากนี้
คำแนะนำสาขา
สาขาทั้งหมดที่เราเพิ่มจะใช้การเข้ารหัส B-Type
31 | 30..25 | 24..20 | 19..15 | 14..12 | 11..8 | 7 | 6..0 |
---|---|---|---|---|---|---|---|
1 | 6 | 5 | 5 | 3 | 4 | 1 | 7 |
หนู | หนู | rs2 | rs1 | func3 | หนู | หนู | รหัสดำเนินการ |
ช่องที่ต่างกันในทันทีจะเชื่อมถึงกันเป็น 12 บิตที่มีการลงชื่อทันที
เนื่องจากรูปแบบไม่เกี่ยวข้องอย่างแท้จริง เราจึงจะเรียกสิ่งนี้ทันที
bimm12
สำหรับสาขา 12 บิตทันที การกระจาย Fragment จะอยู่ใน
บทแนะนำถัดไปเกี่ยวกับการสร้างตัวถอดรหัสไบนารี ทุก
วิธีการของ Branch ให้เปรียบเทียบการลงทะเบียนจำนวนเต็มที่ระบุโดย rs1 และ rs2 หาก
เงื่อนไขเป็นจริง ระบบจะเพิ่มค่าที่ทันทีลงในค่า PC ปัจจุบันเพื่อ
ให้ที่อยู่ของคำสั่งถัดไปที่จะดำเนินการ ตัวถูกดำเนินการของ
คำสั่งสำหรับ Branch จึงควรเป็น { : rs1, rs2, bimm12 : next_pc }
วิธีการสำหรับสาขาที่เราต้องเพิ่มมีดังนี้
beq
- สาขาถ้าเท่ากันbge
- สาขาหากมากกว่าหรือเท่ากับbgeu
- สาขา หากมีมากกว่าหรือเท่ากับ ไม่มีเครื่องหมายblt
- สาขา หากน้อยกว่าbltu
- สาขา หากมีน้อยกว่าที่ไม่ได้ลงชื่อbne
- สาขาหากไม่เท่ากัน
ชื่อ opcode เหล่านี้ไม่ซ้ำกัน จึงนำมาใช้ซ้ำได้ใน .isa
คำอธิบาย แน่นอนว่าคุณต้องเพิ่มชื่อฟังก์ชันทางความหมายใหม่ เช่น
"&RV32IBeq"
ฯลฯ
ตอนนี้ข้อกำหนดในการแยกชิ้นส่วนจะยากขึ้นเล็กน้อย เนื่องจากที่อยู่ของ
คำสั่งนั้นใช้ในการคำนวณปลายทาง โดยไม่ได้เป็นส่วนหนึ่งจริง
ของตัวถูกดำเนินการของคำสั่ง อย่างไรก็ตาม ข้อมูลนี้เป็นส่วนหนึ่งของข้อมูลที่จัดเก็บไว้ใน
ออบเจ็กต์คำสั่ง เพื่อให้พร้อมใช้งาน วิธีแก้ไขคือใช้
ของนิพจน์ในสตริงการแยกส่วน แทนที่จะใช้ "%" ตามด้วย
ชื่อตัวถูกดำเนินการ คุณสามารถพิมพ์ %(นิพจน์: รูปแบบการพิมพ์) ง่ายมากเท่านั้น
รองรับนิพจน์ แต่ที่อยู่บวกออฟเซ็ตเป็นหนึ่งในนั้น โดยมี @
สัญลักษณ์ที่ใช้สำหรับที่อยู่การสอนปัจจุบัน รูปแบบการพิมพ์จะคล้ายกับ
รูปแบบ C แต่ไม่มี %
นำหน้า รูปแบบการถอดแยกชิ้นส่วนสำหรับ
คำสั่ง beq
จะกลายเป็น:
disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"
คำแนะนำแบบข้ามลิงก์
ต้องเพิ่มวิธีการข้ามและลิงก์เพียง 2 รายการเท่านั้น ได้แก่ jal
(ข้ามและลิงก์) และ
jalr
(ข้ามและลิงก์โดยอ้อม)
คำสั่ง jal
ใช้การเข้ารหัสประเภท J ดังนี้
31 | 30..21 | 20 | 19..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
หนู | หนู | หนู | หนู | ปุ่มเลือก | รหัสดำเนินการ |
เช่นเดียวกับคำสั่งสำหรับ Branch ส่วนทันที 20 บิตจะกระจัดกระจายตาม
หลายฟิลด์ ดังนั้นเราจะตั้งชื่อว่า jimm20
การกระจาย Fragment นั้นไม่สำคัญ
ในขณะนี้ แต่จะมีการแก้ไขในภายหลัง
เกี่ยวกับการสร้างตัวถอดรหัสไบนารี ตัวถูกดำเนินการ
แล้วข้อกำหนดจะกลายเป็น { : jimm20 : next_pc, rd }
โปรดทราบว่ามี 2 ประเภท
ตัวถูกดำเนินการปลายทาง ค่า pc ถัดไปและการลงทะเบียนลิงก์ที่ระบุใน
วิธีการ
รูปแบบการแยกชิ้นส่วนคล้ายกับวิธีการของ Branch ข้างต้น โดยรูปแบบการถอดแยกชิ้นส่วนจะเป็นดังนี้
disasm: "jal", "%rd, %(@+jimm20:08x)"
การข้ามและลิงก์ทางอ้อมใช้รูปแบบ I-Type แบบ 12 บิตทันที ทั้งนี้
บวกค่าทันทีที่ขยายเครื่องหมายไปยังการลงทะเบียนจำนวนเต็มที่ระบุโดย
rs1
เพื่อสร้างที่อยู่สำหรับคำสั่งเป้าหมาย การลงทะเบียนลิงก์คือ
การลงทะเบียนจำนวนเต็มที่ระบุโดย rd
31..20 | 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 ดังนี้
31..25 | 24..20 | 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) การเขียนส่งกลับผลลัพธ์ ใน เครื่องมือจำลองซึ่งทำโดยการแบ่งการทำงานเชิงอรรถศาสตร์ของการโหลดออกเป็น แยกคำสั่ง คือคำสั่งหลัก และคำสั่งสำหรับเด็ก นอกจากนี้ เมื่อระบุโอเปอแรนด์ เราต้องระบุตัวถูกดำเนินการทั้งสำหรับพารามิเตอร์หลักและ สำหรับเด็ก ซึ่งทำโดยการรักษาข้อกำหนดตัวถูกดำเนินการเป็น รายการ 3 ตัว ไวยากรณ์คือ
{(predicate : sources : destinations),
(predicate : sources : destinations), ... }
คำแนะนำในการโหลดทั้งหมดจะใช้รูปแบบ I-Type เช่นเดียวกับวิธีการก่อนหน้า คำแนะนำ:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | ปุ่มเลือก | รหัสดำเนินการ |
ข้อมูลจำเพาะของตัวถูกดำเนินการจะแยกตัวถูกดำเนินการที่ต้องใช้ในการคำนวณที่อยู่
และเริ่มการเข้าถึงหน่วยความจำจากปลายทางการลงทะเบียนสำหรับข้อมูลโหลด:
{( : rs1, imm12 : ), ( : : rd) }
เนื่องจากการกระทำเชิงความหมายแบ่งเป็น 2 คำสั่ง ฟังก์ชันทางความหมาย
คุณจะต้องระบุ Callable 2 รายการเช่นเดียวกัน สำหรับ lw
(โหลดคำ) ตัวแปรนี้จะเป็น
เขียน:
semfunc: "&RV32ILw", "&RV32ILwChild"
ข้อกำหนดการถอดแยกชิ้นส่วนเป็นแบบปกติมากกว่า ไม่มีการพูดถึง
การสอนของเด็ก สำหรับ lw
ควรเป็น:
disasm: "lw", "%rd, %imm12(%rs1)"
วิธีการโหลดที่จำเป็นต้องดำเนินการมีดังนี้
lb
- ไบต์ของการโหลดlbu
- โหลดไบต์ที่ไม่มีเครื่องหมายlh
- โหลดครึ่งคำlhu
- โหลดครึ่งคำที่ไม่มีการรับรองlw
- โหลดคำ
ทำการเปลี่ยนแปลง แล้วสร้างได้เลย ตรวจสอบเอาต์พุตที่สร้างขึ้น เพียงแค่ ก่อนหน้านี้ คุณสามารถตรวจสอบงานกับ riscv32i.isa และ rv32i_instructions.h
ขอบคุณที่ช่วยมาถึงจุดนี้ เราหวังว่าข้อมูลนี้จะเป็นประโยชน์