इस ट्यूटोरियल का मकसद ये है:
- जानें कि MPACT-Sim सिम्युलेटर में निर्देश कैसे दिखाए जाते हैं.
- आईएसए की जानकारी वाली फ़ाइल का स्ट्रक्चर और सिंटैक्स जानें.
- निर्देशों के RiscV RV32I सबसेट के लिए ISA विवरण लिखें
खास जानकारी
MPACT-Sim में टारगेट से जुड़े निर्देशों को डिकोड किया जाता है और इंटरनल स्टोरेज में सेव किया जाता है लोगों की जानकारी को ज़्यादा उपलब्ध कराने और सिमैंटिक का इस्तेमाल करने के लिए तेज़ी से लागू करने में मदद करता है. निर्देश के इन इंस्टेंस को किसी निर्देश में कैश मेमोरी में सेव किया जाता है कैश मेमोरी में सेव करें, ताकि बार-बार लागू किए जाने वाले निर्देशों की संख्या को कम किया जा सके लागू किया गया.
निर्देश की क्लास
शुरू करने से पहले, यह समझना ज़रूरी है कि निर्देशों को
MPACT-Sim में दिखाया गया है. Instruction
क्लास की जानकारी इसमें दी गई है
mpact-sim/mpact/sim/generic/instruction.h.
निर्देश क्लास इंस्टेंस में "लागू होने" पर, निर्देश को सिम्युलेट करना, जैसे कि:
- निर्देश का पता, निर्देश का सिम्युलेट किया गया साइज़, जैसे कि .text में साइज़.
- निर्देश का कोड.
- ऑपरेंड इंटरफ़ेस पॉइंटर का विधेय करें (अगर लागू हो).
- सोर्स ऑपरेंड इंटरफ़ेस पॉइंटर का वेक्टर.
- डेस्टिनेशन ऑपरेंड इंटरफ़ेस पॉइंटर का वेक्टर.
- सिमैंटिक फ़ंक्शन कॉल करने लायक.
- आर्किटेक्चरल स्टेट ऑब्जेक्ट की ओर पॉइंटर.
- कॉन्टेक्स्ट ऑब्जेक्ट की ओर पॉइंटर.
- बच्चे की ओर पॉइंटर और अगले निर्देश के इंस्टेंस.
- अलग करने वाली स्ट्रिंग.
आम तौर पर, इस तरह के इंस्टेंस को निर्देश (इंस्टेंस) कैश मेमोरी में सेव किया जाता है और फिर से इस्तेमाल किया जा सकता है. इससे परफ़ॉर्मेंस बेहतर होती है का इस्तेमाल करते हैं.
कॉन्टेक्स्ट ऑब्जेक्ट के पॉइंटर को छोड़कर, सभी जो आईएसए की जानकारी से जनरेट हुआ है. इसके लिए ट्यूटोरियल को इन आइटम के बारे में जानकारी होना ज़रूरी नहीं है, क्योंकि हम उसका इस्तेमाल करके उन्हें सीधे तौर पर जोड़ा जा सकता है. इसके बजाय, उनके इस्तेमाल के तरीके पर गहराई से पकड़ बनाना अच्छा होता है. काफ़ी हैं.
सिमैंटिक फ़ंक्शन कॉल किया जा सकने वाला C++ फ़ंक्शन/तरीका/फ़ंक्शन ऑब्जेक्ट है
(इसमें Lambdas भी शामिल है) जो निर्देश के सिमेंटिक्स को लागू करता है. इसके लिए
उदाहरण के लिए, add
निर्देश के लिए यह हर सोर्स ऑपरेंड को लोड करता है, दोनों
ऑपरेंड, और उसके नतीजे को एक डेस्टिनेशन ऑपरेंड में लिखता है. इसका विषय
सिमैंटिक फ़ंक्शन के ट्यूटोरियल में पूरी जानकारी दी गई है.
निर्देश ऑपरेंड
निर्देश क्लास में तीन तरह के ऑपरेंड इंटरफ़ेस के पॉइंटर शामिल होते हैं: विधेय, स्रोत और गंतव्य. ये इंटरफ़ेस सिमैंटिक फ़ंक्शन की मदद से दिए गए निर्देश के असल टाइप से अलग लिखा जाना चाहिए ऑपरेंड. उदाहरण के लिए, रजिस्टर और इंंटेंट की वैल्यू ऐक्सेस की जाती हैं एक समान इंटरफ़ेस के ज़रिए किया जा सकता है. इसका मतलब है कि वे निर्देश जो एक जैसा परफ़ॉर्म करते हैं ऑपरेशन लेकिन अलग-अलग ऑपरेंड (उदाहरण के लिए, रजिस्टर बनाम इमिडेट) पर लागू किया गया है.
उन ISA के लिए प्रेडिकेट ऑपरेंड इंटरफ़ेस जो अनुमानित निर्देश चलाने की प्रोसेस (अन्य ISAs के लिए यह शून्य है), इसका इस्तेमाल यह तय करने के लिए किया जाता है कि दिए गए निर्देश को प्रेडीकेट की बूलियन वैल्यू के आधार पर एक्ज़ीक्यूट किया जाना चाहिए.
// 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
के लिए इंतज़ार का समय
निर्देश दिए गए हैं. ये चार हो सकते हैं. इस बारे में ज़्यादा जानकारी यहां दी गई है:
सिमैंटिक फ़ंक्शन पर ट्यूटोरियल.
// 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;
};
आईएसए की जानकारी
प्रोसेसर का आईएसए (इंस्ट्रक्शन सेट आर्किटेक्चर) ऐब्स्ट्रैक्ट मॉडल को तय करता है किस तरह सॉफ़्टवेयर, हार्डवेयर के साथ इंटरैक्ट करता है. यह उपलब्ध निर्देश, डेटा टाइप, रजिस्टर, और अन्य मशीन की स्थिति निर्देश और उनके व्यवहार (सिमेंटिक्स) लागू होते हैं. इस मकसद के लिए MPACT-Sim में से, ISA में निर्देशों की वास्तविक एन्कोडिंग शामिल नहीं है. इसे अलग से माना जाता है.
प्रोसेसर आईएसए की जानकारी वाली फ़ाइल में बताया गया है दिए गए निर्देशों को ऐब्स्ट्रैक्ट, एन्कोडिंग ऐग्नोस्टिक लेवल पर सेट किया गया है. विवरण फ़ाइल उपलब्ध निर्देशों के सेट की गिनती करता है. हर निर्देश के लिए यह उसका नाम, उसके ऑपरेंड की संख्या और नाम, और किसी ऐसे C++ फ़ंक्शन/कॉल करने लायक बाइंडिंग से बाइंड करना चाहते हैं जो अपने सिमेंटिक्स लागू करता है. इसके अलावा, एक असेंबली फ़ॉर्मैटिंग स्ट्रिंग तय की जा सकती है और निर्देश के लिए, हार्डवेयर संसाधनों के नाम. टेक्स्ट वाला पहला टेक्स्ट लिखने में मदद मिलती है डीबग, ट्रेस करने या इंटरैक्टिव इस्तेमाल के लिए निर्देश दिखाना. कॉन्टेंट बनाने बाद वाले मॉडल का इस्तेमाल, सिम्युलेशन में साइकल के ज़्यादा सटीक होने के लिए किया जा सकता है.
आईएसए की जानकारी वाली फ़ाइल को isa-parser से पार्स किया जाता है, जो का इस्तेमाल करें. यह डिकोडर इन चीज़ों के लिए काम करता है: निर्देश ऑब्जेक्ट के फ़ील्ड में जानकारी भरें. खास वैल्यू, जैसे कि डेस्टिनेशन रजिस्टर नंबर, जो एक फ़ॉर्मैट खास निर्देश से लिया जाता है डिकोडर. ऐसा ही एक डिकोडर बाइनरी डिकोडर है, जो अगला ट्यूटोरियल.
इस ट्यूटोरियल में बताया गया है कि किसी सरल, अदिश के लिए आईएसए की जानकारी वाली फ़ाइल कैसे लिखें आर्किटेक्चर. हम RiscV RV32I निर्देशों के सबसेट का इस्तेमाल इसे समझें. अन्य ट्यूटोरियल के साथ मिलकर, एक ऐसा सिम्युलेटर बनाएं जो एक "नमस्ते दुनिया" सिम्युलेट करने के बारे में कार्यक्रम. RiscV आईएसए के बारे में ज़्यादा जानकारी के लिए देखें जोखिम-V की जानकारी.
सबसे पहले फ़ाइल खोलें:
riscv_isa_decoder/riscv32i.isa
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
फ़ाइल का कॉन्टेंट कई सेक्शन में बंटा होता है. पहली, आईएसए एलान:
isa RiscV32I {
namespace mpact::sim::codelab;
slots { riscv32; }
}
यह RiscV32I
को आईएसए का नाम बताता है और कोड जनरेटर
RiscV32IEncodingBase
नाम की एक क्लास बनाता है, जो
जनरेट किए गए डिकोडर का इस्तेमाल, ऑपकोड और ऑपरेंड की जानकारी पाने के लिए किया जाएगा. का नाम
यह क्लास आईएसए नाम को पास्कल-केस में बदलकर जनरेट किया जाता है. इसके बाद
इसे EncodingBase
के साथ जोड़ा जा रहा है. एलान वाला फ़ॉर्म slots { riscv32; }
यह बताता है कि RiscV32I में सिर्फ़ एक निर्देश स्लॉट riscv32
है
आईएसए (VLIW निर्देश में एक से ज़्यादा स्लॉट के उलट) और सिर्फ़ यह कि
मान्य निर्देश, वे होते हैं जिन्हें riscv32
में लागू करने के लिए तय किया जाता है.
// First disasm fragment is 15 char wide and left justified.
disasm widths = {-15};
इससे पता चलता है कि किसी भी डिसअसेंबली का पहला फ़्रैगमेंट (ज़्यादा जानकारी नीचे देखें), 15 वर्ण में बताई जाएगी. (wide). बाद के किसी भी फ़्रैगमेंट को इस फ़ील्ड में इनके बिना जोड़ दिया जाएगा कोई अतिरिक्त स्पेस शामिल न करें.
इसके नीचे तीन स्लॉट की घोषणा की गई है: riscv32i
, zicsr
, और riscv32
.
ऊपर दी गई isa
परिभाषा के आधार पर, सिर्फ़ riscv32
के लिए निर्देश तय किए गए हैं
स्लॉट RiscV32I
isa का हिस्सा होगा. बाकी दो स्लॉट किस काम के लिए हैं?
स्लॉटों का इस्तेमाल निर्देशों को अलग-अलग ग्रुप में बांटने के लिए किया जा सकता है. इसके बाद, ये ग्रुप बनाए जा सकते हैं
जिसे आखिर में एक स्लॉट में जोड़ दिया जाता है. नोटेशन : riscv32i, zicsr
पर ध्यान दें
riscv32
स्लॉट के एलान में. इससे पता चलता है कि riscv32
स्लॉट को इनहेरिट किया जाता है
zicsr
और riscv32i
स्लॉट में बताए गए सभी निर्देश. RiscV 32 बिट आईएसए
इसमें RV32I नाम का एक बेस ISA होता है, जिसमें वैकल्पिक एक्सटेंशन का एक सेट
जोड़ा जाना चाहिए. स्लॉट तंत्र की मदद से इन एक्सटेंशन में दिए गए निर्देश
अलग से तय करना होगा और फिर ज़रूरत के हिसाब से आखिर में जोड़ना होगा.
कुल ISA. इस मामले में, RiscV 'I' में निर्देश समूह परिभाषित हैं
अलग से 'zixr' ग्रुप. अतिरिक्त ग्रुप तय किए जा सकते हैं
'M' के लिए (गुणा करें/भाग करें), 'F' (एकल-सटीक फ़्लोटिंग पॉइंट), 'D'
(दोगुने सटीक फ़्लोटिंग पॉइंट), 'C' (कॉम्पैक्ट 16-बिट निर्देश) वगैरह
जो अंतिम 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 {}
सेक्शन में हेडर फ़ाइलों की सूची होती है
जनरेट किए गए कोड में तब शामिल किया जाना चाहिए, जब इस स्लॉट का संदर्भ सीधे तौर पर दिया गया हो या
सीधे तौर पर नहीं किया जा सकता. शामिल करें फ़ाइलों को वैश्विक रूप से भी सूचीबद्ध किया जा सकता है
स्कोप वाला includes {}
सेक्शन. इस मामले में, इन्हें हमेशा शामिल किया जाता है. यह काम कर सकता है
आसान हो, अगर एक ही इनक्लूड फ़ाइल को हर स्लॉट में जोड़ना पड़ता है
परिभाषा शामिल नहीं है.
default size
और default latency
एलानों में यह बताया गया है कि जब तक
अगर एक ही बार में निर्देश दिया जाता है, तो निर्देश का साइज़ 4 है, और
गंतव्य संकार्य लेखन 0 चक्र का है. ध्यान दें, निर्देश का साइज़
यहां बताया गया है, प्रोग्राम काउंटर में बढ़ोतरी का साइज़ है, जिसकी मदद से
सिम्युलेटेड में लागू किए जाने वाले अगले अनुक्रमिक निर्देश का पता
प्रोसेसर चुनें. यह इमेज का साइज़, बाइट में
एक्ज़ीक्यूटेबल फ़ाइल में निर्देश को किस तरह दिखाया जाएगा.
स्लॉट की परिभाषा के बीच में ऑपकोड सेक्शन होता है. जैसा कि आपको दिख रहा है, सिर्फ़ दो
ऑपकोड (निर्देश) fence
और ebreak
को अब तक इसमें तय किया गया है
riscv32i
. fence
ऑपकोड को (fence
) नाम देकर तय किया जाता है और
ऑपरेंड स्पेसिफ़िकेशन ({: imm12 : }
), इसके बाद वैकल्पिक डिसअसेंबली
फ़ॉर्मैट ("fence"
) है और कॉल करने लायक है, जो सिमैंटिक के तौर पर बंधा होना चाहिए
फ़ंक्शन ("&RV32IFence"
).
निर्देश ऑपरेंड को हर कॉम्पोनेंट के साथ तीन बार दिखाया जाता है
सेमीकोलन से अलग करें, predket ':' सोर्स ऑपरेंड सूची ':'
डेस्टिनेशन ऑपरेंड सूची का इस्तेमाल करें. सोर्स और डेस्टिनेशन ऑपरेंड की सूचियां कॉमा होती हैं
ऑपरेंड नामों की अलग-अलग सूचियां. जैसा कि आप देख सकते हैं, आपके निर्देश ऑपरेंड
fence
निर्देश में कोई प्रेडिकेट ऑपरेंड नहीं है, सिर्फ़ एक सोर्स है
संकार्य नाम imm12
, और कोई गंतव्य ऑपरेंड नहीं. RiscV RV32I सबसेट ऐसा करता है
प्रेडिकेट ऑपरेंड की वैल्यू हमेशा खाली रहेगी
इस ट्यूटोरियल में देखें.
सिमैंटिक फ़ंक्शन एक ऐसी स्ट्रिंग के रूप में तय किया जाता है जो C++ तय करने के लिए ज़रूरी है
सिमैंटिक फ़ंक्शन को कॉल करने के लिए फ़ंक्शन या कॉल करने लायक. दस्तावेज़ के हस्ताक्षर
सिमैंटिक फ़ंक्शन/कॉल करने लायक void(Instruction *)
है.
डिसअसेंबली स्पेसिफ़िकेशन में, स्ट्रिंग की कॉमा-सेपरेटेड लिस्ट होती है.
आम तौर पर, सिर्फ़ दो स्ट्रिंग का इस्तेमाल किया जाता है. एक ऑपकोड के लिए और दूसरी
ऑपरेंड. फ़ॉर्मैट किए जाने पर (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 पर हैं
होस्ट - अन्य होस्ट के लिए, k8-फ़ास्टबिल्ड कोई दूसरी स्ट्रिंग होगी).
$ 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
में बताया गया है). अन्य फ़ाइलों में, जनरेट किया गया डिकोडर शामिल होता है,
जिसके बारे में दूसरे ट्यूटोरियल में ज़्यादा जानकारी दी जाएगी.
बेज़ल बिल्ड रूल
Baज़ेल में ISA डिकोडर टारगेट को
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",
],
)
यह नियम C++ कोड जनरेट करने के लिए ISA पार्सर टूल और जनरेटर को कॉल करता है,
फिर, जनरेट किए गए डेटा को एक लाइब्रेरी में कंपाइल करता है, जिसे दूसरे नियम इस्तेमाल कर सकते हैं
//riscv_isa_decoder:riscv32i_isa
लेबल. includes
सेक्शन का इस्तेमाल किया गया है
ताकि अतिरिक्त .isa
फ़ाइलें शामिल की जा सकें. कॉन्टेंट बनाने
isa_name
का इस्तेमाल यह बताने के लिए किया जाता है कि एक से ज़्यादा होने पर, कौनसा isa ज़रूरी है
उस सोर्स फ़ाइल में जिसे डिकोडर जनरेट करना है.
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
फ़ाइल का इस्तेमाल, फ़ॉर्मैट एग्नोस्टिक डिकोडर जनरेट करने के लिए किया जाता है. हालांकि, यह अब भी
इससे एंट्री को गाइड करने के लिए, बाइनरी फ़ॉर्मैट और इसके लेआउट को ध्यान में रखा जाता है. अगर आपने
देख सकते हैं. ऐसे तीन फ़ील्ड हैं जो डिकोडर के लिए काम के होते हैं.
निर्देश ऑब्जेक्ट: 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
में बदलें
डायरेक्ट्री और rebuild. जनरेट की गई सोर्स फ़ाइलों को बेझिझक देखें.
तुरंत 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 | तीसरा | ऑपकोड |
जैसा कि आपको दिख रहा है, ऑपरेंड के नाम rs1
और rd
सिर्फ़ बिट फ़ील्ड के बारे में बताते हैं
और पूर्णांक रजिस्टरों को दिखाने के लिए इस्तेमाल किया जाता है, इसलिए इन नामों को
बनाए रखा गया है. तुरंत वैल्यू वाले फ़ील्ड अलग-अलग लंबाई और जगह के हैं और
दो (uimm5
और uimm20
) अनसाइन किए गए हैं, जबकि imm12
साइन किए हुए हैं. हर
ये अपने नाम का इस्तेमाल करेंगे.
इसलिए, I-Type निर्देशों के ऑपरेंड { : rs1, imm12 :rd
}
होने चाहिए. खास तरह के I-Type निर्देशों के लिए, यह { : rs1, uimm5 : rd}
होना चाहिए.
U-Type निर्देश ऑपरेंड की खास बातें { : uimm20 : rd }
होनी चाहिए.
हमें ये I-Type निर्देश जोड़ने होंगे:
addi
- तुरंत जोड़ें.andi
- बिट के अनुसार और तुरंत.ori
- बिट के अनुसार या तुरंत.xori
- बिट के हिसाब से xor.
हमें इन खास I-Type निर्देशों को जोड़ने की ज़रूरत है:
slli
- तुरंत बाईं ओर शिफ़्ट करें.srai
- दाएं अंकगणित को तुरंत शिफ़्ट करें.srli
- तुरंत दाईं ओर तार्किक रूप से शिफ़्ट करें.
हमें U-Type के लिए ये निर्देश जोड़ने होंगे:
auipc
- पीसी से तुरंत आगे की इमेज जोड़ें.lui
- तुरंत सबसे ऊपर लोड करें.
ऑपकोड के लिए इस्तेमाल किए जाने वाले नाम, निर्देश के नामों से स्वाभाविक रूप से आते हैं
ऊपर दिया गया है (नए सवालों के जवाब देने की ज़रूरत नहीं है - वे सभी यूनीक हैं). जब बात
सिमैंटिक फ़ंक्शन का इस्तेमाल करते हुए, याद रखें कि निर्देश ऑब्जेक्ट एन्कोड किए जाते हैं
ऐसे सोर्स ऑपरेंड के लिए इंटरफ़ेस जो बुनियादी ऑपरेंड के प्रति संतुलित होते हैं
टाइप करें. इसका मतलब यह है कि उन निर्देशों के लिए जिनका काम एक ही है, लेकिन
ऑपरेंड टाइप में अलग हो सकते हैं, तो एक ही सिमैंटिक फ़ंक्शन शेयर कर सकते हैं. उदाहरण के लिए,
addi
निर्देश, add
निर्देश की तरह ही काम करता है, अगर
कोई ऑपरेंड टाइप को अनदेखा करता है, इसलिए वे एक जैसे सिमैंटिक फ़ंक्शन का इस्तेमाल कर सकते हैं
स्पेसिफ़िकेशन "&RV32IAdd"
. इसी तरह andi
, ori
, xori
, और slli
के लिए.
अन्य निर्देशों में नए सिमैंटिक फ़ंक्शन का इस्तेमाल किया गया है, लेकिन उन्हें नाम दिया जाना चाहिए
संकार्य के आधार पर, न कि संकार्य के आधार पर, इसलिए srai
के लिए "&RV32ISra"
का उपयोग करें. कॉन्टेंट बनाने
U-Type निर्देशों auipc
और lui
में एक जैसा रजिस्टर नहीं है, इसलिए यह ठीक है
"&RV32IAuipc"
और "&RV32ILui"
का इस्तेमाल करने के लिए.
डिसअसेंबली स्ट्रिंग, पिछले एक्सरसाइज़ में इस्तेमाल की गई स्ट्रिंग से काफ़ी मिलती-जुलती हैं, लेकिन
जैसा कि आप उम्मीद करते हैं, %rs2
के रेफ़रंस %imm12
, %uimm5
से बदल दिए जाते हैं,
या %uimm20
, जो भी उपयुक्त हो.
आगे बढ़ें और बदलाव करें और बिल्ड बनाएं. जनरेट किया गया आउटपुट देखें. जिस तरह पहले, अपने काम की तुलना करके, riscv32i.isa और rv32i_instructions.h पर टैप करें.
ब्रांच और जंप-ऐंड-लिंक से जुड़े निर्देश जोड़ें
ब्रांच और जंप-ऐंड-लिंक निर्देश, जो हमें जोड़ने के लिए ज़रूरी हैं वे डेस्टिनेशन का इस्तेमाल करते हैं
ऑपरेंड जो केवल निर्देश में ही निहित है, जैसे कि अगला पीसी
वैल्यू. इस चरण में, हम इसे नाम के साथ एक सही संकार्य (ऑपरेशन) मानेंगे
next_pc
. इसकी जानकारी बाद के ट्यूटोरियल में दी जाएगी.
शाखा के लिए निर्देश
हम जिन ब्रांच को जोड़ रहे हैं वे बी-टाइप एन्कोडिंग का इस्तेमाल करते हैं.
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-बिट ब्रांच के लिए तुरंत. फ़्रैगमेंटेशन पर ध्यान दिया जाएगा
बाइनरी डिकोडर बनाने पर अगला ट्यूटोरियल. सभी
ब्रांच निर्देश, rs1 और rs2 के ज़रिए बताए गए पूर्णांक रजिस्टर की तुलना करें, अगर
शर्त सही है, तो तात्कालिक मान को वर्तमान पीसी मान में जोड़ दिया जाता है
अगले निर्देश का पता जनरेट करेगा. इस सीरीज़ के लिए,
इसलिए, ब्रांच के निर्देश { : rs1, rs2, bimm12 : next_pc }
होने चाहिए.
हमें बैंक खाते के लिए ये निर्देश जोड़ने होंगे:
beq
- अगर बराबर है, तो शाखा.bge
- इससे ज़्यादा या इसके बराबर होने पर ब्रांच.bgeu
- ब्रांच, अगर हस्ताक्षर किए गए दस्तावेज़ से ज़्यादा या इसके बराबर है.blt
- इससे कम होने पर ब्रांच.bltu
- ब्रांच, अगर हस्ताक्षर नहीं किए गए से कम है.bne
- अगर बराबर नहीं है, तो शाखा.
ये सभी ऑपकोड नाम यूनीक होते हैं, इसलिए .isa
में उनका फिर से इस्तेमाल किया जा सकता है
ब्यौरा. हालांकि, सिमैंटिक फ़ंक्शन के नए नाम ज़रूर जोड़ने चाहिए, जैसे कि
"&RV32IBeq"
वगैरह
खोलने के लिए इस्तेमाल की जाने वाली चीज़ों को अलग करना अब ज़्यादा आसान हो गया है, क्योंकि
निर्देश का इस्तेमाल, डेस्टिनेशन की गणना करने के लिए किया जाता है, लेकिन असल में वह इसका हिस्सा नहीं है
का इस्तेमाल किया जा सकता है. हालांकि, यह इसमें सेव की गई जानकारी का हिस्सा है
ताकि वह उपलब्ध हो. इसका समाधान है कि
एक्सप्रेशन सिंटैक्स का इस्तेमाल किया जा सकता है. '%' का उपयोग करने के बजाय इसके बाद
ऑपरेंड का नाम है, तो आप %(expression: print format) टाइप कर सकते हैं. बहुत आसान
एक्सप्रेशन समर्थित हैं, लेकिन @
के साथ पता और ऑफ़सेट उनमें से एक है
चिह्न का उपयोग करें. प्रिंट फ़ॉर्मैट इससे मिलता-जुलता है
C शैली के Printf फ़ॉर्मैट में भी शामिल होने चाहिए, लेकिन उसकी शुरुआत में %
का इस्तेमाल नहीं होना चाहिए. डिसअसेंबली फ़ॉर्मैट
फिर beq
निर्देश बन जाता है:
disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"
जंप-ऐंड-लिंक से जुड़े निर्देश
जंप-ऐंड-लिंक के सिर्फ़ दो निर्देश जोड़ने की ज़रूरत है, jal
(जंप-ऐंड-लिंक) और
jalr
(इनडायरेक्ट जंप-ऐंड-लिंक).
jal
निर्देश में, J-Type एन्कोडिंग का इस्तेमाल किया जाता है:
31 | 30..21 | 20 | 19..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
इम्यूम | इम्यूम | इम्यूम | इम्यूम | तीसरा | ऑपकोड |
ब्रांच के निर्देशों की तरह ही, 20-बिट तुरंत एक ही प्रॉपर्टी में बंटा होता है
फ़ील्ड में एक से ज़्यादा फ़ील्ड शामिल होंगे, इसलिए हम इसे jimm20
नाम देंगे. फ़्रैगमेंटेशन ज़रूरी नहीं है
फ़िलहाल, लेकिन आने वाले समय में इस पर ध्यान दिया जाएगा
बाइनरी डिकोडर बनाने पर ट्यूटोरियल. ऑपरेंड
इसके बाद, स्पेसिफ़िकेशन { : jimm20 : next_pc, rd }
हो जाता है. ध्यान दें कि दो
डेस्टिनेशन ऑपरेंड, अगला पीसी मान और लिंक रजिस्टर
निर्देश दिए गए हों.
ऊपर दिए गए ब्रांच निर्देशों की तरह, डिसअसेंबली फ़ॉर्मैट बन जाता है:
disasm: "jal", "%rd, %(@+jimm20:08x)"
सीधे पता न चलने वाला जंप-ऐंड-लिंक, 12-बिट तुरंत वाले I-Type फ़ॉर्मैट का इस्तेमाल करता है. यह
द्वारा निर्दिष्ट पूर्णांक रजिस्टर में चिह्न-विस्तारित तात्कालिक मान जोड़ता है
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 पर टैप करें.
स्टोर निर्देश जोड़ें
स्टोर के लिए निर्देश बहुत आसान हैं. वे सभी एस-टाइप फ़ॉर्मैट का इस्तेमाल करते हैं:
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) नतीजे लिखना. इस सिम्युलेटर के लिए, लोड करने से जुड़ी सिमैंटिक कार्रवाई को दो हिस्सों में बांट दिया जाता है अलग-अलग निर्देशों के साथ-साथ, मुख्य निर्देश और बच्चे के निर्देश को भी शामिल किया जा सकता है. इतना ही नहीं, जब हम ऑपरेंड को निर्दिष्ट करते हैं, तो हमें उन्हें मुख्य और बच्चे के लिए निर्देश देते हैं. ऐसा करने के लिए, ऑपरेंड स्पेसिफ़िकेशन को ट्रिपलेट की सूची. इसका सिंटैक्स यह है:
{(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) }
.
सिमैंटिक कार्रवाई दो निर्देशों में बंटी हुई है, इसलिए सिमैंटिक फ़ंक्शन
इसी तरह कॉल करने के लिए दो विकल्प देने होंगे. lw
(लोड शब्द) के लिए, यह
लिखा गया:
semfunc: "&RV32ILw", "&RV32ILwChild"
अलग करने के निर्देश ज़्यादा आम हैं. जहां किसी ने
बच्चे के लिए निर्देश. lw
के मामले में, यह होना चाहिए:
disasm: "lw", "%rd, %imm12(%rs1)"
लोड करने के लिए नीचे दिए गए निर्देश लागू होने चाहिए:
lb
- बाइट लोड करें.lbu
- बिना साइन इन किए हुए बाइट लोड करें.lh
- आधा शब्द लोड करें.lhu
- बिना साइन इन किया हुआ आधा शब्द लोड करें.lw
- शब्द लोड करें.
आगे बढ़ें और बदलाव करें और फिर बनाएं. जनरेट किया गया आउटपुट देखें. बस देख सकते हैं कि पहले की तरह ही, riscv32i.isa और rv32i_instructions.h पर टैप करें.
यहां तक पहुंचने के लिए धन्यवाद. हमें उम्मीद है कि यह जानकारी आपके लिए मददगार रही होगी.