From 0c95190589972040f1a024fc4ed04b770eb6e627 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Thu, 20 Jul 2023 10:51:28 +0200 Subject: [PATCH] [RLE] Support multiple symbols/pairs in single transaction This commit adds valid signals to PlainData interface. CompressedData interface uses `count > 0` to define valid symbol count pair. This is in preparation for a multisymbol RLE encoder implementation. Signed-off-by: Maciej Dudek --- xls/modules/rle/rle_common.x | 15 ++-- xls/modules/rle/rle_dec.x | 146 +++++++++++++++++++++++++++-------- xls/modules/rle/rle_enc.x | 126 +++++++++++++++++------------- 3 files changed, 196 insertions(+), 91 deletions(-) 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 82bad70c53..51e26949b9 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) @@ -210,6 +215,81 @@ 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] @@ -232,13 +312,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 }, ]; @@ -246,21 +326,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 8732017112..0203747e0b 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) @@ -236,7 +242,7 @@ type OverflowSymbol = TestCommonSymbol; type OverflowCount = bits[OverflowCountWidth]; type OverflowEncInData = TestCommonEncInData; type OverflowEncOutData = - EncOutData; + EncOutData; #[test_proc] proc RunLengthEncoderOverflowTest { @@ -268,10 +274,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: @@ -288,11 +298,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) @@ -331,25 +341,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): @@ -358,7 +376,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); @@ -380,7 +398,7 @@ type OverflowWithLastCount = type OverflowWithLastEncInData = TestCommonEncInData; type OverflowWithLastEncOutData = EncOutData; + OverflowWithLastCountWidth, 1>; #[test_proc] proc RunLengthEncoderOverflowWithLastTest { @@ -409,10 +427,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: @@ -425,11 +447,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)