Clock ও Timing — যেখানে 'তাৎক্ষণিক' ধারণাটা ভেঙে যায়
Clock and Timing
কোনো circuit-ই তাৎক্ষণিক কাজ করে না — প্রতিটা gate সময় নেয়, আর সেই সময়টাই setup/hold constraint, max clock frequency, আর metastability-র জন্ম দেয়। এই লেসনেই ডিজিটাল লজিকের বিমূর্ত জগৎ প্রথমবার সরাসরি physics-এর মুখোমুখি হয়।
আগে এটা বুঝি
লেসন ২ থেকে এই পর্যন্ত আমরা একটা সরলীকরণে চুপচাপ বিশ্বাস রেখেছি — একটা AND gate-এ input বদলালে output সাথে সাথে বদলে যায়। একটা 32-bit adder-এ input দিলে সাথে সাথে যোগফল বেরিয়ে আসে। একটা clock edge আসে, আর সব flip-flop একই মুহূর্তে, নিখুঁতভাবে আপডেট হয়।
এই তিনটাই মিথ্যা কথা। ছোট মিথ্যা — কিন্তু মিথ্যা।
বাস্তবে একটা transistor সুইচ করতে সময় লাগে (charge/discharge-এর জন্য বাস্তব physics)। একটা তার দিয়ে signal ভ্রমণ করতে সময় লাগে। একটা ৩২-স্তরের gate-চেইনের মধ্য দিয়ে সিগন্যাল পার হতে ৩২ গুণ বেশি সময় লাগে একটা single gate-এর চেয়ে। আর “clock edge একই মুহূর্তে সবখানে পৌঁছায়” — একটা modern chip-এ কোটি কোটি flip-flop-এর প্রতিটার কাছে, একদম নিখুঁতভাবে একই মুহূর্তে? সেটাও physically অসম্ভব।
এই লেসনটাই সেই জায়গা যেখানে আমরা এই মিথ্যাগুলো ঠিক করি। আর এটা নিছক তাত্ত্বিক সূক্ষ্মতা না — এই লেসনের ফলাফলই ব্যাখ্যা করে কেন একটা CPU অসীম গতিতে চলতে পারে না, কেন ২০০০-এর দশকের “GHz race” হঠাৎ থেমে গেল, আর কেন মাঝেমধ্যে একটা perfectly-designed circuit “ঠিক নিয়ম মেনেও” ভুল আচরণ করে — যাকে বলে metastability, hardware reliability engineering-এর একটা সত্যিকারের, এখনো-সক্রিয় মাথাব্যথা।
চলুন সময়ের সাথে পরিচয় করাই।
মূল ধারণা
Clock — synchronous সিস্টেমের হৃদস্পন্দন
Clock একটা সহজ জিনিস — একটা periodic square wave, 0 আর 1-এর মধ্যে নিয়মিত ওঠানামা করে।
CLK ‾‾\___/‾‾\___/‾‾\___/‾‾\___
│←──T──→│
(rising edge) (rising edge)তিনটা প্যারামিটার:
- Period (
T) — একটা সম্পূর্ণ চক্রের সময়, সাধারণত ns (nanosecond) বা ps (picosecond)-এ মাপা হয় - Frequency (
f = 1/T) — প্রতি সেকেন্ডে কতটা চক্র, Hz-এ (আধুনিক CPU-তে GHz — বিলিয়ন চক্র প্রতি সেকেন্ড) - Duty cycle — প্রতি চক্রের কত শতাংশ সময় সংকেত high থাকে (সাধারণত ৫০%, যদিও সবসময় না)
লেসন ৯-এ আমরা দেখেছি D flip-flop একটা নির্দিষ্ট edge-এ (সাধারণত rising, 0→1) trigger হয়। Synchronous সিস্টেম মানে সিস্টেমের প্রতিটা flip-flop একই clock signal (বা তার থেকে derived একটা signal) দিয়ে trigger হয় — লেসন ১০-এর register-এর “common CLK bus” ধারণাটাই পুরো চিপ জুড়ে বিস্তৃত হয়। এটাই এই পুরো module-এর ভিত্তি — সব state পরিবর্তন একই ঘড়ির তালে, একসাথে ঘটে বলে সিস্টেমটা যুক্তিসঙ্গতভাবে বিশ্লেষণযোগ্য থাকে।
কিন্তু “একই মুহূর্তে” কথাটা এখনই প্রশ্নবিদ্ধ করা দরকার — বাকি লেসন জুড়ে দেখব ঠিক কতটা “একই মুহূর্তে” সত্যিই সত্যি।
Propagation delay — কিছুই তাৎক্ষণিক না
একটা logic gate-এ input বদলানোর পর output স্থির, সঠিক মানে পৌঁছাতে যে সময় লাগে, তাকে বলে propagation delay (t_pd)।
কেন এই delay আছে? একটা transistor আসলে একটা সুইচ, কিন্তু একটা বাস্তব, physical সুইচ — তাকে ON/OFF করতে গেটের ক্যাপাসিট্যান্স চার্জ/ডিসচার্জ করতে হয়, যেটা সসীম সময় নেয় (RC time constant — resistance × capacitance)। এমনকি তার (wire) দিয়ে signal পাঠাতেও সময় লাগে, বিশেষত আধুনিক ছোট প্রসেসে যেখানে তারই resistance/capacitance-এর প্রধান উৎস হয়ে দাঁড়ায়।
একটা circuit-এর (একাধিক gate-এর chain) propagation delay হলো সবচেয়ে দীর্ঘ পথ ধরে input থেকে output পর্যন্ত যেতে সময় লাগা মোট delay — একে বলে critical path।
IN ──►[G1: 0.05ns]──►[G2: 0.06ns]──►[G3: 0.05ns]──►[G4: 0.08ns]──► OUT
t=0.05ns t=0.11ns t=0.16ns t=0.24ns
↑
output এখানেই সত্যিকারের স্থির হয় —
এর আগে অস্থায়ীভাবে ভুল মানও দেখাতে পারেSetup time আর hold time
D flip-flop একটা clock edge-এ D input-এর মান “capture” করে। কিন্তু capture করাটা তাৎক্ষণিক কোনো ঘটনা না — flip-flop-এর ভেতরের bistable গঠন (লেসন ৯) সঠিকভাবে একটা দিকে settle করতে D-এর মান কিছুক্ষণ স্থির থাকা দরকার, edge-এর আগে এবং পরে দুই দিকেই।
- Setup time (
t_su) — clock edge আসার আগে D input কতক্ষণ স্থির থাকতে হবে - Hold time (
t_h) — clock edge আসার পরে D input কতক্ষণ স্থির থাকতে হবে
CLK __/‾‾\_______/‾‾\______
↑
D ═══════██████═══════════
│←t_su→│←t_h→│
↑
D পুরোপুরি স্থির — capture নিরাপদ
Q ───────────████████═════CLK __/‾‾\_______/‾‾\______
↑
D ═══════▓▓▓╳═════════════
↑ D এখানে বদলাচ্ছে — t_su window-এর ভেতরেই
Q ───────────░░░░═════════
↑
metastable — Q একটা অনির্ধারিত সময় ধরে
0-ও না, 1-ও না, দুইয়ের মাঝামাঝি ভোল্টেজে থাকতে পারেযদি D input edge-এর খুব কাছাকাছি সময়ে বদলায় (setup বা hold window ভায়োলেট করে), flip-flop-এর অভ্যন্তরীণ bistable node দুইটা স্থিতিশীল অবস্থার (0 আর 1) মাঝামাঝি একটা অনির্ধারিত ভোল্টেজে আটকে যেতে পারে — এই অবস্থাকে বলে metastability।
Metastability — একটা বাস্তব, বিখ্যাত সমস্যা
এটা তাত্ত্বিক কৌতূহল না। Metastable অবস্থায় flip-flop-এর output একটা অনির্ধারিত সময় ধরে (তাত্ত্বিকভাবে সীমাহীন, ব্যবহারিকভাবে সাধারণত কয়েক picosecond থেকে কয়েক nanosecond) 0 আর 1-এর মাঝামাঝি একটা ভোল্টেজে থেকে যেতে পারে, তারপর দৈবক্রমে যেকোনো দিকে settle করে — কোন দিকে যাবে তা পূর্বনির্ধারিত না।
কেন এটা সম্ভব? flip-flop-এর ভেতরের cross-coupled গঠন (লেসন ৯) একটা positive feedback loop — এটা দুইটা স্থিতিশীল অবস্থার (একটা “unstable equilibrium”-এর দুই পাশে) মধ্যে দোলনরত একটা বল-এর মতো। বলটা ঠিক চূড়ায় (unstable equilibrium point-এ) বসিয়ে দিলে, তাত্ত্বিকভাবে সেটা অনন্তকাল সেখানেই থাকতে পারে — বাস্তবে সামান্যতম noise-ই তাকে একদিকে ঠেলে দেয়, কিন্তু কতক্ষণ লাগবে তা নিশ্চিত না।
Mean Time Between Failures (MTBF) — engineer-রা metastability সম্পূর্ণ দূর করতে পারেন না, শুধু এটাকে পরিসংখ্যানগতভাবে বিরল করতে পারেন:
যেখানে t_r হলো resolve করার জন্য বরাদ্দ বাড়তি সময় (synchronizer-এ যত বেশি সময় দেওয়া হয়), τ আর T_0 flip-flop-এর নিজস্ব বৈশিষ্ট্য (প্রক্রিয়া প্রযুক্তির উপর নির্ভর করে), আর f_clk, f_data clock ও input signal-এর frequency। মূল অন্তর্দৃষ্টি — t_r সূচকীয়ভাবে (e^{t_r/\tau}) MTBF বাড়ায়, তাই মাত্র কয়েকটা বাড়তি clock cycle অপেক্ষা করলেই ব্যর্থতার সম্ভাবনা astronomically কমে যায় — কিন্তু কখনো শূন্যে নামে না।
এই কারণেই দুইটা ভিন্ন clock domain-এর মধ্যে signal পাঠানোর সময় (Level 9-এ distributed systems-এর “একাধিক ঘড়ি” সমস্যার হার্ডওয়্যার-স্তরের পূর্বসূরি) একটা double-flop synchronizer ব্যবহার করা হয় — দুইটা flip-flop ধারাবাহিকভাবে বসিয়ে, প্রথমটা যদি metastable হয়েও যায়, দ্বিতীয়টা resolve করার জন্য একটা পুরো cycle বাড়তি সময় পায়।
ভেতরে কী ঘটছে
যেখানে delay আসলে জন্মায় — আর কেন এটা তাপমাত্রার সাথে বদলায়
RC delay — transistor আর তারের পদার্থবিজ্ঞান
একটা CMOS gate-এর output একটা capacitor-এর মতো আচরণ করে (পরের gate-এর input, তার নিজের parasitic capacitance)। Output বদলাতে সেই capacitor-কে চার্জ বা ডিসচার্জ করতে হয় একটা finite resistance (transistor-এর নিজস্ব ON-resistance) দিয়ে — আর RC সময় ধ্রুবক অনুযায়ী, এটা সসীম সময় নেয়।
আধুনিক (কয়েক ন্যানোমিটার) প্রসেসে একটা গুরুত্বপূর্ণ, প্রায়ই-অবাক-করা সত্য — তারের delay গেটের delay-কে ছাড়িয়ে যায়। ট্রানজিস্টর ছোট হতে হতে দ্রুততর হয়েছে, কিন্তু তার (wire) একই অনুপাতে দ্রুত হয়নি — resistance/capacitance per unit length বরং প্রসেস ছোট হওয়ার সাথে বেড়েছে। তাই আধুনিক চিপ ডিজাইনে দূরত্বের গুরুত্ব ক্রমশ বাড়ছে — দুইটা যৌক্তিকভাবে সংযুক্ত ব্লক physically কাছাকাছি বসানো (floorplanning) এখন একটা timing-critical সিদ্ধান্ত, শুধু একটা layout সৌন্দর্যের প্রশ্ন না।
PVT — একটা delay সংখ্যা কখনো একটাই সংখ্যা না
গত অংশের উদাহরণে “0.05ns” জাতীয় সংখ্যা লেখা হয়েছিল যেন এটা একটা ধ্রুবক সত্য। বাস্তবে একটা gate-এর delay নির্ভর করে তিনটা জিনিসের উপর, যাদের সংক্ষেপে বলে PVT:
- Process — একই ডিজাইনের দুইটা chip fabrication-এর সামান্য পার্থক্যের কারণে সামান্য ভিন্ন গতির হতে পারে (transistor-এর প্রকৃত মাত্রা কখনো ১০০% নিখুঁতভাবে identical হয় না)
- Voltage — কম supply voltage-এ transistor ধীরে সুইচ করে (কম “push”)
- Temperature — বেশি তাপমাত্রায় transistor-এর resistance বাড়ে, ফলে delay বাড়ে
তাই চিপ ডিজাইনার-রা একটা single delay সংখ্যার বদলে corner-এ কাজ করেন — “slow corner” (worst-case slow process, নিম্ন voltage, উচ্চ তাপমাত্রা — সবচেয়ে বেশি delay) আর “fast corner” (সবচেয়ে কম delay)। একটা ডিজাইন তার টার্গেট frequency-তে চালানোর জন্য slow corner-এও timing মেনে চলতে হবে — এটাই নিশ্চিত করে একটা চিপ গরম দিনে, দুর্বল ব্যাটারিতে, সবচেয়ে খারাপ ভাবে তৈরি হওয়া চিপেও কাজ করবে।
Static Timing Analysis (STA) — বাস্তব ইঞ্জিনিয়ারিং শৃঙ্খলা
একটা আধুনিক CPU চিপে কোটি কোটি gate, লক্ষ লক্ষ flip-flop-থেকে-flip-flop path। প্রতিটা path হাতে যাচাই করা অসম্ভব। তাই আছে Static Timing Analysis (STA) টুল (যেমন Synopsys PrimeTime, Cadence Tempus) — এগুলো পুরো circuit-কে একটা graph হিসেবে ধরে, প্রতিটা register-থেকে-register path-এর delay গণনা করে, আর দেখে কোনো path টার্গেট clock period-এর সীমা লঙ্ঘন করছে কিনা।
একটা design যখন সব path-এ timing মেনে চলে, তখন বলা হয় সেটা timing closure পেয়েছে — বাস্তব চিপ ডিজাইনে এটা প্রায়ই একটা প্রজেক্টের সবচেয়ে দীর্ঘ, সবচেয়ে হতাশাজনক পর্যায়, মাসের পর মাস ধরে চলতে পারে। “আমাদের ডিজাইন timing মিট করছে না” — একটা বাস্তব chip design team-এর সবচেয়ে সাধারণ সংকট।
উদাহরণ
সম্পূর্ণ সংখ্যাগত উদাহরণ — max clock frequency বের করা
একটা accumulator circuit কল্পনা করুন — লেসন ১০-এর মতো একটা register, যার output একটা 4-bit ripple-carry adder-এ ফিরে যায় (লেসন ৫-এর সেই adder), আর যোগফল আবার register-এর input-এ ফিরে আসে।
Critical path — register-এর clock edge থেকে শুরু করে, নতুন মান পরের register-এ নিরাপদে capture হওয়া পর্যন্ত:
যেখানে:
t_cq— flip-flop-এর clock-to-Q delay (clock edge থেকে output স্থির হওয়া পর্যন্ত)t_comb— critical combinational path-এর মোট delay (এখানে: 4-bit ripple-carry adder-এর carry chain)t_su— গন্তব্য flip-flop-এর setup time
ধরা যাক নিচের সংখ্যাগুলো (illustrative, বাস্তব কোনো নির্দিষ্ট chip-এর spec না, কিন্তু বাস্তবসম্মত মাত্রার):
| প্যারামিটার | মান |
|---|---|
t_cq (flip-flop clock-to-Q) | 0.20 ns |
| প্রতি full-adder stage-এর carry propagation | 0.15 ns |
| Adder-এ stage সংখ্যা (4-bit ripple carry) | 4 |
t_comb (মোট) | 4 × 0.15 = 0.60 ns |
t_su (setup time) | 0.15 ns |
| Clock skew (ধরে নেওয়া) | 0.05 ns |
এই circuit ১ GHz-এর বেশি clock করা যাবে না — চেষ্টা করলে setup violation ঘটবে, আর তার ফল metastability, ভুল ফলাফল, বা দুটোই।
Hold time constraint — একটা সম্পূর্ণ আলাদা সমীকরণ
উপরের সূত্রটা max frequency নিয়ন্ত্রণ করে — কিন্তু hold time-এর সমস্যাটা frequency-নিরপেক্ষ, আর এটা প্রায়ই ভুল বোঝা হয়।
Hold violation ঘটে যদি নতুন ডেটা খুব তাড়াতাড়ি পরের register-এ পৌঁছে যায় — এত তাড়াতাড়ি যে পুরনো মান capture হওয়ার আগেই নতুন মান flip-flop-এর input-এ চলে আসে। এর জন্য দরকার সবচেয়ে দ্রুততম path-এর delay, যাকে বলে contamination delay (t_cd) — propagation delay (t_pd, worst-case/সবচেয়ে ধীর path) থেকে আলাদা একটা সংখ্যা:
লক্ষ্য করুন — এই সমীকরণে T_clock (clock period) কোথাও নেই। তার মানে clock ধীর করলে hold violation ঠিক হয় না — এটা একটা genuinely ভিন্ন সমস্যা, ভিন্ন সমাধান দরকার (সবচেয়ে দ্রুত path-এ ইচ্ছাকৃতভাবে বাড়তি delay যোগ করা, “buffer insertion”, যাতে নতুন ডেটা এত তাড়াতাড়ি না পৌঁছায়)। এই সূক্ষ্মতাটা “misconception” অংশে আরও বিস্তারিত।
নিজে চালিয়ে দেখুন
Digital (hneemann)-এ propagation delay আর glitch পর্যবেক্ষণ
১. Digital simulator-এ একটা সাধারণ circuit বানান: OUT = (A AND B) OR (NOT A AND C) — একটা 2-input MUX-এর সমতুল্য।
২. Digital-এর settings-এ gate delay simulate করার mode চালু করুন (প্রতিটা gate-এর একটা non-zero delay থাকবে)।
৩. একটা oscilloscope/timing-diagram component যোগ করে A, B, C, OUT একসাথে দেখুন।
৪. এমনভাবে input সেট করুন যাতে B=1, C=1 (দুই দিক থেকেই একই ফলাফল আসার কথা), তারপর A-কে 0 থেকে 1-এ টগল করুন। তাত্ত্বিকভাবে OUT সবসময় 1 থাকার কথা (B আর C দুইটাই 1) — কিন্তু বাস্তবে দুইটা path-এর ভিন্ন delay-র কারণে OUT-এ একটা ক্ষণস্থায়ী 0 glitch দেখা যেতে পারে (static hazard, K-map লেসনের ধারণারই timing-জনিত রূপ)।
৫. এই glitch-টাই যদি অন্য কোনো circuit-এর clock input বা edge-sensitive input-এ যায়, সেটা একটা genuine bug তৈরি করতে পারে — এই কারণেই “combinational logic সবসময় স্থির হতে সময় দিন, তারপর তার output ব্যবহার করুন” এই নিয়মটা এত গুরুত্বপূর্ণ।
Combinational logic input বদলানোর পরপরই স্থির output দেয় না — বিভিন্ন path দিয়ে ভিন্ন delay-তে সিগন্যাল পৌঁছালে output অস্থায়ীভাবে ভুল মান (glitch) দেখাতে পারে, আগের লেসনগুলোর 'তাৎক্ষণিক' সরলীকরণটা যা লুকিয়ে রেখেছিল।
Critical-path ক্যালকুলেটর — Python দিয়ে max frequency যাচাই
def max_frequency(t_cq, per_stage_delay, n_stages, t_su, t_skew=0.0):
"""T_clock >= t_cq + t_comb + t_su + t_skew → f_max = 1/T_clock"""
t_comb = per_stage_delay * n_stages
t_clock_min = t_cq + t_comb + t_su + t_skew
f_max_ghz = 1.0 / t_clock_min # ns এককে হলে ফলাফল সরাসরি GHz
return t_clock_min, f_max_ghz
t_cq, t_su, t_skew = 0.20, 0.15, 0.05
print(f"{'bit-width':>10} {'t_comb(ns)':>12} {'T_clock(ns)':>13} {'f_max(GHz)':>12}")
for n in [4, 8, 16, 32, 64]:
t_clock, f_max = max_frequency(t_cq, 0.15, n, t_su, t_skew)
t_comb = 0.15 * n
print(f"{n:>10} {t_comb:>12.2f} {t_clock:>13.2f} {f_max:>12.3f}")প্রত্যাশিত output:
bit-width t_comb(ns) T_clock(ns) f_max(GHz)
4 0.60 1.00 1.000
8 1.20 1.60 0.625
16 2.40 2.80 0.357
32 4.80 5.20 0.192
64 9.60 10.00 0.100লক্ষ্য করুন bit-width দ্বিগুণ হওয়ার সাথে সাথে f_max প্রায় অর্ধেক হয়ে যাচ্ছে — ripple-carry-র O(n) delay সরাসরি f_max-এ প্রতিফলিত হচ্ছে। এবার একটা carry-lookahead-এর মতো O(log n) adder ধরে (per-stage delay একই কিন্তু stage সংখ্যা log₂n) তুলনা করুন — নিচের কোড যোগ করুন:
import math
print(f"\n{'bit-width':>10} {'ripple f_max':>14} {'lookahead-স্টাইল f_max':>24}")
for n in [4, 8, 16, 32, 64]:
_, f_ripple = max_frequency(t_cq, 0.15, n, t_su, t_skew)
_, f_lookahead = max_frequency(t_cq, 0.15, max(1, round(math.log2(n))) * 2, t_su, t_skew)
print(f"{n:>10} {f_ripple:>14.3f} {f_lookahead:>24.3f}")n = 64-এ ripple ~0.1 GHz, কিন্তু log-depth-এর কাছাকাছি একটা adder (এখানে 2·log₂n stage ধরে illustrative) ~0.5+ GHz-এর কাছাকাছি থাকে — কয়েক গুণ দ্রুততর, ঠিক লেসন ৫-এর carry-lookahead adder-এর প্রতিশ্রুতি অনুযায়ী।
উপরের 'example' অংশের হাতে-করা হিসাবটা কোডে reproduce করে, আর বিভিন্ন bit-width-এ ripple-carry adder-এর max frequency কীভাবে কমে তা সরাসরি দেখায়।
নিজে বানান
Mini Static Timing Analyzer — Python-এ DAG longest-path
- Circuit-কে একটা graph হিসেবে মডেল করুন — node = register, edge = combinational path (delay সহ)
- Topological order-এ প্রতিটা node-এ পৌঁছানোর সবচেয়ে দীর্ঘ (worst-case) path বের করুন
- সবচেয়ে দীর্ঘ path-টাকেই circuit-এর critical path হিসেবে রিপোর্ট করুন
- একটা target clock period দিয়ে verify করুন circuit সেই frequency-তে timing মেনে চলে কি না
একটা real STA টুলের সবচেয়ে মৌলিক অংশ — DAG-তে longest path — মাত্র কয়েক লাইনের topological-sort + dynamic programming।
from collections import defaultdict, deque
class TimingGraph:
"""Node = রেজিস্টার (বা প্রাইমারি ইনপুট/আউটপুট)।
Edge(u, v, delay) = u থেকে v পর্যন্ত combinational path।"""
def __init__(self):
self.edges = defaultdict(list) # u -> [(v, delay, label)]
self.nodes = set()
def add_path(self, u, v, delay, label=""):
self.nodes.add(u)
self.nodes.add(v)
self.edges[u].append((v, delay, label))
def topo_order(self):
indeg = {n: 0 for n in self.nodes}
for u in self.edges:
for v, _, _ in self.edges[u]:
indeg[v] += 1
q = deque([n for n in self.nodes if indeg[n] == 0])
order = []
while q:
u = q.popleft()
order.append(u)
for v, _, _ in self.edges[u]:
indeg[v] -= 1
if indeg[v] == 0:
q.append(v)
return order
def critical_path(self):
"""সবচেয়ে দীর্ঘ (worst-case delay) path পুরো গ্রাফে।"""
order = self.topo_order()
best = {n: 0.0 for n in self.nodes}
parent = {n: None for n in self.nodes}
for u in order:
for v, delay, label in self.edges[u]:
cand = best[u] + delay
if cand > best[v]:
best[v] = cand
parent[v] = (u, label, delay)
end = max(best, key=lambda n: best[n])
path, cur = [], end
while parent[cur] is not None:
u, label, delay = parent[cur]
path.append((u, cur, label, delay))
cur = u
path.reverse()
return best[end], path
# ── উদাহরণ — 'example' সেকশনের 4-bit ripple-carry accumulator ──
g = TimingGraph()
t_cq, t_su = 0.20, 0.15
# রেজিস্টার থেকে অ্যাডারের প্রতিটা স্টেজ, তারপর সেটআপ পর্যন্ত
g.add_path("REG_Q", "carry1", t_cq + 0.15, "clock-to-Q + FA0 carry")
g.add_path("carry1", "carry2", 0.15, "FA1 carry")
g.add_path("carry2", "carry3", 0.15, "FA2 carry")
g.add_path("carry3", "REG_D", 0.15 + t_su, "FA3 carry + setup")
worst, path = g.critical_path()
print(f"Critical path delay: {worst:.2f} ns → f_max = {1/worst:.3f} GHz\n")
for u, v, label, d in path:
print(f" {u:>10} -> {v:<10} {label:<28} +{d:.2f} ns")
target_period = 1.0 # ns — verify করা হচ্ছে
if worst <= target_period:
print(f"\nOK: {target_period} ns clock period-এ timing মেনে চলে।")
else:
print(f"\nFAIL: {target_period} ns period-এ setup violation ({worst:.2f} > {target_period}) ns।")প্রত্যাশিত output:
Critical path delay: 1.00 ns → f_max = 1.000 GHz
REG_Q -> carry1 clock-to-Q + FA0 carry +0.35 ns
carry1 -> carry2 FA1 carry +0.15 ns
carry2 -> carry3 FA2 carry +0.15 ns
carry3 -> REG_D FA3 carry + setup +0.35 ns
OK: 1.0 ns clock period-এ timing মেনে চলে।এই 1.00 ns সংখ্যাটাই “example” সেকশনে হাতে করা হিসাবের সাথে হুবহু মিলে যাচ্ছে — এবার একটা প্রোগ্রামে যাচাইযোগ্য, আর যেকোনো জটিল circuit-এ সহজে scale করা যায় শুধু আরও add_path() কল যোগ করে।
নিজে বাড়ান:
- একটা branching circuit মডেল করুন — একই register থেকে দুইটা ভিন্ন combinational path বেরিয়ে দুইটা ভিন্ন register-এ যাচ্ছে, ভিন্ন delay নিয়ে — verify করুন algorithm সঠিক critical path (দুইটার মধ্যে বেশিটা) বেছে নিচ্ছে
- Multiple clock domain মডেল করুন — প্রতিটা register-এর নিজস্ব target period থাকুক, প্রতিটা আলাদাভাবে যাচাই করুন
- Hold-time checker যোগ করুন — একই গ্রাফে সবচেয়ে ছোট delay-র path (contamination delay) খুঁজে বের করুন (dynamic programming-এ
maxএর বদলেminব্যবহার করে), আরt_holdএর সাথে তুলনা করুন - একটা “what-if” ফিচার যোগ করুন — bit-width বাড়ালে (আরও stage যোগ হলে) নতুন
f_maxস্বয়ংক্রিয়ভাবে recompute করুক
বাস্তব সিস্টেমে
যেখানে timing বাস্তব সিদ্ধান্ত নেয়
Pentium 4 বনাম Core — GHz race-এর ইতিহাস। ২০০০-এর দশকের শুরুতে Intel-এর NetBurst microarchitecture (Pentium 4) অত্যন্ত গভীর pipeline (২০+ স্তর, Level 3/11-এ বিস্তারিত) ব্যবহার করে প্রতিটা স্তরে কম কাজ রেখে খুব উচ্চ clock frequency (৩.৮ GHz পর্যন্ত) পাওয়ার চেষ্টা করেছিল — প্রতিটা স্তরের t_comb ছোট রেখে T_clock ছোট করার কৌশল। কিন্তু এত গভীর pipeline-এ power খরচ ও তাপ উৎপাদন অসহনীয় হয়ে ওঠে (“power wall”)। ২০০৬-এর Core microarchitecture উল্টো পথে হাঁটে — কম clock frequency (২-৩ GHz), কিন্তু প্রতি cycle-এ বেশি কাজ (higher IPC — Instructions Per Cycle) — আর সামগ্রিক পারফরম্যান্সে জিতে যায়। এখান থেকেই আধুনিক CPU ডিজাইনের মূলনীতি — শুধু clock frequency বাড়ানো (এই লেসনের timing budget কমানো) একটা সীমার পরে ক্ষতিকর হয়ে যায়; Level 11-এ IPC, pipelining, ও multi-core দিয়ে performance বাড়ানোর কৌশল বিস্তারিত দেখবেন।
Overclocking। PC enthusiast-রা যখন একটা CPU-কে manufacturer-এর নির্ধারিত frequency-র চেয়ে বেশি clock করেন, তারা কার্যত T_clock-কে সেই timing margin-এর নিচে নামিয়ে দিচ্ছেন যেটা manufacturer safety-র জন্য রেখেছিল (slow-corner PVT বিবেচনা করে)। এই কারণেই overclocked সিস্টেম গরম হলে (temperature বাড়লে delay বাড়ে) বা load বাড়লে (voltage সামান্য কমতে পারে) “unstable” হয়ে পড়ে — সেটাই setup violation, না ধরা পড়া data corruption, বা ক্র্যাশ হিসেবে প্রকাশ পায়।
DRAM timing spec (CAS latency, ইত্যাদি)। BIOS-এ RAM-এর CL, tRCD, tRP, tRAS জাতীয় সংখ্যা — এগুলো এই একই ধরনের setup/hold-সদৃশ timing constraint, শুধু memory chip-এর জন্য। কম latency মানে দ্রুত কিন্তু margin কম, ঠিক এই লেসনের overclocking আলোচনার মতোই একটা trade-off।
Clock domain crossing (CDC) synchronizer। একটা modern SoC-তে (System-on-Chip) ভিন্ন ভিন্ন অংশ ভিন্ন clock frequency-তে চলে (CPU core, USB controller, display controller — প্রতিটার নিজস্ব সর্বোত্তম frequency)। যখন একটা signal এক clock domain থেকে অন্যটায় যায়, সেটা যেকোনো মুহূর্তে setup/hold ভায়োলেট করতে পারে (দুই ঘড়ির মধ্যে কোনো নির্দিষ্ট সম্পর্ক নেই) — তাই double-flop synchronizer (উপরে “concept” অংশে) প্রায় সব আধুনিক চিপে হাজার হাজার জায়গায় ব্যবহৃত হয়।
FPGA timing closure। FPGA ডিজাইনে “place and route” এর পরে timing report না মেলা একটা অত্যন্ত সাধারণ, পরিচিত সমস্যা — সমাধান প্রায়ই critical path ছোট করতে logic পুনর্বিন্যাস করা, বা pipeline stage যোগ করে একটা দীর্ঘ combinational path-কে দুইটা ছোট path-এ ভাগ করা (এটাই মূলত pipelining-এর জন্ম, Level 3/11-এ বিস্তারিত)।
Dynamic frequency scaling / Turbo Boost। আধুনিক CPU রানটাইমে clock frequency পরিবর্তন করে — কম লোডে/উচ্চ তাপমাত্রায় ধীর (নিরাপদ margin), হালকা লোডে ও ঠান্ডা অবস্থায় সাময়িকভাবে দ্রুত (Turbo Boost) চলে। এটা সরাসরি এই লেসনের PVT আলোচনার একটা রানটাইম প্রয়োগ — চিপ নিজেই টের পায় কখন margin বেশি আছে বলে দ্রুত চলা নিরাপদ।
Static Timing Analysis (STA) tools। Synopsys PrimeTime, Cadence Tempus — শিল্পের মান “সাইনঅফ” টুল, যেগুলো একটা চিপ tape-out (উৎপাদনে পাঠানোর) আগে নিশ্চিত করে প্রতিটা path timing মেনে চলছে। একটা মিসড timing path পুরো চিপকে অকেজো করে দিতে পারে — তাই এই টুলগুলোর গুরুত্ব ও দাম দুটোই বিশাল।
যে ভুলগুলো সবাই করে
“Clock edge-এ সবকিছু একই তাৎক্ষণিক মুহূর্তে ঘটে।”
এই পুরো লেসনের বিপরীত দাবি। বাস্তবে clock edge শুধু একটা শুরুর সংকেত — এরপর flip-flop-এর t_cq সময় লাগে output স্থির হতে, তারপর combinational logic-এর t_comb সময় লাগে, তারপর পরের flip-flop-এর t_su সময় লাগে নিরাপদে সেই মান গ্রহণ করতে। “একই মুহূর্তে” আসলে একটা কার্যকর সরলীকরণ যতক্ষণ T_clock যথেষ্ট বড় রাখা হয় সব delay ধরার জন্য — কিন্তু অন্তর্নিহিত বাস্তবতা সবসময় ক্রমিক, সসীম সময়ের একটা শৃঙ্খল।
“Hold time violation ঠিক করতে clock ধীর করলেই যথেষ্ট।”
সম্পূর্ণ ভুল, আর এটা একটা genuinely বিপজ্জনক ভুল ধারণা কারণ এটা স্বজ্ঞার বিপরীত। “Example” সেকশনের hold constraint-এর সমীকরণটা আবার দেখুন — t_cq,min + t_comb,min ≥ t_hold — এখানে T_clock (clock period) কোথাও নেই। Hold violation ঘটে কারণ নতুন ডেটা খুব তাড়াতাড়ি (একই cycle-এর মধ্যে, edge-এর পরপরই) পরের flip-flop-এ পৌঁছে যায় — clock ধীর করলে ভবিষ্যতের edge-টা আরও পরে আসবে ঠিকই, কিন্তু এই cycle-এর মধ্যেই ডেটা যে তাড়াতাড়ি পৌঁছাচ্ছে সেই সমস্যাটা অপরিবর্তিত থাকে। প্রকৃত সমাধান — সবচেয়ে দ্রুত path-এ ইচ্ছাকৃতভাবে বাড়তি delay (buffer) যোগ করা, যাতে নতুন ডেটা এত তাড়াতাড়ি না পৌঁছায়।
“উচ্চতর clock frequency মানেই দ্রুততর CPU।”
“Realworld” অংশের Pentium 4 বনাম Core ইতিহাস এটা সরাসরি খণ্ডন করে। মোট performance আনুমানিকভাবে \text{frequency} \times \text{IPC} (instructions per cycle) — শুধু frequency বাড়ালে যদি IPC কমে যায় (যেমন খুব গভীর pipeline-এ প্রায়ই হয়, Level 3/11), সামগ্রিক performance আসলে কমতে পারে। এই কারণেই ২০০৫-এর পর থেকে CPU marketing “GHz” থেকে সরে গিয়ে “IPC,” “core সংখ্যা,” আর “instructions per second” জাতীয় metric-এর দিকে ঝুঁকেছে।
“Metastability একটা বিরল কর্নার কেস, বাস্তব সিস্টেমে গুরুত্বপূর্ণ না।”
Metastability সম্পূর্ণ দূর করা অসম্ভব — শুধু পরিসংখ্যানগতভাবে বিরল করা যায় (উপরের MTBF সূত্র)। এটা এতটাই বাস্তব যে প্রতিটা modern SoC-তে হাজার হাজার clock-domain-crossing signal-এর প্রতিটার জন্য synchronizer সাবধানে ডিজাইন করা হয় — একটা ভুলভাবে synchronize করা signal পুরো চিপের মধ্যে মাঝেমধ্যে (হয়তো মাসে একবার, হয়তো বছরে একবার — MTBF অনুযায়ী) একটা explainable-না-হওয়া, reproduce-করা-কঠিন bug তৈরি করতে পারে। Hardware reliability engineering-এর একটা বড় অংশ এই “বিরল কিন্তু অনিবার্য” ঘটনাগুলো পরিচালনার জন্যই ব্যয় হয়।
বুঝেছেন কি না দেখুন
1একটা circuit-এ t_cq = 0.25 ns, critical combinational path-এ ৬টা gate প্রতিটার delay 0.10 ns, t_su = 0.20 ns, clock skew 0.10 ns। এই circuit-এর max clock frequency কত?
প্রয়োগ
t_cq = 0.25 ns, critical combinational path-এ ৬টা gate প্রতিটার delay 0.10 ns, t_su = 0.20 ns, clock skew 0.10 ns। এই circuit-এর max clock frequency কত?সরাসরি সূত্র প্রয়োগ:
উত্তর: প্রায় ৮৭০ MHz। এর চেয়ে বেশি frequency-তে চালালে critical path-এর ডেটা setup window-এর মধ্যে সময়মতো পৌঁছাবে না, ফলে setup violation ও সম্ভাব্য metastability।
2একজন সহকর্মী বলছেন, “আমাদের circuit-এ hold violation দেখাচ্ছে, চলুন clock frequency কমিয়ে দিই, তাহলে ঠিক হয়ে যাবে।” তিনি কি ঠিক বলছেন? ব্যাখ্যা করুন।
যুক্তি
না, তিনি ভুল বলছেন — এটা এই লেসনের সবচেয়ে গুরুত্বপূর্ণ misconception-গুলোর একটা।
Hold constraint সমীকরণ (t_cq,min + t_comb,min ≥ t_hold) clock period-এর উপর নির্ভরই করে না — এটা একই clock cycle-এর ভেতরে ঘটা একটা সমস্যা, যেখানে নতুন ডেটা flip-flop-এর hold window বন্ধ হওয়ার আগেই পৌঁছে গিয়ে পুরনো, capture-হতে-থাকা মান নষ্ট করে দিচ্ছে। Clock ধীর করলে ভবিষ্যতের edge আরও দেরিতে আসবে ঠিকই, কিন্তু এই edge-এর ঠিক পরে ডেটা কত তাড়াতাড়ি বদলাচ্ছে — সেটা clock period-এর সাথে সম্পর্কহীন।
সঠিক সমাধান হলো সবচেয়ে দ্রুত (সবচেয়ে কম delay-র) path-এ ইচ্ছাকৃতভাবে বাফার/delay element যোগ করা, যাতে t_cq,min + t_comb,min বেড়ে t_hold-এর বেশি হয়। এটাই কারণ কেন বাস্তব STA টুল-এ setup আর hold — দুইটা সম্পূর্ণ আলাদা check, আলাদা সমাধান কৌশল সহ।
3Metastability MTBF সূত্রে t_r (বাড়তি resolve করার সময়) সূচকীয়ভাবে (e^{t_r/\tau}) MTBF বাড়ায়। এর মানে ব্যবহারিকভাবে কী — একটা synchronizer ডিজাইন করার সময় engineer-রা কী করেন?
যুক্তি
t_r (বাড়তি resolve করার সময়) সূচকীয়ভাবে (e^{t_r/\tau}) MTBF বাড়ায়। এর মানে ব্যবহারিকভাবে কী — একটা synchronizer ডিজাইন করার সময় engineer-রা কী করেন?এর মানে অল্প বাড়তি সময়ও বিশাল নির্ভরযোগ্যতা এনে দেয় — কারণ সূচকীয় ফাংশন খুব দ্রুত বাড়ে।
ব্যবহারিকভাবে, engineer-রা একটা double-flop synchronizer ব্যবহার করেন — সিগন্যালটা প্রথমে একটা flip-flop দিয়ে যায় (যেটা metastable হয়ে যেতে পারে), তারপর সরাসরি ব্যবহার না করে আরেকটা flip-flop দিয়ে পাঠানো হয়, যেটা একটা সম্পূর্ণ clock cycle পরে সেই মান capture করে। এই বাড়তি একটা cycle-ই t_r-এর ভূমিকা পালন করে — প্রথম flip-flop যদি metastable হয়েও যায়, তার resolve করার জন্য প্রায় পুরো একটা clock period সময় থাকে, যা \tau-র (যা picosecond স্কেলের) তুলনায় বিশাল, ফলে সূচকীয়ভাবে MTBF astronomically বেড়ে যায় (প্রায়ই কয়েক বছর থেকে কয়েক শতাব্দী পরিমাণ, যা ব্যবহারিকভাবে “যথেষ্ট নিরাপদ” ধরা হয়)।
গুরুত্বপূর্ণ — এটা metastability-কে দূর করে না, শুধু এত বিরল করে দেয় যে ব্যবহারিক জীবনে ব্যর্থতার সম্ভাবনা উপেক্ষণীয়। কিছু অতি-উচ্চ-নির্ভরযোগ্যতার সিস্টেমে (যেমন aerospace) triple-flop বা আরও রক্ষণশীল synchronizer ব্যবহৃত হয়।
4একটা 16-bit ripple-carry adder-ভিত্তিক accumulator-এর critical path delay 2.65 ns (আর তাই f_max ≈ 377 MHz)। আপনার প্রজেক্টের target 1 GHz। কী কী উপায়ে এটা achieve করা সম্ভব? অন্তত তিনটা কৌশল প্রস্তাব করুন, প্রতিটার trade-off সহ।
ডিজাইন
2.65 ns (আর তাই f_max ≈ 377 MHz)। আপনার প্রজেক্টের target 1 GHz। কী কী উপায়ে এটা achieve করা সম্ভব? অন্তত তিনটা কৌশল প্রস্তাব করুন, প্রতিটার trade-off সহ।তিনটা মূল কৌশল, প্রতিটা এই module-এর আগের লেসনগুলোর সাথে সরাসরি যুক্ত:
১. Adder architecture বদলান (লেসন ৫)। Ripple-carry-র বদলে carry-lookahead বা carry-select adder ব্যবহার করুন — t_comb-কে O(n) থেকে O(log n)-এর কাছাকাছি নামিয়ে আনে। Trade-off: বেশি gate/transistor, বেশি চিপ-এলাকা, বেশি power।
২. Pipeline করুন (Level 3/11-এর পূর্বাভাস)। পুরো adder-কে একটা single combinational block না রেখে দুইটা বা তার বেশি স্তরে ভাগ করুন, মাঝে একটা register বসিয়ে (এই module-এরই লেসন ১০-এর pipeline register)। প্রতিটা স্তরের t_comb কমে যায়, তাই higher frequency সম্ভব হয় — কিন্তু ফলাফল পেতে এখন একাধিক cycle লাগবে (latency বাড়ে, যদিও throughput বাড়তে পারে যদি বারবার নতুন ইনপুট আসতে থাকে)।
৩. Register-এর t_cq বা t_su কমান। ভিন্ন, দ্রুততর flip-flop cell ব্যবহার করুন (chip library-তে প্রায়ই একাধিক গতির flip-flop cell থাকে) — সাধারণত বেশি area/power খরচে।
বাস্তব সিদ্ধান্ত: প্রায়ই ১ আর ২-এর সংমিশ্রণ — প্রথমে adder architecture উন্নত করুন (সস্তা জয়), তারপর যদি এখনো টার্গেট না মেলে, pipelining যোগ করুন (latency-র বিনিময়ে)। এটাই একটা বাস্তব STA-চালিত ডিজাইন iteration-এর সাধারণ প্যাটার্ন — timing miss দেখুন, সবচেয়ে সস্তা fix প্রয়োগ করুন, আবার যাচাই করুন।
5Setup time আর hold time-এর সংজ্ঞা কী — একটা বাক্যে প্রতিটা?
স্মরণ
Setup time (t_su) — active clock edge আসার আগে D input যে ন্যূনতম সময় ধরে স্থির থাকতে হবে, যাতে flip-flop নির্ভরযোগ্যভাবে সেই মান capture করতে পারে।
Hold time (t_h) — active clock edge আসার পরে D input যে ন্যূনতম সময় ধরে স্থির থাকতে হবে, যাতে capture প্রক্রিয়া সম্পূর্ণ ও নির্ভরযোগ্য হয়, নতুন ডেটা এসে পুরনো ডেটার capture নষ্ট না করে।
দুইটাই ভায়োলেট হলে ঝুঁকি একই — metastability — কিন্তু কারণ আর সমাধান সম্পূর্ণ ভিন্ন (setup ভায়োলেশন clock ধীর করে ঠিক করা যায়, hold ভায়োলেশন যায় না — প্রশ্ন ২ দেখুন)।
6একটা বড় চিপে clock signal একটা কেন্দ্রীয় উৎস (PLL) থেকে হাজার হাজার flip-flop পর্যন্ত পৌঁছাতে হয়। কেন এটা “একই মুহূর্তে সবখানে পৌঁছানো” গ্যারান্টি করতে পারে না, আর এই সমস্যার সাধারণ সমাধান কী?
যুক্তি
Clock signal নিজেও একটা বাস্তব electrical signal — এটাকে চিপের এক প্রান্ত থেকে অন্য প্রান্তে পৌঁছাতে wire-এর মধ্য দিয়ে ভ্রমণ করতে হয়, আর সেই ভ্রমণেও সসীম সময় লাগে (এই লেসনের “hood” অংশের RC delay ঠিক এখানেও প্রযোজ্য)। বিভিন্ন flip-flop-এর কাছে clock পৌঁছানোর পথের দৈর্ঘ্য/buffer সংখ্যা ভিন্ন হলে, তারা ভিন্ন ভিন্ন মুহূর্তে edge দেখে — এই পার্থক্যকে বলে clock skew।
সাধারণ সমাধান একটা balanced clock tree (প্রায়ই H-tree টপোলজি নামে পরিচিত) ডিজাইন করা — যেখানে উৎস থেকে প্রতিটা flip-flop পর্যন্ত পথের দৈর্ঘ্য (আর তাই delay) যতটা সম্ভব সমান রাখা হয়, ইচ্ছাকৃতভাবে buffer/wire যোগ করে ছোট পথগুলোকেও দীর্ঘ পথের সমান করে। এতে skew সম্পূর্ণ দূর হয় না, কিন্তু একটা predictable, ছোট সীমার মধ্যে বাঁধা থাকে — যা timing বাজেটে (এই লেসনের সূত্রে t_skew টার্ম হিসেবে) হিসাব করে নেওয়া হয়। বড় চিপে clock tree ডিজাইন নিজেই একটা গুরুত্বপূর্ণ, শ্রমসাধ্য প্রকৌশল কাজ — clock signal একাই চিপের একটা উল্লেখযোগ্য power বাজেট খরচ করে, কারণ এটা প্রতি cycle-এ পুরো tree জুড়ে টগল করে।
এরপর কী
এরপর কী
আমরা এখন জানি register কীভাবে বানাতে হয় (লেসন ১০), আর জানি সময়ের বাস্তবতা — propagation delay, setup/hold, metastability, আর সেই বাস্তবতা থেকে বেরিয়ে আসা max frequency-র সীমা (এই লেসন)।
এই দুইটা টুকরো একসাথে বসালেই একটা সাধারণ, শক্তিশালী কাঠামো বেরিয়ে আসে — state (register-এ রাখা) + input → next state (combinational logic দিয়ে গণনা করা), ঠিক clock-এর নিয়ম মেনে। এটাই finite state machine (FSM) — প্রতিটা sequential circuit-এর আনুষ্ঠানিক কাঠামো, একটা সাধারণ counter থেকে শুরু করে পুরো CPU-র control unit পর্যন্ত সবকিছুর ভিত্তি।
পরের লেসনে আমরা এই কাঠামোটা আনুষ্ঠানিকভাবে সংজ্ঞায়িত করব, Moore আর Mealy — দুই ধরনের FSM-এর trade-off দেখব, আর একটা সম্পূর্ণ FSM (একটা bit-pattern detector) শুরু থেকে শেষ পর্যন্ত ডিজাইন করব — state diagram থেকে K-map হয়ে সত্যিকারের circuit পর্যন্ত।
আরও পড়ুন
- Digital Design and Computer Architecture, RISC-V Edition — Sarah L. Harris, David Money Harris · Setup/hold, metastability MTBF formula, আর clock skew-এর প্রামাণ্য গণিতসহ আলোচনা
- CMOS VLSI Design: A Circuits and Systems Perspective — Neil H. E. Weste, David Money Harris · Static timing analysis ও clock distribution network-এর industry-standard রেফারেন্স