Scala Option Impact

Viewed 62

I am having trouble with the scala .getorElse command. I want to generate AVec at all times, but I only want AVec and BVec when elemA >= 50, and lastly, I want all of them to be generated when elemA >= 100. I have tried .get with no luck and below you can see the implementation with .getorElse.

Any advice on how to implement this correctly would be greatly appreciated. Thanks!

 val AVec = Reg(Vec(10, Valid(Vec(10, UInt(8.W)))))

 val BVec = if(elemA >= 50){
   Some(Reg(Vec(11, Valid(Vec(11, UInt(8.W))))))
 } else {
   None
 }

 val CVec = if(elemA >= 100){
   Some(Reg(Vec(12, Valid(Vec(12, UInt(8.W))))))
 } else {
   None
 }


 when(io.elemB.fire()){
   when(io.elemC === 0.U){
     AVec.map{_.valid := false.B}
   }.elsewhen(io.elemC === 1.U){
     AVec.map{_.valid := false.B}
     BVec.getOrElse(AVec).map{_.valid := false.B}
   }.otherwise{
     AVec.map{_.valid := false.B}
     BVec.getOrElse(AVec).map{_.valid := false.B}
     CVec.getOrElse(CVec).map{_.valid := false.B} //<------Issues coming here
   }
 }
1 Answers

Given the style of what you're writing, why not do?

CVec.getOrElse(AVec).map{_.valid := false.B}

That being said, there are more idomatic ways use Option. I'd suggest checking out some tutorials on Option in Scala, eg. https://www.baeldung.com/scala/option-type

In particular, you don't always need to "get" the Option. Think about an Option as a List or Array with exactly zero or one elements. Just like you can map or foreach a List (or chisel3 Vec as you're doing here), you can map or foreach an Option. Consider the following:

 when(io.elemB.fire()){
   when(io.elemC === 0.U){
     // Note that I used () instead of {}, this is a stylistic choice
     // Generally you use {} only when there are multiple Statements in the anonymous function
     // If this doesn't make sense to you yet, just ignore it, it's fine to use {} :)
     AVec.map(_.valid := false.B)
   }.elsewhen(io.elemC === 1.U){
     AVec.map(_.valid := false.B)
     BVec.foreach(_.map(_.valid := false.B))
   }.otherwise{
     AVec.map(_.valid := false.B)
     BVec.foreach(_.map(_.valid := false.B))
     CVec.foreach(_.map(_.valid := false.B))
   }

Just like AVec.map(f) applies f to each element of the Vec, BVec.foreach(g) applies g to the contents of BVec if it is Some, or does nothing otherwise. This is 2 levels of function nesting deep which may look a bit alien when you're new to Chisel/Scala, but is a common way to express this.

Alternatively, you could test the options and use .get, this is often more familiar to people new to functional programming (ie. most people new to Chisel):

 when(io.elemB.fire()){
   when(io.elemC === 0.U){
     AVec.map(_.valid := false.B)
   }.elsewhen(io.elemC === 1.U){
     AVec.map(_.valid := false.B)
     if (BVec.isDefined) {
       BVec.get.map(_.valid := false.B)
     }
   }.otherwise{
     AVec.map(_.valid := false.B)
     if (BVec.isDefined) {
       BVec.get.map(_.valid := false.B)
     }
     if (CVec.isDefined) {
       CVec.get.map(_.valid := false.B)
     }
   }

If this looks a little more verbose (you have to write CVec twice), that's what foreach is for. This "if defined, do .get and do something" is literally the implementation of Option.foreach.

Related