package spinal.lib.eda.altera

import spinal.core._
import spinal.lib.bus.amba3.apb.Apb3
import spinal.lib.bus.avalon.AvalonMM
import spinal.lib.bus.amba4.axi.Axi4

import scala.collection.mutable
import scala.collection.mutable.StringBuilder
import spinal.lib.bus.amba4.axilite.AxiLite4

object QSysify{
  def apply(that : Component, filePath : String = "") : Unit = {
    val tool = new QSysify(filePath)
    tool.interfaceEmiters += new AvalonEmitter()
    tool.interfaceEmiters += new ApbEmitter()
    tool.interfaceEmiters += new Axi4Emitter()
    tool.interfaceEmiters += new AxiLite4Emitter()
    tool.interfaceEmiters += new ClockDomainEmitter()
    tool.interfaceEmiters += new ResetEmitterEmitter()
    tool.interfaceEmiters += new InterruptEmitter()
    tool.interfaceEmiters += new ConduitEmitter()

    tool.emit(that)
  }
}

trait QSysifyInterfaceEmiter{
  //Check the 'i' interface, if it recognize something, should return true and complete the TCL `builder` with corresponding things.
  def emit(i : Data,builder : StringBuilder) : Boolean
}

class QSysify(filePath : String) {
  val interfaceEmiters = mutable.ArrayBuffer[QSysifyInterfaceEmiter]()
  def emit(that : Component) {
    val name = that.definitionName
    var out: java.io.FileWriter = null
    out = new java.io.FileWriter(filePath + name + "_hw.tcl")
    val builder = new StringBuilder()

    genHeader()
    genInterfaces()

    out.write(builder.toString())
    out.flush();
    out.close();



    def genHeader(): Unit = {
      builder ++= s"""
  |package require -exact qsys 13.1
  |
  |#
  |# module def
  |#
  |set_module_property DESCRIPTION ""
  |set_module_property NAME ${name}
  |set_module_property VERSION 1.0
  |set_module_property INTERNAL false
  |set_module_property OPAQUE_ADDRESS_MAP true
  |set_module_property AUTHOR ""
  |set_module_property DISPLAY_NAME $name
  |set_module_property INSTANTIATE_IN_SYSTEM_MODULE true
  |set_module_property EDITABLE false
  |set_module_property ANALYZE_HDL false
  |set_module_property REPORT_TO_TALKBACK false
  |set_module_property ALLOW_GREYBOX_GENERATION false
  |
  |#
  |# file sets
  |#
  |add_fileset QUARTUS_SYNTH QUARTUS_SYNTH "" ""
  |set_fileset_property QUARTUS_SYNTH TOP_LEVEL $name
  |set_fileset_property QUARTUS_SYNTH ENABLE_RELATIVE_INCLUDE_PATHS false
  |#add_fileset_file ${name}.vhd VHDL PATH ${name}.vhd TOP_LEVEL_FILE
  |
  |add_fileset SIM_VHDL SIM_VHDL "" ""
  |set_fileset_property SIM_VHDL TOP_LEVEL $name
  |set_fileset_property SIM_VHDL ENABLE_RELATIVE_INCLUDE_PATHS false
  |#add_fileset_file $name.vhd VHDL PATH $name.vhd
  |
  |""".stripMargin
    }
    def genInterfaces() = {
      for(i <- that.getGroupedIO(true)){
        var find = false
        for(emiter <- interfaceEmiters){
          if(!find && emiter.emit(i,builder)){
            assert(!find)
            find = true
          }
        }
        assert(find, "Can't map interface " + i)
      }
    }
  }
}


