Menu

שרשור ושכפול ב-Verilog: האופרטורים {}

איך מדביקים אותות יחד עם {} ומעתיקים תבנית N פעמים עם {N{...}}: אופרטורים חיוניים ב-Verilog לבניית אפיקים רחבים יותר מחלקים.

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

שני אופרטורים קטנים שמצדיקים את קיומם

ל-{} יש ב-Verilog שתי צורות:

  1. שרשור, {a, b, c}: מדביק כמה אותות לאות אחד רחב יותר.
  2. שכפול, {N{pattern}}: מעתיק תבנית N פעמים.

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

שרשור: {a, b, c}

המקרה הפשוט ביותר: מערימים שני אותות לאחד:

קוראים לפי סדר הכתיבה: a הוא החצי העליון, b הוא החצי התחתון. האופרנד הראשון בתוך {} הוא המשמעותי ביותר. רוחב התוצאה הוא סכום רוחבי האופרנדים.

אפשר לשרשר כל מספר של אופרנדים:

wire [31:0] word = {byte3, byte2, byte1, byte0};

והאופרנדים יכולים להיות ברוחבים שונים:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 bit      8 bits     1 bit   2 bits  = 12

כלל נפוץ: לכל האופרנדים של שרשור חייב להיות רוחב מפורש. ליטרלים בלי גודל (1 במקום 1'b1) גורמים לשגיאות, כי המפענח לא יודע כמה ביטים להקצות. תמיד כתבו 1'b0, 1'b1, 4'd0, ולעולם לא 0 או 1 חשופים בתוך {}.

שרשור בצד שמאל

שרשור בצד שמאל של השמה מפצל אות רחב בחזרה לחלקים שלו:

זו התבנית הקנונית של מחבר עם נשא. {carry_out, sum_bits} הוא יעד יחיד של 9 ביטים בצד שמאל של ההשמה, והביטים מתחלקים: הביט העליון ל-carry_out, 8 התחתונים ל-sum_bits.

שרשור בצד שמאל עובד ב-assign ובבלוקים פרוצדורליים:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

שכפול: {N{pattern}}

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

N חייב להיות קבוע, כי הקומפיילר צריך לדעת את רוחב התוצאה בשלב ה-elaboration. התבנית יכולה להיות ליטרל, פרמטר או כל ביטוי שהרוחב שלו ידוע.

אל תשכחו את הסוגריים הפנימיים. {8 1'b1} היא שגיאת תחביר; {8{1'b1}} נכון. ה-{} החיצוניים הם האופרטור; ה-{...} הפנימיים עוטפים את התבנית שמשוכפלת.

שילוב של שניהם: הרחבת סימן והרחבה באפסים

שני האופרטורים משתלבים באופן טבעי לבניית ערכים רחבים יותר:

ה-{ {8{a_negative[7]}}, a_negative } היא תבנית הרחבת הסימן הסטנדרטית. קראו אותה כך: משכפלים את ביט הסימן שמונה פעמים, ואז מוסיפים את 8 הביטים המקוריים. התוצאה: ייצוג משלים ל-2 של אותו מספר ב-16 ביטים.

בהרחבה בלי סימן אפשר לסמוך על ההשמה: Verilog מרחיבה באפסים אוטומטית כשהיעד רחב יותר:

wire [15:0] zext_auto = a_unsigned;   // works, top 8 bits are 0

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

תבניות שימושיות

בניית מסכה של N אחדות

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// e.g. with N=5, mask = 32'h0000_001F

היפוך סדר של וקטור

אין ב-Verilog פעולת היפוך מובנית, אבל אפשר לכתוב אחת בשורה עם שרשור:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

בווקטורים רחבים יותר, לולאת generate או function נקיות יותר.

אריזת דגלים בבייט סטטוס

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] reserved
    1'b0,          // [1] reserved
    valid          // [0]
};

למשבצות השמורות של ביט אחד יש רוחב מפורש: לעולם לא 0 חשוף בתוך {}.

טעויות נפוצות

ליטרלים בלי גודל בתוך {}. {a, 0} הוא שגיאת תחביר או אפס ברוחב 32 ביטים. תמיד ציינו גודל: {a, 1'b0}.

שכחת הסוגריים הפנימיים בשכפול. {8 1'b1} לא עובר פענוח; {8{1'b1}} כן.

בלבול בסדר. {a, b} שם את a בצד העליון ואת b בתחתון. הפכו את ההנחה ותקבלו סדר בייטים הפוך איפשהו.

שכפול אפס פעמים. {0{...}} אסור ב-Verilog סטנדרטי. SystemVerilog מתירה אותו (ומפיקה תוצאה ברוחב אפס). Verilog רגיל ידחה אותו.

מה הלאה

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

שאלות נפוצות

איך משרשרים אותות ב-Verilog?

משתמשים בסוגריים מסולסלים: {a, b, c} מפיק וקטור רחב יחיד על ידי הדבקת האופרנדים לפי הסדר שבו הם כתובים. ה-MSB של a הופך ל-MSB של התוצאה; ה-LSB של c הופך ל-LSB. רוחב התוצאה הוא סכום רוחבי האופרנדים. שרשור עובד גם בצד ימין של השמה (כדי לבנות ערך) וגם בצד שמאל (כדי לפצל ערך).

מה המשמעות של {N{pattern}} ב-Verilog?

זה אופרטור השכפול: הוא מפיק N עותקים של pattern משורשרים יחד. {8{1'b1}} הוא ערך של 8 ביטים שכולם אחדות (8'hFF). {4{2'b10}} הוא הערך בן 8 הביטים 8'b10101010. בעזרת שכפול מרחיבים באפסים, מרחיבים סימן, או בונים כל תבנית ביטים חוזרת בלי לכתוב אותה ידנית.

איך מרחיבים סימן של אות ב-Verilog?

משתמשים בשכפול כדי לחזור על ביט הסימן: { {24{a[7]}}, a } מרחיב a של 8 ביטים ל-32 ביטים על ידי שכפול ביט 7 (ביט הסימן) 24 פעמים והוספת הערך המקורי אחריו. להרחבה באפסים בלי סימן, משכפלים 1'b0, או פשוט נותנים להשמה לעשות זאת באופן מובלע כשהיעד רחב יותר.

אפשר להשתמש בשרשור בצד שמאל של השמה?

כן, כך מבצעים השמה לכמה יעדים ממקור רחב אחד. {carry, sum} = a + b; שם את תוצאת החיבור ב-carry (הביט העליון) וב-sum (השאר) בהוראה אחת. כל יעד שומר על הרוחב שלו; המפענח מחלק את הביטים של צד ימין בהתאם.

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

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

להתחיל