chipsalliance / chipsalliance/chisel

Implicit return for when-elseWhen-otherwise statements

Open
#3,643 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Scala
Stars
4.8k
Forks
658
Avg merge
18h 59m
Merged PRs (30d)
14

Description

**Type of issue**: Feature Request

**Is your feature request related to a problem? Please describe.**

When writing register update sequences, I often find myself having to do the following:

```scala

when (cond_1) {
val val_1 = // ...
my_reg := val_1
} .elseWhen(cond_2) {
val val_2 = // ...
my_reg := val_2
} .otherwise {
val val_3 = // ...
my_reg := val_3
}
```

This is somewhat clunky as it means I have to keep stuffing an assignment at the end of each block.

As an alternative, I've seem some projects (BOOM?) do the following instead:

```scala
val val_1 = // ...
val val_2 = // ...
val val_3 = // ...
my_reg := Mux(cond_1, val_1,
Mux(cond_2, val_2, val_3))
```

which, while denser, is still somewhat odd and closer to the HW implementation than I would normal expect from Chisel. It also gets a bit messy as the all of the intermediate calculations for `val_xxx` are disconnected from their conditions and end up in a pile above the mux tree. Worse, if you have multiple nested conditions (i.e. imagine a second when-otherwise inside the `when(cond1)` block), it becomes legitimately challenging to figure out what the value will actually be as you have to reconstruct the control-flow tree in your head.

**Describe the solution you'd like**
Scala supports implicit returns where the last statement inside a scope is returned. It would be great if this were applied to when-elseWhen-otherwise chains so that we could write the following:

```scala
my_reg :=
when (cond_1) {
val val_1 = // ...
val_1
} .elseWhen(cond_2) {
val val_2 = // ...
val_2
} .otherwise {
val val_3 = // ...
val_3
}
```

For example, a toy ALU could be written as:

```scala
alu_result :=
when (op === ALUOp.ADD) {
arg1 + arg2
} .elseWhen(op === ALUOp.SUB) {
arg1 - arg2
} .otherwise {
DontCare
}
```

In my eyes, this is both cleaner and more Scala-idiomatic.

I'm definitely not a Chisel (or Scala) expert, but I *think* this should be possible to implement (and statically type checkable) by making WhenContext generic on type T where T is the type of the return value of the block. Then, further contexts can be chained iff T1==T2.

One challenge here is what to do when the expression is not exhaustive (i.e. suppose we omit the `otherwise` case in the ALU example above). I would personally prefer if this were treated as an error rather than the assignee keeping its previous value or, worse, getting implicitly DontCare'd as it avoids introducing footguns. This does pose a problem for trying to use this with switch statements which don't currently support a default case but I'd prefer that be solved by simply adding a default case rather than making the construct do strange and dangerous things when you use it wrong.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.