এই পর্বে যা শিখবেন
- Half adder আর full adder-এর truth table, K-map থেকে সূত্র বের করা, gate circuit আর gate গোনা
- n বিটের ripple-carry adder কীভাবে কাজ করে, আর এর delay কেন n-এর সমানুপাতে বাড়ে — gate delay গুনে
- Carry-lookahead-এর generate, propagate, c1…c4-এর দুই স্তরের সূত্র, group G-P আর ১৬ ও ৬৪ বিটের বহুস্তর নকশার delay আর খরচ
- Carry-select, carry-skip আর prefix adder-এর মূল ধারণা; আসল CPU-তে কী ধরনের adder থাকে
- BCD adder-এর সংশোধনের শর্ত প্রমাণসহ, আর adder কীভাবে ALU ও subtractor-এর ভিত্তি হয়
১ · যোগই কম্পিউটারের সবচেয়ে ব্যস্ত কাজ
আপনার ফোনের CPU প্রতি সেকেন্ডে শত কোটিবার যোগ করে। শুধু হিসাবের অ্যাপে নয় — প্রতিটি loop-এর counter বাড়াতে, array-র পরের ঘরের ঠিকানা বের করতে, পরের instruction-এর ঠিকানা (PC + 4) হিসাব করতে, এমনকি বিয়োগ আর তুলনা করতেও। তাই ALU-র ভেতরে যে circuit যোগ করে, সেই adder CPU-র সবচেয়ে বেশি ব্যবহৃত অংশগুলোর একটি। এর গতি প্রায়ই ঠিক করে দেয় পুরো CPU-র clock কত দ্রুত চলতে পারবে।
পর্ব ০৪-এ (সংখ্যা পদ্ধতি) আপনি কাগজে binary যোগ করেছেন: ডান থেকে বাঁয়ে, প্রতিটি ঘরে দুটি বিট আর আগের ঘরের carry যোগ, ঘরে একটি বিট লেখা আর বাঁয়ে carry পাঠানো। পর্ব ০৫-এ (Integer-এর বিট-রূপ) দেখেছেন, একই যোগ two's complement সংখ্যার জন্যও কাজ করে, আর MSB-র carry থেকে overflow flag V পাওয়া যায়। এই পর্বে প্রশ্নটি অন্য: এই কাগজের পদ্ধতিটি logic gate দিয়ে কীভাবে বানানো যায়, আর কীভাবে দ্রুত করা যায়?
ঈদের আগে দোকানের হিসাবের খাতা ভাবুন। লম্বা একটি যোগ করতে গিয়ে দোকানি এককের ঘর যোগ করেন, “হাতে থাকল ১” বলে দশকের ঘরে যান, তারপর শতকের ঘরে। শতকের ঘর যোগ করতে তাঁকে দশকের “হাতে থাকা” জানার জন্য অপেক্ষা করতে হয়। এটাই ripple carry — ঢেউয়ের মতো carry ডান থেকে বাঁয়ে ছড়ায়। এখন ভাবুন একজন অভিজ্ঞ ক্যাশিয়ার, যিনি সব ঘরের অঙ্কে এক নজর দিয়েই বলে দেন কোন ঘরে “হাতে থাকবে”। সেটাই carry-lookahead. দুজনের উত্তর একই, শুধু সময় আলাদা।
এই পর্বের সব circuit combinational circuit — আউটপুট শুধু এই মুহূর্তের ইনপুটের ওপর নির্ভর করে, কোনো স্মৃতি নেই। Boolean algebra-র সূত্র পর্ব ০৭-এ, gate আর gate delay পর্ব ০৮-এ, আর K-map দিয়ে সরলীকরণ পর্ব ০৯-এ বিস্তারিত আছে; এখানে যতটুকু দরকার ততটুকুই ব্যবহার করব। লেখার নিয়ম: A′ মানে NOT A (উল্টো), AB মানে A AND B, A + B মানে A OR B, আর A ⊕ B মানে XOR (ঠিক একটি ১ হলে ১). কোডে এগুলো ~, &, |, ^.
২ · Half adder — দুই বিটের যোগ
সবচেয়ে ছোট প্রশ্ন দিয়ে শুরু: দুটি বিট A আর B যোগ করলে কী হয়? ০ + ০ = ০, ০ + ১ = ১, ১ + ০ = ১, আর ১ + ১ = 10 (binary-তে দুই)। শেষ ক্ষেত্রে উত্তর দুই বিটের, তাই আউটপুটও দুটি: যোগফলের বিট S (sum) আর বাঁয়ে যাওয়া C (carry). এই circuit-এর নাম half adder. এর truth table:
| A | B | যোগফল (দশমিক) | C (carry) | S (sum) |
|---|---|---|---|---|
| 0 | 0 | ০ | 0 | 0 |
| 0 | 1 | ১ | 0 | 1 |
| 1 | 0 | ১ | 0 | 1 |
| 1 | 1 | ২ | 1 | 0 |
K-map থেকে সূত্র। দুই চলকের K-map-এ চারটি ঘর। C-এর ১ মাত্র একটি ঘরে (A = 1, B = 1), পাশে কোনো ১ নেই, তাই C = AB. S-এর ১ দুটি ঘরে — A′B আর AB′ — যারা কর্ণ বরাবর, পাশাপাশি নয়; তাই দলে বাঁধা যায় না, আর S = A′B + AB′ = A ⊕ B. অর্থাৎ sum হলো XOR, carry হলো AND.
| C = ? | B = 0 | B = 1 | S = ? | B = 0 | B = 1 |
|---|---|---|---|---|---|
| A = 0 | 0 | 0 | A = 0 | 0 | 1 |
| A = 1 | 0 | 1 | A = 1 | 1 | 0 |
নাম “half” কেন? কারণ এটি বহু-বিটের যোগের অর্ধেক কাজ করে: শুধু সবচেয়ে ডানের ঘর (LSB) সামলাতে পারে, যেখানে আগের কোনো carry নেই। মাঝের যেকোনো ঘরে তিনটি বিট আসে — ai, bi আর ডানের ঘর থেকে আসা carry. সেটা সামলাতে লাগে full adder.
৩ · Full adder — তিন বিটের যোগ আর majority
Full adder-এর ইনপুট A, B আর Cin; আউটপুট S আর Cout. তিনটি বিটের যোগফল ০ থেকে ৩, তাই দুই বিটেই ধরে: Cout হলো দুইয়ের ঘর, S হলো একের ঘর। নিচের stepper-এ truth table থেকে শুরু করে দুটি সূত্র বের করে circuit পর্যন্ত যাওয়া যাক।
row A B Cin │ total │ Cout S 0 0 0 0 │ 0 │ 0 0 1 0 0 1 │ 1 │ 0 1 2 0 1 0 │ 1 │ 0 1 3 0 1 1 │ 2 │ 1 0 4 1 0 0 │ 1 │ 0 1 5 1 0 1 │ 2 │ 1 0 6 1 1 0 │ 2 │ 1 0 7 1 1 1 │ 3 │ 1 1
S B Cin = 00 01 11 10 A = 0 0 1 0 1 A = 1 1 0 1 0 S = 1 at rows 1, 2, 4, 7 (odd number of 1s) no two 1s are adjacent -> no grouping S = A'B'Cin + A'BCin' + AB'Cin' + ABCin = A xor B xor Cin
Cout B Cin = 00 01 11 10
A = 0 0 0 [1] 0
A = 1 0 [1 1 1]
group 1: column 11, both rows -> B Cin
group 2: row A=1, columns 01,11 -> A Cin
group 3: row A=1, columns 11,10 -> A B
Cout = AB + A Cin + B Cin (majority)
Cout = AB + Cin(A + B)
A + B = (A xor B) + AB
Cout = AB + Cin(A xor B) + Cin·AB
= AB(1 + Cin) + Cin(A xor B)
= AB + Cin(A xor B)
HA1: P = A xor B G = A B HA2: S = P xor Cin T = P Cin OR : Cout = G + T gates: 2 XOR + 2 AND + 1 OR = 5 delay: Cin -> Cout = AND + OR = 2 gate delays A,B -> Cout = XOR + AND + OR = 3 A,B -> S = XOR + XOR = 2
- Truth table. তিনটি ইনপুটে ২৩ = ৮টি সারি। “total” কলাম হলো তিনটি বিটের দশমিক যোগফল; সেটিকে দুই বিটে লিখলে বাঁয়ের বিট Cout, ডানের বিট S. যেমন সারি ৭-এ ১ + ১ + ১ = ৩ = 11.
- S বিজোড় সংখ্যক ১ গোনে। S = 1 ঠিক যেখানে ইনপুটে একটি বা তিনটি ১ (সারি ১, ২, ৪, ৭)। K-map-এ এই ঘরগুলো দাবার ছকের মতো ছড়ানো — কোনো দুটি পাশাপাশি নয়, তাই সরল করা যায় না। চারটি minterm-এর এই যোগ ঠিক তিন ইনপুটের XOR.
- Cout সংখ্যাগরিষ্ঠের রায়। Cout = 1 যেখানে অন্তত দুটি ইনপুট ১ (সারি ৩, ৫, ৬, ৭)। K-map-এ তিনটি দুই-ঘরের দল, প্রতিটি একটি করে চলক বাদ দেয়। ফল AB + ACin + BCin — একে বলে তিন ইনপুটের majority function.
- সমতুল্য রূপ। A + B-কে লেখা যায় (A ⊕ B) + AB, কারণ OR-এ ১ হয় ঠিক একটি ১ হলে অথবা দুটিই ১ হলে। খুলে লিখলে Cin·AB পদটি AB-র ভেতরে শোষিত হয় (AB + AB·Cin = AB). তাই Cout = AB + Cin(A ⊕ B) — এই রূপটিই পরের অংশগুলোর generate-propagate ধারণার বীজ।
- Circuit আর খরচ। প্রথম half adder দেয় P = A ⊕ B আর G = AB; দ্বিতীয়টি P-র সাথে Cin যোগ করে S দেয়, আর T = P·Cin. G বা T যেকোনো একটি ১ হলে carry, তাই একটি OR. মোট ৫টি gate. লক্ষ করুন, Cin থেকে Cout পর্যন্ত মাত্র দুই gate delay — বহু-বিটের adder-এ এই পথটিই বারবার আসবে।
Full adder সরাসরি SOP রূপেও বানানো যায়: Cout-এর জন্য তিনটি দুই-ইনপুটের AND আর একটি তিন-ইনপুটের OR, আর S-এর জন্য চারটি তিন-ইনপুটের AND, একটি চার-ইনপুটের OR আর তিনটি NOT. সেখানে gate বেশি, কিন্তু প্রতিটি আউটপুট মাত্র দুই স্তরের (NOT বাদ দিলে)। কোন রূপ ভালো, তা নির্ভর করে কোন gate কত সস্তা আর দ্রুত তার ওপর; CMOS-এ XOR তুলনামূলক বড় gate (পর্ব ০৮), তাই chip-এর নকশায় full adder প্রায়ই আলাদা transistor-স্তরের circuit হিসেবে আঁকা হয়।
S = A ⊕ B ⊕ Cin (বিজোড় সংখ্যক ১ হলে ১) আর Cout = AB + Cin(A ⊕ B) = majority(A, B, Cin) (অন্তত দুটি ১ হলে ১)। দ্বিতীয় রূপটি একটি গল্প বলে: carry বেরোয় দুই কারণে — ঘরটি নিজেই carry তৈরি করে (AB), অথবা ডান থেকে আসা carry-কে পার করে দেয় (A ⊕ B = 1 আর Cin = 1).
৪ · Ripple-carry adder — full adder-এর শিকল
n বিটের দুটি সংখ্যা a = an−1…a0 আর b = bn−1…b0 যোগ করতে n টি full adder পাশাপাশি বসান। ঘর i-এর full adder পায় ai, bi আর ci; দেয় si আর ci+1, যা সরাসরি বাঁয়ের ঘরের Cin. সবচেয়ে ডানের ঘরের c0 সাধারণত ০ (বিয়োগে এটি ১ হয় — অংশ ৯), আর সবচেয়ে বাঁয়ের cn হলো carry-out, যা থেকে carry flag C আসে। এই নকশার নাম ripple-carry adder, কারণ carry পুকুরের ঢেউয়ের মতো ডান থেকে বাঁয়ে ছড়ায়।
এবার একটি সত্যিকারের যোগ বিট ধরে ধরে দেখুন: 1011 (১১) + 0110 (৬). উত্তর হওয়া উচিত ১৭ = 1 0001. প্রতিটি ধাপে কোন ঘর কাজ করছে আর কোন carry সবে তৈরি হলো, তা দেখুন।
bit 3 2 1 0 carry ? ? ? 0 a 1 0 1 1 b 0 1 1 0 sum ? ? ? ?
bit 3 2 1 0 carry ? ? 0 0 a 1 0 1 1 b 0 1 1 0 sum ? ? ? 1 s0 = 1, c1 = 0
bit 3 2 1 0 carry ? 1 0 0 a 1 0 1 1 b 0 1 1 0 sum ? ? 0 1 s1 = 0, c2 = 1 (generated here)
bit 3 2 1 0 carry 1 1 0 0 a 1 0 1 1 b 0 1 1 0 sum ? 0 0 1 s2 = 0, c3 = 1 (passed on)
bit 3 2 1 0 carry 1 1 0 0 c4 = 1 a 1 0 1 1 b 0 1 1 0 sum 0 0 0 1 result 1 0001 = 16 + 1 = 17
signal c1 c2 c3 c4 s0 s1 s2 s3
ready at 2 2 4 6 2 3 3 5 (gate delays)
c2 is fast: bit 1 generates it (1 AND 1)
c3, c4 must wait for the carry chain: +2 each
whole adder settles after 6 gate delays
- শুরু। a = 1011 (১১), b = 0110 (৬), আর c0 = 0. ঘরগুলোকে ডান থেকে ০, ১, ২, ৩ নম্বর দেওয়া হলো; সবচেয়ে ডানেরটি LSB. প্রশ্নচিহ্নগুলো এখনো তৈরি হয়নি।
- bit 0. FA0 পায় 1, 0 আর c0 = 0. যোগফল ১, তাই s0 = 1 আর c1 = 0 — এখান থেকে কোনো carry বাঁয়ে যায় না।
- bit 1. FA1 পায় 1, 1 আর c1 = 0. ১ + ১ = 10, তাই s1 = 0 আর c2 = 1. এই carry ঘরটি নিজেই তৈরি করেছে, কারণ a1 আর b1 দুটিই ১.
- bit 2. FA2 পায় 0, 1 আর c2 = 1. যোগফল ২ = 10, তাই s2 = 0 আর c3 = 1. এখানে ঘরটি নিজে carry তৈরি করেনি; ডান থেকে আসা carry-কে শুধু পার করে দিয়েছে, কারণ a2 ⊕ b2 = 1.
- bit 3 আর ফল। FA3 পায় 1, 0 আর c3 = 1. আবার ২, তাই s3 = 0 আর c4 = 1. পুরো ফল 1 0001 = ১৬ + ১ = ১৭, ঠিক যা আশা করেছিলাম। চার বিটের ঘরে 0001 আর carry-out ১ — unsigned হিসেবে ৪ বিটে ধরেনি বলে carry flag ১ হবে।
- কত সময় লাগল? প্রতিটি gate-কে এক একক delay ধরুন; আর নিচের simulator-এর মতো, AND-এর একটি ইনপুট ০ বা OR-এর একটি ইনপুট ১ হলেই আউটপুট স্থির (তাই c0 = 0 বলে c1 সময় ২-এ)। c2 তৈরি হয় ২ delay-তে, কারণ bit 1-এর AND সরাসরি ১ দেয়। কিন্তু c3-কে c2-এর জন্য, c4-কে c3-এর জন্য অপেক্ষা করতে হয় — প্রতি ঘরে আরও ২ delay. তাই পুরো adder স্থির হয় ৬ delay পরে। একটু পরে দেখবেন, সবচেয়ে খারাপ ইনপুটে এটি ৯.
Ripple কেন ধীর — gate delay গুনে দেখা
প্রতিটি gate-এর ইনপুট বদলানোর পর আউটপুট বদলাতে কিছু সময় লাগে; একে বলে propagation delay. সরল হিসাবের জন্য আমরা ধরব প্রতিটি gate (AND, OR, XOR) = ১ gate delay, আর a, b, c0 সবই সময় ০-তে হাজির। আসল chip-এ XOR সাধারণত AND-এর চেয়ে ধীর আর delay নির্ভর করে তারের দৈর্ঘ্য ও load-এর ওপর (পর্ব ০৮ আর পর্ব ১৮); কিন্তু তুলনার জন্য এই সরল মডেলই পাঠ্যবই আর পরীক্ষায় চলে।
সবচেয়ে খারাপ ক্ষেত্রের প্রমাণ। ঘর i-তে pi = ai ⊕ bi তৈরি হয় সময় ১-এ। c1 = g0 + p0c0: AND সময় ২-এ, OR সময় ৩-এ। এরপর প্রতিটি ঘরে ci+1 = gi + pici-এর জন্য ci আসার পর একটি AND আর একটি OR, অর্থাৎ ২ delay. তাই আরোহ (induction) দিয়ে ci তৈরি হয় সময় 2i + 1-এ, আর:
অর্থাৎ delay n-এর সাথে রৈখিকভাবে বাড়ে — O(n). এই দীর্ঘতম পথটিকে বলে adder-এর critical path: সব ঘরে pi = 1 হলে c0-এর একটি পরিবর্তন সব ঘর পেরিয়ে cn পর্যন্ত যায় (যেমন 1111 + 0000, c0 = 1). ৪ বিটে ৯, ১৬ বিটে ৩৩, ৩২ বিটে ৬৫, ৬৪ বিটে ১২৯ gate delay.
সংখ্যাটা কতটা বড়? ধরুন একটি gate delay ২০ picosecond (কেবল বোঝানোর জন্য ধরা একটি মান; আসল মান process আর নকশার ওপর অনেক নির্ভর করে)। তাহলে ৬৪ বিট ripple adder-এ ১২৯ × ২০ = ২,৫৮০ ps ≈ ২.৬ ns লাগে, অর্থাৎ শুধু এই যোগের জন্যই clock প্রায় ৩৯০ MHz-এর বেশি হতে পারবে না। অথচ আজকের ডেস্কটপ CPU ৩–৫ GHz-এ চলে, যেখানে একটি cycle মাত্র ২০০–৩৩৩ ps, আর সেই সময়ের মধ্যে register পড়া, mux পেরোনো, ফল লেখা — সবই করতে হয়। তাই ৩২ আর ৬৪ বিটের CPU-তে খাঁটি ripple adder চলে না।
তবে delay ইনপুটের ওপরও নির্ভর করে। নিচের simulator একটু বাস্তবসম্মত মডেল ব্যবহার করে: AND-এর কোনো ইনপুট ০ হয়ে গেলে আউটপুট তখনই ০ নিশ্চিত, আর OR-এর কোনো ইনপুট ১ হয়ে গেলে আউটপুট তখনই ১ — বাকি ইনপুটের জন্য অপেক্ষা লাগে না। প্রতিটি সংকেতের সাথে “কখন স্থির হলো” সেই সময় রাখা হয়:
// Ripple-carry adder, bit by bit, with a simple timing model:
// every gate = 1 gate delay; inputs a, b, c0 are ready at t = 0;
// AND settles early on a 0 input, OR settles early on a 1 input.
const AND = (x, y) => (x.v & y.v) ? { v: 1, t: 1 + Math.max(x.t, y.t) }
: { v: 0, t: 1 + Math.min(...[x, y].filter(z => !z.v).map(z => z.t)) };
const OR = (x, y) => (x.v | y.v)
? { v: 1, t: 1 + Math.min(...[x, y].filter(z => z.v).map(z => z.t)) }
: { v: 0, t: 1 + Math.max(x.t, y.t) };
const XOR = (x, y) => ({ v: x.v ^ y.v, t: 1 + Math.max(x.t, y.t) });
function ripple(aStr, bStr, show) {
const n = aStr.length;
let c = { v: 0, t: 0 }, sum = "", worst = 0;
if (show) console.log(" i a b c_i | s c_i+1 t(s) t(c_i+1)");
for (let i = 0; i < n; i++) {
const a = { v: +aStr[n - 1 - i], t: 0 }, b = { v: +bStr[n - 1 - i], t: 0 };
const g = AND(a, b), p = XOR(a, b);
const s = XOR(p, c), co = OR(g, AND(p, c));
if (show) console.log(`${String(i).padStart(2)} ${a.v} ${b.v} ${c.v} | ${s.v} ${co.v}` +
` ${String(s.t).padStart(2)} ${String(co.t).padStart(2)}`);
worst = Math.max(worst, s.t, co.t); sum = s.v + sum; c = co;
}
console.log(`${aStr} + ${bStr} = ${c.v} ${sum} -> settles after ${worst}` +
` gate delays (n = ${n}, static worst case 2n+1 = ${2 * n + 1})`);
}
ripple("1111", "0001", true);
ripple("0101", "0010", true);
ripple("0110", "0110", true);
ripple("11111111", "00000001", false);
ripple("01100110", "01100110", false);আউটপুট:
i a b c_i | s c_i+1 t(s) t(c_i+1)
0 1 1 0 | 0 1 2 2
1 1 0 1 | 0 1 3 4
2 1 0 1 | 0 1 5 6
3 1 0 1 | 0 1 7 8
1111 + 0001 = 1 0000 -> settles after 8 gate delays (n = 4, static worst case 2n+1 = 9)
i a b c_i | s c_i+1 t(s) t(c_i+1)
0 1 0 0 | 1 0 2 2
1 0 1 0 | 1 0 3 4
2 1 0 0 | 1 0 5 6
3 0 0 0 | 0 0 7 3
0101 + 0010 = 0 0111 -> settles after 7 gate delays (n = 4, static worst case 2n+1 = 9)
i a b c_i | s c_i+1 t(s) t(c_i+1)
0 0 0 0 | 0 0 2 2
1 1 1 0 | 0 1 3 2
2 1 1 1 | 1 1 3 2
3 0 0 1 | 1 0 3 3
0110 + 0110 = 0 1100 -> settles after 3 gate delays (n = 4, static worst case 2n+1 = 9)
11111111 + 00000001 = 1 00000000 -> settles after 16 gate delays (n = 8, static worst case 2n+1 = 17)
01100110 + 01100110 = 0 11001100 -> settles after 4 gate delays (n = 8, static worst case 2n+1 = 17)
প্রথম ক্ষেত্রে (1111 + 0001) bit 0 carry তৈরি করে সময় ২-এ, তারপর প্রতিটি ঘরে +২: ৪, ৬, ৮. ৮ বিটে একই নকশা (11111111 + 00000001) নেয় ১৬ delay — n দ্বিগুণ, সময়ও দ্বিগুণ। দ্বিতীয় ক্ষেত্রটি সবচেয়ে শিক্ষণীয়: 0101 + 0010-এ কোথাও carry ১ হয়ই না, তবু ৭ delay লাগে। কারণ bit 0–2-এ p = 1, আর “carry ০” — এই খবরটিও শিকল বেয়ে আসতে হয়; s3-কে c3 নিশ্চিত হওয়ার জন্য অপেক্ষা করতে হয়। তৃতীয় ক্ষেত্রে (0110 + 0110) প্রতিটি ঘর হয় নিজেই carry তৈরি করে (1 + 1), নয়তো carry মেরে ফেলে (0 + 0), তাই শিকল ভেঙে যায় আর ৩ delay-তেই কাজ শেষ। অর্থাৎ delay ঠিক করে পরপর কতগুলো ঘরে p = 1, carry ১ কি না তা নয়। কিন্তু synchronous CPU-র clock সবচেয়ে খারাপ ক্ষেত্র ধরেই ঠিক করতে হয় (এখানে 2n + 1).
৫ · Carry-lookahead — generate আর propagate
Ripple-এর সমস্যা হলো ci+1 জানতে ci-এর জন্য অপেক্ষা। কিন্তু ci নিজেও তো a, b আর c0-এরই একটি Boolean function! তাহলে সব carry সরাসরি মূল ইনপুট থেকে হিসাব করা যায় না কেন? এই ধারণার নাম carry-lookahead adder (CLA). এর জন্য প্রতিটি ঘরে দুটি সংকেত সংজ্ঞায়িত করুন:
Generate: gi = aibi
দুটি বিটই ১ হলে ঘরটি নিজেই carry তৈরি করে, ডান থেকে কী এলো তাতে কিছু যায় আসে না।
1 + 1 → carry নিশ্চিতPropagate: pi = ai ⊕ bi
ঠিক একটি বিট ১ হলে ঘরটি ডান থেকে আসা carry-কে পার করে দেয়: ci = 1 হলে ci+1 = 1.
1 + 0 → carry যেমন এলো তেমন যায়Kill: ai′bi′
দুটিই ০ হলে ডান থেকে carry এলেও এখানে থেমে যায়; ci+1 = 0.
0 + 0 → carry থামেঅংশ ৩-এর সূত্র Cout = AB + Cin(A ⊕ B) এখন হুবহু লেখা যায় ci+1 = gi + pici, আর sum হয় si = pi ⊕ ci. এবার এই recurrence-কে বারবার নিজের ভেতরে বসিয়ে খুলে ফেলুন:
c2 = g1 + p1g0 + p1p0c0
c3 = g2 + p2g1 + p2p1g0 + p2p1p0c0
c4 = g3 + p3g2 + p3p2g1 + p3p2p1g0 + p3p2p1p0c0
প্রতিটি পদের একটি সহজ অর্থ আছে। যেমন c4-এর p3p2g1 পদ বলে: “bit 1 carry তৈরি করেছে, আর bit 2 ও bit 3 সেটি পার করে দিয়েছে।” অর্থাৎ ci+1 = 1 ঠিক তখনই, যখন i-এর নিচের কোনো ঘর j carry তৈরি করেছে (বা c0 = 1) আর j-এর ওপরের i পর্যন্ত সব ঘর propagate করে। এটাই সূত্রের প্রমাণও — carry একমাত্র এভাবেই ঘর i পর্যন্ত পৌঁছাতে পারে, আর সূত্রটি সব সম্ভাব্য শুরুর ঘর j = −1 (c0), 0, …, i-এর OR.
p = a ⊕ b, নাকি a + b? বইভেদে দুটিই দেখবেন। Carry-র জন্য দুটিই ঠিক: পার্থক্য শুধু ai = bi = 1 ঘরে, যেখানে OR-রূপে pi = 1 কিন্তু XOR-রূপে ০. সেই ঘরে gi = 1, তাই ci+1 = gi + pici = 1 দুই রূপেই — বাড়তি pici পদটি কিছু বদলায় না। (৪ বিটের a, b আর c0-এর সব ৫১২টি সমন্বয়ে একটি স্ক্রিপ্ট দিয়েও আমরা মিলিয়ে দেখেছি।) OR gate XOR-এর চেয়ে ছোট আর দ্রুত, তাই lookahead logic-এ অনেক সময় OR-রূপ নেওয়া হয়। কিন্তু sum-এর জন্য XOR-রূপ লাগে: si = pi ⊕ ci কেবল pi = ai ⊕ bi হলেই ঠিক। আর XOR-রূপে g আর p কখনো একসাথে ১ হয় না, যা carry-skip-এর মতো নকশায় কাজে লাগে।
এবার অংশ ৪-এর একই যোগ 1011 + 0110 নিন, কিন্তু এবার c4 সরাসরি সূত্র দিয়ে — কোনো ঘরের অপেক্ষা ছাড়া:
bit 3 2 1 0 a 1 0 1 1 b 0 1 1 0 g = a AND b 0 0 1 0 g3 g2 g1 g0 p = a XOR b 1 1 0 1 p3 p2 p1 p0 c0 = 0
c1 = g0 + p0 c0 = 0 + 1·0 = 0
c2 = g1 + p1 g0 + p1 p0 c0
= 1 + 0·0 + 0·1·0 = 1 (g1 = 1 is enough)
c3 = g2 + p2 g1 + p2 p1 g0 + p2 p1 p0 c0 = 0 + 1·1 + 1·0·0 + 1·0·1·0 = 1 (bit 1 generates, bit 2 propagates)
c4 = g3 + p3 g2 + p3 p2 g1 + p3 p2 p1 g0 + p3 p2 p1 p0 c0 = 0 + 1·0 + 1·1·1 + 1·1·0·0 + 1·1·0·1·0 = 1 (bit 1 generates, bits 2 and 3 propagate)
carries c4 c3 c2 c1 c0 = 1 1 1 0 0 (same as ripple)
sum s = p xor c = 1101 xor 1100 = 0001 -> 1 0001 = 17
time: g, p at 1 -> all AND terms at 2 -> all ORs at 3
every carry ready at 3, every sum at 4
ripple needed 6 here, 9 in the worst case
- g আর p একসাথে। চারটি ঘরের g আর p একই সময়ে, এক gate delay-তে তৈরি হয় — কেউ কারও জন্য অপেক্ষা করে না। এখানে শুধু bit 1 carry তৈরি করে (g1 = 1), আর bit 0, 2, 3 propagate করে।
- c1 আর c2. c1-এ g0 = 0 আর c0 = 0, তাই c1 = 0. c2-তে প্রথম পদ g1 = 1 — OR-এর একটি পদ ১ হলেই পুরোটা ১, বাকি পদ দেখার দরকার নেই।
- c3. দ্বিতীয় পদ p2g1 = 1·1 = 1: bit 1-এর তৈরি carry bit 2 পার করে দিয়েছে। Ripple-এ এটাই ছিল FA2-এর কাজ, কিন্তু এখানে c2-এর জন্য অপেক্ষা নেই — সূত্রে সরাসরি g1 আর p2 ব্যবহার হয়েছে।
- c4. পাঁচটি পদের মধ্যে তৃতীয়টি p3p2g1 = 1·1·1 = 1. অর্থাৎ bit 1-এর carry দুটি ঘর পেরিয়ে বেরিয়ে গেছে। বাকি চারটি পদ ০, কিন্তু একটিই যথেষ্ট, তাই c4 = 1.
- একই উত্তর, কম সময়। সব carry ripple-এর সাথে মেলে, আর s = p ⊕ c দিয়ে ফল 1 0001 = ১৭. কিন্তু সময় আলাদা: g, p সময় ১-এ, সব AND পদ সময় ২-এ, সব OR সময় ৩-এ — প্রতিটি carry ৩ delay-তে, প্রতিটি sum ৪ delay-তে। ইনপুট যা-ই হোক, এটি বদলায় না।
৪ বিটের CLA ব্লক — delay আর দাম
৪ বিটের CLA ব্লকে তিনটি অংশ: (১) চারটি AND আর চারটি XOR দিয়ে g আর p (১ delay); (২) উপরের চারটি সূত্রের AND-OR লজিক (আরও ২ delay); (৩) চারটি XOR দিয়ে sum (আরও ১ delay). মোট ৪ gate delay, ripple-এর ৯-এর জায়গায়। Carry লজিকে AND লাগে ১ + ২ + ৩ + ৪ = ১০টি আর OR লাগে ৪টি; সব মিলিয়ে ৪ + ৪ + ১৪ + ৪ = ২৬টি gate, ripple-এর ২০টির চেয়ে একটু বেশি।
তাহলে ৬৪ বিটে কেন একবারে খুলে লিখি না? দুটি বাধা। প্রথম, gate-এর fan-in: c64-এর শেষ পদে ৬৫টি ইনপুটের AND, আর পুরো সূত্রে ৬৫ ইনপুটের OR লাগবে। CMOS-এ ৪–৫টির বেশি ইনপুটের gate ধীর হয়ে যায় (series transistor বাড়ে — পর্ব ০৮), তাই “দুই স্তর” আসলে আর দুই স্তর থাকে না। দ্বিতীয়, আকার: n বিট একবারে খুললে AND gate লাগে 1 + 2 + … + n = n(n + 1)/2, অর্থাৎ ৬৪ বিটে ২,০৮০টি — O(n2), আর প্রতিটি pi-কে অসংখ্য gate-এ তার টেনে নিতে হয়। সমাধান: ছোট ব্লক, আর ব্লকের ওপরে আবার lookahead.
৬ · বড় adder-এ lookahead — group G, P আর বহুস্তর CLA
৪ বিটের একটি ব্লককে বাইরে থেকে একটি “বড় ঘর” হিসেবে দেখুন। এটিও দুই কাজের একটি করতে পারে: নিজেই carry তৈরি করা, বা ঢোকা carry পার করে দেওয়া। তাই এর জন্যও group generate আর group propagate সংজ্ঞায়িত করা যায়:
অর্থাৎ ব্লকের carry-out = G + P·cin — একটি বিটের সূত্রের হুবহু একই আকার! P বলে “চারটি ঘরই পার করে” আর G বলে “ব্লকের ভেতরে কোথাও carry তৈরি হয়ে বাইরে পর্যন্ত গেছে।” তাই চারটি ব্লকের (G0, P0) … (G3, P3) নিয়ে একটি দ্বিতীয় স্তরের lookahead carry unit (LCU) ঠিক আগের সূত্রগুলো দিয়ে c4, c8, c12, c16 বের করতে পারে:
Delay-এর হিসাব (একই মডেল: প্রতি gate ১, fan-in ৫ পর্যন্ত চলে, NOT ধরা হয়নি)। ১৬ বিটে: g, p সময় ১-এ; ব্লকের P সময় ২-এ (একটি AND), G সময় ৩-এ (AND-OR); LCU-র c4, c8, c12, c16 সময় ৫-এ (আরও AND-OR); প্রতিটি ব্লক তার ভেতরের carry বের করে সময় ৭-এ; sum সময় ৮-এ। ৬৪ বিটে আরেকটি স্তর: ১৬টি ব্লক → ৪টি ১৬ বিটের দল → একটি শীর্ষ unit. প্রতিটি স্তর ওঠার সময় +২ আর নামার সময় +২ — L স্তরে মোট 4L gate delay, যেখানে L = ⌈log4 n⌉. তাই delay বাড়ে log n হারে: ৪ বিটে ৪, ১৬ বিটে ৮, ৬৪ বিটে ১২. (৩২ বিটেও এই মডেলে তিন স্তর লাগে, তাই ১২.)
আরেকটি সস্তা মাঝামাঝি পথ: ৪ বিটের CLA ব্লকগুলোকে ripple-এর মতো শিকলে জোড়া, অর্থাৎ প্রতিটি ব্লকের cout = G + P·cin পরের ব্লকে যায়। প্রথম ব্লকের c4 সময় ৩-এ, তারপর প্রতি ব্লকে +২, শেষ ব্লকের sum আরও ৩ পরে: মোট n/2 + 2. নিচের calculator প্রথমে ৪ বিট আর ১৬ বিট ইনপুটে প্রতিটি carry সরাসরি খোলা সূত্র দিয়ে বের করে ripple-এর সাথে মেলায়, তারপর চার রকম নকশার delay পাশাপাশি দেখায় (carry-select পরের অংশে):
// Carry-lookahead: g, p and every carry from the flat two-level equation
// c(i+1) = g_i + p_i g_(i-1) + ... + p_i...p_0 c0, checked against ripple.
const str = (arr) => [...arr].reverse().join(""); // MSB on the left
function cla(a, b, n, c0 = 0) {
const g = [], p = [], c = [c0];
for (let i = 0; i < n; i++) { g[i] = (a >> i) & (b >> i) & 1; p[i] = ((a ^ b) >> i) & 1; }
for (let i = 0; i < n; i++) {
let ci = 0; // OR of product terms
for (let j = -1; j <= i; j++) {
let term = j < 0 ? c0 : g[j];
for (let k = j + 1; k <= i; k++) term &= p[k];
ci |= term;
}
c[i + 1] = ci;
}
let rc = c0, ok = true; // ripple check
for (let i = 0; i <= n; i++) { ok = ok && rc === c[i]; if (i < n) rc = g[i] | (p[i] & rc); }
const s = p.map((x, i) => x ^ c[i]);
console.log(`n=${n} g = ${str(g)} p = ${str(p)}`);
console.log(` carries c${n}..c0 = ${str(c)} sum = ${c[n]} ${str(s)} ripple agrees: ${ok}`);
if (n === 16) for (let k = 0; k < 4; k++) { // group G, P per 4-bit block
const [g0, g1, g2, g3] = g.slice(4 * k), [p0, p1, p2, p3] = p.slice(4 * k);
const G = g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & g0), P = p3 & p2 & p1 & p0;
console.log(` block ${k}: G=${G} P=${P} c${4 * k + 4} = G + P*c${4 * k} = ${G | (P & c[4 * k])}`);
}
}
cla(0b1011, 0b0110, 4);
cla(0xBEEF, 0x4111, 16);
// Delay in gate delays (every gate 1, any fan-in up to 5, NOT ignored)
console.log("\n n ripple 4-bit CLA rippled multi-level CLA carry-select(4-bit)");
for (const n of [4, 16, 32, 64]) {
const L = Math.ceil(Math.log(n) / Math.log(4) - 1e-9); // lookahead levels
const row = [2 * n + 1, n / 2 + 2, 4 * L, 9 + 2 * (n / 4 - 1)];
console.log(String(n).padStart(4) + row.map((d, i) => String(d).padStart([8, 19, 17, 21][i])).join(""));
}আউটপুট:
n=4 g = 0010 p = 1101
carries c4..c0 = 11100 sum = 1 0001 ripple agrees: true
n=16 g = 0000000000000001 p = 1111111111111110
carries c16..c0 = 11111111111111110 sum = 1 0000000000000000 ripple agrees: true
block 0: G=1 P=0 c4 = G + P*c0 = 1
block 1: G=0 P=1 c8 = G + P*c4 = 1
block 2: G=0 P=1 c12 = G + P*c8 = 1
block 3: G=0 P=1 c16 = G + P*c12 = 1
n ripple 4-bit CLA rippled multi-level CLA carry-select(4-bit)
4 9 4 4 9
16 33 10 8 15
32 65 18 12 23
64 129 34 12 39
৪ বিটের ফল stepper-এর সাথে হুবহু মেলে: g = 0010, p = 1101, carry 11100, যোগফল 1 0001. ১৬ বিটে 0xBEEF + 0x4111 = 0x10000 ইচ্ছা করে বাছা হয়েছে — শুধু bit 0 carry তৈরি করে আর বাকি ১৫টি ঘর propagate করে, অর্থাৎ ripple-এর সবচেয়ে খারাপ ধরনের শিকল। তবু lookahead সূত্র সরাসরি সব carry ১ পেয়েছে। ব্লকের লাইনগুলো দেখায় ব্লক ০ নিজেই carry তৈরি করে (G = 1), আর ব্লক ১–৩ শুধু পার করে (P = 1). Delay-এর ছকে মূল শিক্ষা: ৬৪ বিটে ripple ১২৯, বহুস্তর CLA মাত্র ১২ — দশ গুণের বেশি দ্রুত। আর ৩২ থেকে ৬৪ বিটে গেলে বহুস্তর CLA-র delay একটুও বাড়েনি, কারণ দুটিতেই তিন স্তর লাগে; ripple-এর delay সেখানে দ্বিগুণ।
খরচ কত? প্রতিটি ৪ বিটের ব্লক বা unit-এ মোটামুটি নির্দিষ্ট সংখ্যক gate, আর ব্লক থাকে n/4টি, তাদের ওপরে n/16টি unit, তার ওপরে n/64… — যোগফল প্রায় n/3টি unit. তাই বহুস্তর CLA-র আকার মোটামুটি n-এর সমানুপাতিক থাকে (ripple-এর কয়েক গুণ), কিন্তু delay হয় O(log n). একবারে খোলা সূত্রের O(n2) আকারের তুলনায় এটি অনেক বাস্তবসম্মত। দাম অবশ্য শুধু gate-এ নয়: লম্বা তার, বেশি fan-out আর বেশি switching মানে বেশি power.
৭ · আরও দ্রুত adder — carry-select, carry-skip আর prefix adder
Lookahead একমাত্র কৌশল নয়। প্রকৌশলীরা বহু দশক ধরে carry-র অপেক্ষা কমানোর নানা উপায় বের করেছেন। তিনটি গুরুত্বপূর্ণ ধারণা:
Carry-select — দুই উত্তর আগেই তৈরি রাখা
পরীক্ষার ফল বের হওয়ার আগে একজন ছাত্র দুটি পরিকল্পনা তৈরি রাখেন — পাস করলে এক পথ, না করলে আরেক পথ; ফল আসামাত্র একটি বেছে নেন। Carry-select adder একই কাজ করে। ১৬ বিটকে চারটি ৪ বিটের ব্লকে ভাগ করুন। ব্লক ০ সাধারণ ripple. বাকি প্রতিটি ব্লকে দুটি ৪ বিটের ripple adder — একটি ধরে নেয় carry-in = 0, অন্যটি carry-in = 1 — দুটিই একই সময়ে কাজ শুরু করে। আসল carry-in এলে একটি multiplexer (পর্ব ১২) ঠিক উত্তরটি বেছে নেয়।
Delay-এর হিসাব: সব ৪ বিটের ripple ব্লক একসাথে শেষ হয় সময় ২ × ৪ + ১ = ৯-এ। ব্লক ০-এর c4 তখনই হাজির। ব্লক k-এর carry-out বাছাই করতে cout = cout(0) + cout(1)·cin — একটি AND আর একটি OR, অর্থাৎ ২ delay (এখানে inverter-ও লাগে না, কারণ cout(1) কখনো cout(0)-এর চেয়ে ছোট নয়)। তাই c8 সময় ১১-এ, c12 ১৩-এ, c16 আর শেষ ব্লকের বাছাই করা sum ১৫-এ। সাধারণভাবে 9 + 2(n/4 − 1): ১৬ বিটে ১৫, ৬৪ বিটে ৩৯ — ripple-এর ৩৩ আর ১২৯-এর চেয়ে অনেক কম, যদিও প্রায় দ্বিগুণ adder লাগে। ব্লকগুলোর আকার সমান না রেখে ধীরে ধীরে বড় করলে (প্রায় √n আকারের ব্লক) delay আরও কমে, O(√n).
Carry-skip বা carry-bypass — লাফিয়ে পার হওয়া
Carry-skip adder-এ প্রতিটি ripple ব্লকের পাশে একটি “বাইপাস সড়ক”। ব্লকের group P = 1 হলে (সব ঘর propagate করে) ঢোকা carry সরাসরি বেরিয়ে যায়, ব্লকের ভেতর দিয়ে হাঁটতে হয় না: cout = (ripple থেকে আসা carry) + P·cin. যুক্তিটি সুন্দর: P = 0 হলে ব্লকের অন্তত একটি ঘর propagate করে না, তাই ব্লকের carry-out ঢোকা carry-র ওপর নির্ভরই করে না — সেটি ব্লকের ভেতরেই তৈরি হয় বা ০ হয়। ফলে বাইরের carry-কে কখনো পুরো ব্লকের ভেতর দিয়ে হাঁটতে হয় না। Hardware সামান্য বাড়ে (প্রতি ব্লকে একটি AND আর একটি OR), কিন্তু সবচেয়ে খারাপ delay অনেক কমে। এর delay ইনপুটভেদে বদলায় বলে এর সঠিক timing বিশ্লেষণ একটু কঠিন।
Prefix adder — Kogge–Stone আর Brent–Kung
একটি বিমূর্ত পর্যবেক্ষণ সব দ্রুত adder-কে একসূত্রে বাঁধে। (G, P) জোড়ার ওপর একটি অপারেটর ভাবুন: (Ghi, Phi) ∘ (Glo, Plo) = (Ghi + PhiGlo, PhiPlo). এটি ঠিক অংশ ৬-এর “দুটি পাশাপাশি দলকে একটি বড় দল বানানো”, আর এটি associative (বন্ধনী যেভাবেই বসান, ফল একই)। তাই সব carry বের করা মানে একটি prefix computation — অনেকটা prefix sum-এর মতো (যা DSA কোর্সের prefix sums পর্বে আছে)। Associative অপারেটরের prefix গাছের আকারে log2 n স্তরে হিসাব করা যায়। এভাবে বানানো adder-কে বলে prefix adder.
| নকশা | স্তর (∘ cell-এর গভীরতা) | ∘ cell-এর সংখ্যা | বৈশিষ্ট্য |
|---|---|---|---|
| Kogge–Stone (১৯৭৩) | log2 n (৬৪ বিটে ৬) | n log2 n − n + 1 (৬৪ বিটে ৩২১) | সবচেয়ে কম স্তর, প্রতিটি cell-এর fan-out কম; কিন্তু অনেক cell আর অনেক লম্বা তার |
| Brent–Kung (১৯৮২) | 2 log2 n − 1 (৬৪ বিটে ১১) | 2n − 2 − log2 n (৬৪ বিটে ১২০) | প্রায় দ্বিগুণ স্তর, কিন্তু cell আর তার অনেক কম — ছোট আর কম power |
এদের মাঝামাঝি আরও নকশা আছে (Sklansky, Han–Carlson, Ladner–Fischer ইত্যাদি), প্রতিটি স্তর, cell, fan-out আর তারের মধ্যে আলাদা আপস করে। আমাদের ৪ বিটের বহুস্তর CLA আসলে prefix-এরই একটি রূপ, যেখানে প্রতি স্তরে চারটি দল একসাথে মেলানো হয়।
আরও গভীরে: আধুনিক উচ্চগতির ৬৪ বিট ALU-তে সাধারণত prefix-ধাঁচের adder থাকে বলে প্রকাশিত circuit-গবেষণা থেকে জানা যায়, প্রায়ই Ling-এর কৌশল (carry-র বদলে একটু সরল “pseudo-carry” হিসাব), sparse tree (প্রতি ঘরে নয়, প্রতি কয়েক ঘরে carry বের করে বাকিটা carry-select দিয়ে) আর বিশেষ transistor-স্তরের circuit মিশিয়ে। কোন CPU-তে ঠিক কোন নকশা, তা কোম্পানিগুলো সাধারণত পুরোপুরি প্রকাশ করে না। একটি পরিচিত উদাহরণ Intel Pentium 4: এর সরল integer ALU core clock-এর দ্বিগুণ হারে চলত বলে Intel জানিয়েছিল — adder-এর গতিকে কেন্দ্র করে বানানো নকশা। আর FPGA-তে উল্টো ছবি: সেখানে বিশেষ, খুব দ্রুত carry-chain hardware থাকে বলে ছোট-মাঝারি width-এ ripple-ই প্রায়ই সেরা।
৮ · BCD adder — দশমিক অঙ্কের যোগ আর +6 সংশোধন
পর্ব ০৪-এ আপনি BCD-তে হাতে যোগ করেছেন: প্রতিটি দশমিক অঙ্ক ৪ বিটে, আর যোগফল ৯-এর বেশি হলে 0110 (৬) যোগ। এবার এর hardware: BCD adder. এক অঙ্কের BCD adder-এ তিনটি অংশ — একটি সাধারণ ৪ বিটের binary adder (আমাদের ripple বা CLA), একটি সংশোধন-শর্ত বের করার ছোট circuit, আর শর্ত ১ হলে 0110 যোগ করার জন্য দ্বিতীয় একটি ৪ বিটের adder.
শর্তের প্রমাণ। দুটি BCD অঙ্ক আর carry-in মিলে যোগফল সর্বোচ্চ ৯ + ৯ + ১ = ১৯. প্রথম adder-এর আউটপুট carry K আর ৪ বিট Z3Z2Z1Z0. সংশোধন লাগে যখন যোগফল ≥ ১০, দুই ক্ষেত্রে:
- যোগফল ১৬–১৯: binary adder নিজেই carry দেয়, K = 1. (Z তখন 0000–0011, যা দেখতে বৈধ BCD, কিন্তু ১৬ বাদ গেছে — তাই K দেখা জরুরি।)
- যোগফল ১০–১৫: K = 0, আর Z = 1010 থেকে 1111. এই ছয়টি প্যাটার্নের K-map-এ Z3 = 1 দুই সারিতে: Z3Z2 = 11 (১২–১৫) পুরো সারিটি একটি দল, আর Z3Z1 = 1-এর ঘরগুলো (১০, ১১, ১৪, ১৫) আরেকটি দল। ১০–১৫ ছাড়া কোনো ৪ বিট প্যাটার্নে এই দুই দলের কোনোটি ১ হয় না, কারণ Z3 = 1 মানে ≥ ৮, আর ৮ (1000) ও ৯ (1001)-এ Z2 = Z1 = 0.
C = 1 হলে Z-এর সাথে 0110 যোগ হয়, আর C নিজেই পরের (দশকের) অঙ্কের carry হয়ে যায়। কেন ঠিক ৬? কারণ ৪ বিট ১৬-তে ঘুরে আসে কিন্তু দশমিক অঙ্ক ১০-এ; ৬ যোগ করলে মাঝের ছয়টি অবৈধ প্যাটার্ন (1010–1111) লাফিয়ে পার হয়, আর ১০ বেশি মানে carry-তে চলে যায়: (Z + 6) mod 16 = Z − 10. নিচের simulator কয়েকটি অঙ্ক-জোড়া, একটি দুই অঙ্কের যোগ আর সব ২০০টি সম্ভাব্য ক্ষেত্র যাচাই করে:
// One-digit BCD adder: 4-bit binary add, then add 0110 when
// C = K + Z3*Z2 + Z3*Z1 (K = binary carry-out, Z3..Z0 = binary sum).
const b4 = (v) => v.toString(2).padStart(4, "0");
function bcdDigit(x, y, cin) {
const t = x + y + cin, K = t >> 4, Z = t & 15;
const z3 = (Z >> 3) & 1, z2 = (Z >> 2) & 1, z1 = (Z >> 1) & 1;
const C = K | (z3 & z2) | (z3 & z1); // correction needed?
const D = C ? (Z + 6) & 15 : Z; // corrected digit
return { K, Z, C, D };
}
console.log("x + y + cin | K Z3..Z0 | C | BCD out | decimal");
for (const [x, y, cin] of [[5, 3, 0], [4, 6, 0], [8, 7, 0], [9, 9, 0], [9, 9, 1]]) {
const { K, Z, C, D } = bcdDigit(x, y, cin);
console.log(`${x} + ${y} + ${cin} | ${K} ${b4(Z)} | ${C} | ${C} ${b4(D)} | ` +
`${C * 10 + D}${C ? " (+0110 added)" : ""}`);
}
// Two-digit BCD: 58 + 67, units digit first, carry goes to the tens digit
const u = bcdDigit(8, 7, 0), t = bcdDigit(5, 6, u.C);
console.log(`\n58 + 67 -> ${t.C} ${b4(t.D)} ${b4(u.D)} = ${t.C}${t.D}${u.D}`);
let bad = 0; // check all 200 cases
for (let x = 0; x <= 9; x++) for (let y = 0; y <= 9; y++) for (let c = 0; c <= 1; c++) {
const r = bcdDigit(x, y, c);
if (r.C * 10 + r.D !== x + y + c) bad++;
}
console.log(`all 200 digit cases checked, mismatches = ${bad}`);আউটপুট:
x + y + cin | K Z3..Z0 | C | BCD out | decimal
5 + 3 + 0 | 0 1000 | 0 | 0 1000 | 8
4 + 6 + 0 | 0 1010 | 1 | 1 0000 | 10 (+0110 added)
8 + 7 + 0 | 0 1111 | 1 | 1 0101 | 15 (+0110 added)
9 + 9 + 0 | 1 0010 | 1 | 1 1000 | 18 (+0110 added)
9 + 9 + 1 | 1 0011 | 1 | 1 1001 | 19 (+0110 added)
58 + 67 -> 1 0010 0101 = 125
all 200 digit cases checked, mismatches = 0
৫ + ৩ = ৮ বৈধ BCD, সংশোধন নেই। ৪ + ৬ আর ৮ + ৭-এ binary যোগফল 1010 আর 1111 — অবৈধ BCD, এখানে Z3Z1 বা Z3Z2 শর্ত ধরা পড়ে। ৯ + ৯ সবচেয়ে চতুর: Z = 0010 দেখতে বৈধ, কিন্তু K = 1 বলে সংশোধন হয় আর 0010 + 0110 = 1000, অর্থাৎ ১৮. দুই অঙ্কের যোগে এককের ঘরের C = 1 দশকের ঘরে carry-in হয়েছে: ৫ + ৬ + ১ = ১২ → অঙ্ক ২, carry ১ → ১২৫. শেষ লাইন দেখায় সূত্রটি সব ১০ × ১০ × ২ = ২০০ ক্ষেত্রেই ঠিক — একটি প্রমাণের পাশে একটি সম্পূর্ণ যাচাই।
৯ · Adder কোথায় বসে — ALU, subtractor, Verilog আর software
বিয়োগ। Two's complement-এ A − B = A + B′ + 1 (পর্ব ০৫)। তাই একটি adder-এর B ইনপুটে প্রতিটি বিটের সামনে একটি XOR বসান, যার অন্য ইনপুট একটি নিয়ন্ত্রণ-সংকেত SUB: SUB = 0 হলে bi ⊕ 0 = bi (যোগ), SUB = 1 হলে bi ⊕ 1 = bi′. আর “+1”-টি আসে c0 = SUB দিয়ে, বিনা খরচে। এই adder-subtractor, তার carry, overflow, zero আর negative flag-এর পুরো হিসাব পর্ব ২০-এ, আর যোগ-বিয়োগ-লজিক এক জায়গায় করা ALU-র নকশা পর্ব ১৩-এ। গুণের দ্রুত circuit (পর্ব ২১) আসলে অনেকগুলো adder-এর সারি, আর তুলনা (A < B?) মূলত একটি বিয়োগের চিহ্ন দেখা। RISC-V-র add, sub, addi, slt, এমনকি load-store-এর ঠিকানা হিসাব — সবখানেই এই একই adder.
Hardware description language-এ। আসল chip-এর নকশাকার gate হাতে আঁকেন না; লেখেন Verilog-এর মতো ভাষায় (পর্ব ১৪)। নিচে একটি full adder আর তা দিয়ে ৪ বিটের ripple adder — synthesizable ধাঁচে:
module full_adder (input wire a, b, cin,
output wire s, cout);
assign s = a ^ b ^ cin;
assign cout = (a & b) | (cin & (a ^ b));
endmodule
module ripple4 (input wire [3:0] a, b,
input wire c0,
output wire [3:0] s,
output wire c4);
wire [4:0] c;
assign c[0] = c0;
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : stage
full_adder fa (.a(a[i]), .b(b[i]), .cin(c[i]), .s(s[i]), .cout(c[i+1]));
end
endgenerate
assign c4 = c[4];
endmoduleবাস্তবে অনেক নকশাকার শুধু assign {c4, s} = a + b + c0; লেখেন, আর synthesis tool timing-এর লক্ষ্য দেখে নিজেই ripple, CLA বা prefix নকশা বেছে নেয়। তবু কোন নকশা কেন বাছা হলো, তা বুঝতে এই পর্বের জ্ঞানই লাগে।
Software-এ full adder. Hardware-এর সূত্র সত্যিই ঠিক কি না, তা software-এ লাখো ইনপুটে চালিয়ে দেখা যায়। নিচের C প্রোগ্রাম ৩২টি full adder-এর সূত্র লুপে চালিয়ে দুটি uint32_t যোগ করে, আর CPU-র নিজের +-এর সাথে মেলায়। ইনপুট আসে নির্দিষ্ট seed-এর একটি সরল pseudo-random generator থেকে, তাই প্রতিবার একই সংখ্যা:
#include <stdio.h>
#include <stdint.h>
/* 32 full adders in software: sum and carry formulas, bit by bit */
static uint32_t ripple_add(uint32_t a, uint32_t b, unsigned *cout) {
uint32_t s = 0, c = 0;
for (int i = 0; i < 32; i++) {
uint32_t x = (a >> i) & 1u, y = (b >> i) & 1u;
s |= (x ^ y ^ c) << i; /* S = A xor B xor Cin */
c = (x & y) | (c & (x ^ y)); /* Cout = AB + Cin(A xor B) */
}
*cout = (unsigned)c;
return s;
}
int main(void) {
uint32_t seed = 2026u; /* fixed seed: same numbers every run */
long bad = 0, N = 1000000;
unsigned c;
for (long t = 0; t < N; t++) {
seed = seed * 1664525u + 1013904223u; uint32_t a = seed;
seed = seed * 1664525u + 1013904223u; uint32_t b = seed;
uint32_t s = ripple_add(a, b, &c);
uint64_t ref = (uint64_t)a + b; /* the CPU's own adder */
if (s != (uint32_t)ref || c != (unsigned)(ref >> 32)) bad++;
if (t < 3) printf("0x%08X + 0x%08X = 0x%08X carry %u\n",
(unsigned)a, (unsigned)b, (unsigned)s, c);
}
uint32_t s = ripple_add(0xFFFFFFFFu, 1u, &c);
printf("0xFFFFFFFF + 0x00000001 = 0x%08X carry %u\n", (unsigned)s, c);
printf("random tests: %ld, mismatches with + : %ld\n", N, bad);
return 0;
}Wandbox-এ (gcc, x86-64 Linux) আউটপুট:
0x05709641 + 0x0B5D7AAC = 0x10CE10ED carry 0
0x09DAB61B + 0xA9BBF4BE = 0xB396AAD9 carry 0
0xA50C1505 + 0x7BE902A0 = 0x20F517A5 carry 1
0xFFFFFFFF + 0x00000001 = 0x00000000 carry 1
random tests: 1000000, mismatches with + : 0
দশ লাখ এলোমেলো জোড়ায় একটিও অমিল নেই — sum আর carry-out দুটিই CPU-র নিজস্ব যোগের সাথে মিলেছে। তৃতীয় লাইনে যোগফল ৩২ বিট ছাড়িয়েছে, তাই carry 1 (৬৪ বিটে হিসাব করা ref-এর bit 32). চতুর্থ লাইন ripple-এর সবচেয়ে খারাপ ক্ষেত্র: 0xFFFFFFFF + 1-এ carry সব ৩২টি ঘর পেরিয়ে বেরিয়ে যায়, ফল ০ আর carry ১. মনে রাখুন, এটি সঠিকতার প্রমাণ নয়, যাচাই — তবে ৩২ বিটের সূত্রটি প্রতিটি ঘরে একই full adder, আর full adder-এর ৮টি সারিই আমরা অংশ ৩-এ মিলিয়েছি; দুটি মিলে আস্থা অনেক বাড়ে। একটি শিক্ষাও আছে: software-এ এই লুপ একটি +-এর চেয়ে বহু গুণ ধীর, কারণ CPU-র adder hardware-এ সব ৩২টি ঘর একসাথে, দ্রুত নকশায় হিসাব করে।
১০ · AI-এর যুগে adder নকশা
AI টুল half adder-এর সূত্র নির্ভুল বলে, কিন্তু lookahead-এর খোলা সূত্র, delay গোনা আর BCD সংশোধনের মতো জায়গায় প্রায়ই সূক্ষ্ম ভুল করে। কয়েকটি সাধারণ উদাহরণ:
| AI-এর প্রচলিত ভুল | ঠিক কথা | কীভাবে যাচাই |
|---|---|---|
| c3 বা c4-এর সূত্রে একটি পদ বাদ, বা p-এর ক্রম ভুল | ci+1-এ ঠিক i + 2টি পদ; প্রতিটি পদে gj-এর পরে j+1 থেকে i পর্যন্ত সব p | সূত্র দিয়ে সব ইনপুটে ripple-এর সাথে মিলিয়ে (অংশ ৬-এর calculator) |
| “CLA-র delay যেকোনো n-এ ধ্রুবক” | শুধু এক ব্লকে ধ্রুবক; fan-in সীমা মানলে বহুস্তর CLA-তে O(log n) | স্তর গুনে নিজে delay হিসাব |
| Ripple-এর delay “n gate delay” বা “4n” | মডেল বলে দিতে হয়; প্রতি gate ১ ধরলে 2n + 1 | প্রথম ঘর আর প্রতি ঘরের পথ আলাদা করে গোনা |
| BCD-তে “যোগফল > ৯ হলে +৬” বলে K বাদ দেওয়া | ৯ + ৯-এ Z = 0010, তবু সংশোধন লাগে; C = K + Z3Z2 + Z3Z1 | ৯ + ৯ আর ৯ + ৯ + ১ দিয়ে পরীক্ষা |
| p = a + b ধরে sum = p ⊕ c লেখা | OR-রূপ carry-র জন্য ঠিক, sum-এর জন্য XOR-রূপ লাগে | a = b = 1 ইনপুটে sum পরীক্ষা |
Verilog-এ always block-এ অসম্পূর্ণ assignment, ফলে অনিচ্ছাকৃত latch | Combinational logic-এ সব শাখায় সব আউটপুট দিতে হয়, বা assign | Simulator আর synthesis-এর warning দেখে |
AI-কে এমনভাবে প্রশ্ন করুন যাতে প্রতিটি ধাপ আর মডেল লেখা থাকে, আর আপনি নিজে মিলিয়ে নিতে পারেন:
AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- Full adder-এর S আর Cout-এর সূত্র? — S = A ⊕ B ⊕ Cin, Cout = AB + Cin(A ⊕ B) = AB + ACin + BCin.
- দুটি half adder দিয়ে full adder কীভাবে? — প্রথমটি A, B থেকে P, G; দ্বিতীয়টি P, Cin থেকে S, T; Cout = G + T (একটি OR).
- n বিট ripple adder-এর delay? — O(n); প্রতি gate ১ ধরলে cn 2n + 1 delay-তে।
- Generate আর propagate কী? — gi = aibi (ঘর নিজেই carry দেয়), pi = ai ⊕ bi (ঢোকা carry পার করে); ci+1 = gi + pici.
- BCD যোগে কখন 0110 যোগ হয়? — binary যোগফল ৯-এর বেশি বা carry এলে: C = K + Z3Z2 + Z3Z1.
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Carry-র সমস্যা কি শুধু electronics-এর? যান্ত্রিক ক্যালকুলেটরের যুগ থেকে carry-lookahead পর্যন্ত ইতিহাসটি কেমন?
Carry-র সমস্যা electronics-এর চেয়ে অনেক পুরোনো। সপ্তদশ শতকে Blaise Pascal-এর যোগ-যন্ত্রে (Pascaline) একটি চাকা ৯ থেকে ০-তে গেলে বাঁয়ের চাকাকে এক ঘর ঘুরিয়ে দিত — একেবারে যান্ত্রিক ripple carry. সমস্যা দেখা দিত 999…9 + 1-এর মতো ক্ষেত্রে: সব চাকা একসাথে ঘোরাতে অনেক জোর লাগত, আর carry একের পর এক চাকায় গড়িয়ে যেতে সময় নিত।
উনবিংশ শতকে Charles Babbage তাঁর Difference Engine আর Analytical Engine-এর নকশায় carry নিয়ে দীর্ঘ সময় ব্যয় করেন। Analytical Engine-এর জন্য তিনি “anticipating carriage” নামে একটি ব্যবস্থার নকশা করেছিলেন বলে ইতিহাসবিদেরা লেখেন — যেখানে কোন চাকাগুলো ৯-এ আছে তা আগে দেখে carry একবারে অনেক ঘর পার করানো যায়। ধারণাটি আজকের lookahead-এর সাথে আশ্চর্য রকম মেলে: “কে পার করবে” (propagate) আগে জেনে নেওয়া।
Electronic কম্পিউটারে প্রথম দিকের যন্ত্রগুলো প্রায়ই bit-serial ছিল — একবারে এক বিট যোগ, পরের ঘরের জন্য carry একটি flip-flop-এ রেখে। Parallel adder এলে ripple-এর ধীরতা স্পষ্ট হয়। ১৯৫০-এর দশকের শেষে Manchester বিশ্ববিদ্যালয়ের Atlas প্রকল্পে carry-কে দ্রুত পার করানোর একটি circuit-কৌশল (পরে যা Manchester carry chain নামে পরিচিত) ব্যবহৃত হয়, আর প্রায় একই সময়ে A. Weinberger আর J. L. Smith carry-lookahead-এর একটি পদ্ধতি প্রকাশ করেন (প্রায়ই ১৯৫৮ সালের কাজ হিসেবে উদ্ধৃত)। ১৯৬০-এর দশকে Sklansky-র conditional-sum adder আর Bedrij-এর carry-select adder আসে। ১৯৭৩ সালে Kogge আর Stone recurrence-এর সাধারণ সমাধান হিসেবে prefix পদ্ধতি দেখান, আর ১৯৮২ সালে Brent আর Kung VLSI-র তার আর আকারের দিক থেকে একটি নিয়মিত নকশা দেন।
লক্ষ করুন, মূল প্রশ্নটি তিনশো বছরেও বদলায়নি: “carry কতদূর যাবে, তা আগেভাগে জানা যায় কি?” বদলেছে শুধু মাধ্যম — দাঁতওয়ালা চাকা, relay, vacuum tube, transistor.
মূল উপলব্ধি: Carry হলো যোগের একমাত্র “দূরের নির্ভরতা” — এক ঘরের ফল দূরের আরেক ঘরের ওপর নির্ভর করে। প্রতিটি দ্রুত adder এই নির্ভরতাকে ভেঙে সমান্তরাল করার একটি কৌশল।
প্র ০২Ripple-carry adder কি সবসময় খারাপ নকশা? কখন এটিই সেরা পছন্দ?
না। প্রকৌশলে “সবচেয়ে দ্রুত” আর “সেরা” এক কথা নয়। Ripple-carry adder সবচেয়ে ছোট (n বিটে প্রায় 5n gate), সবচেয়ে নিয়মিত (একই cell বারবার), তার সবচেয়ে ছোট (প্রতিটি cell শুধু পাশেরটির সাথে জোড়া), আর switching কম বলে power-ও সাধারণত কম। ছোট width-এ — ৪ বা ৮ বিট — delay-এর পার্থক্য সামান্য, কিন্তু আকারের পার্থক্য বড়। তাই ছোট microcontroller, কম-power sensor chip আর counter-এর মতো জায়গায় ripple প্রায়ই যথেষ্ট।
দ্বিতীয় কারণ, adder সবসময় critical path-এ থাকে না। যদি clock অন্য কোনো ধীর অংশ (যেমন মেমরি পড়া) দিয়ে ঠিক হয়, আর adder-এর হাতে যথেষ্ট সময় থাকে, তবে দ্রুত adder-এর বাড়তি আকার আর power শুধুই অপচয়। আধুনিক synthesis tool ঠিক এই হিসাব করে: timing-এর লক্ষ্য মিটলে ছোট নকশা, না মিটলে দ্রুত নকশা।
তৃতীয়, FPGA. Xilinx (এখন AMD-এর অংশ) আর Altera-র FPGA-তে logic block-এর পাশে বিশেষ “carry chain” hardware থাকে — সাধারণ programmable routing-এর চেয়ে অনেক দ্রুত একটি নির্দিষ্ট তার আর multiplexer. সেখানে ripple-এর প্রতি ঘরের delay এত কম যে ৩২ বা এমনকি ৬৪ বিট পর্যন্ত ripple প্রায়ই programmable logic দিয়ে বানানো CLA-র চেয়ে দ্রুত হয়।
চতুর্থ, গড় ক্ষেত্র। ১৯৪৬ সালে Burks, Goldstine আর von Neumann-এর বিখ্যাত প্রতিবেদনে দেখানো হয়েছিল বলে প্রায়ই উদ্ধৃত হয় যে এলোমেলো ইনপুটে দীর্ঘতম carry-শিকলের গড় দৈর্ঘ্য মোটামুটি log2 n — ৬৪ বিটে মাত্র ৬-এর কাছাকাছি, ৬৪ নয়। Synchronous CPU এই সুবিধা নিতে পারে না, কারণ clock সবচেয়ে খারাপ ক্ষেত্র ধরে ঠিক হয়। কিন্তু asynchronous (clock-হীন) নকশায় carry শেষ হলে নিজেই সংকেত দেয় এমন completion-detection adder গড় গতি পায়। অংশ ৪-এর simulator-এ আপনি এই ইনপুট-নির্ভরতা নিজেই দেখেছেন।
মূল উপলব্ধি: নকশার মান বিচার হয় প্রেক্ষাপটে — width, clock-এর লক্ষ্য, আকার, power আর প্রযুক্তি। Ripple-কে “ধীর” বলা অর্ধেক সত্য; সঠিক কথা হলো “বড় width-এ সবচেয়ে খারাপ ক্ষেত্রে ধীর।”
প্র ০৩Kogge–Stone-এর স্তর সবচেয়ে কম — তাহলে সব CPU সরাসরি এটিই ব্যবহার করে না কেন?
কারণ gate delay-ই একমাত্র delay নয়। পুরোনো প্রযুক্তিতে transistor ধীর আর তার তুলনামূলক দ্রুত ছিল, তাই “কত স্তর gate” গুনলেই চলত। আজকের ছোট process node-এ চিত্র উল্টে গেছে: তার খুব সরু, তাই তার প্রতিরোধ (resistance) আর ধারকত্ব (capacitance) বেশি, আর একটি লম্বা তার কয়েকটি gate-এর সমান বা তার চেয়েও বেশি delay দিতে পারে। Kogge–Stone-এ শেষের দিকের স্তরে প্রতিটি cell n/2 ঘর দূরের cell-এর সাথে জোড়া — ৬৪ বিটে ৩২ ঘর দূরে। এমন অনেক লম্বা তার পাশাপাশি চলে, জায়গা খায়, আর একটি অন্যটির সংকেতে বাধা দেয় (crosstalk).
দ্বিতীয় দাম হলো cell-এর সংখ্যা আর power. ৬৪ বিটে Kogge–Stone-এ ৩২১টি ∘ cell, Brent–Kung-এ ১২০টি। প্রতিটি cell প্রতিটি যোগে switch করতে পারে, তাই dynamic power বাড়ে। মোবাইল chip-এ, যেখানে ব্যাটারিই রাজা, এটি বড় বিবেচনা। আর ALU-তে adder একা নয় — একটি CPU core-এ একাধিক ALU, address হিসাবের adder, আর SIMD unit-এ আরও অনেক adder থাকে; প্রতিটির আকার গুণ হয়ে যায়।
তাই বাস্তব নকশা প্রায়ই মাঝামাঝি পথ নেয়। Han–Carlson-এর মতো নকশা Kogge–Stone-এর প্রায় অর্ধেক cell নিয়ে মাত্র এক স্তর বেশি নেয়। Sparse-tree নকশায় prefix গাছ শুধু প্রতি চার বা আট ঘরে carry বের করে, আর বাকি অংশে ছোট carry-select ব্লক বসে — অংশ ৭-এর দুটি ধারণার মিশ্রণ। Ling-এর কৌশল প্রথম স্তরটি একটু সরল করে। এক কথায়, adder নকশা একটি বহুমাত্রিক আপস: স্তর, fan-out, তারের দৈর্ঘ্য, cell-এর সংখ্যা, power আর নকশার নিয়মিততা।
এখানে একটি বড় শিক্ষা আছে, যা পুরো কোর্সে ফিরে আসবে: পাঠ্যবইয়ের সরল মডেল (প্রতি gate ১) তুলনা শেখার জন্য দারুণ, কিন্তু আসল chip-এর সিদ্ধান্ত হয় পূর্ণাঙ্গ timing বিশ্লেষণে, যেখানে তার, load আর process-এর বৈচিত্র্য হিসাবে আসে (পর্ব ১৮)।
মূল উপলব্ধি: তাত্ত্বিকভাবে সবচেয়ে কম স্তর মানেই বাস্তবে সবচেয়ে দ্রুত নয়। Silicon-এ তারও খরচ — আর অনেক সময় সেটিই সবচেয়ে বড় খরচ।
প্র ০৪Binary এত দক্ষ — তবু BCD আর দশমিক হিসাব আজও hardware-এ টিকে আছে কেন?
কারণ মানুষের টাকা দশমিকে। ০.১ টাকা binary-তে নির্ভুলভাবে লেখা যায় না — যেমন ১/৩ দশমিকে নির্ভুলভাবে লেখা যায় না। তাই binary floating point-এ 0.1 + 0.2 ঠিক 0.3 হয় না (এর বিস্তারিত পর্ব ২৩ আর ২৪-এ)। ব্যাংকের সুদ, কর বা বিলের হিসাবে এক পয়সার গোলমালও আইনগত সমস্যা হতে পারে, কারণ নিয়মগুলো দশমিক ভাগ আর দশমিক rounding-এ লেখা। BCD বা দশমিক হিসাব এই সমস্যা এড়ায়: যা লেখা, ঠিক তাই হিসাব হয়।
দ্বিতীয় ক্ষেত্র প্রদর্শন। দোকানের ক্যালকুলেটর, ডিজিটাল ঘড়ি, বিদ্যুতের মিটার — এসবের ভেতরের chip প্রায়ই BCD-তে কাজ করে, কারণ প্রতিটি অঙ্ক সরাসরি 7-segment display-তে পাঠানো যায়; binary থেকে দশমিকে রূপান্তরের ভাগ-প্রক্রিয়া লাগে না। ছোট chip-এ এই সরলতা দামি।
বড় কম্পিউটারেও দশমিক টিকে আছে। IBM-এর mainframe পরিবার (System/360 থেকে আজকের IBM Z) প্রথম থেকেই packed decimal-এর instruction রেখেছে, কারণ ব্যাংক আর বিমা কোম্পানির COBOL প্রোগ্রাম এর ওপর চলে। ২০০৮ সালে IEEE 754 মানদণ্ডে decimal floating-point format যোগ হয়, আর IBM-এর POWER6 (২০০৭) ও পরের mainframe processor-এ এর জন্য hardware unit ছিল বলে IBM জানিয়েছে। অন্যদিকে x86-এ পুরোনো BCD-সংশোধনের instruction (যেমন DAA) ৩২ বিট mode-এ আছে, কিন্তু ৬৪ বিট mode-এ বাদ দেওয়া হয়েছে — সাধারণ software সেখানে দশমিক হিসাব library দিয়ে করে।
দাম কত? এক অঙ্কের BCD adder-এ দ্বিতীয় একটি adder আর সংশোধন-logic লাগে, আর ৪ বিটে ১৬টির জায়গায় মাত্র ১০টি প্যাটার্ন কাজে লাগে — প্রায় ১৭% জায়গা নষ্ট (log2 10 ≈ 3.32 বিটের তথ্য ৪ বিটে)। এজন্য densely packed decimal-এর মতো encoding তিনটি দশমিক অঙ্ক ১০ বিটে রাখে।
মূল উপলব্ধি: সংখ্যার রূপ বেছে নেওয়া শুধু hardware-এর দক্ষতার প্রশ্ন নয়; প্রশ্ন হলো কোন হিসাবে কোন ভুল সহ্য করা যায়। টাকার হিসাবে “দ্রুত কিন্তু সামান্য ভুল” চলে না।
প্র ০৫গুণ করতে অনেকগুলো সংখ্যা যোগ করতে হয়। প্রতিবার carry-র অপেক্ষা করলে তো গুণ খুব ধীর হবে — hardware এটা কীভাবে এড়ায়?
৩২ বিট × ৩২ বিট গুণে ৩২টি partial product — প্রতিটি একটি shift করা সংখ্যা — যোগ করতে হয়। প্রতিটি যোগে CLA দিয়েও log n delay, আর ৩১টি যোগ পরপর করলে মোট সময় অনেক। কৌশলটি হলো carry-কে আপাতত ছড়াতেই না দেওয়া। একটি carry-save adder তিনটি সংখ্যা নিয়ে দুটি সংখ্যা ফেরত দেয়: প্রতিটি ঘরে একটি full adder তিনটি বিট (তিন সংখ্যার একই ঘরের) যোগ করে, sum বিট এক সংখ্যায় আর carry বিট আরেক সংখ্যায় (এক ঘর বাঁয়ে সরিয়ে) রাখে। ঘরগুলো একে অপরের সাথে জোড়া নয়, তাই width যা-ই হোক, delay মাত্র একটি full adder-এর!
অর্থাৎ full adder-এর একটি নতুন ব্যাখ্যা: এটি একটি “৩ থেকে ২ compressor” — তিনটি সমান ওজনের বিটকে দুটি বিটে নামিয়ে আনে (এক ঘরের S আর পরের ঘরের C), মোট মান অপরিবর্তিত রেখে। এভাবে ধাপে ধাপে ৩২টি সংখ্যাকে ৩ → ২ হারে কমিয়ে শেষে দুটি সংখ্যায় আনা যায়; গাছের আকারে সাজালে ধাপ লাগে log1.5 n-এর কাছাকাছি। একে বলে Wallace tree (C. S. Wallace, ১৯৬৪) বা এর রূপভেদ Dadda tree. একদম শেষে দুটি সংখ্যা যোগ করতে একটিমাত্র দ্রুত carry-propagate adder (যেমন prefix adder) লাগে।
একই ধারণা আরও অনেক জায়গায়: অনেক সংখ্যার যোগফল (যেমন DSP-র filter বা AI accelerator-এর dot product), পরপর অনেক যোগ (accumulator) — সবখানে মাঝের ফল “sum + carry” দুই অংশে রেখে শেষে একবার carry ছড়ানো হয়। গুণের এই নকশাগুলোর বিস্তারিত আর Booth-এর পদ্ধতি পর্ব ২১-এ থাকবে।
পুরো ছবিটা ভাবুন: এই পর্বের ছোট্ট full adder একদিকে ripple adder-এর ইট, অন্যদিকে multiplier-এর compressor. একই circuit, ভিন্ন সংযোগ, ভিন্ন উদ্দেশ্য।
মূল উপলব্ধি: দ্রুত হিসাবের একটি গভীর নিয়ম — যে কাজ শেষে একবার করলেই চলে (carry ছড়ানো), তা প্রতিটি ধাপে করবেন না। Redundant রূপে মাঝের ফল রেখে শেষে একবার সমাধান করাই carry-save-এর মূলমন্ত্র।
অনুশীলন
পরীক্ষার ধাঁচে লেখা — আগে খাতায় সমাধান করুন, তারপর উত্তর মিলিয়ে নিন।
-
Delay-এর হিসাব (numerical): একটি ৩২ বিট adder-এর জন্য ধরুন প্রতিটি gate-এর delay ৫০ ps, fan-in ৫ পর্যন্ত চলে আর inverter-এর delay ধরা হবে না। (ক) Ripple-carry, (খ) ৪ বিটের CLA ব্লক ripple-এ জোড়া, (গ) বহুস্তর CLA (প্রতি স্তরে ৪টি দল), (ঘ) ৪ বিটের ripple ব্লক দিয়ে carry-select — প্রতিটির সবচেয়ে খারাপ delay বের করুন। (ঙ) Adder যদি একটি clock cycle-এর পুরো সময় নেয়, (ক) আর (গ)-তে সর্বোচ্চ clock rate কত?
- (ক) Ripple: 2n + 1 = ৬৫ gate delay → ৬৫ × ৫০ = ৩,২৫০ ps.
- (খ) CLA ব্লক ripple-এ: ৮টি ব্লক। c4 সময় ৩-এ, তারপর প্রতি ব্লকে +২: c28 সময় ৩ + ২ × ৬ = ১৫-এ; শেষ ব্লকের ভেতরের carry ১৭-এ, sum ১৮-এ (c32 ১৭-এ)। সূত্র n/2 + 2 = ১৮ → ৯০০ ps.
- (গ) বহুস্তর CLA: L = ⌈log4 32⌉ = ৩ স্তর → 4L = ১২ → ৬০০ ps.
- (ঘ) Carry-select: সব ৪ বিটের ripple ব্লক ৯-এ শেষ; বাকি ৭টি ব্লকে প্রতিটি mux-বাছাই +২ → ৯ + ২ × ৭ = ২৩ → ১,১৫০ ps.
- (ঙ) f = ১ / delay. Ripple: ১ / ৩.২৫ ns ≈ ৩০৮ MHz. বহুস্তর CLA: ১ / ০.৬ ns ≈ ১.৬৭ GHz. একই gate দিয়ে শুধু নকশা বদলে প্রায় সাড়ে পাঁচ গুণ দ্রুত।
-
CLA-র carry সূত্র (trace by hand): ৪ বিটের a = 1100, b = 0101 আর c0 = 1. (ক) g3…g0 আর p3…p0 (p = a ⊕ b) লিখুন। (খ) খোলা সূত্র দিয়ে c1, c2, c3, c4 বের করুন। (গ) Sum আর group G, P বের করে দেখান c4 = G + P·c0.
(ক) g = a AND b =
0100(শুধু g2 = 1); p = a XOR b =1001(p3 = p0 = 1).- c1 = g0 + p0c0 = 0 + 1·1 = 1.
- c2 = g1 + p1g0 + p1p0c0 = 0 + 0 + 0·1·1 = 0 — bit 1 (0 + 0) carry মেরে ফেলে।
- c3 = g2 + p2g1 + p2p1g0 + p2p1p0c0 = 1 + 0 + 0 + 0 = 1.
- c4 = g3 + p3g2 + p3p2g1 + p3p2p1g0 + p3p2p1p0c0 = 0 + 1·1 + 0 + 0 + 0 = 1.
(গ) s = p ⊕ (c3c2c1c0) = 1001 ⊕ 1011 =
0010, আর c4 = 1 → 1 0010 = ১৮. যাচাই: ১২ + ৫ + ১ = ১৮. G = g3 + p3g2 + p3p2g1 + p3p2p1g0 = 0 + 1 + 0 + 0 = 1; P = p3p2p1p0 = 1·0·0·1 = 0. তাই G + P·c0 = 1 + 0 = 1 = c4. লক্ষ করুন, c0 = 1 হলেও তা c4 পর্যন্ত যায়নি (P = 0); c4 এসেছে bit 2-এর তৈরি carry থেকে। -
BCD সংশোধন: (ক) 47 + 38 যোগটি BCD adder-এ অঙ্ক ধরে ধরে করুন — প্রতিটি অঙ্কে binary যোগফল, K, C আর সংশোধিত অঙ্ক লিখুন। (খ) শুধু Z3Z2Z1Z0 দেখে (K বাদ দিয়ে) সংশোধনের শর্ত বানালে কোন কোন যোগফলে ভুল হবে?
(ক) এককের ঘর: 7 + 8 + 0 = ১৫ → binary
0 1111; K = 0, কিন্তু Z3Z2 = 1, তাই C = 1. 1111 + 0110 = 1 0101 → অঙ্ক0101(৫), carry ১. দশকের ঘর: 4 + 3 + 1 = ৮ →0 1000; K = 0, Z3 = 1 কিন্তু Z2 = Z1 = 0, তাই C = 0 — সংশোধন নেই। ফল1000 0101= ৮৫. যাচাই: ৪৭ + ৩৮ = ৮৫.(খ) Z3Z2 + Z3Z1 শুধু ১০–১৫ ধরে। যোগফল ১৬–১৯ হলে Z = 0000–0011 — দেখতে বৈধ, তাই সংশোধন হবে না। যেমন ৯ + ৭ = ১৬ → Z = 0000, ফল ভুলভাবে “০” আর কোনো carry নেই; ৯ + ৯ = ১৮ → “২”. চারটি যোগফলই (১৬, ১৭, ১৮, ১৯) ভুল হবে। তাই K পদটি অপরিহার্য।
-
আউটপুট অনুমান করুন: অংশ ৪-এর ripple simulator-এ (প্রতি gate ১ delay, AND-এ ০ আর OR-এ ১ এলে আগে স্থির) নিচের দুটি ডাক কী ছাপবে? দুটির যোগফল একই হলেও delay আলাদা কেন?
ripple("1011", "0101", false); ripple("1000", "1000", false);1011 + 0101 = 1 0000 -> settles after 8 gate delays (n = 4, static worst case 2n+1 = 9) 1000 + 1000 = 1 0000 -> settles after 4 gate delays (n = 4, static worst case 2n+1 = 9)দুটিরই যোগফল ১৬ = 1 0000. প্রথমটিতে bit 0 carry তৈরি করে (1 + 1, c1 সময় ২-এ), আর bit 1, 2, 3 সবাই propagate করে (1 + 0, 0 + 1, 1 + 0) — তাই c2 = ৪, c3 = ৬, c4 = ৮: ripple-এর পুরো শিকল। দ্বিতীয়টিতে bit 0–2 তিনটিই 0 + 0 (kill), তাই প্রতিটি carry ২–৩ সময়ে নিশ্চিত ০; bit 3 নিজেই 1 + 1, c4 সময় ২-এ; সবচেয়ে দেরি s2 আর s3-এর, সময় ৪-এ। শিক্ষা: delay ঠিক করে propagate-ঘরের দীর্ঘতম শিকল, যোগফলের মান নয়।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রম১৬ বিটের দুই স্তরের carry-lookahead adder-এ সংকেতগুলো যে ক্রমে তৈরি হয়, সেই ক্রমে সাজান (▲▼ চেপে সরান)
- প্রতিটি বিটের gi = aibi আর pi = ai ⊕ bi
- প্রতিটি ৪ বিটের ব্লকের group G আর P
- Lookahead carry unit-এ c4, c8, c12, c16
- প্রতিটি ব্লকের ভেতরের carry (যেমন c5, c6, c7)
- প্রতিটি sum বিট si = pi ⊕ ci
একটি full adder-এর ইনপুট A, B, Cin. নিচের কোনটি Cout-এর সঠিক সূত্র?
- A ⊕ B ⊕ Cin
- AB + Cin′
- AB + Cin(A ⊕ B)
- A·B·Cin
প্রতিটি gate-এর delay ১ একক ধরলে, ১৬ বিটের ripple-carry adder-এ carry-out c16 সবচেয়ে খারাপ ক্ষেত্রে কত একক পরে স্থির হয়?
- 16
- 33
- 64
- 4
Carry-lookahead adder-এ gi = aibi আর pi = ai ⊕ bi হলে c2-এর খোলা সূত্র কোনটি?
- g0 + p0c0
- g1 + p1p0c0
- p1 + g1g0 + g1g0c0
- g1 + p1g0 + p1p0c0
এক অঙ্কের BCD adder-এ 9 + 9 যোগ করলে প্রথম ৪ বিটের binary adder দেয় K = 1 আর Z = 0010. চূড়ান্ত আউটপুট কী?
- carry 0, অঙ্ক 0010 (২)
- carry 1, অঙ্ক 0010 (১২)
- carry 1, অঙ্ক 1000 (১৮)
- carry 1, অঙ্ক 1001 (১৯)
Carry-select adder দ্রুত হয় কোন কৌশলে?
- প্রতিটি carry সরাসরি g আর p-এর দুই স্তরের সূত্রে হিসাব করে
- প্রতিটি ব্লকে carry-in ০ আর ১ দুই ধরে আগেই ফল বানিয়ে রাখে, পরে mux দিয়ে বাছে
- সব ঘর propagate করলে carry ব্লকটি লাফিয়ে পার হয়
- Carry-কে আলাদা সংখ্যায় জমিয়ে রেখে শেষে একবার যোগ করে
প্রতিটি gate ১ delay আর fan-in সীমা ৫ ধরে ৪টি করে দল নিয়ে বহুস্তর carry-lookahead adder বানালে width n বাড়ার সাথে delay কীভাবে বাড়ে?
- লগারিদমিক হারে, O(log n)
- রৈখিক হারে, O(n)
- একদম বাড়ে না, O(1)
- বর্গ হারে, O(n2)
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Half adder: S = A ⊕ B, C = AB. Full adder: S = A ⊕ B ⊕ Cin, Cout = AB + Cin(A ⊕ B) = majority — দুটি half adder + OR, ৫টি gate.
- Ripple-carry: n টি full adder শিকলে; সরল আর ছোট, কিন্তু delay O(n) — প্রতি gate ১ ধরলে 2n + 1. Delay ঠিক করে propagate-ঘরের দীর্ঘতম শিকল।
- Lookahead: gi = aibi, pi = ai ⊕ bi, ci+1 = gi + pici খুলে দুই স্তরের সূত্র; ৪ বিটের ব্লকে ৪ delay. Carry-র জন্য p = a + b-ও চলে, sum-এর জন্য XOR লাগে।
- বড় adder: group G, P দিয়ে স্তরে স্তরে lookahead → delay O(log n), আকার প্রায় O(n). Carry-select, carry-skip আর prefix (Kogge–Stone, Brent–Kung) — গতি, আকার আর তারের ভিন্ন আপস।
- BCD adder: binary যোগের পর C = K + Z3Z2 + Z3Z1 হলে 0110 যোগ, আর C-ই পরের অঙ্কের carry. Adder-ই ALU, subtractor আর multiplier-এর ভিত্তি।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১২ · Multiplexer, decoder আর encoder — বেছে নেওয়া আর ঠিকানা খোঁজাপরবর্তী পর্ববেছে নেওয়ার circuit আর ঠিকানা খোঁজার circuit.
- পর্ব ০৫ · Integer-এর বিট-রূপ — unsigned, sign-magnitude, two's complement আর overflowআগে পড়ুনCarry বনাম overflow flag আর two's complement-এর যোগ।
- কম্পিউটার আর্কিটেকচার ও ডিজিটাল লজিক · কোর্স হোম৬২টি পর্বলজিক গেট থেকে pipeline আর cache — পুরো রোডম্যাপ।
- কম্পিউটার ফান্ডামেন্টালস · Combinational circuitপাশের ট্র্যাকশুরুর স্তরে adder আর অন্যান্য combinational circuit.
- C প্রোগ্রামিং · Bitwise operationsপাশের ট্র্যাক&, |, ^, shift — software-এ full adder লেখার উপকরণ।
- DSA · Prefix sumsপাশের ট্র্যাকPrefix adder-এর পেছনের algorithm-এর ধারণা।