SpinalHDL / NaxRiscv

MIT License
260 stars 39 forks source link

Problem with build a soc with nax in spinal #32

Closed zyn810039594 closed 1 year ago

zyn810039594 commented 1 year ago

Hi^^ I want to build a project with NaxRiscv.I refer to existing works(LiteX and single core generation), and I build a soc like this, with my qspi controller and uart:

class NaxSoc() extends Component {
    val io = new Bundle {
        val debug        = slave(Jtag())
        val debug_resetn = in Bool ()
        val uart         = master(Uart())
        val qspi         = master(Qspi())
    }
    private def priv_port(cpu: NaxRiscv)        = cpu.framework.getService[PrivilegedPlugin].io
    def int_mtimer(cpu: NaxRiscv)               = priv_port(cpu).int.machine.timer
    def int_msoftware(cpu: NaxRiscv)            = priv_port(cpu).int.machine.software
    def int_mexternal(cpu: NaxRiscv)            = priv_port(cpu).int.machine.external
    def int_sexternal(cpu: NaxRiscv)            = priv_port(cpu).int.supervisor.external
    def int_rdtime(cpu: NaxRiscv)               = priv_port(cpu).rdtime
    def dbg_jtag(cpu: NaxRiscv)                 = cpu.framework.getService[EmbeddedJtagPlugin].logic.jtag
//FetchAxi4Parrot split a single axi4 to double axi4, modified from FetchAxi4 plugin
    def inst_main(cpu: NaxRiscv)                = cpu.framework.getService[FetchAxi4Parrot].logic.axiRam
    def inst_perp(cpu: NaxRiscv)                = cpu.framework.getService[FetchAxi4Parrot].logic.axiPeripheral
    def data_ram(cpu: NaxRiscv)                 = cpu.framework.getService[DataCacheAxi4].logic.axi
    def data_perp(cpu: NaxRiscv)                = cpu.framework.getService[LsuPeripheralAxi4].logic.axi

    io.debug.setName("jtag")
    io.uart.setName("uart")
    io.qspi.setName("qspi")
    io.debug_resetn.setName("jtag_rstn")
    val core_cfg = initCfg(
      resetVector = 0x00000000L,
      ioRange = _(31 downto 28) === 0x2,
      fetchRange = _(31 downto 28) =/= 0x2
    )
    core_cfg.foreach {
        case p: EmbeddedJtagPlugin => p.debugCd.load(ClockDomain.current.copy(reset = io.debug_resetn))
        case _                     =>
    }
    val core = new NaxRiscv(core_cfg)

    Axi4SpecRenamer(inst_main(core))
    Axi4SpecRenamer(data_ram(core))
    Axi4SpecRenamer(inst_perp(core))
    Axi4SpecRenamer(data_perp(core))

    io.debug <> dbg_jtag(core)

//Just for test, set all INT to zero
    int_mtimer(core).clear()
    int_msoftware(core).clear()
    int_mexternal(core).clear()
    int_sexternal(core).clear()
    int_rdtime(core).clearAll()
//A qspi controller with two port,axi4 for reading(at address mapping mode,but read only),axilite4 for config and writing
    val qspi_flash = new myQspiTop(
      inst_port_cfg = Axi4Config(addressWidth = 32, dataWidth = 64, idWidth = 2),
      perp_port_cfg = AxiLite4Config(addressWidth = 32, dataWidth = 32)
    )

    val qspi_flash_perp = Axi4(Axi4Config(addressWidth = 32, dataWidth = 32, idWidth = 1))
    qspi_flash.io.perp_port << qspi_flash_perp.toLite

    val ram = new Axi4SharedOnChipRam(dataWidth = 64, byteCount = 256, idWidth = 2, arwStage = true)

    val main_bus = Axi4CrossbarFactory()
    main_bus.addSlaves(
      ram.io.axi -> (0x10000000L,16 MiB)
    )
    main_bus.addConnections(
        inst_main(core) -> List(ram.io.axi),
        data_ram(core) -> List(ram.io.axi)
    )
    main_bus.build()
    val perp_stor_bus = Axi4CrossbarFactory()
    perp_stor_bus.addSlaves(
        qspi_flash.io.inst_port -> (0x00000000L, 16 MiB)
    )
    perp_stor_bus.addConnections(
      inst_perp(core) -> List(qspi_flash.io.inst_port)
    )
    perp_stor_bus.build()

//A simple uart controller with apb bus
    val uart            = new myUartCore()
    val uart_apb_master = Apb3(5, 32)
//Just change the datawidth from 32 to 8,nothing else
    resizeConnect(slave = uart.io.apb, master = uart_apb_master)

    val perpApb = Axi4SharedToApb3Bridge(
      addressWidth = 20,
      dataWidth = 32,
      idWidth = 1
    )
    val perp_bus = Axi4CrossbarFactory()
    perp_bus.addSlaves(
      qspi_flash_perp -> (0x50000000L, 1 KiB),
      perpApb.io.axi  -> (0x20000000L, 16 KiB)
    )
    perp_bus.addConnections(
      data_perp(core) -> List(qspi_flash_perp, perpApb.io.axi)
    )
    perp_bus.build()
    val apbDecoder = Apb3Decoder(
      master = perpApb.io.apb,
      slaves = List(
        uart_apb_master -> (0x00000, 4 KiB)
      )
    )
    io.uart <> uart.io.uart
    io.qspi <> qspi_flash.io.qspi
}

I tried to simulate it in IVerilog with these code:

object socTest extends App {
//A code to load bin file for qspi flash model,and it works well i think.
    val qspi_file = qspiRedirect.startSim("../../bin/test.bin")
    class sim_soc extends Component {
        val io = new Bundle {
            val debug        = slave(Jtag())
            val debug_resetn = in Bool ()
            val uart         = master(Uart())
        }
        val soc = new NaxSoc()
        soc.io.debug <> io.debug
        soc.io.debug_resetn <> io.debug_resetn
        soc.io.uart <> io.uart

        val qspi_flash = new qspiModel

        soc.io.qspi.csn <> qspi_flash.io.CSn
        soc.io.qspi.sck <> qspi_flash.io.CLK

        soc.io.qspi.dout(0) <> qspi_flash.io.DIO.write
        soc.io.qspi.dout(1) <> qspi_flash.io.DO.write
        soc.io.qspi.dout(2) <> qspi_flash.io.WPn.write
        soc.io.qspi.dout(3) <> qspi_flash.io.HOLDn.write

        soc.io.qspi.dout_en(0) <> qspi_flash.io.DIO.writeEnable
        soc.io.qspi.dout_en(1) <> qspi_flash.io.DO.writeEnable
        soc.io.qspi.dout_en(2) <> qspi_flash.io.WPn.writeEnable
        soc.io.qspi.dout_en(3) <> qspi_flash.io.HOLDn.writeEnable

        soc.io.qspi.din(0) <> qspi_flash.io.DIO.read
        soc.io.qspi.din(1) <> qspi_flash.io.DO.read
        soc.io.qspi.din(2) <> qspi_flash.io.WPn.read
        soc.io.qspi.din(3) <> qspi_flash.io.HOLDn.read
    }
    def simCfg = {
        val cfg = SpinalConfig(
          nameWhenByFile = true,
          inlineRom = true,
          targetDirectory = "./gen",
          defaultConfigForClockDomains = ClockDomainConfig(resetActiveLevel = LOW)
        )
        cfg.addTransformationPhase(new MemReadDuringWriteHazardPhase)
        cfg.addTransformationPhase(new MemReadDuringWritePatcherPhase)
        cfg.addTransformationPhase(new MultiPortWritesSymplifier)
        cfg
    }
   SimConfig.withConfig(spinal).withIVerilog.withWave.compile(new sim_soc).doSimUntilVoid { dut =>
        dut.clockDomain.forkStimulus(period = 10000)
        SimTimeout(10000*100000)
    }
}

I don't know if there're any errors in these code, but it could not work in simulation, fetchcache read two line, and dead. At first, I think it's the fault of the code. But I can simulate the code well just with the single Nax core(copy the verilog code to a questasim project and simulate with systemverilog).and I add a line in the simCfg:cfg.includeSimulation.Ok, with this command, fetchcache read four line!(and dead). Both of them have no response on datacache and lsu port, their addr ports are always X.

So my problem is:

  1. Is there any problem with the soc I build and simulation project?What problems could they have?
  2. For includeSimulation and its cousins includeSynthesis ,what kind of roles do they play in this project and spinalhdl?

Thanks for your help!

zyn810039594 commented 1 year ago

My fault,sorry!

Dolu1990 commented 1 year ago

Hi ^^

Ahhh no worries :) What was wrong ?

For includeSimulation and its cousins includeSynthesis ,what kind of roles do they play in this project and spinalhdl?

So in the design, the user can do :

if(GenerationFlags.simulation){

}

To conditionaly add something in the netlit if includeSimulation was specified for instance. That's a bit like a #ifdef

Regards Charles