diff --git a/xls/modules/rle/BUILD b/xls/modules/rle/BUILD index e49b6c98a2..d2750748db 100644 --- a/xls/modules/rle/BUILD +++ b/xls/modules/rle/BUILD @@ -106,6 +106,215 @@ xls_benchmark_ir( }, ) +xls_dslx_library( + name = "rle_enc_adv_reduce_stage_dslx", + srcs = [ + "rle_enc_adv_reduce_stage.x", + ], + deps = [ + ":rle_common_dslx" + ], +) + +xls_dslx_test( + name = "rle_enc_adv_reduce_stage_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_enc_adv_reduce_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_reduce_stage_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_enc_adv_reduce_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_reduce_stage_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_enc_adv_reduce_stage_dslx", +) + +xls_dslx_library( + name = "rle_enc_adv_realign_stage_dslx", + srcs = [ + "rle_enc_adv_realign_stage.x", + ], + deps = [ + ":rle_common_dslx" + ], +) + +xls_dslx_test( + name = "rle_enc_adv_realign_stage_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_enc_adv_realign_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_realign_stage_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_enc_adv_realign_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_realign_stage_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_enc_adv_realign_stage_dslx", +) + +xls_dslx_library( + name = "rle_enc_adv_core_dslx", + srcs = [ + "rle_enc_adv_core.x", + ], + deps = [ + ":rle_common_dslx" + ], +) + +xls_dslx_test( + name = "rle_enc_adv_core_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_enc_adv_core_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_core_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_enc_adv_core_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_core_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_enc_adv_core_dslx", +) + +xls_dslx_library( + name = "rle_enc_adv_adjust_width_stage_dslx", + srcs = [ + "rle_enc_adv_adjust_width_stage.x", + ], + deps = [ + ":rle_common_dslx" + ], +) + +xls_dslx_test( + name = "rle_enc_adv_adjust_width_stage_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_enc_adv_adjust_width_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_adjust_width_stage_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_enc_adv_adjust_width_stage_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_adjust_width_stage_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_enc_adv_adjust_width_stage_dslx", +) + +xls_dslx_library( + name = "rle_enc_adv_dslx", + srcs = [ + "rle_enc_adv.x", + ], + deps = [ + ":rle_common_dslx", + ":rle_enc_adv_reduce_stage_dslx", + ":rle_enc_adv_realign_stage_dslx", + ":rle_enc_adv_core_dslx", + ":rle_enc_adv_adjust_width_stage_dslx", + ], +) + +xls_dslx_test( + name = "rle_enc_adv_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_enc_adv_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_enc_adv_dslx", +) + +xls_dslx_test( + name = "rle_enc_adv_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_enc_adv_dslx", +) + +xls_dslx_ir( + name = "rle_enc_adv_ir", + dslx_top = "RunLengthEncoder8_8_4_2", + library = "rle_enc_adv_dslx", + ir_file = "rle_enc_adv.ir", +) + +xls_ir_opt_ir( + name = "rle_enc_adv_opt_ir", + src = "rle_enc_adv.ir", + top = "__xls_modules_rle_rle_enc_adv_core__RunLengthEncoder8_8_4_2__RunLengthEncoderAdvanced__RunLengthEncoderAdvancedCoreStage_0__8_4_8_next", +) + +xls_ir_verilog( + name = "rle_enc_adv_verilog", + src = ":rle_enc_adv_opt_ir.opt.ir", + verilog_file = "rle_enc_adv.v", + codegen_args = { + "module_name": "rle_enc_adv", + "delay_model": "unit", + "pipeline_stages": "2", + "reset": "rst", + "use_system_verilog": "false", + }, +) + +xls_benchmark_ir( + name = "rle_enc_adv_ir_benchmark", + src = ":rle_enc_adv_opt_ir.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "2", + "delay_model": "unit", + } +) + xls_dslx_library( name = "rle_dec_dslx", srcs = [ diff --git a/xls/modules/rle/rle_enc_adv.x b/xls/modules/rle/rle_enc_adv.x new file mode 100644 index 0000000000..451c5540aa --- /dev/null +++ b/xls/modules/rle/rle_enc_adv.x @@ -0,0 +1,479 @@ +// 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 implements a parametric RLE encoder +// +// The encoder uses Run Length Encoding (RLE) to compress the input stream of +// repeating symbols to the output stream that contains the symbols and +// the number of its consequect occurrences in the input stream. +// Both the input and the output channels use additional `last` flag +// that indicates whether the packet ends the transmission. After sending +// the last packet encoder dumps all the data to the output stream. +// The behavior of the encoder is presented on the waveform below: +// ──────╥─────╥─────╥─────╥─────╥─────╥─────╥─────╥──── +// next evaluation XXXXXX║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ ... +// ──────╨─────╨─────╨─────╨─────╨─────╨─────╨─────╨──── +// ──────╥───────────╥─────╥─────╥─────╥─────╥────────── +// symbol XXXXXX║ A ║ B ║XXXXX║ B ║ C ║XXXXXXXXXX +// (input channel) ──────╨───────────╨─────╨─────╨─────╨─────╨────────── +// last ┌─────┐ ┌─────┐ +// (input channel) ──────────────────┘ └───────────┘ └────────── +// ╥─────╥─────╥─────╥─────╥─────╥─────╥─────╥────────── +// state.prev_symbol ║ 0 ║ A ║ A ║ B ║ 0 ║ B ║ C ║ 0 +// (set state value) ╨─────╨─────╨─────╨─────╨─────╨─────╨─────╨────────── +// ╥─────╥─────╥─────╥─────╥─────╥─────╥─────╥────────── +// state.prev_count ║ 0 ║ 1 ║ 2 ║ 1 ║ 0 ║ 1 ║ 1 ║ 0 +// (set state value) ╨─────╨─────╨─────╨─────╨─────╨─────╨─────╨────────── +// +// do_send ┌───────────┐ ┌───────────┐ +// ──────────────────┘ └─────┘ └──── +// ──────────────────╥─────╥─────╥─────╥─────╥─────╥──── +// symbol, count XXXXXXXXXXXXXXXXXX║ A,2 ║ B,1 ║XXXXX║ B,1 ║ C,1 ║XXXX +// (output channel) ──────────────────╨─────╨─────╨─────╨─────╨─────╨──── +// last ┌─────┐ ┌─────┐ +// (output channel) ────────────────────────┘ └───────────┘ └──── + +import std +import xls.modules.rle.rle_common as rle_common + +import xls.modules.rle.rle_enc_adv_reduce_stage as reduce_stage +import xls.modules.rle.rle_enc_adv_realign_stage as realign_stage +import xls.modules.rle.rle_enc_adv_core as core +import xls.modules.rle.rle_enc_adv_adjust_width_stage as adjust_width_stage + + +type EncInData = rle_common::PlainData; +type EncOutData = rle_common::CompressedData; + + +// RLE encoder implementation +pub proc RunLengthEncoderAdvanced { + + init {()} + + config ( + input_r: chan> in, + output_s: chan> out, + ) { + let (reduced_s, reduced_r) = chan< + EncOutData>; + spawn reduce_stage::RunLengthEncoderAdvancedReduceStage< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH>(input_r, reduced_s); + + let (realigned_s, realigned_r) = chan< + (EncOutData, u32, u32)>; + spawn realign_stage::RunLengthEncoderAdvancedRealignStage< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH>(reduced_r, realigned_s); + + let (compressed_s, compressed_r) = chan< + (EncOutData, u32)>; + + spawn core::RunLengthEncoderAdvancedCoreStage< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH>(realigned_r, compressed_s); + spawn adjust_width_stage::RunLengthEncoderAdvancedAdjustWidthStage< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH, OUTPUT_WIDTH>( + compressed_r, output_s); + () + } + + next (tok: token, state: ()) {()} +} + +// RLE encoder specialization for the codegen +proc RunLengthEncoder8_8_4_2 { + + init {()} + + config ( + input_r: chan> in, + output_s: chan> out, + ) { + spawn RunLengthEncoderAdvanced( + input_r, output_s); + () + } + + next (tok: token, state: ()) { + () + } +} + +// Testing +// Each subprocess is tested individually. + +const TEST_COMMON_SYMBOL_WIDTH = u32:32; +const TEST_COMMON_COUNT_WIDTH = u32:32; +const TEST_COMMON_INPUT_WIDTH = u32:4; +const TEST_COMMON_OUTPUT_WIDTH = u32:2; + +type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; +type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; + +type TestCommonSymbolsIn = TestCommonSymbol[TEST_COMMON_INPUT_WIDTH]; +type TestCommonSymbolValidsIn = bits[1][TEST_COMMON_INPUT_WIDTH]; +type TestCommonStimulus = (TestCommonSymbolsIn, TestCommonSymbolValidsIn); + +type TestCommonSymbolsOut = TestCommonSymbol[TEST_COMMON_OUTPUT_WIDTH]; +type TestCommonCountsOut = TestCommonCount[TEST_COMMON_OUTPUT_WIDTH]; +type TestCommonOutputs = (TestCommonSymbolsOut, TestCommonCountsOut); + +type TestCommonEncInData = EncInData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_INPUT_WIDTH>; +type TestCommonEncOutData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>; + + +#[test_proc] +proc ConsumeMultipleSymbolRepetitionsAtOnce { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init{()} + + config(terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + spawn RunLengthEncoderAdvanced< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH + >(enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[4] = [ + (TestCommonSymbolsIn:[0x1, 0x1, 0x1, 0x1], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + (TestCommonSymbolsIn:[0x1, 0x1, 0x1, 0x1], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + (TestCommonSymbolsIn:[0x1, 0x1, 0x1, 0x1], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + (TestCommonSymbolsIn:[0x1, 0x1, 0x1, 0x1], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.last); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonOutputs[1] = [ + (TestCommonSymbolsOut:[0x1, 0x0], + TestCommonCountsOut:[0x10, 0x0]), + ]; + + let tok = for ((counter, output), tok): + ((u32, TestCommonOutputs) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: output.0, + counts: output.1, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc ConsumeMultipleSymbolsAtOnce { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init{()} + + config(terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + spawn RunLengthEncoderAdvanced< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH + >(enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbolsIn:[0x1, 0x1, 0x2, 0x2], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.last); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonOutputs[1] = [ + (TestCommonSymbolsOut:[0x1, 0x2], + TestCommonCountsOut:[0x2, 0x2]), + ]; + + let tok = for ((counter, output), tok): + ((u32, TestCommonOutputs) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: output.0, + counts: output.1, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc ConsumePacketWithInvalidSymbols { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init{()} + + config(terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + spawn RunLengthEncoderAdvanced< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH + >(enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[4] = [ + (TestCommonSymbolsIn:[0x1, 0x0, 0x1, 0x0], + TestCommonSymbolValidsIn:[0x1, 0x0, 0x1, 0x0]), + (TestCommonSymbolsIn:[0x1, 0x1, 0x0, 0x1], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x0, 0x1]), + (TestCommonSymbolsIn:[0x0, 0x1, 0x1, 0x0], + TestCommonSymbolValidsIn:[0x0, 0x1, 0x1, 0x0]), + (TestCommonSymbolsIn:[0x0, 0x0, 0x0, 0x0], + TestCommonSymbolValidsIn:[0x0, 0x0, 0x0, 0x0]), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.last); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonOutputs[1] = [ + (TestCommonSymbolsOut:[0x1, 0x0], + TestCommonCountsOut:[0x7, 0x0]), + ]; + + let tok = for ((counter, output), tok): + ((u32, TestCommonOutputs) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: output.0, + counts: output.1, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc ConsumePacketWithAllDiffSymbols { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init{()} + + config(terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + spawn RunLengthEncoderAdvanced< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH + >(enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.last); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonOutputs[2] = [ + (TestCommonSymbolsOut:[0x1, 0x2], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x3, 0x4], + TestCommonCountsOut:[0x1, 0x1]), + ]; + + let tok = for ((counter, output), tok): + ((u32, TestCommonOutputs) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: output.0, + counts: output.1, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc ConsumePacketsWhereLastSymbolRepeats { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init{()} + + config(terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + spawn RunLengthEncoderAdvanced< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH + >(enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + (TestCommonSymbolsIn:[0x4, 0x4, 0x4, 0x4], + TestCommonSymbolValidsIn:[0x1, 0x1, 0x1, 0x1]), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.last); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonOutputs[2] = [ + (TestCommonSymbolsOut:[0x1, 0x2], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x3, 0x4], + TestCommonCountsOut:[0x1, 0x5]), + ]; + + let tok = for ((counter, output), tok): + ((u32, TestCommonOutputs) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: output.0, + counts: output.1, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} diff --git a/xls/modules/rle/rle_enc_adv_adjust_width_stage.x b/xls/modules/rle/rle_enc_adv_adjust_width_stage.x new file mode 100644 index 0000000000..9ede70289b --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_adjust_width_stage.x @@ -0,0 +1,563 @@ +// 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. + +import std +import xls.modules.rle.rle_common as rle_common + +type EncInData = rle_common::PlainData; +type EncOutData = rle_common::CompressedData; + +struct RunLengthEncoderAdvancedAdjustWidthStageState< + SYMBOL_WIDTH:u32, COUNT_WIDTH:u32, PAIR_COUNT:u32> { + + stored_pairs: (bits[SYMBOL_WIDTH], bits[COUNT_WIDTH])[PAIR_COUNT], + stored_pairs_count: u32, + stored_last: bool, +} + +pub proc RunLengthEncoderAdvancedAdjustWidthStage< + SYMBOL_WIDTH: u32, COUNT_WIDTH: u32, INPUT_WIDTH:u32, OUTPUT_WIDTH:u32> { + + input_r: chan<(EncOutData, u32)> in; + output_s: chan> out; + + init {( + RunLengthEncoderAdvancedAdjustWidthStageState< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH> { + stored_pairs: zero!<(bits[SYMBOL_WIDTH], bits[COUNT_WIDTH])[INPUT_WIDTH]>(), + stored_pairs_count: u32:0, + stored_last: false, + } + )} + config ( + input_r: chan<(EncOutData, u32)> in, + output_s: chan> out, + ) {(input_r, output_s)} + + next (tok: token, state: RunLengthEncoderAdvancedAdjustWidthStageState< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH>) { + let recv_next_portion = !state.stored_last && + (state.stored_pairs_count <= OUTPUT_WIDTH); + let empty_input = ( + EncOutData { + symbols: zero!(), + counts: zero!(), + last: false + }, + u32:0, + ); + let (tok, input) = recv_if(tok, input_r, recv_next_portion, empty_input); + + let (pairs_to_send, pairs_to_send_count, input_processed_count) = + for (idx, (pairs, pairs_count, input_count)) in range(u32:0, OUTPUT_WIDTH) { + if pairs_count < state.stored_pairs_count { + (update(pairs, pairs_count, state.stored_pairs[pairs_count]), + pairs_count + u32:1, input_count) + } else if input_count < input.1 { + let input_pair = (input.0.symbols[input_count], + input.0.counts[input_count]); + (update(pairs, pairs_count, input_pair), + pairs_count + u32:1, input_count + u32:1) + } else { + (pairs, pairs_count, input_count) + } + } ((zero!<(bits[SYMBOL_WIDTH], bits[COUNT_WIDTH])[OUTPUT_WIDTH]>(), + u32:0, u32:0)); + + // If we recv data, all stored pairs are being send + let (pairs_to_state, pairs_count_to_state) = if recv_next_portion { + for (idx, (pairs, pairs_count)) in range(u32:0, INPUT_WIDTH) { + if input_processed_count <= idx && idx < input.1 { + (update(pairs, pairs_count, + (input.0.symbols[idx], input.0.counts[idx])), + pairs_count + u32:1) + } else { + (pairs, pairs_count) + } + }((zero!<(bits[SYMBOL_WIDTH], bits[COUNT_WIDTH])[INPUT_WIDTH]>(), u32:0)) + } else { + for (idx, (pairs, pairs_count)) in range(u32:0, INPUT_WIDTH){ + if pairs_to_send_count <= idx && idx < state.stored_pairs_count { + ( + update(pairs, pairs_count, state.stored_pairs[idx]), + pairs_count + u32:1 + ) + } else { + (pairs, pairs_count) + } + } ((state.stored_pairs, u32:0)) + }; + + let last_to_send = (state.stored_last || input.0.last) && + (pairs_count_to_state == u32:0); + let last_to_state = (state.stored_last || input.0.last) && + (pairs_count_to_state != u32:0); + + let (symbols_to_send, counts_to_send) = + for (idx, (symbols, counts)) in range(u32:0, OUTPUT_WIDTH) { + let pair = pairs_to_send[idx]; + (update(symbols, idx, pair.0), update(counts, idx, pair.1)) + }((zero!(), + zero!())); + + let output = EncOutData { + symbols: symbols_to_send, + counts: counts_to_send, + last: last_to_send, + }; + let tok = send(tok, output_s, output); + + RunLengthEncoderAdvancedAdjustWidthStageState< + SYMBOL_WIDTH, COUNT_WIDTH, INPUT_WIDTH> { + stored_pairs: pairs_to_state, + stored_pairs_count: pairs_count_to_state, + stored_last: last_to_state, + } + } +} + +const TEST_COMMON_SYMBOL_WIDTH = u32:32; +const TEST_COMMON_COUNT_WIDTH = u32:32; +const TEST_COMMON_INPUT_WIDTH = u32:4; +const TEST_COMMON_OUTPUT_WIDTH = u32:2; + +type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; +type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; + +type TestCommonSymbolsIn = TestCommonSymbol[TEST_COMMON_INPUT_WIDTH]; +type TestCommonCountsIn = TestCommonCount[TEST_COMMON_INPUT_WIDTH]; +type TestCommonSymbolsOut = TestCommonSymbol[TEST_COMMON_OUTPUT_WIDTH]; +type TestCommonCountsOut = TestCommonCount[TEST_COMMON_OUTPUT_WIDTH]; + +type TestCommonStimulus = (TestCommonSymbolsIn, TestCommonCountsIn, u32); + +type TestCommonEncInData = EncOutData; +type TestCommonEncInTuple = (TestCommonEncInData, u32); + +type TestCommonEncOutData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>; + +// Test simple case, where only last is set. +// Check handling of empty transaction. +#[test_proc] +proc PacketContainsOnlyLast { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x0, 0x0, 0x0, 0x0], + u32:0), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, _stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbolsOut, TestCommonCountsOut)[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0], + [TestCommonCount:0x0, TestCommonCount:0x0]), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbolsOut, TestCommonCountsOut)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Test simple case, where number of incoming pairs <= +// max output width +#[test_proc] +proc InputPairCountLessOrEqualOutputWidth { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x1, 0x1, 0x0, 0x0], + u32:2), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, _stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbolsOut, TestCommonCountsOut)[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2], + [TestCommonCount:0x1, TestCommonCount:0x1]), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbolsOut, TestCommonCountsOut)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Test simple case, where number of incoming pairs = input width +// It checks serialization +#[test_proc] +proc InputFullyFilled { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x1, 0x1, 0x1, 0x1], + u32:4), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, _stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbolsOut, TestCommonCountsOut)[2] = [ + (TestCommonSymbolsOut:[0x1, 0x2], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x3, 0x4], + TestCommonCountsOut:[0x1, 0x1]), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbolsOut, TestCommonCountsOut)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Send 2-packet ransaction, where first packet has 3 pairs, and second one has +// single pair. Checks that only 2 output packets are produced. +#[test_proc] +proc CombineStateWithNextInputPairs { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x1, 0x1, 0x1, 0x0], + u32:3), + (TestCommonSymbolsIn:[0x4, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x1, 0x0, 0x0, 0x0], + u32:1), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, _stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbolsOut, TestCommonCountsOut)[2] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2], + [TestCommonCount:0x1, TestCommonCount:0x1]), + ([TestCommonSymbol:0x3, TestCommonSymbol:0x4], + [TestCommonCount:0x1, TestCommonCount:0x1]), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbolsOut, TestCommonCountsOut)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Send 2-packet ransaction, where first packet has 3 pairs, and second one has +// 4 pairs. Checks that only 4 output packets are produced. +#[test_proc] +proc PairStateCombineWithStateUpdate { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + (TestCommonSymbolsIn:[0x1, 0x2, 0x3, 0x4], + TestCommonCountsIn:[0x1, 0x1, 0x1, 0x0], + u32:3), + (TestCommonSymbolsIn:[0x4, 0x5, 0x6, 0x7], + TestCommonCountsIn:[0x1, 0x1, 0x1, 0x1], + u32:4), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, _stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbolsOut, TestCommonCountsOut)[4] = [ + (TestCommonSymbolsOut:[0x1, 0x2], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x3, 0x4], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x5, 0x6], + TestCommonCountsOut:[0x1, 0x1]), + (TestCommonSymbolsOut:[0x7, 0x0], + TestCommonCountsOut:[0x1, 0x0]), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbolsOut, TestCommonCountsOut)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Send 2 transactions, where first one has 3 pairs, and second one has +// single pair. Checks that 2 transactions will be created, first one with 2 +// output packets and second one with single packet. +#[test_proc] +proc NoStateSipllAfterLast { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config(terminator: chan out) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedAdjustWidthStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH, TEST_COMMON_OUTPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok:token, state:()) { + let TestCommonTestStimuli: TestCommonEncInTuple[2] = [ + (TestCommonEncInData{ + symbols:[0x1, 0x2, 0x3, 0x4], + counts:[0x1, 0x1, 0x1, 0x0], + last:true + }, u32:3), + (TestCommonEncInData{ + symbols:[0x4, 0x2, 0x3, 0x4], + counts:[0x1, 0x0, 0x0, 0x0], + last:true + }, u32:1), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonEncInTuple) , token) + in enumerate(TestCommonTestStimuli) { + let tok = send(tok, enc_input_s, stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, pair_count: {}", + counter, stimulus.0.symbols, stimulus.0.counts, stimulus.0.last, stimulus.1); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonEncOutData[3] = [ + TestCommonEncOutData { + symbols: [0x1, 0x2], + counts: [0x1, 0x1], + last: false, + }, + TestCommonEncOutData { + symbols: [0x3, 0x0], + counts: [0x1, 0x0], + last: true, + }, + TestCommonEncOutData { + symbols: [0x4, 0x0], + counts: [0x1, 0x0], + last: true + }, + ]; + let tok = for ((counter, expected), tok): + ((u32, TestCommonEncOutData) , token) + in enumerate(TestCommonTestOutput) { + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} diff --git a/xls/modules/rle/rle_enc_adv_core.x b/xls/modules/rle/rle_enc_adv_core.x new file mode 100644 index 0000000000..bee778351d --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_core.x @@ -0,0 +1,669 @@ +// 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. + +import std +import xls.modules.rle.rle_common as rle_common + +type EncInData = rle_common::PlainData; +type EncOutData = rle_common::CompressedData; + + +// structure to preserve the state of an RLE encoder +struct RunLengthEncoderAdvancedCoreState { + + // symbol from the previous RunLengthEncoder::next evaluation, + // valid if prev_count > 0 + prev_symbol: bits[SYMBOL_WIDTH], + // symbol count from the previous RunLengthEncoder::next evaluation. + // zero means that the previous evaluation sent all the data and + // we start counting from the beginning + prev_count: bits[COUNT_WIDTH], + // flag indicating that the previous symbol was the last one + // in the transmission + prev_last: bool, +} + +// RLE encoder implementation +pub proc RunLengthEncoderAdvancedCoreStage { + + input_r: chan<(EncOutData, + u32, u32)> in; + output_s: chan<(EncOutData, u32)> out; + + init {( + RunLengthEncoderAdvancedCoreState { + prev_symbol: bits[SYMBOL_WIDTH]:0, + prev_count: bits[COUNT_WIDTH]:0, + prev_last: false, + } + )} + + config ( + input_r: chan<(EncOutData, + u32, u32)> in, + output_s: chan<(EncOutData, + u32)> out, + ) {(input_r, output_s)} + + next (tok: token, state: RunLengthEncoderAdvancedCoreState) { + let empty = ( + EncOutData{ + symbols: zero!(), + counts: zero!(), + last: false + }, + u32:0, + u32:0, + ); + let (tok, input) = recv_if(tok, input_r, !state.prev_last, empty); + + let state_has_valid_symbol = + state.prev_count != zero!(); + let symbols_diff = state.prev_symbol != input.0.symbols[0] && + state_has_valid_symbol; + + let overflow = + (input.0.counts[input.1] as bits[COUNT_WIDTH + u32:1] + + state.prev_count as bits[COUNT_WIDTH + u32:1])[COUNT_WIDTH as s32:]; + + let pack_count = input.0.counts[input.1] + state.prev_count + + overflow as bits[COUNT_WIDTH]; + + let total_pair_count = match(symbols_diff, overflow, state.prev_last) { + (false, false, false) => input.2, + _ => input.2 + u32:1, + }; + + // Never true if state.prev_last is set as input.0.last is false + let combine_last = input.0.last && total_pair_count <= INPUT_WIDTH; + + let (symbols, counts) = match(symbols_diff, overflow) { + (true, _) => ( + (state.prev_symbol as bits[SYMBOL_WIDTH][1]) ++ input.0.symbols, + (state.prev_count as bits[COUNT_WIDTH][1]) ++ input.0.counts, + ), + (false, true) => { + let (symbols, counts, _) = for (idx, (symbols, counts, input_idx)) + in range(u32:0, INPUT_WIDTH + u32:1) { + if idx == input.1 { + (update(symbols, idx, input.0.symbols[input_idx]), + update(counts, idx, std::unsigned_max_value()), + input_idx) + } else if idx == (input.1 + u32:1) { + (update(symbols, idx, input.0.symbols[input_idx]), + update(counts, idx, pack_count), + input_idx + u32:1) + } else { + (update(symbols, idx, input.0.symbols[input_idx]), + update(counts, idx, input.0.counts[input_idx]), + input_idx + u32:1) + } + }((zero!(), + zero!(), + u32:0)); + (symbols, counts) + }, + (false, false) => ( + input.0.symbols ++ bits[SYMBOL_WIDTH]:0 as bits[SYMBOL_WIDTH][1], + update(input.0.counts, input.1, pack_count) ++ + bits[COUNT_WIDTH]:0 as bits[COUNT_WIDTH][1], + ), + _ => ( + zero!(), + zero!(), + ), + }; + + let (symobls_to_send, counts_to_send, last_to_send, pair_count_to_send) = + match (combine_last, state.prev_last) { + (false, false) => { + let idx = total_pair_count - u32:1; + let _symbols = update(symbols, idx, zero!()); + let _counts = update(counts, idx, zero!()); + (slice(_symbols, u32:0, zero!()), + slice(_counts, u32:0, zero!()), + false, idx) + }, + _ => (slice(symbols, u32:0, zero!()), + slice(counts, u32:0, zero!()), + true, total_pair_count), + }; + + let (symbol_to_state, count_to_state, last_to_state) = { + let idx = total_pair_count - u32:1; + match (combine_last, input.0.last) { + (false, true) => (symbols[idx], counts[idx], true), + (false, false) => (symbols[idx], counts[idx], false), + _ => (bits[SYMBOL_WIDTH]:0, bits[COUNT_WIDTH]:0, false), + } + }; + + let output = ( + EncOutData { + symbols: symobls_to_send, + counts: counts_to_send, + last: last_to_send, + }, + pair_count_to_send, + ); + let send = pair_count_to_send > u32:0 || last_to_send; + + let tok = send_if(tok, output_s, send, output); + RunLengthEncoderAdvancedCoreState{ + prev_symbol: symbol_to_state, + prev_count: count_to_state, + prev_last: last_to_state + } + } +} + +// Tests + +const TEST_COMMON_SYMBOL_WIDTH = u32:32; +const TEST_COMMON_COUNT_WIDTH = u32:32; +const TEST_COMMON_INPUT_WIDTH = u32:4; + +type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; +type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; + +type TestCommonSymbols = TestCommonSymbol[TEST_COMMON_INPUT_WIDTH]; +type TestCommonCounts = TestCommonCount[TEST_COMMON_INPUT_WIDTH]; + +type TestCommonStimulus = (TestCommonSymbols, TestCommonCounts, u32, u32); + +type TestCommonEncInData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_INPUT_WIDTH>; +type TestCommonEncInTuple = (TestCommonEncInData, u32, u32); + +type TestCommonEncOutData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_INPUT_WIDTH>; +type TestCommonEncOutTuple = (TestCommonEncOutData, u32); + +// Transaction with counter overflow +const TEST_OVERFLOW_SYMBOL_WIDTH = u32:32; +const TEST_OVERFLOW_COUNT_WIDTH = u32:2; +const TEST_OVERFLOW_INPUT_WIDTH = u32:4; + +type TestOverflowSymbol = bits[TEST_OVERFLOW_SYMBOL_WIDTH]; +type TestOverflowCount = bits[TEST_OVERFLOW_COUNT_WIDTH]; + +type TestOverflowSymbols = TestOverflowSymbol[TEST_OVERFLOW_INPUT_WIDTH]; +type TestOverflowCounts = TestOverflowCount[TEST_OVERFLOW_INPUT_WIDTH]; + +type TestOverflowStimulus = (TestOverflowSymbols, TestOverflowCounts, u32, u32); + +type TestOverflowEncInData = EncOutData; +type TestOverflowEncInTuple = (TestOverflowEncInData, u32, u32); + +type TestOverflowEncOutData = EncOutData; +type TestOverflowEncOutTuple = (TestOverflowEncOutData, u32); + + +// Check empty transaction +#[test_proc] +proc PacketOnlyWithLastSet { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + (TestCommonSymbols: [0x0, 0x0, 0x0, 0x0], + TestCommonCounts: [0x0, 0x0, 0x0, 0x0], + u32:0, u32:0), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, stimulus.3 + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, + _stimulus.1, _stimulus.2); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbols, TestCommonCounts, u32)[1] = [ + (TestCommonSymbols: [0x0, 0x0, 0x0, 0x0], + TestCommonCounts: [0x0, 0x0, 0x0, 0x0], u32:0), + ]; + + let tok = for ((counter, (symbols, counts, pairs)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }, pairs + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Test state interaction with packet that starts with pair which +// has `symbol` equal to RLE's `state.prev_symbol` and doesn't cause +// counter overflow +#[test_proc] +proc CombineWithoutOverflow { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + (TestCommonSymbols: [0x1, 0x0, 0x0, 0x0], + TestCommonCounts: [0x1, 0x0, 0x0, 0x0], + u32:0, u32:1), + (TestCommonSymbols: [0x1, 0x2, 0x3, 0x4], + TestCommonCounts: [0x1, 0x1, 0x1, 0x1], + u32:0, u32:4), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, stimulus.3 + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, + _stimulus.1, _stimulus.2); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbols, TestCommonCounts, u32)[1] = [ + (TestCommonSymbols: [0x1, 0x2, 0x3, 0x4], + TestCommonCounts: [0x2, 0x1, 0x1, 0x1], u32:4), + ]; + + let tok = for ((counter, (symbols, counts, pairs)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }, pairs + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Test state interaction with packet that starts with pair which +// has `symbol` equal to RLE's `state.prev_symbol` and does cause +// counter overflow +#[test_proc] +proc CombineWithOverflow { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_OVERFLOW_SYMBOL_WIDTH, TEST_OVERFLOW_COUNT_WIDTH, + TEST_OVERFLOW_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestOverflowTestStimuli: TestOverflowStimulus[2] = [ + (TestOverflowSymbols: [0x1, 0x0, 0x0, 0x0], + TestOverflowCounts: [0x1, 0x0, 0x0, 0x0], + u32:0, u32:1), + (TestOverflowSymbols: [0x1, 0x2, 0x3, 0x4], + TestOverflowCounts: [0x3, 0x1, 0x1, 0x1], + u32:0, u32:4), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestOverflowStimulus) , token) + in enumerate(TestOverflowTestStimuli) { + let last = counter == (array_size(TestOverflowTestStimuli) - u32:1); + let _stimulus = ( + TestOverflowEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, stimulus.3 + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, + _stimulus.1, _stimulus.2); + (tok) + }(tok); + + let TestOverflowTestOutput: (TestOverflowSymbols, TestOverflowCounts, u32)[2] = [ + (TestOverflowSymbols: [0x1, 0x1, 0x2, 0x3], + TestOverflowCounts: [0x3, 0x1, 0x1, 0x1], u32:4), + (TestOverflowSymbols: [0x4, 0x0, 0x0, 0x0], + TestOverflowCounts: [0x1, 0x0, 0x0, 0x0], u32:1), + ]; + + let tok = for ((counter, (symbols, counts, pairs)), tok): + ((u32, (TestOverflowSymbols, TestOverflowCounts, u32)) , token) + in enumerate(TestOverflowTestOutput) { + let last = counter == (array_size(TestOverflowTestOutput) - u32:1); + let expected = ( + TestOverflowEncOutData{ + symbols: symbols, + counts: counts, + last: last + }, pairs + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Check that state is correctly updated and contains lastest +// symbol, count pair +#[test_proc] +proc CombineAfterStateChange { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[3] = [ + (TestCommonSymbols: [0x1, 0x0, 0x0, 0x0], + TestCommonCounts: [0x1, 0x0, 0x0, 0x0], + u32:0, u32:1), + (TestCommonSymbols: [0x2, 0x3, 0x4, 0x5], + TestCommonCounts: [0x1, 0x1, 0x1, 0x1], + u32:0, u32:4), + (TestCommonSymbols: [0x5, 0x0, 0x0, 0x0], + TestCommonCounts: [0x1, 0x0, 0x0, 0x0], + u32:0, u32:1), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, stimulus.3 + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, + _stimulus.1, _stimulus.2); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbols, TestCommonCounts, u32)[2] = [ + (TestCommonSymbols: [0x1, 0x2, 0x3, 0x4], + TestCommonCounts: [0x1, 0x1, 0x1, 0x1], u32:4), + (TestCommonSymbols: [0x5, 0x0, 0x0, 0x0], + TestCommonCounts: [0x2, 0x0, 0x0, 0x0], u32:1), + ]; + + let tok = for ((counter, (symbols, counts, pairs)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }, pairs + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Check last_combine is correctly used +#[test_proc] +proc CombineStateWithLastPacket { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + (TestCommonSymbols: [0x1, 0x0, 0x0, 0x0], + TestCommonCounts: [0x1, 0x0, 0x0, 0x0], + u32:0, u32:1), + (TestCommonSymbols: [0x2, 0x3, 0x4, 0x0], + TestCommonCounts: [0x1, 0x1, 0x1, 0x0], + u32:0, u32:3), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = ( + TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }, stimulus.2, stimulus.3 + ); + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, _stimulus.0.symbols, _stimulus.0.counts, _stimulus.0.last, + _stimulus.1, _stimulus.2); + (tok) + }(tok); + + let TestCommonTestOutput: (TestCommonSymbols, TestCommonCounts, u32)[1] = [ + (TestCommonSymbols: [0x1, 0x2, 0x3, 0x4], + TestCommonCounts: [0x1, 0x1, 0x1, 0x1], u32:4), + ]; + + let tok = for ((counter, (symbols, counts, pairs)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }, pairs + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} + +// Check that state is correctly cleared after `last` is seen +#[test_proc] +proc NoStateSipllAfterLast { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + init{()} + config( + terminator: chan out + ) { + let (input_s, input_r) = chan; + let (output_s, output_r) = chan; + spawn RunLengthEncoderAdvancedCoreStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, + TEST_COMMON_INPUT_WIDTH>(input_r, output_s); + (terminator, input_s, output_r) + } + next(tok: token, state: ()) { + let TestCommonTestStimuli: TestCommonEncInTuple[2] = [ + (TestCommonEncInData { + symbols: TestCommonSymbols:[0x1, 0x0, 0x0, 0x0], + counts: TestCommonCounts:[0x1, 0x0, 0x0, 0x0], + last: true + }, u32:0, u32:1), + (TestCommonEncInData { + symbols: TestCommonSymbols:[0x1, 0x0, 0x0, 0x0], + counts: TestCommonCounts:[0x1, 0x0, 0x0, 0x0], + last: true + }, u32:0, u32:1), + ]; + + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonEncInTuple) , token) + in enumerate(TestCommonTestStimuli) { + let tok = send(tok, enc_input_s, stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}, propagation: {}, pair_count: {}", + counter, stimulus.0.symbols, stimulus.0.counts, stimulus.0.last, + stimulus.1, stimulus.2); + (tok) + }(tok); + + let TestCommonTestOutput: TestCommonEncOutTuple[2] = [ + (TestCommonEncOutData { + symbols: [0x1, 0x0, 0x0, 0x0], + counts: [0x1, 0x0, 0x0, 0x0], + last: true, + }, u32:1), + (TestCommonEncOutData { + symbols: [0x1, 0x0, 0x0, 0x0], + counts: [0x1, 0x0, 0x0, 0x0], + last: true, + }, u32:1), + ]; + + let tok = for ((counter, expected), tok): + ((u32, TestCommonEncOutTuple) , token) + in enumerate(TestCommonTestOutput) { + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, pairs: {}", + counter, enc_output.0.symbols, enc_output.0.counts, enc_output.0.last, + enc_output.1); + assert_eq(enc_output, expected); + (tok) + }(tok); + + send(tok, terminator, true); + () + } +} diff --git a/xls/modules/rle/rle_enc_adv_realign_stage.x b/xls/modules/rle/rle_enc_adv_realign_stage.x new file mode 100644 index 0000000000..30f3fe4cb7 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_realign_stage.x @@ -0,0 +1,405 @@ +// 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. + +import std +import xls.modules.rle.rle_common as rle_common + +type EncInData = rle_common::PlainData; +type EncOutData = rle_common::CompressedData; + +pub proc RunLengthEncoderAdvancedRealignStage< + SYMBOL_WIDTH: u32, COUNT_WIDTH: u32,INPUT_WIDTH:u32> { + + input_r: chan> in; + output_s: chan<(EncOutData, u32, u32)> out; + + init{()} + config( + input_r: chan> in, + output_s: chan<(EncOutData, u32, u32)> out, + ) {(input_r, output_s)} + + next (tok:token, state: ()) { + let (tok, input) = recv(tok, input_r); + let (symbols, counts, end) = + for (idx, (symbols, counts, insert_place)) in range(u32:0, INPUT_WIDTH) { + if input.counts[idx] != bits[COUNT_WIDTH]:0 { + ( + update(symbols, insert_place, input.symbols[idx]), + update(counts, insert_place, input.counts[idx]), + insert_place + u32:1 + ) + } else { + (symbols, counts, insert_place) + } + } ((zero!(), + zero!(), + u32:0)); + let output = EncOutData { + symbols: symbols, + counts: counts, + last: input.last, + }; + let (prop_count, _, _) = + for (idx, (p_count, symbol, valid)) in range(u32:1, INPUT_WIDTH) { + let symbol_eq = symbols[idx] == symbol; + match (valid, symbol_eq) { + (false, _) => (p_count, symbol, valid), + (true, true) => (p_count + u32:1, symbol, valid), + (true, false) => (p_count, symbol, false), + _ => (u32:0, bits[SYMBOL_WIDTH]:0, false), + } + } ((u32:0, symbols[u32:0], counts[u32:0] != bits[COUNT_WIDTH]:0)); + send(tok, output_s, (output, prop_count, end)); + () + } +} + +// Test RealignStage + +// Transaction without overflow +const TEST_COMMON_SYMBOL_WIDTH = u32:32; +const TEST_COMMON_COUNT_WIDTH = u32:32; +const TEST_COMMON_SYMBOL_COUNT = u32:4; + +type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; +type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; + +type TestCommonSymbols = TestCommonSymbol[TEST_COMMON_SYMBOL_COUNT]; +type TestCommonCounts = TestCommonCount[TEST_COMMON_SYMBOL_COUNT]; + +type TestCommonStimulus = (TestCommonSymbols, TestCommonCounts); + +type TestCommonEncData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>; + +type TestCommonEncInData = TestCommonEncData; +type TestCommonEncOutData = (TestCommonEncData, u32, u32); + +// Transaction with counter overflow +const OVERFLOW_COUNT_WIDTH = u32:2; + +type OverflowSymbol = TestCommonSymbol; +type OverflowCount = bits[OVERFLOW_COUNT_WIDTH]; + +type OverflowSymbols = OverflowSymbol[TEST_COMMON_SYMBOL_COUNT]; +type OverflowCounts = OverflowCount[TEST_COMMON_SYMBOL_COUNT]; + +type OverflowStimulus = (TestCommonSymbols, OverflowCounts); + +type OverflowEncData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, OVERFLOW_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>; + +type OverflowEncInData = OverflowEncData; +type OverflowEncOutData = (OverflowEncData, u32, u32); + +#[test_proc] +proc RunLengthEncoderAdvancedRealignStageNoPairs { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedRealignStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0,], + [TestCommonCount:0x0, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x0,] + ), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.counts, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbols, TestCommonCounts, u32, u32)[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0,], + [TestCommonCount:0x0, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x0,], + u32:0, + u32:0, + ), + ]; + let tok = for ((counter, (symbols, counts, propagation, end)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncData{ + symbols: symbols, + counts: counts, + last: last + }, + propagation, + end, + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, propagation: {}", + counter, enc_output.0.symbols, enc_output.0.counts, + enc_output.0.last, enc_output.1 + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedRealignStageAllPairsFilled { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedRealignStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2, + TestCommonSymbol:0x3, TestCommonSymbol:0x4,], + [TestCommonCount:0x1, TestCommonCount:0x1, + TestCommonCount:0x1, TestCommonCount:0x1,] + ), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, last: {}", + counter, _stimulus.symbols, _stimulus.counts, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbols, TestCommonCounts, u32, u32)[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2, + TestCommonSymbol:0x3, TestCommonSymbol:0x4,], + [TestCommonCount:0x1, TestCommonCount:0x1, + TestCommonCount:0x1, TestCommonCount:0x1,], + u32:0, + u32:4, + ), + ]; + let tok = for ((counter, (symbols, counts, propagation, end)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncData{ + symbols: symbols, + counts: counts, + last: last + }, + propagation, + end, + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, propagation: {}", + counter, enc_output.0.symbols, enc_output.0.counts, + enc_output.0.last, enc_output.1 + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedRealignStageFarAwayPair { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedRealignStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x1,], + [TestCommonCount:0x0, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x4,] + ), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.counts, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbol[4], TestCommonCount[4], u32, u32)[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0,], + [TestCommonCount:0x4, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x0,], + u32:0, + u32:1, + ), + ]; + let tok = for ((counter, (symbols, counts, propagation, end)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts, u32, u32)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = ( + TestCommonEncData{ + symbols: symbols, + counts: counts, + last: last + }, + propagation, + end, + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, propagation: {}", + counter, enc_output.0.symbols, enc_output.0.counts, + enc_output.0.last, enc_output.1 + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedRealignStagePropagataion { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedRealignStage< + TEST_COMMON_SYMBOL_WIDTH, OVERFLOW_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let OverflowTestStimuli: OverflowStimulus[1] = [ + ([OverflowSymbol:0x0, OverflowSymbol:0x0, + OverflowSymbol:0x1, OverflowSymbol:0x1,], + [OverflowCount:0x0, OverflowCount:0x0, + OverflowCount:0x3, OverflowCount:0x1,] + ), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, OverflowStimulus) , token) + in enumerate(OverflowTestStimuli) { + let last = counter == (array_size(OverflowTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + counts: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, counts: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.counts, _stimulus.last); + (tok) + }(tok); + let OverflowTestOutput: + (OverflowSymbol[4], OverflowCount[4], u32, u32)[1] = [ + ([OverflowSymbol:0x1, OverflowSymbol:0x1, + OverflowSymbol:0x0, OverflowSymbol:0x0,], + [OverflowCount:0x3, OverflowCount:0x1, + OverflowCount:0x0, OverflowCount:0x0,], + u32:1, + u32:2, + ), + ]; + let tok = for ((counter, (symbols, counts, propagation, end)), tok): + ((u32, (TestCommonSymbols, OverflowCounts, u32, u32)) , token) + in enumerate(OverflowTestOutput) { + let last = counter == (array_size(OverflowTestOutput) - u32:1); + let expected = ( + TestCommonEncData{ + symbols: symbols, + counts: counts, + last: last + }, + propagation, + end, + ); + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}, propagation: {}", + counter, enc_output.0.symbols, enc_output.0.counts, + enc_output.0.last, enc_output.1 + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} diff --git a/xls/modules/rle/rle_enc_adv_reduce_stage.x b/xls/modules/rle/rle_enc_adv_reduce_stage.x new file mode 100644 index 0000000000..6db583bfbb --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_reduce_stage.x @@ -0,0 +1,378 @@ +// 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. + +import std +import xls.modules.rle.rle_common as rle_common + +type EncInData = rle_common::PlainData; +type EncOutData = rle_common::CompressedData; + +pub proc RunLengthEncoderAdvancedReduceStage< + SYMBOL_WIDTH: u32, COUNT_WIDTH: u32,INPUT_WIDTH:u32> { + + input_r: chan> in; + output_s: chan> out; + + init {()} + config ( + input_r: chan> in, + output_s: chan> out, + ) {(input_r, output_s)} + + next (tok: token, state: ()) { + let (tok, input) = recv(tok, input_r); + let (symbols, counts, symbol, count) = + for (idx, (symbols, counts, symbol, count)) in range(u32:1, INPUT_WIDTH) { + let _symbol = input.symbols[idx]; + let valid = input.symbol_valids[idx]; + let symbols_diff = symbol != _symbol; + let overflow = count == std::unsigned_max_value(); + match (valid, overflow, symbols_diff) { + (false, _, _) => (symbols, counts, symbol, count), + (true, false, false) => (symbols, counts, + symbol, count + bits[COUNT_WIDTH]: 1), + _ => ( + update(symbols, idx - u32:1, symbol), + update(counts, idx - u32:1, count), + _symbol, + bits[COUNT_WIDTH]: 1, + ), + } + } ((zero!(), + zero!(), + input.symbols[u32:0], + input.symbol_valids[u32:0] as bits[COUNT_WIDTH])); + let symbols = update(symbols, INPUT_WIDTH - u32:1, symbol); + let counts = update(counts, INPUT_WIDTH - u32:1, count); + let output = EncOutData { + symbols: symbols, + counts: counts, + last: input.last, + }; + let not_empty = input.last || + or_reduce(std::convert_to_bits_msb0(input.symbol_valids)); + send_if(tok, output_s, not_empty, output); + () + } +} + +// Test ReduceStage + +// Transaction without overflow +const TEST_COMMON_SYMBOL_WIDTH = u32:32; +const TEST_COMMON_COUNT_WIDTH = u32:32; +const TEST_COMMON_SYMBOL_COUNT = u32:4; + +type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; +type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; + +type TestCommonSymbols = TestCommonSymbol[TEST_COMMON_SYMBOL_COUNT]; +type TestCommonCounts = TestCommonCount[TEST_COMMON_SYMBOL_COUNT]; + +type TestCommonStimulus = ( + TestCommonSymbols, bits[1][TEST_COMMON_SYMBOL_COUNT]); + +type TestCommonEncInData = EncInData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_SYMBOL_COUNT>; +type TestCommonEncOutData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>; + +// Transaction with counter overflow +const OVERFLOW_COUNT_WIDTH = u32:2; + +type OverflowSymbol = TestCommonSymbol; +type OverflowCount = bits[OVERFLOW_COUNT_WIDTH]; + +type OverflowSymbols = OverflowSymbol[TEST_COMMON_SYMBOL_COUNT]; +type OverflowCounts = OverflowCount[TEST_COMMON_SYMBOL_COUNT]; + +type OverflowStimulus = ( + TestCommonSymbols, bits[1][TEST_COMMON_SYMBOL_COUNT]); + +type OverflowEncInData = TestCommonEncInData; +type OverflowEncOutData = EncOutData< + TEST_COMMON_SYMBOL_WIDTH, OVERFLOW_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>; + + +#[test_proc] +proc RunLengthEncoderAdvancedReduceStageNoSymbols { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedReduceStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[2] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0], + [bits[1]:0, bits[1]:0, bits[1]:0, bits[1]:0], + ), + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0], + [bits[1]:0, bits[1]:0, bits[1]:0, bits[1]:0], + ) + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, symbol_valids: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.symbol_valids, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbols, TestCommonCounts)[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x0,], + [TestCommonCount:0x0, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x0,] + ), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbols, TestCommonCounts)) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedReduceStageNonRepeatingSymbols { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedReduceStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2, + TestCommonSymbol:0x3, TestCommonSymbol:0x4], + [bits[1]:1, bits[1]:1, bits[1]:1, bits[1]:1], + ) + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, symbol_valids: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.symbol_valids, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbol[4], TestCommonCount[4])[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x2, + TestCommonSymbol:0x3, TestCommonSymbol:0x4,], + [TestCommonCount:0x1, TestCommonCount:0x1, + TestCommonCount:0x1, TestCommonCount:0x1,] + ), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbol[4], TestCommonCount[4])) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedReduceStageRepeatingSymbolsNoOverflow { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedReduceStage< + TEST_COMMON_SYMBOL_WIDTH, TEST_COMMON_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let TestCommonTestStimuli: TestCommonStimulus[1] = [ + ([TestCommonSymbol:0x1, TestCommonSymbol:0x1, + TestCommonSymbol:0x1, TestCommonSymbol:0x1], + [bits[1]:1, bits[1]:1, bits[1]:1, bits[1]:1], + ) + ]; + let tok = for ((counter, stimulus), tok): + ((u32, TestCommonStimulus) , token) + in enumerate(TestCommonTestStimuli) { + let last = counter == (array_size(TestCommonTestStimuli) - u32:1); + let _stimulus = TestCommonEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, symbol_valids: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.symbol_valids, _stimulus.last); + (tok) + }(tok); + let TestCommonTestOutput: + (TestCommonSymbol[4], TestCommonCount[4])[1] = [ + ([TestCommonSymbol:0x0, TestCommonSymbol:0x0, + TestCommonSymbol:0x0, TestCommonSymbol:0x1,], + [TestCommonCount:0x0, TestCommonCount:0x0, + TestCommonCount:0x0, TestCommonCount:0x4,] + ), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (TestCommonSymbol[4], TestCommonCount[4])) , token) + in enumerate(TestCommonTestOutput) { + let last = counter == (array_size(TestCommonTestOutput) - u32:1); + let expected = TestCommonEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +} + +#[test_proc] +proc RunLengthEncoderAdvancedReduceStageRepeatingSymbolsOverflow { + terminator: chan out; + enc_input_s: chan out; + enc_output_r: chan in; + + init {()} + config (terminator: chan out) { + let (enc_input_s, enc_input_r) = chan; + let (enc_output_s, enc_output_r) = chan; + + spawn RunLengthEncoderAdvancedReduceStage< + TEST_COMMON_SYMBOL_WIDTH, OVERFLOW_COUNT_WIDTH, TEST_COMMON_SYMBOL_COUNT>( + enc_input_r, enc_output_s); + (terminator, enc_input_s, enc_output_r) + } + next (tok: token, state:()) { + let OverflowTestStimuli: OverflowStimulus[1] = [ + ([OverflowSymbol:0x1, OverflowSymbol:0x1, + OverflowSymbol:0x1, OverflowSymbol:0x1], + [bits[1]:1, bits[1]:1, bits[1]:1, bits[1]:1], + ) + ]; + let tok = for ((counter, stimulus), tok): + ((u32, OverflowStimulus) , token) + in enumerate(OverflowTestStimuli) { + let last = counter == (array_size(OverflowTestStimuli) - u32:1); + let _stimulus = OverflowEncInData{ + symbols: stimulus.0, + symbol_valids: stimulus.1, + last: last + }; + let tok = send(tok, enc_input_s, _stimulus); + trace_fmt!("Sent {} stimuli, symbols: {:x}, symbol_valids: {:x}, lasts: {}", + counter, _stimulus.symbols, _stimulus.symbol_valids, _stimulus.last); + (tok) + }(tok); + let OverflowTestOutput: + (OverflowSymbol[4], OverflowCount[4])[1] = [ + ([OverflowSymbol:0x0, OverflowSymbol:0x0, + OverflowSymbol:0x1, OverflowSymbol:0x1,], + [OverflowCount:0x0, OverflowCount:0x0, + OverflowCount:0x3, OverflowCount:0x1,] + ), + ]; + let tok = for ((counter, (symbols, counts)), tok): + ((u32, (OverflowSymbol[4], OverflowCount[4])) , token) + in enumerate(OverflowTestOutput) { + let last = counter == (array_size(OverflowTestOutput) - u32:1); + let expected = OverflowEncOutData{ + symbols: symbols, + counts: counts, + last: last + }; + let (tok, enc_output) = recv(tok, enc_output_r); + trace_fmt!( + "Received {} pairs, symbols: {:x}, counts: {}, last: {}", + counter, enc_output.symbols, enc_output.counts, enc_output.last + ); + assert_eq(enc_output, expected); + (tok) + }(tok); + send(tok, terminator, true); + () + } +}