Bagian 1: RISC-V / RocketChip di Habitat Tidak Alami

Mengkonfigurasi RocketChip

Baru-baru ini di Habré, sebuah artikel diterbitkan tentang cara bereksperimen dengan arsitektur RISC-V tanpa biaya perangkat keras. Tetapi bagaimana jika Anda melakukan ini pada papan debug? Ingat meme tentang generator game: sekitar 20 tanda centang dalam gaya "Grafik tidak lebih buruk daripada Krisis", "Anda dapat merampok corovans" dan tombol "Hasilkan". Generator RocketChip SoC diatur kira-kira dengan cara yang sama, hanya saja tidak ada jendela dengan tanda centang, tetapi kode Scala dan sedikit assembler dan Membuat file. Pada artikel ini, saya akan menunjukkan betapa mudahnya untuk mem-porting RocketChip ini dari Xilinx asalnya ke Altera / Intel.


PENOLAKAN: penulis tidak bertanggung jawab atas papan "terbakar" - perhatikan dengan cermat bagaimana Anda mengonfigurasi pin, apa yang Anda sambungkan secara fisik, dll. Nah, perhatikan juga tindakan pencegahan keselamatan. Anda seharusnya tidak berpikir bahwa karena semuanya terhubung melalui USB, maka itu pasti aman untuk seseorang: seperti yang saya pahami dalam percobaan sebelumnya, bahkan jika Anda bekerja dengan papan USB, Anda masih tidak harus menyentuh baterai pemanas dengan kaki Anda, karena perbedaan potensial ... Oh ya Saya hampir bukan plisovode profesional atau insinyur elektronik - saya hanya seorang programmer Scala.


Sejauh yang saya mengerti, platform awal untuk debugging RocketChip adalah Xilinx FPGA. Dilihat oleh repositori, yang kami segera kloning, itu juga diangkut ke Microsemi. Di suatu tempat saya mendengar tentang penggunaan Altera, tetapi tidak melihat sumbernya. Ternyata, ini bukan masalah besar: dari saat kami menerima papan dan mulai mempelajari repositori SiFive Freedom ke "memoryless" yang berfungsi (yaitu, hanya memiliki register prosesor, BootROM dan register yang dipetakan dengan memori) 32-bit "mikrokontroler" (walaupun ini sudah apa sesuatu nanocontroller ternyata ...) 3 hari libur dan 4 hari kerja berlalu, dan itu akan menjadi lebih sedikit jika itu datang kepada saya untuk segera mendefinisikan mendefinisikan SYNTHESIS secara global.


Material


Sebagai permulaan - daftar materi dalam arti luas dari kata tersebut. Kami membutuhkan perangkat lunak berikut:


  • RocketChip - berisi prosesor itu sendiri dan lingkungan perakitan (termasuk, menjelaskan beberapa sub-repositori). Mampu membuat RocketChip dengan prosesor in-order Rocket core.
  • SiFive Freedom - mengikat untuk berbagai papan debug - di sana kami akan menambahkan dukungan untuk kami
  • rocket-tools - alat untuk membuat kode dan debugging untuk arsitektur RISC-V 32 dan 64-bit
  • openocd-riscv - port OpenOCD untuk bekerja pada JTAG dengan kernel RISC-V (setidaknya RocketChip)
  • IntelliJ IDEA Community untuk mengedit kode Scala, yang merupakan mayoritas di RocketChip
  • Toolchain pra-rakitan SiFive juga dapat berguna, tautan yang saya lihat di artikel yang telah disebutkan

Apa yang dibutuhkan dari besi:


  • Kit Zeowaa dari Aliexpress: board dengan Cyclone 4 EP4CE115 dan (clone?) USB Blaster
  • RaspberryPi sebagai JTAG debugger untuk soft core

Mesin host diasumsikan sebagai komputer yang relatif kuat dengan Ubuntu dan Quartus Lite 18 diinstal.


Sebenarnya, ada opsi untuk meluncurkan di cloud Amazon menggunakan FPGA-instance dari SiFive yang sama, yang disebut FireSim , tetapi tidak begitu menarik, Ya, dan LEDnya kurang terlihat . Selain itu, dalam hal ini, Anda perlu menentukan kunci API Anda pada contoh kontrol untuk meluncurkan mesin virtual lainnya, dan Anda harus menjaganya dengan sangat baik , jika tidak dikabarkan bahwa Anda dapat bangun suatu hari dengan hutang puluhan ribu dolar ...


Kami mempelajari situasinya


Untuk memulai, saya mengambil proyek pengujian read_write_1G dari vendor board dan mencoba menambahkan sumber yang diperlukan untuk itu. Mengapa tidak membuat yang baru? Karena saya baru, dan dalam proyek ini nama pin sudah dipetakan. Jadi, Anda perlu mengambil kode sumber dari suatu tempat. Untuk melakukan ini, kita akan memerlukan repositori freedom sudah ditentukan (jangan bingung dengan freedom-e-sdk ). Untuk mendapatkan setidaknya sesuatu, kami akan mengumpulkan rocket-tools sesuai dengan instruksi (secara harfiah meluncurkan dua skrip dan banyak menunggu), dan kemudian jalankan


 RISCV=$(pwd)/../rocket-tools make -f Makefile.e300artydevkit verilog mcs 

verilog target verilog menghasilkan file Verilog besar dengan sumber prosesor untuk kami, dan mcs mengkompilasi BootROM. Tidak perlu khawatir bahwa mcs gagal - kami hanya tidak memiliki Xilinx Vivado, sehingga BootROM yang dikompilasi tidak dapat dikonversi ke format yang dibutuhkan Vivado.


Melalui item menu Proyek Quartus -> Tambah / Hapus File dalam Proyek ... tambahkan freedom/builds/e300artydevkit/sifive.freedom.everywhere.e300artydevkit.E300ArtyDevKitConfig.v , atur entitas Tingkat atas: E300ArtyDevKitFPGapengiriman pada tab Umum dan mulailah kompilasi pada tab Umum dan mulailah kompilasi pada tab Umum dan mulailah kompilasi , daftar penyelesaian otomatis entitas tingkat atas hanya akan muncul setelah kompilasi pertama). Akibatnya, kami mendapatkan banyak kesalahan yang memberi tahu kami tentang tidak adanya modul AsyncResetReg , IOBUF , dll. Jika tidak ada kesalahan, maka Anda lupa mengubah entitas Tingkat atas. Jika Anda mencari-cari melalui sumbernya, Anda dapat langsung menemukan file AsyncResetReg.v , tetapi IOBUF adalah pengikatan inti IP Xilinx . Pertama, tambahkan freedom/rocket-chip/src/main/resources/vsrc/AsyncResetReg.v . Dan plusarg_reader.v juga akan ditambahkan.


Jalankan kompilasi dan dapatkan kesalahan lain:


 Error (10174): Verilog HDL Unsupported Feature error at plusarg_reader.v(18): system function "$value$plusargs" is not supported for synthesis 

Pada prinsipnya, konstruksi yang tidak disintesis dapat diharapkan dari file dengan nama yang mirip.


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 

Seperti yang dapat kita lihat, modul ini mungkin membaca opsi simulasi dari baris perintah, dan ketika mensintesisnya hanya mengembalikan nilai default. Masalahnya adalah bahwa proyek kami tidak mendefinisikan define bernama SYNTHESIS . Adalah mungkin untuk ifdef `define SYNTHESIS pada baris sebelumnya tepat sebelum ifdef , dan kemudian menghabiskan setengah minggu mencoba memahami mengapa kernel tidak memulai (dan setelah semua, infeksi disintesis ...). Jangan ulangi kesalahan saya, tetapi cukup buka properti proyek lagi, dan pada pengaturan Compiler-> Verilog HDL Input tab, tentukan makro SYNTHESIS , dan setidaknya 1, tidak dalam <NONE> (baris kosong).


Di sini! Sekarang Quartus bersumpah pada binding yang hilang - saatnya untuk mengatur proyek di Idea dan mulai porting.


Mengenal proyek


Kami memberi tahu gagasan Impor proyek, tentukan jalur ke repositori freedom , tunjukkan jenis proyek sbt, periksa penggunaan sbt shell untuk impor, untuk kotak centang pembuatan. Di sini, dongeng berakhir, sepertinya, tetapi tidak menemukan Ide setengah proyek - semua kode sumber ditandai dengan warna merah. Berdasarkan informasi dari sini , saya mendapatkan prosedur berikut:


  • buka jendela sbt shell
  • masukkan clean
  • tekan tombol Restart hijau di sebelah kiri
  • setelah memulai ulang sbt, kita masukkan perintah pertama ++2.12.4 , dengan demikian mengalihkan semua sub proyek ke Scala versi 2.12.4, lalu kita compile
  • klik Refresh all sbt project Idea
  • KEUNTUNGAN !, sekarang lampu latar bekerja dengan benar

Sejauh ini, saya akan mencoba untuk merakit proyek, setidaknya dalam mode semi-manual. Ngomong-ngomong, seseorang, katakan quartus_ipcreate , apakah quartus_ipcreate dalam Edisi Lite sama sekali? Kami akan membuat Variasi IP secara manual untuk saat ini, dan binding hanya akan ada di Scala dalam bentuk BlackBox.


Dalam hal ini, kami tertarik dengan hierarki direktori berikut:


 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 

Anda juga perlu menambahkan Makefile yang mirip dengan Makefile.e300artydevkit , sesuatu seperti ini:


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 

Binding


Untuk mulai dengan, kami menerapkan IOBUF ini - itu tidak akan sulit. Dilihat oleh kode Scala, ini adalah modul yang mengontrol "kaki" fisik (bola?) Dari sirkuit mikro: dapat dinyalakan, dapat dinyalakan, atau dapat dimatikan sama sekali. Di bagian kanan jendela Quartus, kita akan memasukkan "IOBUF" ke dalam Katalog IP, dan segera mendapatkan komponen bernama ALTIOBUF . Mari kita tentukan beberapa nama untuk file variasi, pilih "Sebagai buffer dua arah". Setelah itu, sebuah modul bernama iobuf akan muncul di proyek kami:


 // ... 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 // ... 

Mari kita menulis modul blackbox untuknya:


 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 } } 

Kami mendeskripsikan masukan Verilogue inout tipe Analog , dan metode desiredName yang desiredName memungkinkan desiredName untuk mengubah nama kelas modul. Ini sangat penting karena kami menghasilkan binder, bukan implementasi.


Kami juga membutuhkan BootROM - untuk ini kami membuat variasi ROM: 1-PORT (kata 2048 x 32-bit, hanya register alamat, buat port rden ). Kami membuat blok dengan nama rom , karena dengan begitu kita harus menulis sebuah adaptor untuk antarmuka yang ROMGenerator kelas ROMGenerator : ROMGenerator bukan me dan kita hilang dari kita (masih melekat pada 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 

Satu masalah lagi segera ditemukan: file hex yang dihasilkan oleh kolektor, untuk beberapa alasan, ternyata tidak sesuai dengan Quartus. Setelah googling ringan pada topik file Intel HEX (Intel jauh sebelum pembelian Intel Altera ini, seperti yang saya mengerti), kita sampai pada perintah yang mengubah file biner ke HEX:


 srec_cat -Output builds/zeowaa-e115/xip.hex -Intel builds/zeowaa-e115/xip.bin -Binary -Output_Block_Size 128 

Oleh karena itu, Makefile kami akan diubah sedikit:


Teks tersembunyi
 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 

Bergerak ^ W disintesis


Jadi, proyek secara keseluruhan disintesis, sekarang yang paling menarik adalah debugging. Kucing pertama kali diizinkan masuk ke rumah, dan mungkin JTAG ke dalam mikrokontroler. Mari kita buat sistem yang hampir minimal untuk debugging: BootROM untuk boot, GPIO ke LED berkedip, dan JTAG untuk memahami mengapa tidak ada yang memuat dan tidak berkedip. Dengan analogi dengan E300ArtyDevKit buat paket dengan empat file di dalamnya. Pertama


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) }) ) 

Deskripsi untuk sebagian besar disalin dan dipotong dari E300: kami bertanya apa kernel kami terdiri dan di mana ia akan terletak di ruang alamat. Harap dicatat bahwa meskipun kami tidak memiliki RAM (ini adalah TinyConfig default di TinyConfig ), ada ruang alamat, apalagi, 32-bit!


Ada juga file yang membawa sejumlah boilerplate.
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 { // Reset vector is set to the location of the mask rom val maskROMParams = p(PeripheryMaskROMKey) global_reset_vector := maskROMParams(0).address.U } 

Sebenarnya, tata letak "motherboard" kami ada di tiga (sejauh) file sederhana:
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 } } 

Seperti yang Anda lihat, Anda dapat menulis generator di Pahat dalam gaya fungsional (kasus yang sangat sederhana ditunjukkan di sini). Tetapi Anda dapat menulis dan dengan jelas setiap kawat:


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)) // Internal wiring val cpu_clock = Wire(Clock()) val cpu_rst = Wire(Bool()) val jtag_rst = Wire(Bool()) withClockAndReset(cpu_clock, false.B) { val counter = Reg(UInt(64.W)) counter := counter + 1.U cpu_rst := (counter > 1000.U) && (counter < 2000.U) jtag_rst := (counter > 3000.U) && (counter < 4000.U) } cpu_clock <> clk25 } 

Mengapa itu dibagi menjadi tiga file? Yah, pertama-tama, itu seperti itu dalam prototipe :) Logika memisahkan Shell dari FPGAChip , ternyata, adalah bahwa Shell adalah deskripsi dari antarmuka ke dunia luar: kesimpulan apa yang kita miliki di papan tertentu (dan bagaimana mereka akan ditampilkan pada kesimpulan chip!), dan FPGAChip ditentukan oleh fakta bahwa kami ingin menjejalkan ke dalam SoC tertentu. Nah, Platform dipisahkan cukup logis: catatan: ZeowaaShell (dan, oleh karena itu, Platform ) adalah RawModule , khususnya, mereka tidak memiliki clock implisit dan reset - ini wajar untuk "kabel papan", tetapi tidak nyaman untuk bekerja (dan, mungkin, penuh dengan kesalahan rumit dengan domain frekuensi produktif). Nah, Platform sudah menjadi modul Pahat biasa, di mana Anda dapat dengan aman menggambarkan register, dll.


Jtag


Beberapa kata tentang cara mengkonfigurasi JTAG. Karena saya sudah memiliki RaspberryPi 3 Model B +, solusi yang jelas adalah entah bagaimana mencoba menggunakan GPIO-nya. Untungnya, semuanya telah diimplementasikan di hadapan kita : di OpenOCD baru ada deskripsi interface/sysfsgpio-raspberrypi.cfg , dengan mana Anda dapat memberitahu debugger untuk terhubung melalui blok (TCK = 11, TMS = 25, TDI = 10, TDO = 9, dan GND pergi sebagai latihan) - pinout di sini .


Selanjutnya, dengan menggunakan Freedom.cfg sebagai dasar dari repositori riscv-tests , saya mendapatkan yang berikut:


 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" 

Agar berfungsi, Anda perlu port riscv-openocd yang dibangun di bawah ARM freebie tidak lulus alih-alih versi pra-rakitan dari SiFive, Anda harus mengkloning repositori dan mengumpulkan:


 ./configure --enable-remote-bitbang --enable-sysfsgpio 

Jika ada yang tahu cara menjalankan bitbang jarak jauh, maka Anda mungkin tidak perlu membangun port khusus untuk ARM ...


Sebagai hasilnya, kami meluncurkan dari root di 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 

Setelah masuk di sana pada SSH dengan port forwarding 3333, mengganti IP yang diinginkan:


 ssh -L 3333:127.0.0.1:3333 ubuntu@192.168.1.104 

Sekarang di host Anda dapat menjalankan GDB di bawah arsitektur 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 ?? () 

Kami akan melompati debugging buta dari JTAG yang tidak lengket karena fakta bahwa makro SYNTHESIS secara aktif digunakan dalam file utama yang dihasilkan, dan memundurkan situasi ke keadaan "JTAG terhubung, tetapi lampu tidak berkedip."


Kode pertama


Seperti yang kita lihat di Makefile, kode untuk bootrom diambil dari file bootrom/xip/xip.S Sekarang terlihat seperti ini:


 // 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 

Saya mengerti bahwa harus ada pohon perangkat (isi dari file dtb) untuk dibaca oleh OS, tetapi OS macam apa yang ada tanpa RAM. Oleh karena itu, gantilah untuk sementara waktu dengan lampu yang berkedip:


Teks tersembunyi
  .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 

Kode ini dikembangkan secara iteratif, jadi tolong permisi untuk penomoran register yang aneh. Selain itu, saya jujur ​​mempelajari assembler RISC-V menggunakan metode "kompilasi sepotong kode ke file objek, membongkar, lihat." Ketika saya membaca buku elektronik beberapa tahun yang lalu, ia berbicara tentang pemrograman ATTiny dalam bahasa assembly. "Ini hal-hal yang membosankan dan rutin, mungkin," pikirku, tetapi sekarang, tampaknya, efek dari satu toko Swedia telah muncul : loker Prosesor dirakit dari suku cadang, bahkan assembler tampaknya asli dan menarik. Sebagai hasil dari mengeksekusi kode ini, LED yang menyala harus “berlari” ke kiri atau ke kanan, tergantung pada tombol mana yang ditekan.


Kita mulai ... Dan tidak ada apa-apa: semua lampu menyala, mereka tidak merespons tombol. Mari terhubung melalui JTAG: penghitung program = 0x00000000 - entah bagaimana semuanya menyedihkan. Tetapi pada 0x64002000 kami memiliki register GPIO yang tersedia:


GPIOCtrlRegs.scala
 // See LICENSE for license details. package sifive.blocks.devices.gpio object GPIOCtrlRegs { val value = 0x00 val input_en = 0x04 val output_en = 0x08 val port = 0x0c val pullup_en = 0x10 val drive = 0x14 val rise_ie = 0x18 val rise_ip = 0x1c val fall_ie = 0x20 val fall_ip = 0x24 val high_ie = 0x28 val high_ip = 0x2c val low_ie = 0x30 val low_ip = 0x34 val iof_en = 0x38 val iof_sel = 0x3c val out_xor = 0x40 } 

Mari kita coba pindahkan secara manual:


 (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 

Jadi ... Salah satu LED mati ... Dan jika bukan 0x1 , tetapi 0x5 ... Itu benar, sekarang LED menyala melalui satu. Juga menjadi jelas bahwa mereka perlu dibalik, tetapi Anda tidak perlu menulis untuk mendaftar 0x00 - Anda perlu membaca dari sana.


 (gdb) x/x 0x64002000 0x64002000: 0x00000030 //    (gdb) x/x 0x64002000 0x64002000: 0x00000020 //   (gdb) x/x 0x64002000 0x64002000: 0x00000010 //   (gdb) x/x 0x64002000 0x64002000: 0x00000000 

Hebat, register yang dipetakan memori diperbarui tanpa memulai prosesor, Anda tidak dapat menekan cont + Ctrl-C setiap kali - agak, tapi bagus.


Tetapi mengapa kita tidak berputar dalam satu lingkaran, tetapi berada pada $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 

APA ITU Pokemon ??? Saya tidak menulis instruksi seperti itu, mereka melemparkan saya! Mari kita lihat lebih dekat:


 (gdb) x/10x 0x10000 0x10000: 0xb7270064 0x9305f000 0x13061000 0x93060003 0x10010: 0x13080001 0x93080002 0x23ac0702 0x23a4b700 0x10020: 0x23a0c700 0x23aab700 

Di sisi lain, apa yang seharusnya ada di sana?


 $ ../../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) ... 

Seperti yang Anda lihat, Quartus dengan jujur ​​memasukkan kata-kata yang sama yang ada di file inisialisasi, tetapi mengubah endianness mereka. Anda dapat google untuk waktu yang lama bagaimana menyelesaikannya, tetapi saya seorang programmer , kruk adalah milik kita, jadi saya hanya akan menulis ulang


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 

Jadi, kami kumpulkan, kami mulai, tidak bersinar.Kami terhubung melalui JTAG: itu dengan $pcjujur ​​menunjukkan suatu tempat di dalam siklus, topeng lampu menyala = 4, oh ya, Anda perlu mengatur output_en:, set variable *0x64002008=0xFklik c(lanjutkan) - semuanya berfungsi! Oh, dan kami sudah memadamkannya. Untuk beberapa alasan, tidak peduli bagaimana saya menyortir urutan penulisan ke register, itu tidak berhasil ... Oleh karena itu, untuk permulaan kita akan mendukungnya dengan tongkat penyangga sederhana, mentransfer pengaturan output_ensebelum menetapkan nilai aktual.


Total sumber daya yang saat ini digunakan:


 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 % ) 

Sumber tentang Pahat


Dilanjutkan ...

Source: https://habr.com/ru/post/id455391/


All Articles