diff --git a/xls/modules/rle/BUILD b/xls/modules/rle/BUILD index ad255af4dd..c84cc8efa4 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_common.x b/xls/modules/rle/rle_common.x index 8b1217ff2c..525271fe7e 100644 --- a/xls/modules/rle/rle_common.x +++ b/xls/modules/rle/rle_common.x @@ -19,16 +19,17 @@ // Structure is used as an input and an output to and from // a preprocessing stage as well as an input to a RLE encoder. // It is also used as an output from RLE decoder. -pub struct PlainData { - symbol: bits[SYMB_WIDTH], // symbol - last: bool, // flush RLE +pub struct PlainData { + symbols: bits[SYMB_WIDTH][SYMB_COUNT], // symbols + symbol_valids: bits[1][SYMB_COUNT], // symbol valid + last: bool, // flush RLE } // Structure contains compressed (symbol, counter) pairs. // Structure is used as an output from RLE encoder and // as an input to RLE decoder. -pub struct CompressedData { - symbol: bits[SYMBOL_WIDTH], // symbol - count: bits[COUNT_WIDTH], // symbol counter - last: bool, // flush RLE +pub struct CompressedData { + symbols: bits[SYMBOL_WIDTH][PAIR_COUNT], // symbol + counts: bits[COUNT_WIDTH][PAIR_COUNT], // symbol counter + last: bool, // flush RLE } diff --git a/xls/modules/rle/rle_dec.x b/xls/modules/rle/rle_dec.x index b9d70808f6..28e820ac51 100644 --- a/xls/modules/rle/rle_dec.x +++ b/xls/modules/rle/rle_dec.x @@ -63,8 +63,8 @@ struct RunLengthDecoderState { } // RLE decoder implementation pub proc RunLengthDecoder { - input_r: chan> in; - output_s: chan> out; + input_r: chan> in; + output_s: chan> out; init {( RunLengthDecoderState { @@ -75,34 +75,38 @@ pub proc RunLengthDecoder { )} config ( - input_r: chan> in, - output_s: chan> out, + input_r: chan> in, + output_s: chan> out, ) {(input_r, output_s)} next (tok: token, state: RunLengthDecoderState) { let state_input = DecInData { - symbol: state.symbol, - count: state.count, + symbols: [state.symbol], + counts: [state.count], last: state.last }; let recv_next_symbol = (state.count == bits[COUNT_WIDTH]:0); let (tok, input) = recv_if(tok, input_r, recv_next_symbol, state_input); - let next_count = if input.count == bits[COUNT_WIDTH]:0 { - fail!("invalid_count_0", input.count) + let next_count = if input.counts[0] == bits[COUNT_WIDTH]:0 { + input.counts[0] } else { - input.count - bits[COUNT_WIDTH]:1 + input.counts[0] - bits[COUNT_WIDTH]:1 }; let done_sending = (next_count == bits[COUNT_WIDTH]:0); let send_last = input.last && done_sending; - let data_tok = send(tok, output_s, DecOutData { - symbol: input.symbol, - last: send_last + let symbol_valid = input.counts[0] > bits[COUNT_WIDTH]:0; + let data_tok = send_if(tok, output_s, + symbol_valid || send_last, + DecOutData { + symbols: [input.symbols[0]], + symbol_valids: [symbol_valid], + last: send_last }); if (send_last) { zero!() } else { RunLengthDecoderState { - symbol: input.symbol, + symbol: input.symbols[0], count: next_count, last: input.last, } @@ -116,8 +120,8 @@ proc RunLengthDecoder32 { init {()} config ( - input_r: chan> in, - output_s: chan> out, + input_r: chan> in, + output_s: chan> out, ) { spawn RunLengthDecoder(input_r, output_s); () @@ -136,8 +140,8 @@ const TEST_COUNT_WIDTH = u32:32; type TestSymbol = bits[TEST_SYMBOL_WIDTH]; type TestCount = bits[TEST_COUNT_WIDTH]; type TestStimulus = (TestSymbol, TestCount); -type TestDecInData = DecInData; -type TestDecOutData = DecOutData; +type TestDecInData = DecInData; +type TestDecOutData = DecOutData; // Check RLE decoder on a transaction #[test_proc] @@ -171,13 +175,13 @@ proc RunLengthDecoderTransactionTest { in enumerate(TransactionTestStimuli) { let last = counter == (array_size(TransactionTestStimuli) - u32:1); let data_in = TestDecInData{ - symbol: stimulus.0, - count: stimulus.1, + symbols: [stimulus.0], + counts: [stimulus.1], last: last }; let tok = send(tok, dec_input_s, data_in); trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, count:{}, last: {}", - counter + u32:1, data_in.symbol, data_in.count, data_in.last); + counter + u32:1, data_in.symbols[0], data_in.counts[0], data_in.last); (tok) }(tok); let TransationTestOutputs: TestSymbol[14] = [ @@ -194,13 +198,14 @@ proc RunLengthDecoderTransactionTest { in enumerate(TransationTestOutputs) { let last = counter == (array_size(TransationTestOutputs) - u32:1); let data_out = TestDecOutData{ - symbol: symbol, + symbols: [symbol], + symbol_valids: [bits[1]:1], last: last }; let (tok, dec_output) = recv(tok, dec_output_r); trace_fmt!( "Received {} transactions, symbol: 0x{:x}, last: {}", - counter, dec_output.symbol, dec_output.last + counter, dec_output.symbols[0], dec_output.last ); assert_eq(dec_output, data_out); (tok) @@ -209,6 +214,80 @@ proc RunLengthDecoderTransactionTest { } } +// Check that RLE decoder will remove empty pairs, `count == 0`. +// Check that RLE decoder will set `symbol_valids` to 0 only in +// the last output packet. +#[test_proc] +proc RunLengthDecoderZeroCountTest { + terminator: chan out; // test termination request + dec_input_s: chan out; + dec_output_r: chan in; + + init {()} + + config(terminator: chan out) { + let (dec_input_s, dec_input_r) = chan; + let (dec_output_s, dec_output_r) = chan; + + spawn RunLengthDecoder( + dec_input_r, dec_output_s); + (terminator, dec_input_s, dec_output_r) + } + + next(tok: token, state: ()) { + let ZeroCountTestStimuli: TestStimulus[6] =[ + (TestSymbol:0xB, TestCount:0x2), + (TestSymbol:0x1, TestCount:0x0), + (TestSymbol:0xC, TestCount:0x1), + (TestSymbol:0xC, TestCount:0x0), + (TestSymbol:0x3, TestCount:0x3), + (TestSymbol:0x2, TestCount:0x0), + ]; + let tok = for ((counter, stimulus), tok): + ((u32, (TestSymbol, TestCount)) , token) + in enumerate(ZeroCountTestStimuli) { + let last = counter == (array_size(ZeroCountTestStimuli) - u32:1); + let data_in = TestDecInData{ + symbols: [stimulus.0], + counts: [stimulus.1], + last: last + }; + let tok = send(tok, dec_input_s, data_in); + trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, count:{}, last: {}", + counter + u32:1, data_in.symbols[0], data_in.counts[0], data_in.last); + (tok) + }(tok); + let ZeroCountTestOutputs: TestDecOutData[7] = [ + TestDecOutData{symbols: [TestSymbol: 0xB], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0xB], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0xC], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0x3], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0x3], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0x3], + symbol_valids: [true], last: false}, + TestDecOutData{symbols: [TestSymbol: 0x2], + symbol_valids: [false], last: true}, + ]; + let tok = for ((counter, output), tok): + ((u32, TestDecOutData) , token) + in enumerate(ZeroCountTestOutputs) { + let (tok, dec_output) = recv(tok, dec_output_r); + trace_fmt!( + "Received {} transactions, symbols: 0x{:x}, last: {}", + counter + u32:1, dec_output.symbols, dec_output.last + ); + assert_eq(dec_output, output); + (tok) + }(tok); + send(tok, terminator, true); + } +} + // Check that RLE decoder will create 2 `last` output packets, // when 2 `last` input packets were consumed. #[test_proc] @@ -231,13 +310,13 @@ proc RunLengthDecoderLastAfterLastTest { next(tok: token, state: ()) { let LastAfterLastTestStimuli: TestDecInData[2] =[ TestDecInData { - symbol: TestSymbol:0x1, - count: TestCount:0x1, + symbols: [TestSymbol:0x1], + counts: [TestCount:0x1], last:true }, TestDecInData { - symbol: TestSymbol:0x2, - count: TestCount:0x1, + symbols: [TestSymbol:0x2], + counts: [TestCount:0x1], last:true }, ]; @@ -245,21 +324,23 @@ proc RunLengthDecoderLastAfterLastTest { ((u32, TestDecInData) , token) in enumerate(LastAfterLastTestStimuli) { let tok = send(tok, dec_input_s, stimulus); - trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, count:{}, last: {}", - counter + u32:1, stimulus.symbol, stimulus.count, stimulus.last); + trace_fmt!("Sent {} stimuli, symbols: 0x{:x}, counts:{}, last: {}", + counter + u32:1, stimulus.symbols, stimulus.counts, stimulus.last); (tok) }(tok); let LastAfterLastTestOutputs: TestDecOutData[2] = [ - TestDecOutData{symbol: TestSymbol: 0x1, last: true}, - TestDecOutData{symbol: TestSymbol: 0x2, last: true}, + TestDecOutData{symbols: [TestSymbol: 0x1], + symbol_valids: [true], last: true}, + TestDecOutData{symbols: [TestSymbol: 0x2], + symbol_valids: [true], last: true}, ]; let tok = for ((counter, output), tok): ((u32, TestDecOutData) , token) in enumerate(LastAfterLastTestOutputs) { let (tok, dec_output) = recv(tok, dec_output_r); trace_fmt!( - "Received {} transactions, symbol: 0x{:x}, last: {}", - counter + u32:1, dec_output.symbol, dec_output.last + "Received {} transactions, symbols: 0x{:x}, last: {}", + counter + u32:1, dec_output.symbols, dec_output.last ); assert_eq(dec_output, output); (tok) diff --git a/xls/modules/rle/rle_enc.x b/xls/modules/rle/rle_enc.x index 386faf9131..0ce2610663 100644 --- a/xls/modules/rle/rle_enc.x +++ b/xls/modules/rle/rle_enc.x @@ -66,8 +66,8 @@ struct RunLengthEncoderState { // RLE encoder implementation pub proc RunLengthEncoder { - input_r: chan> in; - output_s: chan> out; + input_r: chan> in; + output_s: chan> out; init {( RunLengthEncoderState { @@ -78,47 +78,49 @@ pub proc RunLengthEncoder { )} config ( - input_r: chan> in, - output_s: chan> out, + input_r: chan> in, + output_s: chan> out, ) {(input_r, output_s)} next (tok: token, state: RunLengthEncoderState) { let zero_input = EncInData { - symbol: bits[SYMBOL_WIDTH]:0, + symbols: bits[SYMBOL_WIDTH][1]:[bits[SYMBOL_WIDTH]:0], + symbol_valids: bits[1][1]:[bits[1]:0], last: false }; let (input_tok, input) = recv_if( tok, input_r, !state.prev_last, zero_input); + let symbol_valid = input.symbol_valids[0]; let prev_symbol_valid = state.prev_count != bits[COUNT_WIDTH]:0; - let symbol_differ = prev_symbol_valid && ( - input.symbol != state.prev_symbol); + let symbol_differ = prev_symbol_valid && symbol_valid && + (input.symbols[0] != state.prev_symbol); let overflow = state.prev_count == std::unsigned_max_value(); - let (symbol, count, last) = if (state.prev_last) { - ( - bits[SYMBOL_WIDTH]:0, - bits[COUNT_WIDTH]:0, - false - ) - } else if (symbol_differ || overflow) { - ( - input.symbol, - bits[COUNT_WIDTH]:1, - input.last, - ) + let count = match (symbol_valid, overflow, symbol_differ, state.prev_last) { + (true, true, _, false) => bits[COUNT_WIDTH]:1, + (true, false, true, false) => bits[COUNT_WIDTH]:1, + (true, false, false, false) => state.prev_count + bits[COUNT_WIDTH]:1, + (false, false, _, false) => state.prev_count, + _ => bits[COUNT_WIDTH]:0, + }; + + let symbol = if symbol_valid && (symbol_differ || !prev_symbol_valid) { + input.symbols[0] } else { - ( - input.symbol, - state.prev_count + bits[COUNT_WIDTH]:1, - input.last, - ) + state.prev_symbol + }; + + let last = match (state.prev_last, input.last) { + (true, false) => false, + (false, true) => true, + _ => state.prev_last, }; let data = EncOutData { - symbol: state.prev_symbol, - count: state.prev_count, + symbols: [state.prev_symbol], + counts: [state.prev_count], last: state.prev_last }; @@ -139,8 +141,8 @@ proc RunLengthEncoder32 { init {()} config ( - input_r: chan> in, - output_s: chan> out, + input_r: chan> in, + output_s: chan> out, ) { spawn RunLengthEncoder(input_r, output_s); () @@ -159,9 +161,9 @@ const TEST_COMMON_COUNT_WIDTH = u32:32; type TestCommonSymbol = bits[TEST_COMMON_SYMBOL_WIDTH]; type TestCommonCount = bits[TEST_COMMON_COUNT_WIDTH]; -type TestCommonEncInData = EncInData; +type TestCommonEncInData = EncInData; type TestCommonEncOutData = - EncOutData; + EncOutData; // Simple transaction without overflow const CountSymbolTestSymbolWidth = TEST_COMMON_SYMBOL_WIDTH; @@ -197,10 +199,14 @@ proc RunLengthEncoderCountSymbolTest { ((u32, CountSymbolTestStimulus) , token) in enumerate(CountSymbolTestTestStimuli) { let last = counter == (array_size(CountSymbolTestTestStimuli) - u32:1); - let stimulus = CountSymbolTestEncInData{symbol: symbol, last: last}; + let stimulus = CountSymbolTestEncInData{ + symbols: [symbol], + symbol_valids: [bits[1]:1], + last: last + }; let tok = send(tok, enc_input_s, stimulus); trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, last: {}", - counter, stimulus.symbol, stimulus.last); + counter, stimulus.symbols[0], stimulus.last); (tok) }(tok); let CountSymbolTestTestOutput: @@ -213,11 +219,11 @@ proc RunLengthEncoderCountSymbolTest { in enumerate(CountSymbolTestTestOutput) { let last = counter == (array_size(CountSymbolTestTestOutput) - u32:1); let expected = CountSymbolTestEncOutData{ - symbol: symbol, count: count, last: last}; + symbols: [symbol], counts: [count], last: last}; let (tok, enc_output) = recv(tok, enc_output_r); trace_fmt!( "Received {} pairs, symbol: 0x{:x}, count: {}, last: {}", - counter, enc_output.symbol, enc_output.count, enc_output.last + counter, enc_output.symbols[0], enc_output.counts[0], enc_output.last ); assert_eq(enc_output, expected); (tok) @@ -235,7 +241,7 @@ type OverflowSymbol = TestCommonSymbol; type OverflowCount = bits[OverflowCountWidth]; type OverflowEncInData = TestCommonEncInData; type OverflowEncOutData = - EncOutData; + EncOutData; #[test_proc] proc RunLengthEncoderOverflowTest { @@ -267,10 +273,14 @@ proc RunLengthEncoderOverflowTest { in enumerate(OverflowTestStimuli) { let last = counter == ( array_size(OverflowTestStimuli) - u32:1); - let stimulus = OverflowEncInData{symbol: symbol, last: last}; + let stimulus = OverflowEncInData{ + symbols: [symbol], + symbol_valids: [bits[1]:1], + last: last + }; let tok = send(tok, enc_input_s, stimulus); trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, last: {}", - counter, stimulus.symbol, stimulus.last); + counter, stimulus.symbols[0], stimulus.last); (tok) }(tok); let OverflowTestOutput: @@ -287,11 +297,11 @@ proc RunLengthEncoderOverflowTest { in enumerate(OverflowTestOutput) { let last = counter == (array_size(OverflowTestOutput) - u32:1); let expected = OverflowEncOutData{ - symbol: symbol, count: count, last: last}; + symbols: [symbol], counts: [count], last: last}; let (tok, enc_output) = recv(tok, enc_output_r); trace_fmt!( "Received {} pairs, symbol: 0x{:x}, count: {}, last: {}", - counter, enc_output.symbol, enc_output.count, enc_output.last + counter, enc_output.symbols[0], enc_output.counts[0], enc_output.last ); assert_eq(enc_output, expected); (tok) @@ -329,25 +339,33 @@ proc RunLengthEncoderLastAfterLastTest { } next (tok: token, state:()) { let LastAfterLastTestStimuli: LastAfterLastStimulus[2] = [ - LastAfterLastStimulus {symbol: LastAfterLastSymbol:0x1, last: true}, - LastAfterLastStimulus {symbol: LastAfterLastSymbol:0x1, last: true}, + LastAfterLastStimulus { + symbols: [LastAfterLastSymbol:0x1], + symbol_valids: [bits[1]:1], + last: true + }, + LastAfterLastStimulus { + symbols: [LastAfterLastSymbol:0x1], + symbol_valids: [bits[1]:1], + last: true + }, ]; let tok = for ((counter, stimuli), tok): ((u32, LastAfterLastStimulus) , token) in enumerate(LastAfterLastTestStimuli) { let tok = send(tok, enc_input_s, stimuli); trace_fmt!("Sent {} transactions, symbol: 0x{:x}, last: {}", - counter, stimuli.symbol, stimuli.last); + counter, stimuli.symbols[0], stimuli.last); (tok) }(tok); let LastAfterLastTestOutput: LastAfterLastOutput[2] = [ LastAfterLastOutput { - symbol: LastAfterLastSymbol:0x1, - count: LastAfterLastCount:0x1, + symbols: [LastAfterLastSymbol:0x1], + counts: [LastAfterLastCount:0x1], last:true}, LastAfterLastOutput { - symbol: LastAfterLastSymbol:0x1, - count: LastAfterLastCount:0x1, + symbols: [LastAfterLastSymbol:0x1], + counts: [LastAfterLastCount:0x1], last:true}, ]; let tok = for ((counter, expected), tok): @@ -356,7 +374,7 @@ proc RunLengthEncoderLastAfterLastTest { let (tok, enc_output) = recv(tok, enc_output_r); trace_fmt!( "Received {} pairs, symbol: 0x{:x}, count: {}, last: {}", - counter, enc_output.symbol, enc_output.count, enc_output.last + counter, enc_output.symbols[0], enc_output.counts[0], enc_output.last ); assert_eq(enc_output, expected); @@ -377,7 +395,7 @@ type OverflowWithLastCount = type OverflowWithLastEncInData = TestCommonEncInData; type OverflowWithLastEncOutData = EncOutData; + OverflowWithLastCountWidth, 1>; #[test_proc] proc RunLengthEncoderOverflowWithLastTest { @@ -406,10 +424,14 @@ proc RunLengthEncoderOverflowWithLastTest { in enumerate(OverflowWithLastTestStimuli) { let last = counter == ( array_size(OverflowWithLastTestStimuli) - u32:1); - let stimulus = OverflowWithLastEncInData{symbol: symbol, last: last}; + let stimulus = OverflowWithLastEncInData{ + symbols: [symbol], + symbol_valids: [bits[1]:1], + last: last + }; let tok = send(tok, enc_input_s, stimulus); trace_fmt!("Sent {} stimuli, symbol: 0x{:x}, last: {}", - counter, stimulus.symbol, stimulus.last); + counter, stimulus.symbols[0], stimulus.last); (tok) }(tok); let OverflowWithLastTestOutput: @@ -422,11 +444,11 @@ proc RunLengthEncoderOverflowWithLastTest { in enumerate(OverflowWithLastTestOutput) { let last = counter == (array_size(OverflowWithLastTestOutput) - u32:1); let expected = OverflowWithLastEncOutData{ - symbol: symbol, count: count, last: last}; + symbols: [symbol], counts: [count], last: last}; let (tok, enc_output) = recv(tok, enc_output_r); trace_fmt!( "Received {} pairs, symbol: 0x{:x}, count: {}, last: {}", - counter, enc_output.symbol, enc_output.count, enc_output.last + counter, enc_output.symbols[0], enc_output.counts[0], enc_output.last ); assert_eq(enc_output, expected); (tok) diff --git a/xls/modules/rle/rle_enc_adv.md b/xls/modules/rle/rle_enc_adv.md new file mode 100644 index 0000000000..9dcf4bae76 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv.md @@ -0,0 +1,102 @@ +# Multisymbol Run-length 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 consecutive occurrences in the input stream. + +Overall, we can break down the data processing into four stages: reduction, alignment, compression, and output generation. The division of responsibility allowed the specialized blocks to efficiently process data and made it possible test each functionality separately. + +The first block is responsible for taking the input and reducing it to pairs of symbols and the number of its occurrences, as in the [basic RLE implementation](https://github.com/google/xls/pull/974). The second element of the encoder shifts the previously emitted pairs and adjusts them for further processing. Both of these elements have an empty state. The next block takes the prepared data and combines it with the information about previously processed symbols. The last element is responsible for adjusting the width of the output data to the receiver interface. + + +Input width is defined using the `INPUT_WIDTH` parameter and output width is defined with the `OUTPUT_WIDTH` parameter. +Both the input and the output channels use additional `last` flag +that indicates whether the packet ends the transmission. After sending +the last packet the encoder dumps all the data to the output stream. + +## Encoder processing pipeline detailed breakdown. + +### Initial conditions +- input width is 4 symbols wide, +- output width is 2 pairs wide, +- symbol counter is 2 bits wide. + +### Process +1. Reduce step - this process takes incoming symbols, symbol_valid, +last signal and reduces them into symbol count pairs. Last value is +passed through, so it's omitted from the example IO. This step is stateless. + +Example: + +||| +|-----|-------| +|input|output | +|[(A, True), (A, True), (A, True), (A, True)]|[.., .., (A, 3), (A, 1)]| +|input|output | +|[(A, True), (A, True), (A, False), (A, True)]|[.., .., .., (A, 3)]| +|input|output | +|[(A, True), (B, True), (C, True), (D, True)]|[(A, 1), (B, 1), (C, 1), (D, 1)]| + +2. Realign step - this process moves pairs emitted from the reduce step +so that they are aligned to the left, it also calculates propagation +distance for the first pair. This stage doesn't modify last signal value, +so it's omitted in the example. + +Example: + +|||| +|-----|-------|--------------------| +|input|output |propagation distance| +|[.., (A, 2), .., (B, 2)]|[(A, 2), (B, 2), .., ..]| 0| +|input|output |propagation distance| +|[.., .., (A, 3), (A, 1)]|[(A, 3), (A, 1), .., ..]| 1| + +3. Core step - this step is stateful. It takes align pairs from +the realign step, and combines them with its state to create multiple +symbol/count pairs output. State is represented by following tuple +``. It contains symbol and count from last pair +received from the realign step, or current sum of repeating symbol spanning +multiple input widths. + +First set of examples assumes that state doesn't have last set nor +was last received. Second set of examples will indicate value of last +signal in the proc state or input data and how it's emitted to output. + +Example set 1: + +||||| +|------|-----|-------|----------| +|state |input|output |next state| +|(A, 2)| [(A, 2), (B, 2), .., ..]|[(A, 3), (A, 1), .., ..]| (B, 2)| +|state |input|output |next state| +|(A, 1)| [(A, 1), (B, 2), .., ..]|[(A, 2), .., .., ..]| (B, 2)| +|state |input|output |next state| +|(A, 1)| [(A, 1), .., .., ..]|[.., .., .., ..]| (A, 2)| + +Example set 2: + +||||| +|------|-----|-------|----------| +|state: pair, last |input|output |next state| +|(A, 2), false| [(A, 2), (B, 2), .., ..], true |[(A, 3), (A, 1), (B, 2), ..], true| (), false| +|state: pair, last |input|output |next state| +|(A, 1), false| [(B, 1), (C, 1), (D, 1), (A, 1)], true|[(A, 1), (B, 1), (C, 1), (D, 1)]| (A, 1), true| +|state |input|output |next state| +|(A, 1), true| no data taken from input |[(A, 1), .., .. , ..], true| (), false| + +4. Adjust Width step - this step takes output from the core step. +If output can handle more or equal number of pairs as +input number of symbols. This step does nothing. +If the output is narrower than the input, +this step will serialize symbol counter pairs. + +Example: + +||||| +|-----|-----|-------|-----------| +|state|input|output | next state| +|[]|[(A, 3), (A, 2), .., ..]|[(A, 3), (A, 2)]|[]| +|state|input|output | next state| +|[]|[(A, 1), (B, 1), (C, 1), (D, 1)]|[(A, 1), (B, 1)]|[(C, 1), (D, 1)]| +|state|input|output | next state| +|[(C, 1), (D, 1)]|ignored|[(C, 1), (D, 1)]|[]| diff --git a/xls/modules/rle/rle_enc_adv.x b/xls/modules/rle/rle_enc_adv.x new file mode 100644 index 0000000000..501c68e444 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv.x @@ -0,0 +1,480 @@ +// 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 multisymbol 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 consecutive 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: + +// This encoder is implemented as a net of 4 proc. +// 1. Reduce proc - this process takes incoming symbols and symbol_valid +// and reduces them into symbol count pairs. This step is stateless. +// 2. Realign proc - this process moves pairs emitted from the reduce step +// so that they are aligned to the left, it also calculates propagation +// distance for the first pair. +// Example behaviours: +// 1) +// input: [.., (A, 2), .., (B, 2)] +// output: [(A, 2), (B, 2), .., ..] +// propagation distance: 0 +// 2) +// input: [.., .., (A, 3), (A, 1)] +// output: [(A, 3), (A, 1), .., ..] +// propagation distance: 1 +// 3. Core proc - this step is stateful. It takes align pairs from +// the realign step, and combines them with its state to create multiple +// symbol/count pairs output. State is represented by following tuple +// ``. It contains symbol and count from last pair +// received from realign step, or current sum of repeating symbol spanning +// multiple input widths. +// 4. Adjust Width proc - this step takes output from the core step. +// If output can handle more or equal number of pairs as +// input number of symbols. This step does nothing. +// If the output is narrower than the input, +// this step will serialize symbol counter pairs. + + +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..4170ebac69 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_adjust_width_stage.x @@ -0,0 +1,557 @@ +// 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..7d976e20e0 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_core.x @@ -0,0 +1,663 @@ +// 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 (symbols_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: symbols_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..6f74981ab3 --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_realign_stage.x @@ -0,0 +1,400 @@ +// 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..a0f06dc70b --- /dev/null +++ b/xls/modules/rle/rle_enc_adv_reduce_stage.x @@ -0,0 +1,373 @@ +// 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); + } +}