-
Notifications
You must be signed in to change notification settings - Fork 182
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
modules/zstd: add buffer module #1167
Conversation
504368a
to
0c17a99
Compare
xls/modules/zstd/buffer.x
Outdated
() | ||
}; | ||
|
||
let mask = (bits[BSIZE]:1 << length) - bits[BSIZE]:1; |
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.
I think we could use bitslice instead.
buffer.contents[0:length]
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.
Same problem as in #1167 (comment)
0c17a99
to
06cc423
Compare
9382127
to
37ca6a2
Compare
37ca6a2
to
c399a42
Compare
c399a42
to
5e3820d
Compare
0ecd837
to
2286b0c
Compare
4326a46
to
01f2dcd
Compare
// It will fail if the buffer cannot fit the data. For calls that need better | ||
// error handling, check `buffer_append_checked` | ||
pub fn buffer_append<CAPACITY: u32, DSIZE: u32> (buffer: Buffer<CAPACITY>, data: bits[DSIZE]) -> Buffer<CAPACITY> { | ||
if buffer_can_fit(buffer, data) == false { |
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.
q: should we get rid of the check here, since it is supposed to be the unchecked version?
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.
Checked/unchecked variants refer to the way the failure is reported in resulting verilog.
For the unchecked variant this check only indicates that something went wrong at the IR simulation level. Codegen verilog for that won't have any logic for that.
I think it is good idea to keep this check to signal the user potential issues in his/hers DSLX logic
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.
since the fail!
built-in emit as $fatal
in verilog I do think the associated logic for compute the condition will also get codegen'ed (but maybe optimized away by synthesis?)
rather than having two variant of the function, maybe we could only keep the unchecked one (w/ the fail!
) and have the caller rely on calling can_fit
if they need to do something dynamic?
wdyt?
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.
As far as I remember $fatal
is available only in SystemVerilog. For our benchmarks we do codegen with Verilog which does not generate any logic for this. On the other hand, it generates $display()
for trace_fmt!()
and those are then removed in the synthesis process so it is possible that logic for $fatal
would also be removed.
rather than having two variant of the function, maybe we could only keep the unchecked one (w/ the fail!) and have the caller rely on calling can_fit if they need to do something dynamic?
This is good idea, we can probably do that.
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.
On the other hand when I looked closely at the implementation after removing _checked
variants of the functions it does not look that good. For example, we use _checked
functions in frame header decoding in 4 places in single frame_header.x
file. Removing _checked
functions would require placing checks straight in the logic of frame header decoder. It would look something like that for each usage of _checked
variant of the functions:
diff --git a/xls/modules/zstd/frame_header.x b/xls/modules/zstd/frame_header.x
index 785586396..01cd927cd 100644
--- a/xls/modules/zstd/frame_header.x
+++ b/xls/modules/zstd/frame_header.x
@@ -115,7 +115,24 @@ fn test_extract_frame_header_descriptor() {
// returns BufferResult with the outcome of the operations on the buffer and
// information extracted from the Frame_Header_Descriptor
fn parse_frame_header_descriptor<CAPACITY: u32>(buffer: Buffer<CAPACITY>) -> (BufferResult<CAPACITY>, FrameHeaderDescriptor) {
- let (result, data) = buff::buffer_fixed_pop_checked<CAPACITY, u32:8>(buffer);
+ let (result, data) = if (buff:buffer_has_at_least(buffer, u32:8)) {
+ let (buffer_leftover, content) = buff::buffer_fixed_pop<CAPACITY, u32:8>(buffer);
+ (
+ buff::BufferResult {
+ status: buff:BufferStatus::OK,
+ buffer: buffer_leftover
+ },
+ content
+ )
+ } else {
+ (
+ buff::BufferResult {
+ status: buff::BufferStatus:NO_ENOUGH_DATA,
+ buffer: buffer
+ },
+ bits[CAPACITY]:0
+ )
+ };
match result.status {
BufferStatus::OK => {
let frame_header_desc = extract_frame_header_descriptor(data);
It is possible to put the code from match
straight under the if
but this will still lead to unnecessary code duplication. Maybe it would be better to keep those _checked
functions?
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.
I'd be curious to see what the calling logic does when handling buff::BufferStatus:NO_ENOUGH_DATA
, can you point me to it?
My reasoning is that if you're gonna act on something related to the size of the buffer, you'd rather do this early on to make it explicit how you handle this case from the protocol pov, rather than relying on propagating the status back from the buffer call.
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.
OK, so the part that is responsible for handling NO_ENOUGH_DATA
is the top level proc of the ZstdDecoder
. The code for this is not a part of any open PR yet as we still work on fixing bugs found in our C++ tests where we compare our decoder against libzstd
.
Top level proc has states which mark the stage of frame decoding, e.g.:
enum ZstdDecoderStatus : u8 {
DECODE_MAGIC_NUMBER = 0,
DECODE_FRAME_HEADER = 1,
DECODE_BLOCK_HEADER = 2,
FEED_BLOCK_DECODER = 3,
DECODE_CHECKSUM = 4,
ERROR = 255,
}
When NO_ENOUGH_DATA
occurs we just stay in the current state for the next evaluation where we receive data from decoder input and append it to the buffer. Then we can try again to decode the part specified by the state.
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.
xls/modules/zstd/buffer.x
Outdated
BufferResult { | ||
status: BufferStatus::OK, | ||
buffer: Buffer { | ||
content: (data as bits[CAPACITY] << buffer.length) | buffer.content, |
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.
q: should we try to reuse buffer_append_unchecked
here?
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.
Good idea, thanks!
// the function will fail. For calls that need better error handling, | ||
// check `buffer_pop_checked`. | ||
pub fn buffer_pop<CAPACITY: u32>(buffer: Buffer<CAPACITY>, length: u32) -> (Buffer<CAPACITY>, bits[CAPACITY]) { | ||
if buffer_has_at_least(buffer, length) == false { |
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.
q: should we get rid of the check here, since it is supposed to be the unchecked version?
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.
Similarly as with buffer_append()
this only serves the purpose of informing the user about potential issues at the stage of IR simulation. I think it is useful to have this.
xls/modules/zstd/buffer.x
Outdated
// can be used is presented below. It uses the structure to combine several | ||
// smaller transactions into bigger ones. | ||
|
||
proc BufferExampleUsage { |
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.
maybe that should go a in separate file?
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.
Sure, would you like to have it here under xls/modules/zstd
or in xls/examples
(see my comment bellow)?
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.
under zstd
is fine for now I think.
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.
Ok, no problem
xls/modules/zstd/buffer.x
Outdated
// can be used is presented below. It uses the structure to combine several | ||
// smaller transactions into bigger ones. | ||
|
||
proc BufferExampleUsage { |
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.
q: sounds like this could be a generally useful parametric proc (and not just a sample) to window channel data differently?
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.
It seems to be a good idea. If we want to have this example as parametric proc, then it would be reasonable to have it here in xls/modules/zstd
and a specialization of it under xls/examples
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.
I think we could keep the proc and the example in zstd
for now.
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.
No problem, I will update the PR after I check if my changes didn't affect other zstd procs.
a45b143
to
c512a76
Compare
@proppy the example is now placed in a separate file and the proc is renamed to |
xls/modules/zstd/window_buffer.x
Outdated
|
||
// This file contains implementation of a Buffer structure that acts as | ||
// a simple FIFO. Additionally, the file provides various functions that | ||
// can simplify access to the stored. |
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.
update the comment?
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.
Done
xls/modules/zstd/window_buffer.x
Outdated
// a simple FIFO. Additionally, the file provides various functions that | ||
// can simplify access to the stored. | ||
// | ||
// The utility functions containing the `_checked` suffix serve two purposes: |
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.
update the comment?
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.
Done
// in transactions of <INPUT_WIDTH> length and output it in transactions of | ||
// <OUTPUT_WIDTH> length. <BUFFER_SIZE> defines the maximal size of the buffer. | ||
|
||
proc WindowBuffer<BUFFER_SIZE: u32, INPUT_WIDTH: u32, OUTPUT_WIDTH: u32> { |
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.
is there a relation between BUFFER_SIZE
, INPUT_WIDTH
and OUTPUT_WIDTH
that we can const_assert
?
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.
We can for sure assert BUFFER_SIZE >= INPUT WIDTH
and BUFFER_SIZE >= OUTPUT_WIDTH
.
xls/modules/zstd/window_buffer.x
Outdated
|
||
next(tok: token, buffer: Buffer<BUFFER_SIZE>) { | ||
let (tok, recv_data) = recv(tok, input_r); | ||
let buffer = buff::buffer_append<BUFFER_SIZE>(buffer, recv_data); |
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.
shouldn't BUFFER_SIZE
be inferred from the buffer
arg?
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.
Right, fixed
xls/modules/zstd/window_buffer.x
Outdated
let buffer = buff::buffer_append<BUFFER_SIZE>(buffer, recv_data); | ||
|
||
if buffer.length >= OUTPUT_WIDTH { | ||
let (buffer, data_to_send) = buff::buffer_fixed_pop<BUFFER_SIZE, OUTPUT_WIDTH>(buffer); |
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.
shouldn't BUFFER_SIZE
be inferred from the buffer
arg?
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.
Had to change the ordering of the parameters for buffer functions so that BUFFER_SIZE
is now the last on the list. Fixed.
} | ||
|
||
#[test_proc] | ||
proc WindowBufferTest { |
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.
maybe add a test with INPUT_WIDTH > OUTPUT_WIDTH
for completeness?
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.
Done
} | ||
|
||
// Sample for codegen | ||
proc WindowBuffer64 { |
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.
thanks for adding this!
This commit adds a DSLX Buffer library that provides the Buffer struct, and helper functions that can be used to operate on it. The Buffer is meant to be a storage for data coming from the channel. It acts like a FIFO, allowing data of any length to be put in or popped out of it. Provided DSLX tests verify the correct behaviour of the library. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]>
This commit adds a simple test that shows, how one can use the Buffer struct inside a Proc. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]>
Signed-off-by: Pawel Czarnecki <[email protected]>
c512a76
to
3748388
Compare
google#1167 modules/zstd: Add buffer library This commit adds a DSLX Buffer library that provides the Buffer struct, and helper functions that can be used to operate on it. The Buffer is meant to be a storage for data coming from the channel. It acts like a FIFO, allowing data of any length to be put in or popped out of it. Provided DSLX tests verify the correct behaviour of the library. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]> modules/zstd: Add Buffer use-case example This commit adds a simple test that shows, how one can use the Buffer struct inside a Proc. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]>
google#1167 modules/zstd: Add buffer library This commit adds a DSLX Buffer library that provides the Buffer struct, and helper functions that can be used to operate on it. The Buffer is meant to be a storage for data coming from the channel. It acts like a FIFO, allowing data of any length to be put in or popped out of it. Provided DSLX tests verify the correct behaviour of the library. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]> modules/zstd: Add Buffer use-case example This commit adds a simple test that shows, how one can use the Buffer struct inside a Proc. Internal-tag: [#50221] Signed-off-by: Robert Winkler <[email protected]> modules/zstd/buffer: Add benchmarking rules Signed-off-by: Pawel Czarnecki <[email protected]>
This commit adds a DSLX Buffer library that provides the Buffer struct, and helper functions that can be used to operate on it. The Buffer is meant to be a storage for data coming from the channel. It acts like a FIFO containing bits, allowing data of any length to be put in or popped out of it. Includes DSLX tests to verify the behavior of the library.
This bitwise nature of the buffer is expected to be useful later when dealing with FSE-encoded data.
NOTE: this is based on top of #1166 , please ignore commits from that branch when reviewing.