সরাসরি পর্বে যান
পর্ব ১২ · ৬২-এর মধ্যে · মডিউল ২

Multiplexer, decoder আর encoder — বেছে নেওয়া আর ঠিকানা খোঁজা

Multiplexers, Demultiplexers, Decoders, Priority Encoders, Comparators, Parity and Code Converters — the Selection Building Blocks of Every CPU

Taught in Bangla, this lesson designs 2:1, 4:1 and 8:1 multiplexers and mux trees, proves that one 2^(n−1):1 mux implements any n-variable function, builds demultiplexers and 2:4, 3:8 and 4:16 decoders, decodes memory addresses for 16 KiB chips, designs seven-segment decoders, encoders and priority encoders with a valid bit, comparators, parity generators and Gray converters, and shows a register file as muxes plus a decoder, with gate-level simulators.

এই পর্বে যা শিখবেন

  • 2:1, 4:1, 8:1 mux-এর truth table, Boolean সূত্র আর gate-এর নকশা, আর ছোট mux জুড়ে বড় mux বানানো
  • যেকোনো function-কে ২n:1 আর ২n−1:1 mux দিয়ে বাস্তবায়ন — পরীক্ষার সবচেয়ে প্রচলিত ধরনের প্রশ্ন, প্রমাণসহ
  • Demultiplexer, 2:4 আর 3:8 decoder, enable, decoder জুড়ে 4:16 বানানো, আর memory map থেকে address decoding
  • Encoder, priority encoder-এর truth table থেকে সূত্র বের করা, আর interrupt বা keyboard-এ এর ব্যবহার
  • Comparator, parity generator/checker, Gray code converter — আর এই সব block বাস্তব IC ও CPU-র ভেতরে কোথায় বসে

১ · বেছে নেওয়া আর ঠিকানা খোঁজা — combinational logic-এর তৈরি block

পর্ব ০৭ থেকে ০৯-এ আপনি Boolean algebra, logic gate আর K-map দিয়ে একটি একটি function সরল করতে শিখেছেন। পর্ব ১১-তে সেই দক্ষতা দিয়ে adder বানানো হয়েছে। কিন্তু একটি বাস্তব CPU নকশা করতে গিয়ে কেউ প্রতিবার শূন্য থেকে gate বসান না — বারবার যে ধরনের কাজ লাগে, তার জন্য কয়েকটি তৈরি block আছে, ঠিক যেমন রাজমিস্ত্রি প্রতিবার মাটি থেকে ইট বানান না।

এই পর্বের সব block-ই combinational circuit — output নির্ভর করে শুধু এই মুহূর্তের input-এর ওপর, আগের কোনো স্মৃতি নেই। প্রতিটির আচরণ তাই একটি truth table দিয়ে পুরোপুরি বলা যায়। কাজ অনুযায়ী এদের চারটি দলে ভাগ করা যায়:

বেছে নেওয়া

Multiplexer (mux) — অনেক input থেকে একটিকে বেছে output-এ পাঠায়। Demultiplexer — একটি input-কে অনেক output-এর একটিতে পাঠায়।

ঠিকানা খোঁজা

Decoder — n বিটের সংখ্যা দেখে ২nটি লাইনের ঠিক একটি চালু করে। মেমরির chip, register, instruction — সবকিছু বাছাইয়ের মূলে এটি।

সংক্ষেপ করা

Encoder / priority encoder — অনেক লাইনের কোনটি চালু, তার নম্বর বের করে। কে আগে সেবা পাবে, তা ঠিক করে।

তুলনা ও যাচাই

Comparator, parity, code converter — দুটি সংখ্যা সমান কি না, কোনটি বড়; ডেটায় ভুল ঢুকেছে কি না; এক কোড থেকে আরেক কোডে।

এই পর্বে লেখার রীতি: কোনো চলকের উল্টো (complement) লেখা হবে A′ (prime) দিয়ে, যেমন S′ মানে “S নয়”। কোডে একই জিনিস ~A বা 1 - a. XOR লেখা হবে ⊕ দিয়ে। Minterm mi মানে truth table-এর i নম্বর সারি, যেখানে সবচেয়ে বাঁয়ের চলক MSB — পর্ব ০৭-এর রীতি।

আগে একটু ঝালিয়ে নিতে চান? একেবারে সহজ ভাষায়, ছবিসহ encoder, decoder, 7-segment আর mux-এর পরিচয় আছে কম্পিউটার ফান্ডামেন্টালস কোর্সের কম্বিনেশনাল সার্কিটের পর্বে। এখানে আমরা সেখান থেকে অনেক গভীরে যাব — প্রমাণ, নকশার পদ্ধতি, পরীক্ষার ধরনের সমস্যা আর CPU-তে ব্যবহার।

২ · Multiplexer — অনেক রাস্তার একটি বেছে নেওয়া

রেলস্টেশনের জংশনে দুটি লাইন এসে মিশেছে একটি লাইনে। একজন কর্মী একটি লিভার টানেন: লিভার নিচে থাকলে বাঁয়ের লাইনের ট্রেন এগোয়, ওপরে থাকলে ডানের লাইনের। লিভার নিজে কোনো ট্রেন চালায় না — শুধু ঠিক করে কোন লাইনের ট্রেন সামনে যাবে। ঘরের টিভির রিমোটও তাই: শত চ্যানেলের সংকেত একসাথে তারে আসছে, আপনি চ্যানেল নম্বর টিপলে পর্দায় শুধু সেটিই দেখায়। Multiplexer ঠিক এই লিভার বা রিমোট — যাকে বাংলায় বলা যায় ডেটা বাছাইকারী (data selector).

2:1 mux — সবচেয়ে ছোট বাছাইকারী

2:1 mux-এর দুটি data input A আর B, একটি select line S, আর একটি output Y. নিয়ম: S = 0 হলে Y = A, S = 1 হলে Y = B. পুরো truth table-এ ৩টি input, তাই ৮টি সারি — কিন্তু সংক্ষেপে লেখা যায় মাত্র দুই সারিতে:

SABYSY (সংক্ষেপে)
00x00A
01x1
1x001B
1x11

এখানে x মানে “যা খুশি থাকুক, ফলে প্রভাব নেই”। সংক্ষিপ্ত টেবিল থেকে সরাসরি সূত্র আসে: Y = 1 হয় যখন (S = 0 এবং A = 1) অথবা (S = 1 এবং B = 1). অর্থাৎ

Y = S′·A + S·B

Gate-এ এটি একটি NOT (S′ বানাতে), দুটি ২-input AND আর একটি OR — মোট চারটি gate. প্রতিটি AND gate একটি “দরজা”: S′ = 1 হলে ওপরের দরজা খোলা, A ভেতর দিয়ে যায়; তখন S = 0 বলে নিচের দরজা বন্ধ, B আটকে যায়। দুটি দরজা কখনো একসাথে খোলা থাকে না, তাই OR gate-এ সবসময় ঠিক একটি সংকেত পৌঁছায়।

        S ──┬──[NOT]── S′
            │           │
   A ───────┼──────[AND]─┐
            │      (S′·A) ├──[OR]── Y = S′A + SB
   B ───────┴──────[AND]─┘
                   (S·B)

4:1 আর 8:1 mux

Input বাড়লে select বিটও বাড়ে: ২nটি input-এর জন্য n টি select বিট। 4:1 mux-এর select S1S0 একটি ২-বিটের binary সংখ্যা — তার মানই বলে কোন input যাবে: 00 → I0, 01 → I1, 10 → I2, 11 → I3. সূত্রে প্রতিটি data input-কে তার নম্বরের minterm দিয়ে AND করা হয়:

Y = S1′S0′·I0 + S1′S0·I1 + S1S0′·I2 + S1S0·I3  =  Σ mi(S)·Ii

এই সাধারণ রূপটি মনে রাখুন: ২n:1 mux = select বিটের সবগুলো minterm, প্রতিটি নিজের data input-এর সাথে AND, তারপর সব মিলিয়ে একটি বড় OR. 8:1 mux-এ ৩টি select, ৮টি ৪-input AND (৩টি select-literal + ১টি data) আর একটি ৮-input OR. দেখুন, এখানে লুকিয়ে আছে একটি decoder — select বিট থেকে minterm বানানোর অংশটি ঠিক অংশ ৪-এর decoder. নিচের stepper-এ select বদলালে কোন AND “দরজা” খোলে, দেখুন:

Mux-এর অবস্থা (I0 = 1, I1 = 0, I2 = 0, I3 = 1 দিয়ে শুরু)
Select S1 S0
খোলা AND “দরজা”
Data I0 I1 I2 I3
Output Y
  1. S1S0 = 00. Decoder অংশে শুধু S1′S0′ = 1, তাই শুধু I0-এর AND দরজা খোলা। I0 = 1, তাই Y = 1. বাকি তিনটি AND-এর output 0, OR-এ তারা কিছু যোগ করে না।
  2. S1S0 = 01. এবার শুধু S1′S0 = 1 — I1-এর দরজা খোলা। I1 = 0, তাই Y = 0. Data একটুও বদলায়নি, শুধু “লিভার” সরেছে।
  3. S1S0 = 10. Select-এর binary মান ২, তাই I2 যায়। I2 = 0, Y = 0. খেয়াল করুন: S1 হলো MSB — 10 মানে দুই, এক নয়।
  4. S1S0 = 11. শুধু m3 = S1S0 = 1, I3 = 1 বলে Y = 1. চারটি select মানে চারটি input একবার করে output-এ এল।
  5. Select একই রেখে I3 বদলাল 1 → 0. খোলা দরজা দিয়ে নতুন মান সঙ্গে সঙ্গে চলে আসে: Y = 0. Mux কোনো মান ধরে রাখে না — এটি combinational, শুধু gate-এর delay-টুকু লাগে।
  6. এবার বাছাই না-করা I1 বদলাল 0 → 1. Y বদলায় না, ০-ই থাকে — কারণ I1-এর দরজা বন্ধ (S1′S0 = 0). বাছাই না-করা input যা খুশি করুক, output-এ তার প্রভাব নেই। এটাই “বাছাই” কথাটির পুরো মানে।

CPU-তে mux সর্বত্র: ALU-র দ্বিতীয় operand register থেকে আসবে নাকি instruction-এর immediate থেকে, পরের PC হবে PC + 4 নাকি branch-এর ঠিকানা, register-এ লেখা হবে ALU-র ফল নাকি মেমরি থেকে পড়া মান — প্রতিটি সিদ্ধান্ত একটি mux. পর্ব ১৩-এ দেখবেন ALU নিজেও যোগ, AND, OR-এর ফল একসাথে হিসাব করে রাখে, তারপর একটি mux দিয়ে দরকারিটি বেছে নেয়। পর্ব ৩২-এর datapath-এ এমন mux আরও অনেক।

৩ · Mux-এর গাছ, আর mux দিয়ে যেকোনো function

ছোট mux জুড়ে বড় mux

বাজারে বা লাইব্রেরিতে যদি শুধু 4:1 mux থাকে, অথচ লাগে 16:1? উত্তর: একটি mux tree. ১৬টি input-কে ৪টি দলে ভাগ করুন; প্রথম স্তরে ৪টি 4:1 mux, প্রত্যেকে নিচের দুটি select বিট S1S0 দিয়ে নিজের দলের একটিকে বাছে। দ্বিতীয় স্তরে আরেকটি 4:1 mux ওপরের দুটি বিট S3S2 দিয়ে চারটি বিজয়ীর একটিকে বাছে। মোট ৫টি 4:1 mux. একইভাবে 8:1 = দুটি 4:1 + একটি 2:1.

