חזור לתיקייה: תרגול

12 - צנרור מעבד

חיפוש בתיקיה
Ctrl K
לחץ כאן או הקש Ctrl K כדי לחפש בכל התיקיה
  • Hazard
  • מבני
  • נתונים
  • DataHazard כפול
  • בקרה
  • תרגול

12 - צנרור מעבד

00440252

מערכות ספרתיות ומבנה המחשב

רשימות תרגול לקריאה בשבת

צנרור מעבד

הופק מתוך תקיית התואר של שקד

uni-folder.vercel.app

עד כה יכולנו להריץ פקודה אחת בכל פעם במעבד, אך גם ב-MultiCycleMultiCycleMultiCycle יש מקום לשיפור שכן עדיין יש רכיבים שעומדים ולא עושים כלום למשך זמן מה.
כעת אנו עומדים לצנרר את המעבד, זה ייתן לנו שיפור ב-throuputthrouputthrouput אך לאור העובדה שכל פקודה כעת צריכה לעבור את כל השלבים, זאת בניגוד ל-MultiCycleMultiCycleMultiCycle, וגם לאור העובדה שאין פלופים אידיאליים - ה-latencylatencylatency יהיה גבוהה יותר - שזה רע.

כך נראה מעבד מצונרר:
​ואווווווווווווווווווווווו.png
נוכל לבצע אבסטרקציה ולקבל:
​ואוווווווווווווווווווו 2.png
לכאורה נראה שרימינו פה - אנו משתמשים ב-regfileregfileregfile פעמיים - איך זה יכול להיות?

  • קריאה מהרגיסטר, שזה הדבר היחיד שמתבצע בשלב ה-decodedecodedecode, היא אסינכרונית ולכן יכולה להתבצע בכל זמן.
  • כתיבה לרגיסטר, שזה הדבר היחיד שמתבצע בשלב ה-writeBackwriteBackwriteBack, היא סינכרונית ולכן נוכל לבצע אותה שכן אנו לא מבצעים עוד פניות סינכרוניות לרגיסטר.

בשונה מ-MultiCycleMultiCycleMultiCycle אנו לא צריכים את ה-MicroCodeControllerMicroCodeControllerMicroCodeController ואנו יכולים להשתמש ב-ROMROMROM רגיל.
אך יש לנו בעיה - אנו צריכים סיגנלי בקרה מסויימים עבור כל שלב בצינור - איך נפתור זאת? נשרשר את סיגנלי הבקרה באמצעות רגיסטרים גם כן.
כדי שזה יהיה יעיל, אנו נעביר רק את הסיגנלים שאנחנו צריכים:
​עוד תמונה שנראית כמו תמונה אחרת באתר.png

HazardHazardHazard

יש לנו שלושה סוגי האזארדים:

  1. מבני -StructuralStructuralStructural
  2. נתונים - DataDataData
  3. בקרה - ControlControlControl

מבני

הבעיה: שתי פקודות או יותר מתחרות על גישה לאותו משאב פיזי.
פתרון 1: גישה סדרתית, לפי תור, למשאב. חלק מהפקודות נאלצות להמתין.
פתרון 2: הוספת חומרה למכונה.

הערה

אם בניתם את המעבד נכון כמו שראינו בקורס, אין סכנה ל-HazardHazardHazard מבני.

נתונים

אם נניח יש לנו שתי פקודות שמשתמשות ברגיסטר כאשר הראשונה משנה אותו והשניה משתמשת בו והן בהפרש הקטן מ-444 פקודות, הערך החדש של הרגיסטר לא ישמר.
​נמאס לי כבר לחשוב על שמות לקבצים.png
איך נוכל לדאוג שזה לא יקרה?

  1. נוכל לפתור זאת באמצעות השהייה אבל, אנו נקבל בועה. כלומר, לא תתבצע שום פקודה עד שהפקודה תשמר.
  2. נוכל לפתור זאת בדרך טובה יותר באמצעות קידום נתונים. פה אנו שולפים את האופרנד ישירות משלב בצינור במקום להמתין לכתיבה שלו בחזרה ב-RegFileRegFileRegFile.
    ​ספרתיותתתתתתתתתתתתתתתתתת.png
    וכך זה נראה ב-datapathdatapathdatapath:
    ​אני לא אוהב את ספרתיות.png
הערה

המימוש המוצג פה הוא לא מלא והוא נותן לנו רק:
א. WB→IDWB\to IDWB→ID
ב. MEM→EXMEM\to EXMEM→EX
ג. WB→EXWB\to EXWB→EX
אך אם נרצה לעשות דברים אחרים, כמו אם יש לנו Data  HazardData\;HazardDataHazard כפול, לא נוכל לעשות זאת במימוש הזה.

add x1,x1,x2
add x1,x1,x3
add x1,x1,x4

נשים לב כי פה לא נוכל לבצע ForwardingForwardingForwarding ולכן נדרש לעשות BubblingBubblingBubbling.

DataHazardDataHazardDataHazard כפול

נממש כעת את הביעבוע הזה שהוא האפשרות היחידה שלנו לפתור את הבעיה שראינו.
​קורס מזוויע זה ספרתיות - מה אני אפילו רואה פה.png
במבחן הם ידרשו להגיד לנו האם למעבד יש Hazard  Detection  UnitHazard\;Detection\;UnitHazardDetectionUnit או לא - אנחנו לא אמורים לדעת לבד.

בקרה

נניח ואנו מבצעים פקודת branchbranchbranch, הקפיצה תבוצע לכל הפחות בשלב ה-executeexecuteexecute אך עד שהיא תקרה, שתי הפקודות הבאות יכנסו לצינור.
אם אנו קפצנו, שתי הפקודות הללו לא היו אמורות להתבצע.
⟸ על מנת לפתור זאת, אם הקפיצה כן נלקחת, נבצע flashflashflash ע"י המרת הפקודות הללו ל-NOPNOPNOP.
⟸ כל branchbranchbranch שנלקח ב-pipelinepipelinepipeline פשוט עולה לנו ב-222 מחזורים 'ריקים'.

על מנת לשפר ביצועים, ניתן להשתמש ב"חיזוי קפיצות" כדי לנחש את כיוון ה-branchbranchbranch מוקדם יותר ב-pipelinepipelinepipeline.
⟸ רק במקרה שהחיזוי התגלה כשגוי נבצע flashflashflash.

תרגול

תרגיל

נתון מעבד V-RISC pipelined כאשר ה-branch מתבצע בשלב execute. המעבד מניח כי ה- branch אינו מתרחש ועושה flush במקרה שכן. המעבד בעל unit forwarding ,hazard load אשר מכניס bubble כפי שנלמד בכיתה.

main: addi t1,x0,1
      lw t0, 0(x0)
loop: sub t0,t0,t1
	  bne t0,x0,loop
	  addi t2,x0.0xf
	  nop
	  nop
	  nop
	  nop
	  nop

א. ציינו בטבלה איזו פקודה, עד לשלב ה-fetchfetchfetch מתבצע בכל שלב ב-pipelinepipelinepipeline (חלק מהשדות מולאו עבורכם).
ב. כמה מחזורי שעון תקח הרצת התוכנית הנ"ל?

פתרון א':
​כיף כיף כיף כיף - לא.png

פתרון ב':
התוכנית תקח עד אשר כל הפקודות יתבצעו לא כולל ה-nopnopnop-ים ולכן, 141414 מחזורי שעון!

תרגיל

נתון קטע הקוד הבא:

lw t2, 1000(t5)
add t3,t1,t2
sub t2,t1,t2
addi t0,t2,10

כדי להפעיל את הקוד הנ"ל במחשב ה-Pipelined  RISC−VPipelined\;RISC-VPipelinedRISC−V יש צורך אולי במעקפים הוספת שורה/שורות nopnopnop.
הסבירו היכן (אם בכלל) יש להוסיף nops ואלו מעקפים דרושים ב-pipeline (מאיזה חלק לאיזה חלק במעבד ה Pipeline)

תחילה אנו רואים שאנו טוענים את t2t_{2}t2​ ואז ישר משתמשים בו בפקודה הבאה ולכן, נדרש פה לבצע הכנסה של nopnopnop כדי להשהות את ביצוע התוכנית למשך מחזור שעון אחד.
כעת, בזכות הכנסת ה-nopnopnop, נוכל לבצע ForwardingForwardingForwarding מ-WBWBWB ל-EXEEXEEXE
לאחר מכן אנו שוב משתמשים ב-t2t_{2}t2​ בפקודה הבאה, אך הפעם זה לאחר שעשינו nopnopnop אחד ולכן, נוכל לבצע ForwardingForwardingForwarding מ-WBWBWB ל-IDIDID.
לאחר מכן אנו שוב משתמשים ב-t2t_{2}t2​ ולכן, מכיוון שזה ערך חדש וזו פקודה שמגיעה ישר אחרי, נוכל לבצע ForwardingForwardingForwarding מ-MEMMEMMEM ל-EXEXEX.

הערה

מומלץ לשרטט טבלה כדי שהכל יהיה ברור.

תרגיל (3)

נתון מעבד RISC−VRISC-VRISC−V מצונרר אשר בו ה-branchbranchbranch מתבצע בשלב ה-WBWBWB.
מריצים על מעבד זה תוכנית אשר מכילה את הפקודות: add,addi,lw,sw,beq,bneadd,addi,lw,sw,beq,bneadd,addi,lw,sw,beq,bne אך ללא data  hazardsdata\; hazardsdatahazards וללא load  hazardsload\;hazardsloadhazards.
המעבד מניח כי ה-branchbranchbranch אינו מתרחש ועושה flushflushflush במקרה שכן.

בחרו בטענה הנכונה:

  1. הרצת כל התוכנית על המעבד המתואר בשאלה תקח בהכרח זמן רב יותר מאשר הרצה על מעבד אשר בו הקפיצה תתרחש בשלב ה-DecodeDecodeDecode.
  2. הרצת כל תוכנית על המעבד המתואר בשאלה תקח בהכרח אותו הזמן כמו הרצה על מעבד אשר בו הקפיצה תתרחש בשלב ה-DecodeDecodeDecode.
  3. עבור תוכניות מסויימות התוכנית לא תרוץ כהלכה.
  4. ישנן תוכניות אשר מכילות פקודות R  typeR\;typeRtype בלבד אשר לא ירוצו כהלכה.

אנו יודעים כי לא יכולים להיות לנו בעיות datadatadata או loadloadload ולכן כל תוכנית אשר מכילה פקודות R  typeR\;typeRtype בלבד תרוץ כהלכה.
אם הקפיצה תתרחש בשלב ה-decodedecodedecode אז נגלה יותר מוקדם מה אנו נדרשים לבצע ואז לקפוץ מוקדם יותר ולבצע flashflashflash מוקדם יותר, אך נשים לב כי זה לא מתרחש עבור כל תוכנית, אלא רק בתוכנית עם branchbranchbranch שקופץ.
בהתאם להנ"ל - 2 לא נכון גם כן.
אם אנו מקבלים החלטה על branchbranchbranch בשלב ה-WBWBWB, כפי שנתון בשאלה, והפקודה הבאה היא swswsw ששומרת בזיכרון, היא כבר הגיעה לשלב ה-MEMMEMMEM והיא שומרת שם ערך כשאנחנו לא אמורים ולכן תוכנית זו לא תרוץ כהלכה. (טענה 3 נכונה)

הערה

במעבד אמיתי, אי אפשר לבצע קפיצה ב-decodedecodedecode.

בהמשך לשאלה, אם נוסיף למעבד forwarding,  hazard  detection  unitforwarding,\;hazard\;det ection\;unitforwarding,hazarddetectionunit, זה לא ישנה כלום!!