পাঠ ৪৭ · ৫৭-এর মধ্যে · মডিউল ১০
Home / Courses / Computer Architecture & Digital Logic / বাস আর্কিটেকচার

বাস আর্কিটেকচার — সিস্টেম বাস ও প্রোটোকল

Bus architecture — system bus & protocols
৮ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • বাস কী এবং কেন প্রতিটি কম্পিউটার সিস্টেমে এটি অপরিহার্য
  • address bus, data bus, control bus — এই তিনটি লজিক্যাল উপাদান ও তাদের ভূমিকা
  • অ্যাড্রেস বাসের প্রস্থ কীভাবে সর্বোচ্চ অ্যাড্রেসযোগ্য মেমরির আকার নির্ধারণ করে
  • bus arbitration — একাধিক ডিভাইস একসাথে বাস চাইলে প্রায়োরিটি অনুযায়ী কীভাবে সিদ্ধান্ত হয়

১ · বাস কী এবং কেন দরকার

এখন পর্যন্ত এই কোর্সে CPU (M5-M7), মেমরি (M8-M9) ও I/O ডিভাইস (M10/L46) — প্রতিটি উপাদান আলাদাভাবে কভার হয়েছে। কিন্তু বাস্তবে এই উপাদানগুলো একে অপরের সাথে কীভাবে "কথা বলে"? উত্তরটাই হলো বাস — CPU, মেমরি ও I/O ডিভাইসগুলোকে সংযুক্তকারী একটি শেয়ার্ড তারের সেট, যা দিয়ে অ্যাড্রেস, ডেটা ও কন্ট্রোল সিগন্যাল আদান-প্রদান হয়। একে ভাবা যায় একটি শহরের রাস্তা হিসেবে — সব "বাড়ি" (CPU, RAM, ডিস্ক, কীবোর্ড) এই একই রাস্তা ব্যবহার করে যোগাযোগ করে, আলাদা আলাদা তার টানার বদলে।

২ · তিনটি লজিক্যাল উপাদান

একটি সিস্টেম বাসকে তিনটি লজিক্যাল অংশে ভাগ করা হয় — প্রতিটির আলাদা কাজ:

Address Bus
যে মেমরি/ডিভাইস অ্যাড্রেসে অ্যাক্সেস করা হচ্ছে তা বহন করে। প্রস্থ N বিট হলে সর্বোচ্চ 2^N টি ভিন্ন অবস্থান অ্যাড্রেসযোগ্য হয় (L02-এর বাইনারি-ম্যাগনিচিউড যুক্তির সরাসরি প্রয়োগ)।
Data Bus
প্রকৃত ডেটা মান বহন করে — দ্বিমুখী (একবার রিড, একবার রাইট)। প্রস্থ ঠিক করে দেয় প্রতি ট্রান্সফারে কত বাইট একসাথে চলাচল করতে পারে।
Control Bus
অপারেশনের ধরন (রিড না রাইট) এবং টাইমিং/সিঙ্ক্রোনাইজেশন সিগন্যাল বহন করে — কখন ডেটা বাসে ভ্যালিড মান আছে তা জানায়।
CPU মেমরি ও I/O ডিভাইস Address Bus Data Bus (দ্বিমুখী) Control Bus
তিনটি লজিক্যাল বাস মিলেই CPU-কে মেমরি ও I/O ডিভাইসের সাথে যোগাযোগ করতে সক্ষম করে — একই শেয়ার্ড তারের সেটের ওপর।

৩ · বাস আরবিট্রেশন

যেহেতু বাস শেয়ার্ড, একসাথে কেবল একটি ডিভাইসই কার্যকরভাবে বাস ব্যবহার করতে পারে। কিন্তু বাস্তবে একাধিক ডিভাইস (ডিস্ক কন্ট্রোলার, নেটওয়ার্ক কার্ড, কীবোর্ড) একই মুহূর্তে বাস চাইতে পারে — তখন কে আগে পাবে? এই সিদ্ধান্ত নেয় একটি bus arbiterএকাধিক অনুরোধকারী ডিভাইসের মধ্যে প্রায়োরিটি অনুযায়ী বাস-অ্যাক্সেস মঞ্জুর করে এমন হার্ডওয়্যার। — যা L11-এর প্রায়োরিটি এনকোডার-এরই একটি বাস্তব প্রয়োগ, শুধু ইন্টারাপ্ট লাইনের বদলে বাস-অ্যাক্সেস অনুরোধের ওপর প্রয়োগ করা হয়েছে। সাধারণ স্কিমগুলো — ফিক্সড প্রায়োরিটি (একটি নির্দিষ্ট ডিভাইস সবসময় আগে) এবং রাউন্ড-রবিন (পালাক্রমে সবাইকে সুযোগ দেওয়া, Operating Systems কোর্সের Round-Robin শিডিউলিং-এর ধারণারই বাস-অ্যাক্সেসে প্রয়োগ)।

