পাঠ ১৫ · ৫৭-এর মধ্যে · মডিউল ৩
Home / Courses / Computer Architecture & Digital Logic / রেজিস্টার

রেজিস্টার ও শিফট রেজিস্টার

Registers & shift registers
৮ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • N-টা ফ্লিপ-ফ্লপ কীভাবে একটা N-বিট রেজিস্টার তৈরি করে এবং প্যারালাল লোড কী
  • শিফট রেজিস্টারের শিফট-লেফট ও শিফট-রাইট আচরণ
  • SISO, SIPO, PISO — তিন ধরনের শিফট রেজিস্টার কনফিগারেশন ও তাদের বাস্তব ব্যবহার
  • Python-এ শিফট রেজিস্টার বাস্তবায়ন এবং শিফটের ফলাফল ইন্টিজার ডিভিশনের সাথে মিলিয়ে সংখ্যাগতভাবে যাচাই

১ · রেজিস্টার — প্যারালাল লোড

রেজিস্টারRegisterএকই ক্লক সিগন্যাল শেয়ার করা N-টা D ফ্লিপ-ফ্লপের একটা গ্রুপ, যেগুলো একসাথে একটা N-বিট মান ধরে রাখে। হলো L14-এর D ফ্লিপ-ফ্লপেরই সরাসরি সম্প্রসারণ — একটা মাত্র বিট ধরে রাখার বদলে, N-টা D ফ্লিপ-ফ্লপ পাশাপাশি বসিয়ে, সবগুলো একই ক্লক সিগন্যালে চালিয়ে, একটা সম্পূর্ণ N-বিট মান একসাথে ধরে রাখা যায়। এটাই সরাসরি L01-এ উল্লেখিত এবং M5/L26-এ formalized হতে যাওয়া CPU রেজিস্টার-এর হার্ডওয়্যার ভিত্তি।

প্যারালাল লোডParallel Loadরেজিস্টারের সব বিট একই মুহূর্তে, একই ক্লক এজে, একসাথে নতুন মান গ্রহণ করে — বিট বাই বিট ধারাবাহিকভাবে নয়। হলো রেজিস্টারের সবচেয়ে সাধারণ আচরণ — একটা নতুন মান দিলে, সব N বিট ঠিক একই ক্লক এজে একসাথে সেই মান গ্রহণ করে ফেলে, বিট বাই বিট আলাদা আলাদা সময়ে নয়।

২ · শিফট রেজিস্টার — বিট চলাচল

শিফট রেজিস্টারShift Registerপ্যারালাল লোডের বদলে (বা তার পাশাপাশি) প্রতি ক্লক এজে সব বিট এক পজিশন সরিয়ে দেয় এমন রেজিস্টার — এক প্রান্তে নতুন বিট ঢোকে, আরেক প্রান্তের বিট পড়ে যায়। একটু ভিন্নভাবে কাজ করে — প্রতিটা ক্লক এজে সব বিট এক পজিশন বামে বা ডানে সরে যায়, এক প্রান্তে একটা নতুন বিট ঢুকে আসে আর অন্য প্রান্তের বিটটা "পড়ে" যায় (হারিয়ে যায়)। এই সরল বিট-চলাচল কৌশলটাই তিনটা গুরুত্বপূর্ণ বাস্তব কনফিগারেশনের ভিত্তি।

SISO
Serial-In-Serial-Out — একটা একটা করে বিট ঢোকে, একটা একটা করে বিট বের হয়।
SIPO
Serial-In-Parallel-Out — বিটগুলো একটা একটা করে ঢোকে, কিন্তু N বিট জমা হওয়ার পর পুরো মান একসাথে (প্যারালালি) পড়া যায় — সিরিয়াল স্ট্রিমকে প্যারালাল শব্দে রূপান্তর, সরাসরি সিরিয়াল কমিউনিকেশন ইন্টারফেসে ব্যবহৃত।
PISO
Parallel-In-Serial-Out — উল্টো রূপান্তর: একটা N-বিট মান একসাথে লোড হয়, তারপর এক এক করে সিরিয়ালি বের করা হয়।
প্যারালাল লোড: সব N বিট একই ক্লক এজে একসাথে ঢোকে শিফট রেজিস্টার: প্রতি এজে সব বিট এক ঘর সরে যায় নতুন বিট ইন পুরনো বিট আউট
শিফট রেজিস্টারে বিট একমুখী স্রোতের মতো চলে — এক প্রান্তে ঢোকে, বিট বিট করে এগিয়ে গিয়ে অন্য প্রান্তে বেরিয়ে যায়।

৩ · কোড সেলে যাচাই — লোড ও শিফট

নিচের কোডে একটা মান লোড করে ৩ বার ডানে শিফট করা হবে, প্রতিটা টিকের পর রেজিস্টারের মান প্রিন্ট করে। যেহেতু ডানে শিফট করা মানে প্রতিবার সবচেয়ে ছোট স্থানমানের বিটটা পড়ে যাওয়া আর নতুন বিট (0) সবচেয়ে বড় স্থানমানে ঢোকা — এটা ঠিক ২ দিয়ে ইন্টিজার ভাগ করার (ভাগশেষ বাদ দিয়ে) সমতুল্য, যা কোডেই ক্রস-চেক করা হয়েছে।

Python
class Register:
    """N-বিট মান ধরে রাখা রেজিস্টার -- N-টা D ফ্লিপ-ফ্লপ একই ক্লকে একসাথে লোড হয়"""
    def __init__(self, n_bits):
        self.n_bits = n_bits
        self.bits = [0] * n_bits

    def load(self, value_bits):
        if len(value_bits) != self.n_bits:
            raise ValueError(f"এই রেজিস্টার {self.n_bits}-বিট, {len(value_bits)}-বিট মান দেওয়া হয়েছে")
        self.bits = list(value_bits)   # প্যারালাল লোড -- সব বিট একসাথে, একই ক্লক এজে

    def value(self):
        v = 0
        for b in self.bits:
            v = v * 2 + b
        return v


class ShiftRegister:
    """প্রতি ক্লক এজে সব বিট এক পজিশন শিফট হয় -- এক প্রান্তে নতুন বিট ঢোকে, অন্য প্রান্তেরটা পড়ে যায়"""
    def __init__(self, n_bits):
        self.n_bits = n_bits
        self.bits = [0] * n_bits

    def load(self, value_bits):
        self.bits = list(value_bits)

    def shift_right(self, new_bit_in):
        dropped = self.bits[-1]
        self.bits = [new_bit_in] + self.bits[:-1]
        return dropped

    def shift_left(self, new_bit_in):
        dropped = self.bits[0]
        self.bits = self.bits[1:] + [new_bit_in]
        return dropped

    def value(self):
        v = 0
        for b in self.bits:
            v = v * 2 + b
        return v


print("Register -- প্যারালাল লোড")
print("-" * 55)
reg = Register(4)
reg.load([1, 0, 1, 1])   # দশমিক 11
print(f"লোড করার পর: {reg.bits}  (দশমিক {reg.value()})")

print()
print("ShiftRegister -- ডানে শিফট, প্রতি টিকে নতুন বিট হিসেবে 0 ঢোকানো হচ্ছে")
print("-" * 55)
sr = ShiftRegister(4)
sr.load([1, 0, 1, 1])   # দশমিক 11
print(f"শুরুর মান: {sr.bits}  (দশমিক {sr.value()})")

expected = sr.value()
for tick in range(1, 4):
    dropped = sr.shift_right(0)
    expected //= 2   # ডানে ১ শিফট = ২ দিয়ে ইন্টিজার ভাগ (ভাগশেষ বাদ)
    print(f"টিক {tick}: শিফটের পর {sr.bits}  (দশমিক {sr.value()})  | পড়ে যাওয়া বিট={dropped}  | 2-দিয়ে-ভাগ চেক: {expected}")
    assert sr.value() == expected, "শিফট রেজিস্টারের ফলাফল ইন্টিজার ডিভিশনের সাথে মেলেনি!"

