Browse Source

added MyMem template

master
Thorsten Knoll 4 years ago
parent
commit
b9cccf90a0
9 changed files with 871 additions and 0 deletions
  1. +35
    -0
      vexriscv/.gitignore
  2. +201
    -0
      vexriscv/README.md
  3. +20
    -0
      vexriscv/build.sbt
  4. +1
    -0
      vexriscv/project/build.properties
  5. +119
    -0
      vexriscv/src/main/scala/mupq/MulPlugins.scala
  6. +35
    -0
      vexriscv/src/main/scala/mupq/MyMem.scala
  7. +278
    -0
      vexriscv/src/main/scala/mupq/PQVexRiscv.scala
  8. +174
    -0
      vexriscv/src/main/scala/mupq/PQVexRiscvSim.scala
  9. +8
    -0
      vexriscv/vexriscvsim.cfg

+ 35
- 0
vexriscv/.gitignore View File

@ -0,0 +1,35 @@
# ---> SBT
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
.ensime
.idea
# ---> Scala
*.class
*.log
.metals
# ---> Spinal
simWorkspace/
tmp/
# ---> Project
rtl/.Xil
rtl/*.log
rtl/*.dcp
rtl/*.time
rtl/*.json
rtl/*.bit
rtl/*.v
cpu0.yaml
*.bin

+ 201
- 0
vexriscv/README.md View File

@ -0,0 +1,201 @@
# About the source of this VexRiscv-Simulation
This is a stipped down version of the pqriscv resporitory:
https://github.com/mupq/pqriscv
The original README of this repository starts below and might not fit for all functionality anymore.
You might want to read the README in the root folder of this repo instead.
Modifications: Thorsten Knoll, Oct 2020
# PQVexRiscV
[VexRiscv](https://github.com/SpinalHDL/VexRiscv) based Target platforms
for the [pqriscv](https://github.com/mupq/pqriscv) project
## Introduction
The goal of this project is to implement a simple test-platform for the
VexRiscv CPU, to be used as a reference platform for benchmarking and
experimenting with PQC scheme implementations.
## Setup
You'll need the following
* Java JDK **==** 1.8
* [SBT](https://www.scala-sbt.org) >= 1.2.8, the Scala Build Tool
* (For iCE40 based FPGAs) [Icestorm FPGA Toolchain](http://www.clifford.at/icestorm/), including [NextPNR](https://github.com/YosysHQ/nextpnr)
* (For Xilinx based FPGAs) Vivado ~= 2018.3 (probably also works with older and newer versions)
* (For the Simulator) [Verilator](https://www.veripool.org/wiki/verilator)
* (Optionally for Debugging) [OpenOCD for VexRiscv](https://github.com/SpinalHDL/openocd_riscv)
## Synthesis
This project (and VexRiscv) uses the SpinalHDL language, which is
essentially a DSL for hardware design on top of the Scala language.
The Scala sources will then generate equivalent Verilog (or VHDL) of
circuits described in SpinalHDL.
So to create a bitstream for a FPGA, you'll have to generate the verilog
file first and then run synthesis.
Just run `sbt` in the project root and run any of the main classes to
generate the verilog source for one of the targets.
The `rtl` folder contains a Makefile to generate the bitstream for the
FPGAs (the Makefile will also run `sbt` to generate the Verilog).
For example:
```sh
make -C rtl TARGETS=PQVexRiscvUP5K
```
## Simulation
Simulating a VexRiscv core is also possible.
```sh
sbt "runMain mupq.PQVexRiscvSim --init initfile.bin --ram 256,128 --uart uartoutput.txt"
```
Adapt the options to your liking. The `--init` option points to a binary
file which is loaded into the simulated RAM as initialization. Remove
the option to leave the RAM blank, you can also load your binary via
OpenOCD+GDB.
The `--ram` option determines the memory architecture. The
simulator will add a X KiB sized block of RAM to the memory architecture
for each integer. Default is two blocks of ram, one for meant for code,
one for data. The RAM blocks start at address `0x80000000` of the
VexRiscv core and are placed back-to-back.
The `--uart` block may point to a file to which the simulated UART
output of the core is appended. When this option is skipped, UART is
redirected to stdout.
## OpenOCD for VexRiscv
All boards (including the simulator) support debugging via a JTAG port.
For that you'll need a suitable debugging adapter (anything FT2232
should do) and [OpenOCD port for VexRiscv](https://github.com/SpinalHDL/openocd_riscv).
You can use the following to compile a stripped down version of the
tool, which also adds the suffix `-vexriscv` to the program name, as
well as placing all the data into a different dir, so the installation
won't clash with any other OpenOCD version on your system. Adapt the
prefix/datarootdir to your taste.
```sh
./bootstrap
./configure --prefix=/usr/local --program-suffix=-vexriscv \
--datarootdir=/usr/local/share/vexriscv --enable-maintainer-mode \
--disable-werror --enable-ft232r --enable-ftdi --enable-jtag_vpi \
--disable-aice --disable-amtjtagaccel --disable-armjtagew \
--disable-assert --disable-at91rm9200 --disable-bcm2835gpio \
--disable-buspira --disable-cmsis-dap --disable-doxygen-html \
--disable-doxygen-pdf --disable-dummy --disable-ep93xx \
--disable-gw16012 --disable-imx_gpio --disable-jlink \
--disable-kitprog --disable-minidriver-dummy --disable-oocd_trace \
--disable-opendous --disable-openjtag --disable-osbdm \
--disable-parport --disable-parport-giveio --disable-parport-ppdev \
--disable-presto --disable-remote-bitbang --disable-rlink \
--disable-stlink --disable-sysfsgpio --disable-ti-icdi \
--disable-ulink --disable-usb-blaster --disable-usb-blaster-2 \
--disable-usbprog --disable-verbose-jtag-io \
--disable-verbose-usb-comms --disable-verbose-usb-io \
--disable-vsllink --disable-xds110 --disable-zy1000 \
--disable-zy1000-master
make
# sudo make install
```
Some templates for connecting OpenOCD and a Debug adapter to the boards
are at the ready in the project root. For example:
```sh
openocd-vexriscv -f pqvexriscvsim.cfg
```
Since OpenOCD opens up a gdbserver, you can debug on your target with
GDB. For example:
```sh
riscv64-unknown-elf-gdb -ex 'set remotetimeout 10' -ex 'target remote :3333' -ex load -ex 'break main' my_awesome_program.elf
```
## FPGA Platforms
This project will support multiple FPGA targets in future:
* [iCE40 UltraPlus 5K](https://www.latticesemi.com/en/Products/FPGAandCPLD/iCE40UltraPlus) as, for example, present in the [iCE40 UltraPlus Breakout Board](https://www.latticesemi.com/en/Products/DevelopmentBoardsAndKits/iCE40UltraPlusBreakoutBoard)
* WIP: [Icoboard](http://icoboard.org/), which in turn uses the [iCE40 HX 8K](https://www.latticesemi.com/Products/FPGAandCPLD/iCE40)
* WIP: [Xilinx 7 Series](https://www.xilinx.com/products/silicon-devices/fpga.html), with an example file for the [Digilent Arty A7 board](https://store.digilentinc.com/arty-a7-artix-7-fpga-development-board-for-makers-and-hobbyists/)
### iCE40 UltraPlus 5k
A basic design with the default VexRiscv plugins should fit on the iCE40
UP5K.
As the UP5K features 8 DSP blocks, a non-blocking multiplier will fit
comfortably.
Furthermore, four 32kb sized SRAM blocks are present on the FPGA, which
are used to implement two separate 64kb large blocks on the memory bus.
You should link your code to the lower half (starting `0x80000000`), and
stack / data to the upper half (starting `0x80010000`).
The `rtl` folder contains a constraint file to place the design on the
UltraPlus Breakout Board (though other boards should work fine, as long
as the use the UP5K, just adapt the PCF as necessary).
It exposes the JTAG and UART pins to IO ports located on header B as
follows:
| Function | Pin |
|------------|---------|
| JTAG `TDO` | iob_23b |
| JTAG `TCK` | iob_25b |
| JTAG `TDI` | iob_24a |
| JTAG `TMS` | iob_29b |
| UART `TXD` | iob_8a |
| UART `RXD` | iob_9b |
You can use any FTDI FT2232H or FT232H based debugger probe together
with `openocd-vexriscv`, just adapt the connection script
`PQVexRiscvUP5K.cfg` accordingly.
Tip: You could use the
[FT_PROG](https://www.ftdichip.com/Support/Utilities.htm#FT_PROG) to
change the serial number of a generic FT232H breakout board [(e,g, the
Adafruit FT232H Breakout board)](https://www.adafruit.com/product/2264).
Add a line `ftdi_serial "XXX"` to the connection script, and `openocd`
will automatically pick the right FTDI.
Another well known FTDI based probe is the [Olimex
ARM-USB-TINY-H](https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/)
(see the example `openocd-vexriscv` connection script for the Icoboard
`pqvexriscvicoboard.cfg`).
### Icoboarda (WIP)
This FPGA target is still a heavy work in progress.
The FPGA itself has more LUTs than the UP5K, however, doesn't feature
any DSPs.
Thus, the design will likely use a smaller multi-cycle multiplier to
implement the multiplication instruction.
The large SRAM blocks of the UP5K are also missing, however, the
Icoboard offers a 8MBit large SRAM chip, which will be used for
code **and** data.
### Xilinx 7 Series
The Digilent Arty A7 board uses the Xilinx Artix-7 XC7A35T (or XC7A100T
depending on the model), which will comfortably fit even larger variants
of the VexRiscv core).
The Arty board also exists in other variants using the Spartan-7 series
chip, which should also be large enough for the VexRiscv.
Similarly to the UP5K, the default design will use two separate 64kb
blocks for code and data each.
On the Arty Boards, the UART is exposed on the shared FPGA configuration
and UART USB port of the board.
The JTAG (for the VexRiscv core, **not** the JTAG for the FPGA chip) is
exposed on the PMOD JD connector.
| Function | Pin (PMOD JD) |
|----------|---------------|
| `TDO` | 1 |
| `TCK` | 3 |
| `TDI` | 7 |
| `TMS` | 8 |
If you use the Olimex ARM-USB-TINY-H, you'll also need to connect the
VREF.
Note, that the pinout is exactly the same as the [SiFive Freedom E310
Arty FPGA Dev Kit](https://github.com/sifive/freedom), except that
the `nRST`, `nTRST` remain unused.
## Internals
*TODO*: Write up some infos on the platforms, what features of VexRiscv
are enabled, how the memory architecture works, ...

+ 20
- 0
vexriscv/build.sbt View File

@ -0,0 +1,20 @@
ThisBuild / organization := "mupq"
ThisBuild / scalaVersion := "2.11.12"
lazy val pqvexriscv = (project in file("."))
.settings(
name := "pqvexriscv",
version := "0.1",
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.0.5" % "test",
compilerPlugin("com.github.spinalhdl" % "spinalhdl-idsl-plugin_2.11" % "1.4.0")
),
run / connectInput := true,
outputStrategy := Some(StdoutOutput),
).dependsOn(vexRiscv)
lazy val vexRiscv = RootProject(uri("git://github.com/SpinalHDL/VexRiscv#2942d0652a89646c5225bee15dd55cc3b0871766"))
fork := true

+ 1
- 0
vexriscv/project/build.properties View File

@ -0,0 +1 @@
sbt.version=1.3.13

+ 119
- 0
vexriscv/src/main/scala/mupq/MulPlugins.scala View File

@ -0,0 +1,119 @@
package mupq
import vexriscv._
import vexriscv.plugin._
import spinal.core._
/**
* A multiplication plugin using only 16-bit multiplications
*/
class Mul16Plugin extends Plugin[VexRiscv]{
object MUL_LL extends Stageable(UInt(32 bits))
object MUL_LH extends Stageable(UInt(32 bits))
object MUL_HL extends Stageable(UInt(32 bits))
object MUL_HH extends Stageable(UInt(32 bits))
object MUL extends Stageable(Bits(64 bits))
object IS_MUL extends Stageable(Bool)
override def setup(pipeline: VexRiscv): Unit = {
import Riscv._
import pipeline.config._
val actions = List[(Stageable[_ <: BaseType],Any)](
SRC1_CTRL -> Src1CtrlEnum.RS,
SRC2_CTRL -> Src2CtrlEnum.RS,
REGFILE_WRITE_VALID -> True,
BYPASSABLE_EXECUTE_STAGE -> False,
BYPASSABLE_MEMORY_STAGE -> False,
RS1_USE -> True,
RS2_USE -> True,
IS_MUL -> True
)
val decoderService = pipeline.service(classOf[DecoderService])
decoderService.addDefault(IS_MUL, False)
decoderService.add(List(
MULX -> actions
))
}
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
// Prepare signed inputs for the multiplier in the next stage.
// This will map them best to an FPGA DSP.
execute plug new Area {
import execute._
val a,b = Bits(32 bit)
a := input(SRC1)
b := input(SRC2)
val aLow = a(15 downto 0).asUInt
val bLow = b(15 downto 0).asUInt
val aHigh = a(31 downto 16).asUInt
val bHigh = b(31 downto 16).asUInt
insert(MUL_LL) := aLow * bLow
insert(MUL_LH) := aLow * bHigh
insert(MUL_HL) := aHigh * bLow
insert(MUL_HH) := aHigh * bHigh
}
memory plug new Area {
import memory._
val ll = UInt(32 bits)
val lh = UInt(33 bits)
val hl = UInt(32 bits)
val hh = UInt(32 bits)
ll := input(MUL_LL)
lh := input(MUL_LH).resized
hl := input(MUL_HL)
hh := input(MUL_HH)
val hllh = lh + hl
insert(MUL) := ((hh ## ll(31 downto 16)).asUInt + hllh) ## ll(15 downto 0)
}
writeBack plug new Area {
import writeBack._
val aSigned,bSigned = Bool
switch(input(INSTRUCTION)(13 downto 12)) {
is(B"01") {
aSigned := True
bSigned := True
}
is(B"10") {
aSigned := True
bSigned := False
}
default {
aSigned := False
bSigned := False
}
}
val a = (aSigned && input(SRC1).msb) ? input(SRC2).asUInt | U(0)
val b = (bSigned && input(SRC2).msb) ? input(SRC1).asUInt | U(0)
when(arbitration.isValid && input(IS_MUL)){
switch(input(INSTRUCTION)(13 downto 12)){
is(B"00"){
output(REGFILE_WRITE_DATA) := input(MUL)(31 downto 0)
}
is(B"01",B"10",B"11"){
output(REGFILE_WRITE_DATA) := (((input(MUL)(63 downto 32)).asUInt + ~a) + (~b + 2)).asBits
}
}
}
}
}
}

+ 35
- 0
vexriscv/src/main/scala/mupq/MyMem.scala View File

@ -0,0 +1,35 @@
package mupq
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb._
class MyMem() extends Component {
val io = new Bundle {
val bus = slave(Apb3(Apb3Config(addressWidth = 20, dataWidth = 32)))
val interrupt = out Bool
}
/**
val coproc = new KeccakCoproMultimemWrapper(dataWidth, keccakN)
**/
val busReady = Reg(Bool) init(false)
val busAccess = io.bus.PENABLE && io.bus.PSEL(0)
io.bus.PRDATA := 0
busReady := busAccess && !busReady
io.bus.PREADY := busReady
io.interrupt := False
/**
when(busReady) {
when(io.bus.PWRITE) {
start := io.bus.PWDATA(0) // Set flags on write, will trigger the coprocessor
interruptEn := io.bus.PWDATA(2)
} otherwise {
io.bus.PRDATA(0) := busy // Assemble flags for read
io.bus.PRDATA(1) := interrupt
io.bus.PRDATA(2) := interruptEn
interrupt := False
}
}
**/
}

+ 278
- 0
vexriscv/src/main/scala/mupq/PQVexRiscv.scala View File

@ -0,0 +1,278 @@
package mupq
import scala.collection.mutable.ArrayBuffer
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.misc._
import spinal.lib.bus.simple._
import spinal.lib.io._
import spinal.lib.com.jtag._
import spinal.lib.com.uart._
import vexriscv._
import vexriscv.demo.MuraxApb3Timer
import vexriscv.plugin._
abstract class PQVexRiscv(
cpuPlugins : () => Seq[Plugin[VexRiscv]],
ibusRange : SizeMapping,
genUART : Boolean = true,
gpioWidth : Int = 0,
genTimer : Boolean = false
) extends Component {
val coreFrequency : HertzNumber
/* Clock and resets */
val asyncReset: Bool = Bool
val mainClock: Bool = Bool
val resetCtrlClockDomain: ClockDomain = ClockDomain(
clock = mainClock,
config = ClockDomainConfig(resetKind = BOOT))
val resetCtrl = new ClockingArea(resetCtrlClockDomain) {
val bufferedReset = BufferCC(asyncReset)
val mainClockReset = RegNext(bufferedReset)
val systemClockReset = RegNext(bufferedReset)
}
val systemClockDomain: ClockDomain = ClockDomain(
clock = mainClock,
reset = resetCtrl.systemClockReset,
frequency = FixedFrequency(coreFrequency))
val debugClockDomain: ClockDomain = ClockDomain(
clock = mainClock,
reset = resetCtrl.mainClockReset,
frequency = FixedFrequency(coreFrequency))
/* Bus interconnect */
val busConfig = PipelinedMemoryBusConfig(
addressWidth = 32,
dataWidth = 32
)
val busSlaves = ArrayBuffer[(PipelinedMemoryBus, SizeMapping)]()
val busMasters = ArrayBuffer[(PipelinedMemoryBus, SizeMapping)]()
/* VexRiscv Core */
var jtag : Jtag = null
val core = new ClockingArea(systemClockDomain) {
val timerInterrupt = False
val externalInterrupt = False
val config = VexRiscvConfig(
plugins = cpuPlugins() ++ Seq(new DebugPlugin(debugClockDomain, 3)))
val cpu = new VexRiscv(config)
/* Wire the Busses / Lines to the plugins */
var ibus : PipelinedMemoryBus = PipelinedMemoryBus(busConfig)
var dbus : PipelinedMemoryBus = PipelinedMemoryBus(busConfig)
for (plugin <- cpu.plugins) plugin match {
case plugin: IBusSimplePlugin =>
val cpuibus = plugin.iBus.toPipelinedMemoryBus()
ibus.cmd <-/< cpuibus.cmd
ibus.rsp >> cpuibus.rsp
case plugin: DBusSimplePlugin =>
val cpudbus = plugin.dBus.toPipelinedMemoryBus()
dbus.cmd <-/< cpudbus.cmd
dbus.rsp >> cpudbus.rsp
plugin.dBus.rsp.error := False
case plugin: CsrPlugin =>
plugin.externalInterrupt := externalInterrupt
plugin.timerInterrupt := timerInterrupt
case plugin: DebugPlugin =>
plugin.debugClockDomain {
resetCtrl.systemClockReset setWhen (RegNext(plugin.io.resetOut))
jtag = plugin.io.bus.fromJtag()
}
case _ =>
}
busMasters += dbus -> SizeMapping(0l, (1l << 32l))
busMasters += ibus -> ibusRange
}
/* Peripherals */
var gpio : TriStateArray = null
var uart : Uart = null
val peripherals = new ClockingArea(systemClockDomain) {
if (gpioWidth > 0) {
gpio = TriStateArray(gpioWidth bits)
}
if (genUART) {
uart = Uart()
}
if(genUART || gpioWidth > 0 || genTimer) {
val apbBridge = new PipelinedMemoryBusToApbBridge(
apb3Config = Apb3Config(
addressWidth = 20,
dataWidth = 32
),
pipelineBridge = false,
pipelinedMemoryBusConfig = busConfig
)
busSlaves += apbBridge.io.pipelinedMemoryBus -> SizeMapping(0xF0000000l, 1 MiB)
val apbMapping = ArrayBuffer[(Apb3, SizeMapping)]()
if (gpioWidth > 0) {
val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth, withReadSync = true)
gpio <> gpioACtrl.io.gpio
apbMapping += gpioACtrl.io.apb -> (0x00000, 4 KiB)
}
if (genUART) {
val uartCtrlConfig = UartCtrlMemoryMappedConfig(
uartCtrlConfig = UartCtrlGenerics(
dataWidthMax = 8,
clockDividerWidth = 20,
preSamplingSize = 1,
samplingSize = 3,
postSamplingSize = 1
),
initConfig = UartCtrlInitConfig(
baudrate = 115200,
dataLength = 7, //7 => 8 bits
parity = UartParityType.NONE,
stop = UartStopType.ONE
),
busCanWriteClockDividerConfig = false,
busCanWriteFrameConfig = false,
txFifoDepth = 16,
rxFifoDepth = 16
)
val uartCtrl = Apb3UartCtrl(uartCtrlConfig)
uart <> uartCtrl.io.uart
core.externalInterrupt setWhen(uartCtrl.io.interrupt)
apbMapping += uartCtrl.io.apb -> (0x10000, 4 KiB)
}
if (genTimer) {
val timer = new MuraxApb3Timer()
core.timerInterrupt setWhen(timer.io.interrupt)
apbMapping += timer.io.apb -> (0x20000, 4 KiB)
}
val myMem = new MyMem()
core.timerInterrupt setWhen(myMem.io.interrupt)
apbMapping += myMem.io.bus -> (0x30000, 4 KiB)
val apbDecoder = Apb3Decoder(
master = apbBridge.io.apb,
slaves = apbMapping
)
}
}
def buildInterconnect() : Unit = {
assert(!SizeMapping.verifyOverlapping(busSlaves.map(_._2)))
val crossbar = new ClockingArea(systemClockDomain) {
val interconnect = new PipelinedMemoryBusInterconnect()
interconnect.perfConfig()
/* Setup the interconnect */
interconnect.addSlaves(busSlaves: _*)
/* Check which masters overlap with which slaves */
def overlaps(a : SizeMapping, b : SizeMapping) : Boolean = if (a.base < b.base) a.end >= b.base else b.end >= a.base
interconnect.addMasters(busMasters.map(m => m._1 -> busSlaves.filter(s => overlaps(m._2, s._2)).map(s => s._1).toSeq): _*)
}
}
Component.current.addPrePopTask(() => buildInterconnect())
}
object PQVexRiscv
{
type PluginSeq = Seq[Plugin[VexRiscv]]
type PluginGen = () => PluginSeq
/** Basic set of Plugins (conforms mostly to rv32i) */
def baseConfig(base: PluginGen = () => Seq()) = () => base() ++ Seq(
new IBusSimplePlugin(
resetVector = 0x80000000l,
cmdForkOnSecondStage = true,
cmdForkPersistence = false,
prediction = NONE,
catchAccessFault = false,
compressedGen = false
),
new DBusSimplePlugin(
catchAddressMisaligned = false,
catchAccessFault = false,
earlyInjection = false
),
new CsrPlugin(
CsrPluginConfig.smallest(0x80000000l).copy(
mtvecAccess = CsrAccess.READ_WRITE,
mcycleAccess = CsrAccess.READ_ONLY,
minstretAccess = CsrAccess.READ_ONLY
)
),
new DecoderSimplePlugin(
catchIllegalInstruction = false
),
new RegFilePlugin(
regFileReadyKind = plugin.SYNC,
zeroBoot = false
),
new IntAluPlugin,
new SrcPlugin(
separatedAddSub = false,
executeInsertion = false
),
new FullBarrelShifterPlugin,
new HazardSimplePlugin(
bypassExecute = true,
bypassMemory = true,
bypassWriteBack = true,
bypassWriteBackBuffer = true,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false
),
new BranchPlugin(
earlyBranch = false,
catchAddressMisaligned = false
),
new YamlPlugin("cpu0.yaml")
)
/** Plugins for a small multiplier */
def smallMultiplier = Seq(
new MulDivIterativePlugin(
genMul = true,
genDiv = true,
mulUnrollFactor = 1,
divUnrollFactor = 1
)
)
/** Config with a small multiplier */
def withSmallMultiplier(base: PluginGen = baseConfig()) = () => base() ++ smallMultiplier
/** Plugins for a multiplier for FPGAs */
def dspMultiplier = Seq(
new Mul16Plugin,
new MulDivIterativePlugin(
genMul = false,
genDiv = true,
divUnrollFactor = 1
)
)
/** Config with a multiplier for FPGAs */
def withDSPMultiplier(base: PluginGen = baseConfig()) = () => base() ++ dspMultiplier
}