নিয়মটি ক্রিকেট টুর্নামেন্টের নকআউটের মতো: প্রতিটি ম্যাচে দুই দল থেকে এক দল এগোয়। ১৬টি দল থেকে একটি চ্যাম্পিয়ন পেতে ঠিক ১৫টি ম্যাচ লাগে, কারণ প্রতিটি ম্যাচে ঠিক একটি দল বাদ পড়ে আর ১৫টি দল বাদ পড়তে হবে। তাই ২n:1 mux বানাতে ২n − ১টি 2:1 mux লাগে, আর গাছের উচ্চতা n স্তর। একই যুক্তিতে k:1 mux দিয়ে N:1 বানাতে (N − 1)/(k − 1)টি mux লাগে, যখন সংখ্যাটি পূর্ণসংখ্যা — 4:1 দিয়ে 16:1-এ (১৬ − ১)/(৪ − ১) = ৫। খেয়াল করুন: নিচের স্তরে যায় select-এর কম গুরুত্বের বিট, ওপরের স্তরে বেশি গুরুত্বের — উল্টে দিলে ভুল input বাছাই হবে।

Mux একটি সর্বজনীন function generator — প্রমাণ

এবার এই পর্বের সবচেয়ে সুন্দর ধারণা। 8:1 mux-এর select-এ যদি তিনটি চলক A, B, C লাগান, তাহলে প্রতিটি সারি ABC-র জন্য ঠিক একটি data input Ii output-এ আসে। এখন Ii-তে সরাসরি সেই সারির F-এর মান (0 বা 1) বসিয়ে দিলে, output হবে ঠিক F. অর্থাৎ truth table-এর output কলামটিই data input-এ বসিয়ে দিন — কোনো সরলীকরণ লাগে না। সূত্রে: Y = Σ mi·Ii, আর Ii = F(i) হলে Y = ΣF(i)=1 mi — যা F-এর canonical SOP (পর্ব ০৭)।

কিন্তু আরও সস্তায় করা যায় — একটি ছোট, ২n−1:1 mux দিয়ে। এর ভিত্তি Shannon expansion: যেকোনো function-কে একটি চলক x ধরে দুই ভাগ করা যায়,

F(…, x) = x′·F(…, 0) + x·F(…, 1)

যুক্তি: x = 0 বসালে ডান দিক হয় ১·F(…,0) + ০ = F(…,0); x = 1 বসালে ০ + F(…,1). x-এর দুটি মানেই দুই দিক সমান, তাই সূত্রটি সবসময় সত্য। এবার select-এ প্রথম n−1টি চলক দিন। প্রতিটি select মানের জন্য বাকি থাকে শুধু x-এর একটি function — আর এক চলকের function মাত্র চার রকম হতে পারে: সবসময় 0, সবসময় 1, x, অথবা x′. তাই প্রতিটি data input-এ এই চারটির একটি বসালেই কাজ শেষ। কোনটি বসবে, তা ঠিক হয় truth table-এর জোড়া সারি দেখে:

F যখন x = 0F যখন x = 1Data input-এ বসবে
000 (ground)
111 (VCC)
01x
10x′ (একটি NOT gate লাগবে)

কাজ করা উদাহরণ — F(A, B, C) = Σm(1, 3, 5, 6)

প্রথমে truth table-এর F কলাম: সারি ০ থেকে ৭-এ 0 1 0 1 0 1 1 0.

(ক) 8:1 mux দিয়ে: A, B, C → S2, S1, S0. Data: I0 = 0, I1 = 1, I2 = 0, I3 = 1, I4 = 0, I5 = 1, I6 = 1, I7 = 0. অর্থাৎ I1, I3, I5, I6-কে 1-এ (VCC), বাকিগুলো 0-তে (ground).

(খ) 4:1 mux দিয়ে: A, B → S1, S0, আর C যাবে data-তে। নিচের stepper-এ জোড়ায় জোড়ায় সারি দেখে প্রতিটি data input বের করা হলো:

নকশার খাতা
যে সারিগুলো দেখছি
F যখন C = 0 / C = 1
সিদ্ধান্ত
Data I0 I1 I2 I3
  1. শুরু। F-এর কলাম 0 1 0 1 0 1 1 0. প্রথম দুটি চলক A, B যাবে S1, S0-তে; শেষ চলক C যাবে data-তে। প্রতিটি AB মানের জন্য দুটি সারি (C = 0 আর C = 1) একসাথে দেখতে হবে।
  2. AB = 00 (সারি m0, m1). C = 0-তে F = 0, C = 1-এ F = 1 — F ঠিক C-এর মতো আচরণ করছে। তাই I0-তে সরাসরি C-এর তার।
  3. AB = 01 (সারি m2, m3). আবার 0 আর 1 — F = C, তাই I1 = C.
  4. AB = 10 (সারি m4, m5). F = 0, 1 — I2 = C. তিনটি input-এই একই তার, তাই একটি তার তিন জায়গায় শাখা করে দিলেই হয়।
  5. AB = 11 (সারি m6, m7). এবার উল্টো: C = 0-তে F = 1, C = 1-এ F = 0 — অর্থাৎ F = C′. I3-তে একটি NOT gate দিয়ে C′ বসাতে হবে। নকশা শেষ: একটি 4:1 mux আর একটি NOT.
  6. যাচাই। ABC = 110: select = 11, Y = I3 = C′ = 1 — টেবিলে m6 = 1. ABC = 101: select = 10, Y = I2 = C = 1 — টেবিলে m5 = 1. সূত্রে Y = A′B′C + A′BC + AB′C + ABC′ = Σm(1,3,5,6). নিচের কোড আটটি সারিই মিলিয়ে দেখায়।
পরীক্ষার খাতায় যেভাবে লিখবেন — implementation table

অনেক বইয়ে একই কাজ একটি টেবিলে দেখানো হয়: ওপরে কলাম I0 … I3, নিচে দুই সারি — C′ সারিতে minterm ০, ২, ৪, ৬ আর C সারিতে ১, ৩, ৫, ৭. F-এর minterm-গুলোকে বৃত্ত দিন (১, ৩, ৫, ৬)। কোনো কলামে দুটিই বৃত্ত হলে 1, কোনোটিই না হলে 0, শুধু নিচেরটি হলে C, শুধু ওপরেরটি হলে C′. এখানে কলাম I3-এ শুধু ৬ বৃত্ত (ওপরের সারি), তাই I3 = C′. কোন চলককে data-তে দেবেন তা আপনার পছন্দ — প্রশ্নে বলা না থাকলে সাধারণত শেষ চলক (LSB).

একটি বোনাস তথ্য: 2:1 mux নিজেই functionally complete — শুধু 2:1 mux আর ধ্রুবক 0, 1 দিয়ে যেকোনো circuit বানানো যায়, ঠিক NAND-এর মতো (পর্ব ০৮)। NOT A = mux(S = A, I0 = 1, I1 = 0); A·B = mux(S = A, I0 = 0, I1 = B); A + B = mux(S = A, I0 = B, I1 = 1). NOT, AND, OR পাওয়া গেলে বাকি সব পাওয়া যায়।

এবার হাতের পদ্ধতিটি যন্ত্রে চালিয়ে দেখুন। নিচের synthesiser একটি minterm তালিকা থেকে ২n−1:1 mux-এর data input বের করে (0/1/x/x′), তারপর mux-টি simulate করে truth table-এর প্রতিটি সারির সাথে মেলায়। তিনটি function দিয়ে পরীক্ষা করা হয়েছে — নিজের minterm দিয়েও বদলে দেখুন:

// Any n-variable function with ONE 2^(n-1):1 mux.
// First n-1 variables drive the select lines; the last variable x feeds data inputs.
function synth(n, minterms) {
  const on = new Set(minterms), inputs = [];
  for (let i = 0; i < 2 ** (n - 1); i++) {
    const f0 = on.has(2 * i), f1 = on.has(2 * i + 1);      // F at x=0 and at x=1
    inputs.push(f0 && f1 ? "1" : !f0 && !f1 ? "0" : f1 ? "x" : "x'");
  }
  return inputs;
}
function runMux(inputs, v) {                // v = all n inputs as one integer
  const sel = v >> 1, x = v & 1;            // top n-1 bits select, LSB is x
  const d = inputs[sel];
  return d === "1" ? 1 : d === "0" ? 0 : d === "x" ? x : 1 - x;
}
const NAMES = "ABCD";
const tests = [[3, [1, 3, 5, 6]], [3, [3, 5, 6, 7]], [4, [0, 1, 3, 4, 8, 9, 15]]];
for (const [n, mt] of tests) {
  const ins = synth(n, mt), x = NAMES[n - 1];
  console.log(`F(${NAMES.slice(0, n).split("").join(",")}) = Sum m(${mt.join(",")})`);
  console.log(`  ${2 ** n}:1 mux data I0..I${2 ** n - 1} = ` +
    [...Array(2 ** n)].map((_, v) => (mt.includes(v) ? 1 : 0)).join(" "));
  console.log(`  ${2 ** (n - 1)}:1 mux, select = ${NAMES.slice(0, n - 1)}, x = ${x}:`);
  console.log("    " + ins.map((d, i) => `I${i}=${d.replace("x", x)}`).join("  "));
  let bad = 0;
  for (let v = 0; v < 2 ** n; v++)
    if (runMux(ins, v) !== (mt.includes(v) ? 1 : 0)) bad++;
  console.log(`  simulated all ${2 ** n} rows: ${bad === 0 ? "matches truth table" : bad + " mismatches"}`);
}

আউটপুট (নির্ধারিত — প্রতিবার একই):

F(A,B,C) = Sum m(1,3,5,6)
  8:1 mux data I0..I7 = 0 1 0 1 0 1 1 0
  4:1 mux, select = AB, x = C:
    I0=C  I1=C  I2=C  I3=C'
  simulated all 8 rows: matches truth table
F(A,B,C) = Sum m(3,5,6,7)
  8:1 mux data I0..I7 = 0 0 0 1 0 1 1 1
  4:1 mux, select = AB, x = C:
    I0=0  I1=C  I2=C  I3=1
  simulated all 8 rows: matches truth table
F(A,B,C,D) = Sum m(0,1,3,4,8,9,15)
  16:1 mux data I0..I15 = 1 1 0 1 1 0 0 0 1 1 0 0 0 0 0 1
  8:1 mux, select = ABC, x = D:
    I0=1  I1=D  I2=D'  I3=0  I4=1  I5=0  I6=0  I7=D
  simulated all 16 rows: matches truth table

প্রথম function-এর ফল I0=C I1=C I2=C I3=C' — stepper-এ হাতে পাওয়া উত্তরের হুবহু মিল, আর 8:1 সংস্করণের data 0 1 0 1 0 1 1 0 ঠিক F-এর কলাম। দ্বিতীয়টি তিন চলকের majority function (অন্তত দুটি 1 হলে 1): I0=0 I1=C I2=C I3=1 — AB = 00 হলে C যা-ই হোক সংখ্যাগরিষ্ঠতা হয় না, AB = 11 হলে সবসময় হয়। তৃতীয়টি চার চলকের: একটি 8:1 mux-এ I2=D' আর I7=D দুই রকম তারই লাগল, আর I3, I5, I6 সরাসরি ground. প্রতিটি ক্ষেত্রে “matches truth table” মানে সব ২nটি সারিতে simulate করা output আসল F-এর সমান — হাতের নকশা যাচাইয়ের এটাই নিরাপদ পথ।

৪ · Demultiplexer আর decoder — একটি থেকে অনেক

Demultiplexer — উল্টো দিকের লিভার

Mux অনেক থেকে একটি বাছে; demultiplexer (demux) একটি input D-কে n টি select বিট দিয়ে ২nটি output-এর একটিতে পাঠায়, বাকি সব output 0 থাকে। ডাকঘরের বাছাইকারীর মতো: একটি বস্তা থেকে প্রতিটি চিঠি পিনকোড দেখে ঠিক একটি খোপে যায়। 1:4 demux-এর সূত্র:

Y0 = S1′S0′·D,  Y1 = S1′S0·D,  Y2 = S1S0′·D,  Y3 = S1S0·D

