Skip to content

Commit

Permalink
Add simple RLE decoder
Browse files Browse the repository at this point in the history
Right now only simple implementation is provided.

The provided tests check if:
* output of the decoder matches the expected values

Signed-off-by: Maciej Dudek <mdudek@antmicro.com>
  • Loading branch information
mtdudek committed Jun 16, 2023
1 parent eff08fa commit 5958ef4
Show file tree
Hide file tree
Showing 2 changed files with 304 additions and 0 deletions.
69 changes: 69 additions & 0 deletions xls/modules/rle/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
)
235 changes: 235 additions & 0 deletions xls/modules/rle/rle_dec.x
Original file line number Diff line number Diff line change
@@ -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_WIDTH: u32, COUNT_WIDTH: u32> {
// 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<SYMBOL_WIDTH: u32, COUNT_WIDTH: u32> {
input_r: chan<DecInData<SYMBOL_WIDTH, COUNT_WIDTH>> in;
output_s: chan<DecOutData<SYMBOL_WIDTH>> out;

init {(
RLEDecState<SYMBOL_WIDTH, COUNT_WIDTH> {
symbol: bits[SYMBOL_WIDTH]:0,
count: bits[COUNT_WIDTH]:0,
last: bool:false,
}
)}

config (
input_r: chan<DecInData<SYMBOL_WIDTH, COUNT_WIDTH>> in,
output_s: chan<DecOutData<SYMBOL_WIDTH>> out,
) {(input_r, output_s)}

next (tok: token, state: RLEDecState<SYMBOL_WIDTH, COUNT_WIDTH>) {
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<DecInData<32, 2>> in,
output_s: chan<DecOutData<32>> out,
) {
spawn RLEDec<u32:32, u32:2>(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<TEST_SYMBOL_WIDTH, TEST_COUNT_WIDTH>;
type TestDecOutData = DecOutData<TEST_SYMBOL_WIDTH>;

// main proc used to test the RLE decoder
#[test_proc]
proc RLEDecTightTester {
terminator: chan<bool> out; // test termination request
dec_input_s: chan<TestDecInData> out;
dec_output_r: chan<TestDecOutData> in;

init {()}

config(terminator: chan<bool> out) {
let (dec_input_s, dec_input_r) = chan<TestDecInData>;
let (dec_output_s, dec_output_r) = chan<TestDecOutData>;

spawn RLEDec<TEST_SYMBOL_WIDTH, TEST_COUNT_WIDTH>(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);
()
}
}

0 comments on commit 5958ef4

Please sign in to comment.