البرنامج التعليمي لفك ترميز التعليمات الثنائية

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

  • تعرَّف على بنية ملف وصف التنسيق الثنائي وبنيته.
  • تعرّف على كيفية تطابق وصف التنسيق الثنائي مع وصف ISA.
  • اكتب الأوصاف الثنائية للمجموعة الفرعية RiscV RV32I من التعليمات.

نظرة عامة

ترميز التعليمات الثنائي لـ RiscV

إن ترميز التعليمات الثنائي هو الطريقة القياسية لتشفير التعليمات على معالج صغير. يتم تخزينها عادةً في ملف تنفيذي، عادةً بتنسيق ELF. يمكن أن تكون التعليمات إما عرضًا ثابتًا أو متغيرًا العرض.

عادةً ما تستخدم التعليمات مجموعة صغيرة من تنسيقات الترميز، مع كل تنسيق مخصصة لنوع التعليمات المشفرة. على سبيل المثال، قم بتسجيل على التعليمات استخدام تنسيق واحد يزيد عدد رموز العمليات المتاحة، بينما تستخدم تعليمات التسجيل الفورية تعليمات أخرى تستبدل عدد المتاحة لزيادة حجم النسخة الفورية التي يمكن تشفيرها. تستخدم إرشادات الفرع والقفز دائمًا التنسيقات التي تزيد من حجم على الفور لدعم الفروع ذات إزاحة أكبر.

يشير هذا المصطلح إلى تنسيقات التعليمات المستخدَمة في التعليمات التي نريد فك ترميزها في RiscV. المحاكي هي ما يلي:

تنسيق R-Type، يستخدم لتعليمات التسجيل:

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

تنسيق I-Type، يُستخدم لتعليمات التسجيل الفورية، وإرشادات التحميل، تعليمات jalr، 12 بت فوريًا.

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

تنسيق I-Type متخصص، يُستخدم في shift مع تعليمات فورية، 5 بت فوري:

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

تنسيق U-Type، يُستخدَم لتعليمات فورية طويلة (lui، auipc)، 20 بت فوري:

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

تنسيق B-Type، يستخدم للفروع الشرطية، 12 بت فوري.

31 25/30 24 فبراير 19.15 14.12 8.11 7 6.0
1 6 5 5 3 4 1 7
البريد الإلكتروني البريد الإلكتروني rs2 rs1 func3 البريد الإلكتروني البريد الإلكتروني رمز العمليات

تنسيق J-Type، المستخدَم لتعليمات jal، هو 20 بت فوريًا.

31 21..30 20 19.12 7.11 6.0
1 10 1 8 5 7
البريد الإلكتروني البريد الإلكتروني البريد الإلكتروني البريد الإلكتروني الثالث رمز العمليات

تنسيق S-Type، يُستخدَم لتعليمات المتجر، 12 بت فوريًا.

25/31 24 فبراير 19.15 14.12 7.11 6.0
7 5 5 3 5 7
البريد الإلكتروني rs2 rs1 func3 البريد الإلكتروني رمز العمليات

وكما ترون من هذه التنسيقات، يبلغ طول كل هذه التعليمات 32 بت، 7 بتات منخفضة في كل تنسيق هي حقل رمز التشغيل. لاحظ أيضًا أنه في حين أنه في حين العديد من التنسيقات لها نفس الحجم مباشرةً، ومأخوذة وحدات البت الخاصة بها من الأجزاء المختلفة من التعليمات. وكما سنرى، سيظل برنامج فك الترميز الثنائي المواصفات قادرة على التعبير عن ذلك.

وصف الترميز الثنائي

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

سنكتب في هذا البرنامج التعليمي ملف وصف لترميز ثنائي لمجموعة فرعية تعليمات RiscV32I اللازمة لمحاكاة التعليمات المستخدمة في عبارة "مرحبًا بالعالم" صغيرة البرنامج. لمزيد من التفاصيل حول RiscV ISA، يُرجى مراجعة مواصفات Risc-V{.external}.

ابدأ بفتح الملف: riscv_bin_decoder/riscv32i.bin_fmt

ينقسم محتوى الملف إلى عدة أقسام.

أولاً، هو تعريف decoder.

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

