Skip to content

Add .asProducer, .asConsumer and deprecated forms#5413

Open
jackkoenig wants to merge 1 commit into
mainfrom
jackkoenig/asproducerconsumer
Open

Add .asProducer, .asConsumer and deprecated forms#5413
jackkoenig wants to merge 1 commit into
mainfrom
jackkoenig/asproducerconsumer

Conversation

@jackkoenig

Copy link
Copy Markdown
Contributor

Summary

The diff is large but it's mostly documentation and tests.

Release Notes

  • .asProducer marks the aligned fields of a bidirectional value as read-only (e.g. valid and bits).
  • .asConsumer marks the flipped fields of a bidirectional value as read-only (e.g. ready).
  • There are also forms that mark the relevant fields read-only as a deprecation warning instead of as an error.

Development notes

  • AI tool(s) used: Claude Code (Claude Opus 4.8)
  • Merge strategy (defaults to squash): squash
  • Milestone: 7.x

AI-assisted-by: Claude Code (Claude Opus 4.8)
@jackkoenig jackkoenig added the Feature New feature, will be included in release notes label Jun 14, 2026
@jackkoenig

Copy link
Copy Markdown
Contributor Author

Future work: use this (the deprecated form) on methods like Decoupled.map

But because the returned value is just an ordinary `Wire`, nothing stops a caller from accidentally driving `valid` or `bits` themselves — overwriting what the library already drove and silently breaking the design.

`.asProducer` and `.asConsumer` let the library encode that intent into the value it returns.
They tag a component with its intended role, which does two things:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what is a 'component'? Is this a term we use in this doc otherwise?

Specifically, can i tag only asProducer and asConsumer on some deeply nested members of an aggregate?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nvm i guess we did formally define component, and we seem to use the word member interchangeably with field.

So given that, is it true that i can only call asProducer/asConsumer on the 'root' of the component (the root Wire(...), IO(...) thing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

, is it true that i can only call asProducer/asConsumer on the 'root' of the component (the root Wire(...), IO(...) thing?

Nope, so component is probably the wrong word. Maybe just Data?

Recall from [Alignment: Flipped vs Aligned](#alignment-flipped-vs-aligned) that a producer drives its aligned members and is driven on its flipped members (and vice versa for a consumer).
Accordingly:

* `.asProducer` marks the **aligned** members read-only (a producer should never have its outputs driven), leaving flipped members writable. It may only appear on the **right-hand side**.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i am not sure about this "on the right-hand-side". Of what, the :<>= operator? Any of the :<>=, :>=, :<= operators?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Of any of the of the connectable operators, yeah. It's not enforced on <>, maybe it should be.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

<> is dead to me

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'd like to deprecate it 🙂

Flipped fields remain writable, so `Wire(Decoupled(UInt(8.W))).asProducer.ready := true.B` is still allowed.
`.asConsumer` is symmetric: it makes the flipped fields (e.g. `ready`) read-only while leaving aligned fields (e.g. `valid`) writable.

**Using a role on the wrong side.** A producer view on the left-hand side, or a consumer view on the right-hand side, is rejected regardless of which fields are touched.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

again can you clarify if :>= would be allowed / flagged 'correctly' as an error?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For the purposes of RHS as producer and LHS as consumer, the relationship is the same for :>= as it is for :<>=.

@jackkoenig

jackkoenig commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

One example I was putting together that illustrates a bug in this current implementation:

class MyBundle extends Bundle {
  val inward = Flipped(Decoupled(UInt(8.W)))
  val outward = Decoupled(UInt(8.W))

  def wireAndSplit = {
    val _w = Wire(new MyBundle)
    this :<>= _w
    (_w.outward.asConsumer, _w.inward.asProducer)
    // The below should be isomorphic with the above but the below currently errors.
    (_w.asConsumer.outward, _w.asConsumer.inward)
  }
}

class Top extends Module {
  val io = IO(new MyBundle)

  val (out, in) = io.wireAndSplit
  out :<= in
  out :>= in
  out :<>= in
}

Put another way, just like how .asConsumer marks a flipped field as read-only, .asConsumer on a Flipped aggregate field shoud be equivalent to marking that field as a Producer.

This PR needs some more work.

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

Labels

Feature New feature, will be included in release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants