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.
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 ...
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.
// 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 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:
clean
++2.12.4
, thereby switching all the subprojects to Scala version 2.12.4, then command compile
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
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:
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
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:
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.
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”.
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:
.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:
// 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
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 % )
To be continued...
Source: https://habr.com/ru/post/455391/
All Articles