يحدد تعريف برنامج فك الترميز اسم برنامج فك الترميز RiscV32I، وكذلك عن أربع معلومات إضافية. الأول هو namespace، الذي يحدد معرّف مساحة الاسم الذي سيتم وضع الرمز المنشأ فيه. ثانيًا، opcode_enum، يحدد كيفية نوع تعداد رموز العمليات الذي يتم إنشاؤه من خلال برنامج فك ترميز ISA، ضمن الرمز الذي تم إنشاؤه. ثالثًا، يحدّد includes {} الملفات اللازمة للرمز الذي تم إنشاؤه من أجل برنامج فك الترميز هذا. وفي هذه الحالة، هذا هو الملف الذي ينتجه برنامج فك ترميز ISA من البرنامج التعليمي السابق. يمكن تحديد ملفات تضمين إضافية في includes {} على النطاق العالمي. التعريف. ويفيد ذلك في حال تحديد العديد من برامج فك الترميز، وتحتاج جميعها إلى لتضمين بعض الملفات نفسها. رابعها هي قائمة بأسماء التعليمات التي تشكّل التعليمات التي يتمّ إنشاء برنامج فك الترميز لها في هناك حالة واحدة فقط: RiscVInst32.

بعد ذلك، هناك ثلاثة تعريفات للتنسيق. وهي تمثل تعليمات مختلفة لأية كلمة تعليمات 32 بت تستخدمها التعليمات المحددة مسبقًا في الملف.

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

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

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

يحدد الأول تنسيق تعليمات عريض 32 بت يُسمى Inst32Format الذي يحتوي على حقلين: bits (عرض 25 بت) وopcode (عرض 7 بت). كل حقل عبارة عن unsigned، ما يعني أنّ القيمة ستكون إطالة صفرًا عند استخراجها وتوضع في نوع عدد صحيح C++. يجب أن يكون مجموع عرض حقول البت مساويًا لعرض التنسيق. ستعرض الأداة رسالة خطأ إذا كان هناك الخلاف. لا يشتق هذا التنسيق من أي تنسيق آخر، لذا فهو تُعتبر تنسيقًا من المستوى الأعلى.

ويحدد النوع الثاني تنسيقًا تعليمات بعرض 32 بت ويُعرف باسم IType، وهو مشتق من Inst32Format، ما يجعل هذين الشكلَين مرتبطَين. يحتوي التنسيق على 5 الحقول: imm12 وrs1 وfunc3 وrd وopcode. الحقل imm12 هو signed، مما يعني أن القيمة سيتم تمديدها عندما تكون القيمة استخراجها ووضعها في نوع عدد صحيح C++. لاحظ أن IType.opcode في كليهما نفس السمة الموقّعة/غير الموقّعة وتشير إلى نفس وحدات بت كلمة التعليمات باسم Inst32Format.opcode.

التنسيق الثالث هو تنسيق مخصّص لا يستخدمه إلا fence. التعليمات، وهي تعليم محدد بالفعل وليس لدينا حقًا في هذا البرنامج التعليمي.

نقطة أساسية: إعادة استخدام أسماء الحقول بتنسيقات ذات صلة مختلفة طالما أنّها تمثل نفس وحدات البت ولديها نفس التصنيف الموقّع/غير الموقَّع.

بعد تعريفات التنسيق في riscv32i.bin_fmt، تظهر مجموعة تعليمات التعريف. يجب أن تتطابق جميع التعليمات ضِمن مجموعة التعليمات وأن تستخدم تنسيقًا اشتقاقيًا (ربما بشكل غير مباشر) من تنسيق تعليمات المستوى الأعلى. عندما يكون لدى ISA تعليمات بمختلف الأطوال، يتم استخدام مجموعة تعليمات مختلفة لكل طول. بالإضافة إلى ذلك، إذا كان فك ترميز ISA المستهدف يعتمد على وضع التنفيذ، مثل "الذراع" و"الإبهام" التعليمات، يلزم وجود مجموعة تعليمات منفصلة لكل وضع. تشير رسالة الأشكال البيانية ينشئ محلّل bin_fmt برنامج فك ترميز ثنائي لكل مجموعة تعليمات.

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