class ClockDomainEmitter extends QSysifyInterfaceEmiter {
  override def emit(i: Data, builder: StringBuilder): Boolean = i match {
    case i : Bool =>{
      val driver = ClockDomain.getClockDomainDriver(i)
      val tag = ClockDomain.getClockDomainTag(i)
      if(driver == null) return false
      val name = driver.getName()
      val interfaceName = name
      tag match{
        case tag : ClockTag => {
          builder ++=
            s"""
               |#
               |# connection point $interfaceName
               |#
               |add_interface $interfaceName clock end
               |set_interface_property $interfaceName clockRate 0
               |set_interface_property $interfaceName ENABLED true
               |set_interface_property $interfaceName EXPORT_OF ""
               |set_interface_property $interfaceName PORT_NAME_MAP ""
               |set_interface_property $interfaceName SVD_ADDRESS_GROUP ""
               |
               |add_interface_port $interfaceName $name clk Input 1
             """.stripMargin
          true
        }
        case tag : ResetTag => {
          val resetType = if (tag.clockDomain.config.resetActiveLevel == HIGH) "reset" else "reset_n"
          builder ++=
            s"""
              |#
              |# connection point $interfaceName
              |#
              |add_interface $interfaceName reset end
              |set_interface_property $interfaceName associatedClock ${tag.clockDomain.clock.getName()}
              |set_interface_property $interfaceName synchronousEdges DEASSERT
              |set_interface_property $interfaceName ENABLED true
              |set_interface_property $interfaceName EXPORT_OF ""
              |set_interface_property $interfaceName PORT_NAME_MAP ""
              |set_interface_property $interfaceName SVD_ADDRESS_GROUP ""
              |
              |add_interface_port $interfaceName $name $resetType Input 1
             """.stripMargin
          true
        }
        case tag : ClockEnableTag => SpinalError("Can't map clock enable in QSys")
        case _ => false
      }
    }
    case _ => false
  }
}

class AvalonEmitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data,builder : StringBuilder): Boolean = i match {
    case e: AvalonMM =>{
      import e.config._
      val isMaster = e.address.isOutput
      val (masterPinDir,slavePinDir,startEnd) = if(isMaster) ("Output", "Input","start") else ("Input","Output","end")
      val name = e.getName()
      val clockDomainTag = e.getTag(classOf[ClockDomainTag])
      if(clockDomainTag.isEmpty) SpinalError(s"Clock domain of ${i} is not defined, You should apply the ClockDomainTag to the interface\nyourBus.addTag(ClockDomainTag(ClockDomain.current))")
      val clockName = clockDomainTag.get.clockDomain.clock.getName()
      val resetName = clockDomainTag.get.clockDomain.reset.getName()
      builder ++= s"""
|#
|# connection point $name
|#
|add_interface $name avalon $startEnd
|set_interface_property $name addressUnits ${addressUnits.getName.toUpperCase}
|set_interface_property $name burstcountUnits ${burstCountUnits.getName.toUpperCase}
|set_interface_property $name burstOnBurstBoundariesOnly ${burstOnBurstBoundariesOnly}
|set_interface_property $name constantBurstBehavior ${constantBurstBehavior}
|set_interface_property $name holdTime ${holdTime}
|set_interface_property $name linewrapBursts ${linewrapBursts}
|set_interface_property $name maximumPendingReadTransactions ${maximumPendingReadTransactions}
|set_interface_property $name maximumPendingWriteTransactions ${maximumPendingWriteTransactions}
|set_interface_property $name readLatency ${readLatency}
|set_interface_property $name readWaitTime ${readWaitTime}
|set_interface_property $name setupTime ${setupTime}
|set_interface_property $name writeWaitTime ${writeWaitTime}
|set_interface_property $name holdTime ${holdTime}
|
|set_interface_property $name associatedClock $clockName
|set_interface_property $name associatedReset $resetName
|set_interface_property $name bitsPerSymbol 8
|
|set_interface_property $name timingUnits Cycles
|set_interface_property $name ENABLED true
|set_interface_property $name EXPORT_OF ""
|set_interface_property $name PORT_NAME_MAP ""
|set_interface_property $name SVD_ADDRESS_GROUP ""
|
""".stripMargin

      if(isMaster){
        builder ++= s"set_interface_property $name doStreamReads false\n"
        builder ++= s"set_interface_property $name doStreamWrites false\n"
      }

      builder ++= s"add_interface_port $name ${e.address.getName()} address ${masterPinDir} ${addressWidth}\n"
      if(useRead) builder ++= s"add_interface_port $name ${e.read.getName()} read ${masterPinDir} 1\n"
      if(useWrite) builder ++= s"add_interface_port $name ${e.write.getName()} write ${masterPinDir} 1\n"
      if(useWaitRequestn) builder ++= s"add_interface_port $name ${e.waitRequestn.getName()} waitrequest_n ${slavePinDir} 1\n"
      if(useLock) builder ++= s"add_interface_port $name ${e.lock.getName()} lock ${masterPinDir} 1\n"
      if(useBurstCount) builder ++= s"add_interface_port $name ${e.burstCount.getName()} burstcount ${masterPinDir} ${burstCountWidth}\n"
      if(useByteEnable) builder ++= s"add_interface_port $name ${e.byteEnable.getName()} byteenable ${masterPinDir} ${dataByteCount}\n"
      if(useWrite) builder ++= s"add_interface_port $name ${e.writeData.getName()} writedata ${masterPinDir} ${dataWidth}\n"
      if(useResponse) builder ++= s"add_interface_port $name ${e.response.getName()} response ${slavePinDir} 2\n"
      if(useReadDataValid) builder ++= s"add_interface_port $name ${e.readDataValid.getName()} readdatavalid ${slavePinDir} 1\n"
    //  if(useEndOfPacket) builder ++= s"add_interface_port $name ${e.endOfPacket.getName()} endofpacket ${slavePinDir} 1\n"
      if(useRead) builder ++= s"add_interface_port $name ${e.readData.getName()} readdata ${slavePinDir} ${dataWidth}\n"
      if(useDebugAccess)
        ???
      true
      }
    case _ => false
  }
}


class ConduitEmitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data, builder: scala.StringBuilder): Boolean = {
    val name = i.getName()
    builder ++=
      s"""
         |#
         |# connection point $name
         |#
         |add_interface $name conduit end
         |set_interface_property $name associatedClock ""
         |set_interface_property $name associatedReset ""
         |set_interface_property $name ENABLED true
         |set_interface_property $name EXPORT_OF ""
         |set_interface_property $name PORT_NAME_MAP ""
         |set_interface_property $name SVD_ADDRESS_GROUP ""
         |""".stripMargin

    for(e <- i.flatten){
      val dirStr = if(e.isOutput) "Output"
      else if(e.isInput) "Input"
      else null;
      val width = e match {
        case e : BitVector => e.getWidth
        case e : Bool => 1
      }
      if(dirStr != null)
        builder ++= s"add_interface_port $name ${e.getName()} export ${dirStr} ${width}\n"
    }
    builder ++= "\n\n"
    true
  }
}

case class InterruptTag(clockDomain : ClockDomain, addressablePoint : Data) extends SpinalTag
//To not break old API :
object InterruptReceiverTag{
  def apply(addressablePoint : Data, clockDomain : ClockDomain) : InterruptTag = InterruptTag(clockDomain, addressablePoint)
}
object InterruptSenderTag{
  def apply(addressablePoint : Data, clockDomain : ClockDomain) : InterruptTag = InterruptTag(clockDomain, addressablePoint)
}
object InterruptTag{
  def apply(addressablePoint : Data, clockDomain : ClockDomain) : InterruptTag = InterruptTag(clockDomain, addressablePoint)
}

class InterruptEmitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data, builder: scala.StringBuilder): Boolean = {
    val tag = i.getTag(classOf[InterruptTag])
    if (tag.isEmpty) return false
    val interfaceName = i.getName()
    val name = i.getName()
    builder ++=
      s"""
         |#
         |# connection point $interfaceName
         |#
         |add_interface $interfaceName interrupt ${if (i.isInput) "start" else "end"}
         |set_interface_property $interfaceName associatedAddressablePoint ${tag.get.addressablePoint.getName()}
         |set_interface_property $interfaceName associatedClock ${tag.get.clockDomain.clock.getName()}
         |set_interface_property $interfaceName associatedReset ${tag.get.clockDomain.reset.getName()}
         |set_interface_property $interfaceName irqScheme INDIVIDUAL_REQUESTS
         |set_interface_property $interfaceName ENABLED true
         |set_interface_property $interfaceName EXPORT_OF ""
         |set_interface_property $interfaceName PORT_NAME_MAP ""
         |set_interface_property $interfaceName SVD_ADDRESS_GROUP ""
         |
         |add_interface_port $interfaceName $name irq ${if (i.isInput) "Input" else "Output"} ${i.getBitsWidth}
         |""".stripMargin
    true
  }
}

case class ResetEmitterTag(associatedClock : ClockDomain) extends SpinalTag

class ResetEmitterEmitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data, builder: scala.StringBuilder): Boolean = {
    val tag = i.getTag(classOf[ResetEmitterTag])
    if(tag.isEmpty) return false
    val interfaceName = i.getName()
    val name = i.getName()

    builder ++=
      s"""
         |#
         |# connection point $interfaceName
         |#
         |add_interface $interfaceName reset start
         |set_interface_property $interfaceName associatedClock ${tag.get.associatedClock.clock.getName()}
         |set_interface_property $interfaceName associatedDirectReset ""
         |set_interface_property $interfaceName associatedResetSinks ""
         |set_interface_property $interfaceName synchronousEdges DEASSERT
         |set_interface_property $interfaceName ENABLED true
         |set_interface_property $interfaceName EXPORT_OF ""
         |set_interface_property $interfaceName PORT_NAME_MAP ""
         |set_interface_property $interfaceName SVD_ADDRESS_GROUP ""
         |
         |add_interface_port $interfaceName $name reset Output 1
          |""".stripMargin
    true
  }
}



class ApbEmitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data,builder : StringBuilder): Boolean = i match {
    case e: Apb3 =>{
      import e.config._
      val isMaster = e.PADDR.isOutput
      val (masterPinDir,slavePinDir,startEnd) = if(isMaster) ("Output", "Input","start") else ("Input","Output","end")
      val name = e.getName()
      val clockDomainTag = e.getTag(classOf[ClockDomainTag])
      if(clockDomainTag.isEmpty) SpinalError(s"Clock domain of ${i} is not defined, You should apply the ClockDomainTag to the interface\nyourBus.addTag(ClockDomainTag(ClockDomain.current))")
      val clockName = clockDomainTag.get.clockDomain.clock.getName()
      val resetName = clockDomainTag.get.clockDomain.reset.getName()
      builder ++=
s"""
#
# connection point $name
#
add_interface $name apb $startEnd
set_interface_property $name ENABLED true
set_interface_property $name EXPORT_OF ""
set_interface_property $name PORT_NAME_MAP ""
set_interface_property $name CMSIS_SVD_VARIABLES ""
set_interface_property $name SVD_ADDRESS_GROUP ""

set_interface_property $name associatedClock $clockName
set_interface_property $name associatedReset $resetName

add_interface_port $name ${e.PADDR.getName()} paddr ${masterPinDir} ${e.config.addressWidth}
add_interface_port $name ${e.PSEL.getName()} psel ${masterPinDir} ${e.config.selWidth}
add_interface_port $name ${e.PENABLE.getName()} penable ${masterPinDir} 1
add_interface_port $name ${e.PWRITE.getName()} pwrite ${masterPinDir} 1
add_interface_port $name ${e.PWDATA.getName()} pwdata ${masterPinDir} ${e.config.dataWidth}
add_interface_port $name ${e.PRDATA.getName()} prdata ${slavePinDir} ${e.config.dataWidth}
add_interface_port $name ${e.PREADY.getName()} pready ${slavePinDir} 1
"""
      if(useSlaveError) builder ++= s"add_interface_port $name ${e.PSLVERROR.getName()} pslverr ${slavePinDir}  1"
      true
    }
    case _ => false
  }
}

