রিপল ক্যারি অ্যাডার ও সাবট্রাক্টর
এই পাঠে যা শিখবেন
- কীভাবে N-টি ফুল অ্যাডার চেইন করে বহু-বিট সংখ্যা যোগ করা হয়
- "ripple" delay ঠিক কী, এবং কেন এটি একটি বাস্তব পারফরম্যান্স উদ্বেগ
- একই অ্যাডার হার্ডওয়্যার দিয়ে বিয়োগ করার two's-complement ট্রিক
- Python কোডে
ripple_carry_adderওadder_subtractorবানিয়ে Python-এর নিজস্ব পাটিগণিতের সাথে ফলাফল মিলিয়ে যাচাই করা
১ · N-টি ফুল অ্যাডার চেইন করে বহু-বিট যোগফল
L08-এর ফুল অ্যাডার একটি একক বিট-পজিশনের জন্য কাজ করে। বাস্তব সংখ্যা (যেমন 4-বিট বা 32-বিট) যোগ করতে হলে, প্রতিটি বিট-পজিশনের জন্য একটি করে ফুল অ্যাডার লাগবে — এবং একটির Carry-out পরেরটির Carry-in-এ সংযুক্ত হবে। একে বলে রিপল ক্যারি অ্যাডারRipple Carry AdderN-টি ফুল অ্যাডার চেইন করে বানানো একটি সার্কিট, যেখানে carry সবচেয়ে কম গুরুত্বপূর্ণ বিট (LSB) থেকে সবচেয়ে বেশি গুরুত্বপূর্ণ বিট (MSB) পর্যন্ত ধাপে ধাপে বয়ে যায়। — কারণ carry ঠিক ঢেউয়ের মতো, সবচেয়ে নিচের (least-significant) বিট থেকে সবচেয়ে উপরের (most-significant) বিট পর্যন্ত এক-এক ধাপ করে বয়ে যায়।
একটি গুরুত্বপূর্ণ, বাস্তব পারফরম্যান্স সীমাবদ্ধতা এখানেই লুকিয়ে আছে — ripple delay। সবচেয়ে উপরের (MSB) বিটের সঠিক ফলাফল পেতে হলে, carry-কে প্রথমে LSB থেকে শুরু করে প্রতিটি ফুল অ্যাডার একে একে পার হতে হয় — তাই N-বিট সংখ্যায়, সবচেয়ে খারাপ ক্ষেত্রে (worst case) মোট বিলম্ব N-টি ফুল অ্যাডারের বিলম্বের সমষ্টির সমানুপাতিক। বাস্তব হার্ডওয়্যারে দ্রুততর অ্যাডার ডিজাইন (যেমন carry-lookahead adder) বিদ্যমান, কিন্তু এই কোর্স ভিত্তি হিসেবে রিপল ক্যারি অ্যাডারেই সীমাবদ্ধ থাকবে।
২ · একই হার্ডওয়্যার দিয়ে বিয়োগ — two's complement ট্রিক
একটি সত্যিই এলিগ্যান্ট বাস্তব হার্ডওয়্যার কৌশল — বিয়োগের জন্য আলাদা কোনো সার্কিট বানানোর দরকার নেই।
গাণিতিকভাবে, A - B = A + (-B)। আর two's complement রিপ্রেজেন্টেশনে (L18-এ বিস্তারিত আসবে), -B ঠিক
(NOT B) + 1-এর সমান। তাই —
B-এর প্রতিটি বিট ইনভার্ট (NOT) করে দিলে, এবং অ্যাডারের প্রাথমিক Carry-in-কে 0-এর বদলে 1 বসিয়ে দিলে (এই "+1"-টাই সরবরাহ করে), একই রিপল ক্যারি অ্যাডার হার্ডওয়্যার A - B সঠিকভাবে বের করে দেয় — কোনো অতিরিক্ত সার্কিট ছাড়াই, শুধু একটি XOR-নিয়ন্ত্রিত bit-inverter আর একটি mode-select ওয়্যার যোগ করলেই চলে।
নিচের কোড সেলে L08-এর full_adder পুনর্ব্যবহার করে প্রথমে একটি ripple_carry_adder
বানানো হবে (4-বিট যোগফল, Python-এর নিজস্ব পূর্ণসংখ্যা যোগফলের সাথে মিলিয়ে যাচাই), তারপর একটি
adder_subtractor ফাংশন — যা subtract_mode=True হলে B-এর বিট ইনভার্ট করে ও প্রাথমিক
carry 1 বসায় — বানিয়ে একটি বিয়োগের উদাহরণ যাচাই করা হবে।
def XOR(a, b): return a ^ b
def AND(a, b): return a & b
def OR(a, b): return a | b
def half_adder(a, b):
return XOR(a, b), AND(a, b)
def full_adder(a, b, cin):
"""L08 থেকে পুনর্ব্যবহৃত -- দুটি হাফ অ্যাডার + OR"""
s1, c1 = half_adder(a, b)
s2, c2 = half_adder(s1, cin)
return s2, OR(c1, c2)
def int_to_bits(n, width):
"""পূর্ণসংখ্যা -> MSB-first বিট তালিকা"""
return [(n >> i) & 1 for i in range(width - 1, -1, -1)]
def bits_to_int(bits):
"""MSB-first বিট তালিকা -> পূর্ণসংখ্যা"""
value = 0
for b in bits:
value = value * 2 + b
return value
def ripple_carry_adder(a_bits, b_bits):
"""N-টি ফুল অ্যাডার চেইন করে -- (sum_bits, carry_out) রিটার্ন করে, সব MSB-first"""
a_lsb = a_bits[::-1]
b_lsb = b_bits[::-1]
carry = 0
sum_lsb = []
for i in range(len(a_bits)):
s, carry = full_adder(a_lsb[i], b_lsb[i], carry)
sum_lsb.append(s)
return sum_lsb[::-1], carry
# ৪-বিট যোগফল: 5 + 3
a_bits = int_to_bits(5, 4)
b_bits = int_to_bits(3, 4)
sum_bits, cout = ripple_carry_adder(a_bits, b_bits)
print("রিপল ক্যারি অ্যাডার: 5 + 3")
print(f" a_bits={a_bits} b_bits={b_bits}")
print(f" sum_bits={sum_bits} carry_out={cout}")
print(f" bits_to_int(sum_bits) = {bits_to_int(sum_bits)} | Python-এর 5+3 = {5 + 3}")
print(f" মিলেছে কি? {bits_to_int(sum_bits) == 5 + 3}")
def adder_subtractor(a_bits, b_bits, subtract_mode=False):
"""subtract_mode=True হলে B ইনভার্ট + Cin=1 বসিয়ে A-B বের করে, একই অ্যাডার হার্ডওয়্যার দিয়ে"""
b_used = [1 - bit for bit in b_bits] if subtract_mode else b_bits
a_lsb = a_bits[::-1]
b_lsb = b_used[::-1]
carry = 1 if subtract_mode else 0
sum_lsb = []
for i in range(len(a_bits)):
s, carry = full_adder(a_lsb[i], b_lsb[i], carry)
sum_lsb.append(s)
return sum_lsb[::-1], carry
# ৪-বিট বিয়োগ: 7 - 3
a2 = int_to_bits(7, 4)
b2 = int_to_bits(3, 4)
result_bits, cout2 = adder_subtractor(a2, b2, subtract_mode=True)
print()
print("adder_subtractor (একই হার্ডওয়্যার, subtract_mode=True): 7 - 3")
print(f" a_bits={a2} b_bits={b2} (B ইনভার্ট হয়ে ব্যবহৃত হয়েছে, Cin প্রাথমিকভাবে 1)")
print(f" result_bits={result_bits} carry_out={cout2} (৪-বিট রেঞ্জের বাইরে গেলে বাতিল হয়)")
print(f" bits_to_int(result_bits) = {bits_to_int(result_bits)} | Python-এর 7-3 = {7 - 3}")
print(f" মিলেছে কি? {bits_to_int(result_bits) == 7 - 3}")
carry_out=1 এসেছে, কিন্তু সেটি উপেক্ষা (discard) করা হয়েছে — ৪-বিট সীমার মধ্যে
থাকা একটি বৈধ বিয়োগের ফলাফলে এই "অতিরিক্ত" carry স্বাভাবিক এবং প্রত্যাশিত (two's complement বিয়োগে চূড়ান্ত
carry-out ফেলে দেওয়াই নিয়ম) — এই আচরণের পূর্ণাঙ্গ ব্যাখ্যা ও ওভারফ্লো-নির্ণয় L18-L19-এ বিস্তারিত আসবে।
গুরুত্বপূর্ণ বিষয় হলো, চূড়ান্ত result_bits Python-এর নিজস্ব 7 - 3-এর সাথে হুবহু
মিলে গেছে।
রিপল ক্যারি অ্যাডার দেখায় কীভাবে ছোট বিল্ডিং ব্লক (ফুল অ্যাডার) পুনরাবৃত্তি করে বহু-বিট বাস্তব যোগফল বানানো যায়, আর adder-subtractor ট্রিক দেখায় কীভাবে সামান্য অতিরিক্ত লজিক (bit-inverter + mode-select) দিয়েই একই হার্ডওয়্যার দুটি কাজ (যোগ ও বিয়োগ) করতে পারে — বাস্তব CPU-এর ALU-তে ঠিক এই নীতিই ব্যবহৃত হয়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ ৮-বিট রিপল ক্যারি অ্যাডারের তুলনায় ৩২-বিট রিপল ক্যারি অ্যাডারে ripple delay কীভাবে বদলাবে?
ripple delay একটি ফুল অ্যাডারের বিলম্ব × বিটের সংখ্যার সমানুপাতিক (worst case), তাই ৩২-বিট অ্যাডারে ৮-বিটের তুলনায় প্রায় চারগুণ বেশি বিট থাকায় সবচেয়ে খারাপ ক্ষেত্রে বিলম্বও প্রায় চারগুণ বেশি হতে পারে — কারণ carry-কে সবচেয়ে নিচের বিট থেকে শুরু করে চারগুণ বেশি ফুল অ্যাডার পার হয়ে যেতে হয়। এই কারণেই বাস্তব উচ্চ-পারফরম্যান্স CPU-তে দ্রুততর (কিন্তু বেশি জটিল) অ্যাডার ডিজাইন ব্যবহৃত হয়।
প্র ০২ বিয়োগের সময় শুধু B ইনভার্ট করলেই যথেষ্ট নয় কেন — Cin=1 বসানো কেন আবশ্যক?
শুধু B-এর প্রতিটি বিট ইনভার্ট করলে ওয়ান্স-কমপ্লিমেন্ট (~B) পাওয়া যায়, যা -B-এর সমান নয় — two's complement সংজ্ঞা অনুযায়ী -B = (~B) + 1। এই "+1"-টুকু ছাড়া ফলাফল এক কম হয়ে যাবে। রিপল ক্যারি অ্যাডারে এই "+1" আলাদা কোনো এক্সট্রা যোগফল ধাপ ছাড়াই, শুধু সবচেয়ে নিচের ফুল অ্যাডারের প্রাথমিক Cin-কে 0-এর পরিবর্তে 1 বসিয়ে বিনামূল্যে পাওয়া যায় — এটিই এই ট্রিকের আসল কৌশল।
প্র ০৩
উপরের কোডে ripple_carry_adder এবং adder_subtractor(subtract_mode=False) — এই দুটো ফাংশন কি একই কাজ করে?
হ্যাঁ, প্রায় হুবহু একই — adder_subtractor-এ subtract_mode=False দিলে
b_used = b_bits (কোনো ইনভার্শন হয় না) এবং প্রাথমিক carry = 0 হয়, যা ঠিক
ripple_carry_adder-এর আচরণের সমান। এটাই দেখায় কেন বাস্তব হার্ডওয়্যারে যোগ ও বিয়োগের জন্য
আলাদা সার্কিট না বানিয়ে একটি একক, mode-select-নিয়ন্ত্রিত ইউনিট বানানো অর্থনৈতিকভাবে সুবিধাজনক।
অনুশীলন
-
হাতে বসিয়ে দেখুন: ৪-বিট রিপল ক্যারি অ্যাডার দিয়ে 6 + 2 যোগ করার সময় প্রতিটি বিট-পজিশনের Sum ও Carry হাতে-কলমে ট্রেস করুন, তারপর মোট ফলাফল 8-এর সাথে মেলে কিনা যাচাই করুন।
6 = 0110, 2 = 0010। LSB (বিট ০): 0+0+Cin(0) = Sum 0, Carry 0। বিট ১: 1+1+0 = Sum 0, Carry 1। বিট ২: 1+0+1 = Sum 0, Carry 1। বিট ৩ (MSB): 0+0+1 = Sum 1, Carry 0। চূড়ান্ত sum_bits = 1000 = দশমিকে 8 — ঠিক 6+2=8-এর সাথে মেলে, এবং চূড়ান্ত carry_out=0 (কোনো ওভারফ্লো নেই)।
-
চিন্তা করুন: যদি
adder_subtractor-এsubtract_mode=Trueদিয়ে B হিসেবে A-এর চেয়ে বড় একটি সংখ্যা দেওয়া হয় (যেমন 3-7 চেষ্টা করা), ফলাফল বিটগুলো কী প্রকাশ করবে?ফলাফল হবে -4-এর ৪-বিট two's complement রিপ্রেজেন্টেশন (1100), শুধু ধনাত্মক পূর্ণসংখ্যা নয় — কারণ এই সার্কিট two's complement অ্যারিথমেটিকে কাজ করে, যেখানে ঋণাত্মক সংখ্যাও একই বিট-প্যাটার্নে প্রকাশ পায়। এই বিট-প্যাটার্নকে সঠিকভাবে ঋণাত্মক সংখ্যা হিসেবে ব্যাখ্যা করাটাই L18-এর মূল বিষয়বস্তু — এখানে শুধু লক্ষ্য করুন যে চূড়ান্ত carry_out=0 আসবে, যা এই নির্দিষ্ট ক্ষেত্রে ভিন্ন অর্থ বহন করে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ M4-এ (L18-L19) two's complement ও signed arithmetic বিস্তারিতভাবে ফিরে আসবে, এই পাঠের ট্রিককে পূর্ণাঙ্গ প্রেক্ষাপটে ব্যাখ্যা করে।
- আগের পাঠ L08 হাফ অ্যাডার ও ফুল অ্যাডার — এই পাঠের রিপল ক্যারি অ্যাডার ঠিক সেই ফুল অ্যাডারকেই চেইন করে বানানো।
- পরের পাঠ L10 মাল্টিপ্লেক্সার ও ডিমাল্টিপ্লেক্সার — CPU ডেটাপাথে কোন সিগন্যাল কোথায় যাবে তা নির্বাচন করার সার্কিট।