Clean Murax toplevel by extracting integrated Area into dedicated components located in MuraxUtiles.scala

This commit is contained in:
Dolu1990 2017-11-07 22:19:33 +01:00
parent 0cf278b04f
commit a72c7fd0d1
2 changed files with 282 additions and 231 deletions

View File

@ -2,15 +2,15 @@ package vexriscv.demo
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba3.apb.{Apb3SlaveFactory, Apb3Decoder, Apb3Gpio, Apb3}
import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.misc.SizeMapping
import spinal.lib.com.jtag.Jtag
import spinal.lib.com.uart._
import spinal.lib.io.TriStateArray
import spinal.lib.misc.{InterruptCtrl, Timer, Prescaler}
import spinal.lib.soc.pinsec.{PinsecTimerCtrlExternal, PinsecTimerCtrl}
import spinal.lib.misc.{InterruptCtrl, Prescaler, Timer}
import spinal.lib.soc.pinsec.{PinsecTimerCtrl, PinsecTimerCtrlExternal}
import vexriscv.plugin._
import vexriscv.{plugin, VexRiscvConfig, VexRiscv}
import vexriscv.{VexRiscv, VexRiscvConfig, plugin}
import scala.collection.mutable.ArrayBuffer
@ -132,27 +132,6 @@ object MuraxConfig{
}
}
case class SimpleBusCmd() extends Bundle{
val wr = Bool
val address = UInt(32 bits)
val data = Bits(32 bit)
val mask = Bits(4 bit)
}
case class SimpleBusRsp() extends Bundle{
val data = Bits(32 bit)
}
case class SimpleBus() extends Bundle with IMasterSlave {
val cmd = Stream(SimpleBusCmd())
val rsp = Flow(SimpleBusRsp())
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
}
case class Murax(config : MuraxConfig) extends Component{
import config._
@ -211,6 +190,14 @@ case class Murax(config : MuraxConfig) extends Component{
)
val system = new ClockingArea(systemClockDomain) {
val simpleBusConfig = SimpleBusConfig(
addressWidth = 32,
dataWidth = 32
)
//Arbiter of the cpu dBus/iBus to drive the mainBus
//Priority to dBus, !! cmd transactions can change on the fly !!
val mainBusArbiter = new MuraxMasterArbiter(simpleBusConfig)
//Instanciate the CPU
val cpu = new VexRiscv(
@ -222,18 +209,14 @@ case class Murax(config : MuraxConfig) extends Component{
//Checkout plugins used to instanciate the CPU to connect them to the SoC
val timerInterrupt = False
val externalInterrupt = False
var iBus : IBusSimpleBus = null
var dBus : DBusSimpleBus = null
var debugBus : DebugExtensionBus = null
for(plugin <- cpu.plugins) plugin match{
case plugin : IBusSimplePlugin => iBus = plugin.iBus
case plugin : IBusSimplePlugin => mainBusArbiter.io.iBus <> plugin.iBus
case plugin : DBusSimplePlugin => {
if(!pipelineDBus)
dBus = plugin.dBus
mainBusArbiter.io.dBus <> plugin.dBus
else {
dBus = cloneOf(plugin.dBus)
dBus.cmd << plugin.dBus.cmd.halfPipe()
dBus.rsp <> plugin.dBus.rsp
mainBusArbiter.io.dBus.cmd << plugin.dBus.cmd.halfPipe()
mainBusArbiter.io.dBus.rsp <> plugin.dBus.rsp
}
}
case plugin : CsrPlugin => {
@ -248,218 +231,58 @@ case class Murax(config : MuraxConfig) extends Component{
}
val mainBus = SimpleBus()
//Arbiter of the cpu dBus/iBus to drive the mainBus
//Priority to dBus, !! cmd transactions can change on the fly !!
val mainBusArbiter = new Area{
mainBus.cmd.valid := iBus.cmd.valid || dBus.cmd.valid
mainBus.cmd.wr := dBus.cmd.valid && dBus.cmd.wr
mainBus.cmd.address := dBus.cmd.valid ? dBus.cmd.address | iBus.cmd.pc
mainBus.cmd.data := dBus.cmd.data
mainBus.cmd.mask := dBus.cmd.size.mux(
0 -> B"0001",
1 -> B"0011",
default -> B"1111"
) |<< dBus.cmd.address(1 downto 0)
iBus.cmd.ready := mainBus.cmd.ready && !dBus.cmd.valid
dBus.cmd.ready := mainBus.cmd.ready
val rspPending = RegInit(False) clearWhen(mainBus.rsp.valid)
val rspTarget = RegInit(False)
when(mainBus.cmd.fire && !mainBus.cmd.wr){
rspTarget := dBus.cmd.valid
rspPending := True
}
when(rspPending && !mainBus.rsp.valid){
iBus.cmd.ready := False
dBus.cmd.ready := False
mainBus.cmd.valid := False
}
iBus.rsp.ready := mainBus.rsp.valid && !rspTarget
iBus.rsp.inst := mainBus.rsp.data
iBus.rsp.error := False
dBus.rsp.ready := mainBus.rsp.valid && rspTarget
dBus.rsp.data := mainBus.rsp.data
dBus.rsp.error := False
}
//Create an SimpleBus mapped RAM
val ram = new Area{
val bus = SimpleBus()
val ram = Mem(Bits(32 bits), onChipRamSize / 4)
bus.rsp.valid := RegNext(bus.cmd.fire && !bus.cmd.wr) init(False)
bus.rsp.data := ram.readWriteSync(
address = (bus.cmd.address >> 2).resized,
data = bus.cmd.data,
enable = bus.cmd.valid,
write = bus.cmd.wr,
mask = bus.cmd.mask
//****** MainBus slaves ********
val ram = new MuraxSimpleBusRam(
onChipRamSize = onChipRamSize,
onChipRamHexFile = onChipRamHexFile,
simpleBusConfig = simpleBusConfig
)
bus.cmd.ready := True
if(onChipRamHexFile != null){
def readHexFile(path : String, callback : (Int, Int) => Unit): Unit ={
import scala.io.Source
def hToI(that : String, start : Int, size : Int) = Integer.parseInt(that.substring(start,start + size), 16)
var offset = 0
for (line <- Source.fromFile(path).getLines) {
if (line.charAt(0) == ':'){
val byteCount = hToI(line, 1, 2)
val nextAddr = hToI(line, 3, 4) + offset
val key = hToI(line, 7, 2)
key match {
case 0 =>
for(i <- 0 until byteCount){
callback(nextAddr + i, hToI(line, 9 + i * 2, 2))
}
case 2 =>
offset = hToI(line, 9, 4) << 4
case 4 =>
offset = hToI(line, 9, 4) << 16
case 3 =>
case 1 =>
}
}
}
}
val initContent = Array.fill[BigInt](ram.wordCount)(0)
readHexFile(onChipRamHexFile,(address,data) => {
initContent(address >> 2) |= BigInt(data) << ((address & 3)*8)
})
ram.initBigInt(initContent)
}
}
//Bridge simpleBus to apb
val apbBridge = new Area{
val simpleBus = SimpleBus()
val apb = Apb3(
val apbBridge = new MuraxSimpleBusToApbBridge(
apb3Config = Apb3Config(
addressWidth = 20,
dataWidth = 32
)
val simpleBusStage = SimpleBus()
simpleBusStage.cmd << (if(pipelineApbBridge) simpleBus.cmd.halfPipe() else simpleBus.cmd)
simpleBusStage.rsp >-> simpleBus.rsp
val state = RegInit(False)
simpleBusStage.cmd.ready := False
apb.PSEL(0) := simpleBusStage.cmd.valid
apb.PENABLE := state
apb.PWRITE := simpleBusStage.cmd.wr
apb.PADDR := simpleBusStage.cmd.address.resized
apb.PWDATA := simpleBusStage.cmd.data
simpleBusStage.rsp.valid := False
simpleBusStage.rsp.data := apb.PRDATA
when(!state){
state := simpleBusStage.cmd.valid
} otherwise{
when(apb.PREADY){
state := False
simpleBusStage.rsp.valid := !simpleBusStage.cmd.wr
simpleBusStage.cmd.ready := True
}
}
}
//Connect the mainBus to all slaves (ram, apbBridge)
val mainBusDecoder = new Area {
val masterBus = SimpleBus()
if(!pipelineMainBus) {
masterBus.cmd << mainBus.cmd
masterBus.rsp >> mainBus.rsp
} else {
masterBus.cmd <-< mainBus.cmd
masterBus.rsp >> mainBus.rsp
}
val specification = List[(SimpleBus,SizeMapping)](
ram.bus -> (0x00000000l, onChipRamSize kB),
apbBridge.simpleBus -> (0xF0000000l, 1 MB)
),
pipelineBridge = pipelineApbBridge,
simpleBusConfig = simpleBusConfig
)
val slaveBuses = specification.map(_._1)
val memorySpaces = specification.map(_._2)
val hits = for((slaveBus, memorySpace) <- specification) yield {
val hit = memorySpace.hit(masterBus.cmd.address)
slaveBus.cmd.valid := masterBus.cmd.valid && hit
slaveBus.cmd.payload := masterBus.cmd.payload
hit
}
val noHit = !hits.orR
masterBus.cmd.ready := (hits,slaveBuses).zipped.map(_ && _.cmd.ready).orR || noHit
val rspPending = RegInit(False) clearWhen(masterBus.rsp.valid) setWhen(masterBus.cmd.fire && !masterBus.cmd.wr)
val rspNoHit = RegNext(False) init(False) setWhen(noHit)
val rspSourceId = RegNextWhen(OHToUInt(hits), masterBus.cmd.fire)
masterBus.rsp.valid := slaveBuses.map(_.rsp.valid).orR || (rspPending && rspNoHit)
masterBus.rsp.payload := slaveBuses.map(_.rsp.payload).read(rspSourceId)
when(rspPending && !masterBus.rsp.valid) { //Only one pending read request is allowed
masterBus.cmd.ready := False
slaveBuses.foreach(_.cmd.valid := False)
}
}
val gpioACtrl = Apb3Gpio(
gpioWidth = gpioWidth
)
//******** APB peripherals *********
val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth)
io.gpioA <> gpioACtrl.io.gpio
val uartCtrl = Apb3UartCtrl(uartCtrlConfig)
uartCtrl.io.uart <> io.uart
externalInterrupt setWhen(uartCtrl.io.interrupt)
val timer = new Area{
val apb = Apb3(
addressWidth = 8,
dataWidth = 32
)
val prescaler = Prescaler(16)
val timerA,timerB = Timer(16)
val busCtrl = Apb3SlaveFactory(apb)
val prescalerBridge = prescaler.driveFrom(busCtrl,0x00)
val timerABridge = timerA.driveFrom(busCtrl,0x40)(
ticks = List(True, prescaler.io.overflow),
clears = List(timerA.io.full)
)
val timerBBridge = timerB.driveFrom(busCtrl,0x50)(
ticks = List(True, prescaler.io.overflow),
clears = List(timerB.io.full)
)
val interruptCtrl = InterruptCtrl(2)
val interruptCtrlBridge = interruptCtrl.driveFrom(busCtrl,0x10)
interruptCtrl.io.inputs(0) := timerA.io.full
interruptCtrl.io.inputs(1) := timerB.io.full
timerInterrupt setWhen(interruptCtrl.io.pendings.orR)
}
val timer = new MuraxApb3Timer()
timerInterrupt setWhen(timer.io.interrupt)
//******** Memory mappings *********
val apbDecoder = Apb3Decoder(
master = apbBridge.apb,
master = apbBridge.io.apb,
slaves = List(
gpioACtrl.io.apb -> (0x00000, 4 kB),
uartCtrl.io.apb -> (0x10000, 4 kB),
timer.apb -> (0x20000, 4 kB)
timer.io.apb -> (0x20000, 4 kB)
)
)
val mainBusDecoder = new Area {
val logic = new MuraxSimpleBusDecoder(
master = mainBusArbiter.io.masterBus,
specification = List[(SimpleBus,SizeMapping)](
ram.io.bus -> (0x00000000l, onChipRamSize kB),
apbBridge.io.simpleBus -> (0xF0000000l, 1 MB)
),
pipelineMaster = pipelineMainBus
)
}
}
}

View File

@ -0,0 +1,228 @@
package vexriscv.demo
import spinal.core._
import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config, Apb3SlaveFactory}
import spinal.lib.bus.misc.SizeMapping
import spinal.lib.misc.{InterruptCtrl, Prescaler, Timer}
import spinal.lib._
import vexriscv.plugin.{DBusSimpleBus, IBusSimpleBus}
case class SimpleBusConfig(addressWidth : Int, dataWidth : Int)
case class SimpleBusCmd(config : SimpleBusConfig) extends Bundle{
val wr = Bool
val address = UInt(config.addressWidth bits)
val data = Bits(config.dataWidth bits)
val mask = Bits(4 bit)
}
case class SimpleBusRsp(config : SimpleBusConfig) extends Bundle{
val data = Bits(config.dataWidth bits)
}
case class SimpleBus(config : SimpleBusConfig) extends Bundle with IMasterSlave {
val cmd = Stream(SimpleBusCmd(config))
val rsp = Flow(SimpleBusRsp(config))
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
}
}
class MuraxMasterArbiter(simpleBusConfig : SimpleBusConfig) extends Component{
val io = new Bundle{
val iBus = slave(IBusSimpleBus(false))
val dBus = slave(DBusSimpleBus())
val masterBus = master(SimpleBus(simpleBusConfig))
}
io.masterBus.cmd.valid := io.iBus.cmd.valid || io.dBus.cmd.valid
io.masterBus.cmd.wr := io.dBus.cmd.valid && io.dBus.cmd.wr
io.masterBus.cmd.address := io.dBus.cmd.valid ? io.dBus.cmd.address | io.iBus.cmd.pc
io.masterBus.cmd.data := io.dBus.cmd.data
io.masterBus.cmd.mask := io.dBus.cmd.size.mux(
0 -> B"0001",
1 -> B"0011",
default -> B"1111"
) |<< io.dBus.cmd.address(1 downto 0)
io.iBus.cmd.ready := io.masterBus.cmd.ready && !io.dBus.cmd.valid
io.dBus.cmd.ready := io.masterBus.cmd.ready
val rspPending = RegInit(False) clearWhen(io.masterBus.rsp.valid)
val rspTarget = RegInit(False)
when(io.masterBus.cmd.fire && !io.masterBus.cmd.wr){
rspTarget := io.dBus.cmd.valid
rspPending := True
}
when(rspPending && !io.masterBus.rsp.valid){
io.iBus.cmd.ready := False
io.dBus.cmd.ready := False
io.masterBus.cmd.valid := False
}
io.iBus.rsp.ready := io.masterBus.rsp.valid && !rspTarget
io.iBus.rsp.inst := io.masterBus.rsp.data
io.iBus.rsp.error := False
io.dBus.rsp.ready := io.masterBus.rsp.valid && rspTarget
io.dBus.rsp.data := io.masterBus.rsp.data
io.dBus.rsp.error := False
}
class MuraxSimpleBusRam(onChipRamSize : BigInt, onChipRamHexFile : String, simpleBusConfig : SimpleBusConfig) extends Component{
val io = new Bundle{
val bus = slave(SimpleBus(simpleBusConfig))
}
val ram = Mem(Bits(32 bits), onChipRamSize / 4)
io.bus.rsp.valid := RegNext(io.bus.cmd.fire && !io.bus.cmd.wr) init(False)
io.bus.rsp.data := ram.readWriteSync(
address = (io.bus.cmd.address >> 2).resized,
data = io.bus.cmd.data,
enable = io.bus.cmd.valid,
write = io.bus.cmd.wr,
mask = io.bus.cmd.mask
)
io.bus.cmd.ready := True
if(onChipRamHexFile != null){
def readHexFile(path : String, callback : (Int, Int) => Unit): Unit ={
import scala.io.Source
def hToI(that : String, start : Int, size : Int) = Integer.parseInt(that.substring(start,start + size), 16)
var offset = 0
for (line <- Source.fromFile(path).getLines) {
if (line.charAt(0) == ':'){
val byteCount = hToI(line, 1, 2)
val nextAddr = hToI(line, 3, 4) + offset
val key = hToI(line, 7, 2)
key match {
case 0 =>
for(i <- 0 until byteCount){
callback(nextAddr + i, hToI(line, 9 + i * 2, 2))
}
case 2 =>
offset = hToI(line, 9, 4) << 4
case 4 =>
offset = hToI(line, 9, 4) << 16
case 3 =>
case 1 =>
}
}
}
}
val initContent = Array.fill[BigInt](ram.wordCount)(0)
readHexFile(onChipRamHexFile,(address,data) => {
initContent(address >> 2) |= BigInt(data) << ((address & 3)*8)
})
ram.initBigInt(initContent)
}
}
class MuraxSimpleBusToApbBridge(apb3Config: Apb3Config, pipelineBridge : Boolean, simpleBusConfig : SimpleBusConfig) extends Component{
assert(apb3Config.dataWidth == simpleBusConfig.dataWidth)
val io = new Bundle {
val simpleBus = slave(SimpleBus(simpleBusConfig))
val apb = master(Apb3(apb3Config))
}
val simpleBusStage = SimpleBus(simpleBusConfig)
simpleBusStage.cmd << (if(pipelineBridge) io.simpleBus.cmd.halfPipe() else io.simpleBus.cmd)
simpleBusStage.rsp >-> io.simpleBus.rsp
val state = RegInit(False)
simpleBusStage.cmd.ready := False
io.apb.PSEL(0) := simpleBusStage.cmd.valid
io.apb.PENABLE := state
io.apb.PWRITE := simpleBusStage.cmd.wr
io.apb.PADDR := simpleBusStage.cmd.address.resized
io.apb.PWDATA := simpleBusStage.cmd.data
simpleBusStage.rsp.valid := False
simpleBusStage.rsp.data := io.apb.PRDATA
when(!state) {
state := simpleBusStage.cmd.valid
} otherwise {
when(io.apb.PREADY){
state := False
simpleBusStage.rsp.valid := !simpleBusStage.cmd.wr
simpleBusStage.cmd.ready := True
}
}
}
class MuraxSimpleBusDecoder(master : SimpleBus, specification : List[(SimpleBus,SizeMapping)], pipelineMaster : Boolean) extends Area{
val masterPipelined = SimpleBus(master.config)
if(!pipelineMaster) {
masterPipelined.cmd << master.cmd
masterPipelined.rsp >> master.rsp
} else {
masterPipelined.cmd <-< master.cmd
masterPipelined.rsp >> master.rsp
}
val slaveBuses = specification.map(_._1)
val memorySpaces = specification.map(_._2)
val hits = for((slaveBus, memorySpace) <- specification) yield {
val hit = memorySpace.hit(masterPipelined.cmd.address)
slaveBus.cmd.valid := masterPipelined.cmd.valid && hit
slaveBus.cmd.payload := masterPipelined.cmd.payload
hit
}
val noHit = !hits.orR
masterPipelined.cmd.ready := (hits,slaveBuses).zipped.map(_ && _.cmd.ready).orR || noHit
val rspPending = RegInit(False) clearWhen(masterPipelined.rsp.valid) setWhen(masterPipelined.cmd.fire && !masterPipelined.cmd.wr)
val rspNoHit = RegNext(False) init(False) setWhen(noHit)
val rspSourceId = RegNextWhen(OHToUInt(hits), masterPipelined.cmd.fire)
masterPipelined.rsp.valid := slaveBuses.map(_.rsp.valid).orR || (rspPending && rspNoHit)
masterPipelined.rsp.payload := slaveBuses.map(_.rsp.payload).read(rspSourceId)
when(rspPending && !masterPipelined.rsp.valid) { //Only one pending read request is allowed
masterPipelined.cmd.ready := False
slaveBuses.foreach(_.cmd.valid := False)
}
}
class MuraxApb3Timer extends Component{
val io = new Bundle {
val apb = slave(Apb3(
addressWidth = 8,
dataWidth = 32
))
val interrupt = out Bool
}
val prescaler = Prescaler(16)
val timerA,timerB = Timer(16)
val busCtrl = Apb3SlaveFactory(io.apb)
val prescalerBridge = prescaler.driveFrom(busCtrl,0x00)
val timerABridge = timerA.driveFrom(busCtrl,0x40)(
ticks = List(True, prescaler.io.overflow),
clears = List(timerA.io.full)
)
val timerBBridge = timerB.driveFrom(busCtrl,0x50)(
ticks = List(True, prescaler.io.overflow),
clears = List(timerB.io.full)
)
val interruptCtrl = InterruptCtrl(2)
val interruptCtrlBridge = interruptCtrl.driveFrom(busCtrl,0x10)
interruptCtrl.io.inputs(0) := timerA.io.full
interruptCtrl.io.inputs(1) := timerB.io.full
io.interrupt := interruptCtrl.io.pendings.orR
}