package vexriscv.demo.smp

import spinal.core._
import spinal.lib.bus.bmb._
import spinal.lib.bus.misc.{AddressMapping, DefaultMapping, SizeMapping}
import spinal.lib.bus.wishbone.{WishboneConfig, WishboneToBmbGenerator}
import spinal.lib.sim.SparseMemory
import vexriscv.demo.smp.VexRiscvSmpClusterGen.vexRiscvConfig

case class __Unused_Because_EmptyCompilationUnitsCauseScala213IncrementalCompilationIssues();

//case class VexRiscvLitexSmpMpClusterParameter( cluster : VexRiscvSmpClusterParameter,
//                                             liteDram : LiteDramNativeParameter,
//                                             liteDramMapping : AddressMapping)
//
//class VexRiscvLitexSmpMpCluster(p : VexRiscvLitexSmpMpClusterParameter) extends VexRiscvSmpClusterWithPeripherals(p.cluster) {
//  val iArbiter = BmbBridgeGenerator()
//  val iBridge = BmbToLiteDramGenerator(p.liteDramMapping)
//  val dBridge = BmbToLiteDramGenerator(p.liteDramMapping)
//
//  for(core <- cores) interconnect.addConnection(core.cpu.iBus -> List(iArbiter.bmb))
//  interconnect.addConnection(
//    iArbiter.bmb               -> List(iBridge.bmb, peripheralBridge.bmb),
//    invalidationMonitor.output -> List(dBridge.bmb, peripheralBridge.bmb)
//  )
//  interconnect.masters(invalidationMonitor.output).withOutOfOrderDecoder()
//
//  dBridge.liteDramParameter.load(p.liteDram)
//  iBridge.liteDramParameter.load(p.liteDram)
//
//  // Interconnect pipelining (FMax)
//  for(core <- cores) {
//    interconnect.setPipelining(core.cpu.dBus)(cmdValid = true, cmdReady = true, rspValid = true)
//    interconnect.setPipelining(core.cpu.iBus)(cmdHalfRate = true, rspValid = true)
//    interconnect.setPipelining(iArbiter.bmb)(cmdHalfRate = true, rspValid = true)
//  }
//  interconnect.setPipelining(invalidationMonitor.output)(cmdValid = true, cmdReady = true, rspValid = true)
//  interconnect.setPipelining(peripheralBridge.bmb)(cmdHalfRate = true, rspValid = true)
//}
//
//
//object VexRiscvLitexSmpMpClusterGen extends App {
//  for(cpuCount <- List(1,2,4,8)) {
//    def parameter = VexRiscvLitexSmpMpClusterParameter(
//      cluster = VexRiscvSmpClusterParameter(
//        cpuConfigs = List.tabulate(cpuCount) { hartId =>
//          vexRiscvConfig(
//            hartId = hartId,
//            ioRange = address => address.msb,
//            resetVector = 0
//          )
//        }
//      ),
//      liteDram = LiteDramNativeParameter(addressWidth = 32, dataWidth = 128),
//      liteDramMapping = SizeMapping(0x40000000l, 0x40000000l)
//    )
//
//    def dutGen = {
//      val toplevel = new VexRiscvLitexSmpMpCluster(
//        p = parameter
//      ).toComponent()
//      toplevel
//    }
//
//    val genConfig = SpinalConfig().addStandardMemBlackboxing(blackboxByteEnables)
//    //  genConfig.generateVerilog(Bench.compressIo(dutGen))
//    genConfig.generateVerilog(dutGen.setDefinitionName(s"VexRiscvLitexSmpMpCluster_${cpuCount}c"))
//  }
//}