تحدد مجموعة التعليمات الاسم RiscV32I، وهو عرض [32]، واسم نوع تعداد رموز العمليات لاستخدام "OpcodeEnum"، وتعليمات أساسية . يجب أن يكون نوع تعداد رموز العمليات هو نفسه النوع الذي ينتجه برنامج فك ترميز التعليمات المستقل بالتنسيق الذي تم تناوله في البرنامج التعليمي على ISA برنامج فك الترميز.

يتكوّن كل وصف لترميز التعليمات من 3 أجزاء:

  • اسم العملية، والذي يجب أن يكون مطابقًا تمامًا للاسم المستخدم في التعليمات وصف برنامج فك الترميز ليعمل الاثنين معًا.
  • تنسيق التعليمات المراد استخدامه لرمز العملية هذا هو التنسيق الذي التي تُستخدم لتلبية الإشارات إلى حقول البت في الجزء الأخير.
  • قائمة مفصولة بفواصل لقيود حقل البت، ==، !=، <، <=، >، و>= يجب أن يكون كل ذلك صحيحًا لكي يتمكن رمز العملية من مطابقة كلمة التعليمات.

يستخدم المحلل اللغوي .bin_fmt كل هذه المعلومات لإنشاء برنامج فك ترميز:

  • توفر دوال الاستخراج (الموقَّعة/غير الموقَّعة) بالشكل المناسب لكل وحدة بت بكل تنسيق. يتم وضع دوال الاستخراج في مساحات الاسم التي تدعى نسخة حالة الثعبان لاسم التنسيق. على سبيل المثال، يتم وضع دوال الاستخراج للتنسيق IType في مساحة الاسم i_type. يتم الإعلان عن كل دالة استخراج كدالة inline، وبالتالي يتم استخدام أضيق نطاق uint_t نوع يتضمن عرض التنسيق، ويعرض أضيق int_t (للتوقيع)، النوع uint_t (للنوع غير الموقَّع)، والذي يحتوي على الحقل المستخرَج العرض. مثلاً:
inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}
  • دالة فك الترميز لكل مجموعة تعليمات. يكون ناتجها قيمة من type OpcodeEnum، وسيتم تحديد أضيق نوع من النوع uint_t يضم عرض تنسيق مجموعة التعليمات.

تنفيذ عملية الإنشاء الأوّلية

غيِّر الدليل إلى riscv_bin_decoder وأنشئ المشروع باستخدام الأمر التالي:

$ cd riscv_bin_decoder
$ bazel build :all

والآن، غيّر الدليل إلى جذر المستودع، ومن ثم لنلقي نظرة إلى المصادر التي تم إنشاؤها. لذلك، غيِّر الدليل إلى bazel-out/k8-fastbuild/bin/riscv_bin_decoder (لنفترض أنك تستخدم جهاز x86) المضيف - بالنسبة للمضيفات الأخرى، سيكون k8-Fastbuild سلسلة أخرى).

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

ملف الرأس الذي تم إنشاؤه (.h)

افتح تطبيق "riscv32i_bin_decoder.h". يحتوي الجزء الأول من الملف على تنسيق تشمل وسائل الحماية النموذجية، الملفات وإعلانات مساحة الاسم. بعد ذلك، توجد دالة مساعد نموذجية في مساحة الاسم internal. هذه الدالة يُستخدم لاستخراج حقول البت من التنسيقات الطويلة جدًا بحيث لا تناسب لغة C++ 64 بت عدد صحيح.

#ifndef RISCV32I_BIN_DECODER_H
#define RISCV32I_BIN_DECODER_H

#include <iostream>
#include <cstdint>

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


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

namespace mpact {
namespace sim {
namespace codelab {


namespace internal {

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

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

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

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

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

}  // namespace internal

بعد القسم الأولي هناك مجموعة من ثلاث مساحات أسماء، واحدة لكل من تعريفات format في ملف riscv32i.bin_fmt:


namespace fence {

...

}  // namespace fence

namespace i_type {

...

}  // namespace i_type

namespace inst32_format {

...

}  // namespace inst32_format

دالة استخراج حقل البت inline ضمن كل من مساحات الاسم هذه لكل حقل بت بهذا التنسيق. بالإضافة إلى ذلك، إنّ التنسيق الأساسي تكرر دوال الاستخراج من التنسيقات التابعة التي 1) لا تظهر أسماء الحقول إلا في اسم حقل واحد، أو 2) عندما لا يتم تشير أسماء الحقول إلى نفس حقل النوع (مواضع البت والتوقيع/عدم التوقيع) بكل تنسيق تحدث فيه. يؤدي هذا إلى تفعيل حقول البت التي تصف نفس سيتم استخراجها باستخدام الدوال الموجودة في مساحة اسم تنسيق المستوى الأعلى.

في ما يلي الدوال ضمن مساحة الاسم i_type:

namespace i_type {

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

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

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

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

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

}  // namespace i_type

وأخيرًا، الإعلان عن دالة فك الترميز للتعليمة تم الإعلان عن المجموعة RiscVInst32. وهي 32 بت بدون توقيع كقيمة كلمة التعليمات وتعرض عضو فئة التعداد OpcodeEnum يتطابق، أو OpcodeEnum::kNone إذا لم يكن هناك تطابق.

OpcodeEnum DecodeRiscVInst32(uint32_t inst_word);

ملف المصدر الذي تم إنشاؤه (.cc)

افتح الآن تطبيق "riscv32i_bin_decoder.cc". يحتوي الجزء الأول من الملف على تعريفات #include ومساحة الاسم، تليها دالة فك الترميز التعريفات:

#include "riscv32i_bin_decoder.h"

namespace mpact {
namespace sim {
namespace codelab {

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

يُستخدم DecodeRiscVInst32None لإجراءات فك الترميز الفارغة، أي الإجراءات. التي تعرض OpcodeEnum::kNone. وتشكل الدوال الثلاث الأخرى برنامج فك الترميز الذي تم إنشاؤه. يعمل برنامج فك الترميز العام بطريقة هرمية. مجموعة تُحسب وحدات البت في كلمة التعليمات للتمييز بين التعليمات أو مجموعات التعليمات في المستوى الأعلى. لا تحتاج البتات إلى متجاورة. ويحدد عدد وحدات البت حجم جدول البحث الذي بوظائف برنامج فك الترميز من المستوى الثاني. ستتم ملاحظة ذلك في الخطوة التالية قسم من الملف:

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

    ...

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

    ...
};

وأخيرًا، يتم تحديد وظائف برنامج فك الترميز:

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

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

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

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

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

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

في هذه الحالة، حيث لا يوجد سوى 4 تعليمات محددة، يكون هناك مستوى واحد من فك الترميز وجدول بحث متفرق للغاية. وفقًا للتعليمات فستتغير بنية برنامج فك الترميز وعدد المستويات في قد يزيد التسلسل الهرمي لجدول برامج فك الترميز.


إضافة تعليمات ALU للتسجيل والتسجيل

حان الوقت الآن لإضافة بعض التعليمات الجديدة إلى ملف riscv32i.bin_fmt. تشير رسالة الأشكال البيانية هي مجموعة الإرشادات الأولى التي تتمثل في تعليمات ALU للتسجيل والتسجيل مثل 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 الثالث رمز العمليات

أول شيء علينا القيام به هو إضافة التنسيق. امضِ قدمًا وافتح riscv32i.bin_fmt في المحرِّر المفضّل لديك يتيح ذلك بعد Inst32Format مباشرةً أضِف تنسيقًا يُسمى RType مشتقًا من Inst32Format. كل عالم البت في RType unsigned. استخدام الأسماء وعرض البت والترتيب (من اليسار إلى اليمين) من الجدول أعلاه لتحديد التنسيق. إذا كنت بحاجة إلى تلميح، أو أردت الاطّلاع على الحل الكامل، انقر هنا.

بعد ذلك، نحتاج إلى إضافة التعليمات. التعليمات هي:

  • add - إضافة عدد صحيح.
  • and - استخدام البتات و
  • or - على مستوى البت أو
  • sll - التبديل إلى اليسار المنطقي.
  • sltu - تعيين أقل من، بدون توقيع.
  • sub - طرح عدد صحيح
  • xor - استخدام xor على مستوى البت.

وتكون ترميزاتهما كما يلي:

25/31 24 فبراير 19.15 14.12 7.11 6.0 اسم العملية
000 0000 rs2 rs1 000 الثالث 011 0011 إضافة
000 0000 rs2 rs1 111 الثالث 011 0011 و
000 0000 rs2 rs1 110 الثالث 011 0011 أو
000 0000 rs2 rs1 001 الثالث 011 0011 sll
000 0000 rs2 rs1 011 الثالث 011 0011 مسيحية
010 0000 rs2 rs1 000 الثالث 011 0011 sub
000 0000 rs2 rs1 100 الثالث 011 0011 xor
func7 func3 رمز العمليات

أضف تعريفات التعليمات هذه قبل التعليمات الأخرى في مجموعة تعليمات "RiscVInst32" يتم تحديد السلاسل الثنائية البادئة بادئة 0b (تشبه 0x للأرقام السداسية العشرية). لتسهيل لقراءة سلاسل طويلة من الأرقام الثنائية، يمكنك أيضًا إدراج علامة الاقتباس المفردة ' على النحو التالي: كفاصل أرقام في الأماكن التي تراها مناسبة.

سيكون لكل من تعريفات التعليمات هذه ثلاثة قيود، وهي "func7" وfunc3" و"opcode" بالنسبة إلى الجميع باستثناء sub، سينفّذ القيد func7 أن يكون:

func7 == 0b000'0000

يختلف قيد func3 في معظم التعليمات. معلومات تسجيل الدخول إلى add sub هو:

func3 == 0b000

يتم استخدام قيد opcode نفسه في كل من هذه التعليمات:

opcode == 0b011'0011

لا تنسَ إنهاء كل سطر بفاصلة منقوطة ;.

الحل النهائي هو هنا.

انطلق الآن وأنشئ مشروعك كما في السابق، وافتح ملف riscv32i_bin_decoder.cc. ستظهر لك الوظائف الإضافية لبرنامج فك الترميز تم إنشاؤها للتعامل مع التعليمات الجديدة. بالنسبة للجزء الأكبر منها مشابهة لتلك التي تم إنشاؤها من قبل، ولكن انظر إلى DecodeRiscVInst32_0_c المستخدَم لفك ترميز add/sub:

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

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


إضافة تعليمات ALU مع مستخدمين فوريين

إن المجموعة التالية من الإرشادات التي سنضيفها هي تعليمات ALU قيمة فورية بدلاً من أحد السجلات. هناك ثلاث مجموعات من هذه التعليمات (استنادًا إلى الحقل المباشر): التعليمات الفورية من النوع I مع توقيع فوري بحجم 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 الثالث رمز العمليات

تنسيق I-Type مستخدَم حاليًا في riscv32i.bin_fmt، لذا ما مِن حاجة إلى لإضافة هذا التنسيق.

إذا قارننا تنسيق I-Type المتخصص بتنسيق R-Type الذي حددناه في في التمرين السابق، نلاحظ أن الاختلاف الوحيد هو أن حقول rs2 تمت إعادة تسميته إلى uimm5. وبدلاً من إضافة تنسيق جديد كليًا، يمكننا تعزيز تنسيق R-Type. لا يمكننا إضافة حقل آخر، لأن ذلك سيزيد من عرض الشكل، ولكن يمكننا إضافة تراكب. إن التراكب هو اسم مستعار لمجموعة من ويمكن استخدامها لدمج تسلسلات فرعية متعددة في كيان منفصل مُسمّى. ويتمثل الآثار الجانبية في أن التعليمة البرمجية التي تم إنشاؤها الآن أيضًا دالة استخراج للتراكب، بالإضافة إلى وتلك الخاصة بالحقول. في هذه الحالة، عندما لا يتم التوقيع على كل من rs2 وuimm5 لا يُحدِث اختلافًا كبيرًا إلا مع التوضيح بأن الحقل مستخدَم كفوري. لإضافة تراكب يسمى uimm5 إلى تنسيق R-Type، أضف التالية بعد الحقل الأخير:

  overlays:
    unsigned uimm5[5] = rs2;

التنسيق الجديد الوحيد الذي نحتاج إلى إضافته هو تنسيق U-Type. قبل أن نضيف دالة الرسم لنضع في الاعتبار التعليمتين اللتين تستخدمان هذا التنسيق: auipc lui يؤدي كلاهما إلى تغيير القيمة الفورية 20 بت المتبقية بمقدار 12 قبل استخدامها إما بإضافة ملف الكمبيوتر الشخصي إليه (auipc) أو كتابته مباشرةً في سجلّ (lui). باستخدام تراكب، يمكننا توفير إصدار تم نقله مسبقًا من وقد حوّلنا بعض العمليات الحاسوبية من تنفيذ التعليمات إلى العملية التعليمية وفك ترميزه. أضِف التنسيق أولاً وفقًا للحقول المحدّدة في الجدول. أعلاه. ثم يمكننا إضافة التراكب التالي:

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

يتيح لنا بناء جملة التراكب إجراء تسلسل للحقول، وليس فقط الحقول الحرفية أيضًا. في هذه الحالة، ننشئ تسلسلاً له مع 12 صفرًا، ثم ننقله إلى اليسار بحلول 12 عامًا

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

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

وتكون ترميزاتهما كما يلي:

20.31 19.15 14.12 7.11 6.0 opcode_name
imm12 rs1 000 الثالث 001 0011 آدي
imm12 rs1 111 الثالث 001 0011 آندي
imm12 rs1 110 الثالث 001 0011 أوري
imm12 rs1 100 الثالث 001 0011 شوري
func3 رمز العمليات

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

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

وتكون ترميزاتهما كما يلي:

25/31 24 فبراير 19.15 14.12 7.11 6.0 اسم العملية
000 0000 uimm5 rs1 001 الثالث 001 0011 سلي
010 0000 uimm5 rs1 101 الثالث 001 0011 سراي
000 0000 uimm5 rs1 101 الثالث 001 0011 srli
func7 func3 رمز العمليات

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

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

وتكون ترميزاتهما كما يلي:

31.12 7.11 6.0 اسم العملية
uimm20 الثالث 001 0111 auipc
uimm20 الثالث 011 0111 لوي
رمز العمليات

انطلق وقم بإجراء التغييرات ثم بناء. تحقَّق من الناتج الذي تم إنشاؤه. العدل كما في السابق، يمكنك التحقق من عملك مقابل riscv32i.bin_fmt.


المجموعة التالية من التعليمات التي يجب تحديدها هي الفرع المشروط التعليمات وتعليمات الانتقال والارتباط وسجل الانتقال والارتباط التعليمات.

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

25/31 24 فبراير 19.15 14.12 7.11 6.0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 رمز العمليات

رغم أن ترميز B-Type متطابق في تخطيط مع تشفير R-Type، فإننا اختَر استخدام نوع تنسيق جديد كي يتوافق مع وثائق RiscV. ولكن يمكنك أيضًا إضافة تراكب للحصول على الفرع المناسب الإزاحة الفورية للخارج، باستخدام الحقلين func7 وrd من النوع R الترميز.

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

تسلسل وحدات البت في كلمة التعليمات المستخدمة لتشكيل الحرف المباشر هو: 31، 7، 30..25، 11..8. يتوافق هذا مع مراجع الحقول الفرعية التالية، حيث يحدد الفهرس أو النطاق وحدات البت في الحقل، مرقمة من اليمين إلى اليسار، أي تشير imm7[6] إلى msb لـ imm7، وتشير imm5[0] إلى lsb imm5

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

إن جعل هذا التلاعب بالبت جزءًا من تعليمات الفرع نفسها له وعيوب كبيرة. أولًا، يربط تنفيذ الدالة الدلالية التفاصيل في تمثيل التعليمات الثنائية. ثانيًا، يضيف المزيد من وقت التشغيل من الأعلى. الإجابة هي إضافة تراكب إلى تنسيق BType، بما في ذلك اللاحقة "0" لمراعاة التبديل الأيسر.

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

لاحظ أنه تم توقيع التراكب، لذا سيتم تمديده تلقائيًا عند استخراجه من كلمة التعليمات.

تستخدم تعليمات الانتقال والرابط (الفوري) ترميز J-Type:

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

هذا أيضًا تنسيق سهل إضافته، ولكن مرة أخرى، التنسيق المباشر الذي يستخدمه فإن التعليمات ليست مباشرة كما تبدو. تسلسلات البت المستخدمة بالشكل المباشر الكامل هي: 31، 19..12، 20، 30..21، والفوري النهائي هو تم تحريكها لليسار بمقدار واحد لمحاذاة نصف كلمة. الحل هو إضافة فلتر تراكب (21 بت لحساب الانتقال الأيسر) على التنسيق:

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

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

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

أخيرًا، يستخدم رابط الانتقال والرابط (التسجيل) التنسيق I-type كما هو مستخدم، مسبقًا.

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

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

هذه المرة، ما مِن تغيير يجب إجراؤه على التنسيق.

تعليمات الفرع التي نحتاج إلى إضافتها هي:

  • beq - الفرع إذا كان متساويًا
  • bge - الفرع إذا كان أكبر من أو يساوي.
  • bgeu - الفرع إذا كان أكبر من أو يساوي بدون توقيع.
  • blt - الفرع إذا كان أقل من.
  • bltu - الفرع إذا كان أقل من غير الموقع.
  • bne - فرع إذا لم يكن متساويًا.

ويتم ترميزها على النحو التالي:

25/31 24 فبراير 19.15 14.12 7.11 6.0 اسم العملية
imm7 rs2 rs1 000 imm5 110 0011 بك
imm7 rs2 rs1 101 imm5 110 0011 bge
imm7 rs2 rs1 111 imm5 110 0011 بجيو
imm7 rs2 rs1 100 imm5 110 0011 blt
imm7 rs2 rs1 110 imm5 110 0011 bltu
imm7 rs2 rs1 001 imm5 110 0011 bne
func3 رمز العمليات

تم ترميز تعليمات jal على النحو التالي:

31.12 7.11 6.0 اسم العملية
imm20 الثالث 110 1111 جال
رمز العمليات

تم ترميز تعليمات jalr على النحو التالي:

20.31 19.15 14.12 7.11 6.0 opcode_name
imm12 rs1 000 الثالث 110 0111 Jalr
func3 رمز العمليات

انطلق وقم بإجراء التغييرات ثم بناء. تحقَّق من الناتج الذي تم إنشاؤه. العدل كما في السابق، يمكنك التحقق من عملك مقابل riscv32i.bin_fmt.


إضافة تعليمات المتجر

تستخدم تعليمات المتجر ترميز S-Type، الذي يتطابق مع الترميز B-Type وهو الترميز المستخدم في تعليمات الفرع، باستثناء تكوين فورًا. سنضيف تنسيق SType ليتوافق مع RiscV. التوثيق.

25/31 24 فبراير 19.15 14.12 7.11 6.0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 رمز العمليات

في حالة استخدام شكل SType، لحسن الحظ، يكون الوجه مستقيمًا تسلسل للأمام للحقلين المباشرين، وبالتالي فإن مواصفات التراكب هي ببساطة:

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

تجدر الإشارة إلى عدم الحاجة إلى محددات نطاق بت عند ربط الحقول بالكامل.

يتم ترميز تعليمات المتجر على النحو التالي:

25/31 24 فبراير 19.15 14.12 7.11 6.0 اسم العملية
imm7 rs2 rs1 000 imm5 010 0011 sb
imm7 rs2 rs1 001 imm5 010 0011 ش
imm7 rs2 rs1 010 imm5 010 0011 sw
func3 رمز العمليات

انطلق وقم بإجراء التغييرات ثم بناء. تحقَّق من الناتج الذي تم إنشاؤه. العدل كما في السابق، يمكنك التحقق من عملك مقابل riscv32i.bin_fmt.


إضافة تعليمات التحميل

تستخدم تعليمات التحميل تنسيق I-Type. ولا يلزم إجراء أي تغيير هناك.

والترميزات هي:

20.31 19.15 14.12 7.11 6.0 opcode_name
imm12 rs1 000 الثالث 000 0011 لبنان
imm12 rs1 100 الثالث 000 0011 Lbu
imm12 rs1 001 الثالث 000 0011 H
imm12 rs1 101 الثالث 000 0011 ألهو
imm12 rs1 010 الثالث 000 0011 lw
func3 رمز العمليات

انطلق وقم بإجراء التغييرات ثم بناء. تحقَّق من الناتج الذي تم إنشاؤه. العدل كما في السابق، يمكنك التحقق من عملك مقابل riscv32i.bin_fmt.

وبهذا نصل إلى ختام هذا البرنامج التعليمي، نأمل أن يكون مفيدًا.