অর্থাৎ Yi = mi(S)·D. এবার D-কে স্থায়ীভাবে 1 করে দিন: Yi = mi(S) — প্রতিটি output শুধু একটি minterm. এই circuit-টির নামই decoder. উল্টো দিক থেকে বললে: enable input থাকা একটি decoder-ই demultiplexer — data-কে enable-এ দিন, ঠিকানাকে input-এ। এজন্য বাণিজ্যিক IC-র নামেও প্রায়ই একসাথে লেখা থাকে “decoder/demultiplexer”.

2:4 আর 3:8 decoder — প্রতিটি output একটি minterm

n:২n decoder-এর n টি input, আর ২nটি output-এর ঠিক একটি 1 — input-এর binary মান যত, সেই নম্বরের output. এমন “ঠিক একটি 1” নকশাকে বলে one-hot. 3:8 decoder-এ Y5 = A2A1′A0 = m5, আর Y0 = A2′A1′A0′ = m0. গঠন সোজা: n টি NOT gate (প্রতিটি input-এর উল্টো বানাতে) আর ২nটি n-input AND.

প্রায় সব বাস্তব decoder-এ একটি enable input (EN) থাকে: EN = 0 হলে সব output 0, input যা-ই হোক; EN = 1 হলে স্বাভাবিক কাজ। প্রতিটি AND gate-এ একটি বাড়তি input হিসেবে EN যোগ করলেই হয়: Yi = EN·mi. এই ছোট্ট সুবিধাটিই decoder জুড়ে বড় decoder বানানোর চাবি। আরেকটি বাস্তব কথা: অনেক IC-তে output আর enable active-low — বাছাই করা লাইনটি 0 হয়, বাকিগুলো 1 (নামের ওপর দাগ বা শেষে N দিয়ে বোঝানো হয়)। যুক্তি একই, শুধু চিহ্ন উল্টো; ডেটাশিট দেখে নিশ্চিত হওয়া জরুরি।

নিচের simulator decoder আর mux দুটোকেই gate-এর স্তরে বানায় — decoder প্রতিটি output-এর জন্য enable আর n টি literal-এর AND করে, আর mux সেই minterm-গুলোকে data-র সাথে AND করে OR করে। অর্থাৎ mux-এর ভেতরে সত্যিই একটি decoder বসানো:

// Gate-level mux and decoder: a decoder makes minterms, a mux ANDs them with data.
function decoder(sel, en = 1) {            // sel = [S(n-1) ... S0]
  const n = sel.length, out = [];
  for (let i = 0; i < 2 ** n; i++) {
    let m = en;                              // AND gate: enable and n literals
    for (let k = 0; k < n; k++) {
      const want = (i >> (n - 1 - k)) & 1;
      m &= want ? sel[k] : 1 - sel[k];       // literal S or S'
    }
    out.push(m);                             // output i = minterm m_i
  }
  return out;
}
function mux(data, sel) {                    // Y = OR of (m_i AND I_i)
  return decoder(sel).reduce((y, m, i) => y | (m & data[i]), 0);
}
const bits = (v, n) => [...Array(n)].map((_, k) => (v >> (n - 1 - k)) & 1);

console.log("4:1 mux   S1 S0 | Y");
let ok = 0;
for (let s = 0; s < 4; s++) {
  for (let d = 0; d < 16; d++) {             // try all 16 data patterns
    const data = bits(d, 4).reverse();       // data[i] = I_i
    if (mux(data, bits(s, 2)) === data[s]) ok++;
  }
  console.log(`           ${bits(s, 2).join("  ")}  | I${s}`);
}
console.log(`checked ${ok} of 64 cases: Y always equals the selected input`);

console.log("\n3:8 decoder  EN A2 A1 A0 | Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7");
console.log("              0  x  x  x |", decoder([0, 0, 0], 0).join("  "));
for (let v = 0; v < 8; v++)
  console.log(`              1  ${bits(v, 3).join("  ")} |`, decoder(bits(v, 3), 1).join("  "));

আউটপুট (নির্ধারিত):

4:1 mux   S1 S0 | Y
           0  0  | I0
           0  1  | I1
           1  0  | I2
           1  1  | I3
checked 64 of 64 cases: Y always equals the selected input

3:8 decoder  EN A2 A1 A0 | Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7
              0  x  x  x | 0  0  0  0  0  0  0  0
              1  0  0  0 | 1  0  0  0  0  0  0  0
              1  0  0  1 | 0  1  0  0  0  0  0  0
              1  0  1  0 | 0  0  1  0  0  0  0  0
              1  0  1  1 | 0  0  0  1  0  0  0  0
              1  1  0  0 | 0  0  0  0  1  0  0  0
              1  1  0  1 | 0  0  0  0  0  1  0  0
              1  1  1  0 | 0  0  0  0  0  0  1  0
              1  1  1  1 | 0  0  0  0  0  0  0  1

4:1 mux-এর টেবিল দেখাচ্ছে S1S0-এর প্রতিটি মান কোন input বাছে, আর checked 64 of 64 cases মানে ৪টি select × ১৬টি data-নকশা — সব মিলিয়ে ৬৪টি অবস্থায় gate-স্তরের mux ঠিক বাছাই করা input-টিই দিয়েছে। 3:8 decoder-এর টেবিলে প্রথম সারি EN = 0: সব output 0, input x x x (প্রভাব নেই)। বাকি ৮ সারিতে 1-টি একটি সিঁড়ির মতো তির্যকভাবে নেমেছে — প্রতি সারিতে ঠিক একটি 1, তার অবস্থান = A2A1A0-এর দশমিক মান। এটাই one-hot, আর প্রতিটি কলাম Yi ঠিক minterm mi-এর truth table.

কাজ করা উদাহরণ — দুটি 3:8 বা পাঁচটি 2:4 দিয়ে 4:16 decoder

দুটি 3:8 দিয়ে: চার input A, B, C, D (A = MSB). B, C, D দুটি decoder-এরই input-এ সমান্তরালে যায়। A ঠিক করে কোনটি চালু: নিচের decoder-এর EN = A′ (একটি NOT দিয়ে), ওপরের decoder-এর EN = A. A = 0 হলে নিচের decoder Y0–Y7 দেয়, A = 1 হলে ওপরেরটি Y8–Y15. যেমন ABCD = 1011: A = 1, তাই ওপরের decoder চালু, BCD = 011 বলে তার ৩ নম্বর output — অর্থাৎ সামগ্রিক Y11. ঠিক, কারণ 1011 = ১১। (বাস্তব 74138-এর তিনটি enable থাকায় এখানে বাইরের NOT-ও লাগে না — একটির active-low enable-এ A, অন্যটির active-high enable-এ A.)

পাঁচটি 2:4 দিয়ে: এটি একটি দুই স্তরের গাছ। প্রথম স্তরে একটি 2:4 decoder ওপরের দুটি বিট AB নেয়; তার চারটি output চারটি দ্বিতীয়-স্তরের 2:4 decoder-এর EN-এ যায়। দ্বিতীয় স্তরের চারটিই CD নেয়। AB = 10 হলে শুধু তৃতীয় decoder (নম্বর ২) চালু, আর CD = 11 হলে তার ৩ নম্বর output — সামগ্রিক নম্বর ২ × ৪ + ৩ = ১১। মোট ১ + ৪ = ৫টি. সাধারণ নিয়ম: k:২k decoder দিয়ে বড় decoder বানাতে শেষ স্তরে (মোট output ÷ ২k)টি, তার ওপর enable বণ্টনের জন্য আরেকটি ছোট গাছ।

কাজ করা উদাহরণ — decoder আর OR gate দিয়ে function

Decoder-এর প্রতিটি output একটি minterm, আর যেকোনো function হলো তার minterm-গুলোর OR (canonical SOP). তাই একটি n:২n decoder আর প্রতিটি output function-এর জন্য একটি OR gate — যত function-ই হোক, decoder একটিই। সবচেয়ে প্রচলিত উদাহরণ full adder (পর্ব ১১): input A, B, Cin.

  • Sum S = 1 হয় যখন বিজোড় সংখ্যক input 1: S = Σm(1, 2, 4, 7) → Y1, Y2, Y4, Y7 একটি ৪-input OR-এ।
  • Carry Cout = 1 হয় যখন অন্তত দুটি input 1: Cout = Σm(3, 5, 6, 7) → Y3, Y5, Y6, Y7 আরেকটি ৪-input OR-এ।

একটি 3:8 decoder, দুটি OR — পুরো full adder. Y7 দুটি OR-এই যায়, এতে কোনো সমস্যা নেই: একটি output অনেক gate-কে চালাতে পারে (fan-out-এর সীমার মধ্যে)। কোনো function-এ অর্ধেকের বেশি minterm থাকলে উল্টো কৌশল সস্তা: F′-এর minterm-গুলো একটি NOR-এ দিন। Active-low output-এর decoder হলে OR-এর জায়গায় NAND বসে — কারণ De Morgan অনুযায়ী (Y1′·Y2′·…)′ = Y1 + Y2 + …

৫ · Address decoding আর 7-segment — decoder বাস্তবে

মহল্লার কথা ভাবুন। চিঠিতে লেখা “রোড ৩, বাড়ি ১২”. ডাকপিয়ন প্রথমে রোড নম্বর দেখে সঠিক গলিতে ঢোকেন, তারপর বাড়ি নম্বর দেখে দরজা খোঁজেন। কম্পিউটারের মেমরিও এভাবে সাজানো: CPU একটি ঠিকানা (address) পাঠায়; ঠিকানার ওপরের কয়েকটি বিট ঠিক করে কোন chip (গলি), আর নিচের বিটগুলো সেই chip-এর ভেতরের কোন byte (বাড়ি)। ওপরের বিটগুলো থেকে chip বাছাইয়ের কাজটি করে একটি decoder — একে বলে address decoding, আর প্রতিটি chip-এর যে input-এ decoder-এর লাইন যায় তার নাম chip select (CS).

কাজ করা উদাহরণ — চারটি 16 KiB chip, 64 KiB ঠিকানা

ধরুন একটি ছোট ৮-বিট ধরনের computer-এর ১৬টি address লাইন A15…A0, অর্থাৎ ২১৬ = ৬৫,৫৩৬টি ঠিকানা (64 KiB). আমাদের কাছে চারটি 16 KiB-এর মেমরি chip. প্রতিটি chip-এর ভেতরে ঠিকানা লাগে ২১৪ = 16 KiB-এর জন্য ১৪টি বিট — A13…A0, যা চারটি chip-এ সমান্তরালে যায়। বাকি ওপরের দুটি বিট A15A14 যায় একটি 2:4 decoder-এ, যার চারটি output চারটি chip-এর CS-এ। এর ফলে যে ঠিকানা-বণ্টন দাঁড়ায়, তাকে বলে memory map:

A15 A14Decoder outputঠিকানার পরিসরউদাহরণ ঠিকানা
0 0CS00x0000–0x3FFF0x1234 → chip 0, ভেতরে 0x1234
0 1CS10x4000–0x7FFF0x4000 → chip 1, ভেতরে 0x0000
1 0CS20x8000–0xBFFF0x9ABC → chip 2, ভেতরে 0x1ABC
1 1CS30xC000–0xFFFF0xFFFF → chip 3, ভেতরে 0x3FFF

Hex-এ ব্যাপারটি চোখে পড়ে: ঠিকানার প্রথম hex অঙ্ক 0–3 হলে chip 0, 4–7 হলে chip 1, 8–B হলে chip 2, C–F হলে chip 3 — কারণ প্রথম hex অঙ্কের ওপরের দুটি বিটই A15A14. ভেতরের offset পেতে ঠিকানা থেকে ওপরের দুটি বিট মুছে দিন (addr & 0x3FFF).

