Menu

וקטורים ומערכים ב-Verilog: הסבר על אותות מרובי ביטים

איך מצהירים על אותות מרובי ביטים עם [7:0], חותכים אותם, משלבים ביניהם, ומה ההבדל בין וקטור packed לבין מערך זיכרון.

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

wire אחד מול הרבה

עד עכשיו כל אות שראינו היה ברוחב ביט אחד. תכנונים אמיתיים כמעט אף פעם לא נראים כך: כתובות הן של 16 או 32 ביט, bus-ים של נתונים הם של 8 או 64, ופיקסלים של RGB הם של 24. Verilog נותנת מנגנון אחד להפוך כל אות למרובה ביטים: מוסיפים טווח בסוגריים מרובעים.

wire [7:0] data;       // 8-bit wire, bit 7 is MSB, bit 0 is LSB
reg  [15:0] address;   // 16-bit reg
output reg [31:0] result; // 32-bit module output

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

מדי פעם תראו [0:7] במקום: אותו מספר ביטים, עם endianness הפוך מבחינת חיתוך. הצורה [high:low] היא המוסכמה השולטת בתעשייה. היצמדו אליה אלא אם יש לכם סיבה טובה שלא.

דוגמה מלאה: מחבר של 8 ביט

כל + מחבר את שני הווקטורים של 8 ביט ומפיק תוצאה של 9 ביט. ליטרלים מספריים כמו 8'd10 אומרים "ערך עשרוני 10 ברוחב 8 ביט". נכסה אותם בליטרלים מספריים.

חיתוך: שליפת ביטים

כשיש לכם וקטור, אפשר לשלוף ממנו ביטים בודדים או טווחים רציפים:

אל תתעכבו על שורת ה-force ב-testbench: אנחנו רק צריכים דרך להזריק ערך כדי להדגים חיתוך. החלק המעניין הוא החיתוכים עצמם.

כמה כללים:

  • כיוון החיתוך חייב להתאים להצהרה. אם הצהרתם [7:0], חותכים עם [high:low]. היפוך הכיוון הוא שגיאת תחביר.
  • חיתוך מחוץ לטווח מפיק x (לא ידוע) בסימולציה. כלי הסינתזה עשוי להזהיר או להחזיר שגיאה.
  • בחירת ביט מבוססת על האינדקס שכתבתם, החל מאפס: data[0] הוא הביט שנקרא 0, ובהצהרה של [7:0] זה ה-LSB.

חיתוכים עם בסיס משתנה: +: ו--:

צורך נפוץ: "תנו לי 8 ביטים החל מביט N". אי אפשר לכתוב ישירות data[N+7:N], כי Verilog דורשת ששני קצוות הטווח יהיו קבועים. התחביר שפותר את זה:

data[base +: width]   // width bits starting at `base`, going UP
data[base -: width]   // width bits starting at `base`, going DOWN

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

מערכים: צעד מעבר לווקטורים

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

reg [31:0] mem [0:1023];

בהצהרה הזו יש שני טווחים, ולכל אחד משמעות אחרת:

  • [31:0] הוא הממד ה-packed: הרוחב של כל מילה בודדת.
  • [0:1023] הוא הממד ה-unpacked: כמה מילים יש.

כך ש-mem הוא 1024 רגיסטרים נפרדים של 32 ביט. ניגשים לאחד מהם עם אינדקס יחיד:

mem[5] = 32'hCAFE_BABE;       // write word 5
data   = mem[address];        // read the word at `address`

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

packed מול unpacked: למה זה חשוב

ההפרדה בין ממדים packed ל-unpacked מופיעה בכל מקום. לדעת מה זה מה חוסך הרבה דיבאג:

  • וקטור packed הוא אות אחד. אפשר להתייחס אליו כולו כמספר: data + 1 עובד, data == 32'h0 עובד, data[7:0] עובד.
  • מערך unpacked הוא הרבה אותות. אי אפשר להתייחס אליו כולו כמספר: mem + 1 הוא שגיאת תחביר. צריך קודם לבחור מילה מסוימת.

גם כמה ממדים packed הם חוקיים:

reg [3:0][7:0] regs;   // 4 bytes packed together into a 32-bit signal

regs[0] הוא בית (הבית הנמוך). regs כולו הוא 32 ביט. SystemVerilog משתמשת בזה הרבה.

כמה ממדים unpacked יוצרים זיכרון דו-ממדי:

reg [31:0] frame [0:479][0:639];  // 480x640 of 32-bit pixels

ניגשים לפיקסל בודד עם frame[y][x]. כך ייראה buffer של תמונה ב-HDL.

מה הלאה

עכשיו אתם יודעים להצהיר על אות בכל רוחב שתצטרכו ולעבוד איתו. הדף הבא, פרמטרים, מראה איך להפוך את הרוחבים האלה לניתנים להגדרה, כך שאותו מודול יעבוד ב-8 ביט במופע אחד וב-32 במופע אחר. אחר כך נעבור לכללים לכתיבת מספרים ליטרליים (8'b1010_1100, 32'hDEAD_BEEF), ולערכי x/z שמופיעים בכל פעם שמשהו לא מוזן.

שאלות נפוצות

מה זה וקטור ב-Verilog?

וקטור הוא אות מרובה ביטים. מצהירים עליו על ידי הוספת טווח ל-wire או ל-reg: wire [7:0] data הוא wire של 8 ביט. המספרים בסוגריים הם מיקומי הביטים: במקרה הזה ביט 7 הוא המשמעותי ביותר וביט 0 הוא הפחות משמעותי. אפשר לחתוך ביטים בודדים (data[3]) או טווחים רציפים (data[7:4]).

מה המשמעות של [7:0] ב-Verilog?

[7:0] מצהיר על טווח מביט 7 ועד ביט 0, כולל: אות של 8 ביט שבו ביט 7 הוא הביט המשמעותי ביותר. המספר הראשון הוא האינדקס הגבוה, והשני הוא הנמוך. אפשר גם לכתוב [0:7] לאינדוקס little-endian, אבל הצורה [high:low] היא ללא ספק המוסכמה הנפוצה יותר בקוד בתעשייה.

איך חותכים ביטים ב-Verilog?

משתמשים באינדוקס בסוגריים מרובעים. data[3] בוחר ביט אחד. data[7:4] בוחר את ארבעת הביטים העליונים כווקטור של 4 ביט. החיתוך חייב להיות באותו כיוון כמו ההצהרה: אם הצהרתם [7:0], חתכו עם [high:low]. SystemVerilog מוסיפה גם data[3 +: 4] לחיתוכים ברוחב קבוע עם בסיס משתנה.

מה ההבדל בין מערך packed למערך unpacked ב-Verilog?

מערך packed הוא bus רציף אחד: reg [31:0] word הוא אות אחד של 32 ביט. מערך unpacked (או 'זיכרון') הוא אוסף של מילים עצמאיות: reg [31:0] mem [0:1023] הוא 1024 רגיסטרים נפרדים של 32 ביט. אפשר לקרוא או לכתוב מילה שלמה של מערך unpacked, אבל אי אפשר לפעול על כולו כאות אחד.

איך מצהירים על זיכרון ב-Verilog?

reg [31:0] mem [0:1023]; מצהיר על זיכרון של 1024 רשומות, כל אחת ברוחב 32 ביט. זוג הסוגריים הראשון הוא רוחב המילה (packed), והשני הוא מספר המילים (unpacked). ניגשים לרשומה עם mem[address], ואפשר לקרוא או לכתוב חלק מהרשומה עם mem[address][7:0] כשאינדוקס של SystemVerilog-2005 מופעל.

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

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

להתחיל