זמן בתוכנה מול זמן בחומרה
בתוכנית, הזמן מתקדם הוראה אחת בכל פעם. המעבד מסיים את שורה 1 ואז מריץ את שורה 2. אם רוצים ששני דברים יקרו בבת אחת, פונים ל-threads, ל-async או למכונה שנייה.
בחומרה, הכול קורה בבת אחת. מעגל לא עובד בתורות. המחבר תמיד מחבר, ה-multiplexer תמיד בוחר, ה-flip-flop תמיד מסתכל על השעון. אין program counter. אין "שורה נוכחית".
Verilog צריכה לתאר את העולם הזה בטקסט. הדרך שבה היא עושה את זה היא המקור לכל בלבול בפרק הזה.
שתי שכבות של מקביליות
כשקוראים מודול Verilog, מסתכלים על שני דברים שונים בו זמנית:
- התיאור הסטטי של המעגל. wires, אוגרים, מופעים של שערים, מופעים של תת מודולים, השמות רציפות. כל אלה קיימים בו זמנית. הסדר שלהם בקובץ לא משנה.
- בלוקים פרוצדורליים,
initialו-always, שנראים כמו תוכניות קטנות שהסימולטור עובר עליהן. בתוך בלוק כזה, ההוראות כן רצות בסדר כלשהו. אבל כמה בלוקים שלalwaysיכולים להיות פעילים בבת אחת, כל אחד ב-thread זעיר משלו של זמן מדומה.
module example(input wire a, input wire b, output wire y, output wire z);
assign y = a & b; // קיים בכל רגע
assign z = a | b; // גם קיים בכל רגע, במקביל
always @(posedge a) begin
// מגיב נפרד "שתמיד פועל" שמתעורר בכל
// קצה עולה של `a`
end
endmodule
שתי שורות ה-assign אינן רצף. הן מתארות שני רכיבים של לוגיקה צירופית שכלי הסינתזה יכול למקם זה לצד זה. בלוק ה-always הוא דבר שלישי שקורה במקביל.
מה המשמעות של "אות"
בתוכנה, משתנה מחזיק ערך עד שמשנים אותו. ב-Verilog, אות מחזיק ערך ברציפות, ובכל רגע הוא תלוי במה שמזין אותו.
wire מוזן מבחוץ (assign, פורט פלט של תת מודול, חיבור inout). reg מוזן מתוך בלוק פרוצדורלי. לשניהם יש ערך בכל רגע. אין דבר כזה "לא מאותחל" במובן של C: אותות מוזנים לערך מוגדר, לערך הלא ידוע המיוחד x, או לעכבה גבוהה z. נעסוק בשני האחרונים ב-ערכי X ו-Z.
שעונים משנים הכול
ברגע שמכניסים שעון, הזמן מתחיל להיות חשוב. Flip-flop הוא רכיב חומרה זעיר שלוכד את ערך הכניסה שלו בקצה העולה (או היורד) של שעון ומחזיק אותו עד הקצה הבא. מה שמאפשר לבנות מונים, מכונות מצבים, pipelines, כל דבר שיש לו זיכרון, הוא השעון.
שימו לב ל-q <= d במקום q = d. זו השמה לא חוסמת, סוס העבודה של לוגיקה מסונכרנת לשעון. היא אומרת "בקצה השעון הבא, תזמן ל-q לקבל את הערך ש-d מחזיק עכשיו". נצלול לכללים ב-השמה חוסמת מול לא חוסמת; בינתיים, חשוב לזהות שההשמה הזו לא מתיימרת להיות הוראת תוכנה.
RTL: המודל המנטלי של העברת אוגרים
רוב קוד ה-Verilog בר הסינתזה נכתב בסגנון שנקרא Register Transfer Level, או RTL. הרעיון פשוט:
- מחליטים איזה מצב המעגל צריך (האוגרים).
- לכל אוגר מתארים שני דברים: מה מאפס אותו, ואיזו לוגיקה צירופית מחשבת את הערך הבא שלו.
- מחברים את יציאות הלוגיקה הצירופית לכניסות האוגרים, ויש לכם מעגל עובד.
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = done ? IDLE : RUNNING;
default: next_state = IDLE;
endcase
end
זו מכונת שני מצבים. בלוק ה-always הראשון מסונכרן לשעון: הוא flip-flop. השני צירופי לחלוטין: הוא רק משוואה. כמעט כל מכונת מצבים, מונה ו-pipeline שתכתבו בנויים בצורה הזו.
ההרגלים שתוקעים אתכם
אם אתם מגיעים מתוכנה, הנה רשימה קצרה של הרגלים שכדאי להניח בצד:
- "משתנים מתעדכנים כשאני מבצע אליהם השמה." לא בקצה שעון: השמה לא חוסמת מתזמנת את העדכון לסוף צעד הזמן.
- "הוראות רצות מלמעלה למטה." מחוץ לבלוקים פרוצדורליים, לא. בתוך בלוק מסונכרן לשעון, בערך, אבל השמה חוסמת מול לא חוסמת משנה את המשמעות של "לפי הסדר".
- "אקצה את זה כשאצטרך." חומרה לא מקצה. כל אוגר וכל שער חייבים להתקיים בזמן הסינתזה. הגודל של כל וקטור קבוע.
- "הלולאה הזו מהירה, זו רק פעולה אחת." לולאת
forב-Verilog בר סינתזה נפרשת לחומרה מקבילית. לולאה של 64 איטרציות הופכת ל-64 עותקים של הגוף, לא להוראת מעבד שרצה 64 פעמים.
אתם לא לומדים לכתוב תוכנית. אתם לומדים לתאר מעגל. האינסטינקט לקרוא מלמעלה למטה הוא בדיוק האינסטינקט הלא נכון, ולוקח זמן להרגיל את עצמכם אחרת.
מה הלאה
המסמכים הבאים מסבירים איך מתקינים סביבת כלים מקומית (לא חובה, העורך בדפדפן מספיק), ואחר כך איך כותבים את המודול הראשון שלכם מאפס. נחזור להשוואה בין חומרה לתוכנה בכל פעם שמשהו ייראה מוזר, כי לרוב החלקים ה"מוזרים" יש אותו שורש: זו לא תוכנה.
שאלות נפוצות
מה ההבדל בין חומרה לתוכנה במונחים של Verilog?
תוכנה היא רצף של הוראות שמעבד מבצע, אחת אחרי השנייה. חומרה, מה ש-Verilog מתארת, היא רשת של שערים ו-wires שכולם נושאים אותות באותו זמן. קובץ Verilog מתאר את הרשת הזו. הסימולטור מחקה את ההתנהגות המקבילית; כלי סינתזה הופך אותה לסיליקון אמיתי.
האם קוד Verilog רץ מלמעלה למטה כמו C?
לא, ולהתייחס אליו כך היא טעות המתחילים הנפוצה ביותר. הוראות ברמה העליונה (assigns, מופעים של מודולים, בלוקי always) 'קיימות' כולן בבת אחת. רק בתוך בלוקים פרוצדורליים, initial ו-always, קורה משהו שדומה לביצוע סדרתי, וגם שם השמות לא חוסמות שוברות את האשליה.
מה פירוש 'מקבילי' ב-Verilog?
הכוונה שכמה הוראות מתארות חלקים של מעגל שפועלים כולם באותו זמן. שתי הוראות assign באותו מודול הן לא 'שורה 1 ואז שורה 2', אלא שני רכיבי חומרה שרצים במקביל, ושניהם מגיבים לכניסות שלהם ברציפות.
מה זה תכנון RTL?
RTL הוא ראשי תיבות של Register Transfer Level. זה סגנון כתיבה של Verilog שבו מתארים את המעגל כאוגרים (flip-flops) ואת הלוגיקה הצירופית שמחשבת את הערכים הבאים שלהם. רוב קוד ה-Verilog בר הסינתזה הוא RTL. הרמה שמעליו היא התנהגותית; הרמה שמתחתיו היא רמת השערים.