הממשק של מודול
רשימת הפורטים של מודול היא הממשק שלו: כל מה שהעולם החיצון יכול לראות ולגעת בו. בתוך המודול, הגוף מחשב פלטים מכניסות. הפורטים הם ה-wires שחוצים את הגבול.
ההצהרה המודרנית בסגנון ANSI שמה כיוון, טיפוס ורוחב באותה שורה:
module my_module(
input wire clk,
input wire reset,
input wire [7:0] data_in,
output reg [7:0] data_out,
output wire valid
);
// body
endmodule
זו כל הצורה. לכל פורט יש:
- כיוון:
input,outputאוinout. - טיפוס:
wireאוreg(אוlogicב-SystemVerilog). - רוחב: טווח כמו
[7:0], או ביט יחיד אם משמיטים אותו. - שם: המזהה שתשתמשו בו בתוך הגוף.
שלושת הכיוונים
input: מוזן מבחוץ
כניסות הן תמיד wire. מקור ההזנה נמצא מחוץ למודול: אות של מודול ברמה גבוהה יותר, או reg של ה-testbench. בתוך המודול הזה אפשר לקרוא כניסות בביטויים, אבל אף פעם לא לבצע אליהן השמה:
module reader(input wire [7:0] data);
initial $display("data = %h", data);
endmodule
כתיבת data = ... בתוך המודול הייתה שגיאה.
output: מוזן מבפנים
יציאות מוזנות על ידי משהו בתוך המודול הזה. הן יכולות להיות wire (מוזנות על ידי assign או פלט של תת מודול) או reg (מוזנות מ-always/initial).
module driver(
input wire a,
input wire b,
input wire clk,
output wire y, // wire: מוזן על ידי assign
output reg q // reg: מוזן על ידי always
);
assign y = a & b; // תקין כי y הוא wire
always @(posedge clk)
q <= a; // תקין כי q הוא reg
endmodule
ניסיון לבצע השמה ל-y בתוך בלוק ה-always, או להזין את q עם assign, היה שגיאת קומפילציה. בחירת מילת המפתח חייבת להתאים למקור ההזנה.
inout: דו כיווני
פורטים מסוג inout הם wires שיכולים להיות מוזנים לסירוגין על ידי המודול או על ידי מה שמחובר מבחוץ. הם מופיעים בגבולות השבב: קווי SDA של I²C, פיני GPIO דו כיווניים, אפיקי נתונים משותפים. בתוך המודול שולטים בכיוון עם תבנית tri-state:
module bidir_pin(
input wire data_out,
input wire output_enable,
output wire data_in,
inout wire pin
);
assign pin = output_enable ? data_out : 1'bz;
assign data_in = pin;
endmodule
pin הוא ה-wire המשותף. כש-output_enable גבוה, המודול מזין את pin בערך של data_out. כשהוא נמוך, המודול משחרר את הפין לעכבה גבוהה (z), ומאפשר למקור הזנה חיצוני להשתמש בו. data_in תמיד רואה את מה שנמצא כרגע על הפין.
inout נדיר בלוגיקה פנימית טהורה. אם אתם בונים בקר זיכרון, ליבת מעבד או pipeline לעיבוד תמונה, ייתכן שלעולם לא תצטרכו אותו. זו יכולת בשביל טבעת ה-I/O בגבולות השבב.
פורטים של ביט אחד מול כמה ביטים
טווח הרוחב אופציונלי: משמיטים אותו בפורט של ביט אחד:
input wire valid, // 1 bit
input wire [7:0] data, // 8 bits
input wire [31:0] addr, // 32 bits
אפשר להשתמש בפרמטרים לרוחבים, וכך עובדים מודולים עם פרמטרים:
module bus #(
parameter WIDTH = 32
)(
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);
assign out = in;
endmodule
סגנון ANSI מול Verilog-1995
מדי פעם תראו קוד ישן שמפצל את רשימת הפורטים ואת ההצהרות:
// סגנון Verilog-1995 ישן: אל תעשו את זה בקוד חדש
module foo(clk, data_in, data_out);
input clk;
input [7:0] data_in;
output reg [7:0] data_out;
// ...
endmodule
רשימת הפורטים מכילה רק שמות; כל פורט מוצהר אחר כך בנפרד בתוך הגוף. זה ארוך, מועד לשגיאות מסוג "מופיע ברשימת הפורטים אבל לא הוצהר אף פעם", ודורש פי שניים הקלדה. סגנון ANSI-2001 (זה שמוצג לאורך כל המסמכים האלה) מחליף את שניהם:
module foo(
input wire clk,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// ...
endmodule
השתמשו בסגנון ANSI. הכלים תומכים בו מאז 2001.
דוגמה מלאה
במודול היחיד הזה יש:
- פרמטר (
WIDTH). - כניסות לשעון, ל-reset, ל-load enable, לנתונים ול-shift enable.
- יציאת
reg(data_out) שמוזנת בתוך בלוקalways. - יציאת
wire(msb_out) שמוזנת על ידי השמה רציפה. - הצהרה מלאה בסגנון ANSI שמציינת לכל פורט כיוון, טיפוס ורוחב באותה שורה.
כך בנוי כמעט כל מודול בר סינתזה שתכתבו.
מה הלאה
המסמך הבא, יצירת מופעים של מודולים, מראה איך לקחת את המודול הזה ולהשתמש בו בתוך תכנון גדול יותר. ה-shifter שכתבתם זה עתה לא שימושי לבדו; הוא מצדיק את קיומו כשמשהו יוצר ממנו מופע ומחבר אותו למערכת.
שאלות נפוצות
מה שלושת כיווני הפורטים ב-Verilog?
input, output ו-inout. input מוזן מחוץ למודול. output מוזן מתוך המודול. inout הוא דו כיווני: שימושי לאפיקי tri-state שבהם המודול גם מזין וגם קורא את אותו פין. הרוב המוחלט של הפורטים הפנימיים הם input או output; inout מופיע רק בפינים של השבב.
מה ההבדל בין output wire ל-output reg?
output wire אומר שהפלט מוזן על ידי השמה רציפה או על ידי פלט של תת מודול, כלומר כל מה שמחוץ לבלוק always. output reg אומר שהוא מוזן מבלוק פרוצדורלי (initial או always). מילת המפתח קובעת אם אפשר לכתוב לאות מתוך always, ולא אם החומרה אחרי הסינתזה מכילה flip-flop.
מה זה סגנון ANSI לפורטים ב-Verilog?
סגנון ANSI מצהיר על הכיוון והטיפוס של כל פורט ישירות ברשימת הפורטים: module foo(input wire [7:0] data, output reg [7:0] result);. סגנון Verilog-1995 הישן רק מנה שמות ברשימת הפורטים והצהיר עליהם שוב בתוך המודול. תמיד השתמשו בסגנון ANSI בקוד חדש: הוא קצר יותר, פחות מועד לטעויות, והסטנדרט בכל מקום מודרני.
אפשר כמה כניסות ויציאות במודול Verilog?
כן, למודול יכולים להיות כמה פורטים שצריך, בכל צירוף של כיוונים. מפרידים ביניהם בפסיקים ברשימת הפורטים. הסדר ברשימה קובע את סדר החיבור לפי מיקום ביצירת מופע, אבל כמעט תמיד משתמשים בחיבורים לפי שם (.port(signal)) כדי לא להסתמך על הסדר.