//
//class StreamEmitter extends QSysifyInterfaceEmiter{
//  override def emit(i: Data,builder : StringBuilder): Boolean = i match {
//    case e: Stream =>{
//      import e.c._
//      val isMaster = e.address.isOutput
//      val (masterPinDir,slavePinDir,startEnd) = if(isMaster) ("Output", "Input","start") else ("Input","Output","end")
//      val name = e.getName()
//      val clockDomainTag = e.getTag(classOf[ClockDomainTag])
//      if(clockDomainTag.isEmpty) SpinalError(s"Clock domain of ${i} is not defined, You should apply the ClockDomainTag to the interface\nyourBus.addTag(ClockDomainTag(ClockDomain.current))")
//      val clockName = clockDomainTag.get.clockDomain.clock.getName()
//      val resetName = clockDomainTag.get.clockDomain.reset.getName()
//      builder ++= s"""
//                     |#
//                     |# connection point $name
//          |#
//          |add_interface $name avalon $startEnd
//          |set_interface_property $name addressUnits ${addressUnits.getName.toUpperCase}
//          |set_interface_property $name burstcountUnits ${burstCountUnits.getName.toUpperCase}
//          |set_interface_property $name burstOnBurstBoundariesOnly ${burstOnBurstBoundariesOnly}
//          |set_interface_property $name constantBurstBehavior ${constantBurstBehavior}
//          |set_interface_property $name holdTime ${holdTime}
//          |set_interface_property $name linewrapBursts ${linewrapBursts}
//          |set_interface_property $name maximumPendingReadTransactions ${maximumPendingReadTransactions}
//          |set_interface_property $name maximumPendingWriteTransactions ${maximumPendingWriteTransactions}
//          |set_interface_property $name readLatency ${readLatency}
//          |set_interface_property $name readWaitTime ${readWaitTime}
//          |set_interface_property $name setupTime ${setupTime}
//          |set_interface_property $name writeWaitTime ${writeWaitTime}
//          |set_interface_property $name holdTime ${holdTime}
//          |
//          |set_interface_property $name associatedClock $clockName
//          |set_interface_property $name associatedReset $resetName
//          |set_interface_property $name bitsPerSymbol 8
//                                         |
//                                         |set_interface_property $name timingUnits Cycles
//                                                                        |set_interface_property $name ENABLED true
//                                                                                                       |set_interface_property $name EXPORT_OF ""
//                                                                                                                                      |set_interface_property $name PORT_NAME_MAP ""
//                                                                                                                                                                     |set_interface_property $name SVD_ADDRESS_GROUP ""
//                                                                                                                                                                                                    |
//""".stripMargin
//
//      if(isMaster){
//        builder ++= s"set_interface_property $name doStreamReads false\n"
//        builder ++= s"set_interface_property $name doStreamWrites false\n"
//      }
//
//      builder ++= s"add_interface_port $name ${e.address.getName} address ${masterPinDir} ${addressWidth}\n"
//      if(useRead) builder ++= s"add_interface_port $name ${e.read.getName} read ${masterPinDir} 1\n"
//      if(useWrite) builder ++= s"add_interface_port $name ${e.write.getName} write ${masterPinDir} 1\n"
//      if(useWaitRequestn) builder ++= s"add_interface_port $name ${e.waitRequestn.getName} waitrequest_n ${slavePinDir} 1\n"
//      if(useLock) builder ++= s"add_interface_port $name ${e.lock.getName} lock ${masterPinDir} 1\n"
//      if(useBurstCount) builder ++= s"add_interface_port $name ${e.burstCount.getName} burstcount ${masterPinDir} ${burstCountWidth}\n"
//      if(useByteEnable) builder ++= s"add_interface_port $name ${e.byteEnable.getName} byteenable ${masterPinDir} ${dataByteCount}\n"
//      if(useWrite) builder ++= s"add_interface_port $name ${e.writeData.getName} writedata ${masterPinDir} ${dataWidth}\n"
//      if(useResponse) builder ++= s"add_interface_port $name ${e.response.getName} response ${slavePinDir} 2\n"
//      if(useReadDataValid) builder ++= s"add_interface_port $name ${e.readDataValid.getName} readdatavalid ${slavePinDir} 1\n"
//      if(useRead) builder ++= s"add_interface_port $name ${e.readData.getName} readdata ${slavePinDir} ${dataWidth}\n"
//      if(useDebugAccess)
//        ???
//      true
//    }
//    case _ => false
//  }
//}
//#
//# connection point streamSinkPort
//#
//add_interface streamSinkPort avalon_streaming end
//set_interface_property streamSinkPort associatedClock clock
//set_interface_property streamSinkPort associatedReset reset
//set_interface_property streamSinkPort dataBitsPerSymbol 16
//set_interface_property streamSinkPort errorDescriptor ""
//set_interface_property streamSinkPort firstSymbolInHighOrderBits true
//set_interface_property streamSinkPort maxChannel 0
//set_interface_property streamSinkPort readyLatency 0
//set_interface_property streamSinkPort ENABLED true
//set_interface_property streamSinkPort EXPORT_OF ""
//set_interface_property streamSinkPort PORT_NAME_MAP ""
//set_interface_property streamSinkPort SVD_ADDRESS_GROUP ""
//
//add_interface_port streamSinkPort asi_in0_data data Input 32
//add_interface_port streamSinkPort asi_in0_valid valid Input 1
//add_interface_port streamSinkPort asi_in0_ready ready Output 1

