إذا كنت مهتمًا بالبرمجة منخفضة المستوى وترغب في تعلم برمجة التجميع على المعماريات الحديثة، فإن RISC-V هي إحدى أفضل نقاط الدخول. تتمتع هذه الـISA المفتوحة بجاذبية كبيرة في الصناعة والأوساط الأكاديميةيسمح لك بالتدرب من المحاكيات البسيطة إلى تشغيلها على FPGA، من خلال سلاسل أدوات كاملة لتجميع C/C++ وفحص ASM الناتج.
في هذا الدليل العملي سأخبرك خطوة بخطوة وبنهج أرضي للغاية، ما تحتاجه لبدء البرمجة في لغة التجميع RISC-V: الأدوات، سير العمل، الأمثلة الرئيسية (الشرطية، الحلقات، الوظائف، استدعاءات النظام)، تمارين المختبر النموذجية، وإذا كنت مستعدًا لذلك، نظرة على كيفية تنفيذ وحدة المعالجة المركزية RV32I وكيفية تشغيل الثنائي الخاص بك على نواة تم تصنيعها بواسطة FPGA.
ما هو مجمع RISC-V وكيف يرتبط بلغة الآلة؟
يحدد RISC-V بنية مجموعة التعليمات المفتوحة (ISA): يتضمن ذخيرة RV32I الأساسية 39 تعليمة متعامد جدًا وسهل التنفيذ. لغة التجميع (ASM) هي لغة برمجة منخفضة المستوى تستخدم اختصارات مثل add وsub وlw وsw وjal، وغيرها، متوافقة مع ISA. شيفرة الآلة الأساسية هي البتات التي يفهمها المعالج؛ أما المُجمِّع فهو تمثيله السهل القراءة. أقرب إلى الأجهزة من أي لغة عالية المستوى.
إذا كنت قادمًا من C، فسوف تلاحظ أن ASM لا يعمل كما هو: يجب تجميعها وربطها لإنتاج ملف ثنائي. في المقابل، يسمح لك بالتحكم في السجلات، وأوضاع العنونة، واستدعاءات النظام بدقة متناهية. وإذا كنت تعمل مع مُحاكي تعليمي، فسترى "ecall" كآلية إدخال/إخراج وإنهاء. مع اتفاقيات محددة اعتمادًا على البيئة (على سبيل المثال، Jupiter مقابل Linux).
الأدوات والبيئة: المحاكيات وسلسلة الأدوات وFPGA
للحصول على بداية سريعة، يعد محاكي الرسوميات Jupiter مثاليًا. إنه برنامج تجميع/محاكاة مصمم للتدريسمستوحى من SPIM/MARS/VENUS، ويُستخدم في الدورات الجامعية. بواسطته، يمكنك كتابة وتجميع وتشغيل برامج RV32I دون الحاجة إلى تهيئة سلسلة أدوات كاملة من الصفر.
إذا كنت تريد الذهاب إلى خطوة أبعد، فقد تكون مهتمًا بسلسلة الأدوات المعدنية العارية: riscv32-none-elf (GCC/LLVM) لتجميع ملفات C/C++ الثنائية إلى RISC-V، وأدوات مثل objdump للتفكيك. لمحاكاة الأجهزة، يتيح لك GHDL تجميع VHDL وتنفيذه وتفريغ الإشارات في ملف .ghw للفحص باستخدام GtkWave. وإذا كنت مستعدًا لاستخدام الأجهزة الفعلية، يمكنك تجميع وحدة المعالجة المركزية RV32I في FPGA مع بيئات التصنيع (على سبيل المثال Intel Quartus) أو سلاسل الأدوات المجانية.
البدء باستخدام جوبيتر: قواعد التدفق والتجميع الأساسية
يُبسط كوكب المشتري منحنى التعلم. يمكنك إنشاء الملفات وتحريرها في علامة التبويب "المحرر"يبدأ كل برنامج عند وسم __start العالمي. تأكد من إعلانه باستخدام تعليمة .globl (نعم، هو .globl وليس .global). تنتهي الوسوم بنقطتين، ويمكن أن تبدأ التعليقات بعلامة # أو ;.
بعض القواعد المفيدة للبيئة: تعليمة واحدة لكل سطروعندما تكون جاهزًا، احفظه واضغط على F3 لتجميعه وتشغيله. يجب أن تنتهي البرامج باستدعاء الخروج ecall؛ في جوبيتر، يُشير ضبط القيمة 10 إلى a0 إلى نهاية البرنامج، كما هو الحال مع "الخروج".
على أقل تقدير، قد يبدو هيكل ASM الخاص بك على كوكب المشتري على هذا النحو، مع نقطة الدخول واضحة والإنهاء بواسطة ecall: وهو أساس بقية التمارين.
.text
.globl __start
__start:
li a0, 10 # código 10: terminar
ecall # finalizar programa
اتفاقيات الاستدعاء (ABI) وإدارة المكدس
تتطلب وظائف البرمجة في لغة التجميع احترام الاتفاقية: عادة ما تصل الحجج في a0..a7عادةً ما تُعاد النتيجة في a0، ويجب أن تحافظ الاستدعاءات على عناوين الإرجاع (ra) والسجلات المحفوظة (s0..s11). للقيام بذلك، يُعدّ المكدس (sp) حليفك: فهو يحجز مساحة عند الدخول ويستعيدها عند الخروج.
بعض الإرشادات التي ستستخدمها طوال الوقت: li و la لتحميل العناوين والرسائل الفورية، add/addi للجمع، lw/sw للوصول إلى الذاكرة، القفزات غير الشرطية j/jal وإرجاع jr ra، بالإضافة إلى الجمل الشرطية مثل beq/bne/bge. إليك تذكير سريع بأمثلة نموذجية:
# cargar inmediato y una dirección
li t1, 5
la t1, foo
# aritmética y actualización de puntero de pila
add t3, t1, t2
addi sp, sp, -8 # reservar 8 bytes en stack
sw ra, 4(sp) # salvar ra
sw s0, 0(sp) # salvar s0
# acceso a memoria con base+desplazamiento
lw t1, 8(sp)
sw a0, 8(sp)
# saltos y comparaciones
beq t1, t2, etiqueta
j etiqueta
jal funcion
jr ra
يمكن هيكلة حلقة كلاسيكية في RISC-V بشكل واضح، حالة الفصل والجسم والخطوةفي Jupiter، يمكنك أيضًا طباعة القيم باستخدام ecall استنادًا إلى الكود الذي تقوم بتحميله في a0:
.text
.globl __start
__start:
li t0, 0 # i
li t1, 10 # max
cond:
bge t0, t1, endLoop
body:
mv a1, t0 # pasar i en a1
li a0, 1 # código ecall para imprimir entero
ecall
step:
addi t0, t0, 1
j cond
endLoop:
li a0, 10 # código ecall para salir
ecall
بالنسبة للوظائف المتكررة، اعتني بحفظ/استعادة السجلات وra. العامل هو المثال القانوني الذي يجبرك على التفكير في إطار المكدس وإعادة التحكم إلى العنوان الصحيح:
.text
.globl __start
__start:
li a0, 5 # factorial(5)
jal factorial
# ... aquí podrías imprimir a0 ...
li a0, 10
ecall
factorial:
# a0 trae n; ra tiene la dirección de retorno; sp apunta a tope de pila
bne a0, x0, notZero
li a0, 1 # factorial(0) = 1
jr ra
notZero:
addi sp, sp, -8
sw s0, 0(sp)
sw ra, 4(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
الإدخال/الإخراج باستخدام ecall: الاختلافات بين Jupiter وLinux
يتم استخدام تعليمة ecall لاستدعاء الخدمات من البيئة. في كوكب المشتري، أكواد بسيطة في a0 (على سبيل المثال، 1 طباعة عدد صحيح، 4 طباعة سلسلة، 10 خروج) التحكم في العمليات المتاحة. أما في لينكس، فعادةً ما تحتوي a0..a2 على معلمات، وa7 رقم استدعاء النظام، والدلالات تتوافق مع استدعاءات النواة (الكتابة، الخروج، إلخ).
يوضح هذا الأمر "Hello World" لنظام Linux النمط: تقوم بإعداد السجلات a0..a2 وa7 وشغّلت ecall. لاحظ التوجيه .global ونقطة الإدخال _start:
# a0-a2: argumentos; a7: número de syscall
.global _start
_start:
addi a0, x0, 1 # 1 = stdout
la a1, holamundo # puntero al mensaje
addi a2, x0, 11 # longitud
addi a7, x0, 64 # write
ecall
addi a0, x0, 0 # return code 0
addi a7, x0, 93 # exit
ecall
.data
holamundo: .ascii "Hola mundo\n"
إذا كان هدفك هو ممارسة منطق التحكم والذاكرة والوظائف، يمنحك كوكب المشتري ردود فعل فورية تتضمن العديد من المختبرات مُقيّمًا تلقائيًا للتحقق من صحة حلّك. إذا كنت ترغب في التدرب على التفاعل مع النظام الحقيقي، فستقوم بالتجميع لنظام لينكس واستخدام استدعاءات نظام النواة.
تمارين البدء: الشرطيات والحلقات والوظائف
تغطي مجموعة التمارين الكلاسيكية للبدء في RISC-V ASM ثلاثة ركائز: الشرطيات والحلقات واستدعاءات الوظائف، مع التركيز على الإدارة السليمة للسجلات والمكدسات:
- سلبي: دالة ترجع 0 إذا كان الرقم موجبًا و1 إذا كان سالبًا. يستقبل الحجة في a0 ويعيدها في a0، دون تدمير السجلات غير المتطايرة.
- العامل: تنفيذ حلقة من خلال مقسومات الرقم، وطباعتها في وقت التشغيل وإرجاع المبلغ الإجمالي. ستتدرب على الدورات والتقسيم/التعديل والمكالمات للاتصال لطباعة.
- أعلى: نظرًا لوجود مؤشر إلى سلسلة، قم بالمرور عليه وتحويل الأحرف الصغيرة إلى أحرف كبيرة في مكانها. إرجاع نفس العنوانإذا قمت بتحريك المؤشر أثناء الحلقة، قم بإعادة تعيينه قبل العودة.
بالنسبة لجميع الثلاثة، فهو يحترم اتفاقية تمرير المعلمات وإرجاعها، وينتهي البرنامج بـ exit ecall عند تجربته على كوكب المشتري، تتناول هذه التمارين التحكم في التدفق، والذاكرة، ووظائف الحالة.
التعمق أكثر: من RV32I ISA إلى وحدة المعالجة المركزية القابلة للتركيب
يتميز RISC-V بانفتاحه: يمكن لأي شخص استخدام نواة RV32I. تتوفر تصاميم تعليمية توضح خطوة بخطوة كيفية بناء وحدة معالجة مركزية أساسية تُشغّل برامج حقيقية. تم تجميعها باستخدام GCC/LLVM لـ riscv32-none-elfتُعلمك التجربة الكثير عما يحدث "تحت الغطاء" عندما تقوم بتشغيل المُجمِّع الخاص بك.
يتضمن التنفيذ النموذجي وحدة تحكم في الذاكرة تقوم بتلخيص ROM وRAM، مترابطة مع النواة. عادةً ما تحتوي واجهة وحدة التحكم هذه على:
- AddressIn (32 بت): العنوان الذي يجب الوصول إليه. يحدد أصل الوصول من التعليمات أو البيانات.
- البيانات الداخلة (٣٢ بت): البيانات المراد كتابتها. لأنصاف الكلمات، يُستخدم ١٦ بتًا فقط من النوع الأقل طولًا (LSB)؛ وللبايتات، يُستخدم ٨ بتات من النوع الأقل طولًا (LSB). تم تجاهله في القراءة.
- العرض: 0=بايت، 1=نصف كلمة (16 بت)، 2 أو 3=كلمة (32 بت). التحكم في الحجم.
- ExtendSignIn: ما إذا كان سيتم تمديد تسجيل الدخول في DataOut عند قراءة 8/16 بت. يتم تجاهله في الكتابات.
- WEIn: 0=قراءة، 1=كتابة. اتجاه الوصول.
- StartIn: حافة البدء؛ يؤدي تعيينها على 1 إلى بدء المعاملة، متزامنة مع الساعة.
عندما يكون ReadyOut=1، تكون العملية مكتملة: عند القراءة، يحتوي DataOut على البيانات (مع تمديد الإشارة إن وجد)؛ عند الكتابة، تكون البيانات موجودةً بالفعل في الذاكرة. تتيح لك هذه الطبقة تبديل ذاكرة الوصول العشوائي (RAM) الداخلية من نوع FPGA، أو ذاكرة الوصول العشوائي الديناميكية (SDRAM)، أو ذاكرة الوصول العشوائي الثابتة (PSRAM) الخارجية دون لمس النواة.
تعرف منظمة تعليمية بسيطة ثلاثة مصادر VHDL: ROM.vhd (4 كيلوبايت)، RAM.vhd (4 كيلوبايت) وMemory.vhd (8 كيلوبايت) الذي يتكامل مع مساحة متجاورة (ذاكرة ROM عند 0x0000..0x0FFF، وذاكرة RAM عند 0x1001..0x1FFF) ومدخل/مدخلات عام (GPIO) مُعيَّن عند 0x1000 (بت 0 إلى دبوس). يُنشئ مُتحكم MemoryController.vhd "الذاكرة" ويُوفِّر واجهة للنواة.
حول النواة: تحتوي وحدة المعالجة المركزية على 32 سجلًا بطول 32 بت (x0..x31)، حيث يكون x0 مرتبطًا بالصفر وغير قابل للكتابة. في VHDL من الشائع نمذجتها باستخدام المصفوفات وإنشاء الكتل. لتجنب تكرار المنطق يدويًا، وفك تشفير 5 إلى 32 لتحديد السجل الذي يستقبل الإخراج من وحدة الحساب والمنطق.
يتم تنفيذ وحدة الحساب والمنطق بشكل تركيبي باستخدام محدد (ALUSel) للعمليات مثل الجمع والطرح وXOR وOR وAND، النزوحات (SLL، SRL، SRA) والمقارنات (LT، LTU، EQ، GE، GEU، NE)لحفظ جداول البحث (LUTs) في وحدات FPGA، هناك تقنية شائعة تتمثل في تنفيذ تحولات 1 بت وتكرارها N دورة باستخدام آلة الحالة؛ وهذا يزيد من زمن الوصول، ولكن يتم تقليل استهلاك الموارد.
يتم التحكم باستخدام أجهزة الإرسال المتعددة لمدخلات وحدة الحساب والمنطق (ALUIn1/2 وALUSel)، واختيار سجل الوجهة (RegSelForALUOut)، إشارات إلى وحدة تحكم الذاكرة (MCWidthIn، MCAddressIn، MCStartIn، MCWEIn، MCExtendSignIn، MCDataIn)، وسجلات خاصة PC، وIR، وعداد لحساب التحولات. يتم التحكم في كل هذا بواسطة آلة حالة تحتوي على حوالي ٢٣ حالة.
المفهوم الرئيسي في FSM هو "التحميل المتأخر": يتجسد تأثير اختيار مدخل MUX عند حافة الساعة التاليةعلى سبيل المثال، عند تحميل IR مع التعليمات الواردة من الذاكرة، تمر التسلسل عبر حالات الجلب (تشغيل القراءة على عنوان PC)، وانتظار ReadyOut، ونقل DataOut إلى IR، وفي الدورة التالية، فك التشفير والتنفيذ.
مسار الجلب النموذجي: عند إعادة التعيين، تقوم بإجبار PC=RESET_VECTOR (0x00000000)، ثم تقوم بتكوين برنامج التشغيل لقراءة 4 بايتات على عنوان PC، تم انتظار ReadyOut وتم تحميل IRمن هناك، تدير حالات مختلفة وحدات المنطق الحسابية ذات الدورة الواحدة، والتحولات متعددة الدورات، والأحمال/المخازن، والفروع، والقفزات، و"الخاصة" (يمكن أن يؤدي تنفيذ التدريس إلى توقف المعالج عن عمد بسبب ebreak).
قم بتجميع الكود الحقيقي وتشغيله على RISC-V الخاص بك
إن أحد الطرق التعليمية للغاية "لإثبات المفهوم" هو تجميع برنامج C/C++ باستخدام المترجم المتقاطع riscv32-none-elf، إنشاء الملف الثنائي وتفريغه إلى ROM VHDLثم تقوم بالمحاكاة في GHDL وتحليل الإشارات في GtkWave؛ إذا سارت الأمور على ما يرام، تقوم بالتوليف في FPGA وتشاهد النظام يعمل في السيليكون.
أولاً، نص رابط يتكيف مع خريطتك: ROM من 0x00000000 إلى 0x00000FFF، GPIO عند 0x00001000 وذاكرة الوصول العشوائي (RAM) من 0x00001001 إلى 0x00001FFF. للتبسيط، يمكنك وضع ملف .text (بما في ذلك قسم .startup) في ذاكرة القراءة فقط (ROM) وملف .data في ذاكرة الوصول العشوائي (RAM)، مع استبعاد تهيئة البيانات إذا كنت ترغب في جعل الإصدار الأول أقصر.
باستخدام هذه الخريطة، تقوم روتين التمهيد البسيط بوضع المكدس في نهاية SRAM واستدعاء main؛ تم وضع علامة "عارية" عليها وفي قسم .startup لوضعه في RESET_VECTOR. بعد التجميع، يُتيح لك objdump رؤية ASM الفعلي الذي سينفذه المعالج (lui/addi لبناء sp، jal لل main، إلخ).
أحد الأمثلة الكلاسيكية على الوميض هو تبديل البت 0 من GPIO المخصص: انتظار قصير لتصحيح الأخطاء في جهاز المحاكاة (GHDL+GtkWave) وعلى الأجهزة الحقيقية، زد العدد بحيث يكون الوميض ملحوظًا. يمكن لملف Makefile إنتاج ملف .bin ونص برمجي يحول هذا الملف الثنائي إلى ملف initialization.vhd لذاكرة القراءة فقط؛ بمجرد التكامل، تقوم بتجميع VHDL بأكمله، ومحاكاته، ثم تجميعه..
يعمل هذا النهج التعليمي حتى على وحدات FPGA القديمة (على سبيل المثال، Intel Cyclone II)، حيث يتم استنتاج ذاكرة الوصول العشوائي الداخلية باستخدام القالب الموصى به ويمكن أن يكون التصميم فعالاً في استخدام الموارد بنسبة 66% تقريبًا. الفائدة التربوية هائلة:شاهد كيف يتقدم جهاز الكمبيوتر، وكيف يتم تشغيل عمليات القراءة (mcstartin)، ويقوم ReadyOut بالتحقق من صحة البيانات، ويقوم IR بالتقاط التعليمات وكيف يتم نشر كل قفزة أو انتقال عبر FSM.
القراءات والممارسات والمصحح التلقائي: خريطة طريق
في الأوساط الأكاديمية، من الشائع أن تكون هناك أهداف واضحة: التدرب على الشرطيات والحلقات، وكتابة الدوال مع مراعاة الاتفاقية وإدارة الذاكرة. عادةً ما توفر الأدلة قوالب، ومحاكيًا (جوبيتر)، وتعليمات تثبيت، وبرنامج تصحيح تلقائي.
لتحضير بيئتك، اقبل المهمة في Github Classroom إذا طُلب منك ذلك، واستنسخ المستودع، وافتح Jupiter. تذكر أن __start يجب أن يكون عالميًا، أن التعليقات يمكن أن تكون # أو ;، وأن هناك تعليمة واحدة لكل سطر، وأنه يجب إنهاء الأمر بـ ecall (الرمز 10 في a0). ترجم باستخدام F3 وشغّل الاختبارات. إذا لم يبدأ التشغيل، فالحل الأمثل هو إعادة تشغيل الجهاز.
فيما يتعلق بالشكل المتوقع لكل تمرين، تتضمن العديد من الأدلة لقطات شاشة وتحدد: على سبيل المثال، يقوم Factor بطباعة المقسومات المفصولة بمسافات ويعيد العدد؛ يجب على Upper المرور عبر السلسلة وتحويل الأحرف الصغيرة فقط إلى أحرف كبيرة، دون لمس المسافات أو الأرقام أو علامات الترقيم، وإرجاع المؤشر الأصلي.
يقوم التقييم عادة بتوزيع النقاط لكل سلسلة (10/40/50) و يمكنك تشغيل فحص لرؤية درجة التصنيف التلقائي.عندما تكون راضيًا، أضف/أرسل/ادفع، ثم حمّل رابط المستودع أينما هو مُحدد. هذا النظام المُتبع في دورة الحياة يُعوّدك على التحقق الدقيق والتسليم.
تمارين أخرى لتعزيز: فيبوناتشي، هانوي، وقراءة لوحة المفاتيح
بمجرد أن تتقن الأساسيات، يمكنك العمل على ثلاثة كلاسيكيات إضافية: fibonacci.s, hanoi.sy syscall.s (أو أي متغير آخر يقرأ من لوحة المفاتيح ويكرر السلسلة).
- فيبوناتشي: يمكنك جعله متكررًا أو تكراريًا؛ إذا جعلته متكررًا، كن حذرا مع التكلفة ومع الحفاظ على ra/s0؛ تمارين تكرارية حول الحلقات والإضافات.
- هانوي: ترجمة الدالة التكرارية إلى لغة التجميع. مع الحفاظ على السياق والوسيطات بين الاستدعاءات. إطار مكدس منضبط. يطبع حركات "الأصل → الوجهة" باستخدام ecall.
- القراءة والتكرار: قراءة عدد صحيح وسلسلة، وطباعة السلسلة N مرة. على كوكب المشتري، استخدم رموز الاتصال الإلكترونية المناسبة متوفر في ممارستك؛ على Linux، قم بإعداد a7 وa0..a2 للقراءة/الكتابة.
تعمل هذه التمارين على تعزيز تمرير المعلمات والحلقات والإدخال والإخراج. إنهم يجبرونك على التفكير في التفاعل مع البيئة (Jupiter vs Linux)، وهيكلة ASM لتكون قابلة للقراءة وقابلة للصيانة.
تفاصيل التنفيذ الدقيقة: السجلات، ووحدات الحساب والمنطق، والحالات
بالعودة إلى جوهر التعليم RV32I، يجدر بنا مراجعة العديد من التفاصيل الدقيقة التي تربط ما تراه عند البرمجة بكيفية تنفيذ الأجهزة: جدول عمليات وحدة الحساب والمنطق يتم تحديدها بواسطة ALUSel (ADD، SUB، XOR، OR، AND، SLL، SRL، SRA، المقارنات الموقعة وغير الموقعة)، و"الهوية" كحالة افتراضية، و"الحيلة" لاستخدام عداد لتجميع التحولات متعددة الدورات.
يُنتج منطق التسجيل مع التوليد فك تشفير 5→32، و الحالة RegSelForALUOut=00000 لا تفعل شيئًا (x0 غير قابل للكتابة، فهو دائمًا يساوي صفرًا). لدى كلٍّ من الكمبيوتر الشخصي (PC) والمراقب (IR) والعداد (Counter) وحدات MUX خاصة بهم، تُدار بواسطة FSM: من إعادة الضبط، الجلب، فك التشفير/التنفيذ (وحدات المنطق الحسابية ذات الدورة الواحدة أو حلقات التحويل)، الأحمال/المخازن، الفروع الشرطية، jal/jalr، والتخصصات مثل ebreak.
في الوصول إلى ذاكرة البيانات، يعد تنسيق MUX→Controller أمرًا أساسيًا: MCWidthIn (8/16/32 بت)، وMCWEIn (R/W)، وMCAddressIn (من السجلات أو الكمبيوتر الشخصي)، وMCExtendSignIn (لـ LB/LH الموقع) وMCStartIn. يجب عليك التقاط DataOut فقط عندما يكون ReadyOut=1 وحالة التقدم. هذا يُوائِم عقلية مبرمج ASM لديك مع واقع الأجهزة الزمني.
كل هذا يرتبط بشكل مباشر بما تلاحظه في المحاكاة: في كل مرة يتقدم فيها الكمبيوتر الشخصي، يتم تشغيل قراءة التعليماتيُخبرك MCReadyOut بإمكانية تحميل IR، ومن ثم تُفعّل التعليمات (مثلًا: «lui x2,0x2» متبوعًا بـ «addi x2,x2,-4» لإعداد sp، و«jal x1, …» لاستدعاء main). رؤية هذه التعليمات في GtkWave مُثيرة للاهتمام.
الموارد والتبعيات والنصائح النهائية
لإعادة إنتاج هذه التجربة، تحتاج إلى بعض التبعيات: GHDL لتجميع VHDL و GtkWave لتحليل الإشاراتبالنسبة للمُجمِّع المتقاطع، يُمكن استخدام أي GCC riscv32-none-elf (يمكنك تجميع مُجمِّعك الخاص أو تثبيت مُجمَّع مُسبقًا). لنقل النواة إلى FPGA، استخدم بيئة المُصنِّع (مثل Quartus على Intel/Altera) أو سلاسل أدوات مجانية متوافقة مع جهازك.
بالإضافة إلى ذلك، من المفيد قراءة أدلة وملاحظات RISC-V (على سبيل المثال، الإرشادات والبطاقات الخضراء)، والاستشارة كتب البرمجة، وممارسة مع المختبرات بما في ذلك Jupiter و Autograder. الحفاظ على الروتين: التخطيط والتنفيذ والاختبار باستخدام الحالات الحدية، ثم دمجه في مشاريع أكبر (مثل Blinker على FPGA).
مع كل هذه المعلومات، لديك بالفعل الأساسيات اللازمة للبدء: لماذا يتم استخدام لغة التجميع مقابل الكود الآلي، وكيفية إعداد بيئة باستخدام Jupiter أو Linux، أنماط الحلقة، والشرطيات، والوظائف مع التعامل الصحيح مع المكدس، ونافذة على التنفيذ المادي لفهم أفضل لما يحدث عند تنفيذ كل تعليمة.
إذا كنتَ من مُحبي التعلم بالممارسة، فابدأ بـ Negative وFactor وUpper، ثم انتقل إلى Fibonacci/Hanoi وبرنامج قراءة لوحة المفاتيح. عندما تشعر بالراحة، قم بتجميع لغة C++ بسيطة، ثم انقل ذاكرة القراءة فقط (ROM) إلى VHDL، وقم بالمحاكاة باستخدام GHDL، ثم انتقل إلى FPGA. إنها رحلة من الأقل إلى الأكثر حيث تتناسب كل قطعة مع القطعة التالية.، والرضا الناتج عن رؤية الكود الخاص بك يحرك GPIO أو يومض مؤشر LED لا يقدر بثمن.