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

Change the width of static shift right #3824

Merged
merged 2 commits into from
Feb 17, 2024

Conversation

jackkoenig
Copy link
Contributor

@jackkoenig jackkoenig commented Feb 15, 2024

This requires a recent change to CIRCT (llvm/circt#6698).

Note: The release notes were updated after merging #3841

Once firtool is released with the linked change, bumped in this repository, and this PR is merged, I think we should crack 7.0.0-M1 as it will serve as a useful milestone for dealing with this semantic change.

I also added a CLI option to emulate the old behavior: --legacy-shift-right-width. I'm not wedded to the name so if anyone has a better name for it, please suggest it.

Instructions on using this option will be in the release notes but we should probably also add a page to the website about migrating from Chisel 6 to Chisel 7 that calls this out.

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

  • API modification

Desired Merge Strategy

  • Rebase

Release Notes

  • A UInt shifted right by a static amount >= its width will now result
    in a 0-bit UInt
  • An SInt shifted right by a static amount >= its width will now result
    in a 1-bit SInt (the sign bit)

This is a change for SInts which Chisel would treat the output as a 0-bit SInt. However, FIRRTL implemented different behavior where both UInts and SInts would result in 1-bit values (which shifted right by an amount >= the width of the input).

Users can emulate the old behavior by providing CLI option --use-legacy-shift-right-width. Users are encouraged to generate Verilog with and without this option and diff it to ensure the width change does not affect the correctness of their design. Note that this option is purely for code migration and should not be used long term--it will eventually be removed.

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.6.x, 5.x, or 6.x depending on impact, API modification or big change: 7.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 this to the 7.0 milestone Feb 15, 2024
@jackkoenig jackkoenig force-pushed the jackkoenig/static-shift-right-width-change branch from 6517cd0 to bb1abcd Compare February 16, 2024 00:37
@@ -124,38 +126,50 @@ trait ChiselRunners extends Assertions {
assert(!runTester(t, additionalVResources, annotations))
}

def assertKnownWidth(expected: Int)(gen: => Data): Unit = {
def assertKnownWidth(expected: Int, args: Iterable[String] = Nil)(gen: => Data)(implicit pos: Position): Unit = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Huh, so this is for scalatest to properly pass the source locator to the assert? Interesting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, I added this because a test was failing and I wasn't sure which one.

Copy link
Contributor

@azidar azidar left a comment

Choose a reason for hiding this comment

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

LGTM!

binop(sourceInfo, UInt(this.width.shiftRight(that)), ShiftRightOp, validateShiftAmount(that))

// Implement legacy [buggy] UInt shr behavior for both Chisel and FIRRTL
@nowarn("msg=method shiftRight in class Width is deprecated")
Copy link
Contributor

Choose a reason for hiding this comment

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

What does nowarn mean again?

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 deprecated Width.shiftRight and it would trigger a warning here (and warnings are errors), so this @nowarn suppresses it. Obviously we are allowed to use our own deprecated APIs, but the API is public so I couldn't/shouldn't just delete it.

* A UInt shifted right by a static amount >= its width will now result
  in a 0-bit UInt
* An SInt shifted right by a static amount >= its width will now result
  in a 1-bit SInt (the sign bit)

This is a change for SInts which Chisel would treat the output as a
0-bit SInt. However, FIRRTL implemented different behavior where both
UInts and SInts would result in 1-bit values (which shifted right by an
amount >= the width of the input).

This makes the width behavior consistent with the FIRRTL 4.0.0 spec.
New ChiselOption UseLegacyShiftRightWidthBehavior with CLI
--legacy-shift-right-width will configure Chisel to emulate the old
Chisel and FIRRTL behavior for the width of static shift right.

This option is only intended for migrating old code and will eventually
be removed.
@jackkoenig jackkoenig force-pushed the jackkoenig/static-shift-right-width-change branch from bb1abcd to cdb3793 Compare February 17, 2024 01:01
@jackkoenig jackkoenig merged commit 0a437d8 into main Feb 17, 2024
13 checks passed
@jackkoenig jackkoenig deleted the jackkoenig/static-shift-right-width-change branch February 17, 2024 01:15
Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

LGTM

Nits and suggestions for compacting some code and tests.

core/src/main/scala/chisel3/Bits.scala Show resolved Hide resolved
core/src/main/scala/chisel3/Bits.scala Show resolved Hide resolved
core/src/main/scala/chisel3/Bits.scala Show resolved Hide resolved
Comment on lines +21 to +23
def unsignedShiftRight(that: Int): Width = this.op(this, (a, b) => 0.max(a - that))
def signedShiftRight(that: Int): Width = this.op(this, (a, b) => 1.max(a - that))
def dynamicShiftLeft(that: Width): Width =
Copy link
Member

Choose a reason for hiding this comment

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

Mega-nit: the three new methods could all be made final. This would make them inconsistent with other methods here, though. There's also very limited risk here because this thing is sealed.

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 think the sealing is more than sufficient. I probably don't actually want them to be final because I have the ulterior motive of possibly introducing a "WidthChanged" private subclass of KnownWidth that could capture information about semantic changes in widths and warn when they are observable. I don't intend to implement it any time soon but there may be future changes were we would want to.

Comment on lines +251 to +295
assertKnownWidth(4, args) {
val in = IO(Input(SInt(8.W)))
in >> 4
}
assertKnownWidth(0, args) {
val in = IO(Input(SInt(8.W)))
in >> 8
}
assertKnownWidth(0, args) {
val in = IO(Input(SInt(8.W)))
in >> 16
}
assertKnownWidth(0, args) {
val in = IO(Input(SInt(0.W)))
in >> 8
}
assertKnownWidth(0, args) {
val in = IO(Input(SInt(0.W)))
in >> 0
}
assertInferredWidth(4, args) {
val in = IO(Input(SInt(8.W)))
val w = WireInit(SInt(), in)
w >> 4
}
assertInferredWidth(1, args) {
val in = IO(Input(SInt(8.W)))
val w = WireInit(SInt(), in)
w >> 8
}
assertInferredWidth(1, args) {
val in = IO(Input(SInt(8.W)))
val w = WireInit(SInt(), in)
w >> 16
}
assertInferredWidth(1, args) {
val in = IO(Input(SInt(0.W)))
val w = WireInit(SInt(), in)
w >> 8
}
assertInferredWidth(1, args) {
val in = IO(Input(SInt(0.W)))
val w = WireInit(SInt(), in)
w >> 0
}
Copy link
Member

Choose a reason for hiding this comment

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

There's a lot of duplication between this and the previous test. Consider if the patch could combine them in some way.

There is related commonality between these and the UInt tests.

val chiselCircuit: Option[Circuit] = None,
val sourceRoots: Vector[File] = Vector.empty,
val warningFilters: Vector[WarningFilter] = Vector.empty,
val legacyShiftRightWidth: Boolean = false) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: the naming of this may be better as useLegacyShiftRightWidth, i.e., something which is more obviously true/false.

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

Successfully merging this pull request may close these issues.

3 participants