diff --git a/xls/modules/rle/BUILD b/xls/modules/rle/BUILD index eb3e0d7318..8c7e43f1d8 100644 --- a/xls/modules/rle/BUILD +++ b/xls/modules/rle/BUILD @@ -106,3 +106,72 @@ xls_benchmark_ir( "delay_model": "unit", } ) + +xls_dslx_library( + name = "rle_dec_dslx", + srcs = [ + "rle_dec.x", + ], + deps = [ + ":rle_common_dslx" + ], +) + +xls_dslx_test( + name = "rle_dec_dslx_test", + dslx_test_args = { + "compare": "none", + }, + library = "rle_dec_dslx", +) + +xls_dslx_test( + name = "rle_dec_dslx_ir_test", + dslx_test_args = { + "compare": "interpreter", + }, + library = "rle_dec_dslx", +) + +xls_dslx_test( + name = "rle_dec_dslx_jit_test", + dslx_test_args = { + "compare": "jit", + }, + library = "rle_dec_dslx", +) + +xls_dslx_ir( + name = "rle_dec_ir", + dslx_top = "RLEDec32", + library = "rle_dec_dslx", + ir_file = "rle_dec.ir", +) + +xls_ir_opt_ir( + name = "rle_dec_opt_ir", + src = "rle_dec.ir", + top = "__rle_dec__RLEDec32__RLEDec_0__2_32_next", +) + +xls_ir_verilog( + name = "rle_dec_verilog", + src = ":rle_dec_opt_ir.opt.ir", + verilog_file = "rle_dec.v", + codegen_args = { + "module_name": "rle_dec", + "delay_model": "unit", + "pipeline_stages": "2", + "reset": "rst", + "use_system_verilog": "false", + }, +) + +xls_benchmark_ir( + name = "rle_dec_ir_benchmark", + src = ":rle_dec_opt_ir.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "2", + "delay_model": "unit", + } +) diff --git a/xls/modules/rle/rle_dec.x b/xls/modules/rle/rle_dec.x new file mode 100644 index 0000000000..7af8155774 --- /dev/null +++ b/xls/modules/rle/rle_dec.x @@ -0,0 +1,235 @@ +// Copyright 2023 The XLS Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements a parametric RLE decoder +// +// The RLE decoder decompresses incoming stream of +// (`symbol`, `count`) pairs, representing that `symbol` was +// repeated `count` times in original stream. Output stream +// should be equal to the RLE encoder input stream. +// Both input and output channels use additional `last` flag +// that indicates whether the packet ends the transmission. +// Decoder in its current form only propagates last signal. +// The behavior of the decoder is presented on the waveform below: +// ──────╥─────╥─────╥─────╥─────╥─────╥─────╥─────╥──── +// next evaluation XXXXXX║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ ... +// ──────╨─────╨─────╨─────╨─────╨─────╨─────╨─────╨──── +// do_recv ┌─────┐ ┌─────────────────┐ ┌──── +// ──────┘ └─────┘ └───────────┘ +// ──────╥─────╥─────╥─────╥─────╥─────╥──────────────── +// symbol, count XXXXXX║ A,2 ║XXXXX║ B,1 ║ B,1 ║ C,3 ║XXXXXXXXXXXXXXXX +// (input channel) ──────╨─────╨─────╨─────╨─────╨─────╨──────────────── +// last ┌─────┐ ┌─────┐ +// (input channel) ──────────────────┘ └─────┘ └──────────────── +// ╥─────╥───────────╥───────────╥─────────────────╥──── +// state.symbol ║ 0 ║ A ║ B ║ C ║ 0 +// (set state value) ╨─────╨───────────╨───────────╨─────────────────╨──── +// ╥─────╥─────╥─────╥───────────╥─────╥─────╥─────╥──── +// state.count ║ 0 ║ 1 ║ 0 ║ 0 ║ 2 ║ 1 ║ 0 ║ 0 +// (set state value) ╨─────╨─────╨─────╨───────────╨─────╨─────╨─────╨──── +// +// ──────╥───────────╥───────────╥─────────────────╥──── +// symbol XXXXXX║ A ║ B ║ C ║XXXX +// (output channel) ──────╨───────────╨───────────╨─────────────────╨──── +// last ┌─────┐ ┌─────┐ +// (output channel) ──────────────────┘ └─────────────────┘ └──── + + +import std +import xls.modules.rle.rle_common as rle_common + +type DecInData = rle_common::EncData; +type DecOutData = rle_common::PlainData; + +// structure to preserve the state of an RLE decoder +struct RLEDecState { + // symbol to be repeated on output + symbol: bits[SYMBOL_WIDTH], + // count of symbols that has to be send + count: bits[COUNT_WIDTH], + // send last when repeat ends + last: bool, +} + // RLE decoder implementation +pub proc RLEDec { + input_r: chan> in; + output_s: chan> out; + + init {( + RLEDecState { + symbol: bits[SYMBOL_WIDTH]:0, + count: bits[COUNT_WIDTH]:0, + last: bool:false, + } + )} + + config ( + input_r: chan> in, + output_s: chan> out, + ) {(input_r, output_s)} + + next (tok: token, state: RLEDecState) { + let zero_input = DecInData { symbol: bits[SYMBOL_WIDTH]:0, count: bits[COUNT_WIDTH]:0, last: false }; + let empty = state.count == bits[COUNT_WIDTH]:0; + let (input_tok, input) = recv_if(tok, input_r, empty, zero_input); + let (next_symbol, next_count, next_last) = if (empty) { + let t_count = input.count - bits[COUNT_WIDTH]:1; + (input.symbol, t_count, input.last) + } else { + let t_count = state.count - bits[COUNT_WIDTH]:1; + (state.symbol, t_count, state.last) + }; + let send_last = next_last & (next_count == bits[COUNT_WIDTH]:0); + let data_tok = send(input_tok, output_s, DecOutData {symbol: next_symbol, last: send_last}); + RLEDecState { + symbol: next_symbol, + count: next_count, + last: next_last, + } + } +} + + +// RLE decoder specialization for the codegen +proc RLEDec32 { + init {()} + + config ( + input_r: chan> in, + output_s: chan> out, + ) { + spawn RLEDec(input_r, output_s); + () + } + + next (tok: token, state: ()) { + () + } +} + +// Tests + +const TEST_SYMBOL_WIDTH = u32:32; +const TEST_COUNT_WIDTH = u32:2; + +type TestSymbol = bits[TEST_SYMBOL_WIDTH]; +type TestCount = bits[TEST_COUNT_WIDTH]; +type TestDecInData = DecInData; +type TestDecOutData = DecOutData; + +// main proc used to test the RLE decoder +#[test_proc] +proc RLEDecTightTester { + 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 RLEDec(dec_input_r, dec_output_s); + (terminator, dec_input_s, dec_output_r) + } + + next(tok: token, state: ()) { + + // Simple transaction without repeats + let trans_send: (TestSymbol, TestCount)[2] = + [(TestSymbol:0xA, TestCount:0x3), (TestSymbol:0xB, TestCount:0x1)]; + let tok = for ((counter, (symbol_in, count_in)), tok): ((u32, (TestSymbol, TestCount)) , token) in enumerate(trans_send) { + let _last = counter == (array_size(trans_send) - u32:1); + let data_in = TestDecInData{symbol: symbol_in, count: count_in, last: _last}; + let tok = send(tok, dec_input_s, data_in); + let _ = trace_fmt!("Sent {} transactions, symbol: 0x{:x}, count:{}, last: {}", + counter, data_in.symbol, data_in.count, data_in.last); + (tok) + }(tok); + let trans_recv: TestSymbol[4] = [ + TestSymbol: 0xA, TestSymbol: 0xA, TestSymbol: 0xA, TestSymbol: 0xB + ]; + let tok = for ((counter, symbol_out), tok): ((u32, TestSymbol) , token) in enumerate(trans_recv) { + let _last = counter == (array_size(trans_recv) - u32:1); + let data_out = TestDecOutData{symbol: symbol_out, last: _last}; + let (tok, dec_output) = recv(tok, dec_output_r); + let _ = trace_fmt!( + "Received {} transactions, symbol: 0x{:x}, last: {}", + counter, dec_output.symbol, dec_output.last + ); + let _ = assert_eq(dec_output, data_out); + (tok) + }(tok); + + // Transaction with repeating symbols + let trans_send: (TestSymbol, TestCount)[6] =[ + (TestSymbol:0xB, TestCount:0x2), (TestSymbol:0x1, TestCount:0x1), + (TestSymbol:0xC, TestCount:0x3), (TestSymbol:0xC, TestCount:0x3), + (TestSymbol:0x3, TestCount:0x3), (TestSymbol:0x2, TestCount:0x2), + ]; + let tok = for ((counter, (symbol_in, count_in)), tok): ((u32, (TestSymbol, TestCount)) , token) in enumerate(trans_send) { + let _last = counter == (array_size(trans_send) - u32:1); + let data_in = TestDecInData{symbol: symbol_in, count: count_in, last: _last}; + let tok = send(tok, dec_input_s, data_in); + let _ = trace_fmt!("Sent {} transactions, symbol: 0x{:x}, count:{}, last: {}", + counter, data_in.symbol, data_in.count, data_in.last); + (tok) + }(tok); + let trans_recv: TestSymbol[14] = [ + TestSymbol: 0xB, TestSymbol: 0xB, TestSymbol: 0x1, TestSymbol: 0xC, + TestSymbol: 0xC, TestSymbol: 0xC, TestSymbol: 0xC, TestSymbol: 0xC, + TestSymbol: 0xC, TestSymbol: 0x3, TestSymbol: 0x3, TestSymbol: 0x3, + TestSymbol: 0x2, TestSymbol: 0x2, + ]; + let tok = for ((counter, symbol_out), tok): ((u32, TestSymbol) , token) in enumerate(trans_recv) { + let _last = counter == (array_size(trans_recv) - u32:1); + let data_out = TestDecOutData{symbol: symbol_out, last: _last}; + let (tok, dec_output) = recv(tok, dec_output_r); + let _ = trace_fmt!( + "Received {} transactions, symbol: 0x{:x}, last: {}", + counter, dec_output.symbol, dec_output.last + ); + let _ = assert_eq(dec_output, data_out); + (tok) + }(tok); + + // `last` after `last` check + let trans_send: TestDecInData[2] =[ + TestDecInData {symbol: TestSymbol:0x1, count: TestCount:0x1, last:true}, + TestDecInData {symbol: TestSymbol:0x1, count: TestCount:0x1, last:true}, + ]; + let tok = for ((counter, data_in), tok): ((u32, TestDecInData) , token) in enumerate(trans_send) { + let tok = send(tok, dec_input_s, data_in); + let _ = trace_fmt!("Sent {} transactions, symbol: 0x{:x}, count:{}, last: {}", + counter, data_in.symbol, data_in.count, data_in.last); + (tok) + }(tok); + let trans_recv: TestDecOutData[2] = [ + TestDecOutData{symbol: TestSymbol: 0x1, last: true}, + TestDecOutData{symbol: TestSymbol: 0x1, last: true}, + ]; + let tok = for ((counter, data_out), tok): ((u32, TestDecOutData) , token) in enumerate(trans_recv) { + let (tok, dec_output) = recv(tok, dec_output_r); + let _ = trace_fmt!( + "Received {} transactions, symbol: 0x{:x}, last: {}", + counter, dec_output.symbol, dec_output.last + ); + let _ = assert_eq(dec_output, data_out); + (tok) + }(tok); + let _ = send(tok, terminator, true); + () + } +}