سلام رفقا! تا حالا فکر کردین چطور یه CPU با فرکانس مثلاً ۳ گیگاهرتز میتونه همزمان چندین کار رو انجام بده و اصلاً تنگنا (Bottleneck) ایجاد نشه؟ رازش تو یه کلمه خلاصه میشه: Pipeline. اگه این مفهوم رو درک نکنیم، فهمیدن معماریهای مدرن عملاً غیرممکنه. بریم ببینیم چطور کار میکنه.
تصور کن تو یه کارخونه ماشینسازی هستی. اگه یه نفر تمام مراحل ساخت ماشین رو از صفر تا صد خودش انجام بده، روزی شاید یه ماشین بسازه. ولی اگه خط تولید (Assembly Line) راه بندازیم، یه نفر لاستیک میبنده، یه نفر موتور میذاره و یه نفر رنگ میکنه. سرعت خروجی ماشینها به شدت بالا میره! پردازنده هم دقیقاً همینطور کار میکنه.
مراحل Pipeline پردازنده
توی یه پردازنده استاندارد (مثل MIPS)، اجرای یه Instruction به ۵ مرحله (Stage) تقسیم میشه:
- IF (Instruction Fetch): خوندن دستور از حافظه.
- ID (Instruction Decode): دیکد کردن دستور و خوندن رجیسترها.
- EX (Execute): انجام عملیات تو ALU.
- MEM (Memory Access): خوندن یا نوشتن تو دیتا مموری (اگه نیاز باشه).
- WB (Write Back): نوشتن نتیجه نهایی تو رجیستر.
بدون Pipeline، پردازنده باید صبر کنه تا هر Instruction این ۵ مرحله رو طی کنه، بعد بره سراغ بعدی. ولی با Pipeline، وقتی Instruction اول از IF میره تو ID، Instruction دوم وارد IF میشه. این یعنی تو هر کلاک، یه Instruction جدید وارد پردازنده میشه و یه دونه هم تمام میشه! IF && ID && EX > parallel_magic
پس در واقع Pipeline سرعت اجرای هر دستور رو کم نمیکنه (Latency ثابت میمونه)، بلکه تعداد دستوراتی که تو واحد زمان اجرا میشن (Throughput) رو به شدت بالا میبره.
یه نگاه به پیادهسازی ساده
توی Verilog، Pipeline با استفاده از فلیپفلاپها (رجیسترهای واسط) بین هر مرحله پیاده میشه تا دیتای هر Stage تو یک کلاک نگه داشته بشه:
always @(posedge clk) begin
// Stage 1: IF
IF_ID_IR <= Memory[PC];
PC <= PC + 4;
// Stage 2: ID
ID_EX_A <= RegFile[IF_ID_IR[25:21]];
ID_EX_B <= RegFile[IF_ID_IR[20:16]];
// ... and so on for EX, MEM, WB
end
نکات کلیدی
- Latency vs Throughput: Pipeline زمان اجرای یه دستور (Latency) رو کم نمیکنه، بلکه نرخ تمام شدن دستورات (Throughput) رو بالا میبره.
- تعداد مراحل: پنج مرحله یه مدل کلاسیکه. پردازندههای مدرن (مثل Intel/AMD) گاهی ۱۵ تا ۲۰ مرحله Pipeline دارن (بهش میگن Super-pipelined).
- مشکل بزرگ: وقتی دستورات به هم وابسته میشن، Pipeline نمیتونه پر بشه و اینجا پای
Hazardها به میون میآد!
پایپلاین کردن شیرینه، ولی همونطور که حدس زدین، وابستگی بین دستورات کلکسیون رو به هم میریزه. فکر میکنید اگه دستور دوم به نتیجه دستور اول نیاز داشته باشه چی میشه؟ تو پست بعدی دقیقاً درباره Hazardها حرف میزنیم. 💡
$ echo "happy hacking" | jtag --program