Partial decoding-এর ফাঁদ — একই chip দুই ঠিকানায়। খরচ বাঁচাতে নকশাকার যদি কোনো address বিট decoder-এ না দেন, সেই বিটের দুই মানেই একই chip সাড়া দেয়। যেমন 8 KiB chip (ভেতরে A12…A0) কিন্তু decoder-এ শুধু A15A14, A13 কোথাও যুক্ত নয় — তাহলে chip 0 সাড়া দেবে 0x0000–0x1FFF আর 0x2000–0x3FFF দুই জায়গাতেই। এই নকল ঠিকানাকে বলে alias বা mirror. সমস্যা না হলেও ঠিকানার জায়গা নষ্ট হয়, আর ভুলে দুটি device একই ঠিকানায় বসে গেলে দুজনেই একসাথে bus-এ মান পাঠাতে চায় — তখন পড়া মান অর্থহীন।

আধুনিক computer-এ এই কাজ আরও বড় পরিসরে হয় — DRAM-এর ভেতরে row আর column decoder, chipset বা SoC-এর ভেতরে ঠিকানা দেখে RAM, ROM নাকি কোনো device — সব একই নীতিতে। বাস্তব মেমরির address decoding দেখবেন পর্ব ৪৯-এ (Main memory), আর device-গুলোকে ঠিকানা দেওয়ার memory-mapped I/O ও port I/O-র decoding পর্ব ৫১-এ (I/O আর bus). নিচের অংশ ৬-এর simulator-এ এই memory map-টি কোডে চালিয়ে দেখবেন।

7-segment display decoder — ক্যালকুলেটরের অঙ্ক

বাজারের ক্যালকুলেটর বা ডিজিটাল ঘড়ির প্রতিটি অঙ্ক সাতটি দাগে (segment a থেকে g) তৈরি — এটি 7-segment display. একটি BCD অঙ্ক (৪ বিট, ০–৯) থেকে কোন দাগগুলো জ্বলবে তা বের করার circuit-কে 7-segment decoder বলে। নামে decoder হলেও এটি one-hot নয় — একটি ৪-input, ৭-output code converter, যার প্রতিটি output আলাদা একটি function. যেমন ওপরের দাগ a জ্বলে ০, ২, ৩, ৫, ৬, ৭, ৮, ৯-এ (১ আর ৪-এ নয়)। ১০–১৫ BCD-তে কখনো আসে না, তাই সেগুলো don't care. পর্ব ০৯-এর K-map দিয়ে don't care কাজে লাগিয়ে পাওয়া যায় (A = MSB):

a = A + C + BD + B′D′

৭টি segment-এর জন্য ৭টি এমন সূত্র — K-map অনুশীলনের ক্লাসিক সমস্যা, পর্ব ০৯-এ বিস্তারিত। ল্যাবে এর জন্য তৈরি IC-ও পাওয়া যায় (যেমন 7447 বা 4511 ধরনের BCD-to-7-segment decoder).

৬ · Encoder আর priority encoder — অনেক লাইন থেকে একটি নম্বর

Encoder decoder-এর উল্টো: ২nটি input লাইনের কোনটি চালু, তার n-বিটের নম্বর বের করে। 4:2 encoder-এর input D0…D3, output Y1Y0. যদি ধরে নিই একবারে ঠিক একটি input 1, তাহলে Y1 = 1 হয় D2 বা D3 চালু হলে, Y0 = 1 হয় D1 বা D3 চালু হলে:

Y1 = D2 + D3,   Y0 = D1 + D3

8:3 encoder-ও একই রকম: Y2 = D4 + D5 + D6 + D7, Y1 = D2 + D3 + D6 + D7, Y0 = D1 + D3 + D5 + D7 — প্রতিটি output বিট সেই input-গুলোর OR, যাদের নম্বরে ওই বিট 1. মাত্র তিনটি OR gate!

দুটি সমস্যা — একসাথে কয়েকজন, আর কেউ না

কিন্তু “ঠিক একটি input 1” অনুমানটি বাস্তবে টেকে না। সমস্যা ১: D1 আর D2 একসাথে 1 হলে সাধারণ 4:2 encoder দেয় Y1Y0 = 11 — অর্থাৎ ৩, যে লাইন আদৌ চালু নয়! সমস্যা ২: কোনো input চালু না থাকলে output 00 — আর D0 চালু থাকলেও 00. দুটি সম্পূর্ণ ভিন্ন অবস্থা একই রকম দেখায়।

সমাধান priority encoder: প্রতিটি input-এর একটি অগ্রাধিকার আছে (সাধারণত বড় নম্বর = বেশি অগ্রাধিকার), আর একাধিক চালু থাকলে output হয় সবচেয়ে উঁচু চালু input-এর নম্বর। সাথে একটি বাড়তি output V (valid): অন্তত একটি input চালু থাকলে V = 1. ব্যাংকের লাইনে একজন বয়স্ক মানুষ বা প্রতিবন্ধী গ্রাহক এলে তাঁকে আগে ডাকা হয় — বাকিরা যতক্ষণ থাকুন, কাউন্টার একবারে একজনকেই ডাকে, আর লাইন ফাঁকা হলে “এখন কেউ নেই” বাতি জ্বলে।

D3D2D1D0Y1Y0V
0000xx0
0001001
001x011
01xx101
1xxx111

টেবিলে দুই ধরনের x আছে। Input-এর x মানে “এই নিচু অগ্রাধিকারের লাইন যা খুশি থাকুক” — তাই পাঁচ সারিতেই ১৬টি সারি ঢাকা পড়েছে। Output-এর x মানে don't care: V = 0 হলে Y কেউ পড়ে না। সূত্র বের করা: Y1 = 1 শেষ দুই সারিতে: D3 + D3′D2 = D3 + D2 (absorption: X + X′Y = X + Y). Y0 = 1 তৃতীয় আর পঞ্চম সারিতে: D3′D2′D1 + D3 = D3 + D2′D1. আর V = D0 + D1 + D2 + D3.

Y1 = D3 + D2,   Y0 = D3 + D2′D1,   V = D3 + D2 + D1 + D0

সাধারণ encoder-এর সাথে পার্থক্য শুধু Y0-এ D2′ — “D1 তখনই গোনা হবে যখন তার চেয়ে উঁচু D2 নীরব”। একই যুক্তিতে 8:3 priority encoder (I7 সবচেয়ে উঁচু):

  • Y2 = I7 + I6 + I5 + I4
  • Y1 = I7 + I6 + I5′I4′(I3 + I2)
  • Y0 = I7 + I6′I5 + I6′I4′I3 + I6′I4′I2′I1
  • V = I0 + I1 + … + I7

Y0-এর পদগুলো পড়ে দেখুন: I5 গোনা হয় যদি I6 নীরব থাকে (I7 চালু হলে প্রথম পদই 1 করে দেয়, তাই I7′ লেখার দরকার হয়নি); I3 গোনা হয় যদি I6 আর I4 নীরব (I5 চালু থাকলে Y0 এমনিতেই 1). এই ছোট সরলীকরণগুলোই পরীক্ষায় সূত্র ছোট করার কৌশল। এবার একটি বাস্তব দৃশ্যে দেখুন priority encoder একসাথে আসা অনুরোধ কীভাবে সামলায়:

আটটি device-এর অনুরোধ লাইন (I7 সবচেয়ে উঁচু অগ্রাধিকার)
অনুরোধ I7 … I0
চালু লাইন
বিজয়ী
Y2 Y1 Y0
V
  1. সব নীরব। কোনো অনুরোধ নেই, তাই V = 0. Y-এর মান অর্থহীন (অনেক chip তখন 000 দেয়) — V না দেখে Y পড়লে মনে হতো I0 অনুরোধ করেছে। এজন্যই V বিট।
  2. I2-এর অনুরোধ এল (ধরুন keyboard). একমাত্র চালু লাইন I2, তাই Y = 010 (২), V = 1. CPU জানল ২ নম্বর device সেবা চাইছে।
  3. I2 অপেক্ষায় থাকতেই I5 এল (ধরুন disk). এখন দুটি লাইন চালু। সাধারণ encoder দিত 010 OR 101 = 111 — ভুল। Priority encoder সবচেয়ে উঁচু চালুটি নেয়: Y = 101 (৫)।
  4. এবার I7-ও এল (ধরুন timer). তিনটি চালু, সবচেয়ে উঁচু I7 — Y = 111 (৭)। নিচের I5 আর I2 বাদ যায়নি, শুধু অপেক্ষা করছে।
  5. I7-এর সেবা শেষ, লাইন নামল। বাকি I5, I2 — Y আবার 101. কোনো স্মৃতি লাগেনি; output প্রতিবার শুধু এই মুহূর্তের input দেখে।
  6. I5-এর সেবাও শেষ। বাকি শুধু I2 — Y = 010. সবচেয়ে নিচের অগ্রাধিকারের device সবার শেষে সেবা পেল।
  7. I2-ও শেষ। আবার সব নীরব, V = 0. খেয়াল করুন: উঁচু অগ্রাধিকারের device বারবার অনুরোধ করতে থাকলে I2 হয়তো কখনোই সুযোগ পেত না — এই “না খেয়ে থাকা” (starvation) এড়াতে বাস্তব interrupt controller-এ ঘুরিয়ে-অগ্রাধিকারের মতো কৌশলও থাকে।

এই কারণেই priority encoder interrupt সামলানোর hardware-এর একটি মূল অংশ: কয়েকটি device একসাথে CPU-র মনোযোগ চাইলে কে আগে পাবে, তা এক নিমেষে ঠিক হয়ে যায় — বিস্তারিত পর্ব ৫২-এ (Interrupt আর DMA). আরেকটি পুরোনো ব্যবহার keyboard: একটি চাপা চাবিকে তার কোডে বদলানো, আর একসাথে দুটি চাবি চাপলে একটিকে বেছে নেওয়া। (আধুনিক keyboard সাধারণত একটি microcontroller দিয়ে চাবির matrix বারবার scan করে, কিন্তু ধারণা একই।) Floating point-এর normalization-এ “প্রথম 1 বিটটি কোথায়” খোঁজার leading-zero counter-ও আসলে একটি বড় priority encoder (পর্ব ২৪)।

নিচের কোড প্রথমে হাতে বের করা 8:3 সূত্রগুলোকে ২৫৬টি সম্ভাব্য input-এ আচরণের সাথে মেলায়, তারপর কয়েকটি নকশায় encoder চালায়, শেষে অংশ ৫-এর memory map-এর decoder চালায়:

// 8:3 priority encoder (I7 = highest priority) checked against its gate equations,
// then a 2:4 decoder on A15 A14 choosing one of four 16 KiB memory chips.
function priorityEncoder(I) {               // I[k] = request on input k
  for (let k = 7; k >= 0; k--) if (I[k]) return { y: k, v: 1 };
  return { y: 0, v: 0 };                     // no request: V = 0, Y meaningless
}
function gates(I) {                          // equations derived by hand
  const n = (b) => 1 - b;
  const y2 = I[7] | I[6] | I[5] | I[4];
  const y1 = I[7] | I[6] | (n(I[5]) & n(I[4]) & (I[3] | I[2]));
  const y0 = I[7] | (n(I[6]) & I[5]) | (n(I[6]) & n(I[4]) & I[3]) | (n(I[6]) & n(I[4]) & n(I[2]) & I[1]);
  return 4 * y2 + 2 * y1 + y0;
}
let agree = 0;
for (let p = 0; p < 256; p++) {
  const I = [...Array(8)].map((_, k) => (p >> k) & 1);
  const r = priorityEncoder(I);
  if (!r.v || gates(I) === r.y) agree++;
}
console.log(`gate equations agree with behaviour on ${agree}/256 inputs`);
console.log("I7..I0     Y2Y1Y0  V");
for (const s of ["00000000", "00000001", "00010100", "01100000", "00001010", "10000001"]) {
  const I = s.split("").reverse().map(Number);    // string is I7..I0
  const r = priorityEncoder(I);
  console.log(`${s}   ${r.v ? r.y.toString(2).padStart(3, "0") : "xxx"}     ${r.v}`);
}
const hex = (a) => "0x" + a.toString(16).toUpperCase().padStart(4, "0");
console.log("\nchip  A15 A14  range");
for (let c = 0; c < 4; c++)
  console.log(` CS${c}    ${c >> 1}   ${c & 1}   ${hex(c << 14)}-${hex((c << 14) | 0x3FFF)}`);
for (const a of [0x1234, 0x3FFF, 0x4000, 0x9ABC, 0xC000, 0xFFFF]) {
  const top = a >> 14;                       // decoder input = A15 A14
  console.log(`${hex(a)} -> CS${top} active, offset inside chip ${hex(a & 0x3FFF)}`);
}

আউটপুট (নির্ধারিত):

gate equations agree with behaviour on 256/256 inputs
I7..I0     Y2Y1Y0  V
00000000   xxx     0
00000001   000     1
00010100   100     1
01100000   110     1
00001010   011     1
10000001   111     1

chip  A15 A14  range
 CS0    0   0   0x0000-0x3FFF
 CS1    0   1   0x4000-0x7FFF
 CS2    1   0   0x8000-0xBFFF
 CS3    1   1   0xC000-0xFFFF
0x1234 -> CS0 active, offset inside chip 0x1234
0x3FFF -> CS0 active, offset inside chip 0x3FFF
0x4000 -> CS1 active, offset inside chip 0x0000
0x9ABC -> CS2 active, offset inside chip 0x1ABC
0xC000 -> CS3 active, offset inside chip 0x0000
0xFFFF -> CS3 active, offset inside chip 0x3FFF

প্রথম লাইন 256/256 — হাতে বের করা Y2, Y1, Y0-এর gate-সূত্র প্রতিটি সম্ভাব্য input-এ (V = 1 যেখানে) “সবচেয়ে উঁচু চালু লাইন” নিয়মের সাথে মিলেছে; একটি পদও ভুল হলে এই সংখ্যা কমত। নমুনাগুলোতে 00010100 (I4, I2 চালু) → 100, 01100000 (I6, I5) → 110, আর 10000001 → 111 — সবচেয়ে নিচের I0-কে কেউ দেখছেই না। সব শূন্যে xxx 0: V = 0. Memory map অংশে প্রতিটি chip-এর পরিসর অংশ ৫-এর টেবিলের সাথে মেলে; 0x3FFF chip 0-এর শেষ byte আর ঠিক পরের 0x4000 chip 1-এর প্রথম byte (offset 0x0000) — সীমানা ঠিক জায়গায়।

৭ · Comparator, parity আর code converter

সমতা — XNOR দিয়ে

দুটি বিট A আর B সমান কি না, তা বলে XNOR: A ⊙ B = (A ⊕ B)′ = A′B′ + AB — দুটি সমান হলে 1. দুটি n-বিটের সংখ্যা সমান হয় যখন প্রতিটি অবস্থানের বিট সমান। তাই প্রতিটি বিটজোড়ার জন্য xi = Ai ⊙ Bi, আর সমতা E = xn−1·…·x1·x0 — n টি XNOR আর একটি AND. Cache-এ tag মেলানো (পর্ব ৪৫) ঠিক এই রকম একটি বড় equality comparator.

Magnitude comparator — কোনটি বড়

দুটি সংখ্যা তুলনা আমরা যেভাবে করি, magnitude comparator-ও তাই করে: বাঁ দিক (MSB) থেকে শুরু করুন; প্রথম যে অবস্থানে বিট আলাদা, সেখানে যার 1, সে-ই বড়। ২-বিটের A = A1A0, B = B1B0-এর জন্য:

G (A > B) = A1B1′ + x1·A0B0′
L (A < B) = A1′B1 + x1·A0′B0
E (A = B) = x1·x0

প্রথম পদ: MSB-তেই A-র 1, B-র 0 — A বড়, আর কিছু দেখার দরকার নেই। দ্বিতীয় পদ: MSB সমান (x1 = 1), তখন পরের বিট ঠিক করে। ৪-বিটে একই ছক আরও দুই ধাপ লম্বা হয়:

G = A3B3′ + x3A2B2′ + x3x2A1B1′ + x3x2x1A0B0′,   E = x3x2x1x0,   L = (G + E)′

L-কে আলাদা করে বানানোর দরকার নেই: তিনটি অবস্থার ঠিক একটি সত্য, তাই G আর E দুটিই 0 হলে L = 1. (এই সূত্রগুলো ১৬ × ১৬ = ২৫৬টি জোড়ায় script দিয়ে যাচাই করা হয়েছে।)

Cascading — বড় সংখ্যার তুলনা: ৮-বিট তুলনার জন্য দুটি ৪-বিট comparator জোড়া যায়। ওপরের (বেশি গুরুত্বের) ৪ বিটের comparator-এর তিনটি বাড়তি “cascade input” থাকে, যেখানে নিচের comparator-এর G, E, L আসে। নিয়ম: ওপরের ৪ বিট আলাদা হলে সেটিই উত্তর; সমান হলে নিচের অংশের ফল পার করে দেওয়া হয়। ল্যাবের 7485 ধরনের ৪-বিট magnitude comparator IC-তে এই cascade input থাকে। মনে রাখুন, এটি unsigned তুলনা; two's complement-এর signed তুলনায় sign বিটের নিয়ম উল্টো। পর্ব ১৩-এ দেখবেন CPU প্রায়ই আলাদা magnitude comparator না বানিয়ে ALU-তে বিয়োগ করেই তুলনা করে — x86 আর ARM ফলটি flag-এ রাখে, আর RISC-V-এর slt আর blt কোনো flag register ছাড়াই একই বিয়োগের নিয়ম (N ⊕ V) কাজে লাগায়।

Parity generator আর checker — XOR-এর গাছ

ডেটা পাঠানোর সময় একটি বিট উল্টে গেলে ধরার সবচেয়ে সস্তা উপায় parity bit. Even parity-তে বাড়তি বিট P এমনভাবে বসানো হয় যাতে মোট 1-এর সংখ্যা জোড় হয়: P = D3 ⊕ D2 ⊕ D1 ⊕ D0 — এটি parity generator. গ্রহণকারী সব বিট আর P একসাথে XOR করে: ফল 0 হলে ঠিক আছে, 1 হলে বিজোড় সংখ্যক বিট উল্টেছে — এটি parity checker. যেমন 1011 0010-এ চারটি 1, তাই even parity বিট 0.

৮ বিটের parity-তে ৭টি ২-input XOR লাগে, কিন্তু সাজানোর ধরন গুরুত্বপূর্ণ। একটির পর একটি শিকল বানালে সংকেতকে ৭টি gate পার হতে হয়; জোড়ায় জোড়ায় গাছ বানালে (৪ → ২ → ১) মাত্র ৩ স্তর — log2৮ = ৩। XOR associative বলে দুটির ফল একই, কিন্তু গাছটি অনেক দ্রুত। এক বিটের ভুল ধরা যায়, দুই বিট উল্টালে parity ধরতে পারে না — CRC আর Hamming code-এর মতো শক্তিশালী পদ্ধতি দেখুন কম্পিউটার নেটওয়ার্কস কোর্সের error detection পর্বে। ল্যাবে 74280 ধরনের ৯-বিট parity generator/checker IC পাওয়া যায়।

Code converter — binary ↔ Gray

পর্ব ০৪-এ Gray code-এর সূত্র দেখেছেন: g = b ⊕ (b ≫ 1). Hardware-এ এটি সরাসরি XOR gate: ৪ বিটে g3 = b3, g2 = b3 ⊕ b2, g1 = b2 ⊕ b1, g0 = b1 ⊕ b0 — তিনটি XOR, সবগুলো সমান্তরালে, মাত্র এক স্তরের delay. উল্টো দিকে b3 = g3, b2 = b3 ⊕ g2, b1 = b2 ⊕ g1, b0 = b1 ⊕ g0 — এখানে প্রতিটি বিট আগেরটির ওপর নির্ভর করে, তাই XOR-গুলো শিকলের মতো (n − 1 স্তর)। উদাহরণ: binary 1011 → Gray 1110; আর Gray 1110 → b3 = 1, b2 = 1 ⊕ 1 = 0, b1 = 0 ⊕ 1 = 1, b0 = 1 ⊕ 0 = 1 → 1011. BCD → excess-3 (প্রতিটি অঙ্কে +3), BCD → 7-segment — এগুলোও code converter, প্রতিটি output-এর জন্য একটি K-map.

৮ · CPU-র ভেতরে আর ল্যাবের IC-তে

Register file — কয়েকটি বড় mux আর একটি decoder

RISC-V (RV32I)-এর ৩২টি register, প্রতিটি ৩২ বিটের। একটি add x5, x6, x7 instruction-এ একসাথে দুটি register পড়তে হয় (x6, x7) আর একটি লিখতে হয় (x5). এই কাজের hardware-কে বলে register file, আর তার ভেতরটা এই পর্বের block দিয়েই তৈরি:

  • দুটি read port = দুটি 32:1 mux, প্রতিটি ৩২ বিট চওড়া (অর্থাৎ প্রতি বিটে একটি করে, মোট ৩২টি 32:1 mux-এর সারি)। Instruction-এর ৫ বিটের rs1 ফিল্ড প্রথম mux-এর select-এ, rs2 দ্বিতীয়টির — ২৫ = ৩২, তাই ঠিক ৫ বিট।
  • Write port = একটি 5:32 decoder: rd ফিল্ড decoder-এর input-এ, আর RegWrite control সংকেত তার enable-এ। শুধু বাছাই করা register-এর write enable চালু হয়, আর পরের clock-এ শুধু সেটিই নতুন মান নেয়।
  • RISC-V-এর x0 সবসময় ০ — hardware-এ decoder-এর ০ নম্বর output কোথাও লেখার অনুমতি দেয় না, আর read mux-এর ০ নম্বর input-এ সরাসরি ০ বসানো থাকে।

পুরো datapath-এ register file কীভাবে বসে, আর instruction-এর ফিল্ড থেকে এই select-গুলো কীভাবে আসে, দেখবেন পর্ব ৩২-এ (Single-cycle datapath). বড় processor-এ একসাথে অনেক instruction চলায় read port অনেক বেশি লাগে — তখন এই mux-গুলোই chip-এর জায়গা আর সময়ের বড় খরচ হয়ে দাঁড়ায় (ভাবনার প্রশ্ন ০৫)।

Verilog-এ একই block — উদাহরণ

বাস্তব নকশায় কেউ gate একটি একটি আঁকেন না; hardware description language-এ আচরণ লেখেন, আর synthesis tool gate বানায়। পর্ব ১৪-এ Verilog বিস্তারিত শিখবেন; এখানে শুধু দেখুন এই পর্বের তিনটি block কত ছোট করে লেখা যায় (synthesizable Verilog-2001, চালানো যায় না — উদাহরণ):

// উদাহরণ — 4:1 mux, 3:8 decoder with enable, 8:3 priority encoder
module mux4 (input wire [3:0] i, input wire [1:0] s, output wire y);
  assign y = i[s];                          // synthesis builds the AND-OR tree
endmodule

module dec3to8 (input wire [2:0] a, input wire en, output reg [7:0] y);
  always @* begin
    y = 8'b0;                               // default: every output 0
    if (en) y[a] = 1'b1;                    // exactly one line high
  end
endmodule

module prienc8 (input wire [7:0] i, output reg [2:0] y, output reg v);
  integer k;
  always @* begin
    y = 3'd0; v = 1'b0;
    for (k = 0; k < 8; k = k + 1)
      if (i[k]) begin y = k[2:0]; v = 1'b1; end   // higher k overwrites lower
  end
endmodule

Priority encoder-এর loop-টি hardware-এ “লুপ” নয় — synthesis tool এটিকে খুলে আটটি শর্তের একটি শিকল বানায়, যেখানে পরে আসা (বড় k) শর্ত আগেরটিকে ছাপিয়ে যায়। ফলাফল ঠিক অংশ ৬-এর সূত্রগুলোর সমান। প্রতিটি always @*-এ শুরুতে default মান দেওয়া জরুরি; না দিলে tool অনিচ্ছাকৃত latch বানিয়ে ফেলতে পারে — একটি প্রচলিত নতুনদের ভুল।

