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

Clean up AXI generator channels #1867

Merged
merged 2 commits into from
Jan 28, 2024
Merged

Clean up AXI generator channels #1867

merged 2 commits into from
Jan 28, 2024

Conversation

nathanielnrn
Copy link
Contributor

See #1865.
This PR touches both the hardcoded calyx implementation as well as the generator.
Doing this PR has made me realize we should start making efforts to get rid of the hardcoded implementation as things will pretty easily drift out of sync otherwise.

  1. Renames base_addr and curr_addr to curr_addr_axi and curr_addr_interal_mem respectively
  2. Asserts a few properties we expect from our yxi input. Namely widths being divisible by 8 and mem sizes being powers of
  3. Simplifies the was_high signals, see the issue for more details. This is the thing I'm most unsure of w.r.t the correctness of the implementation. This ended up deviating a bit from the talk @sampsyo and I had. If anyone takes a look at this part I'd be happy to hear any and all feedback/thoughts.

After this there is still a need to refactor out shared channels in the generator.

Renamed some registers, simplified/corrected(?) the was_high signals
Assert a few properties we expect from our yxi input.

Still TODO: refactor out shared channels/code
@nathanielnrn nathanielnrn added the C: FPGA Changes for the FPGA backend label Jan 24, 2024
@nathanielnrn nathanielnrn requested a review from sampsyo January 24, 2024 03:05
@nathanielnrn nathanielnrn mentioned this pull request Jan 24, 2024
4 tasks
@sampsyo
Copy link
Contributor

sampsyo commented Jan 26, 2024

Doing this PR has made me realize we should start making efforts to get rid of the hardcoded implementation as things will pretty easily drift out of sync otherwise.

Yep, makes sense! I guess the milestone that will enable this is when we have the Python-generated code passing the same test as the original code (and not necessarily any other tests)… at which point we can very confidently delete the latter.

Copy link
Contributor

@sampsyo sampsyo left a comment

Choose a reason for hiding this comment

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

Nice!!! This comports with my understanding of the newly defined "handshake occurred" register and its role in avoiding spurious extra handshakes. Wahoo!

@@ -78,24 +78,21 @@ component m_arread_channel(
// this contains blocking logic previously in its own group
group do_ar_transfer {
//assert ARVALID as long as this is the first time we are asserting it
is_arvalid.in = !arvalid_was_high.out ? 1'b1;
is_arvalid.in = !ar_handshake_occurred.out ? 1'b1;
Copy link
Contributor

Choose a reason for hiding this comment

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

This now seems quite easy to understand, with the new name in mind!

@@ -446,21 +443,21 @@ component m_awwrite_channel(
// this contains blocking logic previously in its own group
group do_aw_transfer {
//assert AWVALID
is_awvalid.in = !awvalid_was_high.out ? 1'b1;
is_awvalid.in = !(is_awvalid.out & AWREADY) & !aw_handshake_occurred.out ? 1'b1;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the way to read this is:

This channel becomes valid whenever the remote side tells us it's ready. But we only do that when we were not previously valid, and we only do the handshake once during the execution of this group.

I guess one small question is: do we need !is_awvalid.out as part of this condition? That is, it seems like the condition AWREADY & !aw_handshake_occurred.out might suffice, because it will "suppress validity" on every cycle after we are valid for a single cycle.

Copy link
Contributor Author

@nathanielnrn nathanielnrn Jan 28, 2024

Choose a reason for hiding this comment

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

I think this is nice in that it helps point to the idea that .in is partitioned by our guards. In other words, it hopefully makes the application of De Morgan's more obvious. Another thing is that this will make is_awvalid.in depend on AWREADY in the abstract sense, which goes against the spirit of the recommendation we have discussed previously.

Also, I believe there is a subtle dependency on is_awvalid.out

is_awvalid.out AWREADY aw_handshake_occurred.out Desired input
0 0 0 1
0 0 1 0
0 1 0 1
0 1 1 0
1 0 0 1
1 0 1 0
1 1 0 0
1 1 1 0

If the above is correct(?) we care about is_awvalid.out when {AWREADY, aw_handshake_occurred.out} is 10. Thinking about this case, is_awvalid.out can probably help us save a cycle, by making AWVALID low immediately after the handshake occurs, as opposed to waiting 2 cycles for aw_handshake_occurred.out to propagate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Aha, I'm pretty sure that makes sense! Thanks for clarifying. The crux for me here was that AWREADY gives us the signal to stop the handshake one cycle earlier than !is_awvalid.out.

// but for now we will be explicit and reduce this in generation maybe. Not sure
// it even matters.
// This makes AWVALID go low after a single cycle. Without it it stays high for 2.
is_awvalid.in = is_awvalid.out & AWREADY & awvalid_was_high.out ? 1'b0;
is_awvalid.in = (is_awvalid.out & AWREADY) | aw_handshake_occurred.out ? 1'b0;
Copy link
Contributor

Choose a reason for hiding this comment

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

The condition seems like the "De Morgan's negative" of the other condition for is_awvalid.in. Perhaps, in the generator, we want a utility to emit this pattern:

lhs = cond ? 1;
lhs = !cond ? 0;

…with a single call to, like, assign_logic(lhs, cond) or something like that. Could make things a little more readable at the generator level.

Copy link
Contributor Author

@nathanielnrn nathanielnrn Jan 28, 2024

Choose a reason for hiding this comment

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

Note to self: Maybe something like partition_assignment(lhs, cond)?

Comment on lines +345 to +346
wvalid.in_ = (~(wvalid.out & WREADY) & ~w_handshake_occurred.out) @ 1
wvalid.in_ = ((wvalid.out & WREADY) | w_handshake_occurred.out) @ 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's a use case for that assign_logic utility I was pondering above…

@nathanielnrn
Copy link
Contributor Author

Merging this as is. Will look out for more use-cases that would benefit from the assign_logic builder-helper as an excuse to implement it.

@nathanielnrn nathanielnrn merged commit 57463e6 into main Jan 28, 2024
7 checks passed
@nathanielnrn nathanielnrn deleted the axi-gen-cleanup branch January 28, 2024 20:16
rachitnigam pushed a commit that referenced this pull request Feb 16, 2024
* Clean up AXI generator channels

Renamed some registers, simplified/corrected(?) the was_high signals
Assert a few properties we expect from our yxi input.

Still TODO: refactor out shared channels/code

* Rename `*_addr` registers in hardcoded axi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: FPGA Changes for the FPGA backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants