Skip to content

Commit

Permalink
modules/dbe: add lz4 encoder and decoder
Browse files Browse the repository at this point in the history
Example implementation of Lz4 - a dictionary-based data
compression algorithm.

Signed-off-by: Roman Dobrodii <[email protected]>
  • Loading branch information
rdob-ant committed Aug 24, 2023
1 parent 1b669d9 commit 715e1d1
Show file tree
Hide file tree
Showing 6 changed files with 1,823 additions and 0 deletions.
155 changes: 155 additions & 0 deletions xls/modules/dbe/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# 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.

# Build rules for XLS DBE/LZ4 algorithm implementation.

load(
"//xls/build_rules:xls_build_defs.bzl",
"xls_dslx_library",
"xls_dslx_ir",
"xls_ir_opt_ir",
"xls_ir_verilog",
"xls_dslx_test",
)

package(
default_applicable_licenses = ["//:license"],
default_visibility = ["//xls:xls_users"],
licenses = ["notice"],
)

# ---------------------------------------------------------------------------
# Common
# ---------------------------------------------------------------------------

xls_dslx_library(
name = "dbe_common_dslx",
srcs = [
"common.x",
"common_test.x"
],
)

# ---------------------------------------------------------------------------
# LZ4 decoder
# ---------------------------------------------------------------------------

xls_dslx_library(
name = "dbe_lz4_decoder_dslx",
srcs = [
"lz4_decoder.x"
],
deps = [
":dbe_common_dslx",
"//xls/examples:ram_dslx",
],
)

xls_dslx_test(
name = "dbe_lz4_decoder_dslx_test",
dslx_test_args = {
"compare": "none",
},
library = "dbe_lz4_decoder_dslx",
)

xls_dslx_ir(
name = "dbe_lz4_decoder_ir",
dslx_top = "decoder",
library = "dbe_lz4_decoder_dslx",
ir_file = "dbe_lz4_decoder_ir.ir",
)

xls_ir_opt_ir(
name = "dbe_lz4_decoder_opt_ir",
src = "dbe_lz4_decoder_ir.ir",
top = "__lz4_decoder__decoder__decoder_base_0__16_16_8_next",
ram_rewrites = [
":lz4_decoder_ram1r1w_rewrites.textproto",
],
)

xls_ir_verilog(
name = "dbe_lz4_decoder_verilog",
src = "dbe_lz4_decoder_opt_ir.opt.ir",
verilog_file = "dbe_lz4_decoder.v",
codegen_args = {
"module_name": "dbe_lz4_decoder",
"delay_model": "unit",
"pipeline_stages": "3",
"reset": "rst",
"use_system_verilog": "false",
"streaming_channel_data_suffix": "_data",
"ram_configurations": "ram_hb:1R1W:{rd_req}:{rd_resp}:{wr_req}:{wr_comp}".format(
rd_req = "ram_hb_read_req",
rd_resp = "ram_hb_read_resp",
wr_req = "ram_hb_write_req",
wr_comp = "ram_hb_write_completion",
),
},
)

# ---------------------------------------------------------------------------
# LZ4 encoder
# ---------------------------------------------------------------------------

xls_dslx_library(
name = "dbe_lz4_encoder_dslx",
srcs = [
"lz4_encoder.x"
],
deps = [
":dbe_common_dslx",
"//xls/examples:ram_dslx",
# decoder is referenced by test code
":dbe_lz4_decoder_dslx",
],
)

xls_dslx_test(
name = "dbe_lz4_encoder_dslx_test",
dslx_test_args = {
"compare": "none",
},
library = "dbe_lz4_encoder_dslx",
)

#8K hash specialization
xls_dslx_ir(
name = "dbe_lz4_encoder_8k_ir",
dslx_top = "encoder_8k",
library = "dbe_lz4_encoder_dslx",
ir_file = "dbe_lz4_encoder_8k_ir.ir",
)

xls_ir_opt_ir(
name = "dbe_lz4_encoder_8k_opt_ir",
src = "dbe_lz4_encoder_8k_ir.ir",
top = "__lz4_encoder__encoder_8k__encoder_base_0__16_3_13_12_13_65536_8192_4_16_8_next",
)

xls_ir_verilog(
name = "dbe_lz4_encoder_8k_verilog",
src = "dbe_lz4_encoder_8k_opt_ir.opt.ir",
verilog_file = "dbe_lz4_encoder_8k.v",
codegen_args = {
"module_name": "dbe_lz4_encoder",
"delay_model": "unit",
"pipeline_stages": "3",
"worst_case_throughput": "3",
"reset": "rst",
"use_system_verilog": "false",
"streaming_channel_data_suffix": "_data",
},
)
53 changes: 53 additions & 0 deletions xls/modules/dbe/common.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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

pub enum Mark : u4 {
// Default initialization value used when marker field is not needed
NONE = 0,
// Signals end of sequence/block
END = 1,
// Requests reset of the processing chain
RESET = 2,

_ERROR_FIRST = 8,
// Only error marks have values >= __ERROR_FIRST
ERROR_BAD_MARK = 8,
ERROR_INVAL_CP = 9,
}

pub fn is_error(mark: Mark) -> bool {
(mark as u32) >= (Mark::_ERROR_FIRST as u32)
}

pub enum TokenKind : u2 {
LITERAL = 0,
COPY_POINTER = 1,
MARK = 2,
}

pub struct Token<SYM_WIDTH: u32, PTR_WIDTH: u32, CNT_WIDTH: u32> {
kind: TokenKind,
literal: uN[SYM_WIDTH],
copy_pointer_offset: uN[PTR_WIDTH],
copy_pointer_count: uN[CNT_WIDTH],
mark: Mark
}

pub struct PlainData<DATA_WIDTH: u32> {
is_mark: bool,
data: uN[DATA_WIDTH], // symbol
mark: Mark, // marker code
}
171 changes: 171 additions & 0 deletions xls/modules/dbe/common_test.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// 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 xls.modules.dbe.common as dbe

type TokenKind = dbe::TokenKind;
type Token = dbe::Token;
type PlainData = dbe::PlainData;

///
/// This library contains helper processes and functions used by DBE tests
///

pub proc data_sender<NUM_SYMS: u32, SYM_WIDTH:u32> {
data: PlainData<SYM_WIDTH>[NUM_SYMS];
o_data: chan<PlainData<SYM_WIDTH>> out;

init { u32:0 }

config (
data: PlainData<SYM_WIDTH>[NUM_SYMS],
o_data: chan<PlainData<SYM_WIDTH>> out,
) {
(data, o_data)
}

next (tok: token, state: u32) {
let next_idx = state + u32:1;
let is_done = (state >= NUM_SYMS);

if (!is_done) {
let tosend = data[state];
trace_fmt!("Sending {}", tosend);
send(tok, o_data, tosend);
next_idx
} else {
state
}
}
}

pub proc data_validator<NUM_SYMS: u32, SYM_WIDTH:u32> {
ref_data: PlainData<SYM_WIDTH>[NUM_SYMS];
i_data: chan<PlainData<SYM_WIDTH>> in;
o_term: chan<bool> out;

init { u32:0 }

config(
ref_data: PlainData<SYM_WIDTH>[NUM_SYMS],
i_data: chan<PlainData<SYM_WIDTH>> in,
o_term: chan<bool> out
) {
(ref_data, i_data, o_term)
}

next (tok: token, state: u32) {
// state = [0, NUM_SYMS-1] - expect data
// state >= NUM_SYMS - expect nothing
let next_idx = state + u32:1;
let is_end = (state == NUM_SYMS - u32:1);
let is_done = (state >= NUM_SYMS);

let (tok, rx) = recv(tok, i_data);
trace_fmt!("Received {}", rx);

let (fail, next_state) = if is_done {
// Shouldn't get here
(true, state)
} else {
let expect = ref_data[state];
let fail = if (rx != expect) {
trace_fmt!("MISMATCH! Expected {}, got {}", expect, rx);
true
} else {
false
};
(fail, next_idx)
};

send_if(tok, o_term, fail || is_end, !fail);

next_state
}
}

pub proc token_sender<
NUM_TOKS: u32, SYM_WIDTH: u32, PTR_WIDTH: u32, CNT_WIDTH: u32> {
toks: Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>[NUM_TOKS];
o_toks: chan<Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>> out;

init { u32:0 }

config (
toks: Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>[NUM_TOKS],
o_toks: chan<Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>> out,
) {
(toks, o_toks)
}

next (tok: token, state: u32) {
let next_idx = state + u32:1;
let is_done = (state >= NUM_TOKS);

if (!is_done) {
let tosend = toks[state];
trace_fmt!("Sending {}", tosend);
send(tok, o_toks, tosend);
next_idx
} else {
state
}
}
}

pub proc token_validator<
NUM_TOKS: u32, SYM_WIDTH: u32, PTR_WIDTH: u32, CNT_WIDTH: u32> {
ref_toks: Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>[NUM_TOKS];
i_token: chan<Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>> in;
o_term: chan<bool> out;

init { u32:0 }

config(
ref_toks: Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>[NUM_TOKS],
i_token: chan<Token<SYM_WIDTH, PTR_WIDTH, CNT_WIDTH>> in,
o_term: chan<bool> out
) {
(ref_toks, i_token, o_term)
}

next (tok: token, state: u32) {
// state = [0, NUM_TOKS-1] - expect token
// state >= NUM_TOKS - expect nothing
let next_idx = state + u32:1;
let is_end = (state == NUM_TOKS - u32:1);
let is_done = (state >= NUM_TOKS);

let (tok, rx) = recv(tok, i_token);
trace_fmt!("Received {}", rx);

let (fail, next_state) = if is_done {
// Shouldn't get here
(true, state)
} else {
let expect = ref_toks[state];
let fail = if (rx != expect) {
trace_fmt!("MISMATCH! Expected {}, got {}", expect, rx);
true
} else {
false
};
(fail, next_idx)
};

send_if(tok, o_term, fail || is_end, !fail);

next_state
}
}
Loading

0 comments on commit 715e1d1

Please sign in to comment.