ল্যাবের পরিচিত IC

বিশ্ববিদ্যালয়ের ডিজিটাল লজিক ল্যাবে প্রায়ই 74-series-এর TTL বা CMOS (74HC, 74LS ইত্যাদি) chip দিয়ে এই block-গুলো হাতে জোড়া হয়। কয়েকটি বহুল ব্যবহৃত নম্বর (বিস্তারিত pin আর সংকেতের জন্য নির্মাতার datasheet দেখুন — সংস্করণভেদে সামান্য পার্থক্য থাকতে পারে):

ICকাজমনে রাখার বিষয়
741518:1 mux৩টি select, active-low enable (strobe), সাধারণ আর উল্টো — দুই রকম output
74153 / 74157দুটি 4:1 / চারটি 2:1 muxএকই select দিয়ে কয়েকটি বিট একসাথে বাছাই
741383:8 decoder/demultiplexeroutput active-low; তিনটি enable (একটি active-high, দুটি active-low) — cascading-এর জন্য সুবিধাজনক
741488:3 priority encoderinput আর output active-low; EI, EO আর GS সংকেত দিয়ে দুটি জুড়ে 16:4 বানানো যায়
7485৪-বিট magnitude comparatorA > B, A = B, A < B output আর cascade input
74280৯-বিট parity generator/checkereven আর odd — দুই output

আরও গভীরে: FPGA chip-এর মূল একক look-up table (LUT) আসলে ঠিক অংশ ৩-এর “mux দিয়ে যেকোনো function” — একটি k-input LUT হলো ২kটি ছোট মেমরি সেল আর একটি ২k:1 mux tree, যেখানে input সংকেতগুলো select হিসেবে কাজ করে আর সেলে truth table-এর কলাম লেখা থাকে। আধুনিক অনেক FPGA ৪ থেকে ৬ input-এর LUT ব্যবহার করে। CMOS chip-এর ভেতরে mux প্রায়ই AND-OR gate দিয়ে না বানিয়ে transmission gate (একজোড়া transistor-এর সুইচ) দিয়ে বানানো হয়, যা অনেক ছোট — পর্ব ০৮-এর transistor-এর জ্ঞান এখানে কাজে লাগে। আর high-performance CPU-তে forwarding বা bypass network-এর চওড়া mux প্রায়ই clock speed-এর সীমা ঠিক করা critical path-এর অংশ হয়।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • একটি 4:1 mux দিয়ে তিন চলকের যেকোনো function কি বানানো যায়? — হ্যাঁ; দুটি চলক select-এ, আর প্রতিটি data input-এ 0, 1, তৃতীয় চলক বা তার উল্টো (Shannon expansion).
  • 16:1 mux বানাতে কতটি 2:1 বা 4:1 mux লাগে? — ১৫টি 2:1 (২n − ১), অথবা ৫টি 4:1 (চারটি + একটি)।
  • Decoder আর demultiplexer-এর সম্পর্ক কী? — enable-সহ decoder-এর enable-এ data দিলেই সেটি demux; প্রতিটি output = minterm × data.
  • Priority encoder-এ V output কেন লাগে? — “কোনো input চালু নেই” আর “শুধু I0 চালু” দুটিতেই Y = 0…0; V ছাড়া এদের আলাদা করা যায় না।
  • 3:8 decoder দিয়ে full adder কীভাবে? — S = Y1 + Y2 + Y4 + Y7, Cout = Y3 + Y5 + Y6 + Y7 — একটি decoder, দুটি OR.

৯ · AI-এর যুগে — নকশা নিজে simulate করে যাচাই

“এই function-টি 4:1 mux দিয়ে বানান” ধরনের প্রশ্নে AI চ্যাটবট দ্রুত একটি উত্তর দেয় — দেখতে নিখুঁত, কিন্তু প্রায়ই একটি data input ভুল। ভাষা মডেল truth table মনে মনে simulate করে না; কোন চলক select-এ আর কোনটি data-তে, MSB কোনটি — এসব নিয়মে সামান্য গোলমাল পুরো উত্তর বদলে দেয়। এই পর্বের বিষয়ে সাধারণ ভুলগুলো:

AI প্রায়ই যা ভুল করেকীভাবে ধরবেন
Mux-এর data input-এ x আর x′ উল্টে ফেলা, বা ভুল জোড়া সারি দেখাপ্রতিটি সারি select মান বসিয়ে হাতে বা script-এ simulate করুন
S1 আর S0-এর কোনটি MSB, তা উল্টে ফেলা (I1 আর I2 অদলবদল)প্রশ্নে কোন চলক কোন select-এ, স্পষ্ট লিখে দিন; S1S0 = 10 মানে I2
Priority encoder-এর সূত্রে উঁচু input-এর উল্টো (I6′ ইত্যাদি) বাদ দেওয়া, বা V ভুলে যাওয়াএকসাথে দুটি input চালু রেখে কয়েকটি নকশা পরীক্ষা করুন
Address range-এর শেষ ঠিকানায় এক বেশি বা কম (0x3FFF বনাম 0x4000)chip-এর আকার − ১ যোগ করে শেষ ঠিকানা হিসাব করুন; hex-এ যাচাই
Active-low output-এর decoder-এ OR বসিয়ে দেওয়া (NAND লাগে)Datasheet দেখুন; De Morgan প্রয়োগ করে মিলিয়ে নিন
IC-র নম্বর বা pin-এর কাজ বানিয়ে বলাসবসময় নির্মাতার datasheet; নম্বর মনে থাকলেও pin মিলিয়ে নিন
Verilog-এ default ছাড়া always @* — অনিচ্ছাকৃত latchSimulation আর synthesis report-এ latch warning দেখুন (পর্ব ১৪)

AI-কে শুধু উত্তর না চেয়ে প্রতিটি ধাপ আর একটি পূর্ণ যাচাই চাইলে ভুল সহজে ধরা পড়ে:

আমি ডিজিটাল লজিক শিখছি। F(A,B,C,D) = Σm(1,3,4,11,12,13,14,15) একটি 8:1 multiplexer দিয়ে বানাতে চাই। A, B, C যাবে S2, S1, S0-তে (A = MSB), আর D যাবে data input-এ। ১. পুরো truth table লেখো (১৬ সারি)। ২. প্রতিটি select মান (ABC) এর জন্য D = 0 আর D = 1-এ F কত, দেখাও, আর I0 থেকে I7 প্রতিটিতে 0, 1, D নাকি D′ বসবে, কারণসহ লেখো। ৩. শেষে প্রতিটি ১৬ সারির জন্য mux-এর output হিসাব করে দেখাও যে F-এর সাথে মিলছে। ৪. একটি ছোট JavaScript বা Python script লেখো যা এই যাচাই নিজে করে। নিশ্চিত না হলে অনুমান করো না, বলো।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

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

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

প্র ০১FPGA-তে কোনো নির্দিষ্ট AND বা OR gate নেই, তবু সেখানে যেকোনো digital circuit বসানো যায়। “Mux দিয়ে যেকোনো function” — এই পুরোনো পাঠ্যবইয়ের কৌশল কীভাবে একটি গোটা শিল্পের ভিত্তি হলো?

FPGA (field-programmable gate array) এমন একটি chip যার ভেতরের circuit কারখানায় স্থির হয় না — কেনার পরে, “মাঠে” (field) বসে নকশাকার নতুন configuration লোড করে পুরো circuit বদলে দিতে পারেন। প্রথম বাণিজ্যিকভাবে সফল FPGA সাধারণত ধরা হয় ১৯৮০-এর দশকের মাঝামাঝি Xilinx-এর XC2064-কে। তখন থেকে মূল প্রশ্ন একটিই: এমন কোন একটি একক বানাব, যা পরে যেকোনো ছোট logic function হয়ে উঠতে পারে?

উত্তর ছিল অংশ ৩-এর প্রথম পদ্ধতি: ২k:1 mux-এর data input-এ truth table-এর output কলাম বসিয়ে দেওয়া। FPGA-তে data input-গুলো ধ্রুবক তার নয়, ছোট ছোট SRAM মেমরি সেল — configuration লোড করার সময় সেগুলোতে truth table লেখা হয়। k টি সংকেত mux-এর select-এ ঢোকে। এই এককের নাম look-up table বা LUT, কারণ এটি আক্ষরিক অর্থেই টেবিলে উত্তর খুঁজে দেয়। একটি k-input LUT k চলকের ২২kটি সম্ভাব্য function-এর যেকোনোটি হতে পারে — ৪-input-এ ৬৫,৫৩৬ রকম।

LUT কত বড় হবে, তা একটি আসল প্রকৌশল-সমঝোতা। k বাড়ালে প্রতিটি LUT বেশি কাজ করে, circuit-এ কম স্তর লাগে, তাই দ্রুত; কিন্তু সেলের সংখ্যা ২k হারে বাড়ে, আর বেশিরভাগ বাস্তব function-এ সব input ব্যবহার হয় না — জায়গা নষ্ট। দীর্ঘদিন ৪-input LUT প্রচলিত ছিল; ২০০০-এর দশকের মাঝামাঝি থেকে বড় নির্মাতারা ৬-input LUT-এর দিকে যান, যা প্রায়ই দুটি ছোট ৫-input function হিসেবেও ভাগ করে ব্যবহার করা যায়।

আজ FPGA ব্যবহার হয় টেলিকম যন্ত্রপাতি, নেটওয়ার্ক সুইচ, ডেটা সেন্টারের accelerator, আর ASIC বানানোর আগে নকশা পরীক্ষায় (prototyping). এমনকি নতুন CPU-ও বাজারে আসার আগে প্রায়ই FPGA-তে চালিয়ে দেখা হয় — অনেক RISC-V core শখের প্রকল্প হিসেবেও সস্তা FPGA বোর্ডে চালানো হয়।

মূল উপলব্ধি: “mux-এর data input-এ truth table” শুধু পরীক্ষার কৌশল নয় — এটি “মেমরি দিয়ে logic” ধারণার বীজ। একটি function আসলে একটি টেবিল, আর টেবিল রাখার সবচেয়ে নমনীয় জায়গা মেমরি; mux সেই টেবিল থেকে উত্তর বের করে আনে।

প্র ০২প্রথম দিকের PC-তে কয়েকটি device একসাথে CPU-কে ডাকলে কে আগে সাড়া পাবে, তা কীভাবে ঠিক হতো? Fixed priority-র দুর্বলতা কী, আর তার সমাধান কী?

১৯৮১ সালের IBM PC-তে interrupt সামলাত Intel-এর 8259A নামের একটি Programmable Interrupt Controller (PIC). এর আটটি অনুরোধ লাইন (IRQ0–IRQ7), আর ভেতরে মূলত এই পর্বের priority encoder-এর কাজ — কয়েকটি লাইন একসাথে চালু থাকলে সবচেয়ে উঁচু অগ্রাধিকারেরটির নম্বর CPU-কে জানানো। সাধারণ ব্যবস্থায় IRQ0 (system timer) সবচেয়ে উঁচু, তারপর IRQ1 (keyboard) — অর্থাৎ এখানে ছোট নম্বর মানে বেশি অগ্রাধিকার, এই পর্বের উদাহরণের উল্টো। কোনটি “উঁচু”, তা নকশাকারের সিদ্ধান্ত।

১৯৮৪ সালের IBM PC/AT-এ দ্বিতীয় একটি 8259A যোগ করে cascading করা হয় — দ্বিতীয়টির output প্রথমটির একটি লাইনে (IRQ2) ঢোকে, ফলে ১৫টি ব্যবহারযোগ্য লাইন। এটি ঠিক অংশ ৪-এর “ছোট decoder জুড়ে বড় decoder”-এর মতোই “ছোট priority encoder জুড়ে বড়”।