L11-এর সাথে সম্পর্ক

priority encoder (L11) একাধিক সক্রিয় লাইনের মধ্যে সর্বোচ্চ-প্রায়োরিটি লাইনটি বের করে দিত। এখানে ঠিক একই লজিক প্রয়োগ হচ্ছে — শুধু "সক্রিয় ইন্টারাপ্ট লাইন" এর জায়গায় "বাস-অ্যাক্সেস অনুরোধ" বসেছে।

নিচের কোড সেলে একটি Bus ক্লাস ও একটি প্রায়োরিটি-ভিত্তিক BusArbiter সিমুলেট করা হলো — তিনটি ডিভাইস একসাথে বাস চাইলে আরবিটার সঠিক প্রায়োরিটি অনুযায়ী মঞ্জুর করে কিনা তা দেখা যাক।

Python
# সিমুলেটেড সিস্টেম বাস ও আরবিটার -- বাস্তব হার্ডওয়্যার অ্যাক্সেস নয়, শুধুই ধারণা বোঝানোর সিমুলেশন
class Bus:
    """একটি শেয়ার্ড সিস্টেম বাস -- address_bus, data_bus, control_bus আর প্রতিটি transaction-এর লগ রাখে"""
    def __init__(self):
        self.address_bus = None
        self.data_bus = None
        self.control_bus = None
        self.transaction_log = []

    def transfer(self, address, data, is_write):
        self.address_bus = address
        self.data_bus = data
        self.control_bus = "WRITE" if is_write else "READ"
        self.transaction_log.append({"address": address, "data": data, "op": self.control_bus})
        return self.control_bus


class BusArbiter:
    """একাধিক ডিভাইস একসাথে বাস চাইলে -- প্রায়োরিটি অনুযায়ী কে আগে পাবে তা ঠিক করে (L11-এর priority encoder-এর প্রয়োগ)"""
    def grant(self, requests):
        """requests: (device_name, priority) টিউপলের লিস্ট -- বড় priority সংখ্যা মানে বেশি গুরুত্বপূর্ণ"""
        ordered = sorted(requests, key=lambda r: r[1], reverse=True)
        granted = ordered[0]
        queued = ordered[1:]
        return granted, queued


bus = Bus()
print("-- একটি রেগুলার বাস ট্রানজেকশন --")
op = bus.transfer(address=0x2000, data=0x4F, is_write=True)
print(f"address_bus={hex(bus.address_bus)}  data_bus={hex(bus.data_bus)}  control_bus={bus.control_bus}")

print()
print("-- বাস আরবিট্রেশন: ৩টি ডিভাইস একসাথে বাস চাইছে --")
requests = [("Disk Controller", 2), ("Keyboard", 1), ("Network Card", 3)]
arbiter = BusArbiter()
granted, queued = arbiter.grant(requests)

print(f"বাস মঞ্জুর হলো: {granted[0]}  (priority={granted[1]})")
print("অপেক্ষমাণ (প্রায়োরিটি অনুযায়ী ক্রমে):")
for name, pr in queued:
    print(f"  - {name}  (priority={pr})")

assert granted[0] == "Network Card"
assert [q[0] for q in queued] == ["Disk Controller", "Keyboard"]

    
লক্ষ্য করুন — priority=3 নিয়ে "Network Card" সবার আগে বাস পেল, বাকি দুটো ডিভাইস প্রায়োরিটি অনুযায়ী সাজানো কিউতে অপেক্ষা করছে। এটাই bus arbitration-এর মূল কাজ — একটি শেয়ার্ড রিসোর্সে সুশৃঙ্খল অ্যাক্সেস নিশ্চিত করা।
মূল কথা · Key takeaway

বাস হলো কম্পিউটারের সব উপাদানের মধ্যে যোগাযোগের ভৌত ভিত্তি — address bus কোথায়, data bus কী, control bus কীভাবে বলে দেয়। আর বাস যেহেতু শেয়ার্ড, bus arbitration নিশ্চিত করে একাধিক ডিভাইস সংঘর্ষ ছাড়াই সুশৃঙ্খলভাবে এই শেয়ার্ড রিসোর্স ব্যবহার করতে পারে — পরের পাঠে (L48) দেখা যাবে ঠিক এই একই বাস কীভাবে ইন্টারাপ্ট সিগন্যাল বহন করে CPU-কে সতর্ক করে।

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

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

প্র ০১ একটি সিস্টেমে অ্যাড্রেস বাস মাত্র ১৬ বিট চওড়া হলে, সর্বোচ্চ কত বাইট মেমরি অ্যাড্রেসযোগ্য হবে? এই সীমাবদ্ধতা কীভাবে আসে?

১৬-বিট অ্যাড্রেস বাস দিয়ে সর্বোচ্চ 2^16 = 65,536টি ভিন্ন অ্যাড্রেস প্রকাশ করা যায় — অর্থাৎ সর্বোচ্চ 64 KB মেমরি অ্যাড্রেসযোগ্য। এটি সরাসরি L02-এর বাইনারি-ম্যাগনিচিউড যুক্তি — N বিট দিয়ে ঠিক 2^N টি ভিন্ন প্যাটার্ন প্রকাশ করা যায়, এর বেশি নয়। এই কারণেই আধুনিক সিস্টেমে ৩২-বিট বা ৬৪-বিট অ্যাড্রেস বাস ব্যবহার হয় — বড় মেমরি অ্যাড্রেস করতে হলে বাসের প্রস্থও বাড়াতে হয়।

প্র ০২ ডেটা বাসকে "দ্বিমুখী" বলা হয়েছে, কিন্তু অ্যাড্রেস বাস সাধারণত একমুখী (CPU থেকে মেমরি/ডিভাইসের দিকে) থাকে — কেন এই পার্থক্য?

অ্যাড্রেস সবসময় CPU-ই নির্ধারণ করে — "আমি কোন অবস্থানে যেতে চাই" এই তথ্য শুধু CPU থেকে বের হয়, মেমরি/ডিভাইস কখনো CPU-কে একটি অ্যাড্রেস "ফেরত পাঠায়" না। কিন্তু ডেটা দুই দিকেই যেতে পারে — রিড অপারেশনে মেমরি থেকে CPU-এর দিকে ডেটা আসে, রাইট অপারেশনে CPU থেকে মেমরির দিকে যায়। তাই ডেটা বাসকে দ্বিমুখী ডিজাইন করতে হয়, অ্যাড্রেস বাসকে নয়।

প্র ০৩ ধরুন bus arbiter শুধু ফিক্সড প্রায়োরিটি ব্যবহার করে (সবসময় একই ডিভাইসকে সর্বোচ্চ প্রায়োরিটি দেয়)। এতে কোন বাস্তব সমস্যা হতে পারে?

কম-প্রায়োরিটির ডিভাইসগুলো ("starvation") — যদি সর্বোচ্চ-প্রায়োরিটি ডিভাইসটি ক্রমাগত বাস চাইতে থাকে, তাহলে নিম্ন-প্রায়োরিটি ডিভাইসগুলো কখনোই বাস অ্যাক্সেস নাও পেতে পারে, এমনকি অসীম সময় অপেক্ষা করলেও। এই কারণেই round-robin-এর মতো স্কিমগুলো বাস্তব সিস্টেমে গুরুত্বপূর্ণ — প্রতিটি ডিভাইসকে অন্তত পালাক্রমে একটি সুযোগ নিশ্চিত করে, ঠিক যেমন OS কোর্সের Round-Robin শিডিউলিং প্রতিটি প্রসেসকে CPU টাইমের একটি ন্যায্য ভাগ নিশ্চিত করে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে requests-এ একটি চতুর্থ ডিভাইস ("Printer", 3) যোগ করলে (Network Card-এর সমান প্রায়োরিটি) কী ঘটবে বলে মনে করেন — sorted() এই টাই কীভাবে হ্যান্ডল করবে?

    Python-এর sorted() একটি স্থিতিশীল (stable) সর্ট — সমান প্রায়োরিটির আইটেমগুলো তাদের মূল ইনপুট-অর্ডার বজায় রাখে। যেহেতু requests-এ "Network Card" আগে আর "Printer" পরে থাকবে, দুজনেরই priority=3 হলেও "Network Card" আগে মঞ্জুর হবে। বাস্তব হার্ডওয়্যার আরবিটারেও এই ধরনের টাই-ব্রেকিং নিয়ম স্পষ্টভাবে সংজ্ঞায়িত করা থাকে, নাহলে আচরণ অনির্ধারিত (undefined) হয়ে যায়।

  2. পরীক্ষা করুন: কোড সেলে bus.transfer() দুইবার কল করুন — একবার is_write=True দিয়ে, একবার is_write=False দিয়ে — এবং প্রতিবার bus.control_bus-এর মান প্রিন্ট করুন।

    প্রথমবার control_bus হবে "WRITE", দ্বিতীয়বার "READ" — কারণ transfer() মেথড is_write প্যারামিটার অনুযায়ী সরাসরি control_bus-এর মান সেট করে। এটাই দেখায় কন্ট্রোল বাসের ভূমিকা — একই অ্যাড্রেস ও ডেটা বাস ব্যবহার করেও, কন্ট্রোল বাসের সিগন্যালই ঠিক করে দেয় অপারেশনটি রিড না রাইট।

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

পূর্ববর্তী পাঠ
I/O ইন্টারফেসিং — পোর্ট-ম্যাপড বনাম মেমরি-ম্যাপড I/O