আগের আর্টিকেলে দেখেছিলাম বাস্তব জগতের তথ্য — সংখ্যা, text, image, sound — কীভাবে ০ আর ১-এ রূপ নেয় এবং memory-তে voltage হিসেবে জমা হয়। এবার দেখবো সেই voltage-এর কী হয়।
ধরা যাক, আমাদের CPU-এর ভেতরে Register A-তে 2 এবং Register B-তে 3 রাখা আছে। এই দুটি সংখ্যা CPU-এর কাছে কোনো abstract "number" হিসেবে নেই। এগুলো আসলে কিছু electrical state — যেমন নির্দিষ্ট voltage level — যা binary 0010 এবং 0011-কে represent করছে।
এখন CPU-কে বলা হলো:
Register A + Register B → Register C
কিছুক্ষণ পর Register C-তে 0101, অর্থাৎ 5, পাওয়া গেল।
প্রশ্ন হলো, সিলিকনের একটা জড় টুকরো কীভাবে "জানল" যে 2 + 3 = 5?
আসলে তার মানুষের মতো কিছু বোঝারও প্রয়োজন নেই। CPU-র ভেতরের logic gate-গুলো এমনভাবে সাজানো থাকে যে নির্দিষ্ট input voltage pattern দিলে নির্দিষ্ট output voltage pattern তৈরি হয়। সেই pattern-গুলোর অর্থ আমরা 0, 1, 2, 3, 5 ইত্যাদি হিসেবে ব্যাখ্যা করি।
এই article-এ আমরা সেই 2 আর 3-এর hardware-level journey অনুসরণ করব। Python, operating system, compiler — এসবের পুরো গল্প এখানে নয়। আমরা ধরে নিচ্ছি, CPU-র কাছে প্রয়োজনীয় data এবং operation-এর control information ইতিমধ্যেই পৌঁছে গেছে। এখন শুধু দেখব, electronic circuit কীভাবে সেই data-কে ব্যবহার করে একটি result তৈরি করে এবং কোথায় সেটা store করে।
বোঝার সুবিধার জন্য আমরা কয়েকটি গুরুত্বপূর্ণ building block-এর ওপর focus করব:
- ALU (Arithmetic Logic Unit) — যেখানে arithmetic ও logic operation implement করা থাকে।
- Registers — CPU-র নিজের খুব ছোট, খুব দ্রুত state-holding storage।
- Datapath / internal connections — data কোন কোন পথে যেতে পারে।
- Control logic — কোন path, কোন operation, কোন destination ব্যবহার হবে তা configure করে।
- Clock — কখন নতুন state capture হবে তার timing boundary।
একটি জরুরি পার্থক্য: অনেকেই CPU আর ALU-কে একই জিনিস মনে করে গুলিয়ে ফেলেন। আসলে ALU হলো CPU-র ভেতরের একটি নির্দিষ্ট department মাত্র। একটা বাড়ির রান্নাঘর যেমন পুরো বাড়ির প্রতিনিধি নয়, কিন্তু রান্নার কাজটা সেখানেই হয় — ঠিক তেমনি ALU পুরো processor নয়, কিন্তু গাণিতিক ও যৌক্তিক হিসাবের মূল দায়িত্বটা তারই।
ALU: logic gate থেকে গণিত
CPU-র execution logic-এর মধ্যে ALU একটি গুরুত্বপূর্ণ অংশ, যেখানে addition, subtraction, bitwise operation এবং বিভিন্ন ধরনের comparison-এর মতো arithmetic/logic operation implement করা হয়।
কিন্তু ALU-র ভেতরে কোনো রহস্যময় বুদ্ধিমত্তা নেই। এটা তৈরি হয়েছে আগের আর্টিকেলে দেখা সেই logic gate-গুলো নিখুঁত বিন্যাসে জোড়া লাগিয়ে।
binary যোগের circuit
দুইটা ১-bit binary সংখ্যা (ধরুন A আর B) যোগ করার circuit কীভাবে বানানো যায়, সেটা একটু দেখা যাক। Binary যোগের নিয়মগুলো সরল:
0 + 0 = 00 0 + 1 = 01 1 + 0 = 01 1 + 1 = 10 (decimal 2)
খেয়াল করুন:
- ডানপাশের bit (Sum) কেবল তখনই ১ হয় যখন A অথবা B-এর যেকোনো একটার মান ১, কিন্তু দুইটাই ১ হলে ০ — ঠিক একটা XOR gate-এর মতো।
- বাঁপাশের bit (Carry) কেবল তখনই ১ হয় যখন A এবং B — দুইটাই ১ — এটা ঠিক AND gate-এর মতো।
XOR আর AND gate পাশাপাশি জোড়া দিলে তৈরি হয় একটা Half Adder। কিন্তু এখানে একটা সমস্যা আছে।
Half Adder শুধু দুটি একক bit যোগ করতে পারে — মানুষ যেমন হাতে একটা "carry" মনে রাখে বড় সংখ্যা যোগ করার সময়, Half Adder সেটা পারে না। তাহলে এটি 1111 + 0001 যোগ করবে কীভাবে?
এই সমস্যার সমাধান হলো Full Adder। একই কাঠামোর সাথে "Carry In" নামে একটা অতিরিক্ত input যোগ করা হয়, যাতে আগের bit-এর carry পরের bit-এ এসে ঢুকতে পারে।
এখন processor-এ ৬৪-bit-এর দুইটা সংখ্যা যোগ করতে চাইলে কী করতে হবে? সহজ বুদ্ধি হলো — এমন ৬৪টি Full Adder একের পর এক সিরিজে জোড়া দেওয়া। একটা ট্রেনের কথা কল্পনা করুন: প্রতিটা বগি একটা Full Adder। প্রথম বগি carry পাঠায় দ্বিতীয় বগিতে, দ্বিতীয়টা তৃতীয়তে — এভাবে ৬৪ নম্বর বগি পর্যন্ত সেই ছোট্ট carry signal দৌড়াতে থাকে। এই নকশাকে বলা হয় Ripple Carry Adder।
একটা গুরুত্বপূর্ণ নোট: এই train analogy-টি Ripple Carry Adder-এর mental model হিসেবে খুব ভালো, কিন্তু এটা আধুনিক সব CPU কী করে তার প্রতিনিধিত্ব করে না। Ripple Carry Adder-এ একটি bit-এর carry পরের bit-এ পৌঁছানোর জন্য আগের stage-এর ওপর নির্ভর করতে হয়। তাই bit-এর সংখ্যা বাড়লে worst-case propagation delay-ও বাড়ে। এই bottleneck কমানোর জন্য processor design-এ Carry-Lookahead, Prefix এবং অন্যান্য faster adder architecture ব্যবহার করা যেতে পারে — এগুলো carry information এমনভাবে calculate করে যাতে প্রতিটি carry-কে একটির পর একটি stage পেরিয়ে যেতে না হয়।
তবে মূল ধারণাটা একই থাকে: শেষ পর্যন্ত logic gate-ই এমন একটি circuit তৈরি করে, যা দুইটি binary input এবং তাদের carry information থেকে sum তৈরি করে।
Register: state কোথায় থাকে
যোগফল তৈরি হলো। কিন্তু এই যোগফলটা রাখা হবে কোথায়?
CPU সাধারণত প্রতিটি intermediate value তৈরি হওয়ার সঙ্গে সঙ্গে RAM-এ পাঠিয়ে দেয় না। তার বদলে processor-এর ভেতরে এমন কিছু খুব দ্রুত storage location থাকে যেখানে calculation-এর জন্য প্রয়োজনীয় value রাখা যায়। এগুলোই register।
Register-কে শুধু "CPU-র নিজের memory" বললে পুরো ব্যাপারটা বোঝা যায় না। আরও ভালো mental model হলো:
Register হলো CPU-র খুব ছোট, খুব দ্রুত state-holding storage।
এখানে operand, address, pointer, intermediate value, এবং অন্যান্য গুরুত্বপূর্ণ processor state রাখা যেতে পারে।
RAM-এর তুলনায় register অনেক ছোট এবং CPU-র execution logic-এর খুব কাছাকাছি। তবে শুধু "RAM অনেক দূরে বলে slow" — এভাবে ব্যাপারটা ব্যাখ্যা করা ঠিক নয়। মূল পার্থক্য হলো তাদের storage architecture, size, access mechanism এবং latency।
register আসলে কী?
আগের আর্টিকেলে দেখা সেই flip-flop-এর কথা মনে আছে? সহজ mental model হিসেবে, একটি register-কে অনেকগুলো 1-bit storage element পাশাপাশি সাজানো হিসেবে ভাবতে পারি। একটি 64-bit register-এ এমন 64টি bit-এর state রাখা যায়।
Register A
bit 63 bit 0
↓ ↓
[1][0][0][1] ... [0][1][0][0]
64 bitsবিশ্ববিদ্যালয়ে AX, BX, PC-এর মতো নাম শুনলে register-কে রহস্যময় কোনো বিশেষ hardware মনে হতে পারে। আসলে register হলো CPU-র state রাখার একটি অত্যন্ত গুরুত্বপূর্ণ storage structure। নাম আলাদা, কিন্তু মৌলিক ধারণা হলো — কিছু binary state ধরে রাখা।
নিচের instrument-টা ঠিক এই পার্থক্যটাই হাতে ধরে দেখায় — ALU-র input বদলান, তার output সঙ্গে সঙ্গে বদলাবে; কিন্তু Register C অপেক্ষা করবে clock edge-এর জন্য।
Datapath, multiplexer, decoder: CPU-র ভেতরের পথ
ব্যাখ্যা সহজ রাখার জন্য এখানে কয়েকটা কাল্পনিক register-এর নাম ধরা যাক — Register A, B, C, D। বাস্তব processor-এ এদের নাম অন্যরকম হয়, কিন্তু কাজের ধরন একই।
এখন আমাদের কাছে register আছে, ALU আছে। কিন্তু Register A-এর value কীভাবে ALU-র input-এ যাবে? আর ALU result কীভাবে Register C-তে যাবে?
এখানেই আসে datapath — CPU-র ভেতরে data যে বিভিন্ন পথ ধরে এক জায়গা থেকে অন্য জায়গায় যায়, সেই পুরো কাঠামো। বাস্তব modern CPU-তে একটিমাত্র central "data bus" ধরে সবকিছু চলাচল করে — এমনটা ধরে নেওয়া ঠিক নয়। অনেক ধরনের internal connection, datapath এবং interconnect থাকতে পারে।
একটি CPU-কে 64-bit বলা মানেই তার ভেতরে একটি মাত্র 64-wire data bus আছে — এমন নয়। এখানে বোঝার সুবিধার জন্য আমরা একটি 64-bit-wide datapath ধরে নিচ্ছি, যেখানে 64টি bit parallel-এ represent/transfer করা যায়।
Register A ──┐
├──► MUX ──► ALU ──► Register C
Register B ──┘multiplexer (MUX): একটি electronic selector
MUX-এর কাজ traffic police-এর মতো অন্য register-কে রাস্তা থেকে সরিয়ে দেওয়া নয়। MUX-এর একাধিক input থাকে এবং একটি output থাকে। Select signal বলে দেয় কোন input-টি output-এ দেখা যাবে।
A ──┐
B ──┤
C ──┤──► MUX ──► ALU input
D ──┘ ↑
Selectধরা যাক select signal বলছে B বেছে নিতে হবে। তাহলে MUX-এর output-এ B-এর বর্তমান bit pattern দেখা যাবে। এখানে MUX-এর ভেতরেই selection-এর logic আছে। Register A, B, C, D সবাই "একই wire-এ নিজেদের data ঠেলে দিচ্ছে" — এমনটা ধরে নেওয়া উচিত নয়।
MUX নিজে clock-এর জন্য অপেক্ষা করে না। Select signal বা input বদলালে MUX-এর output-ও propagation delay-এর পর নতুন value-তে settle করে। Clock তার output-কে "চালু" করে না।
decoder: কোন storage location-এ write হবে?
এবার ধরা যাক ALU একটি result তৈরি করেছে এবং আমরা চাই সেটি Register C-তে store হোক। একটি simplified register structure ধরে ভাবা যাক — এখানে decoder-এর কাজ শুরু।
destination
│
▼
┌─────────┐
│ Decoder │
└────┬────┘
│
┌────────┼────────┐
▼ ▼ ▼
WE_A WE_B WE_C
0 0 1এখানে WE মানে Write Enable। Destination field-এ যদি Register C-এর identifier থাকে, decoder সেটিকে এমন control signal-এ পরিণত করতে পারে যাতে Register C-এর write enable active হয় এবং অন্য register-গুলোর write enable inactive থাকে। তারপর যখন clock-এর নির্দিষ্ট edge আসে, Register C input-এ থাকা value-টি capture করতে পারে।
এখানে একটা গুরুত্বপূর্ণ distinction আছে:
Decoder result-টাকে physically "একটা দরজা দিয়ে ঢুকিয়ে" C-তে পাঠায় না।
বরং result-এর datapath Register C-সহ প্রয়োজনীয় destination register-গুলোর input-এ পৌঁছাতে পারে; decoder/control logic ঠিক করে কোন register সেই value capture করবে। অর্থাৎ:
ALU result ─────────────► Register A
────────────► Register B
────────────► Register C
────────────► Register D
Write Enable:
A B C D
0 0 1 0বাস্তব register file আরও sophisticated হতে পারে, কিন্তু এই simplified model-টি একটি গুরুত্বপূর্ণ ধারণা শেখায়:
Datapath value কোথায় যেতে পারে সেটা নির্ধারণ করে; control logic নির্ধারণ করে কোন path/operation/destination ব্যবহার হবে; storage element নির্দিষ্ট timing boundary-তে state ধরে রাখে।
Clock: CPU-র timing boundary
এখন পর্যন্ত আমরা দেখলাম:
- logic gate কীভাবে computation তৈরি করে,
- ALU কীভাবে সেই computation করে,
- register কীভাবে binary state ধরে রাখে,
- আর datapath ও control logic কীভাবে ঠিক করে কোন data কোন operation-এর মধ্যে দিয়ে যাবে।
কিন্তু একটা প্রশ্ন রয়ে যায়: কখন একটি নতুন value-কে "officially" state হিসেবে ধরে নেওয়া হবে? এখানেই আসে clock।
CPU-র clock সাধারণত একটি periodic electrical signal। এটি নির্দিষ্ট rhythm-এ 0 এবং 1-এর মতো state-এর মধ্যে পরিবর্তিত হয়। একটি 3 GHz clock-এর ক্ষেত্রে প্রতি সেকেন্ডে প্রায় 3 billion clock cycle হয়।
কিন্তু এখানে একটি খুব গুরুত্বপূর্ণ ভুল ধারণা এড়িয়ে যেতে হবে:
Clock CPU-র প্রতিটি component-কে "এখন কাজ শুরু করো" বলে না।
MUX বা ALU clock-এর tick-এর জন্য অপেক্ষা করে বসে থাকে না। এগুলো combinational logic। Input বা control signal বদলালে তারা electrical signal-এর propagation-এর মাধ্যমে নতুন output তৈরি করে।
তাহলে clock করে কী? Clock মূলত CPU-র state change-এর timing framework তৈরি করে। বিশেষ করে register-এর মতো storage element নির্দিষ্ট clock edge-এ তাদের input-এর value capture করতে পারে।
একটি simplified CPU-কে এভাবে ভাবা যায়:
Current State
│
▼
┌──────────────────┐
│ Combinational │
│ Logic │
│ │
│ MUX → ALU → ... │
└────────┬─────────┘
│
▼
Next State
│
CLOCK EDGE
│
▼
Current Stateএখানে clock হলো সেই boundary, যেখানে storage element নতুন state capture করার সুযোগ পায়।
তাহলে clock দরকার কেন?
কারণ বাস্তব electronic signal instantaneously এক জায়গা থেকে আরেক জায়গায় পৌঁছায় না। Logic gate-এর মধ্যে propagation delay থাকে। একটি বড় combinational circuit-এর input বদলানোর পর তার output-এর প্রতিটি bit একই মুহূর্তে final অবস্থায় পৌঁছাবে — এমন নিশ্চয়তা নেই।
ধরা যাক Register A এবং B থেকে ALU-তে data গেল। ALU-র ভেতরের অনেকগুলো gate-এর মধ্য দিয়ে signal propagate করার পর output ধীরে ধীরে একটি stable pattern-এ settle করবে। CPU যদি জানতেই না পারে কখন এই result-টাকে valid ধরে পরের register-এ capture করা নিরাপদ, তাহলে বিভিন্ন অংশের মধ্যে coordination কঠিন হয়ে যায়।
Clock একটি shared timing framework দেয়। একটি simplified synchronous design-এ আমরা বলতে পারি:
- একটি clock edge-এ register current state capture করে।
- সেই state থেকে combinational logic তার output তৈরি করতে থাকে।
- কিছু সময় পরে output যথেষ্ট stable হয়।
- পরবর্তী clock edge-এ destination register সেই result capture করে।
এই কারণেই clock-কে "CPU-র heartbeat" বলা যায়। Clock নিজে MUX বা ALU-কে চালায় না। বরং এটি পুরো synchronous system-কে একটি common timing reference দেয়। আরেকভাবে বললে:
Control logic ঠিক করে কী হবে।
Combinational logic সেই operation-এর electrical result তৈরি করে।
Clock ঠিক করে কখন নতুন state capture করা হবে।
২ + ৩ = ৫: প্রসেসরের ভেতরের একটি simplified journey
এবার আমাদের তৈরি করা mental model ব্যবহার করে 2 + 3-এর journey দেখি। ধরে নিই:
Register A = 0010 (2) Register B = 0011 (3)
এবং আমাদের লক্ষ্য:
Register A + Register B → Register C
১ · current state
এই মুহূর্তে Register A এবং Register B তাদের respective binary value ধরে রেখেছে, আর সেই value registers-এর output-এ available।
A = 0010 B = 0011
২ · control signal operation-টাকে configure করে
CPU-র control logic এমন signal তৈরি করে যাতে:
MUX 1 → Register A MUX 2 → Register B ALU → ADD Destination → Register C WE_C → 1
অর্থাৎ, কোন data ALU-তে যাবে, ALU কী operation করবে এবং result কোন register-এ store হবে — এসব configure করা হলো।
৩ · MUX ও ALU কাজ করে — clock তাদের "start" করায় না
MUX-গুলো তাদের select signal অনুযায়ী A এবং B-এর value ALU-র input-এ present করে।
A = 0010 ──► MUX ──► ALU input A B = 0011 ──► MUX ──► ALU input B
ALU-র ভেতরের combinational logic এই input pattern-এর ওপর কাজ করতে থাকে। Signal বিভিন্ন logic gate-এর মধ্য দিয়ে propagate করে এবং propagation delay পেরোনোর পর ALU output settle করে:
0010 + 0011 ────── 0101
অর্থাৎ ALU output এখন 0101। এখানে clock এসে ALU-কে "যোগ করো" বলেনি। Control signal ALU-কে ADD operation-এর জন্য configure করেছে, আর combinational logic input অনুযায়ী result তৈরি করেছে।
৪ · clock edge: Register C result capture করে
এখন Register C-এর write enable active এবং ALU result তার input-এ available। পরবর্তী appropriate clock edge-এ Register C সেই value capture করে:
ALU result = 0101
│
▼
Register C captures
│
▼
C = 0101 (5)পুরো ব্যাপারটাকে এক লাইনে
Current State
↓
Control + Combinational Logic
↓
Next State
↓
Clock Edge
↓
New Current Stateএখানে সবচেয়ে গুরুত্বপূর্ণ বিষয় হলো:
Clock MUX-কে চালু করেনি।
Clock ALU-কে হিসাব শুরু করতে বলেনি।
Clock data-কে ঠেলে Register C-তে পাঠায়নি।
বরং MUX এবং ALU তাদের input/control signal অনুযায়ী continuously respond করেছে। Clock শুধু একটি নির্দিষ্ট timing boundary দিয়েছে যেখানে Register C result-টিকে capture করতে পারে। এটাই synchronous digital logic বোঝার সবচেয়ে গুরুত্বপূর্ণ mental model-গুলোর একটি।
সবকিছু এত সরল নয়
এই আর্টিকেলে CPU-কে ইচ্ছাকৃতভাবে অনেক সহজ করে দেখানো হয়েছে।
বাস্তব processor-এ pipeline, cache hierarchy, branch prediction, out-of-order execution, multiple execution unit, register renaming, sophisticated interconnect এবং আরও অনেক mechanism থাকে। একটি modern CPU একই সময়ে একাধিক instruction-এর বিভিন্ন stage নিয়ে কাজ করতে পারে — ফলে "একটি instruction → একটি ALU calculation → একটি clock tick" এমন সরল model বাস্তব CPU-র ক্ষেত্রে যথেষ্ট নয়।
এখানে আমরা বরং একটি conceptual synchronous datapath ব্যবহার করেছি — নিচের চারটি মৌলিক ধারণা পরিষ্কার করার জন্য, বাস্তব CPU-র পুরো ছবি আঁকার জন্য নয়। এবং এটাই এই model-এর শক্তি: বাস্তব CPU-র complexity এই basic idea-গুলোকে বাতিল করে না, বরং এগুলোর ওপরেই অনেক বেশি sophisticated structure তৈরি করে।
- CPU "গণিত বোঝে" না — hardware-এর গণিতের অর্থ বোঝার দরকার নেই, তার physical structure-ই সংশ্লিষ্ট logical transformation-টা implement করে। আমরা সেই electrical pattern-কে number ও operation হিসেবে interpret করি।
- Combinational logic হিসাব করে; registers state ধরে রাখে — ALU ও MUX-এর মতো circuit input ও control signal অনুযায়ী output তৈরি করে; register সেই state ধরে রাখে।
- Control logic ঠিক করে কী হবে; clock ঠিক করে কখন state update হবে — Clock MUX বা ALU-কে "start" করায় না, এটি storage element-এর state capture করার timing boundary দেয়।
- MUX হলো electronic selector — অনেক input-এর মধ্যে select signal অনুযায়ী একটি input output-এ পাঠায়।
- Decoder/control logic destination নির্ধারণে সাহায্য করে — result-কে কোনো physical "door" দিয়ে Register C-তে ঢোকানোর বদলে, এটি ঠিক করে কোন register নতুন value capture করবে।
- CPU-র ভেতরে একটিমাত্র universal data bus আছে — এমন নয়। বাস্তব processor-এ অনেক ধরনের datapath ও interconnect থাকতে পারে; এখানে বোঝার সুবিধার জন্য simplified datapath ব্যবহার করা হয়েছে।
- সবচেয়ে গুরুত্বপূর্ণ mental model: Current State → Combinational Logic → Next State → Clock Edge → New Current State।