+ 174
- 0
vexriscv/src/main/scala/mupq/PQVexRiscvSim.scala View File

@ -0,0 +1,174 @@
package mupq
import java.io.{File, FileInputStream, FileOutputStream, IOException, OutputStream}
import scopt.OptionParser
import spinal.sim._
import spinal.core._
import spinal.lib._
import spinal.core.sim._
import spinal.lib.bus.simple._
import spinal.lib.bus.misc.SizeMapping
import spinal.lib.io.TriStateArray
import spinal.lib.com.jtag.Jtag
import spinal.lib.com.uart.Uart
import spinal.lib.com.jtag.sim.JtagTcp
import vexriscv.VexRiscv
import vexriscv.plugin.Plugin
case class PipelinedMemoryBusRam(size : BigInt, initialContent : File = null) extends Component{
require(size % 4 == 0, "Size must be multiple of 4 bytes")
require(size > 0, "Size must be greater than zero")
val busConfig = PipelinedMemoryBusConfig(log2Up(size), 32)
val io = new Bundle{
val bus = slave(PipelinedMemoryBus(busConfig))
}
val ram = Mem(Bits(32 bits), size / 4)
io.bus.rsp.valid := RegNext(io.bus.cmd.fire && !io.bus.cmd.write) init(False)
io.bus.rsp.data := ram.readWriteSync(
address = io.bus.cmd.address >> 2,
data = io.bus.cmd.data,
enable = io.bus.cmd.valid,
write = io.bus.cmd.write,
mask = io.bus.cmd.mask
)
io.bus.cmd.ready := True
if (initialContent != null) {
val input = new FileInputStream(initialContent)
val initContent = Array.fill[BigInt](ram.wordCount)(0)
val fileContent = Array.ofDim[Byte](Seq(input.available, initContent.length * 4).min)
input.read(fileContent)
for ((byte, addr) <- fileContent.zipWithIndex) {
val l = java.lang.Byte.toUnsignedLong(byte) << ((addr & 3) * 8)
initContent(addr >> 2) |= BigInt(l)
}
ram.initBigInt(initContent)
}
}
class PQVexRiscvSim(
val ramBlockSizes : Seq[BigInt] = Seq[BigInt](256 KiB, 128 KiB),
val initialContent : File = null,
val coreFrequency : HertzNumber = 12 MHz,
cpuPlugins : () => Seq[Plugin[VexRiscv]] = PQVexRiscv.withDSPMultiplier()
) extends PQVexRiscv(
cpuPlugins = cpuPlugins,
ibusRange = SizeMapping(0x80000000l, ramBlockSizes.reduce(_ + _))
) {
val io = new Bundle {
val asyncReset = in Bool
val mainClock = in Bool
val uart = master(Uart())
val jtag = slave(Jtag())
}
asyncReset := io.asyncReset
mainClock := io.mainClock
uart <> io.uart
jtag <> io.jtag
val memory = new ClockingArea(systemClockDomain) {
val ramBlocks = ramBlockSizes.zipWithIndex.map(t => PipelinedMemoryBusRam(t._1, if (t._2 == 0) initialContent else null))
var curAddr : BigInt = 0x80000000l
for (block <- ramBlocks) {
busSlaves += block.io.bus -> SizeMapping(curAddr, block.size)
curAddr += block.size
}
}
}
object PQVexRiscvSim {
def main(args: Array[String]) = {
case class PQVexRiscvSimConfig(
uartOutFile: OutputStream = System.out,
initFile: File = null,
ramBlocks: Seq[BigInt] = Seq(256 KiB, 128 KiB),
cpuPlugins: () => Seq[Plugin[VexRiscv]] = PQVexRiscv.withDSPMultiplier()
)
val optParser = new OptionParser[PQVexRiscvSimConfig]("PQVexRiscvSim") {
head("PQVexRiscvSim simulator")
help("help") text("print usage text")
opt[File]("uart") action((f, c) => c.copy(uartOutFile = new FileOutputStream(f, true))) text("File for UART output (will be appended)") valueName("<output>")
opt[File]("init") action((f, c) => c.copy(initFile = f)) text("Initialization file for first RAM block") valueName("<bin>")
opt[Seq[Int]]("ram") action((r, c) => c.copy(ramBlocks = r.map(_ KiB))) text("SRAM Blocks in KiB") valueName("<block1>,<block2>")
}
val config = optParser.parse(args, PQVexRiscvSimConfig()) match {
case Some(config) => config
case None => ???
}
val compiled = SimConfig.allOptimisation.compile {
new PQVexRiscvSim(config.ramBlocks, config.initFile, cpuPlugins=config.cpuPlugins)
}
compiled.doSim("PqVexRiscvSim", 42) { dut =>
val mainClkPeriod = (1e12 / dut.coreFrequency.toDouble).toLong
val jtagClkPeriod = mainClkPeriod * 4
val uartBaudRate = 115200
val uartBaudPeriod = (1e12 / uartBaudRate.toDouble).toLong
val clockDomain = ClockDomain(dut.io.mainClock, dut.io.asyncReset)
clockDomain.forkStimulus(mainClkPeriod)
val tcpJtag = JtagTcp(
jtag = dut.io.jtag,
jtagClkPeriod = jtagClkPeriod
)
println(s"Simulating ${dut.getClass.getName} with JtagTcp on port 7894")
val uartPin = dut.io.uart.txd
val uartDecoder = fork {
sleep(1)
waitUntil(uartPin.toBoolean == true)
try {
while (true) {
waitUntil(uartPin.toBoolean == false)
sleep(uartBaudPeriod / 2)
if (uartPin.toBoolean != false) {
println("\rUART frame error (start bit)")
} else {
sleep(uartBaudPeriod)
var byte = 0
var i = 0
while (i < 8) {
if (uartPin.toBoolean) {
byte |= 1 << i
}
sleep(uartBaudPeriod)
i += 1
}
if (uartPin.toBoolean) {
config.uartOutFile.write(byte)
} else {
println("\rUART frame error (stop bit)")
}
}
}
} catch {
case io: IOException =>
}
println("\rUART decoder stopped")
}
var running = true
while (running) {
sleep(mainClkPeriod * 50000)
}
}
}
}

+ 8
- 0
vexriscv/vexriscvsim.cfg View File

@ -0,0 +1,8 @@
# Adapt this to your favourite FTDI-based debugger
source [find interface/jtag_tcp.cfg]
# The Murax target needs a YAML file, even if it is empty
set MURAX_CPU0_YAML cpu0.yaml
# The Murax target should work for all PQVexRiscv based chips
source [find target/murax.cfg]

Loading…
Cancel
Save