Menu

המודול הראשון שלכם ב-Verilog: מדריך צעד אחר צעד

כתבו את מודול ה-Verilog המלא הראשון שלכם מאפס: הצהרה, פורטים, קטע של לוגיקה צירופית ו-testbench שמזין אותו. אפשר להריץ בדפדפן.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מודול הוא יחידת הבסיס של Verilog

כל דבר ב-Verilog חי בתוך module. מודול עוטף חלק ממעגל: הוא מצהיר אילו אותות נכנסים, אילו אותות יוצאים, ואילו wire-ים, רגיסטרים ולוגיקה נמצאים ביניהם. כל שבב שראיתם אי פעם הוא עץ של מודולים שיוצרים מופעים של מודולים אחרים, עד לרמת השערים הבסיסיים שהיצרן מספק.

הצורה תמיד זהה:

module name(port_list);
    // הצהרות: wire, reg, parameter
    // גוף: assigns, מופעים, בלוקי always
endmodule

נבנה מודול שלם בשלבים.

המודול השימושי הקטן ביותר: שער AND עם שתי כניסות

אנחנו צריכים משהו פשוט ועצמאי. שער AND עם שתי כניסות מושלם: שתי כניסות, יציאה אחת ושורה אחת של לוגיקה.

הריצו את הקוד. אתם אמורים לראות את ארבע השורות של טבלת האמת של AND. בואו נעבור על כל חלק.

קריאת הצהרת המודול

module and_gate(
    input  wire a,
    input  wire b,
    output wire y
);
  • module and_gate מצהיר על מודול בשם and_gate. דרך השם הזה מודולים אחרים ייצרו ממנו מופעים.
  • הרשימה בסוגריים היא רשימת הפורטים: האותות שנראים מבחוץ.
  • input wire a: a הוא פורט כניסה, והוא wire (מוזן מבחוץ).
  • output wire y: y הוא פורט יציאה שמוזן על ידי משהו בתוך המודול.

אם רוצים לקצר אפשר לכתוב input a במקום input wire a: הכיוון לבדו קובע שהטיפוס המוגדר כברירת מחדל הוא wire. אבל להיות מפורשים זה הרגל ששווה לפתח. פורטים של מודול מכסה את כל צורות הפורטים.

הגוף

assign y = a & b;

זו השמה רציפה. היא אומרת "בכל פעם ש-a או b משתנים, חשב מחדש את y כ-AND ביטי של שניהם". אין שעון ואין תזמון: הקשר תמיד מתקיים. זו לוגיקה צירופית טהורה.

endmodule סוגר את הבלוק. המודול גמור.

ה-testbench

אי אפשר להריץ את and_gate לבד. צריך מודול שני שמזין את הכניסות שלו וצופה ביציאות. זה ה-testbench, ולפי המוסכמה קוראים לו test, tb או <design>_tb.

module test;
    reg  a, b;
    wire y;

    and_gate dut(.a(a), .b(b), .y(y));
    ...
endmodule

שלושה דברים לשים לב אליהם:

  • אין רשימת פורטים. testbench הוא הרמה העליונה של הסימולציה: אין שום דבר מחוצה לו.
  • reg a, b ו-wire y. הכניסות של התכנון שנבדק (DUT) הן reg ב-testbench, כי אנחנו מזינים אותן מתוך בלוק פרוצדורלי. היציאה היא wire, כי ה-DUT מזין אותה.
  • and_gate dut(.a(a), .b(b), .y(y)). זו יצירת מופע (instantiation). אנחנו מייצרים עותק של and_gate וקוראים לו dut (שם נפוץ, קיצור של "design under test"). התחביר .a(a) אומר "חבר את הפורט בשם a במופע לאות המקומי בשם a". יצירת מופע של מודול נכנס לעומק.

הגירוי

initial begin
    a = 0; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 0; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    $finish;
end

