Skip to content

Add support for zero-width bit extraction #3352

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

Merged
merged 1 commit into from
Jun 9, 2023
Merged

Conversation

jackkoenig
Copy link
Contributor

@jackkoenig jackkoenig commented Jun 9, 2023

With #3321, it becomes very important that users can extract zero bits. While the (-1, 0) looks a little funny, it is the correct API for parameterized code.

Motivating example:

import chisel3._
import chisel3.util.log2Ceil
import circt.stage.ChiselStage.emitCHIRRTL

class Example(size: Int) extends Module {
  val vec  = IO(Input(Vec(size, UInt(8.W))))
  // Say we have a value we will use as an index but its width is unrelated to the Vec
  val addr  = IO(Input(UInt(8.W)))
  val out   = IO(Output(UInt(8.W)))

  // Chisel warns on mismatched widths so we bit extract
  val safeIdx = addr(log2Ceil(size) - 1, 0)
  out := vec(safeIdx)
}

// Normal sizes work great!
emitCHIRRTL(new Example(8))

// What happens with the size of the Vec is 1?
emitCHIRRTL(new Example(1))
// [error] src/main/scala/main.scala:14:21: Invalid bit range (-1,0)
// [error]   val safeIdx = addr(log2Ceil(size) - 1, 0)
// [error]                     ^
// [error] There were 1 error(s) during hardware elaboration.

(Scastie link: https://scastie.scala-lang.org/d6prKTmSRgeo2wOJErImTQ)

This change makes the extraction from (-1, 0) legal. We could make the extraction for any hi = lo - 1 legal, but I am concerned about users accidentally writing things like myUInt(5, 6) when they meant myUInt(6, 5). So instead, Chisel will give a specific suggestion in that case:

[error] .../Main.scala:13:22: Invalid bit range [hi=5, lo=6]. If you are trying to extract zero-width range, right-shift by 'lo' before extracting.
[error]       val op = myUInt(x, y)
[error]                      ^
[error] There were 1 error(s) during hardware elaboration.

The user should instead write:

val op = (myUInt >> y)(x - y, 0)

We will soon also add .take which will make it possible to write (myUInt >> y).take(x - y)

Note all of this is static so there is no hardware overhead to the shift.

Contributor Checklist

  • Did you add Scaladoc to every public function/method?
  • Did you add at least one test demonstrating the PR?
  • Did you delete any extraneous printlns/debugging code?
  • Did you specify the type of improvement?
  • Did you add appropriate documentation in docs/src?
  • Did you request a desired merge strategy?
  • Did you add text to be included in the Release Notes for this change?

Type of Improvement

  • Feature (or new API)

Desired Merge Strategy

  • Squash

Release Notes

Reviewer Checklist (only modified by reviewer)

  • Did you add the appropriate labels? (Select the most appropriate one based on the "Type of Improvement")
  • Did you mark the proper milestone (Bug fix: 3.5.x or 3.6.x depending on impact, API modification or big change: 5.0.0)?
  • Did you review?
  • Did you check whether all relevant Contributor checkboxes have been checked?
  • Did you do one of the following when ready to merge:
    • Squash: You/ the contributor Enable auto-merge (squash), clean up the commit message, and label with Please Merge.
    • Merge: Ensure that contributor has cleaned up their commit history, then merge with Create a merge commit.

@jackkoenig jackkoenig added the Feature New feature, will be included in release notes label Jun 9, 2023
@jackkoenig jackkoenig requested a review from aswaterman June 9, 2023 19:10
}
val w = x - y + 1
val resultWidth = x - y + 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed this because there is variable name shadowing going on inside that getOrElse (hit expand to see it) and it confused me.

@jackkoenig jackkoenig force-pushed the zero-width-extract branch 2 times, most recently from 68e812e to f2ea702 Compare June 9, 2023 20:04
@jackkoenig jackkoenig force-pushed the zero-width-extract branch from f2ea702 to 726c06d Compare June 9, 2023 20:51
Copy link
Member

@aswaterman aswaterman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I endorse the API and from experience know that not having this special case results in large amounts of boilerplate--which is itself likely to introduce additional bugs, sometimes as a result of code clutter.

@jackkoenig jackkoenig added this to the 3.6.x milestone Jun 9, 2023
@jackkoenig jackkoenig merged commit b910bf9 into main Jun 9, 2023
@jackkoenig jackkoenig deleted the zero-width-extract branch June 9, 2023 21:15
@mergify mergify bot added the Backported This PR has been backported label Jun 9, 2023
mergify bot pushed a commit that referenced this pull request Jun 9, 2023
(cherry picked from commit b910bf9)

# Conflicts:
#	core/src/main/scala/chisel3/Bits.scala
mergify bot pushed a commit that referenced this pull request Jun 9, 2023
mergify bot added a commit that referenced this pull request Jun 9, 2023
(cherry picked from commit b910bf9)

Co-authored-by: Jack Koenig <[email protected]>
@terpstra
Copy link
Contributor

For my entire 7-year chisel career, this has been a thorn in my side. Thank you. Thank you so much.

@terpstra
Copy link
Contributor

Why doesn't x(3, 4) work? When I left my previous comment, I assumed this was actually fixed. Now I discover that it wasn't.

😡

@terpstra
Copy link
Contributor

Also awful is that the work-around method (extract) is broken---it returns a 0.U(1.W) for the 0-wide extract case instead of 0.U(0.W). So having chisel handle this correctly would let us incrementally move away from the broken extract method.

Relatedly, 0.U really ought to have 0.W by default, not 1.W. This is the root cause of that extract bug (and a whole plethora of related bugs I've seen in real code).

@aswaterman
Copy link
Member

extract was defined before 0-width wires could even be created, so returning 0.U(1.W) was the best we could do at the time. "Broken" is a bit of a stretch.

IMO, we should change Chisel to allow e.g. x(3, 4), incrementally move away from extract, then delete extract.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backported This PR has been backported Feature New feature, will be included in release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants