VexRiscv in Briey SoC is working on FPGA (including jtag debugging)

This commit is contained in:
Charles Papon 2017-05-29 21:17:14 +02:00
parent 213e154b40
commit 6b62d8da52
8 changed files with 485 additions and 73 deletions

View File

@ -7,6 +7,6 @@ scalaVersion := "2.11.8"
EclipseKeys.withSource := true
libraryDependencies ++= Seq(
"com.github.spinalhdl" % "spinalhdl-core_2.11" % "0.10.11",
"com.github.spinalhdl" % "spinalhdl-lib_2.11" % "0.10.11"
"com.github.spinalhdl" % "spinalhdl-core_2.11" % "0.10.13",
"com.github.spinalhdl" % "spinalhdl-lib_2.11" % "0.10.13"
)

View File

@ -3,6 +3,7 @@ package SpinalRiscv.Plugin
import SpinalRiscv._
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba4.axi._
case class DBusSimpleCmd() extends Bundle{
@ -22,6 +23,20 @@ case class DBusSimpleRsp() extends Bundle with IMasterSlave{
}
}
object DBusSimpleBus{
def getAxi4Config() = Axi4Config(
addressWidth = 32,
dataWidth = 32,
useId = false,
useRegion = false,
useBurst = false,
useLock = false,
useQos = false,
useLen = false,
useResp = true
)
}
case class DBusSimpleBus() extends Bundle with IMasterSlave{
val cmd = Stream(DBusSimpleCmd())
val rsp = DBusSimpleRsp()
@ -30,8 +45,56 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{
master(cmd)
slave(rsp)
}
def toAxi4Shared(stageCmd : Boolean = false): Axi4Shared = {
val axi = Axi4Shared(DBusSimpleBus.getAxi4Config())
val pendingWritesMax = 7
val pendingWrites = CounterUpDown(
stateCount = pendingWritesMax + 1,
incWhen = axi.sharedCmd.fire && axi.sharedCmd.write,
decWhen = axi.writeRsp.fire
)
val cmdPreFork = if (stageCmd) cmd.stage.stage() else cmd
val (cmdFork, dataFork) = StreamFork2(cmdPreFork.haltWhen((pendingWrites =/= 0 && !cmdPreFork.wr) || pendingWrites === pendingWritesMax))
axi.sharedCmd.arbitrationFrom(cmdFork)
axi.sharedCmd.write := cmdFork.wr
axi.sharedCmd.prot := "010"
axi.sharedCmd.cache := "1111"
axi.sharedCmd.size := cmdFork.size.resized
axi.sharedCmd.addr := cmdFork.address
val dataStage = dataFork.throwWhen(!dataFork.wr)
axi.writeData.arbitrationFrom(dataStage)
axi.writeData.last := True
axi.writeData.data := dataStage.data
axi.writeData.strb := (dataStage.size.mux(
U(0) -> B"0001",
U(1) -> B"0011",
default -> B"1111"
) << dataStage.address(1 downto 0)).resized
rsp.ready := axi.r.valid
rsp.error := !axi.r.isOKAY()
rsp.data := axi.r.data
axi.r.ready := True
axi.b.ready := True
//TODO remove
val axi2 = Axi4Shared(DBusSimpleBus.getAxi4Config())
axi.arw >/-> axi2.arw
axi.w >/-> axi2.w
axi.r <-/< axi2.r
axi.b <-/< axi2.b
axi2
}
}
class DBusSimplePlugin(catchAddressMisaligned : Boolean, catchAccessFault : Boolean) extends Plugin[VexRiscv]{
var dBus : DBusSimpleBus = null
@ -124,7 +187,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean, catchAccessFault : Bool
insert(MEMORY_READ_DATA) := dBus.rsp.data
arbitration.haltIt setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !dBus.rsp.ready)
arbitration.haltIt setWhen(arbitration.isValid && input(MEMORY_ENABLE) && input(REGFILE_WRITE_VALID) && !dBus.rsp.ready)
if(catchAccessFault){
memoryExceptionPort.valid := arbitration.isValid && input(MEMORY_ENABLE) && dBus.rsp.ready && dBus.rsp.error

View File

@ -5,6 +5,7 @@ import SpinalRiscv._
import SpinalRiscv.ip._
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb.{Apb3Config, Apb3}
case class DebugExtensionCmd() extends Bundle{
@ -24,6 +25,24 @@ case class DebugExtensionBus() extends Bundle with IMasterSlave{
master(cmd)
in(rsp)
}
def toApb3(): Apb3 ={
val apb = Apb3(Apb3Config(
addressWidth = 8,
dataWidth = 32,
useSlaveError = false
))
cmd.valid := apb.PSEL(0) && apb.PENABLE
cmd.wr := apb.PWRITE
cmd.address := apb.PADDR
cmd.data := apb.PWDATA
apb.PREADY := cmd.ready
apb.PRDATA := rsp.data
apb
}
}
case class DebugExtensionIo() extends Bundle with IMasterSlave{

View File

@ -3,6 +3,7 @@ package SpinalRiscv.Plugin
import SpinalRiscv.{Stageable, ExceptionService, ExceptionCause, VexRiscv}
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba4.axi._
case class IBusSimpleCmd() extends Bundle{
@ -19,8 +20,21 @@ case class IBusSimpleRsp() extends Bundle with IMasterSlave{
}
}
case class IBusSimpleBus() extends Bundle with IMasterSlave{
object IBusSimpleBus{
def getAxi4Config() = Axi4Config(
addressWidth = 32,
dataWidth = 32,
useId = false,
useRegion = false,
useBurst = false,
useLock = false,
useQos = false,
useLen = false,
useResp = true,
useSize = false
)
}
case class IBusSimpleBus(interfaceKeepData : Boolean) extends Bundle with IMasterSlave{
var cmd = Stream(IBusSimpleCmd())
var rsp = IBusSimpleRsp()
@ -28,6 +42,31 @@ case class IBusSimpleBus() extends Bundle with IMasterSlave{
master(cmd)
slave(rsp)
}
def toAxi4ReadOnly(): Axi4ReadOnly = {
assert(!interfaceKeepData)
val axi = Axi4ReadOnly(IBusSimpleBus.getAxi4Config())
axi.ar.valid := cmd.valid
axi.ar.addr := cmd.pc(axi.readCmd.addr.getWidth -1 downto 2) @@ U"00"
axi.ar.prot := "110"
axi.ar.cache := "1111"
cmd.ready := axi.ar.ready
rsp.ready := axi.r.valid
rsp.inst := axi.r.data
rsp.error := !axi.r.isOKAY()
axi.r.ready := True
//TODO remove
val axi2 = Axi4ReadOnly(IBusSimpleBus.getAxi4Config())
axi.ar >/-> axi2.ar
axi.r <-/< axi2.r
axi2
}
}
class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean) extends Plugin[VexRiscv]{
@ -45,19 +84,41 @@ class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean)
override def build(pipeline: VexRiscv): Unit = {
import pipeline._
import pipeline.config._
iBus = master(IBusSimpleBus(interfaceKeepData)).setName("iBus")
val pendingCmd = RegInit(False) clearWhen(iBus.rsp.ready) setWhen(iBus.cmd.fire)
require(interfaceKeepData)
iBus = master(IBusSimpleBus()).setName("iBus")
//Emit iBus.cmd request
iBus.cmd.valid := prefetch.arbitration.isFiring //prefetch.arbitration.isValid && !prefetch.arbitration.isStuckByOthers
iBus.cmd.pc := prefetch.output(PC)
prefetch.arbitration.haltIt setWhen(!iBus.cmd.ready)
prefetch.arbitration.haltIt setWhen(!iBus.cmd.ready || (pendingCmd && !iBus.rsp.ready)) //TODO rework arbitration of iBusCmdvalid and halt it
//Bus rsp buffer
val rspBuffer = if(!interfaceKeepData) new Area{
val valid = RegInit(False) setWhen(iBus.rsp.ready) clearWhen(!fetch.arbitration.isStuck)
val error = Reg(Bool)
val data = Reg(Bits(32 bits))
when(!valid) {
data := iBus.rsp.inst
error := iBus.rsp.error
}
} else null
//Insert iBus.rsp into INSTRUCTION
fetch.insert(INSTRUCTION) := iBus.rsp.inst
fetch.insert(IBUS_ACCESS_ERROR) := iBus.rsp.error
fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iBus.rsp.ready)
if(!interfaceKeepData) {
when(rspBuffer.valid) {
fetch.insert(INSTRUCTION) := rspBuffer.data
fetch.insert(IBUS_ACCESS_ERROR) := rspBuffer.error
}
}
if(interfaceKeepData)
fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iBus.rsp.ready)
else
fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iBus.rsp.ready && !rspBuffer.valid)
decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck,decode.input(INSTRUCTION),fetch.output(INSTRUCTION))
decode.insert(INSTRUCTION_READY) := True

View File

@ -220,16 +220,17 @@ object TopLevel {
new LightShifterPlugin,
// new HazardSimplePlugin(true, true, true, true),
// new HazardSimplePlugin(false, true, false, true),
// new HazardSimplePlugin(
// bypassExecute = false,
// bypassMemory = false,
// bypassWriteBack = false,
// bypassWriteBackBuffer = false,
// pessimisticUseSrc = false,
// pessimisticWriteRegFile = false,
// pessimisticAddressMatch = false
// ),
new HazardPessimisticPlugin,
new HazardSimplePlugin(
bypassExecute = false,
bypassMemory = false,
bypassWriteBack = false,
bypassWriteBackBuffer = false,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false
),
// new HazardPessimisticPlugin,
new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))),
// new MulPlugin,
// new DivPlugin,
// new MachineCsr(csrConfig),
@ -250,56 +251,10 @@ object TopLevel {
interfaceKeepData = true,
catchAccessFault = true
),
// new IBusCachedPlugin(
// config = InstructionCacheConfig(
// cacheSize = 4096,
// bytePerLine =32,
// wayCount = 1,
// wrappedMemAccess = true,
// addressWidth = 32,
// cpuDataWidth = 32,
// memDataWidth = 32,
// catchIllegalAccess = true,
// catchAccessFault = true,
// catchMemoryTranslationMiss = true,
// asyncTagMemory = false,
// twoStageLogic = true
// ),
// askMemoryTranslation = true,
// memoryTranslatorPortConfig = MemoryTranslatorPortConfig(
// portTlbSize = 4
// )
// ),
new DBusSimplePlugin(
catchAddressMisaligned = true,
catchAccessFault = true
),
// new DBusCachedPlugin(
// config = new DataCacheConfig(
// cacheSize = 4096,
// bytePerLine = 32,
// wayCount = 1,
// addressWidth = 32,
// cpuDataWidth = 32,
// memDataWidth = 32,
// catchAccessError = true,
// catchIllegal = true,
// catchUnaligned = true,
// catchMemoryTranslationMiss = true,
// tagSizeShift = 2
// ),
// askMemoryTranslation = true,
// memoryTranslatorPortConfig = MemoryTranslatorPortConfig(
// portTlbSize = 6
// )
// ),
// new MemoryTranslatorPlugin(
// tlbSize = 32,
// virtualRange = _(31 downto 28) === 0xC,
// ioRange = _(31 downto 28) === 0xF
// ),
new CsrPlugin(csrConfigSmall),
new DecoderSimplePlugin(
catchIllegalInstruction = true
@ -336,8 +291,8 @@ object TopLevel {
)
)
val toplevel = new VexRiscv(configFull)
// val toplevel = new VexRiscv(configLight)
// val toplevel = new VexRiscv(configFull)
val toplevel = new VexRiscv(configLight)
// val toplevel = new VexRiscv(configTest)
toplevel.decode.input(toplevel.config.INSTRUCTION).addAttribute(Verilator.public)
toplevel.decode.input(toplevel.config.PC).addAttribute(Verilator.public)

View File

@ -0,0 +1,314 @@
package SpinalRiscv.demo
import SpinalRiscv.Plugin._
import SpinalRiscv._
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.amba4.axi._
import spinal.lib.com.jtag.Jtag
import spinal.lib.com.uart.{Uart, UartCtrlGenerics, UartCtrlMemoryMappedConfig, Apb3UartCtrl}
import spinal.lib.graphic.RgbConfig
import spinal.lib.graphic.vga.{Vga, Axi4VgaCtrlGenerics, Axi4VgaCtrl}
import spinal.lib.io.TriStateArray
import spinal.lib.memory.sdram._
import spinal.lib.soc.pinsec.{PinsecTimerCtrlExternal, PinsecTimerCtrl}
import spinal.lib.system.debugger.{JtagAxi4SharedDebugger, SystemDebuggerConfig}
case class BrieyConfig(axiFrequency : HertzNumber,
onChipRamSize : BigInt,
sdramLayout: SdramLayout,
sdramTimings: SdramTimings)
object BrieyConfig{
def default = {
val config = BrieyConfig(
axiFrequency = 50 MHz,
onChipRamSize = 4 kB,
sdramLayout = IS42x320D.layout,
sdramTimings = IS42x320D.timingGrade7
)
config
}
}
class Briey(config: BrieyConfig) extends Component{
//Legacy constructor
def this(axiFrequency: HertzNumber) {
this(BrieyConfig.default.copy(axiFrequency = axiFrequency))
}
import config._
val debug = true
val interruptCount = 4
def vgaRgbConfig = RgbConfig(5,6,5)
val io = new Bundle{
//Clocks / reset
val asyncReset = in Bool
val axiClk = in Bool
val vgaClk = in Bool
//Main components IO
val jtag = slave(Jtag())
val sdram = master(SdramInterface(sdramLayout))
//Peripherals IO
val gpioA = master(TriStateArray(32 bits))
val gpioB = master(TriStateArray(32 bits))
val uart = master(Uart())
val vga = master(Vga(vgaRgbConfig))
val timerExternal = in(PinsecTimerCtrlExternal())
}
val resetCtrlClockDomain = ClockDomain(
clock = io.axiClk,
config = ClockDomainConfig(
resetKind = BOOT
)
)
val resetCtrl = new ClockingArea(resetCtrlClockDomain) {
val axiResetUnbuffered = False
val coreResetUnbuffered = False
//Implement an counter to keep the reset axiResetOrder high 64 cycles
// Also this counter will automaticly do a reset when the system boot.
val axiResetCounter = Reg(UInt(6 bits)) init(0)
when(axiResetCounter =/= U(axiResetCounter.range -> true)){
axiResetCounter := axiResetCounter + 1
axiResetUnbuffered := True
}
when(BufferCC(io.asyncReset)){
axiResetCounter := 0
}
//When an axiResetOrder happen, the core reset will as well
when(axiResetUnbuffered){
coreResetUnbuffered := True
}
//Create all reset used later in the design
val axiReset = RegNext(axiResetUnbuffered)
val coreReset = RegNext(coreResetUnbuffered)
val vgaReset = BufferCC(axiResetUnbuffered)
}
val axiClockDomain = ClockDomain(
clock = io.axiClk,
reset = resetCtrl.axiReset,
frequency = FixedFrequency(axiFrequency) //The frequency information is used by the SDRAM controller
)
val coreClockDomain = ClockDomain(
clock = io.axiClk,
reset = resetCtrl.coreReset
)
val vgaClockDomain = ClockDomain(
clock = io.vgaClk,
reset = resetCtrl.vgaReset
)
val jtagClockDomain = ClockDomain(
clock = io.jtag.tck
)
val axi = new ClockingArea(axiClockDomain) {
val core = new ClockingArea(coreClockDomain){
val configLight = VexRiscvConfig(
plugins = List(
new PcManagerSimplePlugin(0x00000000l, false),
new IBusSimplePlugin(
interfaceKeepData = false,
catchAccessFault = false
),
new DBusSimplePlugin(
catchAddressMisaligned = false,
catchAccessFault = false
),
new DecoderSimplePlugin(
catchIllegalInstruction = false
),
new RegFilePlugin(
regFileReadyKind = Plugin.SYNC,
zeroBoot = false
),
new IntAluPlugin,
new SrcPlugin(
separatedAddSub = false
),
new LightShifterPlugin,
new HazardSimplePlugin(
bypassExecute = false,
bypassMemory = false,
bypassWriteBack = false,
bypassWriteBackBuffer = false,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false
),
new DebugPlugin(axiClockDomain),
new BranchPlugin(
earlyBranch = false,
catchAddressMisaligned = false,
prediction = NONE
)
)
)
val cpu = new VexRiscv(configLight)
var iBus : Axi4Bus = null
var dBus : Axi4Bus = null
var debugBus : Apb3 = null
for(plugin <- configLight.plugins) plugin match{
case plugin : IBusSimplePlugin => iBus = plugin.iBus.toAxi4ReadOnly()
case plugin : DBusSimplePlugin => dBus = plugin.dBus.toAxi4Shared()
case plugin : DebugPlugin => {
resetCtrl.coreResetUnbuffered setWhen(plugin.io.resetOut)
debugBus = plugin.io.bus.toApb3()
}
case _ =>
}
}
val ram = Axi4SharedOnChipRam(
dataWidth = 32,
byteCount = onChipRamSize,
idWidth = 4
)
val sdramCtrl = Axi4SharedSdramCtrl(
axiDataWidth = 32,
axiIdWidth = 4,
layout = sdramLayout,
timing = sdramTimings,
CAS = 3
)
val jtagCtrl = JtagAxi4SharedDebugger(SystemDebuggerConfig(
memAddressWidth = 32,
memDataWidth = 32,
remoteCmdWidth = 1
))
val apbBridge = Axi4SharedToApb3Bridge(
addressWidth = 20,
dataWidth = 32,
idWidth = 4
)
val gpioACtrl = Apb3Gpio(
gpioWidth = 32
)
val gpioBCtrl = Apb3Gpio(
gpioWidth = 32
)
val timerCtrl = PinsecTimerCtrl()
val uartCtrlConfig = UartCtrlMemoryMappedConfig(
uartCtrlConfig = UartCtrlGenerics(
dataWidthMax = 8,
clockDividerWidth = 20,
preSamplingSize = 1,
samplingSize = 5,
postSamplingSize = 2
),
txFifoDepth = 16,
rxFifoDepth = 16
)
val uartCtrl = Apb3UartCtrl(uartCtrlConfig)
val vgaCtrlConfig = Axi4VgaCtrlGenerics(
axiAddressWidth = 32,
axiDataWidth = 32,
burstLength = 8,
frameSizeMax = 2048*1512*2,
fifoSize = 512,
rgbConfig = vgaRgbConfig,
vgaClock = vgaClockDomain
)
val vgaCtrl = Axi4VgaCtrl(vgaCtrlConfig)
val axiCrossbar = Axi4CrossbarFactory()
axiCrossbar.addSlaves(
ram.io.axi -> (0x00000000L, onChipRamSize),
sdramCtrl.io.axi -> (0x40000000L, sdramLayout.capacity),
apbBridge.io.axi -> (0xF0000000L, 1 MB)
)
axiCrossbar.addConnections(
core.iBus -> List(ram.io.axi, sdramCtrl.io.axi),
core.dBus -> List(ram.io.axi, sdramCtrl.io.axi, apbBridge.io.axi),
jtagCtrl.io.axi -> List(ram.io.axi, sdramCtrl.io.axi, apbBridge.io.axi),
vgaCtrl.io.axi -> List( sdramCtrl.io.axi)
)
axiCrossbar.addPipelining(apbBridge.io.axi,(crossbar,bridge) => {
crossbar.sharedCmd.halfPipe() >> bridge.sharedCmd
crossbar.writeData.halfPipe() >> bridge.writeData
crossbar.writeRsp << bridge.writeRsp
crossbar.readRsp << bridge.readRsp
})
axiCrossbar.addPipelining(sdramCtrl.io.axi,(crossbar,ctrl) => {
crossbar.sharedCmd.halfPipe() >> ctrl.sharedCmd
crossbar.writeData >/-> ctrl.writeData
crossbar.writeRsp << ctrl.writeRsp
crossbar.readRsp << ctrl.readRsp
})
axiCrossbar.build()
val apbDecoder = Apb3Decoder(
master = apbBridge.io.apb,
slaves = List(
gpioACtrl.io.apb -> (0x00000, 4 kB),
gpioBCtrl.io.apb -> (0x01000, 4 kB),
uartCtrl.io.apb -> (0x10000, 4 kB),
timerCtrl.io.apb -> (0x20000, 4 kB),
vgaCtrl.io.apb -> (0x30000, 4 kB),
core.debugBus -> (0xF0000, 4 kB)
)
)
}
io.gpioA <> axi.gpioACtrl.io.gpio
io.gpioB <> axi.gpioBCtrl.io.gpio
io.timerExternal <> axi.timerCtrl.io.external
io.jtag <> axi.jtagCtrl.io.jtag
io.uart <> axi.uartCtrl.io.uart
io.sdram <> axi.sdramCtrl.io.sdram
io.vga <> axi.vgaCtrl.io.vga
}
object Briey{
def main(args: Array[String]) {
val config = SpinalConfig().dumpWave()
config.generateVerilog({
val toplevel = new Briey(BrieyConfig.default)
/*toplevel.axi.ram.ram.initialContent = new Array[BigInt](toplevel.axi.ram.ram.wordCount)
toplevel.axi.ram.ram.initialContent(0) = 0x00000013
toplevel.axi.ram.ram.initialContent(1) = 0x10000013
toplevel.axi.ram.ram.initialContent(2) = 0x20000013
toplevel.axi.ram.ram.initialContent(3) = 0x30000013
toplevel.axi.ram.ram.initialContent(4) = 0x40000013
toplevel.axi.ram.ram.initialContent(5) = 0x50000013
toplevel.axi.ram.ram.initialContent(6) = 0x60000013
toplevel.axi.ram.ram.initialContent(7) = 0x70000013*/
toplevel
})
}
}

View File

@ -466,7 +466,7 @@ public:
ws->iBusAccess(top->iBus_cmd_payload_pc,&inst_next,&error_next);
}
}
//TODO doesn't catch when instruction removed ?
virtual void postCycle(){
top->iBus_rsp_ready = !pending;
if(pending && (!ws->iStall || VL_RANDOM_I(7) < 100)){
@ -772,7 +772,7 @@ public:
uint32_t address = *((uint32_t*)(buffer + 2));
uint32_t data = *((uint32_t*)(buffer + 6));
if((address & ~ 0x4) == 0xFFF00000){
if((address & ~ 0x4) == 0xF00F0000){
assert(size == 2);
top->debug_bus_cmd_valid = 1;
@ -1035,7 +1035,7 @@ public:
socklen_t addr_size = sizeof serverAddr;
int error = connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
// printf("!! %x\n",readCmd(2,0x8));
uint32_t debugAddress = 0xFFF00000;
uint32_t debugAddress = 0xF00F0000;
uint32_t readValue;
while(resetDone != true){usleep(100);}

View File

@ -3,8 +3,8 @@ DBUS=DBUS_SIMPLE
TRACE?=no
TRACE_ACCESS?=no
TRACE_START=0
CSR=yes
MMU=yes
CSR=no
MMU=no
DEBUG_PLUGIN=yes
DEBUG_PLUGIN_EXTERNAL?=no
DHRYSTONE=yes