-
Notifications
You must be signed in to change notification settings - Fork 4
Implement StreamBuffer modules #15
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
Open
lucat1
wants to merge
1
commit into
fpgasystems:main
Choose a base branch
from
lucat1:stream-cache
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| `timescale 1ns / 1ps | ||
|
|
||
| import libstf::*; | ||
|
|
||
| `include "libstf_macros.svh" | ||
|
|
||
| /* | ||
| * This interface links the StreamBufferWriter and StreamBufferWriter, acting as | ||
| * a stream where tokens are shared. Each tokens represents the amount of | ||
| * bytes written by the latest card write. This is used to pause reads until | ||
| * data for a region has been fully written, so that partial data is never | ||
| * read. | ||
| */ | ||
| interface stream_buffer_link_i ( | ||
| input logic clk, | ||
| input logic rst_n | ||
| ); | ||
| vaddress_t vaddr; | ||
| vaddress_t size; | ||
| logic last; | ||
| logic valid; | ||
| logic ready; | ||
|
|
||
| task tie_off_m(); | ||
| valid = 1'b0; | ||
| endtask | ||
|
|
||
| task tie_off_s(); | ||
| ready = 1'b1; | ||
| endtask | ||
|
|
||
| modport m ( | ||
| import tie_off_m, | ||
| output vaddr, size, last, valid, | ||
| input ready | ||
| ); | ||
|
|
||
| modport s ( | ||
| import tie_off_s, | ||
| output ready, | ||
| input vaddr, size, last, valid | ||
| ); | ||
|
|
||
| `ifndef SYNTHESIS | ||
| `STF_ASSERT_SIGNAL_STABLE(vaddr); | ||
| `STF_ASSERT_SIGNAL_STABLE(size); | ||
| `STF_ASSERT_SIGNAL_STABLE(last); | ||
|
|
||
| `STF_ASSERT_NOT_UNDEFINED(valid); | ||
| `STF_ASSERT_NOT_UNDEFINED(ready); | ||
| `endif | ||
| endinterface | ||
|
|
||
| interface mem_read_config_i ( | ||
| input logic clk, | ||
| input logic rst_n | ||
| ); | ||
| vaddress_t vaddr; | ||
| data32_t size; | ||
| logic valid; | ||
| logic ready; | ||
|
|
||
| task tie_off_m(); | ||
| valid = 1'b0; | ||
| endtask | ||
|
|
||
| task tie_off_s(); | ||
| ready = 1'b1; | ||
| endtask | ||
|
|
||
| modport m ( | ||
| import tie_off_m, | ||
| output vaddr, size, valid, | ||
| input ready | ||
| ); | ||
|
|
||
| modport s ( | ||
| import tie_off_s, | ||
| output ready, | ||
| input vaddr, size, valid | ||
| ); | ||
|
|
||
| `ifndef SYNTHESIS | ||
| `STF_ASSERT_STABLE(vaddr, valid, ready); | ||
| `STF_ASSERT_STABLE(size, valid, ready); | ||
| `STF_ASSERT_NOT_UNDEFINED(valid); | ||
| `STF_ASSERT_NOT_UNDEFINED(ready); | ||
| `endif | ||
| endinterface |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| `timescale 1ns / 1ps | ||
|
|
||
| import lynxTypes::*; | ||
| import libstf::*; | ||
|
|
||
| `include "axi_macros.svh" | ||
| `include "lynx_macros.svh" | ||
| `include "libstf_macros.svh" | ||
|
|
||
| /* | ||
| * NOTE: the input_data should be wired to the AXI stream where incmonig data | ||
| * will be streamed after a request has been sent via sq_rd. | ||
| * For example, in the case of card memory, it should be | ||
| * axis_card_recv[AXI_STRM_ID]. | ||
| * NOTE: the TRANSFER_LENGTH_BYTES must be the same as configured in the | ||
| * writer. | ||
| */ | ||
| module StreamBufferReader #( | ||
| parameter AXI_STRM_ID = 0, | ||
| parameter TRANSFER_SIZE = TRANSFER_SIZE_BYTES | ||
| ) ( | ||
| input logic clk, | ||
| input logic rst_n, | ||
|
|
||
| metaIntf.m sq_rd, | ||
| metaIntf.s cq_rd, | ||
|
|
||
| stream_buffer_link_i.s link, | ||
|
|
||
| AXI4SR.s in, | ||
| AXI4S.m out | ||
| ); | ||
|
|
||
| `RESET_RESYNC // Reset pipelining | ||
|
|
||
| mem_read_config_i mem_config (.clk(clk), .rst_n(reset_synced)); | ||
|
|
||
| assign mem_config.vaddr = link.vaddr; | ||
| assign mem_config.size = link.size; | ||
| assign mem_config.valid = link.valid; | ||
| assign link.ready = mem_config.ready; | ||
|
|
||
| AXI4S inner_out (.aclk(clk), .aresetn(reset_synced)); | ||
|
|
||
| StreamReader #( | ||
| .STRM(STRM_CARD), | ||
| .AXI_STRM_ID(AXI_STRM_ID), | ||
| .TRANSFER_LENGTH_BYTES(TRANSFER_SIZE) | ||
| ) inst_stream_reader ( | ||
| .clk(clk), | ||
| .rst_n(reset_synced), | ||
|
|
||
| .sq_rd(sq_rd), | ||
| .cq_rd(cq_rd), | ||
|
|
||
| .mem_config(mem_config), | ||
|
|
||
| .input_data(in), | ||
| .output_data(inner_out) | ||
| ); | ||
|
|
||
| logic last_received, n_last_received; | ||
|
|
||
| always_ff @(posedge clk) begin | ||
| if (reset_synced == 1'b0) begin | ||
| last_received <= 1'b0; | ||
| end else begin | ||
| last_received <= n_last_received; | ||
| end | ||
| end | ||
|
|
||
| always_comb begin | ||
| n_last_received = last_received; | ||
|
|
||
| if (link.ready && link.valid) begin | ||
| n_last_received = link.last; | ||
| end | ||
| end | ||
|
|
||
| assign out.tdata = inner_out.tdata; | ||
| assign out.tkeep = inner_out.tkeep; | ||
| assign out.tlast = inner_out.tlast && last_received; | ||
| assign out.tvalid = inner_out.tvalid; | ||
| assign inner_out.tready = out.tready; | ||
|
|
||
| endmodule |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| `timescale 1ns / 1ps | ||
|
|
||
| import libstf::*; | ||
| import lynxTypes::*; | ||
|
|
||
| `include "axi_macros.svh" | ||
| `include "libstf_macros.svh" | ||
|
|
||
| /* | ||
| * NOTE: out must be wired to axis_card_send[AXI_STRM_ID]. | ||
| */ | ||
| module StreamBufferWriter #( | ||
| parameter AXI_STRM_ID = 0, | ||
| parameter TRANSFER_SIZE = TRANSFER_SIZE_BYTES, | ||
| // NOTE: this is the number of tranfers that will be allocated at a time | ||
| // when more memory is provided to the underlying StreamWriter. | ||
| parameter TRANFERS_PER_ALLOCATION = MAXIMUM_HOST_ALLOCATION_SIZE_BYTES / TRANSFER_SIZE | ||
| ) ( | ||
| input logic clk, | ||
| input logic rst_n, | ||
|
|
||
| metaIntf.m sq_wr, | ||
| metaIntf.s cq_wr, | ||
|
|
||
| AXI4S.s in, | ||
|
|
||
| stream_buffer_link_i.m link, | ||
| AXI4SR.m out | ||
| ); | ||
|
|
||
| `RESET_RESYNC // Reset pipelining | ||
|
|
||
| localparam int ALLOCATION_BYTES = TRANFERS_PER_ALLOCATION * TRANSFER_SIZE; | ||
|
|
||
| // This stream is used on the host to allocate more data for the StreamWriter. | ||
| // On card memory, there's no need for allocations, new mem_config_i regions | ||
| // are provided when requested from the code below. Thus, we can just tie off | ||
| // this signal. | ||
| stream_writer_notify_i notify (.clk(clk), .rst_n(reset_synced)); | ||
| mem_config_i mem_config (.clk(clk), .rst_n(reset_synced)); | ||
| vaddress_t next_vaddr, next_buffer_vaddr, last_allocation_end_vaddr; | ||
|
|
||
| buffer_t next_buffer; | ||
| assign next_buffer.vaddr = next_vaddr; | ||
| assign next_buffer.size = TRANFERS_PER_ALLOCATION; | ||
| assign mem_config.flush_buffers = 1'b0; | ||
| assign mem_config.buffer_data = next_buffer; | ||
| assign mem_config.buffer_valid = 1'b1; | ||
|
|
||
| // This state machine ensures that the notification of a compled write is | ||
| // received by the consumer on the other end of the link. It also ensures that | ||
| // when the current memory allocation is exhausted, a new memory allocation is | ||
| // provided on the mem_config interface and acknowledged. | ||
| typedef enum logic { | ||
| ST_NOT_FULL, | ||
| ST_NEEDS_ALLOCATION | ||
| } state_t; | ||
| state_t state; | ||
|
|
||
| vaddress_t n_next_vaddr; | ||
| assign n_next_vaddr = next_vaddr + notify.size; | ||
|
|
||
| always_ff @(posedge clk) begin | ||
| if (reset_synced == 1'b0) begin | ||
| next_vaddr <= '0; | ||
| next_buffer_vaddr <= '0; | ||
| last_allocation_end_vaddr <= '0; | ||
|
|
||
| state <= ST_NEEDS_ALLOCATION; | ||
| end else begin | ||
| case (state) | ||
| ST_NOT_FULL: begin | ||
| if (notify.ready && notify.valid) begin | ||
| next_vaddr <= n_next_vaddr; | ||
|
|
||
| // When we receive a last, the writer is going to assume | ||
| // that we want a new allocation for the next stream. This | ||
| // is the case when sending data to the host, but not when | ||
| // sending data to the card (we don't want to waste memory | ||
| // for a new allocation, leaving the current one half-used). | ||
| if (notify.last || n_next_vaddr >= last_allocation_end_vaddr) begin | ||
| state <= ST_NEEDS_ALLOCATION; | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ST_NEEDS_ALLOCATION: begin | ||
| if (mem_config.buffer_ready) begin | ||
| next_buffer_vaddr <= next_buffer_vaddr + ALLOCATION_BYTES; | ||
| last_allocation_end_vaddr <= next_vaddr + ALLOCATION_BYTES; | ||
|
|
||
| state <= ST_NOT_FULL; | ||
| end | ||
| end | ||
| endcase | ||
| end | ||
| end | ||
|
|
||
| always_comb begin | ||
| if (reset_synced == 1'b0) begin | ||
| notify.ready = 1'b0; | ||
| mem_config.buffer_valid = 1'b0; | ||
| end else begin | ||
| case (state) | ||
| ST_NOT_FULL: begin | ||
| notify.ready = link.ready; | ||
| mem_config.buffer_valid = 1'b0; | ||
| end | ||
|
|
||
| ST_NEEDS_ALLOCATION: begin | ||
| notify.ready = 1'b0; | ||
| mem_config.buffer_valid = 1'b1; | ||
| end | ||
| endcase | ||
| end | ||
|
|
||
| link.vaddr = next_vaddr; | ||
| link.size = notify.size; | ||
| link.last = notify.last; | ||
| link.valid = state == ST_NOT_FULL && notify.valid; | ||
| end | ||
|
|
||
| StreamWriter #( | ||
| .STRM(STRM_CARD), | ||
| .AXI_STRM_ID(AXI_STRM_ID), | ||
| .TRANSFER_LENGTH_BYTES(TRANSFER_SIZE) | ||
| ) inst_stream_writer ( | ||
| .clk(clk), | ||
| .rst_n(reset_synced), | ||
|
|
||
| .sq_wr(sq_wr), | ||
| .cq_wr(cq_wr), | ||
|
|
||
| .notify(notify), | ||
| .mem_config(mem_config), | ||
|
|
||
| .input_data(in), | ||
| .output_data(out) | ||
| ); | ||
|
|
||
| endmodule |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The StreamBufferReader needs a config to be able to replay the same data multiple times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea for this was just that the link instead of being piped directly from the writer to the reader, would go through by a middleman component. There we decide what to have the reader read, and if repeatedly, how many times, and such.
But that's conceptually logic that belongs to another module IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, fair enough. That makes sense. Let me try out the stuff and then I will merge it. We need it fairly soon.