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

להתחיל