SpinalHDL / VexiiRiscv

Like VexRiscv, but, Harder, Better, Faster, Stronger
MIT License
63 stars 7 forks source link

BuildBefore bug in plugin framework #2

Closed dreamflyings closed 6 months ago

dreamflyings commented 6 months ago

Hi~, I found a bug related to Lock and buildBefore in the plugin framework. Please pay attention to the TODO notes in the commented code lines. Demo code is as follows:

import spinal.core._
import spinal.core.fiber.Lock
import spinal.lib.growableAnyPimped
import spinal.lib.misc.database.Database
import spinal.lib.misc.plugin.{FiberPlugin, Hostable, PluginHost}

import scala.collection.mutable.ArrayBuffer

object PluginBugCase extends App{
  class DemoHost extends Component{
    val database = new Database
    val host = database on (new PluginHost)
  }
  val sc = SpinalConfig()
  val report = sc.generateVerilog {
    class Test0Plugin extends FiberPlugin{
      val lazyIO = during setup new Bundle{
        val ci  = in Bool()
        val a   = in UInt(4 bits)
        val b   = in UInt(4 bits)
        val s   = out UInt(4 bits)
        val co  = out Bool()
      }
      val logic = during build new AreaRoot {
        val io = lazyIO.get
        import io._
        val ret = a +^ b + ci.asUInt.resized
        co := ret.msb
        s  := ret.resize(4)
      }
    }
    class Test1Plugin extends FiberPlugin {
      lazy val t2p = host[Test2Plugin]
      buildBefore(t2p.elaborationLock)
      val logic = during build new Area {
        val sx = in UInt(4 bits)
        val en = in Bool()
        val sy = UInt(4 bits)
        sy := RegNextWhen(sx, en, sx.getZero)
        t2p.newPort(sy)
      }
    }
    class Test2Plugin extends FiberPlugin {
      val elaborationLock = Lock()
      def newPort(s:UInt) = soutS.addRet(s)
      val soutS = ArrayBuffer[UInt]()
      val logic = during build new Area {
        elaborationLock.await()
        val sout = Vec(soutS).reduce(_|_)
        sout.asOutput()
      }
    }
    val plugins = ArrayBuffer[Hostable]()
//    plugins += new Test0Plugin() // TODO please open it !!!
    plugins += new Test1Plugin()
    plugins += new Test1Plugin()
    plugins += new Test2Plugin()
    val v = new DemoHost
    v.host.asHostOf(plugins)
    v
  }
}
Dolu1990 commented 6 months ago

Hi,

Thanks, i will take a look. Also, i'm thinking about shifting toward a slightly less verbose synthax :

For instance more something in this kind : (grosso modo)

    class Test1Plugin extends FiberPlugin {
      val logic = Fiber fork new Area {
        val t2p = host[Test2Plugin]
        val t2pr = t2p.elaborationLock.retainer()

        Fiber.waitBuild()

        val sx = in UInt(4 bits)
        val en = in Bool()
        val sy = UInt(4 bits)
        sy := RegNextWhen(sx, en, sx.getZero)
        t2p.newPort(sy)

        t2pr.release()
      }
    }

That way we avoid all the lazy val trick stuff and unify stuff. also, note that now intead of retain() it use retainer(), allowing the thread which does the await to accuratly know who is locking him.

dreamflyings commented 6 months ago

Thank you very much for your reply, and thank you for your attention to this issue. I hope that our framework can balance usability and completeness. Usability means that the code framework is concise and clear (our current framework is more clear than the naxriscv framework I used before); completeness means that I hope that our framework can be the third kind of general design framework like Component and Area, which can handle linear dependencies, cross dependencies, etc., and can support various combinations and recursive situations. This requires time, deeper understanding, and support from various projects.

Dolu1990 commented 6 months ago

Here is the reworked API example (work on trap branch)

import scala.collection._
import spinal.core._
import spinal.core.fiber._
import spinal.lib._
import spinal.lib.misc.plugin._

object FiberPlay extends App{
  SpinalVerilog(new Component{

    val logic = new Area {
      var value = 0
      val xRetainer = Retainer()

      val x = Fiber setup new Area {
        //      AsyncThread.current.setName("Xthread")
        println("setup x")
        Fiber.awaitBuild()
        println("build x")
        val miaou = Bool()
        xRetainer.await()
        println(value)
        assert(value == 42)
      }

      val y = Fiber setup new Area {
        println("setup y")
        val xLock = xRetainer()
        Fiber.awaitBuild()
        println("build y")
        value = 42
        xLock.release()
      }

      val z = Fiber build new Area {
        println("build z")
      }
    }
  })
}

Forgeting the xLock.release()

will produce the error :

! SpinalHDL async engine is stuck !
Waiting on global_elab_inflightLock defined at ???:
1) global_elab loader
Waiting on logic_y_xLock defined at vexiiriscv.scratchpad.plugin.Retainer$Retainer.<init>(PluginV2.scala:13):
1) logic_x

So note the logic_y_xLock indicator, which is a big win

Dolu1990 commented 6 months ago

Got the whole vexiiriscv trap branch updated with it.

Now, when there is some dead lock, it provide much more named stuff :

! SpinalHDL async engine is stuck !
Waiting on global_elab_inflightLock defined at ???:
1) global_elab loader
Waiting on DispatchPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:16):
1) PipelineBuilderPlugin_logic
Waiting on BtbPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) fetch_logic
Waiting on execute_lane0_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:16):
1) integer_RegFilePlugin_logic
Waiting on DispatchPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:16):
1) execute_logic
Waiting on BtbPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) PcPlugin_logic
Waiting on DecodePredictionPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) decode_logic
Waiting on Decode_INSTRUCTION_WIDTH defined at spinal.lib.misc.database.ElementBlocking$$anonfun$getHandle$1.apply(DataBase.scala:64):
1) AlignerPlugin_logic
2) DecodePredictionPlugin_logic
3) LsuCachelessPlugin_logic
4) early0_BranchPlugin_logic
5) late0_BranchPlugin_logic
6) early1_BranchPlugin_logic
7) late1_BranchPlugin_logic
Waiting on DispatchPlugin_logic_dpRetains defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) DecoderPlugin_logic
Waiting on early0_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) execute_lane0_logic
Waiting on early0_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) early0_SrcPlugin_logic
Waiting on early0_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) lane0_IntFormatPlugin_logic
Waiting on PrivilegedPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) CsrAccessPlugin_logic
Waiting on DecoderPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) PrivilegedPlugin_logic
Waiting on late0_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) late0_SrcPlugin_logic
Waiting on lane0_IntFormatPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) lane0_integer_WriteBackPlugin_logic
Waiting on early1_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) execute_lane1_logic
Waiting on early1_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) early1_SrcPlugin_logic
Waiting on early1_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) lane1_IntFormatPlugin_logic
Waiting on late1_IntAluPlugin_logic_uopRetainer defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) late1_SrcPlugin_logic
Waiting on lane1_IntFormatPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) lane1_integer_WriteBackPlugin_logic
Waiting on ??? defined at vexiiriscv.execute.ExecuteLaneService$class.$init$(Service.scala:93):
1) DispatchPlugin_logic
Waiting on DecoderPlugin_logic_buildBefore defined at spinal.lib.misc.plugin.FiberPlugin.retains(Fiber.scala:17):
1) ReschedulePlugin_logic
Dolu1990 commented 6 months ago

I just tried the PluginBugCase.

What is the issue with it ? (tried on the recently updated Fiber / FiberPlugin, so the bug may have vanished)

dreamflyings commented 6 months ago

I just tried the PluginBugCase.

What is the issue with it ? (tried on the recently updated Fiber / FiberPlugin, so the bug may have vanished)

Thank you 🙏 I tested the trap branch and the issue was resolved. I also tested the demo program you provided and some more complex nested structures. I did not find any problems so far.