Fixed priority-র দুর্বলতা হলো starvation (না খেয়ে থাকা)। উঁচু অগ্রাধিকারের একটি device ঘনঘন অনুরোধ করলে নিচের device কখনো সুযোগই পায় না — stepper-এর শেষ ধাপে ঠিক এই ঝুঁকির কথা বলা হয়েছে। 8259A-তে তাই কয়েকটি mode ছিল, যার মধ্যে rotating priority: যে device এইমাত্র সেবা পেল, সে লাইনের সবচেয়ে শেষে চলে যায়। হিসাবে এটি একটি priority encoder-এর সামনে একটি “ঘোরানো” circuit — রেস্তোরাঁর কাউন্টারের মতো যেখানে প্রতিবার পালাক্রমে ডাকা হয়। Bus arbitration-এ (একাধিক device একই bus চাইলে) একই সমস্যার সমাধান হিসেবে round-robin arbiter খুব প্রচলিত।

আধুনিক x86 computer-এ এই কাজ করে APIC পরিবার (local APIC আর I/O APIC), যা অনেক বেশি লাইন, একাধিক core-এ interrupt পাঠানো আর বার্তার মতো interrupt (MSI) সামলায়। ARM-ভিত্তিক chip-এ এর সমতুল্য GIC. কিন্তু ভেতরে, “অনেক অনুরোধ থেকে সবচেয়ে জরুরিটি বেছে নেওয়া” — সেই একই priority encoder-এর ধারণা।

মূল উপলব্ধি: Priority encoder একটি নীতি বাস্তবায়ন করে — কে আগে। নীতিটি স্থির রাখলে hardware সবচেয়ে সরল হয়, কিন্তু ন্যায্যতা (fairness) চাইলে অবস্থা মনে রাখার মতো বাড়তি circuit লাগে। বিস্তারিত পর্ব ৫২-এ।

প্র ০৩পুরোনো ৮-বিট home computer-এ নকশাকাররা প্রায়ই জেনেবুঝে “অসম্পূর্ণ” address decoding করতেন, ফলে একই chip কয়েকটি ঠিকানায় দেখা যেত। কেন এমন করা হতো, আর আজ এই ধারণা কোথায় টিকে আছে?

১৯৭০-এর শেষ আর ১৯৮০-এর দশকের অনেক ৮-বিট computer (6502 বা Z80 ধরনের CPU-ভিত্তিক) ১৬টি address লাইনে 64 KiB ঠিকানা দেখত। Circuit board-এ প্রতিটি অতিরিক্ত chip মানে বাড়তি খরচ, জায়গা আর বিদ্যুৎ — আর তখন ব্যবসায়িক সাফল্য প্রায়ই নির্ভর করত দামের ওপর। ধরুন একটি device-এর মাত্র চারটি register; তাকে ঠিক চারটি ঠিকানায় বসাতে হলে ১৬টি বিটের সবগুলো decode করতে হয়, যার জন্য কয়েকটি gate বা decoder লাগে। কিন্তু যদি শুধু ওপরের কয়েকটি বিট দেখা হয়, একটি 74138-এর একটি output-ই যথেষ্ট — বিনিময়ে device-টি পুরো একটি ঠিকানা-ব্লক জুড়ে বারবার পুনরাবৃত্তি হয় (mirror).

যতক্ষণ সেই ব্লকে অন্য কিছু বসানোর দরকার নেই, এতে কোনো ক্ষতি নেই — ঠিক অংশ ৫-এর warning-এর alias. সমস্যা শুরু হয় যখন পরে কেউ সেই “ফাঁকা” মনে হওয়া জায়গায় নতুন হার্ডওয়্যার বসান; তখন দুটি device একসাথে সাড়া দেয়, আর bus-এ ঝগড়া (contention) বাঁধে। এজন্য বাড়তি সম্প্রসারণ কার্ডের নকশায় memory map ভালোভাবে জানা জরুরি ছিল। কিছু প্রোগ্রামার আবার এই mirror ঠিকানা জেনেবুঝে ব্যবহার করতেন — যা পরে নতুন মডেলে চলত না।

আজ ব্যাপারটি অনেক বড় পরিসরে, কিন্তু নীতি একই। একটি SoC-এর ভেতরের interconnect প্রতিটি লেনদেনের ঠিকানা দেখে ঠিক করে সেটি DRAM controller, কোনো peripheral নাকি on-chip SRAM-এ যাবে — এটি একটি বিশাল address decoder, যার “memory map” chip-এর ডেটাশিটে বহু পৃষ্ঠা জুড়ে লেখা থাকে। Microcontroller প্রোগ্রামিং-এ (যেমন ARM Cortex-M) প্রতিটি peripheral register-এর ঠিকানা এই map থেকেই আসে। PC-তে PCIe device-গুলোর ঠিকানার পরিসর boot-এর সময় firmware আর OS ঠিক করে দেয় (পর্ব ৫১)। আর নিরাপত্তার দিক থেকে, কোন ঠিকানা কোন hardware-এ যায় তা ঠিকভাবে নিয়ন্ত্রণ করা আজকের chip-এর সুরক্ষার একটি গুরুত্বপূর্ণ অংশ।

মূল উপলব্ধি: Address decoding হলো খরচ আর পূর্ণতার একটি সমঝোতা। কম বিট decode করলে hardware সস্তা, কিন্তু ঠিকানার জায়গা নষ্ট আর ভবিষ্যতে সংঘাতের ঝুঁকি; পূর্ণ decoding নিরাপদ কিন্তু দামি।

প্র ০৪একটি shared bus-এ কয়েকটি উৎস থেকে একটিকে বেছে নেওয়ার দুই উপায়: mux, অথবা প্রত্যেকের সামনে tri-state buffer. পুরোনো board-এ tri-state খুব প্রচলিত ছিল, অথচ আধুনিক chip-এর ভেতরে প্রায় সবসময় mux. কেন?

একটি tri-state buffer-এর output তিন অবস্থার একটিতে থাকতে পারে: 0, 1, অথবা “বিচ্ছিন্ন” (high impedance, Z) — যেন তারটি কেটে দেওয়া হয়েছে (পর্ব ০৮)। Shared bus-এ প্রতিটি উৎসের সামনে একটি tri-state buffer বসিয়ে, একটি decoder দিয়ে একবারে শুধু একটির enable চালু করলে কাজটি একটি mux-এর মতোই হয়: এক সময়ে একজনই কথা বলে। এর বড় সুবিধা, একটিই তার অনেক উৎসের মধ্যে ভাগ হয় — পুরোনো computer-এর motherboard বা সম্প্রসারণ slot-এর bus এভাবেই বানানো হতো, কারণ board-এ তার টানা দামি আর কার্ড যোগ-বিয়োগ করা যায়।

কিন্তু chip-এর ভেতরে এর কয়েকটি ঝুঁকি বড় হয়ে দাঁড়ায়। (১) Contention: decoder বদলের মুহূর্তে, এক ঝলকের জন্য দুটি buffer একসাথে চালু থাকতে পারে — একটি 1 টানছে, অন্যটি 0. তখন power থেকে ground-এ সরাসরি স্রোত বয়, বিদ্যুৎ নষ্ট হয়, এমনকি transistor-এর ক্ষতিও হতে পারে। (২) ভাসমান bus: কেউ চালু না থাকলে তারটি অনির্দিষ্ট ভোল্টেজে ভাসে, পরের gate-এ অপ্রত্যাশিত স্রোত বয়। (৩) পরীক্ষা আর timing: chip কারখানায় পরীক্ষা (testing) আর সময়ের হিসাব করার tool-গুলো tri-state সংকেতে জটিল হয়ে পড়ে।

অন্যদিকে mux-এর output সবসময় সক্রিয়ভাবে 0 বা 1 টানে — কোনো ঝগড়া নেই, কোনো ভাসা নেই। তাই আধুনিক on-chip bus-এর মানগুলোতে সাধারণত mux-ভিত্তিক interconnect ব্যবহৃত হয়; যেমন ARM-এর AMBA AHB-তে slave-দের থেকে আসা read data একটি কেন্দ্রীয় multiplexer দিয়ে master-এর কাছে যায়, আর কোন slave বাছাই হবে তা ঠিক করে একটি address decoder — এই পর্বের দুটি block একসাথে। Tri-state টিকে আছে মূলত chip-এর বাইরের pin-এ, যেখানে একই তার দুই দিকে ব্যবহার হয় (যেমন মেমরির data pin).

মূল উপলব্ধি: যুক্তির দিক থেকে mux আর tri-state bus একই কাজ করে, কিন্তু বিদ্যুৎ, নির্ভরযোগ্যতা আর পরীক্ষার খরচ আলাদা। কাগজে সমান দুটি নকশা বাস্তবে সমান নয় — এটাই ডিজিটাল লজিক থেকে বাস্তব hardware-এ যাওয়ার শিক্ষা।

প্র ০৫Register file দেখতে সরল — কিছু mux আর একটি decoder. তাহলে আধুনিক বড় processor-এ এটি কেন সবচেয়ে কঠিন নকশার অংশগুলোর একটি? Read port বাড়ালে কী হয়?

একটি সরল RISC-V core (পর্ব ৩২) প্রতি cycle-এ একটি instruction চালায়, তাই দুটি read port আর একটি write port যথেষ্ট। কিন্তু একটি superscalar processor (পর্ব ৩৯) প্রতি cycle-এ ৪, ৬ বা আরও বেশি instruction একসাথে শুরু করতে পারে। প্রতিটি instruction-এর দুটি operand পড়তে হলে চার-চওড়া নকশায় আটটি read port আর চারটি write port লাগতে পারে। আর out-of-order processor-এ (পর্ব ৪০) register renaming-এর জন্য physical register-এর সংখ্যা ৩২-এর অনেক বেশি — প্রায়ই কয়েকশো।

সমস্যা হলো, port-এর খরচ সরলরেখায় বাড়ে না। বাস্তব register file-এ প্রতিটি মেমরি সেলের ভেতর দিয়ে প্রতিটি port-এর জন্য আলাদা একটি word line (কোন সারি, decoder থেকে) আর আলাদা bit line (ডেটা বের হওয়ার তার) টানতে হয়। Port বাড়লে সেলটি দৈর্ঘ্যে আর প্রস্থে দুই দিকেই বড় হয়, তাই ক্ষেত্রফল মোটামুটি port-সংখ্যার বর্গের হারে বাড়ে। বড় সেল মানে লম্বা তার, লম্বা তার মানে বেশি delay আর বেশি বিদ্যুৎ। এই পর্বের ভাষায়: read port-এর mux-গুলো চওড়া আর লম্বা হতে থাকে, আর decoder-কে আরও বেশি load চালাতে হয়।

নকশাকাররা তাই নানা কৌশল নেন। Banking: register file-কে কয়েকটি ছোট ভাগে ভাগ করা, প্রতিটিতে কম port — তবে দুটি instruction একই ভাগ চাইলে একজনকে অপেক্ষা করতে হয়। Clustering: execution unit-গুলোকে দলে ভাগ করে প্রতিটি দলের নিজস্ব register file-এর কপি; দলের বাইরে ডেটা পাঠাতে বাড়তি এক cycle. Bypass/forwarding: সদ্য হিসাব করা ফল register file-এ না গিয়ে সরাসরি পরের instruction-এর কাছে পাঠানো (পর্ব ৩৬) — যা নিজেও একটি চওড়া mux. অনেক বড় core-এ register file এক cycle-এ পড়া না গেলে pipeline-এ আলাদা ধাপ যোগ করা হয়।

মূল উপলব্ধি: Mux আর decoder যুক্তিতে সরল, কিন্তু তাদের চওড়া আর সংখ্যা বাড়লে তারের দৈর্ঘ্যই আসল খরচ হয়ে দাঁড়ায়। আধুনিক processor নকশার অনেক সিদ্ধান্ত আসলে “কতটি mux, কত চওড়া” — এই প্রশ্নের উত্তর।

অনুশীলন

পরীক্ষার খাতার মতো করে পুরো ধাপ লিখে সমাধান করুন, তারপর উত্তর মিলিয়ে দেখুন। অংশ ৩-এর synthesiser-এ নিজের mux-নকশা যাচাইও করতে পারেন।

  1. Mux দিয়ে function (numerical): F(A, B, C, D) = Σm(1, 3, 4, 11, 12, 13, 14, 15). (ক) একটি 8:1 mux দিয়ে বানান, যেখানে A, B, C → S2, S1, S0 আর D data-তে। (খ) একই function একটি 8:1 mux দিয়ে, কিন্তু এবার B, C, D select-এ আর A data-তে।

    (ক) প্রতিটি ABC-র জন্য জোড়া সারি (D = 0, D = 1): 000 → m0, m1 = 0, 1 → I0 = D; 001 → m2, m3 = 0, 1 → I1 = D; 010 → m4, m5 = 1, 0 → I2 = D′; 011 → m6, m7 = 0, 0 → I3 = 0; 100 → m8, m9 = 0, 0 → I4 = 0; 101 → m10, m11 = 0, 1 → I5 = D; 110 → m12, m13 = 1, 1 → I6 = 1; 111 → m14, m15 = 1, 1 → I7 = 1.

    উত্তর: I0..I7 = D, D, D′, 0, 0, D, 1, 1.

    (খ) এবার select = BCD, মান j (০–৭)। A = 0-এর সারি mj, A = 1-এর সারি mj+8. Implementation table: A′ সারিতে ০–৭, A সারিতে ৮–১৫; বৃত্ত দেওয়া minterm ১, ৩, ৪, ১১, ১২, ১৩, ১৪, ১৫.

    • j = ০: m0, m8 — কোনোটিই নেই → I0 = 0
    • j = ১: m1 আছে, m9 নেই → I1 = A′
    • j = ২: m2, m10 — নেই → I2 = 0
    • j = ৩: m3, m11 — দুটিই আছে → I3 = 1
    • j = ৪: m4, m12 — দুটিই → I4 = 1
    • j = ৫, ৬, ৭: m5, m6, m7 নেই কিন্তু m13, m14, m15 আছে → I5 = I6 = I7 = A

    উত্তর: I0..I7 = 0, A′, 0, 1, 1, A, A, A. দুটি নকশাই সঠিক; কোন চলক data-তে যাবে, তার ওপর ভিত্তি করে উত্তরের চেহারা পুরো বদলে যায় — তাই খাতায় শুরুতেই সংযোগ স্পষ্ট লিখুন। যাচাই: ABCD = 0100 → (ক)-তে select 010, I2 = D′ = 1; (খ)-তে select BCD = 100, I4 = 1. দুটিই F = 1 (m4).

  2. Decoder দিয়ে নকশা: (ক) একটি 3:8 decoder আর OR gate দিয়ে full subtractor বানান (input A, B, Bin; output পার্থক্য D = A − B − Bin-এর বিট, আর borrow Bout). (খ) শুধু 3:8 আর 2:4 decoder (enable-সহ) দিয়ে একটি 5:32 decoder বানাতে কতটি লাগবে, আর কীভাবে জুড়বেন?

    (ক) Truth table (A B Bin → D Bout): 000 → 0 0; 001 → 1 1; 010 → 1 1; 011 → 0 1; 100 → 1 0; 101 → 0 0; 110 → 0 0; 111 → 1 1. তাই D = Σm(1, 2, 4, 7) — full adder-এর Sum-এর সমান (দুটিই তিনটি বিটের XOR), আর Bout = Σm(1, 2, 3, 7). নকশা: A, B, Bin → decoder-এর A2, A1, A0; একটি ৪-input OR-এ Y1, Y2, Y4, Y7 → D; আরেকটিতে Y1, Y2, Y3, Y7 → Bout.

    (খ) ৩২টি output-এর জন্য ৩২ ÷ ৮ = চারটি 3:8 decoder, প্রত্যেকে নিচের তিন বিট A2A1A0 নেয়। কোনটি চালু হবে, ঠিক করে ওপরের দুই বিট A4A3 — সেগুলো যায় একটি 2:4 decoder-এ, যার চারটি output চারটি 3:8-এর EN-এ। মোট ৫টি decoder. যাচাই: input 10110 (= ২২) → A4A3 = 10, তাই তৃতীয় 3:8 (নম্বর ২) চালু; A2A1A0 = 110, তার ৬ নম্বর output → সামগ্রিক ২ × ৮ + ৬ = ২২।

  3. Address decoding (trace): একটি ১৬-বিট address-এর system-এ 8 KiB-এর মেমরি chip; chip বাছাই করে একটি 3:8 decoder, যার input A15A14A13. (ক) Y5 কোন ঠিকানার পরিসর বাছে? (খ) ঠিকানা 0x6F00 আর 0x2345-এ কোন output চালু হবে? (গ) খরচ কমাতে কেউ যদি 3:8-এর বদলে শুধু A15A14 দিয়ে একটি 2:4 decoder বসান (A13 কোথাও যুক্ত নয়), তাহলে কী ঘটবে?

    প্রতিটি chip 8 KiB = ২১৩ byte, তাই ভেতরে A12…A0 (১৩ বিট); ওপরের তিন বিট chip বাছে — প্রতিটি output একটি 0x2000-চওড়া ব্লক।

    (ক) Y5 মানে A15A14A13 = 101. শুরু = ৫ × 0x2000 = 0xA000, শেষ = 0xA000 + 0x1FFF = 0xBFFF. পরিসর 0xA000–0xBFFF.

    (খ) 0x6F00 = 0110 1111 0000 0000: ওপরের তিন বিট 011 → Y3. 0x2345 = 0010 0011 0100 0101: ওপরের তিন বিট 001 → Y1.

    (গ) Partial decoding: A13-এর দুই মানেই একই chip সাড়া দেবে। যেমন CS0-এর chip 0x0000–0x1FFF আর 0x2000–0x3FFF — দুই জায়গাতেই (alias). মাত্র চারটি chip বসানো যাবে, মোট 32 KiB, অথচ ঠিকানার জায়গা 64 KiB — অর্ধেক নষ্ট। আর কেউ 0x2000-এ আলাদা device বসাতে চাইলে bus contention হবে।

  4. Circuit লিখুন, তারপর output অনুমান করুন: একটি 4:2 priority encoder নকশা করুন যেখানে D0-এর অগ্রাধিকার সবচেয়ে বেশি (D3 সবচেয়ে কম)। don't care-সহ truth table, Y1, Y0, V-এর সরল সূত্র লিখুন। তারপর input D3D2D1D0 = 1010 আর 1000-এর জন্য output অনুমান করুন।

    Truth table (output = সবচেয়ে ছোট চালু input-এর নম্বর):

    D3 D2 D1 D0 | Y1 Y0 V
     0  0  0  0 |  x  x 0
     x  x  x  1 |  0  0 1
     x  x  1  0 |  0  1 1
     x  1  0  0 |  1  0 1
     1  0  0  0 |  1  1 1

    Y1 = 1 শেষ দুই সারিতে, যেখানে D0 = D1 = 0 আর D2 বা D3 চালু: D0′D1′(D2 + D3). প্রথম সারি (সব 0)-এর Y don't care, তাই (D2 + D3) বাদ দেওয়া যায়: Y1 = D0′D1′.

    Y0 = 1 তৃতীয় আর পঞ্চম সারিতে: D0′D1 + D0′D1′D2′D3. প্রথম পদের সাথে absorption (X + X′Y = X + Y, এখানে X = D1) আর don't care ব্যবহার করে: Y0 = D0′(D1 + D2′). V = D0 + D1 + D2 + D3.

    অনুমান: 1010 → সবচেয়ে ছোট চালু D1: Y1 = 1·0 = 0, Y0 = 1·(1 + 1) = 1 → 01, V = 1. 1000 → শুধু D3: Y1 = 1·1 = 1, Y0 = 1·(0 + 1) = 1 → 11, V = 1. দুটিই টেবিলের সাথে মেলে (সূত্রগুলো ১৫টি অশূন্য input-এ script দিয়েও যাচাই করা)।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি n চলকের function-কে ২n−1:1 mux দিয়ে বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Function-এর পুরো truth table লেখা
  2. কোন n−1টি চলক select-এ যাবে আর কোনটি data-তে, তা ঠিক করা
  3. প্রতিটি select মানের জন্য data-চলকের দুই মানে F-এর জোড়া সারি দেখা
  4. প্রতিটি data input-এ 0, 1, x বা x′ বসানো
  5. সব সারিতে simulate করে truth table-এর সাথে মেলানো

Data input A ও B, আর select S-এর একটি 2:1 multiplexer-এ (S = 0 হলে A বাছাই হয়) output Y-এর সূত্র কোনটি?

  • Y = S·A + S′·B
  • Y = S′·A + S·B
  • Y = A + B
  • Y = A ⊕ B

শুধু 4:1 multiplexer ব্যবহার করে একটি 16:1 multiplexer বানাতে কমপক্ষে কতটি 4:1 mux লাগবে?

  • ৪টি
  • ৮টি
  • ৫টি
  • ১৫টি

F(A, B, C) = Σm(0, 2, 6, 7)-কে একটি 4:1 mux দিয়ে বানাতে A ও B যায় S1 ও S0-এ (A = MSB), আর C data-তে। I0, I1, I2, I3 কী হবে?

  • C, C, 0, 1
  • C′, C′, 1, 0
  • 1, 0, 1, 0
  • C′, C′, 0, 1

একটি 3:8 decoder-এর enable = 1 আর input A2A1A0 = 101 (output active-high). কোন output 1 হবে?

  • শুধু Y5
  • শুধু Y3
  • Y0 আর Y2
  • কোনোটিই না

একটি 8:3 priority encoder-এ I7-এর অগ্রাধিকার সবচেয়ে বেশি। Input I7…I0 = 0010 1100 হলে output Y2Y1Y0 আর V কত?

  • 010, V = 1
  • 111, V = 1
  • 101, V = 1
  • 101, V = 0

১৬-বিট address-এর একটি system-এ চারটি 16 KiB মেমরি chip; A15A14 একটি 2:4 decoder-এ যায়, আর decoder-এর output Yi chip i বাছে। ঠিকানা 0x9ABC-এ কোন chip বাছাই হবে, আর chip-এর ভেতরে offset কত?

  • chip 1, offset 0x1ABC
  • chip 2, offset 0x1ABC
  • chip 2, offset 0x0ABC
  • chip 3, offset 0x1ABC

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. ২n:1 mux: Y = Σ mi(S)·Ii; 2:1-এ Y = S′A + SB. বড় mux ছোট mux-এর গাছ — ২n − ১টি 2:1, বা 16:1-এর জন্য ৫টি 4:1.
  2. Shannon expansion F = x′F(x=0) + xF(x=1) দিয়ে n চলকের যেকোনো function একটি ২n−1:1 mux-এ; data input-এ 0, 1, x বা x′ — জোড়া সারি দেখে।
  3. Decoder-এর প্রতিটি output একটি minterm (one-hot); enable থাকলে সেটিই demux. Decoder + OR = যেকোনো function; decoder জুড়ে বড় decoder; ঠিকানার ওপরের বিট decode করে chip select.
  4. Priority encoder সবচেয়ে উঁচু চালু input-এর নম্বর দেয়, আর V বলে আদৌ কেউ চালু কি না; interrupt আর keyboard-এ ব্যবহার।
  5. XNOR → সমতা, MSB থেকে তুলনা → magnitude comparator, XOR-গাছ → parity, XOR → Gray; register file = read port-এ mux + write-এ decoder.

পরের পর্বে: এ পর্যন্ত বানানো adder, mux আর লজিক gate জুড়ে CPU-র হিসাবের হৃদয় — ALU. যোগ আর বিয়োগ একই circuit-এ কীভাবে, AND-OR-এর ফল কীভাবে একটি mux দিয়ে বাছাই হয়, আর zero, negative, carry, overflow flag কীভাবে তৈরি হয়।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান