برنامج فك ترميز RiscV ISA

تتمثل أهداف هذا البرنامج التعليمي في ما يلي:

  • تعرَّف على كيفية عرض التعليمات في محاكي محاكاة شرائح SIM متعددة القنوات (MPACT).
  • تعرَّف على بنية ملف وصف ISA وبنيته.
  • اكتب أوصاف ISA لمجموعة التعليمات الفرعية RiscV RV32I.

نظرة عامة

وفي MPACT-Sim، يتم فك ترميز التعليمات المستهدفة وتخزينها في وحدة تخزين التمثيل لجعل معلوماته متاحة بشكل أكبر والدلالات الدلالية بشكل أسرع في التنفيذ. يتم تخزين مثيلات التعليمات مؤقتًا في التعليمات ذاكرة التخزين المؤقت لتقليل عدد المرات التي يتم فيها تنفيذ التعليمات بشكل متكرر وتنفيذه.

فئة التعليمات

قبل أن نبدأ، من المفيد أن ننظر قليلاً في كيفية تنفيذ التعليمات ويتم تمثيلها في MPACT-Sim. يتم تحديد الفئة Instruction في mpact-sim/mpact/sim/generic/instruction.h.

يحتوي مثيل فئة التعليمات على جميع المعلومات اللازمة محاكاة التعليمات عند "تنفيذها"، مثل:

  1. عنوان التعليمات ومحاكاة حجم التعليمات، أي حجمها في .text
  2. رمز تشغيل التعليمات
  3. استخدِم مؤشر واجهة المعامل (إن أمكن).
  4. متّجه لمؤشرات واجهة معامل المصدر
  5. متّجه لمؤشرات واجهة معامل الوجهة
  6. الدالة الدلالية قابلة للاستدعاء.
  7. يشير إلى عنصر خاص بالحالة المعمارية.
  8. يشير إلى كائن السياق.
  9. يشير إلى مثيلات التعليمات الفرعية ومثيلات التعليمات التالية.
  10. سلسلة التفكيك.

يتم تخزين هذه المثيلات بشكل عام في ذاكرة تخزين مؤقت للتعليمات (مثيل)، إعادة استخدامه عند إعادة تنفيذ التعليمات. يؤدي ذلك إلى تحسين الأداء أثناء وقت التشغيل

يتم ملء جميع الحقول بواسطة التعليمات التي يتم إنشاؤها من وصف ISA. لهذا الغرض ليس من الضروري معرفة تفاصيل هذه العناصر لأننا لن وتستخدمها مباشرةً. بدلاً من ذلك، يعد الفهم العالي المستوى لكيفية استخدامها كافية.

الدالة الدلالية القابلة للاستدعاء هي كائن الدالة/الطريقة/الدالة C++ (بما في ذلك lambdas) التي تنفّذ دلالات التعليمات. بالنسبة على سبيل المثال، في تعليمات add، يتم تحميل كل معامل مصدر، تتم إضافة المعاملين. ويكتب النتيجة في معامل وجهة واحد. موضوع ستجد شرحًا متعمقًا للدوال الدلالية في البرنامج التعليمي حول دالة الدلالة.

معامِلات التعليمات

تتضمن فئة التعليمات مؤشرات لثلاثة أنواع من واجهات المعاملات: القائم والمصدر والوجهة. تسمح هذه الواجهات للدوال الدلالية تُكتب بشكل مستقل عن النوع الفعلي لتعليمات التعليمات الأساسية المُعامِل. على سبيل المثال، يتم الوصول إلى قيم السجلات والسجلات المباشرة من خلال الواجهة نفسها. هذا يعني أن التعليمات التي تؤدي نفس العملية ولكن على مُعامِلات مختلفة (على سبيل المثال، السجلات مقابل المحاكيات) وتنفيذه باستخدام نفس الدالة الدلالية.

واجهة معامل التنبؤ، لـ 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 الذي ينشئ تعليمة برمجية برنامج فك ترميز التعليمات غير المرتبطة بالتمثيل. برنامج فك الترميز هذا مسؤول عن لملء حقول كائنات التعليمات. القيم المحددة، لنفترض أن رقم سجل الوجهة، يتم الحصول عليها من تعليمات خاصة بالتنسيق برنامج فك الترميز. وأحد برامج فك الترميز هذه هو برنامج فك الترميز الثنائي، وهو محور البرنامج التعليمي التالي.

يتناول هذا البرنامج التعليمي كيفية كتابة ملف وصف 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 إلى حالة باسكال، جارٍ فصله مع EncodingBase. البيان slots { riscv32; } أن هناك منفذ تعليمات واحد فقط riscv32 في RiscV32I 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. معيار ISA 32 بت لبرنامج RiscV من وحدة ISA الأساسية تسمى RV32I، والتي قد تتيح لها مجموعة من الإضافات الاختيارية إضافتها. تتيح آلية الخانة إدخال التعليمات في هذه الإضافات على حدة، ثم يتم دمجها حسب الحاجة في النهاية لتحديد المساعدة السريعة الذكية بشكل عام. في هذه الحالة، تشير التعليمات الموجودة في RiscV 'I' مجموعة محددة منفصلة عن تلك الموجودة في 'zicsr' المجموعة. يمكن تحديد مجموعات إضافية لـ 'M' (ضرب/تقسيم)، 'F' (نقطة عائمة أحادية الدقة)، "D" (نقطة عائمة مزدوجة الدقة)، الحرف "C" (تعليمات مكثفة 16 بت) وما إلى ذلك المطلوب لـ ISA النهائي المطلوب في RiscV.

// 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 دورة. ملاحظة: حجم التعليمات المحدد هنا، هو حجم زيادة عدّاد البرنامج لحساب عنوان التعليمات التسلسلية التالية لتنفيذها في المحاكاة معالج البيانات. وقد يكون هذا العنصر مماثلاً لحجم وحدة البايت التعليمات في ملف الإدخال القابل للتنفيذ.

يوجد قسم رموز التشغيل المركزي لتعريف الخانة. كما ترى، اثنان فقط تم تحديد عمليتَي التشغيل (التعليمات) fence وebreak حتى الآن في riscv32i يتم تحديد رمز التشغيل fence من خلال تحديد الاسم (fence) مواصفات المعامل ({: imm12 : })، تليها طريقة التفكيك الاختياري ("fence")، والقابل للاستدعاء الذي يجب ربطه باعتباره دلاليًا ("&RV32IFence").

يتم تحديد معامِلات التعليمات كثلاثية، ويتضمّن كل مكوّن مفصولة بفاصلة منقوطة, predicate ':' قائمة عوامل التشغيل المصدر ':' قائمة مُعامِل الوجهة. قوائم معامِلات المصدر والوجهة تكون فاصلة. قوائم منفصلة بأسماء المعاملات. كما ترون، يمكن لمعاملات التعليمات الخاصة تحتوي تعليمات 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) المضيف - بالنسبة للمضيفات الأخرى، سيكون 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

يتم تحديد هدف برنامج فك ترميز 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 لتحديد أي عنصر بيانات محدد، مطلوب إذا كان هناك أكثر من عنصر المحدد، في الملف المصدر الذي سيتم إنشاء برنامج فك الترميز له.


إضافة تعليمات Register-Register ALU

حان الوقت الآن لإضافة بعض التعليمات الجديدة إلى ملف riscv32i.isa. الأول هي تعليمات التسجيل في ALU، مثل add، and، وما إلى ذلك. في RiscV32، تستخدم جميعها تنسيق التعليمات الثنائية من نوع R:

25/31 24 فبراير 19.15 14.12 7.11 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 تذكر أنه ينبغي لنا تحديد الاسم ورموز العمليات وتفكيكها والدالة الدلالية لكل تعليمة. الاسم سهل، لنستخدم أسماء العمليات أعلاه. أيضًا، تستخدم جميعها نفس المعاملات، لذا يمكننا استخدام { : rs1, rs2 : rd} لمواصفات المعامل. هذا يعني أنّ سيحتوي معامل مصدر التسجيل المحدد بواسطة rs1 على الفهرس 0 في المصدر متجه المعامل في كائن التعليمات، معامل مصدر التسجيل المحدد بواسطة rs2 سيحتوي على الفهرس 1، ومعامل وجهة التسجيل المحدد بواسطة rd سيكون العنصر الوحيد في متجه معامل الوجهة (في الفهرس 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 مع توقيع فوري بحجم 12 بت، فإن التعليمات الفورية المتخصصة من نوع I-Type في التحولات، ونوع U- إذن فوري، مع قيمة فورية غير موقّعة بحجم 20 بت. يتم عرض التنسيقات أدناه:

تنسيق I-Type الفوري:

20.31 19.15 14.12 7.11 6.0
12 5 3 5 7
imm12 rs1 func3 الثالث رمز العمليات

التنسيق الفوري المتخصص من نوع I:

25/31 24 فبراير 19.15 14.12 7.11 6.0
7 5 5 3 5 7
func7 uimm5 rs1 func3 الثالث رمز العمليات

تنسيق U-Type الفوري:

31.12 7.11 6.0
20 5 7
uimm20 الثالث رمز العمليات

كما ترى، يشير اسم المعامل rs1 وrd إلى حقول البت نفسها مثل سابقًا، وتستخدم لتمثيل سجلات الأعداد الصحيحة، ولذا يمكن تسمية هذه الأسماء الاحتفاظ بها. تختلف حقول القيم المباشرة باختلاف الطول والموقع، اثنين (uimm5 وuimm20) ليسا موقَّعين، بينما imm12 موقَّعة. كل من فستستخدم هذه التطبيقات اسمها الخاص.

وبالتالي، يجب أن تكون معامِلات تعليمات النوع I-Type { : rs1, imm12 :rd }. بالنسبة إلى التعليمات المتخصّصة من نوع I-Type، يجب أن يكون { : rs1, uimm5 : rd}. يجب أن تكون مواصفات معامل تعليمات نوع U هي { : uimm20 : rd }.

تعليمات I-Type التي نحتاج إلى إضافتها هي:

  • addi - إضافة فورية
  • andi - استخدام على مستوى البت وبشكل فوري.
  • ori - استنادًا إلى استخدام البت أو بشكل فوري.
  • xori - xor على مستوى البت مع كلمة مباشرة.

التعليمات المتخصّصة من نوع I-Type التي نحتاج إلى إضافتها هي:

  • slli: التبديل إلى اليسار منطقيًا بشكل فوري
  • srai - تحريك الحساب لليمين بشكل فوري
  • srli - التبديل إلى اليمين المنطقي بشكل فوري

إرشادات نوع U التي نحتاج إلى إضافتها هي:

  • auipc - إضافة حرف فوري إلى جهاز الكمبيوتر الشخصي
  • lui - تحميل الجزء العلوي فورًا

تأتي الأسماء التي يتم استخدامها للعمليات بشكل طبيعي من أسماء التعليمات. أعلاه (لا حاجة لابتكار علامات جديدة - فكلها فريدة من نوعها). عندما يتعلق الأمر لتحديد الدوال الدلالية، وتذكر أن كائنات التعليمات تُرمّز الواجهات إلى مُعامِلات المصدر غير المرتبطة بالمُعامل الأساسي الكتابة. هذا يعني أنه بالنسبة للتعليمات التي لها نفس العملية، ولكن قد تختلف في أنواع المعاملات، ويمكن أن تشارك في نفس الدالة الدلالية. على سبيل المثال: تنفذ تعليمات addi العملية نفسها التي تنفذها تعليمات add إذا يتجاهل إحداهما نوع المعامل، بحيث يمكنه استخدام نفس الدالة الدلالية المواصفات "&RV32IAdd". ينطبق الأمر نفسه على andi وori وxori وslli. تستخدم التعليمات الأخرى دوال دلالية جديدة، ولكن يجب تسميتها يستند إلى العملية، وليس إلى المعاملات، لذلك استخدم "&RV32ISra" مع srai. تشير رسالة الأشكال البيانية إنّ تعليمات الكتابة U-Type auipc وlui ليست تتضمّن مكافئات تسجيل، لذا حسنًا. لاستخدام "&RV32IAuipc" و"&RV32ILui".

سلاسل التفكيك مشابهة جدًا لتلك الموجودة في التمرين السابق، لكنها كما هو متوقع، تم استبدال الإشارات إلى %rs2 بـ %imm12، %uimm5، أو %uimm20 حسب الحاجة.

انطلق وقم بإجراء التغييرات والبناء. تحقَّق من الناتج الذي تم إنشاؤه. تمامًا سابقًا، يمكنك التحقق من عملك مقابل riscv32i.isa و rv32i_instructions.h.


نحتاج إلى تعليمات كلٍ من الفرع والرابط والربط لإضافة كليهما استخدام وجهة المعامل الذي يتضمن فقط في التعليمات نفسها، وهو جهاز الكمبيوتر التالي في هذه المرحلة، سنتعامل مع هذا الأمر على أنه معامل مناسب يحمل الاسم next_pc وسيتم تحديده بشكل أكبر في برنامج تعليمي لاحق.

تعليمات الفرع

الفروع التي نضيفها جميعًا تستخدم تشفير النوع B.

31 25/30 24 فبراير 19.15 14.12 8.11 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. الوصف. وبالطبع، يجب إضافة أسماء دوال دلالية جديدة، مثل: "&RV32IBeq" وغير ذلك

أصبحت مواصفات التفكيك الآن أكثر تعقيدًا، نظرًا لأن عنوان تُستخدم التعليمات لحساب الوجهة، دون أن تكون جزءًا لمعاملات التعليمات. ومع ذلك، فهي جزء من المعلومات المخزنة في كائن التعليمات، بحيث يكون متاحًا. الحل هو استخدام بناء جملة تعبير في سلسلة التفكيك. بدلاً من استخدام '%' يليها اسم المعامل، يمكنك كتابة %(expression: print format). بسيط جدًا تكون التعبيرات متوافقة، إلا أنّ إزاحة العنوان + إزاحة العنوان هي أحد هذه التعبيرات، مع السمة @ يُستخدَم لعنوان التعليمات الحالي. يشبه تنسيق الطباعة بتنسيقات C style printf، ولكن بدون % البادئة. كان تنسيق التفكيك تصبح تعليمات beq بعد ذلك:

    disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"

يجب إضافة اثنين فقط من التعليمات المتعلقة بالربط والربط، jal jalr (انتقال وربط غير مباشر)

تستخدم تعليمات jal ترميز J-Type:

31 21..30 20 19.12 7.11 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.

20.31 19.15 14.12 7.11 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:

25/31 24 فبراير 19.15 14.12 7.11 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) كتابة النتيجة. في جلسة المعمل، لمحاكي التحميل، يتم تنفيذ ذلك من خلال قسمة الإجراء الدلالي للتحميل إلى اثنين تعليمات منفصلة، وهي التعليمات الرئيسية وتعليمات الفرعية. علاوة على ذلك، عندما نحدد المعاملات، نحتاج إلى تحديدها لكل من المعاملات الرئيسية child. ويتم ذلك من خلال التعامل مع مواصفات المعامل على أنها قائمة من الثلاثيات. بناء الجملة هو:

{(predicate : sources : destinations), (predicate : sources : destinations), ... }

تستخدم جميع إرشادات التحميل تنسيق I-Type تمامًا مثل العديد من الإرشادات التعليمات:

20.31 19.15 14.12 7.11 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.

نشكرك على الوصول إلى هذا الحد. نأمل أن تكون هذه المعلومات مفيدة.