print()
print(f"চূড়ান্ত মান {sr.value()} == 11 // 8 == {11 // 8}  (৩ বার 2 দিয়ে ভাগের সমতুল্য, ৩টা ভাগশেষই বাদ পড়েছে) -- মিলেছে")

    
কোডের assert লাইনটা প্রতিটা শিফটের পরই চেক করছে যে শিফট রেজিস্টারের মান ঠিক ইন্টিজার ভাগের ফলাফলের সাথে মিলছে কি না — এটা নিছক দাবি নয়, প্রতিটা ধাপে সত্যিই কোড চালিয়ে যাচাই করা একটা গাণিতিক সামঞ্জস্য। ডানে শিফট মানেই বাইনারিতে "2 দিয়ে ভাগ, ভাগশেষ বাদ" — এটাই এখানে হাতেকলমে প্রমাণিত হলো।
মূল কথা · Key takeaway

রেজিস্টার হলো ফ্লিপ-ফ্লপের সমষ্টি যা একটা সম্পূর্ণ N-বিট মান ধরে রাখে, আর শিফট রেজিস্টার সেই একই ফ্লিপ-ফ্লপগুলোকে সিরিজে সাজিয়ে বিট চলাচলের ক্ষমতা দেয় — সিরিয়াল ও প্যারালাল ডেটার মধ্যে রূপান্তরের মূল হার্ডওয়্যার হাতিয়ার। পরের পাঠে (L16) এই একই শিফট রেজিস্টার কাঠামো দিয়েই একটা সম্পূর্ণ ভিন্ন ধরনের কাউন্টার — রিং কাউন্টার — তৈরি হবে।

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

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

প্র ০১ SIPO রেজিস্টার সিরিয়াল ডেটাকে প্যারালালে রূপান্তর করে কেন দরকারি — CPU তো ভেতরে সবসময় প্যারালাল ডেটা নিয়েই কাজ করে না?

ঠিকই — CPU ভেতরে প্যারালালি কাজ করে, কিন্তু বাইরের জগতের সাথে যোগাযোগ (যেমন একটা সিরিয়াল পোর্ট, USB, বা নেটওয়ার্ক লিংক) প্রায়ই একটা মাত্র তারে এক এক করে বিট পাঠায় — কারণ একাধিক তার (প্যারালাল বাস) দূরত্ব বাড়লে ব্যয়বহুল ও সিগন্যাল-সিঙ্ক্রোনাইজেশনের দিক থেকে জটিল হয়ে যায়। SIPO রেজিস্টার ঠিক এই "বাইরে থেকে এক এক করে বিট আসছে" আর "ভেতরে একসাথে পুরো শব্দ দরকার" — এই দুই জগতের সেতুবন্ধন করে।

প্র ০২ প্যারালাল লোড রেজিস্টার আর শিফট রেজিস্টার — এই দুটো কি একই হার্ডওয়্যারে একসাথে থাকতে পারে, নাকি একটা বেছে নিতে হয়?

বাস্তব হার্ডওয়্যারে প্রায়ই দুটোই একসাথে থাকে — এমন একটা রেজিস্টারকে বলা হয় "universal shift register", যেখানে একটা মোড-সিলেক্ট সিগন্যাল (আসলে L10-এর MUX-এর মতোই একটা নির্বাচন প্রক্রিয়া) ঠিক করে দেয় পরবর্তী ক্লক এজে রেজিস্টার প্যারালাল লোড করবে, নাকি বামে শিফট করবে, নাকি ডানে শিফট করবে, নাকি hold করবে। এটাই দেখায় কীভাবে ছোট ছোট বিল্ডিং ব্লক (ফ্লিপ-ফ্লপ + MUX) মিলিয়ে বহুমুখী হার্ডওয়্যার তৈরি হয়।

প্র ০৩ কোড সেলে ডানে শিফট করার সময় নতুন বিট হিসেবে সবসময় 0 ঢোকানো হয়েছে — এটা কি সবসময়ই সঠিক আচরণ?

অ্যানসাইনড (unsigned) সংখ্যার জন্য 0 ঢোকানো ("logical shift") ঠিকই ইন্টিজার ভাগের সাথে মেলে, যেমন কোডে দেখানো হয়েছে। কিন্তু সাইনড (signed, two's complement, L18-এ বিস্তারিত) সংখ্যার ক্ষেত্রে ডানে শিফট করার সময় সাইন বিট (সবচেয়ে বাম দিকের বিট) পুনরাবৃত্তি করে ঢোকাতে হয় (একে "arithmetic shift" বলা হয়), যাতে সংখ্যার ঋণাত্মক/ধনাত্মক ধর্ম বজায় থাকে — নাহলে একটা ঋণাত্মক সংখ্যাকে ২ দিয়ে ভাগ করলে ভুলভাবে ধনাত্মক হয়ে যেতে পারে। এই পার্থক্যটা L20-এর Booth's Algorithm-এ গুরুত্বপূর্ণ হয়ে উঠবে।

অনুশীলন

  1. হাতে ট্রেস করুন: একটা 4-বিট শিফট রেজিস্টারে মান 0110 (দশমিক 6) লোড করা হলো। এরপর দুইবার বামে শিফট করা হলো, প্রতিবার নতুন বিট হিসেবে 1 ঢোকানো হলো। প্রতিটা ধাপের পর রেজিস্টারের বিট ও দশমিক মান হাতে লিখুন।

    শুরু: 0110 (6)। ধাপ ১ (বামে শিফট, নতুন বিট 1 ডানে ঢোকে, বাম প্রান্তের 0 পড়ে যায়): 1101 (দশমিক 13)। ধাপ ২ (আবার বামে শিফট, নতুন বিট 1 ঢোকে, বাম প্রান্তের 1 পড়ে যায়): 1011 (দশমিক 11)। লক্ষ্য করুন বামে শিফট এখানে সাধারণ "2 দিয়ে গুণ"-এর সমতুল্য নয়, কারণ নতুন বিট হিসেবে 1 ঢোকানো হচ্ছে এবং 4-বিট সীমার বাইরের বিট হারিয়ে যাচ্ছে — এই "ওভারফ্লো করে বিট হারানো" আচরণটাই বাস্তব হার্ডওয়্যার রেজিস্টারের একটা গুরুত্বপূর্ণ সীমাবদ্ধতা।

  2. চিন্তা করুন: PISO শিফট রেজিস্টার ব্যবহার করে একটা 8-বিট মান সিরিয়ালি পাঠাতে হলে কতবার ক্লক টিক লাগবে, এবং কেন প্রথমে একটা প্যারালাল লোড ধাপও দরকার?

    প্রথমে একটা প্যারালাল লোড ধাপ লাগবে (৮ বিট মানটা একসাথে রেজিস্টারে বসাতে), তারপর ৮টা আলাদা শিফট টিক লাগবে — প্রতিটা টিকে একটা করে বিট সিরিয়ালি বেরিয়ে যাবে (MSB আগে বা LSB আগে, ডিজাইনের উপর নির্ভর করে)। প্যারালাল লোড ধাপটা অপরিহার্য কারণ এটাই সেই মুহূর্ত যখন পুরো 8-বিট মান একসাথে রেজিস্টারে ঢোকে — এরপর প্রতিটা শিফট টিক শুধু ইতিমধ্যে লোড হওয়া বিটগুলোকে এক এক করে বের করে আনে, নতুন কোনো তথ্য যোগ করে না।

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

আগের পাঠ
ফ্লিপ-ফ্লপ — D, JK, T ফ্লিপ-ফ্লপ