ตัวถอดรหัส RiscV ISA

วัตถุประสงค์ของบทแนะนำนี้ได้แก่

  • ดูวิธีการแสดงคำสั่งในเครื่องจำลอง MPACT-Sim
  • เรียนรู้โครงสร้างและไวยากรณ์ของไฟล์คำอธิบาย ISA
  • เขียนคำอธิบาย ISA สำหรับชุดย่อยของคำแนะนำ RiscV RV32I

ภาพรวม

ใน MPACT-ซิม คำสั่งเป้าหมายจะถูกถอดรหัสและจัดเก็บไว้ใน การนำเสนอข้อมูล ซึ่งทำให้ข้อมูลของพวกเขามีมากขึ้นและมีความหมาย ดำเนินการได้รวดเร็วยิ่งขึ้น อินสแตนซ์คำสั่งเหล่านี้จะแคชตามคำสั่ง แคชเพื่อลดจำนวนคำสั่งที่ดำเนินการตามคำสั่งเป็นประจำ ดำเนินการแล้ว

ชั้นเรียนการสอน

ก่อนที่เราจะเริ่มต้น คุณควรดูวิธีการสักเล็กน้อย แสดงเป็น MPACT-Sim คลาส Instruction ได้รับการกำหนดไว้ใน mpact-sim/mpact/sim/generic/instruction.h.

อินสแตนซ์คลาสวิธีการมีข้อมูลทั้งหมดที่จำเป็นในการ จำลองคำสั่งเมื่อ "ดำเนินการ" เช่น

  1. ที่อยู่ที่ใช้สอน ขนาดคำสั่งจำลอง เช่น ขนาดในรูปแบบ .text
  2. รหัสการดำเนินการสำหรับวิธีการ
  3. ตัวชี้อินเทอร์เฟซตัวถูกดำเนินการแบบคาดการณ์ (หากมี)
  4. เวกเตอร์ของตัวชี้อินเทอร์เฟซตัวถูกดำเนินการต้นทาง
  5. เวกเตอร์ของตัวชี้อินเทอร์เฟซตัวถูกดำเนินการปลายทาง
  6. ฟังก์ชันความหมายที่เรียกใช้ได้
  7. ชี้ไปที่ออบเจ็กต์สถานะทางสถาปัตยกรรม
  8. ชี้ไปที่ออบเจ็กต์บริบท
  9. ตัวชี้ไปยังอินสแตนซ์ย่อยและอินสแตนซ์คำสั่งถัดไป
  10. สตริงการแยกส่วน

โดยทั่วไปแล้ว อินสแตนซ์เหล่านี้จะจัดเก็บไว้ในแคชคำสั่ง (อินสแตนซ์) และ นำมาใช้ซ้ำเมื่อใดก็ตามที่มีการดำเนินการตามคำแนะนำซ้ำ ซึ่งจะช่วยปรับปรุงประสิทธิภาพ ระหว่างรันไทม์

ยกเว้นตัวชี้ไปยังออบเจ็กต์บริบท ระบบจะเติมค่าทั้งหมดด้วยฟังก์ชัน เครื่องมือถอดรหัสคำสั่งที่สร้างขึ้นจากคำอธิบาย 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 - เพิ่มส่วนบนทันทีลงใน PC
  • lui - โหลดด้านบนทันที

ชื่อที่ใช้สำหรับ 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


คำสั่ง 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

ขอบคุณที่ช่วยมาถึงจุดนี้ เราหวังว่าข้อมูลนี้จะเป็นประโยชน์