מה שצריך כדי להתחיל לתכנת באסמבלר RISC-V

העדכון אחרון: 08/10/2025
מחבר: יצחק
  • כולל RV32I: אוגרים, ABI ובקרת זרימה עם ecall.
  • תרגול עם צדק ותרגילים: מינוס, גורמים, שרשראות, רקורסיה.
  • שרשרת כלים צולבת ראשית, תסריט קישור וניפוי שגיאות באמצעות objdump.

אסמבלר RISC-V: דרישות ותחילת העבודה

אם אתם סקרנים לגבי אסמבלר ומרגישים ש-RISC-V היא הדרך הנכונה, הגעתם למקום הנכון. התחלה עם ASM על RISC-V היא משתלמת יותר ממה שנראה אם אתה מבין את הכלים, את המודל של תכנות וכמה כללים מרכזיים של אדריכלות.

בשורות הבאות שילבתי את הטוב ביותר מכמה מקורות: תרגולים עם סימולטורים מסוג יופיטר, מוסכמות של רפרטואר הבסיס של RV32I, דוגמאות ללולאות ורקורסיות, קריאות מערכת, ואפילו מבט על תכנון מעבד RISC-V ב-VHDL (עם ALU, בקרת זיכרון ומכונת מצבים), בנוסף לסקירה של סקריפטים של שרשרת כלים צולבת וקישור.

מהו אסמבלר RISC-V וכיצד הוא שונה משפת מכונה?

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

RISC-V מגדיר ISA פתוח עם רפרטואר בסיס נקי מאוד. פרופיל RV32I (32 סיביות) כולל 39 הוראות משתמש עם אורתוגונליות יוצאת דופן, המפרידה בין גישה לזיכרון לחישוב טהור, ועם תמיכה מצוינת ב-GCC/LLVM.

רשומות, הסכמים ונקודת כניסה

ב-RV32I יש לך 32 אוגרים לשימוש כללי (x0–x31) 32 סיביות; x0 תמיד נקרא כ-0 ולא ניתן לכתוב עליו. כינויים כגון a0–a7 (ארגומנטים), t0–t6 (קבצים זמניים) או s0–s11 ​​​​(שמורים) שימושיים גם הם למעקב אחר ה-ABI.

בהתאם לסביבה או לסימולטור, התוכנית עשויה להתחיל בתווית ספציפית. ב-Jupiter, תוכניות מתחילות בתג __start הגלובלי., אשר עליך להכריז כגלוי (לדוגמה, באמצעות .globl) כדי לסמן את נקודת הכניסה.

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

כלים וסימולטורים: יופיטר וזרימת עבודה בסיסית

כדי להתאמן ללא סיבוכים, יש לך את סימולטור/אסמבלר של יופיטר, כלי גרפי בהשראת SPIM/MARS/VENUS המאפשר עריכה, הרכבה וביצוע בסביבה אחת.

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

התוכניות חייבות להסתיים בקריאה לסביבה: הגדרת שיחת יציאה a0 עם קוד 10 (יציאה). ב-RISC-V, קריאות חוזרות (ecalls) שוות ערך לקריאות מערכת או מלכודות לסביבה/מערכת.

מבנה מינימלי של תוכנית וקריאות מערכת

המבנה הטיפוסי בדוגמאות אקדמיות מגדיר נקודת התחלה, עושה את העבודה ומסתיים ב-ecall. ארגומנטים של recall נעים בדרך כלל ב-a0–a2 ובורר השירות ב-a7, בהתאם לסביבה.

באחד לינוקס ב-RISC-V, לדוגמה, ניתן להדפיס באמצעות syscall write ולצאת עם הקוד המתאים. לכתיבה משתמשים ב-a0 (fd), a1 (buffer), a2 (length) ו-a7 עם מספר השירותלבסוף, a0 מוגדר כקוד החזרה ו-a7 כמספר היציאה.

# Ejemplo mínimo (Linux RISC-V) para escribir y salir
.global _start
_start:
  addi a0, x0, 1        # fd = 1 (stdout)
  la   a1, msg          # a1 = &msg
  addi a2, x0, 12       # a2 = longitud
  addi a7, x0, 64       # write
  ecall

  addi a0, x0, 0        # return code
  addi a7, x0, 93       # exit
  ecall

.data
msg: .ascii "Hola mundo\n"

אם אתה עובד מחוץ ללינוקס, כמו למשל ב סימולטורים חינוכיים עם שירות IoT משלהם, שנה את מספר הקריאה לאחור והרישומים בהתאם לתיעוד הסביבה.

  תיקון: מקלדת USB לא עובדת בעיה בחלונות הבית 11/10

תרגילים ראשוניים שיעזרו לכם להרגיש בנוח עם מילות תנאי, לולאות וזיכרון

חימום טיפוסי הוא לזהות האם מספר שלם הוא שלילי. ניתן להחזיר 0 אם הוא חיובי ו-1 אם הוא שלילי.עם RV32I, השוואה עם 0 ו-set-on-less-than פותרת את הבעיה בהוראה אחת מחושבת היטב.

תרגיל שימושי נוסף הוא רשימת גורמים של מספר: חוצה מ-1 ל-n, מדפיסה את המחלקים ומחזירה כמה היותתרגלו ענפים מותנים, חילוק (או חיסור חוזר) ולולאות עם חיבור והשוואה.

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

לולאות, פונקציות ורקורסיה: פקטוריאל, פיבונאצ'י ומגדל האנוי

כשאתם מעצבים לולאות, חשבו על שלושה בלוקים: condition (מצב), body (גוף) ו-step (צעד). עם beq/bne/bge וקפיצות ללא תנאי jal/j while/for נבנים ללא מסתורין, בהסתמך על תוספות והשוואות.

.text
.globl __start
__start:
  li t0, 0        # i
  li t1, 10       # max
cond:
  bge t0, t1, end # si i >= max, salta
  # cuerpo: usar a0/a1 segun servicio IO del entorno
  addi t0, t0, 1  # i++
  j cond
end:
  li a0, 10
  ecall

בקריאות לפונקציה, יש לכבד את ה-ABI: חסוך אם אתה מתכוון לשרשר שיחות נוספות, שומר על s0–s11 ​​אם משנים אותם, ומשתמש במחסנית כאשר sp נע בכפולות של מילה.

פקטוריאלי הוא הרקורסיה הקלאסית: מקרה בסיס n==0 מחזיר 1; אחרת, קרא ל- factorial(n-1) והכפל ב-n. הגן על ra ועל אוגרים שנשמרו במחסנית לפני הקריאה ושחזר אותם עם החזרה.

factorial:
  beq a0, x0, base
  addi sp, sp, -8
  sw   ra, 4(sp)
  sw   s0, 0(sp)
  mv   s0, a0
  addi a0, a0, -1
  jal  factorial
  mul  a0, a0, s0
  lw   s0, 0(sp)
  lw   ra, 4(sp)
  addi sp, sp, 8
  jr   ra
base:
  li a0, 1
  jr ra

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

גישה לזיכרון, מערכים ומניפולציה של מחרוזות

ב-RISC-V, גישה לזיכרון מתבצעת באמצעות load/store: lw/sw עבור מילים, lh/sh עבור חצאי מילים, ו-lb/sb עבור בתים, עם וריאציות חתומות או לא חתומות בהאשמות (lb לעומת lbu, lh לעומת lhu).

  כיצד להסיר את Advanced Mac Cleaner

כדי לעבור בין מערכי מספרים שלמים, השתמשו בהיסטים של 4 בייטים לכל אינדקס; עבור מחרוזות טקסט, מתקדם בייט אחר בייט עד שהוא מוצא את הטרמינטור אם המוסכמה דורשת זאת (למשל, \0). זכרו לשמור כתובות בסיס ולטפל במצביעים באמצעות addi/auipc/la כנדרש.

תכנון מעבד RV32I מאפס: סקירה כללית

אם בא לכם לרדת לסיליקון, פרויקט חינוכי בונה מעבד RV32I ב-VHDL, ניתן לסינתזה ב-FPGA טווח נמוך-בינוני. כולל זיכרון RAM לתוכניות, זיכרון RAM לנתונים ו-GPIO פשוט להדלקת נורית LED.

הליבה מממשת את רפרטואר הבסיס (ללא הרחבות M/A/C או CSRs), משתמש באפיק כתובות של 32 סיביות ומאפשר גישה לזיכרון מורחב-סימנים של 8/16/32 סיביות במידת הצורך. התכנון מפריד בבירור בין אוגרים, ALU, בקר זיכרון ומכונת מצבים.

ALU, משמרות, והרעיון של "טעינה מושהית"

ה-ALU מתואר בשילוב עם פעולות כגון חיבור, חיסור, XOR, OR, AND, השוואות (עם חתימה ולא חתימה) ותזוזות לוגיות/אריתמטיות.

כדי לחסוך ב-LUTs ב-FPGA, מיושמות הסטות מרובות סיביות איטרציה של היסט של 1 ביט הנשלט על ידי מכונת המצביםאתה צורך מספר מחזורים, אך מפחית משאבים לוגיים.

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

בקר זיכרון ומפת זיכרון: ROM, RAM ו-GPIO

בלוק זיכרון משלב ROM ו-RAM למרחב רציף, פישוט ממשק המעבדהבקר מקבל את AddressIn (32 סיביות), את DataIn, את הרוחב (בייט/חצי/מילה), את אות הרחבת הסימן, את WE (קריאה/כתיבה) ואת Start כדי ליזום עסקאות.

כאשר המבצע מסתיים, ReadyOut מוגדר ל-1, ואם הוא נקרא, DataOut מכיל את הנתונים (עם מורחב סימן כאשר מתבקש). אם הם נכתבו, הנתונים נשארים ב-RAM.

דוגמה למפה מעשית: ROM מ-0x0000 עד 0x0FFF, בייט GPIO בגודל 0x1000 (ביט 0 לפין) ו- זיכרון RAM מ-0x1001 עד 0x1FFFבעזרת זה אתה יכול ליצור מהבהב על ידי כתיבה והפעלה/כיבוי של ביט הפלט.

אוגרים, מרבבים ומכונות מצב

המעבד מגדיר 32 אוגרים לשימוש כללי המיוצרים באמצעות מערכים ב-VHDL, עם מפענח כדי לבחור את יעד הכתיבה מה-ALU ולשמור את השאר.

מרבבים שולטים בכניסות ALU (אופרנדים ותפעול), האותות לבקר הזיכרון (רוחבים, כתובת, בקרת התחלה וקריאה/כתיבה) והאוגרים המיוחדים: PC, IR ומונה עזר עבור היסט איטרטיבי.

מכונת המצב מתחילה ב לאתחל, מאחזר את ההוראה אליה מצביע המחשב (קריאה של 4 בייטים), הוא נטען לתוך ה-IR כשהוא מוכן ועובר לצמתי הביצוע: ALU (הוראה אחת במחזור אחד למעט הזזות), טעינה/אחסון, ענפים וקפיצות, בנוסף להוראות מיוחדות כגון ebreak.

  דרכים קלות לתיקון מערכת שבורה באקסל

שרשרת כלים צולבת, קישור וניפוי שגיאות

כדי ליצור קבצים בינאריים של RV32I, השתמש ב- Cross GCC (מטרה riscv32-none-elf)אתה קומפיל את קוד המקור של C/C++/ASM, מקשר אותו לסקריפט שמגדיר את מפת הזיכרון, וממיר את הפלט לצורה שה-ROM/FPGA שלך מצפה לה.

סקריפט וו פשוט יכול למקם טקסט ב-ROM מ-0x0000 ו-.data ב-RAM מ-0x1004 (אם 0x1000–0x1003 תפוס על ידי אוגרי GPIO). שגרת האתחול יכולה להיות "עירומה" ולמקם את מצביע מחסנית בסוף ה-RAM (למשל 0x1FFC) לפני שמתקשרים לראשי.

/* Mapa simple
 * ROM: 0x00000000 - 0x00000FFF
 * GPIO: 0x00001000 - 0x00001003
 * RAM: 0x00001004 - 0x00001FFF
 */
SECTIONS {
  . = 0x00000000;
  .text : { *(.startup) *(.text) *(.text.*) *(.rodata*) }
  . = 0x00001004;
  .data : { *(.data) *(.data.*) }
}

עם riscv32-none-elf-objdump אתה יכול לפרק את ה-ELF ולבדוק כתובות; לדוגמה, תראו את אתחול ב- 0x00000000 עם הוראות כמו lui/addi/jal והמעבר לקובץ הראשי שלך. עבור סימולציית VHDL, GHDL מייצר עקבות שניתן לפתוח באמצעות GtkWave.

לאחר אימות בסימולציה, העבר את התכנון ל-FPGA (Quartus או שרשרת כלים אחרת). אם זיכרון ה-RAM מוסק כבלוקים פנימיים והקוד ברור RTL, כדאי לסנתז בלי הפתעות, אפילו במכשירים ותיקים.

תזכורות מעשיות וטעויות נפוצות בהתחלה

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

כשאתם מיישמים תכונות, שמירת רשומות ra ו-sN שאתם משנים, ומנהל את המחסנית עם חיבורים/חיסורים מיושרים-מילים ל- sp. עם החזרה, היא משחזרת בסדר הפוך וקופצת עם jr ra.

בסימולטורים כמו יופיטר, בדקו ש __start הוא גלובלי ואתה מסיים אותו עם ecall נכון (a0=10 ליציאה). אם משהו לא מתחיל, בדוק את התווית, את הגלובליות ובצע קומפילציה מחדש (F3).

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

עם בסיס ISA ברור (RV32I, אוגרים ו-ABI), סימולטור נוח כמו יופיטר, ודוגמאות הולכות וגדלות (שלילי, גורמים, אותיות גדולות, לולאות, עצרת, פיבונאצ'י והאנוי), אסמבלר RISC-V מפסיק להיות חומה והופך לשטח עסיסי להבנת איך מעבד חושב. ואם תעזו לרדת ל-VHDL, תראו איך ALU, זיכרון ובקרה משתלבים יחד.החל מאחזור הוראות וטעינה עצלה ועד ממשקי זיכרון ומפה עם ROM, RAM ו-GPIO המאפשרת לך להבהב LED עם המעבד שלך.

התוכניות הטובות ביותר לתכנת
Artaculo relacionado:
7 התוכניות הטובות ביותר לתכנת