בלוק ה-initial רץ פעם אחת בתחילת הסימולציה. בתוכו:

  • a = 0; b = 0; מזינים את הכניסות. אלה השמות blocking: הסדר חשוב, וכל אחת מתבצעת לפני הבאה.
  • #1 מקדם את זמן הסימולציה ביחידה אחת. צריך את זה כדי ש-y יספיק להתייצב אחרי שהכניסה משתנה. בלי ה-#1, $display היה מדפיס את הערך הישן של y.
  • $display(...) מדפיס לקונסולת הסימולציה. מחרוזת הפורמט עובדת כמו printf של C: %b הוא בינארי, %d הוא עשרוני, %h הוא הקסדצימלי, %t הוא זמן הסימולציה.
  • $finish מסיים את הסימולציה. בלעדיו הסימולטור היה ממשיך לקדם את הזמן לנצח ולחכות לאירוע שלא מגיע.

נסו לשבור את זה

שנו את המודול לשער OR (החליפו את & ב-|) והריצו שוב. טבלת האמת משתנה. עכשיו נסו XOR:

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

מודול עם שתי יציאות

למודולים יכולה להיות יותר מיציאה אחת. הנה חצי מחבר: שתי כניסות, סכום ונשא יוצא:

שתי פקודות ה-assign יושבות זו ליד זו אבל מתרחשות במקביל: אין כאן "קודם מחשבים את הסכום ואז את הנשא". שתיהן מתקיימות תמיד. זה המקביליות שדיברנו עליה בחומרה מול תוכנה, בצורה מוחשית.

מה אתם יודעים עכשיו

ראיתם את כל השלד של קובץ Verilog: מודול תכנון עם פורטים מוצהרים וגוף, ועוד מודול testbench שיוצר ממנו מופע, מזין את הכניסות שלו בבלוק initial ומדווח על התוצאות. כמעט כל קובץ מקור של Verilog שתקראו אי פעם מתאים לתבנית הזו. שאר השפה הוא פשוט מילוי של לוגיקה עשירה יותר, אותות מרובי ביטים, התנהגות מתוזמנת בשעון ו-testbench גדולים יותר.

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

שאלות נפוצות

מהו מודול ה-Verilog הפשוט ביותר?

מודול ה-Verilog החוקי הקטן ביותר הוא פשוט module name; endmodule: בלי פורטים ובלי גוף. הקטן ביותר ששימושי הוא מודול עם פלט אחד: module and_gate(input wire a, input wire b, output wire y); assign y = a & b; endmodule. זה קטע אמיתי של לוגיקה צירופית שאפשר לשלב בכל תכנון גדול יותר.

איך מריצים מודול Verilog?

אי אפשר להריץ מודול לבד: הוא תיאור של מעגל, לא תוכנית. כותבים מודול testbench שיוצר מופע של התכנון ומזין את הכניסות שלו, ואז מקמפלים את שניהם עם iverilog -o sim design.v test.v ומריצים vvp sim. העורך בדפדפן שבדף הזה מבצע את שני השלבים בשבילכם כשמריצים את הקוד.

מה זה testbench ב-Verilog?

testbench הוא מודול שני, בדרך כלל בלי פורטים, שתפקידו להפעיל את התכנון שלכם. הוא יוצר מופע של התכנון, משנה את הכניסות שלו דרך בלוק initial, צופה ביציאות עם $display או $monitor, וקורא ל-$finish בסיום. testbench לא ניתן לסינתזה: הוא קיים רק כדי לאמת התנהגות.

למה הקוד שלי ב-Verilog צריך $finish?

כי חומרה אף פעם לא עוצרת. סימולטור מדמה זמן שעובר, ובלי $finish מפורש הוא ימשיך להתקדם לנצח ולחכות לאירועים חדשים. $finish אומר לסימולטור 'סיימנו, צא בצורה מסודרת'. ב-testbench זו השורה האחרונה בבלוק ה-initial: מריצים את הבדיקה ואז מסיימים.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל