diff --git a/xls/modules/zstd/BUILD b/xls/modules/zstd/BUILD index 31d494035a..cb3d8df3b5 100644 --- a/xls/modules/zstd/BUILD +++ b/xls/modules/zstd/BUILD @@ -139,3 +139,19 @@ cc_test( "@com_google_fuzztest//fuzztest:googletest_fixture_adapter", ], ) + +xls_dslx_library( + name = "raw_block_dec_dslx", + srcs = [ + "raw_block_dec.x", + ], + deps = [ + ":buffer_dslx", + ":common_dslx", + ], +) + +xls_dslx_test( + name = "raw_block_dec_dslx_test", + library = ":raw_block_dec_dslx", +) diff --git a/xls/modules/zstd/raw_block_dec.x b/xls/modules/zstd/raw_block_dec.x new file mode 100644 index 0000000000..aca7db220f --- /dev/null +++ b/xls/modules/zstd/raw_block_dec.x @@ -0,0 +1,96 @@ +// Copyright 2023 The XLS Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the implementation of RawBlockDecoder responsible for decoding +// ZSTD Raw Blocks. More information about Raw Block's format can be found in: +// https://datatracker.ietf.org/doc/html/rfc8878#section-3.1.1.2.2 + +import xls.modules.zstd.common as common + +type BlockDataPacket = common::BlockDataPacket; +type BlockData = common::BlockData; + +struct RawBlockDecoderState { + prev_id: u32, // ID of the previous block + prev_last: bool, // if the previous packet was the last one that makes up the whole block + prev_valid: bool, // if prev_id and prev_last contain valid data +} + +const DATA_WIDTH = common::DATA_WIDTH; +const ZERO_RAW_BLOCK_DECODER_STATE = zero!(); + +// RawBlockDecoder is responsible for decoding Raw Blocks, +// it should be a part of the ZSTD Decoder pipeline. +pub proc RawBlockDecoder { + input_r: chan in; + output_s: chan out; + + init { (ZERO_RAW_BLOCK_DECODER_STATE) } + + config( + input_r: chan in, + output_s: chan out + ) {(input_r, output_s)} + + next(tok: token, state: RawBlockDecoderState) { + let (tok, data) = recv(tok, input_r); + if state.prev_valid && (data.id != state.prev_id) && (state.prev_last == false) { + trace_fmt!("ID changed but previous packet have no last!"); + fail!("no_last", ()); + } else {}; + + let tok = send(tok, output_s, data); + + RawBlockDecoderState { + prev_valid: true, + prev_id: data.id, + prev_last: data.last + } + } +} + +#[test_proc] +proc RawBlockDecoderTest { + terminator: chan out; + dec_input_s: chan out; + dec_output_r: chan in; + + config(terminator: chan out) { + let (dec_input_s, dec_input_r) = chan; + let (dec_output_s, dec_output_r) = chan; + spawn RawBlockDecoder(dec_input_r, dec_output_s); + (terminator, dec_input_s, dec_output_r) + } + + init { } + + next(tok: token, state: ()) { + let data_to_send: BlockDataPacket[5] = [ + BlockDataPacket { id: u32:1, last: u1:false, last_block: u1:false, data: BlockData:1, length: u32:32 }, + BlockDataPacket { id: u32:1, last: u1:false, last_block: u1:false, data: BlockData:2, length: u32:32 }, + BlockDataPacket { id: u32:1, last: u1:true, last_block: u1:false, data: BlockData:3, length: u32:32 }, + BlockDataPacket { id: u32:2, last: u1:false, last_block: u1:false, data: BlockData:4, length: u32:32 }, + BlockDataPacket { id: u32:2, last: u1:true, last_block: u1:true, data: BlockData:5, length: u32:32 }, + ]; + + let tok = for ((_, data), tok): ((u32, BlockDataPacket), token) in enumerate(data_to_send) { + let tok = send(tok, dec_input_s, data); + let (tok, received_data) = recv(tok, dec_output_r); + assert_eq(data, received_data); + (tok) + }(tok); + + send(tok, terminator, true); + } +}