NaxRiscv
NaxRiscv copied to clipboard
Why do I need to call the create method when using NaxParameter[T].
The current usage is as follows:
NaxScope.create(xlen = 32)
val frame = new MyPlugins(plugins).framework
Can the parameter configuration and auto-negotiation mechanism be generalized and independent. like this: https://github.com/chipsalliance/cde/blob/master/cde/src/chipsalliance/rocketchip/config.scala
Thanks!
Hi,
So, there is quite a few different experiments in the way to define things in Nax, including NaxParameter an NaxScope. SpinalHDL implement a way to define variable for a given portion of the scala runtime, that's what the NaxScope is about.
Then on the top of this, NaxParameter is a way to define a new value related to the current NaxScope. It's a bit like having a global variable, but instead of being global it is related to the current NaxScope. That way you can elaborate multiple NaxRIscv in parallel without conflicting.
So the reasons why you need to call NaxScope.create(xlen = 32) before using NaxParameter is to create the host of all the future NaxParameter for a given CPU instance.
In general, i think it could be generalized more, i was planning to go back on it to clean things up. About the rocketchip config, i'm don't realy know how they manage their parametrization.
Looking at https://github.com/chipsalliance/cde/blob/aa49948fe412840a0786a70df3dedf18ef002479/cde/tests/src/SanityTest.scala#L10, it seems that they pass the database explicitly around, which isn't the same case.
Do you have some practical demo ?
Hi,
So, there is quite a few different experiments in the way to define things in Nax, including NaxParameter an NaxScope. SpinalHDL implement a way to define variable for a given portion of the scala runtime, that's what the NaxScope is about.
Then on the top of this, NaxParameter is a way to define a new value related to the current NaxScope. It's a bit like having a global variable, but instead of being global it is related to the current NaxScope. That way you can elaborate multiple NaxRIscv in parallel without conflicting.
So the reasons why you need to call NaxScope.create(xlen = 32) before using NaxParameter is to create the host of all the future NaxParameter for a given CPU instance.
In general, i think it could be generalized more, i was planning to go back on it to clean things up. About the rocketchip config, i'm don't realy know how they manage their parametrization.
Looking at https://github.com/chipsalliance/cde/blob/aa49948fe412840a0786a70df3dedf18ef002479/cde/tests/src/SanityTest.scala#L10, it seems that they pass the database explicitly around, which isn't the same case.
Do you have some practical demo ?
This is a great project, you did a great job. It took me half a year to use it. Thanks!!!
I haven't done the above project for more than a year, but I think it's good to learn a little Chisel's excellent projects to learn from each other's strengths.
I try to restore its intent
its definition is here. https://github.com/chipsalliance/rocket-chip/blob/86a2f2cca699f149bcc082ef2828654a0a4e3f4b/src/main/scala/subsystem/RocketSubsystem.scala
case object RocketTilesKey extends Field[Seq[RocketTileParams]](Nil)
RocketTilesKey extends FieldSeq[RocketTileParams]
RocketTilesKey: key name Seq[RocketTileParams]: key type Nil: key default value
This is the specific usage in engineering, like this:
https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/subsystem/Configs.scala
class WithRV32 extends Config((site, here, up) => {
case XLen => 32
case RocketTilesKey => up(RocketTilesKey, site) map { r =>
r.copy(core = r.core.copy(
fpu = r.core.fpu.map(_.copy(fLen = 32)),
mulDiv = Some(MulDivParams(mulUnroll = 8))))
}
})
New configuration class: WithRV32, which needs to inherit from the Config class. (site, here, up) : site: global key-value Map here: current location identifier up: forward search identifier up(RocketTilesKey, site) will find the closest RocketTilesKey. RocketTilesKey: Seq[RocketTileParams] type, so it can be map. Modify parameters like this. For details, it is recommended to refer to the official documentation of chipyard
t I think it's good to learn a little Chisel's excellent projects to learn from each other's strengths.
Yes i think that's right, different origine / path can lead to different solutions, diversity of backgrounds is a +
I found some doc about the SpinalHDL ScopeProperty here : https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Other%20language%20features/scope_property.html?highlight=scopeproperty
I found some doc about the SpinalHDL ScopeProperty here : https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Other%20language%20features/scope_property.html?highlight=scopeproperty
Thanks!!!
In chisel, implicit parameters are passed to various modules (but I think it can be done better). A simple demo is like this:
package demo
import mylib.xilinx.config.{Config, Field, Parameters}
import spinal.core.ScopeProperty
case object XID extends Field[Int]
case object XID0 extends Field[Int]
case object XID1 extends Field[Int]
case object XID00 extends Field[Int]
case object XID01 extends Field[Int]
case object XID10 extends Field[Int]
case object XID11 extends Field[Int]
object Xlen extends ScopeProperty[Int]
case class BusParams(
beatBytes: Int = 32,
dataWidth: Int = 8,
xlen: Int = 0
)
object SpinalChiselConfigPlay extends App{
Xlen.set(1)
println(Xlen.get) //1
Xlen(2){
println(Xlen.get) //2
Xlen(3){
println(Xlen.get) //3
Xlen.set(4)
println(Xlen.get) //4
}
println(Xlen.get) //2
}
implicit val cfg:Parameters = new TestConfig
println(cfg(XID))
println(cfg(XID0))
println(cfg(XID00))
println(cfg(XID01))
println(cfg(XID11))
implicit val cfg1:Parameters = new Test0Config
println(cfg1(XID))
println(cfg1(XID0))
println(cfg1(XID00))
println(cfg1(XID01))
// println(cfg1(XID11))
implicit val cfg2:Parameters = new Test1Config
println(cfg2(XID))
println(cfg2(XID0))
println(cfg2(XID00))
// println(cfg2(XID01))
// println(cfg2(XID11))
}
class TestConfig extends Config(
new Test0Config ++
new Test1Config
)
class Test0Config extends Config(
new Config((site, here, up) => {
case XID => 4
case XID0 => 30
// case _ => Xlen.set(1111)
}) ++
new Test00Config ++
new Test01Config
)
class Test1Config extends Config(
new Config((site, here, up) => {
case XID => 4
case XID0 => 30
case XID00 => Xlen.get
case _ => Xlen.set(2222)
}) ++
new Test11Config
)
class Test00Config extends Config((site, here, up) => {
case XID0 => 300
case XID00 => 3
case _ => Xlen.set(3333)
})
class Test01Config extends Config((site, here, up) => {
case XID => 6
case XID01 => 12
case _ => Xlen.set(4444)
})
class Test11Config extends Config((site, here, up) => {
case XID => 9
case XID11 => 18
case _ => Xlen.set(5555)
})
mylib.xilinx.config needs to be dealt with accordingly
I just pushed a cleanup for the NaxScope thing, which make the parent classes reusable in other contexts.
So, overall it seems it is a quite different paradigme.
Seems like the NaxRiscv aproache is more like having kind of a database that you can enrich by running code with side effects, in a very not functional programming way.
Note that the NaxScope purpose isn't realy to provide parameters, but instead to be a way for the scala code which elaborate NaxRiscv to share variables without having to pass them manualy all around (ex : no use of implicit). It could also be used to pass parameters from the toplevel, but most of the time, parameters pass around the Plugin's constructors themself.
For instance : https://github.com/SpinalHDL/NaxRiscv/blob/main/src/main/scala/naxriscv/Parameters.scala#L61
Which is loaded by the FetchCachePlugin, and could be refer everywhere durring scalarun durring NaxRiscv elaboration without implicit.
Thank you very much for your detailed explanation and optimization of this.