//
////addAttribute("""mark_debug = "true"""")
//class VexRiscvLitexSmpMpCluster(val p : VexRiscvLitexSmpMpClusterParameter,
//                                val debugClockDomain : ClockDomain,
//                                val jtagClockDomain : ClockDomain) extends Component{
//
//  val peripheralWishboneConfig = WishboneConfig(
//    addressWidth = 30,
//    dataWidth = 32,
//    selWidth = 4,
//    useERR = true,
//    useBTE = true,
//    useCTI = true
//  )
//
//  val cpuCount = p.cluster.cpuConfigs.size
//
//  val io = new Bundle {
//    val dMem = Vec(master(LiteDramNative(p.liteDram)), cpuCount)
//    val iMem =  Vec(master(LiteDramNative(p.liteDram)), cpuCount)
//    val peripheral = master(Wishbone(peripheralWishboneConfig))
//    val clint = slave(Wishbone(Clint.getWisboneConfig()))
//    val plic  = slave(Wishbone(WishboneConfig(addressWidth = 20, dataWidth = 32)))
//    val interrupts = in Bits(32 bits)
//    val jtagInstruction = slave(JtagTapInstructionCtrl())
//    val debugReset = out Bool()
//  }
//  val clint = Clint(cpuCount)
//  clint.driveFrom(WishboneSlaveFactory(io.clint))
//
//  val cluster = VexRiscvSmpCluster(p.cluster, debugClockDomain)
//  cluster.io.debugReset <> io.debugReset
//  cluster.io.timerInterrupts <> B(clint.harts.map(_.timerInterrupt))
//  cluster.io.softwareInterrupts <> B(clint.harts.map(_.softwareInterrupt))
//  cluster.io.time := clint.time
//
//  val debug = debugClockDomain on new Area{
//    val jtagConfig = SystemDebuggerConfig()
//
//    val jtagBridge = new JtagBridgeNoTap(jtagConfig, jtagClockDomain)
//    jtagBridge.io.ctrl << io.jtagInstruction
//
//    val debugger = new SystemDebugger(jtagConfig)
//    debugger.io.remote <> jtagBridge.io.remote
//
//    cluster.io.debugBus << debugger.io.mem.toBmb()
//
////    io.jtagInstruction.allowDirectionLessIo.setAsDirectionLess
////    val bridge = Bscane2BmbMaster(1)
////    cluster.io.debugBus << bridge.io.bmb
//
//
////    val bscane2 = BSCANE2(usedId)
////    val jtagClockDomain = ClockDomain(bscane2.TCK)
////
////    val jtagBridge = new JtagBridgeNoTap(jtagConfig, jtagClockDomain)
////    jtagBridge.io.ctrl << bscane2.toJtagTapInstructionCtrl()
////
////    val debugger = new SystemDebugger(jtagConfig)
////    debugger.io.remote <> jtagBridge.io.remote
////
////    io.bmb << debugger.io.mem.toBmb()
//  }
//
//  val dBusDecoder = BmbDecoderOutOfOrder(
//    p            = cluster.io.dMem.p,
//    mappings     = Seq(DefaultMapping, p.liteDramMapping),
//    capabilities = Seq(cluster.io.dMem.p, cluster.io.dMem.p),
//    pendingRspTransactionMax = 32
//  )
////  val dBusDecoder = BmbDecoderOut(
////    p            = cluster.io.dMem.p,
////    mappings     = Seq(DefaultMapping, p.liteDramMapping),
////    capabilities = Seq(cluster.io.dMem.p, cluster.io.dMem.p),
////    pendingMax = 31
////  )
//  dBusDecoder.io.input << cluster.io.dMem.pipelined(cmdValid = true, cmdReady = true, rspValid = true)
//
//
//  val perIBus = for(id <- 0 until cpuCount) yield new Area{
//    val decoder = BmbDecoder(
//      p            = cluster.io.iMems(id).p,
//      mappings     = Seq(DefaultMapping, p.liteDramMapping),
//      capabilities = Seq(cluster.io.iMems(id).p,cluster.io.iMems(id).p),
//      pendingMax   = 15
//    )
//
//    decoder.io.input << cluster.io.iMems(id)
//    io.iMem(id).fromBmb(decoder.io.outputs(1).pipelined(cmdHalfRate = true), wdataFifoSize = 0, rdataFifoSize = 32)
//    val toPeripheral = decoder.io.outputs(0).resize(dataWidth = 32).pipelined(cmdHalfRate = true, rspValid = true)
//  }
//
//  val dBusDecoderToPeripheral = dBusDecoder.io.outputs(0).resize(dataWidth = 32).pipelined(cmdHalfRate = true, rspValid = true)
//
//  val peripheralAccessLength = Math.max(perIBus(0).toPeripheral.p.lengthWidth, dBusDecoder.io.outputs(0).p.lengthWidth)
//  val peripheralArbiter = BmbArbiter(
//    p = dBusDecoder.io.outputs(0).p.copy(
//      sourceWidth = List(perIBus(0).toPeripheral, dBusDecoderToPeripheral).map(_.p.sourceWidth).max + log2Up(cpuCount + 1),
//      contextWidth = List(perIBus(0).toPeripheral, dBusDecoderToPeripheral).map(_.p.contextWidth).max,
//      lengthWidth = peripheralAccessLength,
//      dataWidth = 32
//    ),
//    portCount = cpuCount+1,
//    lowerFirstPriority = true
//  )
//
//  for(id <- 0 until cpuCount){
//    peripheralArbiter.io.inputs(id) << perIBus(id).toPeripheral
//  }
//  peripheralArbiter.io.inputs(cpuCount) << dBusDecoderToPeripheral
//
//  val peripheralWishbone = peripheralArbiter.io.output.pipelined(cmdValid = true).toWishbone()
//  io.peripheral << peripheralWishbone
//
//
//  val dBusDemux = BmbSourceDecoder(dBusDecoder.io.outputs(1).p)
//  dBusDemux.io.input << dBusDecoder.io.outputs(1).pipelined(cmdValid = true, cmdReady = true,rspValid = true)
//  val dMemBridge = for(id <- 0 until cpuCount)  yield {
//    io.dMem(id).fromBmb(dBusDemux.io.outputs(id), wdataFifoSize = 32, rdataFifoSize = 32)
//  }
//
//
//  val plic = new Area{
//    val priorityWidth = 2
//
//    val gateways = for(i <- 1 until 32) yield PlicGatewayActiveHigh(
//      source = io.interrupts(i),
//      id = i,
//      priorityWidth = priorityWidth
//    )
//
//    val bus = WishboneSlaveFactory(io.plic)
//
//    val targets = for(i <- 0 until cpuCount) yield new Area{
//      val machine = PlicTarget(
//        gateways = gateways,
//        priorityWidth = priorityWidth
//      )
//      val supervisor = PlicTarget(
//        gateways = gateways,
//        priorityWidth = priorityWidth
//      )
//
//      cluster.io.externalInterrupts(i) := machine.iep
//      cluster.io.externalSupervisorInterrupts(i) := supervisor.iep
//    }
//
//    val bridge = PlicMapper(bus, PlicMapping.sifive)(
//      gateways = gateways,
//      targets = targets.flatMap(t => List(t.machine, t.supervisor))
//    )
//  }
////
////  io.dMem.foreach(_.cmd.valid.addAttribute("""mark_debug = "true""""))
////  io.dMem.foreach(_.cmd.ready.addAttribute("""mark_debug = "true""""))
////  io.iMem.foreach(_.cmd.valid.addAttribute("""mark_debug = "true""""))
////  io.iMem.foreach(_.cmd.ready.addAttribute("""mark_debug = "true""""))
////
////  cluster.io.dMem.cmd.valid.addAttribute("""mark_debug = "true"""")
////  cluster.io.dMem.cmd.ready.addAttribute("""mark_debug = "true"""")
////  cluster.io.dMem.rsp.valid.addAttribute("""mark_debug = "true"""")
////  cluster.io.dMem.rsp.ready.addAttribute("""mark_debug = "true"""")
//}
//
//object VexRiscvLitexSmpMpClusterGen extends App {
//  for(cpuCount <- List(1,2,4,8)) {
//    def parameter = VexRiscvLitexSmpMpClusterParameter(
//      cluster = VexRiscvSmpClusterParameter(
//        cpuConfigs = List.tabulate(cpuCount) { hartId =>
//          vexRiscvConfig(
//            hartId = hartId,
//            ioRange = address => address.msb,
//            resetVector = 0
//          )
//        }
//      ),
//      liteDram = LiteDramNativeParameter(addressWidth = 32, dataWidth = 128),
//      liteDramMapping = SizeMapping(0x40000000l, 0x40000000l)
//    )
//
//    def dutGen = {
//      val toplevel = new VexRiscvLitexSmpMpCluster(
//        p = parameter,
//        debugClockDomain = ClockDomain.current.copy(reset = Bool().setName("debugResetIn")),
//        jtagClockDomain = ClockDomain.external("jtag", withReset = false)
//      )
//      toplevel
//    }
//
//    val genConfig = SpinalConfig().addStandardMemBlackboxing(blackboxByteEnables)
//    //  genConfig.generateVerilog(Bench.compressIo(dutGen))
//    genConfig.generateVerilog(dutGen.setDefinitionName(s"VexRiscvLitexSmpMpCluster_${cpuCount}c"))
//  }
//
//}
//
//
//object VexRiscvLitexSmpMpClusterOpenSbi extends App{
//  import spinal.core.sim._
//
//  val simConfig = SimConfig
//  simConfig.withWave
//  simConfig.withFstWave
//  simConfig.allOptimisation
//
//  val cpuCount = 2
//
//  def parameter = VexRiscvLitexSmpMpClusterParameter(
//    cluster = VexRiscvSmpClusterParameter(
//      cpuConfigs = List.tabulate(cpuCount) { hartId =>
//        vexRiscvConfig(
//          hartId = hartId,
//          ioRange =  address => address(31 downto 28) === 0xF,
//          resetVector = 0x80000000l
//        )
//      }
//    ),
//    liteDram = LiteDramNativeParameter(addressWidth = 32, dataWidth = 128),
//    liteDramMapping = SizeMapping(0x80000000l, 0x70000000l)
//  )
//
//  def dutGen = {
//    val top = new VexRiscvLitexSmpMpCluster(
//      p = parameter,
//      debugClockDomain = ClockDomain.current.copy(reset = Bool().setName("debugResetIn")),
//      jtagClockDomain = ClockDomain.external("jtag", withReset = false)
//    ){
//      io.jtagInstruction.allowDirectionLessIo.setAsDirectionLess
//      val jtag = slave(Jtag())
//      jtagClockDomain.readClockWire.setAsDirectionLess() := jtag.tck
//      val jtagLogic = jtagClockDomain on new Area{
//        val tap = new JtagTap(jtag, 4)
//        val idcodeArea = tap.idcode(B"x10001FFF")(1)
//        val wrapper = tap.map(io.jtagInstruction, instructionId = 2)
//      }
//    }
//    top.rework{
//      top.io.clint.setAsDirectionLess.allowDirectionLessIo
//      top.io.peripheral.setAsDirectionLess.allowDirectionLessIo.simPublic()
//
//      val hit = (top.io.peripheral.ADR <<2 >= 0xF0010000l && top.io.peripheral.ADR<<2 < 0xF0020000l)
//      top.io.clint.CYC := top.io.peripheral.CYC && hit
//      top.io.clint.STB := top.io.peripheral.STB
//      top.io.clint.WE := top.io.peripheral.WE
//      top.io.clint.ADR := top.io.peripheral.ADR.resized
//      top.io.clint.DAT_MOSI := top.io.peripheral.DAT_MOSI
//      top.io.peripheral.DAT_MISO := top.io.clint.DAT_MISO
//      top.io.peripheral.ACK := top.io.peripheral.CYC  && (!hit || top.io.clint.ACK)
//      top.io.peripheral.ERR := False
//
////        top.dMemBridge.unburstified.cmd.simPublic()
//    }
//    top
//  }
//  simConfig.compile(dutGen).doSimUntilVoid(seed = 42){dut =>
//    dut.clockDomain.forkStimulus(10)
//    fork {
//      dut.debugClockDomain.resetSim #= false
//      sleep (0)
//      dut.debugClockDomain.resetSim #= true
//      sleep (10)
//      dut.debugClockDomain.resetSim #= false
//    }
//
//    JtagTcp(dut.jtag, 10*20)
//
//    val ram = SparseMemory()
//    ram.loadBin(0x80000000l, "../opensbi/build/platform/spinal/vexriscv/sim/smp/firmware/fw_jump.bin")
//    ram.loadBin(0xC0000000l, "../buildroot/output/images/Image")
//    ram.loadBin(0xC1000000l, "../buildroot/output/images/dtb")
//    ram.loadBin(0xC2000000l, "../buildroot/output/images/rootfs.cpio")
//
//    for(id <- 0 until cpuCount) {
//      dut.io.iMem(id).simSlave(ram, dut.clockDomain)
//      dut.io.dMem(id).simSlave(ram, dut.clockDomain)
//    }
//
//    dut.io.interrupts #= 0
//
//
////      val stdin = mutable.Queue[Byte]()
////      def stdInPush(str : String) = stdin ++= str.toCharArray.map(_.toByte)
////      fork{
////        sleep(4000*1000000l)
////        stdInPush("root\n")
////        sleep(1000*1000000l)
////        stdInPush("ping localhost -i 0.01 > /dev/null &\n")
////        stdInPush("ping localhost -i 0.01 > /dev/null &\n")
////        stdInPush("ping localhost -i 0.01 > /dev/null &\n")
////        stdInPush("ping localhost -i 0.01 > /dev/null &\n")
////        sleep(500*1000000l)
////        while(true){
////          sleep(500*1000000l)
////          stdInPush("uptime\n")
////          printf("\n** uptime **")
////        }
////      }
//    dut.clockDomain.onFallingEdges {
//      if (dut.io.peripheral.CYC.toBoolean) {
//        (dut.io.peripheral.ADR.toLong << 2) match {
//          case 0xF0000000l => print(dut.io.peripheral.DAT_MOSI.toLong.toChar)
//          case 0xF0000004l => dut.io.peripheral.DAT_MISO #= (if (System.in.available() != 0) System.in.read() else 0xFFFFFFFFl)
//          case _ =>
//          //            case 0xF0000004l => {
//          //              val c = if(stdin.nonEmpty) {
//          //                stdin.dequeue().toInt & 0xFF
//          //              } else {
//          //                0xFFFFFFFFl
//          //              }
//          //              dut.io.peripheral.DAT_MISO #= c
//          //            }
//          //            case _ =>
//          //          }
//          //          println(f"${dut.io.peripheral.ADR.toLong}%x")
//        }
//      }
//    }
//
//    fork{
//      val at = 0
//      val duration = 1000
//      while(simTime() < at*1000000l) {
//        disableSimWave()
//        sleep(100000 * 10)
//        enableSimWave()
//        sleep(  200 * 10)
//      }
//      println("\n\n********************")
//      sleep(duration*1000000l)
//      println("********************\n\n")
//      while(true) {
//        disableSimWave()
//        sleep(100000 * 10)
//        enableSimWave()
//        sleep(  400 * 10)
//      }
//    }
//  }
//}
