बाइनरी निर्देश डिकोडर ट्यूटोरियल

इस ट्यूटोरियल का मकसद ये है:

  • बाइनरी फ़ॉर्मैट की जानकारी वाली फ़ाइल के स्ट्रक्चर और सिंटैक्स को जानें.
  • जानें कि बाइनरी फ़ॉर्मैट की जानकारी, आईएसए फ़ॉर्मैट की जानकारी से कैसे मेल खाती है.
  • निर्देशों के RiscV RV32I सबसेट के लिए बाइनरी ब्यौरा लिखें.

खास जानकारी

RiscV बाइनरी निर्देश एन्कोडिंग

बाइनरी निर्देश एन्कोडिंग, निर्देशों को कोड में बदलने का मानक तरीका है पर लागू होता है. आम तौर पर, इन्हें एक्ज़ीक्यूटेबल फ़ाइल में सेव किया जाता है, का इस्तेमाल आम तौर पर ईएलएफ़ फ़ॉर्मैट में करते हैं. निर्देश या तो तय चौड़ाई वाले या अलग-अलग हो सकते हैं चौड़ाई.

आम तौर पर, निर्देश हर फ़ॉर्मैट के साथ कोड में बदलने के फ़ॉर्मैट के छोटे सेट का इस्तेमाल करते हैं कोड में बदले गए निर्देशों के हिसाब से कस्टमाइज़ किया गया हो. उदाहरण के लिए, निर्देशों में ऐसे किसी एक फ़ॉर्मैट का इस्तेमाल किया जा सकता है जो उपलब्ध ऑपकोड की संख्या को बढ़ाता है, हालांकि, तुरंत निर्देश पाने के लिए एक अन्य सुविधा का इस्तेमाल किया जाता है. कोड में बदले जा सकने वाले एडिट का साइज़ बढ़ाने के लिए उपलब्ध ऑपकोड. Branch और जंप के निर्देश करीब-करीब ऐसे फ़ॉर्मैट का इस्तेमाल करते हैं जो बड़े ऑफ़सेट वाली शाखाओं को बढ़ावा देने के लिए तुरंत किया जा सकता है.

उन निर्देशों के फ़ॉर्मैट जिनका इस्तेमाल हम अपने RiscV में डिकोड करने के लिए निर्देशों के मुताबिक करते हैं सिम्युलेटर के बारे में यहां बताया गया है:

आर-टाइप फ़ॉर्मैट, जिसका इस्तेमाल रजिस्टर-रजिस्टर करने के निर्देशों के लिए किया जाता है:

31..25 24..20 19..15 14..12 11..7 6..0
7 5 5 3 5 7
func7 rs2 rs1 func3 तीसरा ऑपकोड

I-Type फ़ॉर्मैट, इसका इस्तेमाल तुरंत रजिस्टर करने के निर्देशों, लोड करने के निर्देशों, और jalr निर्देश, 12 बिट तुरंत.

31..20 19..15 14..12 11..7 6..0
12 5 3 5 7
imm12 rs1 func3 तीसरा ऑपकोड

खास I-Type फ़ॉर्मैट, जिसका इस्तेमाल तुरंत निर्देशों के साथ शिफ़्ट के लिए किया जाता है, 5 बिट तुरंत:

31..25 24..20 19..15 14..12 11..7 6..0
7 5 5 3 5 7
func7 uimm5 rs1 func3 तीसरा ऑपकोड

U-Type फ़ॉर्मैट, जिसका इस्तेमाल तुरंत निर्देशों के लिए किया जाता है (lui, auipc), 20 बिट तुरंत:

31..12 11..7 6..0
20 5 7
uimm20 तीसरा ऑपकोड

बी-टाइप फ़ॉर्मैट, जिसका इस्तेमाल कंडिशनल ब्रांच के लिए किया जाता है, जो 12-बिट तुरंत पहले होता है.

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 इम्यूम इम्यूम ऑपकोड

J-Type फ़ॉर्मैट, jal निर्देश के लिए इस्तेमाल किया जाता है. यह 20 बिट फटाफट का होता है.

31 30..21 20 19..12 11..7 6..0
1 10 1 8 5 7
इम्यूम इम्यूम इम्यूम इम्यूम तीसरा ऑपकोड

S-Type फ़ॉर्मैट, स्टोर के निर्देशों के लिए इस्तेमाल किया जाता है, 12 बिट तुरंत.

31..25 24..20 19..15 14..12 11..7 6..0
7 5 5 3 5 7
इम्यूम rs2 rs1 func3 इम्यूम ऑपकोड

जैसा कि इन फ़ॉर्मैट में देखा जा सकता है, ये सभी निर्देश 32 बिट लंबे हैं, और हर फ़ॉर्मैट में कम 7 बिट, ऑपरेटर फ़ील्ड है. यह भी ध्यान रखें कि कई फ़ॉर्मैट के साइज़ तुरंत एक जैसे होते हैं, इसलिए उनके बिट इनसे लिए जाते हैं अलग-अलग हिस्सों में लागू किया जाता है. जैसा कि हमें दिखेगा, बाइनरी डिकोडर स्पेसिफ़िकेशन वाले फ़ॉर्मैट की मदद से, इसे समझा जा सकता है.

बाइनरी एन्कोडिंग की जानकारी

निर्देश की बाइनरी एन्कोडिंग को बाइनरी फ़ॉर्मैट में दिखाया जाता है (.bin_fmt) जानकारी फ़ाइल. यह आईएसए में निर्देश शामिल करें, ताकि बाइनरी फ़ॉर्मैट के निर्देश डिकोडर को जनरेट किया गया. जनरेट किया गया डिकोडर, ऑपकोड का पता लगाता है, आईएसए के लिए ज़रूरी जानकारी देने के लिए, ऑपरेंड और तुरंत दिखने वाले फ़ील्ड पिछले ट्यूटोरियल में बताया गया एन्कोडिंग-एग्नोस्टिक डिकोडर.

इस ट्यूटोरियल में हम किसी सबसेट के लिए, बाइनरी एन्कोडिंग की जानकारी वाली फ़ाइल लिखेंगे में इस्तेमाल किए गए निर्देशों को सिम्युलेट करने के लिए ज़रूरी है छोटा "नमस्ते वर्ल्ड" कार्यक्रम. RiscV आईएसए के बारे में ज़्यादा जानकारी के लिए देखें जोखिम-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 के नाम के साथ-साथ, यह भी बताती है कि Google की मदद करता है. पहला विकल्प namespace है, जो इसकी जानकारी देता है वह नेमस्पेस जिसमें जनरेट किया गया कोड रखा जाएगा. दूसरा, opcode_enum, जो ऑपकोड इन्यूमरेशन टाइप जनरेट होने का नाम देता है जनरेट किए गए कोड में आईएसए डिकोडर का इस्तेमाल किया जाना चाहिए. तीसरा, 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];
};

पहला यूआरएल, Inst32Format नाम के 32 बिट के चौड़े निर्देश फ़ॉर्मैट के बारे में बताता है, जिसमें दो फ़ील्ड: bits (25 बिट चौड़ी) और opcode (7 बिट चौड़ी). हर फ़ील्ड unsigned का मतलब है कि वैल्यू को निकालने पर, उसकी लंबाई शून्य हो जाएगी और उसे C++ पूर्णांक टाइप में रखा जाना चाहिए. बिटफ़ील्ड की चौड़ाई का योग ज़रूरी है फ़ॉर्मैट की चौड़ाई के बराबर हो. अगर कोई गड़बड़ी होती है, तो यह टूल एक गड़बड़ी जनरेट करेगा असहमति. इस फ़ॉर्मैट का इस्तेमाल किसी दूसरे फ़ॉर्मैट से नहीं किया गया है, इसलिए यह को टॉप लेवल फ़ॉर्मैट माना जाता है.

दूसरा निर्देश IType नाम के 32 बिट वाले निर्देश फ़ॉर्मैट के बारे में बताता है, जो कि Inst32Format से लिया गया है. इस वजह से, ये दोनों फ़ॉर्मैट मिलते-जुलते हैं. फ़ॉर्मैट में पांच फ़ील्ड: imm12, rs1, func3, rd, और opcode. imm12 फ़ील्ड यह है signed का मतलब है कि उस वैल्यू को तब साइन किया जाएगा, जब उस वैल्यू को एक्सट्रैक्ट करके C++ पूर्णांक टाइप में रखा जाना चाहिए. ध्यान दें कि IType.opcode दोनों में वही साइन किया हुआ/साइन नहीं किया गया एट्रिब्यूट और उसी निर्देश शब्द के बिट का हवाला देता है Inst32Format.opcode के तौर पर.

तीसरा फ़ॉर्मैट पसंद के मुताबिक बनाया गया फ़ॉर्मैट है, जिसका इस्तेमाल सिर्फ़ fence करता है निर्देश दिया गया है, जो एक ऐसा निर्देश है जिसे पहले ही बताया जा चुका है और हमारे पास चिंता न करें.

खास जानकारी: फ़ील्ड के नामों का इस्तेमाल अलग-अलग मिलते-जुलते फ़ॉर्मैट में तब तक करें, जब तक कि वे वे एक जैसे बिट दिखाते हैं और उनमें साइन/अनसाइन किया गया एट्रिब्यूट एक जैसा होता है.

riscv32i.bin_fmt में फ़ॉर्मैट की परिभाषाओं के बाद, एक निर्देश ग्रुप आता है परिभाषा शामिल नहीं है. निर्देश ग्रुप में सभी निर्देश एक जैसे होने चाहिए और ऐसे प्रारूप का उपयोग करें जो उसी में दी गई जानकारी शामिल होती है. जब किसी 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" का इस्तेमाल करने के लिए, opcode की गिनती का टाइप और एक बुनियादी निर्देश फ़ॉर्मैट. ऑपकोड की गिनती का टाइप वही होना चाहिए जो आईएसए के ट्यूटोरियल में बताए गए इंडिपेंडेंट निर्देश डिकोडर का फ़ॉर्मैट डिकोडर.

कोड में बदलने के हर निर्देशों के ब्यौरे के तीन हिस्से होते हैं:

  • ऑपकोड का नाम, जो निर्देश में इस्तेमाल किए गए तरीके से मेल खाना चाहिए डिकोडर की जानकारी, ताकि दोनों एक साथ काम कर सकें.
  • ऑपकोड के लिए इस्तेमाल के लिए निर्देश का फ़ॉर्मैट. यह ऐसे फ़ॉर्मैट में इसका इस्तेमाल आखिरी हिस्से में बिटफ़ील्ड के रेफ़रंस के लिए किया जाता है.
  • बिट फ़ील्ड कंस्ट्रेंट की कॉमा-सेपरेटेड लिस्ट, ==, !=, <, <=, >, और >= ये सभी सही होने चाहिए, ताकि ऑपरेटर निर्देश शब्द का इस्तेमाल करें.

.bin_fmt पार्सर इस सारी जानकारी का इस्तेमाल एक डिकोडर बनाने के लिए करता है, जो:

  • हर बिट के लिए ज़रूरत के हिसाब से एक्सट्रैक्शन फ़ंक्शन (साइन इन/साइन नहीं किया गया) देता है फ़ील्ड को खाली छोड़ दें. डेटा इकट्ठा करने वाले फ़ंक्शन को नेमस्पेस में रखा जाता है यह नाम फ़ॉर्मैट के नाम में मौजूद स्नेक-केस वाले वर्शन के हिसाब से दिया गया है. उदाहरण के लिए, IType फ़ॉर्मैट के लिए एक्सट्रैक्टर फ़ंक्शन को नेमस्पेस i_type में रखा गया है. डेटा इकट्ठा करने वाले हर फ़ंक्शन को inline एलान किया जाता है, इसलिए यह सबसे छोटी uint_t वैल्यू लेता है ऐसा टाइप जो फ़ॉर्मैट की चौड़ाई में बदलाव करता है और सबसे छोटा int_t दिखाता है (हस्ताक्षर किए गए के लिए), uint_t (बिना हस्ताक्षर वाला) टाइप, जिसमें एक्सट्रैक्ट किया गया फ़ील्ड होता है चौड़ाई. उदाहरण:
inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}
  • हर निर्देश ग्रुप के लिए एक डिकोड फ़ंक्शन. यह फ़ंक्शन का टाइप OpcodeEnum, और सबसे छोटा uint_t टाइप लेता है, जो में बदलाव कर सकते हैं.

शुरुआती बिल्ड करें

डायरेक्ट्री को riscv_bin_decoder में बदलें और इसका इस्तेमाल करके प्रोजेक्ट बनाएं निम्न आदेश:

$ cd riscv_bin_decoder
$ bazel build :all

अब अपनी डायरेक्ट्री को वापस डेटा स्टोर करने की जगह के रूट में बदलें. इसके बाद, आइए एक नज़र डालते हैं जनरेट किए गए सोर्स पर दिखते हैं. इसके लिए, डायरेक्ट्री को इसमें बदलें bazel-out/k8-fastbuild/bin/riscv_bin_decoder (यह मानते हुए कि आप x86 पर हैं होस्ट - अन्य होस्ट के लिए, k8-फ़ास्टबिल्ड कोई दूसरी स्ट्रिंग होगी).

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

जनरेट की गई हेडर फ़ाइल (.h)

riscv32i_bin_decoder.h खोलें. फ़ाइल के पहले भाग में मानक है बॉयलरप्लेट गार्ड, इनमें फ़ाइलें और नेमस्पेस की जानकारी शामिल होती है. उसके बाद internal नेमस्पेस में एक टेंप्लेट वाला हेल्पर फ़ंक्शन मौजूद है. यह फ़ंक्शन का इस्तेमाल बहुत लंबे फ़ॉर्मैट से बिट फ़ील्ड निकालने के लिए किया जाता है, ताकि वे 64-बिट C++ में फ़िट हो सकें पूर्णांक.

#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

शुरुआती सेक्शन के बाद तीन नेमस्पेस का एक सेट है, हर नेमस्पेस के लिए एक riscv32i.bin_fmt फ़ाइल में format एलानों की जानकारी:


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;
}

इस मामले में, जहां सिर्फ़ चार निर्देश परिभाषित किए गए हैं, वहां सिर्फ़ में डिकोड किया जा सकता है. जैसा कि निर्देश जोड़ने के बाद, डिकोडर की संरचना बदल जाएगी और डिकोडर टेबल का क्रम बढ़ सकता है.


रजिस्टर-रजिस्टर करने के लिए ALU के निर्देश जोड़ना

अब riscv32i.bin_fmt फ़ाइल में कुछ नए निर्देश जोड़े जा सकते हैं. कॉन्टेंट बनाने निर्देशों का पहला ग्रुप, एएलयू रजिस्टर करने के लिए निर्देश हैं, जैसे कि 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 तीसरा ऑपकोड

हमें सबसे पहले फ़ॉर्मैट जोड़ना है. आगे बढ़ें और अपने मन की बात कहें आपके पसंदीदा एडिटर में riscv32i.bin_fmt. Inst32Format के ठीक बाद RType नाम का एक फ़ॉर्मैट जोड़ें, जो Inst32Format से लिया गया है. सभी बिटफ़ील्ड RType में unsigned हैं. नाम, बिट की चौड़ाई, और क्रम का इस्तेमाल करें (बाएं से दाएं) ऊपर दी गई टेबल से फ़ॉर्मैट तय करें. अगर आपको कोई संकेत चाहिए या आपको हमारा सुझाव है कि यहां क्लिक करें.

इसके बाद हमें निर्देश जोड़ने होंगे. निर्देश:

  • add - पूर्णांक जोड़ें.
  • and - बिट के अनुसार और.
  • or - बिट के अनुसार या.
  • sll - तार्किक रूप से बाएं शिफ़्ट करें.
  • sltu - इससे कम, अहस्ताक्षरित.
  • sub - पूर्णांक को घटाना.
  • xor - बिट के अनुसार xor.

इनकी एन्कोडिंग हैं:

31..25 24..20 19..15 14..12 11..7 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 एसएल
000 0000 rs2 rs1 011 तीसरा 011 0011 एसएलटीयू
010 0000 rs2 rs1 000 तीसरा 011 0011 बदले में खेलने वाला खिलाड़ी
000 0000 rs2 rs1 100 तीसरा 011 0011 एक्सओआर
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-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 तीसरा ऑपकोड

riscv32i.bin_fmt में I-Type फ़ॉर्मैट पहले से मौजूद है, इसलिए कोई ज़रूरत नहीं है वह फ़ॉर्मैट जोड़ना ज़रूरी है.

अगर हम खास तौर पर बनाए गए I-Type फ़ॉर्मैट की तुलना उस R-Type फ़ॉर्मैट से करते हैं जिसमें हमने उसे बताया है तो हमने देखा कि अंतर सिर्फ़ इतना है कि rs2 फ़ील्ड का नाम बदलकर uimm5 कर दिया गया है. नया फ़ॉर्मैट जोड़ने के बजाय, हम कॉन्टेंट को बेहतर बनाने के लिए आर-टाइप फ़ॉर्मैट. हम कोई अन्य फ़ील्ड नहीं जोड़ सकते, क्योंकि इससे फ़ील्ड की चौड़ाई बढ़ जाएगी फ़ॉर्मैट के हिसाब से बदलाव कर सकते हैं, लेकिन हम एक ओवरले जोड़ सकते हैं. ओवरले, बिट शामिल होना चाहिए और इसका इस्तेमाल फ़ॉर्मैट को नाम वाली किसी अलग इकाई में सबमिट करें. इसका खराब असर यह होता है कि जनरेट किया गया कोड अब ओवरले के लिए एक्सट्रैक्शन फ़ंक्शन भी शामिल होगा, फ़ील्ड के लिए. इस मामले में, जब 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.

इनकी एन्कोडिंग हैं:

31..20 19..15 14..12 11..7 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 - तुरंत दाईं ओर तार्किक रूप से शिफ़्ट करें.

इनकी एन्कोडिंग हैं:

31..25 24..20 19..15 14..12 11..7 6..0 ऑपकोड का नाम
000 0000 uimm5 rs1 001 तीसरा 001 0011 स्ली
010 0000 uimm5 rs1 101 तीसरा 001 0011 सराय
000 0000 uimm5 rs1 101 तीसरा 001 0011 स्रली
func7 func3 ऑपकोड

हमें U-Type के लिए ये निर्देश जोड़ने होंगे:

  • auipc - पीसी से तुरंत आगे की इमेज जोड़ें.
  • lui - तुरंत सबसे ऊपर लोड करें.

इनकी एन्कोडिंग हैं:

31..12 11..7 6..0 ऑपकोड का नाम
uimm20 तीसरा 001 0111 auipc
uimm20 तीसरा 011 0111 लुई
ऑपकोड

आगे बढ़ें और बदलाव करें और फिर बनाएं. जनरेट किया गया आउटपुट देखें. बस देख सकते हैं कि पहले की तरह ही, riscv32i.bin_fmt भी शामिल है.


निर्देशों का अगला सेट कंडीशनल ब्रांच है निर्देश, जंप-ऐंड-लिंक निर्देश, और जंप-ऐंड-लिंक रजिस्टर निर्देश दिए गए हों.

हम जिन कंडिशनल ब्रांच को जोड़ रहे हैं वे बी-टाइप एन्कोडिंग का इस्तेमाल करती हैं.

31..25 24..20 19..15 14..12 11..7 6..0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 ऑपकोड

जबकि B-टाइप एन्कोडिंग, लेआउट में R-टाइप एन्कोडिंग के समान होती है, लेकिन हम RiscV दस्तावेज़ के साथ अलाइन करने के लिए इसके लिए नए फ़ॉर्मैट टाइप का इस्तेमाल करें. हालांकि, सही ब्रांच पाने के लिए आपने एक ओवरले भी जोड़ा होगा आर-टाइप के func7 और rd फ़ील्ड का इस्तेमाल करते हुए, डिसप्लेसमेंट तुरंत शुरू हो जाता है एन्कोडिंग

ऊपर बताए गए फ़ील्ड के साथ BType फ़ॉर्मैट जोड़ना ज़रूरी है, लेकिन नहीं काफ़ी हैं. जैसा कि आपको दिख रहा है, पहला निर्देश दो निर्देशों के फ़ील्ड में बंटा हुआ है. इसके अलावा, ब्रांच के निर्देश इसे दोनों फ़ील्ड को फ़िल्टर कर सकते हैं. इसके बजाय, हर फ़ील्ड को आगे बांटा जाता है और ये उन्हें किसी अलग क्रम में रखा गया है. अंत में, वह मान बाईं ओर शिफ़्ट हो जाता है एक 16-बिट संरेखित ऑफ़सेट प्राप्त करने के लिए.

तुरंत बनाने के लिए इस्तेमाल किए गए निर्देश शब्द में बिट के क्रम हैं: 31, 7, 30..25, 11..8. यह नीचे दिए गए सब-फ़ील्ड रेफ़रंस के बारे में बताता है, जहां इंडेक्स या रेंज, फ़ील्ड में बिट को तय करती है. इन बिट को दाएं से बाएं नंबर दिया जाता है, जैसे कि imm7[6] का मतलब imm7 के एमएसबी से है और imm5[0] का मतलब एलएसबी का है 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 11..7 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 तात्कालिक प्रारूप:

31..20 19..15 14..12 11..7 6..0
12 5 3 5 7
imm12 rs1 func3 तीसरा ऑपकोड

इस बार, फ़ॉर्मैट में कोई बदलाव करने की ज़रूरत नहीं है.

हमें बैंक खाते के लिए ये निर्देश जोड़ने होंगे:

  • beq - अगर बराबर है, तो शाखा.
  • bge - इससे ज़्यादा या इसके बराबर होने पर ब्रांच.
  • bgeu - ब्रांच, अगर हस्ताक्षर किए गए दस्तावेज़ से ज़्यादा या इसके बराबर है.
  • blt - इससे कम होने पर ब्रांच.
  • bltu - ब्रांच, अगर हस्ताक्षर नहीं किए गए से कम है.
  • bne - अगर बराबर नहीं है, तो शाखा.

उन्हें इस तरह कोड में बदला जाता है:

31..25 24..20 19..15 14..12 11..7 6..0 ऑपकोड का नाम
imm7 rs2 rs1 000 imm5 110 0011 Beq
imm7 rs2 rs1 101 imm5 110 0011 बीजीई
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 छोटा
func3 ऑपकोड

jal निर्देश को इस तरह कोड में बदला जाता है:

31..12 11..7 6..0 ऑपकोड का नाम
imm20 तीसरा 110 1111 जल
ऑपकोड

jalr निर्देश को इस तरह कोड में बदला जाता है:

31..20 19..15 14..12 11..7 6..0 opcode_name
imm12 rs1 000 तीसरा 110 0111 Jalr
func3 ऑपकोड

आगे बढ़ें और बदलाव करें और फिर बनाएं. जनरेट किया गया आउटपुट देखें. बस देख सकते हैं कि पहले की तरह ही, riscv32i.bin_fmt भी शामिल है.


स्टोर के लिए निर्देश जोड़ें

स्टोर के निर्देश में, एस-टाइप को कोड में बदलने के तरीके का इस्तेमाल किया जाता है, जो B-टाइप के जैसा होता है शाखा के निर्देशों में इस्तेमाल की गई एन्कोडिंग को छोड़कर, तुरंत. RiscV के मुताबिक बने रहने के लिए, हम SType फ़ॉर्मैट को जोड़ते हैं दस्तावेज़.

31..25 24..20 19..15 14..12 11..7 6..0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 ऑपकोड

SType फ़ॉर्मैट के मामले में, इस जवाब को समझने में मदद मिलती है. दो निकटतम फ़ील्ड का आगे का योग, ताकि ओवरले विशिष्टता इसका मतलब है:

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

ध्यान दें कि पूरे फ़ील्ड को जोड़ते समय, किसी बिट रेंज की जानकारी देने वाली सुविधा की ज़रूरत नहीं होती.

स्टोर के निर्देशों को इस तरह कोड में बदला जाता है:

31..25 24..20 19..15 14..12 11..7 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 फ़ॉर्मैट का इस्तेमाल करते हैं. उसमें कोई बदलाव नहीं करना पड़ेगा.

एन्कोडिंग इस तरह हैं:

31..20 19..15 14..12 11..7 6..0 opcode_name
imm12 rs1 000 तीसरा 000 0011 lb
imm12 rs1 100 तीसरा 000 0011 एलबीयू
imm12 rs1 001 तीसरा 000 0011 एलएच
imm12 rs1 101 तीसरा 000 0011 एलहू
imm12 rs1 010 तीसरा 000 0011 lw
func3 ऑपकोड

आगे बढ़ें और बदलाव करें और फिर बनाएं. जनरेट किया गया आउटपुट देखें. बस देख सकते हैं कि पहले की तरह ही, riscv32i.bin_fmt भी शामिल है.

इस ट्यूटोरियल में यह जानकारी दी गई है. हमें उम्मीद है कि यह जानकारी आपके लिए मददगार रही होगी.