📜 ⬆️ ⬇️

RISC-V: RocketChip in unnatural habitat

Configuring RocketChip

Recently on Habré an article was published on how to experiment with the architecture of RISC-V without the cost of "iron". And what if you do this on the debug board? Remember the memes about the game generator: 20 checkboxes in the style of “Graphics are not worse than Crisis”, “You can rob cows” and the “Generate” button. The RocketChip SoC generator is approximately the same, but there is not a window with checkmarks, but Scala code and some assembler and Make files. In this article I will show how easy it is to port this RocketChip from its native Xilinx to Altera / Intel.


DISCLAIMER: the author is not responsible for the “burnt” board - watch carefully how you configure pins, what you physically connect, etc. Well, also observe safety precautions. You shouldn't think that since everything is connected via USB, then it’s definitely safe for a person: as I understood in my previous experiments, even if you work with a USB card, you still shouldn’t touch the heating battery with your foot, because the potential difference ... Oh yes I am not close at all as a professional high-voltage guide or electronics engineer - I am just a Scala programmer.


As far as I understand, the initial platform for debugging RocketChip was Xilinx FPGA. Judging by the repository, which we will soon clone, it was also ported to Microsemi. About the use of Altera somewhere I heard something, but I did not see the source code. As it turned out, this is not a big problem: from the moment of receiving the board and starting to study the SiFive Freedom repository to the working "mindless" (that is, having only the processor registers, BootROM and memory-mapped registers) of a 32-bit "microcontroller" (although this is already something nanocontroller turns out ...) 3 days off and 4 week evenings, and it would take even less if it immediately came to me to define define SYNTHESIS globally.


Materials


To begin with - a list of materials in the broad sense of the word. We will need the following software:



What you need from iron:



It is assumed that the host machine is a relatively powerful computer with Ubuntu and Quartus Lite 18 installed.


Actually, there is a launch option in the Amazon cloud on their FPGA instances from the same SiFive called FireSim , but this is not so interesting, Yes, and LEDs are poorly visible . In addition, in this case, you will need to specify your API key on the managing instance to start other virtual machines, and you need to take great care of it , and then, according to rumors, you can wake up one day with a debt of ten thousand dollars ...


We study the situation


To begin with, I read_write_1G took the read_write_1G test project from the board supplier and tried to add the required sources to it. Why not create a new one? Because I'm new, and in this project the names of the pins have already been compared. So, you need to get the source from somewhere. To do this, we will need the already specified freedom repository (not to be confused with freedom-e-sdk ). To get at least something, we will assemble according to the rocket-tools instructions (literally launching two scripts and a lot of waiting), and then we will run


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

The verilog target verilog generate us a huge file on Verilog with the processor sources, and mcs compile the BootROM. Do not worry that mcs fails - we just don’t have Xilinx Vivado, so the compiled BootROM cannot be converted to the correct format for Vivado.


Through the menu item Quartus Project -> Add / Remove Files in Project ... add freedom/builds/e300artydevkit/sifive.freedom.everywhere.e300artydevkit.E300ArtyDevKitConfig.v , set the Top-level entity: E300ArtyDevKitFPGAChip on the General tab and start the compilation ( , the autocompletion list of top-level entity will appear only after the first compilation). As a result, we get tons of errors, telling us about the absence of AsyncResetReg , IOBUF , etc. modules. If there are no errors, then you forgot to change the Top-level entity. If you rummage in the source, you can directly find the file AsyncResetReg.v , but IOBUF is a binding to the IP core from Xilinx. First, let's add freedom/rocket-chip/src/main/resources/vsrc/AsyncResetReg.v . And plusarg_reader.v also add.


Run the compilation and get another error:


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

In principle, from a file with a similar name, one could expect non-synthesized constructions.


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 

As we can see, this module probably reads the simulation options from the command line, and during the synthesis simply gives the default value. The problem is that in our project there is no define defined with the name SYNTHESIS . It would be possible to ifdef right before the ifdef on the previous line `define SYNTHESIS , and then spend half a week trying to understand why the kernel does not start (and after all, the infection is synthesized ...). Do not repeat my mistakes, but simply re-open the project properties, and on the Compiler settings-> Verilog HDL Input tab, define the SYNTHESIS macro, at least in 1, not in <NONE> (empty line).


Here! Now Quartus swears at the missing bindings - it's time to set up the project in Idea and start porting.


We get acquainted with the project


We speak to the Idea Import project, indicate the path to the freedom repository, indicate the type of project sbt, tick off use sbt shell for imports, for builds. Here and the fairy-tale end, it would seem, but here the idea of ​​a half-project does not find - all the sources are cleaned in red. Based on the information from here , I got the following procedure:



For now, I will try to somehow build a project, at least in semi manual mode. By the way, somebody, tell quartus_ipcreate is there a quartus_ipcreate in Lite Edition? We will create IP Variations for the time being manually, and the bindings will be only on Scala in the form of a BlackBox.


In this case, we are interested in the following directory hierarchy:


 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 

You also need to add a Makefile by analogy with Makefile.e300artydevkit , like this:


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


To begin with, we will implement this IOBUF - it will hardly be difficult. Judging by the Scala code, this is the module that controls the physical “leg” (ball?) Of the microcircuit: it can be turned on at the input, it can be on the exit, or it can be turned off altogether. On the right side of the Quartus window, enter the “IOBUF” into the IP Catalog, and immediately get a component named ALTIOBUF . Give some name for the variation file, select “As a bidirectional buffer”. After that, we will have a module in the project with the name 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 // ... 

Let's write a blackbox module for it:


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

Using the Analog type, we describe the Verilog inout , while the desiredName method allows changing the module's class name. This is especially important because we are generating a binding, not an implementation.


We also need the BootROM - for this we create a ROM variation : 1-PORT (2048 x 32-bit words, register address only, create the rden port). We create a block with the name rom , because then we have to write an adapter to the interface that expects the ROMGenerator class: ROMGenerator instead of me and oe missing (it is still tied to 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 

Immediately, another problem is discovered: the hex files generated by the collector are for some reason incompatible with Quartus. After a light googling on the subject of Intel HEX files (it was Intel long before it was purchased by Intel itself, as I understand it), we arrive at such a command converting binary files into HEX:


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

Therefore, our Makefile will change a bit:


Hidden text
 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 

It moves ^ W is synthesized


So, the project as a whole is synthesized, now the most interesting thing is debugging. A cat is allowed into the house first, and a JTAG, perhaps, into the microcontroller. Let's create an almost minimal debugging system: BootROM to boot, GPIO to blink LEDs, and JTAG to understand why it isn’t loading anything and not blinking. By analogy with E300ArtyDevKit we will create a package with four files in it. First of all,


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

The description is mostly copied and truncated from E300: we ask what our core consists of and where it will lie in the address space. Please note that although we do not have RAM (as the default is set in TinyConfig ), the address space is, moreover, 32-bit!


There is also a file that carries some amount of 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 } 

Actually, the layout of our "motherboard" is in three (for now) simple files:
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 } } 

As you can see, on Chisel you can write generators in a functional style (a very simple case is shown here). But you can write and clearly each wire:


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 } 

Why is it split into three files? Well, firstly, it was so in the prototype :) The logic of separating Shell from FPGAChip , apparently, was that Shell is a description of the interface to the outside world: what conclusions we have on a specific board (and how they will be displayed on the conclusions of the chip!), and FPGAChip is dictated by the fact that we want to cram into a particular SoC. Well, the Platform is quite logical: note: ZeowaaShell (and therefore Platform ) is RawModule , in particular, they do not have an implicit clock and reset - this is natural for “wiring the board”, but inconvenient for work (and, probably, fraught with cunning errors with proliferated frequency domains). Well, the Platform is already the usual Chisel module, in which you can safely describe registers, etc.


JTAG


A few words on how to configure JTAG. Since I already had a RaspberryPi 3 Model B +, the obvious solution was to somehow try to use its GPIO. Fortunately, everything is already implemented to us : in the fresh OpenOCD there is a description of the interface/sysfsgpio-raspberrypi.cfg , with which you can tell the debugger to connect via the pad (TCK = 11, TMS = 25, TDI = 10, TDO = 9, and GND leave as an exercise) - pinout here .


Next, based on the Freedom.cfg from the riscv-tests repository, I got the following:


 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" 

To work, you will need port riscv-openocd , compiled under ARM, therefore the freebie failed instead of the pre-assembled version from SiFive, you will have to clone the repository and compile:


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

If anyone knows how to run remote bitbang, then it may not be necessary to build a custom port for ARM ...


As a result, we run from the root on 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 

Pre-going there via SSH with probrosy port 3333, substituting the desired IP:


 ssh -L 3333:127.0.0.1:3333 ubuntu@192.168.1.104 

Now you can run GDB on the host under the riscv32 architecture :


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

Let us skip some amount of blindly debugging JTAG due to the fact that the SYNTHESIS macro is actively used in the main generated file and rewind the situation to the state “JTAG is hooked up, but the lights are not flashing”.


First code


As we already saw in the Makefile, the code for the bootrom is taken from the bootrom/xip/xip.S . Now it looks like this:


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

I understand that there must be a device tree (the contents of the dtb file) to be read by the OS, but what kind of OS is there without RAM. So feel free to temporarily replace it with a flasher with light bulbs:


Hidden text
  .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 

This code developed iteratively, so please excuse me for the strange numbering of registers. In addition, I honestly studied the assembler RISC-V using the method “compile a piece of code into an object file, disassemble, see”. When I read a book on electronics a few years ago, they talked about programming ATTiny in assembler. “This is boredom and routine, probably,” I thought, but now, apparently, the effect of a Swedish store has manifested itself: locker the processor, independently assembled from spare parts, even the assembler seems native and interesting. As a result of executing this code, the lit LED should “run” left or right, depending on which button is pressed.


We start ... And nothing: all the lights are on, they do not react to the buttons. Connect via JTAG: program counter = 0x00000000 - somehow everything is sad. But at the address 0x64002000 we have GPIO registers available:


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 } 

Let's try to move them manually:


 (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 

Taak ... One of the LEDs went out ... And if not 0x1 , but 0x5 ... That's right, now the LEDs are on after one. It also became clear that they need to be inverted, and there is no need to write to the register 0x00 - from there you need to read.


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

Great, memory-mapped registers are updated without starting the processor, you can not press cont + Ctrl-C every time - a trifle, but nice.


But why are we not spinning in a cycle, but standing at $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 

THIS IS WHAT POKEMON ??? I did not write such instructions, I planted! Let's take a closer look:


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

On the other hand, what should we lay there?


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

As you can see, Quartus honestly put the same words that were in the initialization file, but changed their endianness. You can google for a long time how to solve it, but I'm a programmer , crutches are our everything, so I’ll just rewrite


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 

So, we collect, we start, does not shine. 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


To be continued...


')

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


All Articles