class Axi4Emitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data,builder : StringBuilder): Boolean = i match {
    case e: Axi4 =>{
      import e.config._
      val isMaster = e.isMasterInterface
      val (masterPinDir,slavePinDir,startEnd) = if(isMaster) ("Output", "Input","start") else ("Input","Output","end")
      val name = e.getName()
      val clockDomainTag = e.getTag(classOf[ClockDomainTag])
      if(clockDomainTag.isEmpty) SpinalError(s"Clock domain of ${i} is not defined, You should apply the ClockDomainTag to the interface\nyourBus.addTag(ClockDomainTag(ClockDomain.current))")
      val clockName = clockDomainTag.get.clockDomain.clock.getName()
      val resetName = clockDomainTag.get.clockDomain.reset.getName()
      builder ++= s"""
|#
|# connection point $name
|#
|add_interface $name axi4 $startEnd
|
|set_interface_property $name associatedClock $clockName
|set_interface_property $name associatedReset $resetName
|
|set_interface_property $name ENABLED true
|set_interface_property $name EXPORT_OF ""
|set_interface_property $name PORT_NAME_MAP ""
|set_interface_property $name SVD_ADDRESS_GROUP ""
""".stripMargin

      if(isMaster) {
        if(readIssuingCapability > -1)
          builder ++= s"set_interface_property $name readIssuingCapability ${readIssuingCapability}\n"
        if(writeIssuingCapability > -1)
          builder ++= s"set_interface_property $name writeIssuingCapability ${writeIssuingCapability}\n"
        if(combinedIssuingCapability > -1)
          builder ++= s"set_interface_property $name combinedIssuingCapability ${combinedIssuingCapability}\n"
      }
      else {
        if(readIssuingCapability > -1)
          builder ++= s"set_interface_property $name readAcceptanceCapability ${readIssuingCapability}\n"
        if(writeIssuingCapability > -1)
          builder ++= s"set_interface_property $name writeAcceptanceCapability ${writeIssuingCapability}\n"
        if(combinedIssuingCapability > -1)
          builder ++= s"set_interface_property $name combinedAcceptanceCapability ${combinedIssuingCapability}\n"
        if(readDataReorderingDepth > -1)
          builder ++= s"set_interface_property $name readDataReorderingDepth ${readDataReorderingDepth}\n"
      }

      // emit AR
      builder ++= s"""
|add_interface_port $name ${e.ar.valid.getName()} arvalid ${masterPinDir} 1
|add_interface_port $name ${e.ar.ready.getName()} arready ${slavePinDir} 1
|add_interface_port $name ${e.ar.payload.addr.getName()} araddr ${masterPinDir} ${addressWidth}
""".stripMargin

      if(useId) builder ++= s"add_interface_port $name ${e.ar.payload.id.getName()} arid ${masterPinDir} ${idWidth}\n"
      if(useLen) builder ++= s"add_interface_port $name ${e.ar.payload.len.getName()} arlen ${masterPinDir} 8\n"
      if(useSize) builder ++= s"add_interface_port $name ${e.ar.payload.size.getName()} arsize ${masterPinDir} 3\n"
      if(useBurst) builder ++= s"add_interface_port $name ${e.ar.payload.burst.getName()} arburst ${masterPinDir} 2\n"
      if(useLock) builder ++= s"add_interface_port $name ${e.ar.payload.lock.getName()} arlock ${masterPinDir} 1\n"
      if(useCache) builder ++= s"add_interface_port $name ${e.ar.payload.cache.getName()} arcache ${masterPinDir} 4\n"
      if(useProt) builder ++= s"add_interface_port $name ${e.ar.payload.prot.getName()} arprot ${masterPinDir} 3\n"
      if(useArUser) builder ++= s"add_interface_port $name ${e.b.payload.user.getName()} aruser ${slavePinDir} ${arUserWidth}\n"
      
      // emit AW
      builder ++= s"""
|add_interface_port $name ${e.aw.valid.getName()} awvalid ${masterPinDir} 1
|add_interface_port $name ${e.aw.ready.getName()} awready ${slavePinDir} 1
|add_interface_port $name ${e.aw.payload.addr.getName()} awaddr ${masterPinDir} ${addressWidth}
""".stripMargin

      if(useId) builder ++= s"add_interface_port $name ${e.aw.payload.id.getName()} awid ${masterPinDir} ${idWidth}\n"
      if(useLen) builder ++= s"add_interface_port $name ${e.aw.payload.len.getName()} awlen ${masterPinDir} 8\n"
      if(useSize) builder ++= s"add_interface_port $name ${e.aw.payload.size.getName()} awsize ${masterPinDir} 3\n"
      if(useBurst) builder ++= s"add_interface_port $name ${e.aw.payload.burst.getName()} awburst ${masterPinDir} 2\n"
      if(useLock) builder ++= s"add_interface_port $name ${e.aw.payload.lock.getName()} awlock ${masterPinDir} 1\n"
      if(useCache) builder ++= s"add_interface_port $name ${e.aw.payload.cache.getName()} awcache ${masterPinDir} 4\n"
      if(useProt) builder ++= s"add_interface_port $name ${e.aw.payload.prot.getName()} awprot ${masterPinDir} 3\n"
      if(useAwUser) builder ++= s"add_interface_port $name ${e.b.payload.user.getName()} awuser ${slavePinDir} ${awUserWidth}\n"
      
      // emit R
      builder ++= s"""
|add_interface_port $name ${e.r.valid.getName()} rvalid ${slavePinDir} 1
|add_interface_port $name ${e.r.ready.getName()} rready ${masterPinDir} 1
|add_interface_port $name ${e.r.payload.data.getName()} rdata ${slavePinDir} ${dataWidth}
""".stripMargin

      if(useId) builder ++= s"add_interface_port $name ${e.r.payload.id.getName()} rid ${slavePinDir} ${idWidth}\n"
      if(useLast) builder ++= s"add_interface_port $name ${e.r.payload.last.getName()} rlast ${slavePinDir} 1\n"
      if(useResp) builder ++= s"add_interface_port $name ${e.r.payload.resp.getName()} rresp ${slavePinDir} 2\n"
      if(useRUser) builder ++= s"add_interface_port $name ${e.b.payload.user.getName()} ruser ${slavePinDir} ${rUserWidth}\n"

      // emit W
      builder ++= s"""
|add_interface_port $name ${e.w.valid.getName()} wvalid ${masterPinDir} 1
|add_interface_port $name ${e.w.ready.getName()} wready ${slavePinDir} 1
|add_interface_port $name ${e.w.payload.data.getName()} wdata ${masterPinDir} ${dataWidth}
""".stripMargin

      if(useLast) builder ++= s"add_interface_port $name ${e.w.payload.last.getName()} wlast ${masterPinDir} 1\n"
      if(useStrb) builder ++= s"add_interface_port $name ${e.w.payload.strb.getName()} wstrb ${masterPinDir} ${dataWidth/8}\n"
      if(useWUser) builder ++= s"add_interface_port $name ${e.b.payload.user.getName()} wuser ${slavePinDir} ${wUserWidth}\n"

      // emit B
      builder ++= s"""
|add_interface_port $name ${e.b.valid.getName()} bvalid ${slavePinDir} 1
|add_interface_port $name ${e.b.ready.getName()} bready ${masterPinDir} 1
""".stripMargin

      if(useId) builder ++= s"add_interface_port $name ${e.b.payload.id.getName()} bid ${slavePinDir} ${idWidth}\n"
      if(useResp) builder ++= s"add_interface_port $name ${e.b.payload.resp.getName()} bresp ${slavePinDir} 2\n"
      if(useBUser) builder ++= s"add_interface_port $name ${e.b.payload.user.getName()} buser ${slavePinDir} ${bUserWidth}\n"


      true
      }
    case _ => false
  }
}

class AxiLite4Emitter extends QSysifyInterfaceEmiter{
  override def emit(i: Data,builder : StringBuilder): Boolean = i match {
    case e: AxiLite4 =>{
      import e.config._
      val isMaster = e.isMasterInterface
      val (masterPinDir,slavePinDir,startEnd) = if(isMaster) ("Output", "Input","start") else ("Input","Output","end")
      val name = e.getName()
      val clockDomainTag = e.getTag(classOf[ClockDomainTag])
      if(clockDomainTag.isEmpty) SpinalError(s"Clock domain of ${i} is not defined, You should apply the ClockDomainTag to the interface\nyourBus.addTag(ClockDomainTag(ClockDomain.current))")
      val clockName = clockDomainTag.get.clockDomain.clock.getName()
      val resetName = clockDomainTag.get.clockDomain.reset.getName()
      builder ++= s"""
|#
|# connection point $name
|#
|add_interface $name axi4lite $startEnd
|
|set_interface_property $name associatedClock $clockName
|set_interface_property $name associatedReset $resetName
|
|set_interface_property $name ENABLED true
|set_interface_property $name EXPORT_OF ""
|set_interface_property $name PORT_NAME_MAP ""
|set_interface_property $name SVD_ADDRESS_GROUP ""
""".stripMargin

      if(isMaster) {
        if(readIssuingCapability > -1)
          builder ++= s"set_interface_property $name readIssuingCapability ${readIssuingCapability}\n"
        if(writeIssuingCapability > -1)
          builder ++= s"set_interface_property $name writeIssuingCapability ${writeIssuingCapability}\n"
        if(combinedIssuingCapability > -1)
          builder ++= s"set_interface_property $name combinedIssuingCapability ${combinedIssuingCapability}\n"
      }
      else {
        if(readIssuingCapability > -1)
          builder ++= s"set_interface_property $name readAcceptanceCapability ${readIssuingCapability}\n"
        if(writeIssuingCapability > -1)
          builder ++= s"set_interface_property $name writeAcceptanceCapability ${writeIssuingCapability}\n"
        if(combinedIssuingCapability > -1)
          builder ++= s"set_interface_property $name combinedAcceptanceCapability ${combinedIssuingCapability}\n"
        if(readDataReorderingDepth > -1)
          builder ++= s"set_interface_property $name readDataReorderingDepth ${readDataReorderingDepth}\n"
      }

      // emit AR
      builder ++= s"""
|add_interface_port $name ${e.ar.valid.getName()} arvalid ${masterPinDir} 1
|add_interface_port $name ${e.ar.ready.getName()} arready ${slavePinDir} 1
|add_interface_port $name ${e.ar.payload.addr.getName()} araddr ${masterPinDir} ${addressWidth}
|add_interface_port $name ${e.ar.payload.prot.getName()} arprot ${masterPinDir} 3
""".stripMargin

      // emit AW
      builder ++= s"""
|add_interface_port $name ${e.aw.valid.getName()} awvalid ${masterPinDir} 1
|add_interface_port $name ${e.aw.ready.getName()} awready ${slavePinDir} 1
|add_interface_port $name ${e.aw.payload.addr.getName()} awaddr ${masterPinDir} ${addressWidth}
|add_interface_port $name ${e.aw.payload.prot.getName()} awprot ${masterPinDir} 3
""".stripMargin

      // emit R
      builder ++= s"""
|add_interface_port $name ${e.r.valid.getName()} rvalid ${slavePinDir} 1
|add_interface_port $name ${e.r.ready.getName()} rready ${masterPinDir} 1
|add_interface_port $name ${e.r.payload.data.getName()} rdata ${slavePinDir} ${dataWidth}
|add_interface_port $name ${e.r.payload.resp.getName()} rresp ${slavePinDir} 2
""".stripMargin

      // emit W
      builder ++= s"""
|add_interface_port $name ${e.w.valid.getName()} wvalid ${masterPinDir} 1
|add_interface_port $name ${e.w.ready.getName()} wready ${slavePinDir} 1
|add_interface_port $name ${e.w.payload.data.getName()} wdata ${masterPinDir} ${dataWidth}
|add_interface_port $name ${e.w.payload.strb.getName()} wstrb ${masterPinDir} ${dataWidth/8}
""".stripMargin

      // emit B
      builder ++= s"""
|add_interface_port $name ${e.b.valid.getName()} bvalid ${slavePinDir} 1
|add_interface_port $name ${e.b.ready.getName()} bready ${masterPinDir} 1
|add_interface_port $name ${e.b.payload.resp.getName()} bresp ${slavePinDir} 2
""".stripMargin

      true
      }
    case _ => false
  }
}
