Home > Software engineering >  How is the following chisel statement decoded?
How is the following chisel statement decoded?

Time:09-21

I am learning SonicBoom. The following sentence should be based on the decoding table to assign the corresponding decoding signal.

class CtrlSigs extends Bundle
{
  val legal           = Bool()
  val fp_val          = Bool()
  val fp_single       = Bool()
  val uopc            = UInt(UOPC_SZ.W)
  val iq_type         = UInt(IQT_SZ.W)
  val fu_code         = UInt(FUC_SZ.W)
  val dst_type        = UInt(2.W)
  val rs1_type        = UInt(2.W)
  val rs2_type        = UInt(2.W)
  val frs3_en         = Bool()
  val imm_sel         = UInt(IS_X.getWidth.W)
  val uses_ldq        = Bool()
  val uses_stq        = Bool()
  val is_amo          = Bool()
  val is_fence        = Bool()
  val is_fencei       = Bool()
  val mem_cmd         = UInt(freechips.rocketchip.rocket.M_SZ.W)
  val wakeup_delay    = UInt(2.W)
  val bypassable      = Bool()
  val is_br           = Bool()
  val is_sys_pc2epc   = Bool()
  val inst_unique     = Bool()
  val flush_on_commit = Bool()
  val csr_cmd         = UInt(freechips.rocketchip.rocket.CSR.SZ.W)
  val rocc            = Bool()

  def decode(inst: UInt, table: Iterable[(BitPat, List[BitPat])]) = {
    val decoder = freechips.rocketchip.rocket.DecodeLogic(inst, XDecode.decode_default, table)
    val sigs =
      Seq(legal, fp_val, fp_single, uopc, iq_type, fu_code, dst_type, rs1_type,
          rs2_type, frs3_en, imm_sel, uses_ldq, uses_stq, is_amo,
          is_fence, is_fencei, mem_cmd, wakeup_delay, bypassable,
          is_br, is_sys_pc2epc, inst_unique, flush_on_commit, csr_cmd)
      sigs zip decoder map {case(s,d) => s := d}
      rocc := false.B
      this
  }
}

But I want to know how this step is achieved. sigs zip decoder map {case(s,d) => s := d}What does this code mean?

Zip and map seem to be functions within Vec. I tried to check its official manual.

https://www.chisel-lang.org/api/latest/chisel3/index.html?search=zip This manual is the most difficult manual I have ever seen.

He only gave the following explanation:

defzip[A1 >: T, B, That](that: GenIterable[B])(implicit bf: CanBuildFrom[IndexedSeq[T], (A1, B), That]): That
Definition Classes
IterableLikeGenIterableLike

defmap[B, That](f: (T) ⇒ B)(implicit bf: CanBuildFrom[IndexedSeq[T], B, That]): That
Definition Classes
TraversableLikeGenTraversableLikeFilterMonadic

I can't understand what the manual is saying at all? I checked Chisel's books again. There is still no introduction to these two functions. Can someone please introduce? Many thanks!

CodePudding user response:

zip is a standard Scala method. It join two sequences together in a sequence of 2-Tuples (Couple of values). Think about jacket zipper.

map is also a standard scala method. It apply a function to each membre of the sequence.

The function given to map here is case(s,d) => s := d this function use pattern matching to get the two tuple members s and d. The value returned by this function is an assignation (connection) of d to s: s := d.

For better understanding of Chisel, I think that we must know a bit of Scala before.

CodePudding user response:

I have not attempted to compile it, and to be honest, I have only a vary vague understanding of what Chisel does at all, so this is just an educated guess:

  • sigs is the sequence Seq(legal, fp_val, ..., csr_cmd) defined in the line above
  • decoder is also a sequence of some entities that can appear on the right hand side of :=.
  • zip pairs sigs and decoder-sequences elementwise, i.e. [s1, s2, s3, ..., sN] and [d1, d2, d3, ..., dN] are zipped into a sequence of pairs [(s1, d1), (s2, d2), ..., (sN, dN)].
  • map goes through the sequence, and does something to each pair. Since the result is unused, I assume that it actually should have been a foreach.
  • case deconstructs the pairs into first and second components s and d
  • s := d somehow wires together s and d (if you are working with Chisel, you probably know better than me what the := means)

It could have been rewritten with a for-yield:

for ((s, d) <- sigs.zip(decoder))
yield s := d

but since the result is unused, it probably could have been written as

sigs.zip(decoder).foreach { case (s, d) => s := d }

or with the corresponding for-syntax:

for ((s, d) <- sigs.zip(decoder)) {
  s := d
}

Regarding the manual entry: that's a signature which is quite typical for Scala collection libraries. The pile of generic parameters is a type-level specification that ensures that the type of the returned collection is "as precise as possible". This has the advantage that one can write generic methods for a whole bunch of collections, without having to return one-size-fits-all-Lists everywhere. Unfortunately, this also makes the signatures undecipherable to the novices.

This is the signature:

def zip[A1 >: T, B, That](that: GenIterable[B])(implicit bf: CanBuildFrom[IndexedSeq[T], (A1, B), That]): That 

Here is how you read those signatures:

  • Identify all the type variables that occur only in the implicit parameters. In this case, those would be A1 and That.
  • Identify all the type variables that occur in the return type. In this case, it's the That.

Now:

  • If a type variable occurs only in the type of the implicit parameters, but not in the return type, then simply ignore them: this is just a way to specify the search space for the most appropriate implicit argument. You can ignore them, unless you are a library author who is attempting to plug-in your collections into the standard collection library. Consider it to be irrelevant library-author-to-library-author chatter.
  • If some type variable X occurs both in the type of the implicit parameters and in the return type, then you can assume that it's "the best possible X you can think of".

Applied to this signature:

  • T is the element type of your original vector
  • B is the element type of the first argument (the collection you are zipping with)
  • A1 occurs only in the type of the implicit parameter: ignore it.
  • That occurs in implicit parameter and in return type: assume it's "the best possible collection type".

All together, it gives you:

  • You are starting with a vector with elements of type T
  • You are zipping it with a sequence of elements of type B
  • You can assume that That will be a collection of the most precise type, something like Vec[(T, B)] or at least IndexedSeq[(T, B)]. What it is, doesn't really matter, because you are invoking foreach on it anyway.

More generally, the idea would be that you can simply rely on the type inference, and assume that it will figure out the most appropriate result types everywhere.

  • Related