
في الآونة الأخيرة ، تم نشر مقال على Habr حول كيفية تجربة بنية RISC-V دون تكلفة الأجهزة. ولكن ماذا لو كنت تفعل هذا على لوحة التصحيح؟ تذكر الميمات حول منشئ اللعبة: حوالي 20 علامة اختيار بأسلوب "الرسومات ليست أسوأ من الأزمة" ، "يمكنك سرقة corovans" وزر "إنشاء". يتم ترتيب مولد RocketChip SoC بنفس الطريقة تقريبًا ، فقط لا توجد نافذة تحتوي على علامات اختيار ، ولكن رمز Scala وقليلًا من أدوات التجميع وإنشاء الملفات. في هذه المقالة ، سأبيّن مدى سهولة نقل RocketChip من Xilinx الأصلي إلى Altera / Intel.
إخلاء المسئولية : المؤلف غير مسؤول عن اللوحة "المحروقة" - راقب بعناية كيفية تكوين الدبابيس وما تتصل به جسديًا وما إلى ذلك. حسنا ، لاحظ أيضًا احتياطات السلامة. لا يجب أن تعتقد أنه نظرًا لأن كل شيء متصل عبر USB ، فمن المؤكد أنه آمن لشخص: كما فهمت في تجاربي السابقة ، حتى لو كنت تعمل باستخدام لوحة USB ، فلا يزال عليك عدم لمس بطارية التدفئة بقدمك ، لأن الفرق المحتمل ... أوه نعم ، أنا لست تقريبا plisovode محترف أو مهندس الكتروني - أنا مجرد مبرمج Scala.
بقدر ما أفهم ، كان النظام الأساسي للتصحيح RocketChip Xilinx FPGA. إذا حكمنا من خلال المستودع ، الذي سنستنسخه قريبًا ، تم نقله أيضًا إلى Microsemi. في مكان ما سمعت عن استخدام ألتيرا ، لكن لم أر المصدر. كما اتضح ، ليست هذه مشكلة كبيرة: منذ اللحظة التي استقبلنا فيها اللوحة وبدأنا في دراسة مخزن SiFive Freedom إلى "ذاكرة خالية" (أي امتلاك سجلات المعالج و BootROM والسجلات المعينة للذاكرة) فقط 32 بت "متحكم دقيق" شيء يتحول nanocontroller به ...) 3 أيام عطلة و 4 ليالٍ في الأسبوع ، وقد استغرق الأمر أقل من ذلك إذا كان الأمر يتعلق بي على الفور لتحديد تعريف SYNTHESIS
العالمي.
المواد
بالنسبة للمبتدئين - قائمة المواد بالمعنى الواسع للكلمة. نحن بحاجة إلى البرنامج التالي:
- RocketChip - يحتوي على المعالج نفسه وبيئة التجميع (بما في ذلك ، يصف عدة مستودعات فرعية). قادرة على إنشاء RocketChip مع معالج في ترتيب الصواريخ.
- حرية SiFive - ملزمة لمختلف لوحات التصحيح - هناك سنضيف الدعم لدينا
- أدوات صاروخية - أدوات لإنشاء كود وتصحيح الأخطاء لبنية RISC-V ذات 32 و 64 بت
- openocd-riscv - منفذ OpenOCD للعمل على JTAG مع حبات RISC-V (على الأقل RocketChip)
- مجتمع IntelliJ IDEA لتحرير كود Scala ، والذي يمثل الأغلبية في RocketChip
- قد تكون سلسلة أدوات SiFive سابقة التجميع في متناول يدي ، وهو رابط رأيته في المقال المذكور بالفعل
ما هو مطلوب من الحديد:
- طقم Zeowaa من Aliexpress: لوح مع إعصار 4 EP4CE115 و (استنساخ؟) USB Blaster
- RaspberryPi كما المصحح JTAG لبنية لينة
يفترض أن الجهاز المضيف هو كمبيوتر قوي نسبياً مع تثبيت Ubuntu و Quartus Lite 18.
في الواقع ، هناك خيار للإطلاق في سحابة Amazon على مثيلات FPGA الخاصة بهم من SiFive نفسه ، يطلق عليه FireSim ، لكنه ليس ممتعًا للغاية ، نعم ، ومصابيح LED سيئة الرؤية . بالإضافة إلى ذلك ، في هذه الحالة ، ستحتاج إلى تحديد مفتاح API الخاص بك على مثيل التحكم لتشغيل أجهزة افتراضية أخرى ، وتحتاج إلى عناية كبيرة به ، وإلا يُشاع أنه يمكنك الاستيقاظ في يوم ما بعشرات الآلاف من الدولارات ...
نحن ندرس الوضع
للبدء ، أخذت مشروع اختبار read_write_1G
من بائع اللوحة وحاولت إضافة المصادر المطلوبة إليه. لماذا لا تنشئ واحدة جديدة؟ لأنني جديد ، وفي هذا المشروع تم بالفعل تعيين أسماء المسامير. لذلك ، تحتاج إلى أخذ التعليمات البرمجية المصدر من مكان ما. للقيام بذلك ، سنحتاج إلى مستودع freedom
المحدد بالفعل (يجب عدم الخلط بينه وبين freedom-e-sdk
). للحصول على شيء على الأقل ، سنقوم بتجميع rocket-tools
وفقًا للتعليمات (إطلاق حرفي نصين وكثير من الانتظار) ، ثم تشغيل
RISCV=$(pwd)/../rocket-tools make -f Makefile.e300artydevkit verilog mcs
mcs
Verilog الهدف بإنشاء ملف Verilog ضخم يحتوي على مصادر للمعالج بالنسبة لنا ، وسيجمع mcs
BootROM. لا داعي للقلق من فشل mcs
- ليس لدينا Xilinx Vivado ، لذلك لا يمكن تحويل BootROM المترجمة إلى التنسيق الذي يحتاجه Vivado.
عبر عنصر القائمة Quartus Project -> إضافة / إزالة الملفات في Project ... إضافة freedom/builds/e300artydevkit/sifive.freedom.everywhere.e300artydevkit.E300ArtyDevKitConfig.v
، قم بتعيين كيان المستوى الأعلى: E300ArtyDevKitFPGAChip ، لن تظهر قائمة الإكمال التلقائي لكيان المستوى الأعلى إلا بعد التحويل البرمجي الأول). نتيجة لذلك ، نحصل على أطنان من الأخطاء تخبرنا عن غياب AsyncResetReg
، وحدات IOBUF
، إلخ. إذا لم تكن هناك أخطاء ، فقد نسيت تغيير كيان المستوى الأعلى. إذا بحثت عن المصدر ، فيمكنك العثور مباشرة على ملف AsyncResetReg.v
، لكن IOBUF
ملزم بنواة IP في Xilinx . أولاً ، قم بإضافة freedom/rocket-chip/src/main/resources/vsrc/AsyncResetReg.v
. وسيتم إضافة plusarg_reader.v
أيضًا.
قم بتشغيل التحويل البرمجي واحصل على خطأ آخر:
Error (10174): Verilog HDL Unsupported Feature error at plusarg_reader.v(18): system function "$value$plusargs" is not supported for synthesis
من حيث المبدأ ، يمكن توقع الإنشاءات غير المركبة من ملف يحمل نفس الاسم.
plusarg_reader.v // See LICENSE.SiFive for license details. //VCS coverage exclude_file // No default parameter values are intended, nor does IEEE 1800-2012 require them (clause A.2.4 param_assignment), // but Incisive demands them. These default values should never be used. module plusarg_reader #(parameter FORMAT="borked=%d", DEFAULT=0) ( output [31:0] out ); `ifdef SYNTHESIS assign out = DEFAULT; `else reg [31:0] myplus; assign out = myplus; initial begin if (!$value$plusargs(FORMAT, myplus)) myplus = DEFAULT; end `endif endmodule
كما نرى ، من المحتمل أن تقرأ هذه الوحدة النمطية خيارات المحاكاة من سطر الأوامر ، وعند تجميعها ببساطة تقوم بإرجاع القيمة الافتراضية. المشكلة هي أن مشروعنا لا يعرف تعريف SYNTHESIS
المسمى. سيكون من الممكن ifdef
`define SYNTHESIS
ifdef
في السطر السابق مباشرة قبل ifdef
، ثم قضاء نصف أسبوع في محاولة لفهم سبب عدم بدء النواة (وبعد كل ذلك ، يتم تصنيع العدوى ...). لا تكرر أخطائي ، ولكن ببساطة افتح خصائص المشروع مرة أخرى ، وعلى علامة التبويب إعدادات برنامج التحويل البرمجي-> Verilog HDL Input ، حدد الماكرو SYNTHESIS
، وعلى الأقل 1 ، وليس في <NONE>
(سطر فارغ).
هنا! الآن أقسمت Quartus الارتباطات المفقودة - لقد حان الوقت لإقامة مشروع في Idea وبدء النقل.
التعرف على المشروع
نخبر فكرة مشروع الاستيراد ، وحدد المسار إلى مستودع freedom
، وأشر إلى نوع مشروع sbt ، وتحقق من استخدام sbt shell للاستيراد ، ولخانات الاختيار للبناء. يبدو أن الحكاية الخيالية تنتهي هنا ، لكنها لا تجد فكرة مشروع نصف - جميع رموز المصدر باللون الأحمر. بناءً على المعلومات الواردة من هنا ، حصلت على الإجراء التالي:
- فتح نافذة قذيفة sbt
- دخول
clean
- اضغط على زر إعادة التشغيل الأخضر على اليسار
- بعد إعادة تشغيل sbt ، أدخل الأمر الأول
++2.12.4
، وبالتالي تحويل جميع المشاريع الفرعية إلى Scala الإصدار 2.12.4 ، ثم سنقوم بتشغيل compile
- انقر على تحديث جميع مشاريع sbt Idea
- الربح! ، الآن تعمل الإضاءة الخلفية بشكل صحيح
حتى الآن ، سأحاول تجميع المشروع بطريقة ما ، على الأقل في الوضع شبه اليدوي. بالمناسبة ، شخص ما ، قل quartus_ipcreate
، هو quartus_ipcreate
في الطبعة لايت على الإطلاق؟ سننشئ أشكال IP المتنوعة يدويًا في الوقت الحالي ، وسوف تكون الروابط في Scala فقط في شكل BlackBox.
في هذه الحالة ، نحن مهتمون بالتسلسل الهرمي للدليل التالي:
fpga-shells () | +-src/main/scala | +- ip/intel <-- IP Variations | | | +- Intel.scala | +- shell/intel <-- | +- ZeowaaShell.scala src/main/scala | +- everywhere.e300artydevkit <-- "" , | +- zeowaa/e115 <-- "" SoC | +- Config +- FPGAChip +- Platform +- System
تحتاج أيضًا إلى إضافة Makefile يشبه Makefile.e300artydevkit
، شيء مثل هذا:
Makefile.zeowaa-E115:
# See LICENSE for license details. base_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) BUILD_DIR := $(base_dir)/builds/zeowaa-e115 FPGA_DIR := $(base_dir)/fpga-shells/intel MODEL := FPGAChip PROJECT := sifive.freedom.zeowaa.e115 export CONFIG_PROJECT := sifive.freedom.zeowaa.e115 export CONFIG := ZeowaaConfig export BOARD := zeowaa export BOOTROM_DIR := $(base_dir)/bootrom/xip rocketchip_dir := $(base_dir)/rocket-chip sifiveblocks_dir := $(base_dir)/sifive-blocks include common.mk
الارتباطات
بادئ ذي بدء ، نحن ننفذ هذا IOBUF
- سيكون بالكاد صعبًا. اذا حكمنا من خلال كود Scala ، فهذه هي الوحدة التي تتحكم في "الساق" المادية (الكرة؟) للدائرة الصغيرة: يمكن تشغيلها ، أو يمكن تشغيلها ، أو يمكن إيقافها بالكامل. في الجزء الأيمن من نافذة Quartus ، سوف ندخل "IOBUF" في كتالوج IP ، وعلى الفور الحصول على مكون باسم ALTIOBUF
. دعونا نضع اسمًا لملف الأشكال ، حدد "كمنطقة عازلة ثنائية الاتجاه". بعد ذلك ، ستظهر وحدة تسمى iobuf
في مشروعنا:
// ... module obuf ( datain, oe, dataout); input [0:0] datain; input [0:0] oe; output [0:0] dataout; wire [0:0] sub_wire0; wire [0:0] dataout = sub_wire0[0:0]; obuf_iobuf_out_d5t obuf_iobuf_out_d5t_component ( .datain (datain), .oe (oe), .dataout (sub_wire0)); endmodule // ...
دعنا نكتب وحدة Blackbox لذلك:
package ip.intel import chisel3._ import chisel3.core.{Analog, BlackBox} import freechips.rocketchip.jtag.Tristate class IOBUF extends BlackBox { val io = IO(new Bundle { val datain = Input(Bool()) val dataout = Output(Bool()) val oe = Input(Bool()) val dataio = Analog(1.W) }) override def desiredName: String = "iobuf" } object IOBUF { def apply(a: Analog, t: Tristate): IOBUF = { val res = Module(new IOBUF) res.io.datain := t.data res.io.oe := t.driven a <> res.io.dataio res } }
وصفنا Verilogue inout
النوع Analog
، وطريقة desiredName
تسمح desiredName
بتغيير اسم فئة الوحدة النمطية. هذا مهم بشكل خاص لأننا نقوم بإنشاء المجلدات وليس التطبيقات.
نحتاج أيضًا إلى BootROM - لهذا نقوم بإنشاء صيغة ROM: 1-PORT (2048 × 32 بت كلمات ، سجل العنوان فقط ، وإنشاء منفذ rden
). نقوم بإنشاء كتلة باسم rom
، لأنه بعد ذلك سيتعين علينا كتابة محول للواجهة التي ROMGenerator
فئة ROMGenerator
: ROMGenerator
بدلاً me
و oe
مفقود منا (لا يزال مرفقًا بـ 1):
BootROM.v:
module BootROM( input wire [10:0] address, input wire clock, input wire me, input wire oe, output wire [31:0] q ); rom r( .address(address), .clock(clock), .rden(me), .q(q) ); endmodule
تم اكتشاف مشكلة أخرى على الفور: الملفات السداسية التي أنشأها المجمع ، لسبب ما ، تتعارض مع Quartus. بعد ضوء googling حول موضوع ملفات Intel HEX (كانت Intel قبل وقت طويل من شراء هذا Intel Altera ، كما أفهمها) ، وصلنا إلى مثل هذا الأمر الذي يحول الملفات الثنائية إلى HEX:
srec_cat -Output builds/zeowaa-e115/xip.hex -Intel builds/zeowaa-e115/xip.bin -Binary -Output_Block_Size 128
لذلك ، سيتحول Makefile الخاص بنا قليلاً:
النص المخفي base_dir := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) BUILD_DIR := $(base_dir)/builds/zeowaa-e115 FPGA_DIR := $(base_dir)/fpga-shells/intel MODEL := FPGAChip PROJECT := sifive.freedom.zeowaa.e115 export CONFIG_PROJECT := sifive.freedom.zeowaa.e115 export CONFIG := ZeowaaConfig export BOARD := zeowaa export BOOTROM_DIR := $(base_dir)/bootrom/xip rocketchip_dir := $(base_dir)/rocket-chip sifiveblocks_dir := $(base_dir)/sifive-blocks all: verilog $(MAKE) -C $(BOOTROM_DIR) clean romgen || true srec_cat -Output $(BUILD_DIR)/xip.hex -Intel $(BUILD_DIR)/xip.bin -Binary -Output_Block_Size 128 include common.mk
يتحرك يتم تصنيع ^ W
لذلك ، يتم تجميع المشروع ككل ، والآن الشيء الأكثر إثارة للاهتمام هو تصحيح الأخطاء. يتم السماح لأول مرة القط بالدخول إلى المنزل ، وربما JTAG في متحكم. فلنقم بإنشاء نظام الحد الأدنى تقريبًا للتصحيح: BootROM للتمهيد ، و GPIO وميض المصابيح ، و JTAG لفهم لماذا يتم تحميل أي شيء وليس وميض. عن طريق القياس مع E300ArtyDevKit
بإنشاء حزمة مع أربعة ملفات فيه. أولا،
Config.scala:
class DefaultZeowaaConfig extends Config ( new WithNBreakpoints(2) ++ new WithNExtTopInterrupts(0) ++ new WithJtagDTM ++ new TinyConfig ) class Peripherals extends Config((site, here, up) => { case PeripheryGPIOKey => List( GPIOParams(address = BigInt(0x64002000L), width = 6) ) case PeripheryMaskROMKey => List( MaskROMParams(address = 0x10000, name = "BootROM")) }) class ZeowaaConfig extends Config( new Peripherals ++ new DefaultZeowaaConfig().alter((site, here, up) => { case JtagDTMKey => new JtagDTMConfig ( idcodeVersion = 2, idcodePartNum = 0xe31, idcodeManufId = 0x489, debugIdleCycles = 5) }) )
يتم نسخ وصف الجزء الأكبر من E300 في معظمه: نسأل عن ماهية تتكون النواة الخاصة بنا وأين تقع في مساحة العنوان. يرجى ملاحظة أنه على الرغم من عدم وجود ذاكرة الوصول العشوائي (هذا هو TinyConfig
الافتراضي في TinyConfig
) ، إلا أن هناك مساحة عنوان ، علاوة على ذلك ، 32 بت!
يوجد أيضًا ملف يحمل قدرًا معينًا من الصلع.
System.scala:
class System(implicit p: Parameters) extends RocketSubsystem with HasPeripheryMaskROMSlave with HasPeripheryDebug with HasPeripheryGPIO { override lazy val module = new SystemModule(this) } class SystemModule[+L <: System](_outer: L) extends RocketSubsystemModuleImp(_outer) with HasPeripheryDebugModuleImp with HasPeripheryGPIOModuleImp {
في الواقع ، يكون تخطيط "اللوحة الأم" في ثلاثة ملفات بسيطة (حتى الآن):
Platform.scala:
class PlatformIO(implicit val p: Parameters) extends Bundle { val jtag = Flipped(new JTAGIO(hasTRSTn = false)) val jtag_reset = Input(Bool()) val gpio = new GPIOPins(() => new BasePin(), p(PeripheryGPIOKey)(0)) } class Platform(implicit p: Parameters) extends Module { val sys = Module(LazyModule(new System).module) override val io = IO(new PlatformIO) val sjtag = sys.debug.systemjtag.get sjtag.reset := io.jtag_reset sjtag.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W) sjtag.jtag <> io.jtag io.gpio <> sys.gpio.head }
FPGAChip.scala:
class FPGAChip(override implicit val p: Parameters) extends ZeowaaShell { withClockAndReset(cpu_clock, cpu_rst) { val dut = Module(new Platform) dut.io.jtag.TCK := jtag_tck dut.io.jtag.TDI := jtag_tdi IOBUF(jtag_tdo, dut.io.jtag.TDO) dut.io.jtag.TMS := jtag_tms dut.io.jtag_reset := jtag_rst Seq(led_0, led_1, led_2, led_3) zip dut.io.gpio.pins foreach { case (led, pin) => led := Mux(pin.o.oe, pin.o.oval, false.B) } dut.io.gpio.pins.foreach(_.i.ival := false.B) dut.io.gpio.pins(4).i.ival := key1 dut.io.gpio.pins(5).i.ival := key2 } }
كما ترون ، يمكنك كتابة المولدات في الإزميل بأسلوب وظيفي (تظهر هنا حالة بسيطة جدًا). لكن يمكنك الكتابة بوضوح بكل سلك:
ZeowaaShell.scala object ZeowaaShell { class MemIf extends Bundle { val mem_addr = IO(Analog(14.W)) val mem_ba = IO(Analog(3.W)) val mem_cas_n = IO(Analog(1.W)) val mem_cke = IO(Analog(2.W)) val mem_clk = IO(Analog(2.W)) val mem_clk_n = IO(Analog(2.W)) val mem_cs_n = IO(Analog(2.W)) val mem_dm = IO(Analog(8.W)) val mem_dq = IO(Analog(64.W)) val mem_dqs = IO(Analog(8.W)) val mem_odt = IO(Analog(2.W)) val mem_ras_n = IO(Analog(1.W)) val mem_we_n = IO(Analog(1.W)) } } class ZeowaaShell(implicit val p: Parameters) extends RawModule { val clk25 = IO(Input(Clock())) val clk27 = IO(Input(Clock())) val clk48 = IO(Input(Clock())) val key1 = IO(Input(Bool())) val key2 = IO(Input(Bool())) val key3 = IO(Input(Bool())) val led_0 = IO(Output(Bool())) val led_1 = IO(Output(Bool())) val led_2 = IO(Output(Bool())) val led_3 = IO(Output(Bool())) val jtag_tdi = IO(Input(Bool())) val jtag_tdo = IO(Analog(1.W)) val jtag_tck = IO(Input(Clock())) val jtag_tms = IO(Input(Bool())) val uart_rx = IO(Input(Bool())) val uart_tx = IO(Analog(1.W))
لماذا يتم تقسيمها إلى ثلاثة ملفات؟ حسنًا ، أولاً ، كان الأمر كذلك في النموذج الأولي :) منطق فصل Shell
عن FPGAChip
، على ما يبدو ، هو أن Shell هي وصف للواجهة إلى العالم الخارجي: ما هي الاستنتاجات التي توصلنا إليها على لوحة محددة (وكيف سيتم عرضها على استنتاجات رقاقة!) ، و FPGAChip
تمليها حقيقة أننا نريد أن حشر في شركة نفط الجنوب محددة. حسنًا ، يتم فصل Platform بشكل منطقي تمامًا: ملاحظة: ZeowaaShell
(وبالتالي ، Platform
) عبارة عن RawModule
، على وجه الخصوص ، ليس لديهم clock
ضمنية وإعادة reset
- وهذا أمر طبيعي لـ "توصيل اللوحة" ، لكن غير مريح للعمل (وربما ، غير مناسب للعمل) محفوفة بالأخطاء الصعبة مع مجالات تردد غزيرة الإنتاج). حسنًا ، تعتبر Platform بالفعل وحدة إزميل عادية ، يمكنك فيها وصف السجلات بأمان ، إلخ.
JTAG
بضع كلمات حول كيفية تكوين JTAG. نظرًا لأنني حصلت بالفعل على RaspberryPi 3 Model B + ، كان الحل الواضح هو محاولة استخدام GPIO بطريقة أو بأخرى. لحسن الحظ ، تم تنفيذ كل شيء بالفعل أمامنا : يوجد في OpenOCD الجديد وصف interface/sysfsgpio-raspberrypi.cfg
، والذي يمكنك من خلاله إعلام مصحح الأخطاء بالاتصال عبر الكتلة (TCK = 11 و TMS = 10 و TDI = 10 و TDO = 9 و GND ترك كممارسة) - pinout هنا .
علاوة على ذلك ، مع أخذ Freedom.cfg كأساس من مستودع riscv-tests
، حصلت على ما يلي:
adapter_khz 10000 source [find interface/sysfsgpio-raspberrypi.cfg] set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20e31913 set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv init halt echo "Ready for Remote Connections"
للعمل ، ستحتاج إلى منفذ riscv-openocd المبني تحت ARM ، وبالتالي الهدية الترويجية لم تمر بدلاً من الإصدار الذي تم تجميعه مسبقًا من SiFive ، عليك استنساخ المستودع وجمع:
./configure --enable-remote-bitbang --enable-sysfsgpio
إذا كان أي شخص يعرف كيفية تشغيل bitbang عن بعد ، فقد لا تحتاج إلى إنشاء منفذ مخصص لـ ARM ...
نتيجة لذلك ، نطلق من الجذر على Malinka
root@ubuntu:~/riscv-openocd# ./src/openocd -s tcl -f ../riscv.tcl Open On-Chip Debugger 0.10.0+dev-00614-g998fed1fe-dirty (2019-06-03-10:27) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html adapter speed: 10000 kHz SysfsGPIO nums: tck = 11, tms = 25, tdi = 10, tdo = 9 SysfsGPIO nums: swclk = 11, swdio = 25 Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'. Info : SysfsGPIO JTAG/SWD bitbang driver Info : JTAG and SWD modes enabled Warn : gpio 11 is already exported Warn : gpio 25 is already exported Info : This adapter doesn't support configurable speed Info : JTAG tap: riscv.cpu tap/device found: 0x20e31913 (mfg: 0x489 (SiFive, Inc.), part: 0x0e31, ver: 0x2) Info : datacount=1 progbufsize=16 Info : Disabling abstract command reads from CSRs. Info : Examined RISC-V core; found 1 harts Info : hart 0: XLEN=32, misa=0x40001105 Info : Listening on port 3333 for gdb connections Ready for Remote Connections Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections
بعد تسجيل الدخول إلى SSH مع إعادة توجيه المنفذ 3333 ، استبدل IP المطلوب:
ssh -L 3333:127.0.0.1:3333 ubuntu@192.168.1.104
الآن على المضيف ، يمكنك تشغيل GDB تحت بنية riscv32 :
$ ../../rocket-tools/bin/riscv32-unknown-elf-gdb -q (gdb) target remote :3333 Remote debugging using :3333 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x00000000 in ?? ()
سنقوم بتخطي بعض الأخطاء العمياء في JTAG غير الشائكة نظرًا لحقيقة أن الماكرو SYNTHESIS
يستخدم بشكل نشط في الملف الرئيسي الذي تم إنشاؤه ، وسنعيد الوضع إلى الحالة "JTAG موصلة ، لكن الأضواء لا تومض."
الكود الأول
كما رأينا في Makefile ، يتم أخذ رمز bootrom من ملف bootrom/xip/xip.S
الآن يبدو مثل هذا:
// See LICENSE for license details. // Execute in place // Jump directly to XIP_TARGET_ADDR .section .text.init .option norvc .globl _start _start: csrr a0, mhartid la a1, dtb li t0, XIP_TARGET_ADDR jr t0 .section .rodata dtb: .incbin DEVICE_TREE
أدرك أنه يجب أن يكون هناك شجرة جهاز (محتويات ملف dtb) ليتم قراءتها بواسطة نظام التشغيل ، ولكن ما نوع نظام التشغيل الموجود بدون ذاكرة الوصول العشوائي. لذلك ، استبدلها بجرأة مؤقتًا بمصابيح إضاءة وامض:
النص المخفي .section .text.init .option norvc .globl _start _start: li a5, 0x64002000 li a1, 0x0F li a2, 0x01 li a3, 0x30 li a6, 0x10 li a7, 0x20 sw zero, 0x38(a5) // iof_en sw a1, 0x08(a5) // output_en sw a2, 0x00(a5) // value sw a1, 0x14(a5) // drive sw a3, 0x04(a5) // input_en // a0 <- timer // a1 <- 0x0F // a2 <- [state] // a3 <- 0x30 // a4 <- [buttons] // a5 <- [gpio addr] // a6 <- 0x10 // a7 <- 0x20 loop: li a4, 0x1000 add a0, a0, a4 bgtu a0, zero, loop lw a4, 0x00(a5) // value beq a4, a6, plus beq a4, a7, minus j store plus: srai a2, a2, 1 beq a2, zero, pzero j store pzero: li a2, 0x08 j store minus: slli a2, a2, 1 beq a2, a6, mzero j store mzero: li a2, 0x01 store: sw a2, 0x0c(a5) // value j loop
تم تطوير هذا الرمز بشكل متكرر ، لذا يرجى المعذرة على الترقيم الغريب للسجلات. بالإضافة إلى ذلك ، لقد درست بصدق أداة تجميع RISC-V باستخدام طريقة "تجميع جزء من التعليمات البرمجية في ملف كائن ، تفكيك ، انظر". عندما قرأت كتابًا للإلكترونيات قبل بضع سنوات ، تحدث عن برمجة ATTiny بلغة التجميع. "هنا هي الأشياء المملّة والروتينية ، على الأرجح" ، فكرت ، لكن الآن ، على ما يبدو ، ظهر تأثير أحد المتاجر السويدية : خزانة تم تجميع المعالج من قطع الغيار ، حتى المجمع يبدو أنه أصلي وممتع. نتيجة لتنفيذ هذا الرمز ، يجب أن "يضيء مصباح LED" يسارًا أو يمينًا ، وفقًا للزر الذي يتم الضغط عليه.
نبدأ ... ولا شيء: جميع الأنوار مضاءة ، فهي لا تستجيب للأزرار. دعونا الاتصال عبر JTAG: عداد البرنامج = 0x00000000 - بطريقة أو بأخرى كل شيء محزن. ولكن في 0x64002000
لدينا سجلات GPIO المتاحة:
لنجرب نقلهم يدويًا:
(gdb) set variable *0x64002038=0 (gdb) set variable *0x64002008=0xF (gdb) set variable *0x64002000=0x1 (gdb) set variable *0x64002014=0xF (gdb) set variable *0x6400200c=0x1
لذلك ... خرج أحد مصابيح LED ... وإذا لم يكن 0x1
، ولكن 0x5
... هذا صحيح ، الآن تضاء مصابيح LED من خلال واحد. أصبح من الواضح أيضًا أنه يجب قلبهم ، لكنك لست بحاجة إلى الكتابة للتسجيل 0x00 - تحتاج إلى القراءة من هناك.
(gdb) x/x 0x64002000 0x64002000: 0x00000030 // (gdb) x/x 0x64002000 0x64002000: 0x00000020 // (gdb) x/x 0x64002000 0x64002000: 0x00000010 // (gdb) x/x 0x64002000 0x64002000: 0x00000000
يتم تحديث سجلات رائعة معدة للذاكرة دون بدء المعالج ، ولا يمكنك الضغط على cont + Ctrl-C في كل مرة - تافهًا ، لكنه لطيف.
ولكن لماذا لا ندور في حلقة ، ولكن في $pc=0x0000000
؟
(gdb) x/10i 0x10000 0x10000: addi s1,sp,12 0x10002: fsd ft0,-242(ra) 0x10006: srli a4,a4,0x21 0x10008: addi s0,sp,32 0x1000a: slli t1,t1,0x21 0x1000c: lb zero,-1744(a2) 0x10010: nop 0x10012: addi a0,sp,416 0x10014: c.slli zero,0x0 0x10016: 0x9308
ما هو البوكيمون ؟؟ أنا لم أكتب مثل هذه التعليمات ، ألقوا بي! دعنا نلقي نظرة فاحصة:
(gdb) x/10x 0x10000 0x10000: 0xb7270064 0x9305f000 0x13061000 0x93060003 0x10010: 0x13080001 0x93080002 0x23ac0702 0x23a4b700 0x10020: 0x23a0c700 0x23aab700
من ناحية أخرى ، ماذا يجب أن يكون هناك؟
$ ../../rocket-tools/bin/riscv32-unknown-elf-objdump -d builds/zeowaa-e115/xip.elf builds/zeowaa-e115/xip.elf: elf32-littleriscv .text: 00010054 <_start>: 10054: 640027b7 lui a5,0x64002 10058: 00f00593 li a1,15 1005c: 00100613 li a2,1 10060: 03000693 li a3,48 10064: 01000813 li a6,16 10068: 02000893 li a7,32 1006c: 0207ac23 sw zero,56(a5) # 64002038 <__global_pointer$+0x63ff0770> 10070: 00b7a423 sw a1,8(a5) 10074: 00c7a023 sw a2,0(a5) 10078: 00b7aa23 sw a1,20(a5) 1007c: 00d7a223 sw a3,4(a5) ...
كما ترون ، وضعت Quartus بأمانة نفس الكلمات التي كانت في ملف التهيئة ، لكنها غيرت نهايتها. يمكنك google لفترة طويلة كيفية حلها ، لكنني مبرمج ، العكازات هي ملكنا ، لذلك سأعيد الكتابة
BootROM.v module BootROM( input wire [10:0] address, input wire clock, input wire me, input wire oe, output wire [31:0] q ); wire [31:0] q_r; rom r( .address(address), .clock(clock), .rden(me), .q(q_r) ); assign q[31:24] = q_r[7:0]; assign q[23:16] = q_r[15:8]; assign q[15:8] = q_r[23:16]; assign q[7:0] = q_r[31:24]; endmodule
لذلك ، نجمع ، نبدأ ، لا يتوهج. JTAG: $pc
- , = 4, , output_en
: set variable *0x64002008=0xF
, c
(continue) — ! , . -, , … , output_en
.
:
Total logic elements 17,370 / 114,480 ( 15 % ) Total registers 8357 Total pins 16 / 281 ( 6 % ) Total virtual pins 0 Total memory bits 264,000 / 3,981,312 ( 7 % ) Embedded Multiplier 9-bit elements 4 / 532 ( < 1 % )
Chisel
...