এই টিউটোরিয়ালের উদ্দেশ্য হল:
- MPACT-সিম সিমুলেটরে নির্দেশাবলী কীভাবে উপস্থাপন করা হয় তা শিখুন।
- ISA বর্ণনা ফাইলের গঠন এবং সিনট্যাক্স শিখুন।
- নির্দেশাবলীর RiscV RV32I উপসেটের জন্য ISA বিবরণ লিখুন
ওভারভিউ
MPACT-সিমে লক্ষ্য নির্দেশাবলী ডিকোড করা হয় এবং একটি অভ্যন্তরীণ উপস্থাপনায় সংরক্ষণ করা হয় যাতে তাদের তথ্য আরও সহজলভ্য হয় এবং শব্দার্থবিদ্যা দ্রুত কার্যকর করা যায় । এই নির্দেশের দৃষ্টান্তগুলি একটি নির্দেশ ক্যাশে ক্যাশে করা হয় যাতে ঘন ঘন নির্বাহিত নির্দেশগুলি কার্যকর করা হয় তার সংখ্যা কমাতে।
নির্দেশনা ক্লাস
আমরা শুরু করার আগে, MPACT-Sim-এ নির্দেশাবলী কীভাবে উপস্থাপন করা হয় তা একটু দেখে নেওয়া দরকারী। Instruction
ক্লাস mpact-sim/mpact/sim/generic/instruction.h এ সংজ্ঞায়িত করা হয়েছে।
ইন্সট্রাকশন ক্লাস ইনস্ট্যান্সে নির্দেশনাকে "সম্পাদিত" করার সময় অনুকরণ করার জন্য প্রয়োজনীয় সমস্ত তথ্য থাকে, যেমন:
- নির্দেশের ঠিকানা, সিমুলেটেড নির্দেশের আকার, অর্থাৎ, .টেক্সটে আকার।
- নির্দেশ opcode.
- প্রিডিকেট অপারেন্ড ইন্টারফেস পয়েন্টার (যদি প্রযোজ্য হয়)।
- সোর্স অপারেন্ড ইন্টারফেস পয়েন্টারগুলির ভেক্টর।
- গন্তব্য অপারেন্ড ইন্টারফেস পয়েন্টার ভেক্টর.
- শব্দার্থিক ফাংশন কলযোগ্য।
- আর্কিটেকচারাল স্টেট অবজেক্টের পয়েন্টার।
- প্রসঙ্গ বস্তুর নির্দেশক।
- শিশু এবং পরবর্তী নির্দেশাবলীর জন্য নির্দেশক।
- Disassembly স্ট্রিং।
এই দৃষ্টান্তগুলি সাধারণত একটি নির্দেশ (উদাহরণ) ক্যাশে সংরক্ষণ করা হয় এবং যখনই নির্দেশটি পুনরায় কার্যকর করা হয় তখন পুনরায় ব্যবহার করা হয়। এটি রানটাইমের সময় কর্মক্ষমতা উন্নত করে।
প্রসঙ্গ অবজেক্টের পয়েন্টার ব্যতীত, সমস্ত আইএসএ বর্ণনা থেকে তৈরি করা নির্দেশনা ডিকোডার দ্বারা পূর্ণ হয়। এই টিউটোরিয়ালের জন্য এই আইটেমগুলির বিস্তারিত জানার প্রয়োজন নেই কারণ আমরা সেগুলি সরাসরি ব্যবহার করব না। পরিবর্তে, তারা কীভাবে ব্যবহার করা হয় সে সম্পর্কে একটি উচ্চ স্তরের উপলব্ধি যথেষ্ট।
শব্দার্থিক ফাংশন কলযোগ্য হল C++ ফাংশন/পদ্ধতি/ফাংশন অবজেক্ট (ল্যাম্বডাস সহ) যেটি নির্দেশের শব্দার্থবিদ্যা প্রয়োগ করে। উদাহরণস্বরূপ, একটি add
নির্দেশনার জন্য এটি প্রতিটি উত্স অপারেন্ড লোড করে, দুটি অপারেন্ড যোগ করে এবং ফলাফলটি একটি একক গন্তব্য অপারেন্ডে লেখে। শব্দার্থিক ফাংশন বিষয়ক শব্দার্থবিদ্যা ফাংশন টিউটোরিয়াল গভীরভাবে আচ্ছাদিত করা হয়েছে.
নির্দেশ অপারেন্ড
নির্দেশ শ্রেণীতে তিন ধরনের অপারেন্ড ইন্টারফেসের পয়েন্টার রয়েছে: পূর্বনির্ধারণ, উৎস এবং গন্তব্য। এই ইন্টারফেসগুলি অন্তর্নিহিত নির্দেশ অপারেন্ডের প্রকৃত প্রকারের থেকে স্বাধীনভাবে শব্দার্থিক ফাংশন লেখার অনুমতি দেয়। উদাহরণস্বরূপ, একই ইন্টারফেসের মাধ্যমে রেজিস্টারের মান এবং তাৎক্ষণিক অ্যাক্সেস করা হয়। এর মানে হল যে নির্দেশগুলি একই ক্রিয়াকলাপ সম্পাদন করে কিন্তু বিভিন্ন অপারেন্ডে (যেমন, রেজিস্টার বনাম ইমিডেট) একই শব্দার্থিক ফাংশন ব্যবহার করে প্রয়োগ করা যেতে পারে।
প্রিডিকেট অপারেন্ড ইন্টারফেস, যে আইএসএগুলির জন্য পূর্বনির্ধারিত নির্দেশ কার্যকর করা সমর্থন করে (অন্যান্য আইএসএগুলির জন্য এটি শূন্য), প্রিডিকেটের বুলিয়ান মানের উপর ভিত্তি করে একটি প্রদত্ত নির্দেশ কার্যকর করা উচিত কিনা তা নির্ধারণ করতে ব্যবহৃত হয়।
// 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 (নির্দেশনা সেট আর্কিটেকচার) বিমূর্ত মডেলকে সংজ্ঞায়িত করে যার মাধ্যমে সফ্টওয়্যার হার্ডওয়্যারের সাথে যোগাযোগ করে। এটি উপলব্ধ নির্দেশাবলীর সেট, ডেটার ধরন, রেজিস্টার এবং অন্যান্য মেশিনের অবস্থা নির্দেশ করে যে নির্দেশাবলী কাজ করে, সেইসাথে তাদের আচরণ (শব্দার্থবিদ্যা)। MPACT-Sim-এর উদ্দেশ্যে, ISA নির্দেশাবলীর প্রকৃত এনকোডিং অন্তর্ভুক্ত করে না। যা আলাদাভাবে চিকিৎসা করা হয়।
প্রসেসর ISA একটি বর্ণনা ফাইলে প্রকাশ করা হয় যা একটি বিমূর্ত, এনকোডিং অজ্ঞেয়মূলক স্তরে নির্দেশনা সেটকে বর্ণনা করে। বর্ণনা ফাইল উপলব্ধ নির্দেশাবলী সেট গণনা. প্রতিটি নির্দেশের জন্য এটির নাম, এর অপারেন্ডের সংখ্যা এবং নাম তালিকাভুক্ত করা বাধ্যতামূলক এবং এটি একটি C++ ফাংশন/ক্যালেবলের সাথে আবদ্ধ যা এর শব্দার্থবিদ্যা প্রয়োগ করে। উপরন্তু, কেউ একটি disassembly ফরম্যাটিং স্ট্রিং এবং হার্ডওয়্যার রিসোর্স নামের নির্দেশের ব্যবহার নির্দিষ্ট করতে পারে। আগেরটি ডিবাগ, ট্রেসিং বা ইন্টারেক্টিভ ব্যবহারের জন্য নির্দেশের একটি পাঠ্য উপস্থাপনা তৈরির জন্য দরকারী। পরবর্তীটি সিমুলেশনে আরও চক্র-নির্ভুলতা তৈরি করতে ব্যবহার করা যেতে পারে।
ISA বর্ণনা ফাইলটি isa-পার্সার দ্বারা পার্স করা হয় যা উপস্থাপনা-অজ্ঞেয়বাদী নির্দেশনা ডিকোডারের জন্য কোড তৈরি করে। এই ডিকোডারটি নির্দেশনা বস্তুর ক্ষেত্রগুলিকে জনবহুল করার জন্য দায়ী। নির্দিষ্ট মান, গন্তব্য নিবন্ধন নম্বর বলুন, একটি বিন্যাস নির্দিষ্ট নির্দেশ ডিকোডার থেকে প্রাপ্ত করা হয়। এরকম একটি ডিকোডার হল বাইনারি ডিকোডার, যা পরবর্তী টিউটোরিয়ালের ফোকাস।
এই টিউটোরিয়ালটি একটি সহজ, স্কেলার আর্কিটেকচারের জন্য কীভাবে একটি ISA বর্ণনা ফাইল লিখতে হয় তা কভার করে। আমরা এটিকে ব্যাখ্যা করার জন্য RiscV RV32I নির্দেশনা সেটের একটি উপসেট ব্যবহার করব এবং অন্যান্য টিউটোরিয়ালগুলির সাথে একটি "হ্যালো ওয়ার্ল্ড" প্রোগ্রাম অনুকরণ করতে সক্ষম একটি সিমুলেটর তৈরি করব। RiscV ISA সম্পর্কে আরও বিস্তারিত জানার জন্য Risc-V স্পেসিফিকেশন দেখুন।
ফাইলটি খোলার মাধ্যমে শুরু করুন: riscv_isa_decoder/riscv32i.isa
ফাইলের বিষয়বস্তু একাধিক বিভাগে বিভক্ত করা হয়েছে। প্রথমটি হল আইএসএ ঘোষণা:
isa RiscV32I {
namespace mpact::sim::codelab;
slots { riscv32; }
}
এটি RiscV32I
ISA-এর নাম বলে ঘোষণা করে এবং কোড জেনারেটর RiscV32IEncodingBase
নামে একটি ক্লাস তৈরি করবে যা উত্পন্ন ডিকোডার অপকোড এবং অপারেন্ড তথ্য পেতে ব্যবহার করবে এমন ইন্টারফেসকে সংজ্ঞায়িত করে। ISA নামটিকে Pascal-case-এ রূপান্তর করে, তারপর EncodingBase
এর সাথে সংযুক্ত করে এই শ্রেণীর নাম তৈরি করা হয়। ঘোষণার slots { riscv32; }
সুনির্দিষ্ট করে যে RiscV32I ISA-তে শুধুমাত্র একটি একক নির্দেশনা স্লট riscv32
রয়েছে (একটি VLIW নির্দেশে একাধিক স্লটের বিপরীতে), এবং শুধুমাত্র বৈধ নির্দেশাবলী যা riscv32
এ কার্যকর করার জন্য সংজ্ঞায়িত করা হয়েছে।
// First disasm fragment is 15 char wide and left justified.
disasm widths = {-15};
এটি নির্দিষ্ট করে যে কোনো disassembly স্পেসিফিকেশনের প্রথম বিচ্ছিন্ন করা অংশ (নীচে আরও দেখুন), 15 অক্ষর প্রশস্ত ক্ষেত্রে ন্যায়সঙ্গত রাখা হবে। পরবর্তী যেকোন টুকরো এই ক্ষেত্রটিতে কোনো অতিরিক্ত ব্যবধান ছাড়াই যুক্ত করা হবে।
এর নীচে তিনটি স্লট ঘোষণা রয়েছে: riscv32i
, zicsr
এবং riscv32
। উপরে isa
সংজ্ঞার উপর ভিত্তি করে, শুধুমাত্র riscv32
স্লটের জন্য সংজ্ঞায়িত নির্দেশাবলী RiscV32I
isa এর অংশ হবে। অন্য দুটি স্লট কি জন্য?
স্লটগুলিকে পৃথক গোষ্ঠীতে নির্দেশাবলী তৈরি করতে ব্যবহার করা যেতে পারে, যা শেষে একটি একক স্লটে একত্রিত করা যেতে পারে। নোটেশনটি লক্ষ্য করুন: riscv32
স্লট ঘোষণায় : riscv32i, zicsr
। এটি সুনির্দিষ্ট করে যে স্লট riscv32
স্লট zicsr
এবং riscv32i
তে সংজ্ঞায়িত সমস্ত নির্দেশাবলী উত্তরাধিকার সূত্রে প্রাপ্ত। RiscV 32 বিট আইএসএ RV32I নামে একটি বেস আইএসএ নিয়ে গঠিত, যেখানে ঐচ্ছিক এক্সটেনশনের একটি সেট যোগ করা যেতে পারে। স্লট মেকানিজম এই এক্সটেনশনগুলির নির্দেশাবলীকে আলাদাভাবে নির্দিষ্ট করার অনুমতি দেয় এবং তারপরে সামগ্রিক ISA সংজ্ঞায়িত করার জন্য শেষে প্রয়োজন অনুসারে একত্রিত করা হয়। এই ক্ষেত্রে, RiscV 'I' গ্রুপের নির্দেশাবলী 'zicsr' গ্রুপের নির্দেশাবলী থেকে আলাদাভাবে সংজ্ঞায়িত করা হয়েছে। অতিরিক্ত গোষ্ঠীগুলিকে 'M' (গুণ/ভাগ), 'F' (একক-নির্ভুল ভাসমান বিন্দু), 'D' (ডাবল-নির্ভুল ভাসমান বিন্দু), 'C' (কমপ্যাক্ট 16-বিট নির্দেশাবলী) ইত্যাদির জন্য সংজ্ঞায়িত করা যেতে পারে। পছন্দসই চূড়ান্ত RiscV ISA এর জন্য প্রয়োজন।
// The RiscV 'I' instructions.
slot riscv32i {
...
}
// RiscV32 CSR manipulation instructions.
slot zicsr {
...
}
// The final instruction set combines riscv32i and zicsr.
slot riscv32 : riscv32i, zicsr {
...
}
zicsr
এবং riscv32
স্লট সংজ্ঞা পরিবর্তন করার প্রয়োজন নেই। তবে এই টিউটোরিয়ালের ফোকাস হল riscv32i
স্লটে প্রয়োজনীয় সংজ্ঞা যোগ করা। আসুন এই স্লটে বর্তমানে কী সংজ্ঞায়িত করা হয়েছে তা ঘনিষ্ঠভাবে দেখে নেওয়া যাক:
// The RiscV 'I' instructions.
slot riscv32i {
// Include file that contains the declarations of the semantic functions for
// the 'I' instructions.
includes {
#include "learning/brain/research/mpact/sim/codelab/riscv_semantic_functions/solution/rv32i_instructions.h"
}
// These are all 32 bit instructions, so set default size to 4.
default size = 4;
// Model these with 0 latency to avoid buffering the result. Since RiscV
// instructions have sequential semantics this is fine.
default latency = 0;
// The opcodes.
opcodes {
fence{: imm12 : },
semfunc: "&RV32IFence"c
disasm: "fence";
ebreak{},
semfunc: "&RV32IEbreak",
disasm: "ebreak";
}
}
প্রথমত, একটি includes {}
বিভাগ রয়েছে যা হেডার ফাইলগুলিকে তালিকাভুক্ত করে যা জেনারেট করা কোডে অন্তর্ভুক্ত করা প্রয়োজন যখন এই স্লটটি চূড়ান্ত ISA-তে প্রত্যক্ষ বা পরোক্ষভাবে উল্লেখ করা হয়। অন্তর্ভুক্ত ফাইলগুলিকে একটি বিশ্বব্যাপী স্কোপড includes {}
বিভাগে তালিকাভুক্ত করা যেতে পারে, যে ক্ষেত্রে সেগুলি সর্বদা অন্তর্ভুক্ত থাকে৷ এটি সহজ হতে পারে যদি একই অন্তর্ভুক্ত ফাইল অন্যথায় প্রতিটি স্লট সংজ্ঞাতে যোগ করতে হবে।
default size
এবং default latency
ঘোষণাগুলি সংজ্ঞায়িত করে যে, অন্যথায় নির্দিষ্ট করা না থাকলে, একটি নির্দেশের আকার 4 এবং একটি গন্তব্য অপারেন্ড লেখার লেটেন্সি 0 চক্র। দ্রষ্টব্য, এখানে উল্লেখিত নির্দেশের আকার, সিমুলেটেড প্রসেসরে কার্যকর করার জন্য পরবর্তী অনুক্রমিক নির্দেশের ঠিকানা গণনা করার জন্য প্রোগ্রাম কাউন্টার ইনক্রিমেন্টের আকার। এটি ইনপুট এক্সিকিউটেবল ফাইলে নির্দেশনা উপস্থাপনার বাইটের আকারের সমান হতে পারে বা নাও হতে পারে।
স্লট সংজ্ঞার কেন্দ্রীয় হল opcode বিভাগ। আপনি দেখতে পাচ্ছেন, riscv32i
এ এ পর্যন্ত শুধুমাত্র দুটি অপকোড (নির্দেশ) fence
এবং ebreak
সংজ্ঞায়িত করা হয়েছে। fence
অপকোড নাম ( fence
) এবং অপারেন্ড স্পেসিফিকেশন ( {: imm12 : }
) উল্লেখ করে সংজ্ঞায়িত করা হয়, তারপরে ঐচ্ছিক বিচ্ছিন্নকরণ বিন্যাস ( "fence"
), এবং কলযোগ্য যা শব্দার্থিক ফাংশন হিসাবে আবদ্ধ হতে হবে ( "&RV32IFence"
)।
নির্দেশ অপারেন্ডগুলি একটি ট্রিপল হিসাবে নির্দিষ্ট করা হয়েছে, প্রতিটি উপাদান একটি সেমিকোলন দিয়ে আলাদা করা হয়েছে, predicate ':' উত্স অপারেন্ড তালিকা ':' গন্তব্য অপারেন্ড তালিকা । উৎস এবং গন্তব্য অপারেন্ড তালিকা হল অপারেন্ড নামের কমা দ্বারা পৃথক করা তালিকা। আপনি দেখতে পাচ্ছেন, fence
নির্দেশের নির্দেশনা অপারেন্ডে থাকে, কোন পূর্বনির্ধারিত অপারেন্ড নেই, শুধুমাত্র একটি একক উৎস অপারেন্ড নাম imm12
, এবং কোন গন্তব্য অপারেন্ড নেই। RiscV RV32I সাবসেট পূর্বনির্ধারিত সম্পাদন সমর্থন করে না, তাই এই টিউটোরিয়ালে প্রিডিকেট অপারেন্ড সবসময় খালি থাকবে।
শব্দার্থিক ফাংশন C++ ফাংশন বা শব্দার্থিক ফাংশন কল করতে ব্যবহার করার জন্য কলযোগ্য নির্দিষ্ট করার জন্য প্রয়োজনীয় স্ট্রিং হিসাবে নির্দিষ্ট করা হয়। শব্দার্থিক ফাংশন/কলেবলের স্বাক্ষর void(Instruction *)
।
disassembly স্পেসিফিকেশন স্ট্রিংগুলির একটি কমা বিভক্ত তালিকা নিয়ে গঠিত। সাধারণত শুধুমাত্র দুটি স্ট্রিং ব্যবহার করা হয়, একটি অপকোডের জন্য এবং একটি অপারেন্ডের জন্য। যখন ফরম্যাট করা হয় (নির্দেশে 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
এ পরিবর্তন করুন (অন্য হোস্টের জন্য, 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
অ্যারে আছে যা opcodes এর সমস্ত নাম সংরক্ষণ করে (এটি riscv32i_enums.cc
তে সংজ্ঞায়িত করা হয়েছে)। অন্যান্য ফাইলগুলিতে জেনারেটেড ডিকোডার রয়েছে, যা অন্য টিউটোরিয়ালে আরও কভার করা হবে।
Bazel বিল্ড নিয়ম
Bazel-এ ISA ডিকোডার টার্গেট mpact_isa_decoder
নামে একটি কাস্টম নিয়ম ম্যাক্রো ব্যবহার করে সংজ্ঞায়িত করা হয়, যা mpact-sim
সংগ্রহস্থলে mpact/sim/decoder/mpact_sim_isa.bzl
থেকে লোড করা হয়। এই টিউটোরিয়ালের জন্য 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 | ৬..০ |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | rs2 | rs1 | func3 | rd | অপকোড |
যদিও .isa
ফাইলটি একটি বিন্যাস অজ্ঞেয়বাদী ডিকোডার তৈরি করতে ব্যবহৃত হয়, এটি এখনও বাইনারি বিন্যাস এবং এন্ট্রিগুলিকে গাইড করার জন্য তার বিন্যাস বিবেচনা করা দরকারী। আপনি দেখতে পাচ্ছেন, তিনটি ক্ষেত্র রয়েছে যা ডিকোডারের সাথে প্রাসঙ্গিক যা নির্দেশনা অবজেক্টগুলিকে পপুলেট করে: rs2
, rs1
, এবং rd
। এই মুহুর্তে আমরা পূর্ণসংখ্যা রেজিস্টারগুলির জন্য এই নামগুলি ব্যবহার করতে বেছে নেব যেগুলি একইভাবে এনকোড করা হয়েছে (বিট সিকোয়েন্স), একই নির্দেশের ক্ষেত্রে, সমস্ত নির্দেশে।
আমরা যে নির্দেশাবলী যোগ করতে যাচ্ছি তা হল:
-
add
- পূর্ণসংখ্যা যোগ করুন। -
and
- bitwise এবং. -
or
- bitwise or. -
sll
- লজিক্যাল বাম স্থানান্তর করুন। -
sltu
- কম-এর চেয়ে সেট করুন, স্বাক্ষরবিহীন। -
sub
-পূর্ণসংখ্যা বিয়োগ। -
xor
- bitwise xor.
এই নির্দেশাবলীর প্রতিটি riscv32i
স্লট সংজ্ঞার opcodes
বিভাগে যোগ করা হবে। মনে রাখবেন যে প্রতিটি নির্দেশের জন্য আমাদের নাম, অপকোড, বিচ্ছিন্নকরণ এবং শব্দার্থিক ফাংশন নির্দিষ্ট করতে হবে। নামটি সহজ, আসুন উপরের অপকোড নামগুলি ব্যবহার করি। এছাড়াও, তারা সবাই একই অপারেন্ড ব্যবহার করে, তাই আমরা অপারেন্ড স্পেসিফিকেশনের জন্য { : rs1, rs2 : rd}
ব্যবহার করতে পারি। এর মানে হল যে rs1 দ্বারা নির্দিষ্ট করা রেজিস্টার সোর্স অপারেন্ডের ইনস্ট্রাকশন অবজেক্টের সোর্স অপারেন্ড ভেক্টরে ইনডেক্স 0 থাকবে, rs2 দ্বারা নির্দিষ্ট করা রেজিস্টার সোর্স অপারেন্ডের ইনডেক্স 1 থাকবে এবং rd দ্বারা নির্দিষ্ট করা রেজিস্টার ডেস্টিনেশন অপারেন্ডে একমাত্র উপাদান হবে গন্তব্য অপারেন্ড ভেক্টর (সূচক 0 এ)।
পরবর্তী শব্দার্থিক ফাংশন স্পেসিফিকেশন. এটি semfunc
কীওয়ার্ড এবং একটি C++ স্ট্রিং ব্যবহার করে করা হয় যা একটি কলযোগ্য নির্দিষ্ট করে যা একটি std::function
এ বরাদ্দ করতে ব্যবহার করা যেতে পারে। এই টিউটোরিয়ালে আমরা ফাংশন ব্যবহার করব, তাই কলযোগ্য স্ট্রিং হবে "&MyFunctionName"
। fence
নির্দেশ দ্বারা প্রস্তাবিত নামকরণ স্কিম ব্যবহার করে, এগুলি হওয়া উচিত "&RV32IAdd"
, "&RV32IAnd"
, ইত্যাদি।
শেষ হল disassembly স্পেসিফিকেশন. এটি কীওয়ার্ড disasm
দিয়ে শুরু হয় এবং স্ট্রিংগুলির একটি কমা দ্বারা বিভক্ত তালিকা দ্বারা অনুসরণ করা হয় যা নির্দেশ করে কীভাবে একটি স্ট্রিং হিসাবে প্রিন্ট করা উচিত। একটি অপারেন্ড নামের সামনে একটি %
চিহ্ন ব্যবহার করা সেই অপারেন্ডের স্ট্রিং উপস্থাপনা ব্যবহার করে একটি স্ট্রিং প্রতিস্থাপন নির্দেশ করে। add
নির্দেশনার জন্য, এটি হবে: disasm: "add", "%rd, %rs1,%rs2"
। এর মানে হল যে add
নির্দেশনার জন্য এন্ট্রি দেখতে হবে:
add{ : rs1, rs2 : rd},
semfunc: "&RV32IAdd",
disasm: "add", "%rd, %rs1, %rs2";
এগিয়ে যান এবং riscv32i.isa
ফাইলটি সম্পাদনা করুন এবং .isa
বিবরণে এই সমস্ত নির্দেশাবলী যোগ করুন। আপনার যদি সাহায্যের প্রয়োজন হয় (বা আপনার কাজটি পরীক্ষা করতে চান), সম্পূর্ণ বিবরণ ফাইলটি এখানে রয়েছে।
একবার riscv32i.isa
ফাইলে নির্দেশাবলী যোগ করা হলে, `../semantic_functions/-এ অবস্থিত rv32i_instructions.h
ফাইলে উল্লেখ করা প্রতিটি নতুন শব্দার্থিক ফাংশনের জন্য ফাংশন ঘোষণা যোগ করতে হবে। আবার, যদি আপনার সাহায্যের প্রয়োজন হয় (বা আপনার কাজ পরীক্ষা করতে চান), উত্তরটি এখানে ।
একবার এই সব হয়ে গেলে, এগিয়ে যান এবং riscv_isa_decoder
ডিরেক্টরিতে ফিরে যান এবং পুনর্নির্মাণ করুন। উত্পন্ন উৎস ফাইল পরীক্ষা নির্দ্বিধায়.
অবিলম্বে ALU নির্দেশাবলী যোগ করুন
পরবর্তী নির্দেশাবলীর সেট যা আমরা যোগ করব ALU নির্দেশাবলী যা একটি রেজিস্টারের পরিবর্তে একটি তাৎক্ষণিক মান ব্যবহার করে। এই নির্দেশাবলীর তিনটি গ্রুপ রয়েছে (তাত্ক্ষণিক ক্ষেত্রের উপর ভিত্তি করে): 12 বিট স্বাক্ষরিত অবিলম্বে I-টাইপ অবিলম্বে নির্দেশাবলী, শিফটের জন্য বিশেষায়িত I-টাইপ তাত্ক্ষণিক নির্দেশাবলী এবং 20-বিট সহ অবিলম্বে U-টাইপ স্বাক্ষরবিহীন তাৎক্ষণিক মান। বিন্যাসগুলি নীচে দেখানো হয়েছে:
আই-টাইপ তাৎক্ষণিক বিন্যাস:
31..20 | 19..15 | 14..12 | 11..7 | ৬..০ |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | অপকোড |
বিশেষায়িত আই-টাইপ তাৎক্ষণিক বিন্যাস:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | ৬..০ |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | uimm5 | rs1 | func3 | rd | অপকোড |
ইউ-টাইপ তাৎক্ষণিক বিন্যাস:
31..12 | 11..7 | ৬..০ |
---|---|---|
20 | 5 | 7 |
uimm20 | rd | অপকোড |
আপনি দেখতে পাচ্ছেন, অপারেন্ডের নাম rs1
এবং rd
আগের মতো একই বিট ক্ষেত্রগুলিকে উল্লেখ করে এবং পূর্ণসংখ্যা রেজিস্টারগুলিকে উপস্থাপন করতে ব্যবহৃত হয়, তাই এই নামগুলি বজায় রাখা যেতে পারে। তাৎক্ষণিক মানের ক্ষেত্রগুলি বিভিন্ন দৈর্ঘ্য এবং অবস্থানের, এবং দুটি ( uimm5
এবং uimm20
) স্বাক্ষরবিহীন, যেখানে imm12
স্বাক্ষরিত। এর প্রত্যেকেই তাদের নিজস্ব নাম ব্যবহার করবে।
I-টাইপ নির্দেশাবলীর জন্য অপারেন্ডগুলি তাই { : rs1, imm12 :rd }
হওয়া উচিত। বিশেষায়িত I-টাইপ নির্দেশাবলীর জন্য এটি { : rs1, uimm5 : rd}
হওয়া উচিত। U-টাইপ নির্দেশনা অপারেন্ড স্পেসিফিকেশন { : uimm20 : rd }
হওয়া উচিত।
আই-টাইপ নির্দেশাবলী আমাদের যোগ করতে হবে:
-
addi
- অবিলম্বে যোগ করুন। -
andi
- Bitwise এবং অবিলম্বে। -
ori
- বিটওয়াইজ বা অবিলম্বে। -
xori
- অবিলম্বে বিটওয়াইজ xor।
বিশেষ আই-টাইপ নির্দেশাবলী আমাদের যোগ করতে হবে:
-
slli
- অবিলম্বে লজিক্যাল বামে স্থানান্তর করুন। -
srai
- অবিলম্বে ডান পাটিগণিত স্থানান্তর করুন। -
srli
- অবিলম্বে ডান লজিক্যাল স্থানান্তর করুন।
আমাদের যে U-টাইপ নির্দেশাবলী যোগ করতে হবে তা হল:
-
auipc
- পিসিতে উপরের অবিলম্বে যোগ করুন। -
lui
- লোড উপরের অবিলম্বে.
অপকোডগুলির জন্য যে নামগুলি ব্যবহার করতে হবে সেগুলি উপরে নির্দেশিত নামগুলি থেকে স্বাভাবিকভাবেই অনুসরণ করে (নতুনগুলির সাথে আসার দরকার নেই - সেগুলি সবই অনন্য)৷ যখন শব্দার্থিক ফাংশনগুলি নির্দিষ্ট করার কথা আসে, তখন মনে রাখবেন যে নির্দেশ অবজেক্টগুলি অন্তর্নিহিত অপারেন্ড প্রকারের জন্য অজ্ঞেয়বাদী সোর্স অপারেন্ডগুলিতে ইন্টারফেসগুলিকে এনকোড করে৷ এর মানে হল যে নির্দেশাবলীর জন্য যেগুলি একই ক্রিয়াকলাপ রয়েছে, কিন্তু অপারেন্ড প্রকারে ভিন্ন হতে পারে, একই শব্দার্থিক ফাংশন ভাগ করতে পারে। উদাহরণস্বরূপ, যদি কেউ অপারেন্ড টাইপকে উপেক্ষা করে তাহলে addi
নির্দেশনাটি add
নির্দেশের মতো একই কাজ করে, তাই তারা একই শব্দার্থিক ফাংশন স্পেসিফিকেশন "&RV32IAdd"
ব্যবহার করতে পারে। একইভাবে andi
, ori
, xori
, এবং slli
এর জন্য। অন্যান্য নির্দেশাবলী নতুন শব্দার্থিক ফাংশন ব্যবহার করে, কিন্তু তাদের নামকরণ করা উচিত অপারেশনের উপর ভিত্তি করে, অপারেন্ড নয়, তাই srai
এর জন্য "&RV32ISra"
ব্যবহার করুন। U-টাইপ নির্দেশাবলী auipc
এবং lui
এর রেজিস্টার সমতুল্য নেই, তাই "&RV32IAuipc"
এবং "&RV32ILui"
ব্যবহার করা ঠিক আছে।
বিচ্ছিন্ন করা স্ট্রিংগুলি আগের অনুশীলনের সাথে খুব মিল, কিন্তু আপনি যেমনটি আশা করবেন, %rs2
এর রেফারেন্সগুলিকে %imm12
, %uimm5
, বা %uimm20
দিয়ে প্রতিস্থাপিত করা হয়েছে।
এগিয়ে যান এবং পরিবর্তন করুন এবং নির্মাণ করুন. উত্পন্ন আউটপুট পরীক্ষা করুন. ঠিক আগের মতই, আপনি riscv32i.isa এবং rv32i_instructions.h-এর বিরুদ্ধে আপনার কাজ পরীক্ষা করতে পারেন।
শাখা যোগ করুন এবং জাম্প-এন্ড-লিঙ্ক নির্দেশাবলী
আমাদের যে শাখা এবং জাম্প-এন্ড-লিঙ্ক নির্দেশাবলী যোগ করতে হবে উভয়ই একটি গন্তব্য অপারেন্ড ব্যবহার করে যা শুধুমাত্র নির্দেশের মধ্যেই নিহিত থাকে, যথা পরবর্তী পিসি মান। এই পর্যায়ে আমরা এটিকে next_pc
নামের একটি সঠিক অপারেন্ড হিসাবে বিবেচনা করব। এটি পরবর্তী টিউটোরিয়ালে আরও সংজ্ঞায়িত করা হবে।
শাখা নির্দেশাবলী
আমরা যে শাখাগুলি যোগ করছি সেগুলি B-টাইপ এনকোডিং ব্যবহার করে।
31 | 30..25 | 24..20 | 19..15 | 14..12 | 11..8 | 7 | ৬..০ |
---|---|---|---|---|---|---|---|
1 | 6 | 5 | 5 | 3 | 4 | 1 | 7 |
imm | imm | rs2 | rs1 | func3 | imm | imm | অপকোড |
বিভিন্ন তাৎক্ষণিক ক্ষেত্রগুলিকে একটি 12 বিট স্বাক্ষরিত তাৎক্ষণিক মানের মধ্যে সংযুক্ত করা হয়। যেহেতু বিন্যাসটি প্রকৃতপক্ষে প্রাসঙ্গিক নয়, তাই 12-বিট শাখার জন্য আমরা তাৎক্ষণিক bimm12
কল করব। বাইনারি ডিকোডার তৈরির পরবর্তী টিউটোরিয়ালে ফ্র্যাগমেন্টেশনের বিষয়ে আলোচনা করা হবে। সমস্ত শাখা নির্দেশাবলী rs1 এবং rs2 দ্বারা নির্দিষ্ট করা পূর্ণসংখ্যা রেজিস্টারের সাথে তুলনা করে, যদি শর্তটি সত্য হয়, তাহলে তাৎক্ষণিক মান বর্তমান পিসি মানের সাথে যোগ করা হয় যাতে কার্যকর করা পরবর্তী নির্দেশের ঠিকানা তৈরি করা হয়। শাখা নির্দেশাবলীর জন্য অপারেন্ডগুলি তাই { : rs1, rs2, bimm12 : next_pc }
হওয়া উচিত।
শাখা নির্দেশাবলী আমাদের যোগ করতে হবে, হল:
-
beq
- শাখা সমান হলে। -
bge
- এর চেয়ে বড় বা সমান হলে শাখা। -
bgeu
- এর চেয়ে বড় বা সমান স্বাক্ষরবিহীন শাখা। -
blt
- এর চেয়ে কম হলে শাখা। -
bltu
- স্বাক্ষরবিহীন থেকে কম হলে শাখা। -
bne
- শাখা সমান না হলে।
এই অপকোড নামগুলি সবই অনন্য, তাই সেগুলি .isa
বিবরণে পুনরায় ব্যবহার করা যেতে পারে৷ অবশ্যই, নতুন শব্দার্থিক ফাংশনের নাম যোগ করতে হবে, যেমন, "&RV32IBeq"
ইত্যাদি।
ডিসঅ্যাসেম্বলি স্পেসিফিকেশন এখন একটু কৌশলী, যেহেতু নির্দেশের ঠিকানাটি গন্তব্য গণনা করতে ব্যবহৃত হয়, এটি আসলে নির্দেশনা অপারেন্ডের অংশ না হয়েও। যাইহোক, এটি নির্দেশনা বস্তুতে সংরক্ষিত তথ্যের অংশ, তাই এটি উপলব্ধ। সমাধান হল disassembly স্ট্রিং-এ এক্সপ্রেশন সিনট্যাক্স ব্যবহার করা। অপারেন্ড নামের পরে '%' ব্যবহার করার পরিবর্তে, আপনি %( expression : print format ) টাইপ করতে পারেন। শুধুমাত্র খুব সাধারণ অভিব্যক্তি সমর্থিত, কিন্তু ঠিকানা প্লাস অফসেট তাদের মধ্যে একটি, বর্তমান নির্দেশের ঠিকানার জন্য @
চিহ্ন ব্যবহার করা হয়েছে। মুদ্রণ বিন্যাসটি C শৈলী printf বিন্যাসের অনুরূপ, কিন্তু অগ্রণী %
ছাড়াই। beq
নির্দেশের জন্য disassembly বিন্যাস তারপরে পরিণত হয়:
disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"
জাম্প-এন্ড-লিঙ্ক নির্দেশাবলী
শুধু দুটি জাম্প-এন্ড-লিংক নির্দেশনা যোগ করতে হবে, jal
(জাম্প-এন্ড-লিঙ্ক) এবং jalr
(পরোক্ষ জাম্প-এন্ড-লিঙ্ক)।
jal
নির্দেশ J-টাইপ এনকোডিং ব্যবহার করে:
31 | 30..21 | 20 | 19..12 | 11..7 | ৬..০ |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
imm | imm | imm | imm | rd | অপকোড |
ঠিক যেমন শাখা নির্দেশাবলীর জন্য, 20-বিট তাৎক্ষণিক একাধিক ক্ষেত্র জুড়ে খণ্ডিত, তাই আমরা এটির নাম দেব jimm20
। ফ্র্যাগমেন্টেশন এই মুহুর্তে গুরুত্বপূর্ণ নয়, তবে বাইনারি ডিকোডার তৈরির পরবর্তী টিউটোরিয়ালে তা সম্বোধন করা হবে। অপারেন্ড স্পেসিফিকেশন তখন { : jimm20 : next_pc, rd }
হয়ে যায়। নোট করুন যে দুটি গন্তব্য অপারেন্ড আছে, পরবর্তী পিসি মান এবং নির্দেশে উল্লেখিত লিঙ্ক রেজিস্টার।
উপরের শাখা নির্দেশাবলী অনুরূপ, disassembly বিন্যাস হয়:
disasm: "jal", "%rd, %(@+jimm20:08x)"
পরোক্ষ জাম্প-এন্ড-লিংক 12-বিট ইমিডিয়েট সহ I-টাইপ বিন্যাস ব্যবহার করে। এটি লক্ষ্য নির্দেশের ঠিকানা তৈরি করতে rs1
দ্বারা নির্দিষ্ট পূর্ণসংখ্যা রেজিস্টারে সাইন-বর্ধিত তাত্ক্ষণিক মান যোগ করে। লিঙ্ক রেজিস্টার হল rd
দ্বারা নির্দিষ্ট করা পূর্ণসংখ্যা রেজিস্টার।
31..20 | 19..15 | 14..12 | 11..7 | ৬..০ |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | অপকোড |
আপনি যদি প্যাটার্নটি দেখে থাকেন তবে আপনি এখন অনুমান করবেন যে jalr
জন্য অপারেন্ড স্পেসিফিকেশনটি হওয়া উচিত { : rs1, imm12 : next_pc, rd }
, এবং disassembly স্পেসিফিকেশন:
disasm: "jalr", "%rd, %rs1, %imm12"
এগিয়ে যান এবং পরিবর্তন করুন এবং তারপর তৈরি করুন। উত্পন্ন আউটপুট পরীক্ষা করুন. ঠিক আগের মতই, আপনি riscv32i.isa এবং rv32i_instructions.h এর বিরুদ্ধে আপনার কাজ পরীক্ষা করতে পারেন।
দোকান নির্দেশাবলী যোগ করুন
দোকান নির্দেশাবলী খুব সহজ. তারা সকলেই এস-টাইপ বিন্যাস ব্যবহার করে:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | ৬..০ |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
imm | rs2 | rs1 | func3 | imm | অপকোড |
আপনি দেখতে পাচ্ছেন, এটি একটি খণ্ডিত 12-বিট অবিলম্বে আরেকটি কেস, আসুন এটিকে simm12
বলি। দোকান নির্দেশাবলী সমস্ত 12-বিট তাৎক্ষণিক সাইন-এক্সটেন্ডেড মানের সাথে rs1 দ্বারা নির্দিষ্ট পূর্ণসংখ্যা রেজিস্টারের মান যোগ করে প্রাপ্ত মেমরিতে কার্যকর ঠিকানায় rs2 দ্বারা নির্দিষ্ট পূর্ণসংখ্যা রেজিস্টারের মান সংরক্ষণ করে। সমস্ত স্টোর নির্দেশাবলীর জন্য অপারেন্ড ফর্ম্যাটটি { : 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-টাইপ বিন্যাস ব্যবহার করে:
31..20 | 19..15 | 14..12 | 11..7 | ৬..০ |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | অপকোড |
অপারেন্ড স্পেসিফিকেশন ঠিকানা গণনা করার জন্য প্রয়োজনীয় অপারেন্ডগুলিকে বিভক্ত করে এবং লোড ডেটার জন্য রেজিস্টার গন্তব্য থেকে মেমরি অ্যাক্সেস শুরু করে: {( : rs1, imm12 : ), ( : : rd) }
।
যেহেতু শব্দার্থিক ক্রিয়া দুটি নির্দেশে বিভক্ত, তাই শব্দার্থিক ফাংশনকে একইভাবে দুটি কলেবল নির্দিষ্ট করতে হবে। lw
(লোড শব্দ) এর জন্য, এটি লেখা হবে:
semfunc: "&RV32ILw", "&RV32ILwChild"
disassembly স্পেসিফিকেশন আরো প্রচলিত. শিশু নির্দেশের কোন উল্লেখ নেই। lw
জন্য, এটি হওয়া উচিত:
disasm: "lw", "%rd, %imm12(%rs1)"
লোড নির্দেশাবলী বাস্তবায়ন করা প্রয়োজন হয়:
-
lb
- লোড বাইট। -
lbu
- লোড বাইট স্বাক্ষরবিহীন। -
lh
- অর্ধশব্দ লোড করুন। -
lhu
- লোড অর্ধশব্দ স্বাক্ষরবিহীন। -
lw
- লোড শব্দ।
এগিয়ে যান এবং পরিবর্তন করুন এবং তারপর তৈরি করুন। উত্পন্ন আউটপুট পরীক্ষা করুন. ঠিক আগের মতই, আপনি riscv32i.isa এবং rv32i_instructions.h এর বিরুদ্ধে আপনার কাজ পরীক্ষা করতে পারেন।
এই পর্যন্ত পাওয়ার জন্য আপনাকে ধন্যবাদ. আমরা আশা করি এটি কার্যকর হয়েছে।