পাঠ ২৪ · ৫৭-এর মধ্যে · মডিউল ৫
Home / Courses / Computer Architecture & Digital Logic / ইনস্ট্রাকশন ফরম্যাট ও এনকোডিং

ইনস্ট্রাকশন ফরম্যাট ও এনকোডিং

Instruction format & encoding
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • একটি মেশিন ইনস্ট্রাকশন কীভাবে ফিক্সড-দৈর্ঘ্য বাইনারি সংখ্যা হিসেবে ফিল্ডে ভাগ হয়ে থাকে
  • সাধারণ ইনস্ট্রাকশন ফিল্ড — opcode, রেজিস্টার, immediate — এবং তাদের ভূমিকা
  • R-type ও I-type ইনস্ট্রাকশন ফরম্যাটের গঠন ও পার্থক্য
  • বিট-শিফটিং ও মাস্কিং দিয়ে বাস্তবে ইনস্ট্রাকশন এনকোড/ডিকোড করা, এবং রাউন্ড-ট্রিপে যাচাই করা

১ · মেশিন ইনস্ট্রাকশন আসলে কী

L23-এ আমরা দেখেছি ISA হলো CPU-এর বোঝা ইনস্ট্রাকশনগুলোর তালিকা। কিন্তু হার্ডওয়্যারের দৃষ্টিকোণ থেকে একটি ইনস্ট্রাকশন আসলে শুধুই একটি সংখ্যা — একটি ফিক্সড-দৈর্ঘ্য (RISC-স্টাইল ISA-তে) বাইনারি প্যাটার্ন, যাকে নির্দিষ্ট, অর্থবহ অংশে (ফিল্ড) ভাগ করা থাকে। এটি সরাসরি L11-এর ডিকোডারN ইনপুট লাইন থেকে 2^N আউটপুটের ঠিক একটিকে সক্রিয় করে এমন সার্কিট — ইনস্ট্রাকশনের opcode ফিল্ড ডিকোড করে হার্ডওয়্যার ঠিক করে কোন অপারেশন চালাতে হবে। ধারণার বাস্তব প্রয়োগ — হার্ডওয়্যার একটি ডিকোডার দিয়ে ঠিক করে কোন ফিল্ড কী বোঝায়। এবং L02-এর হেক্স/বাইনারি গ্রুপিং ট্রিকের বাস্তব ব্যবহারও এখানে দেখা যায় — মেশিন কোড প্রায়ই হেক্সাডেসিমেলে প্রদর্শন করা হয়, কারণ এটি বাইনারির চেয়ে পড়া সহজ অথচ বাইনারিতে রূপান্তর trivial।

২ · সাধারণ ইনস্ট্রাকশন ফিল্ড

এই কোর্সের শিক্ষামূলক উদ্দেশ্যে আমরা একটি সরলীকৃত, প্রতিনিধিত্বমূলক RISC-স্টাইল ১৬-বিট ফরম্যাট ব্যবহার করব। একটি সাধারণ ইনস্ট্রাকশনে সাধারণত এই ফিল্ডগুলো থাকে —

  • opcode: কোন অপারেশন (add, load, branch...) — হার্ডওয়্যার এই ফিল্ড ডিকোড করে ALU অপারেশন ও কন্ট্রোল সিগন্যাল ঠিক করে (সরাসরি M6/L29-এর হার্ডওয়্যার্ড কন্ট্রোলের প্রিভিউ)।
  • ডেস্টিনেশন রেজিস্টার (rd): ফলাফল কোন রেজিস্টারে লেখা হবে।
  • সোর্স রেজিস্টার(গুলো) (rs1, rs2): অপারেন্ড কোথা থেকে আসবে।
  • immediate (imm): ইনস্ট্রাকশনের ভেতরেই এমবেড করা একটি সরাসরি constant মান — শুধু I-type-এ থাকে, rs2-এর জায়গা নেয়।

৩ · ইনস্ট্রাকশন টাইপ/ফরম্যাট — R-type, I-type

একটি সরলীকৃত তিন-ফরম্যাট শিক্ষামূলক স্কিম —

  • R-type (register-register): সব অপারেন্ডই রেজিস্টার, যেমন add rd, rs1, rs2 — rd = rs1 + rs2।
  • I-type (register-immediate): একটি অপারেন্ড ইনস্ট্রাকশনেই এমবেড করা constant, যেমন addi rd, rs1, imm — rd = rs1 + imm।
  • Branch/jump-স্টাইল ফরম্যাট: একটি টার্গেট অ্যাড্রেস/অফসেট ফিল্ড থাকে (L27-এর BEQ-এ বিস্তারিত দেখা যাবে)।
R-type (১৬ বিট) opcode (৪) rd (৩) rs1 (৩) rs2 (৩) unused (৩) I-type (১৬ বিট) opcode (৪) rd (৩) rs1 (৩) imm (৬) সব ফরম্যাটেই প্রথম ৪ বিট opcode -- হার্ডওয়্যার এই ফিল্ড দেখেই বাকি বিটের অর্থ ঠিক করে
একই ১৬-বিট জায়গা R-type-এ তিনটি রেজিস্টার আর I-type-এ দুটি রেজিস্টার + একটি ৬-বিট immediate-এ ভাগ হয়।

নিচের কোড সেলে এই ঠিক এই ফরম্যাট দুটো বিট-শিফটিং (<<) ও বিটওয়াইজ-OR (|) দিয়ে একটি একক ইন্টিজারে প্যাক (encode) করা হচ্ছে, এবং শিফটিং ও মাস্কিং (&) দিয়ে ফিল্ডগুলো আবার বের (decode) করে দেখানো হচ্ছে যে প্রতিটি ফিল্ড হুবহু ফেরত পাওয়া যাচ্ছে কিনা।

Python
# ১৬-বিট শিক্ষামূলক ইনস্ট্রাকশন ফরম্যাট
# R-type: [opcode(4)][rd(3)][rs1(3)][rs2(3)][unused(3)]
# I-type: [opcode(4)][rd(3)][rs1(3)][imm(6)]

def encode_r_type(opcode, rd, rs1, rs2):
    """চারটি ফিল্ডকে বিট-শিফট করে একটি ১৬-বিট ইন্টিজারে প্যাক করে"""
    return (opcode << 12) | (rd << 9) | (rs1 << 6) | (rs2 << 3)

def encode_i_type(opcode, rd, rs1, imm):
    imm_field = imm & 0x3F  # শুধু নিচের ৬ বিট রাখা হচ্ছে
    return (opcode << 12) | (rd << 9) | (rs1 << 6) | imm_field

def decode_r_type(encoded):
    opcode = (encoded >> 12) & 0xF
    rd     = (encoded >> 9)  & 0x7
    rs1    = (encoded >> 6)  & 0x7
    rs2    = (encoded >> 3)  & 0x7
    return {"opcode": opcode, "rd": rd, "rs1": rs1, "rs2": rs2}

def decode_i_type(encoded):
    opcode = (encoded >> 12) & 0xF
    rd     = (encoded >> 9)  & 0x7
    rs1    = (encoded >> 6)  & 0x7
    imm    = encoded & 0x3F
    return {"opcode": opcode, "rd": rd, "rs1": rs1, "imm": imm}

def decode_instruction(encoded, instr_type):
    if instr_type == "R":
        return decode_r_type(encoded)
    elif instr_type == "I":
        return decode_i_type(encoded)
    raise ValueError(f"অজানা ইনস্ট্রাকশন টাইপ: {instr_type}")

# --- টেস্ট ১: R-type -- ADD r3, r1, r2 (opcode=1 ধরা হলো) ---
enc_r = encode_r_type(opcode=1, rd=3, rs1=1, rs2=2)
print("R-type এনকোডেড বিট:", format(enc_r, '016b'), "=", enc_r)
dec_r = decode_instruction(enc_r, "R")
print("R-type ডিকোডেড:    ", dec_r)
assert dec_r == {"opcode": 1, "rd": 3, "rs1": 1, "rs2": 2}
print("R-type রাউন্ড-ট্রিপ ঠিক আছে ✓\n")

# --- টেস্ট ২: I-type -- ADDI r5, r4, 7 (opcode=2 ধরা হলো) ---
enc_i = encode_i_type(opcode=2, rd=5, rs1=4, imm=7)
print("I-type এনকোডেড বিট:", format(enc_i, '016b'), "=", enc_i)
dec_i = decode_instruction(enc_i, "I")
print("I-type ডিকোডেড:    ", dec_i)
assert dec_i == {"opcode": 2, "rd": 5, "rs1": 4, "imm": 7}
print("I-type রাউন্ড-ট্রিপ ঠিক আছে ✓\n")

# --- টেস্ট ৩: I-type -- immediate ফিল্ডের সর্বোচ্চ মান (৬ বিট = সর্বোচ্চ 63) ---
enc_i2 = encode_i_type(opcode=2, rd=5, rs1=4, imm=63)
dec_i2 = decode_instruction(enc_i2, "I")
print("I-type (imm সর্বোচ্চ 63):", format(enc_i2, '016b'), dec_i2)
assert dec_i2 == {"opcode": 2, "rd": 5, "rs1": 4, "imm": 63}
print("সীমা-মান রাউন্ড-ট্রিপও ঠিক আছে ✓")

    
লক্ষ্য করুন এনকোডিং-ডিকোডিং সম্পূর্ণ নির্ভরযোগ্য — encode করার সময় যে ফিল্ড-বিন্যাস ব্যবহার হয়েছে, ঠিক একই বিন্যাস অনুসরণ করেই decode করলে প্রতিটি ফিল্ড হুবহু ফেরত পাওয়া যায়। এটাই আসলে একটি ডিকোডার হার্ডওয়্যার সার্কিট (L11) যা করে — শুধু এখানে আমরা Python-এর বিটওয়াইজ অপারেটর দিয়ে সেই আচরণ সিমুলেট করছি।
মূল কথা · Key takeaway

একটি মেশিন ইনস্ট্রাকশন কোনো রহস্যময় জিনিস নয় — এটি শুধুই একটি ফিক্সড-দৈর্ঘ্য বাইনারি সংখ্যা, যার প্রতিটি বিট-রেঞ্জের একটি সুনির্দিষ্ট, চুক্তিবদ্ধ অর্থ আছে (opcode, রেজিস্টার নম্বর, immediate)। এনকোডিং মানে এই ফিল্ডগুলোকে বিট-শিফট করে একত্র করা, ডিকোডিং মানে শিফট-ও-মাস্ক করে আলাদা করা — এই একই প্যাটার্ন M6-এর পুরো ডেটাপাথ ডিজাইনের ভিত্তি হয়ে থাকবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ opcode ফিল্ডটি সবসময় সবচেয়ে বাম দিকের (most-significant) বিটগুলোতে রাখা হয় কেন — এটা কি নির্দিষ্ট কোনো কারণে, নাকি নিছক কনভেনশন?

এটি একটি বাস্তব ডিজাইন সিদ্ধান্ত, নিছক কনভেনশন নয় — যেহেতু হার্ডওয়্যারের সবার আগে জানা দরকার এটি কোন ইনস্ট্রাকশন টাইপ (R-type নাকি I-type), তাই opcode ফিল্ড সব ফরম্যাটেই একই বিট-পজিশনে রাখলে ডিকোডার ইনস্ট্রাকশনের বাকি অংশ interpret করার আগেই দ্রুত opcode বের করে টাইপ ঠিক করতে পারে। যদি opcode ফরম্যাট অনুযায়ী ভিন্ন পজিশনে থাকত, ডিকোডারকে প্রথমে টাইপ বের করার জন্য অন্য কোনো তথ্যের উপর নির্ভর করতে হতো — একটি অপ্রয়োজনীয় জটিলতা।

প্র ০২ I-type-এ immediate ফিল্ড মাত্র ৬ বিট — অর্থাৎ সর্বোচ্চ মান 63। এটি কি একটি বাস্তব সীমাবদ্ধতা?

হ্যাঁ, এটি একটি বাস্তব এবং গুরুত্বপূর্ণ ডিজাইন-ট্রেডঅফ — যত বেশি বিট immediate-এর জন্য বরাদ্দ করা হবে, তত কম বিট opcode বা রেজিস্টার-নম্বরের জন্য থাকবে (মোট ইনস্ট্রাকশন-দৈর্ঘ্য ফিক্সড থাকলে)। বাস্তব ISA-তে (যেমন RISC-V) এই সমস্যার সমাধানে কখনো কখনো একটি বড় constant একাধিক ইনস্ট্রাকশনে ভেঙে লোড করা হয় (একটি ইনস্ট্রাকশন উপরের বিট লোড করে, আরেকটি নিচের বিট যোগ করে) — ফিক্সড-দৈর্ঘ্য ফরম্যাটের এই সীমাবদ্ধতা মেনে নিয়ে তার চারপাশে ডিজাইন করার একটি বাস্তব সমাধান।

প্র ০৩ উপরের কোডে R-type-এর "unused (৩ বিট)" ফিল্ড আসলে কী কাজে লাগতে পারে — সবসময় কি অপচয়?

বাস্তব ISA-তে এই ধরনের "সংরক্ষিত" বিট প্রায়ই ভবিষ্যতের এক্সটেনশনের জন্য রাখা হয় — যেমন ভবিষ্যতে যদি একই opcode-এর একাধিক ভ্যারিয়েন্ট (যেমন signed বনাম unsigned add) দরকার হয়, এই সংরক্ষিত বিটগুলোকে একটি "function code" সাব-ফিল্ড হিসেবে ব্যবহার করা যায় ISA-এর মূল ফরম্যাট পুরোপুরি পুনর্ডিজাইন না করেই। এটি একটি বাস্তব উদাহরণ কেন ISA ডিজাইনাররা প্রায়ই ইচ্ছাকৃতভাবে কিছু বিট শুরুতে "unused" রেখে দেন।

অনুশীলন

  1. হাতে-কলমে করুন: R-type ফরম্যাট ব্যবহার করে opcode=3, rd=6, rs1=2, rs2=5 এর জন্য ১৬-বিট এনকোডেড বিট প্যাটার্নটি হাতে-কলমে বের করুন (প্রতিটি ফিল্ডকে তার বিট-পজিশনে বসিয়ে), তারপর encode_r_type(3, 6, 2, 5) চালিয়ে মিলিয়ে দেখুন।

    opcode=3 → 0011, rd=6 → 110, rs1=2 → 010, rs2=5 → 101, আর unused = 000। এগুলো পাশাপাশি বসালে: 0011 110 010 101 000 = 0011110010101000₂। দশমিকে এটি (3<<12)|(6<<9)|(2<<6)|(5<<3) = 12288+3072+128+40 = 15528। encode_r_type(3,6,2,5) চালালে ঠিক এই মানই আসার কথা।

  2. চিন্তা করুন: যদি এই ISA-তে রেজিস্টার সংখ্যা ৮ থেকে বাড়িয়ে ৩২ করতে হয় (অর্থাৎ প্রতিটি রেজিস্টার ফিল্ডে ৩ বিটের বদলে ৫ বিট দরকার), ১৬-বিট ফিক্সড ইনস্ট্রাকশন-দৈর্ঘ্যের মধ্যে থেকে R-type ফরম্যাটে এটি কীভাবে সম্ভব হতে পারে, বা কী সমস্যা হবে?

    সমস্যা হবে — বর্তমান R-type ফরম্যাটে ৪ (opcode) + ৩×৩ (rd, rs1, rs2) + ৩ (unused) = ১৬ বিট, একদম পূর্ণ ব্যবহৃত। যদি প্রতিটি রেজিস্টার ফিল্ড ৩ থেকে ৫ বিটে বাড়ে, তাহলে ৪ + ৩×৫ = ১৯ বিট লাগবে, যা ১৬-বিট ফিক্সড দৈর্ঘ্যের মধ্যে আর ধরবে না। বাস্তবে এই পরিস্থিতিতে হয় ইনস্ট্রাকশনের সামগ্রিক দৈর্ঘ্য বাড়াতে হয় (যেমন ১৬ থেকে ৩২ বিটে, বাস্তব RISC-V যেমন করেছে), অথবা opcode ফিল্ড ছোট করতে হয় (কম সংখ্যক আলাদা অপারেশন সমর্থন করা যাবে) — একটি বাস্তব ISA-ডিজাইন ট্রেডঅফ।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
L23 · ISA কী — RISC বনাম CISC