Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assignDontCare on Bundle element inside Union does not work #1337

Open
KireinaHoro opened this issue Mar 4, 2024 · 14 comments
Open

assignDontCare on Bundle element inside Union does not work #1337

KireinaHoro opened this issue Mar 4, 2024 · 14 comments

Comments

@KireinaHoro
Copy link
Contributor

KireinaHoro commented Mar 4, 2024

import spinal.core._
import spinal.lib._

case class TestBundle() extends Bundle {
  val xb5 = Bits(5 bits).assignDontCare()
  val u = UInt(32 bits)
}

case class TestUnion() extends Union {
  val b = newElement(TestBundle())
}

object TestStreamUnion extends App {
  SpinalVerilog(new Component {
    val a = TestUnion()
    val b = TestBundle()
    b.u := U(7, 32 bits)
    a.b.u := b.u
  })
}

Complains:

LATCH DETECTED from the combinatorial signal (toplevel/a :  Bits[37 bits]), unassigned bit mask is 00000_00000000000000000000000000011111, defined at
    pionic.TestStreamUnion$$anon$1.<init>(TestStreamUnion.scala:18)
    pionic.TestStreamUnion$.$anonfun$new$1(TestStreamUnion.scala:17)
    spinal.sim.JvmThread.run(SimManager.scala:51)

Notice how for b it doesn't complain i.e. assignDontCare actually works.

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 4, 2024

Hi,

val xb5 = Bits(5 bits).assignDontCare()

No assignement should be done inside a Bundle definition, Bundle is realy just for type definition with eventualy tag and directions

What's about :

    a.b.xb5.assignDontCare()
    a.b.u := b.u

or

    a.raw.assignDontCare()
    a.b.u := b.u

Notice how for b it doesn't complain i.e. assignDontCare actually works.

Right it isn't realy a bug.
The reason is that the union host everything in one single Bits signal and then emulate the union elements, while the vanilla bundle keep things as they are defined (separate signals for each elements)

More that Spinal will ignore some linting on signals which have no purpose
If you do :

    val b = out(TestBundle())
    b.u := U(7, 32 bits)    

You will get a

NO DRIVER ON (toplevel/b_xb5 : out Bits[5 bits]), defined at
...

But as in your original example, b_xb5 had no purpose, it is ignored (to not be too ennoying with lint)

@KireinaHoro
Copy link
Contributor Author

KireinaHoro commented Mar 4, 2024

Ah I get it now. But supporting such a use case would be useful, for example for defining message bitfields: https://github.com/KireinaHoro/spinal-blocks/blob/245a348e277b6aebc543a2c1d831dd8ba208f606/blocks/jsteward/blocks/eci/EciCmdDefs.scala#L77-L179

Some of the bitfields are defined by protocol to not have any meaning. It'd be quite annoying if I'd have to assign dontcare to them manually. One thought is to do some sort of filtering based on name in postInitCallBack, but this feels a bit hacky...

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 4, 2024

What you can do is the following :

case class EciVcCatMreq0to10() extends Bundle {
  val opcode = EciOpcode
  val xb4 = Bits(4 bits)
  val rreqId = EciId
  val dmask = EciDmask
  val ns = Bool()
  val xb3 = Bits(3 bits)
  val xb2 = Bits(2 bits)
  val address = EciAddress
  def dontCare(){
    xb2.assignDontCare()
    xb3.assignDontCare()
    xb4.assignDontCare()
  }
}

And later call the dontCare function

But overall, i think what you realy want is to have some hole in the data structure right ?
maybe we should add some proper support for that
Maybe a tag to add some post/pre padding ?

@KireinaHoro
Copy link
Contributor Author

Yes I'd say that's the intention. Using a xb field is IMO a hack, since you can still depend on the hole value somehow in the code, which breaks the contract.

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 4, 2024

Probably, we should find a way to define blanks in bundles, that need to impact the asBits / assignFromBits / aliasAs functions

That would solve it cleanly

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 4, 2024

Something like :

case class EciVcCatMreq0to10() extends Bundle {
  val opcode = EciOpcode
  blank(4 bits)
  val rreqId = EciId
  val dmask = EciDmask
  val ns = Bool()
  blank(3 bits)
  blank(2 bits)
  val address = EciAddress
}

Or/and maybe something more explicit, specifying which field is at which offset

mapping(opcode -> 0, rreqId -> 6, ...)

@KireinaHoro
Copy link
Contributor Author

I like the idea of having blank(BitWidth) better compared to the mapping function, since that seems a bit more error-prone and repetitive:

  • we have to calculate offsets manually (with something like lastOffset + lastSize)
  • what if two fields overlap?

@andreasWallner
Copy link
Collaborator

andreasWallner commented Mar 6, 2024

I'm thinking about whether PackedBundle would be a solution to this problem as it allows you to specify how members of a bundle are layed out.

It's not well documented atm, but you can get an idea on how to use it from the tests:

class SpinalSimPackedBundleTester extends SpinalAnyFunSuite {

@KireinaHoro
Copy link
Contributor Author

Looks quite good, but I'm having some trouble understanding the comments in the test case:

          val a = Bits(3 bit) // 0 to 2
          val b = Bits(3 bit).packFrom(4) // 4 to 6
          val c = Bits(3 bit).packTo(9) // 7 to 9
          val d = Bits(3 bit).pack(10 to 14, LITTLE) // 10 11 12 00 00
          val e = Bits(3 bit).pack(15 to 19, BIG) // 00 00 17 18 19
          val f = Bits(6 bit).pack(20 to 24, LITTLE) // 19 20 21 22 23 (24 drop)
          val g = Bits(6 bit).pack(25 to 29, BIG) // (25 drop) 26 27 28 29 30

The 20 to 24 field comment says it starts from 19 and goes to 23 with bit 24 dropped. Shouldn't this be from 20 to 24 with bit 25 (f(5)) dropped?

@KireinaHoro
Copy link
Contributor Author

I took a closer look and think that this can work, but it's still quite tedious to write these packFrom clauses since that requires hand-calculation of the bit offsets. How about introducing a hole command to create a hole?

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 8, 2024

Another idea of API would be something which mix implicit / explicit. With explicit commands to seek / skip stuff, and else things just get sequancial ? That would kinda support all the usages.

case class EciVcCatMreq0to10() extends Bundle {
  val opcode = EciOpcode
  blank(4 bits) //skip 4 bits
  val rreqId = EciId
  val dmask = EciDmask
  val ns = Bool()
  moveAt(33) //seek to bit 33
  val address = EciAddress
  val addressxxx = EciAddress
}

@Dolu1990
Copy link
Member

Dolu1990 commented Mar 8, 2024

I took a closer look and think that this can work, but it's still quite tedious to write these packFrom clauses since that requires hand-calculation of the bit offsets. How about introducing a hole command to create a hole?

No idea

@KireinaHoro
Copy link
Contributor Author

Do you want to implement this on Bundle or PackedBundle?

@Dolu1990
Copy link
Member

Fundamentaly, it maybe implemented directly in MultiData, maybe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants