`default_nettype none
typedef enum
{ Char_escape = 1, Char_EOS = 255, Char_0 = 48, Char_9 = 57 } Chars_Special;
module icomp_none
#( int size_lg = 4,
int n_bytes_binary = 4,
int size = 1 << size_lg )
( output uwire [7:0] char_out,
output uwire can_insert, can_remove,
input uwire [7:0] char_in,
input uwire insert_req, remove_req,
input uwire reset, clk);
logic [7:0] storage [size];
logic [size_lg:1] head, tail;
uwire empty = head == tail;
uwire full = tail + 1 == head;
assign can_insert = !full;
assign can_remove = !empty;
assign char_out = storage[head];
always_ff @( posedge clk ) if ( reset ) begin
tail <= 0;
end else begin
if ( insert_req ) begin
storage[tail] = char_in;
tail <= tail + 1;
end
end
always_ff @( posedge clk ) if ( reset ) begin
head <= 0;
end else if ( remove_req ) begin
head <= head + 1;
end
cadence logic [n_bytes_binary-1:0][7:0] incoming_val_binary, outgoing_val_binary;
logic start_encoding, now_encoding, end_encoding, use_encoding;
logic start_draining, end_draining, have_outgoing_val_binary;
uwire [size_lg:1] write_idx;
logic [size_lg:1] tail_at_enc_start;
logic [7:0] incoming_n_bytes_ascii;
cadence
endmodule
module icomp_1cyc
#( int size_lg = 4,
int n_bytes_binary = 4,
int size = 1 << size_lg )
( output uwire [7:0] char_out,
output uwire can_insert, can_remove,
input uwire [7:0] char_in,
input uwire insert_req, remove_req,
input uwire reset, clk);
logic [7:0] storage [size];
logic esc_here [size];
logic [n_bytes_binary-1:0][7:0] incoming_val_binary;
logic [n_bytes_binary-1:0][7:0] outgoing_val_binary;
logic have_outgoing_val_binary;
logic [size_lg:1] head; uwire [size_lg:1] write_idx; logic [size_lg:1] tail; logic [size_lg:1] tail_at_enc_start;
logic [7:0] incoming_n_bytes_ascii;
logic draining;
logic [$clog2(n_bytes_binary)-1:0] drain_idx;
uwire start_draining, end_draining;
uwire empty, full;
uwire is_digit = char_in >= Char_0 && char_in <= Char_9;
uwire is_nz_digit = char_in > Char_0 && char_in <= Char_9;
uwire [3:0] char_bin = char_in - Char_0;
uwire [n_bytes_binary:0] [7:0] next_incoming_val_binary =
incoming_val_binary * 10 + char_bin;
uwire overflow = next_incoming_val_binary[n_bytes_binary] != 0;
logic was_encoding;
logic was_digit;
always_ff @( posedge clk )
if ( reset ) was_digit <= 0;
else if ( insert_req ) was_digit <= is_digit;
uwire start_encoding =
insert_req && is_nz_digit && ( !was_digit || overflow );
uwire end_encoding =
insert_req && was_encoding && ( !is_digit || overflow );
uwire now_encoding = start_encoding || was_encoding && !end_encoding;
always_ff @( posedge clk )
if ( reset ) was_encoding <= 0;
else if ( insert_req ) was_encoding <= now_encoding;
uwire use_encoding = end_encoding
&& ( incoming_n_bytes_ascii > n_bytes_binary )
&& ( !have_outgoing_val_binary || end_draining );
always_ff @( posedge clk ) if ( insert_req ) begin
if ( start_encoding ) begin
incoming_val_binary <= char_bin;
tail_at_enc_start <= write_idx;
incoming_n_bytes_ascii <= 1;
end else begin
incoming_val_binary <= next_incoming_val_binary;
incoming_n_bytes_ascii <= incoming_n_bytes_ascii + 1;
end
if ( use_encoding ) outgoing_val_binary <= incoming_val_binary;
end
uwire [size_lg:1] tail_adj = tail_at_enc_start + n_bytes_binary + 1;
assign write_idx = use_encoding ? tail_adj : tail;
always_ff @( posedge clk ) if ( reset ) begin
tail <= 0;
end else begin
if ( insert_req ) begin
esc_here[write_idx] <= 0;
storage[write_idx] <= char_in;
tail <= write_idx + 1;
end
if ( use_encoding ) esc_here[tail_at_enc_start] <= 1;
end
assign char_out =
start_draining ? Char_escape :
draining ? outgoing_val_binary[drain_idx] :
storage[head];
assign start_draining = !empty && esc_here[head];
assign end_draining = remove_req && draining && drain_idx == 0;
always_ff @( posedge clk )
if ( reset ) have_outgoing_val_binary <= 0;
else if ( use_encoding ) have_outgoing_val_binary <= 1;
else if ( end_draining ) have_outgoing_val_binary <= 0;
always_ff @( posedge clk ) if ( reset ) begin
draining <= 0;
drain_idx <= 0;
head <= 0;
end else begin
if ( remove_req ) begin
head <= head + 1;
drain_idx <= start_draining ? n_bytes_binary-1 :
drain_idx > 0 ? drain_idx - 1 :
0;
draining <= start_draining ? 1 :
remove_req && drain_idx == 0 ? 0 :
draining;
end
end
assign empty = head == tail;
assign full = tail + 1 == head;
assign can_remove = !empty && ( !was_encoding || head != tail_at_enc_start );
assign can_insert = !full;
endmodule
module icomp_2cyc
#( int size_lg = 4,
int n_bytes_binary = 4,
bit separate_port = 1 )
( output uwire [7:0] char_out,
output uwire can_insert, can_remove,
input uwire [7:0] char_in,
input uwire insert_req, remove_req,
input uwire reset, clk);
localparam int size = 1 << size_lg;
logic [7:0] storage [size];
logic esc_here [size];
logic [n_bytes_binary:0][7:0] incoming_val_binary_1, incoming_val_binary_2;
logic [n_bytes_binary-1:0][7:0] outgoing_val_binary;
logic have_outgoing_val_binary;
logic [size_lg:1] head; uwire [size_lg:1] write_idx_1; logic [size_lg:1] tail_1; logic [size_lg:1] tail_at_enc_start_1;
logic [7:0] incoming_n_bytes_ascii;
logic draining;
logic [$clog2(n_bytes_binary)-1:0] drain_idx;
uwire start_draining, end_draining;
uwire empty, full;
logic [7:0] char_in_1;
uwire is_digit = char_in >= Char_0 && char_in <= Char_9;
uwire is_nz_digit = char_in > Char_0 && char_in <= Char_9; logic is_digit_1, is_digit_2, is_nz_digit_1, is_nz_digit_2;
always_ff @( posedge clk )
if ( reset ) begin
is_digit_1 <= 0;
is_digit_2 <= 0;
is_nz_digit_1 <= 0;
is_nz_digit_2 <= 0;
end else if ( insert_req ) begin
is_digit_1 <= is_digit;
is_nz_digit_1 <= is_nz_digit;
is_digit_2 <= is_digit_1;
is_nz_digit_2 <= is_nz_digit_1;
end
uwire [3:0] char_bin = char_in[3:0];
uwire [3:0] char_bin_1 = char_in_1[3:0];
uwire [n_bytes_binary:0] [7:0] incoming_val_binary_0 =
incoming_val_binary_1 * 10 + char_bin;
always_ff @( posedge clk )
if ( insert_req ) incoming_val_binary_2 <= incoming_val_binary_1;
uwire overflow_1 = incoming_val_binary_1[n_bytes_binary] != 0;
logic encoding_2;
uwire start_encoding = encoding_2 && overflow_1 && is_digit_1 && is_digit
|| !is_digit_2 && is_nz_digit_1 && is_digit;
uwire end_encoding = encoding_2 && ( overflow_1 || insert_req && !is_digit );
uwire use_encoding = end_encoding
&& ( incoming_n_bytes_ascii > n_bytes_binary )
&& ( !have_outgoing_val_binary || end_draining );
uwire encoding_1 = start_encoding || encoding_2 && !end_encoding;
always_ff @( posedge clk )
if ( reset ) encoding_2 <= 0;
else if ( insert_req ) encoding_2 <= encoding_1;
always_ff @( posedge clk )
if ( reset ) begin
incoming_val_binary_1 <= 0;
tail_at_enc_start_1 <= 0;
end else if ( insert_req ) begin
incoming_val_binary_1 <= start_encoding
? char_bin + char_bin_1 * 10
: incoming_val_binary_0;
incoming_n_bytes_ascii <=
start_encoding ? 2 : incoming_n_bytes_ascii + 1;
if ( start_encoding ) tail_at_enc_start_1 <= write_idx_1;
if ( use_encoding )
outgoing_val_binary <=
overflow_1 ? incoming_val_binary_2 : incoming_val_binary_1;
char_in_1 <= char_in;
end
uwire [size_lg:1] tail_adj =
tail_at_enc_start_1 + n_bytes_binary + overflow_1;
assign write_idx_1 = use_encoding ? tail_adj : tail_1;
logic reset_1;
always_ff @( posedge clk )
if ( reset ) reset_1 <= 1; else if ( insert_req ) reset_1 <= 0;
always_ff @( posedge clk ) if ( reset ) begin
tail_1 <= 0;
end else begin
if ( insert_req && !reset_1 ) begin
if ( separate_port ) begin
if ( use_encoding ) begin
esc_here[tail_adj] <= 0;
storage[tail_adj] <= char_in_1;
end else begin
esc_here[tail_1] <= 0;
storage[tail_1] <= char_in_1;
end
end else begin
esc_here[write_idx_1] <= 0;
storage[write_idx_1] <= char_in_1;
end
tail_1 <= write_idx_1 + 1;
end
if ( use_encoding ) esc_here[tail_at_enc_start_1] <= 1;
end
assign char_out =
start_draining ? Char_escape :
draining ? outgoing_val_binary[drain_idx] :
storage[head];
assign start_draining = !empty && esc_here[head];
assign end_draining = remove_req && draining && drain_idx == 0;
always_ff @( posedge clk )
if ( reset ) have_outgoing_val_binary <= 0;
else if ( use_encoding && insert_req ) have_outgoing_val_binary <= 1;
else if ( end_draining ) have_outgoing_val_binary <= 0;
always_ff @( posedge clk ) if ( reset ) begin
draining <= 0;
drain_idx <= 0;
head <= 0;
end else if ( remove_req ) begin
head <= head + 1;
drain_idx <= start_draining ? n_bytes_binary-1 :
drain_idx > 0 ? drain_idx - 1 :
0;
draining <= start_draining ? 1 :
remove_req && drain_idx == 0 ? 0 :
draining;
end
assign empty = head == tail_1;
assign full = tail_1 + 1 == head;
assign can_remove = !empty && ( !encoding_2 || head != tail_at_enc_start_1 );
assign can_insert = !full;
cadence uwire [n_bytes_binary:0] [7:0] incoming_val_binary = incoming_val_binary_1;
uwire [size_lg:1] write_idx = write_idx_1;
uwire [size_lg:1] tail = tail_1;
uwire [size_lg:1] tail_at_enc_start = tail_at_enc_start_1;
uwire now_encoding = encoding_1;
cadence
endmodule
cadence
program reac(output uwire clock_reactive, input uwire clock);
assign clock_reactive = clock;
endprogram
module testbench();
localparam int elts_lg = 4;
localparam int elts = 1 << elts_lg;
localparam int n_bytes_binary = 2;
uwire [7:0] char_out;
uwire can_insert, can_remove;
logic [7:0] char_in;
logic insert_req, remove_req, reset, clk;
icomp_1cyc #(elts_lg,n_bytes_binary) b1
(char_out,can_insert,can_remove,char_in,insert_req,remove_req,reset,clk);
int cycle_num;
uwire clk_reactive;
reac reactivator(clk_reactive, clk);
initial begin
clk = 0;
cycle_num = 0;
fork
forever #1 clk = !clk;
forever @( posedge clk ) cycle_num++;
join
end
string in_str = "One 1 two 12 three 317 four 1029 six 123456 ten 1234567890. There are 60 seconds in a minute and 31536123 - 123 in a year.";
string out_str = "";
initial begin
automatic string in_str_w_suffix = {in_str, Char_EOS, Char_EOS };
automatic int insert_finished_cyc = 0;
automatic int out_size = 0;
automatic bit tb_insert_done = 0;
automatic bit tb_remove_done = 0;
automatic bit trace_this_cycle = 0;
automatic bit key_printed = 0;
automatic int err_oversize = 0;
automatic int bytes_remaining = 0;
automatic bit enc_start = 0; automatic bit enc_middle = 0;
reset = 0;
insert_req = 0;
remove_req = 0;
@( negedge clk ) reset = 1;
@( negedge clk ) reset = 0;
@( negedge clk );
if ( can_insert !== 1 ) begin
$display("Module did not reset, can_insert: %h\n", can_insert);
$fatal(1);
end
fork
fork begin
automatic int cyc_limit = in_str.len() * 100;
fork
wait ( cycle_num == cyc_limit );
wait ( tb_insert_done && tb_remove_done );
join_any
if ( cycle_num >= cyc_limit ) begin
$write("Exceeded cycle limit, exiting.\n");
$fatal(1);
end
end join_none
while ( !tb_insert_done || !tb_remove_done )
@( insert_req or remove_req or can_insert or can_remove
or b1.tail or b1.head or tb_insert_done or tb_remove_done )
trace_this_cycle = 1;
while ( !tb_insert_done || !tb_remove_done ) begin
@( negedge clk_reactive ) if ( !trace_this_cycle ) continue;
trace_this_cycle = 0;
if ( !key_printed ) begin
localparam string space = " ";
key_printed = 1;
$write("%sTrace Key:\n",space);
$write("%ss___ ___ : Start encoding.\n",space);
$write("%s_n__ ___ : Now encoding.\n",space);
$write("%s__e_ ___ : End encoding.\n",space);
$write("%s___u ___ : Use encoding.\n",space);
$write("%s____ f__ : Have outgoing binary value.\n",space);
$write("%s____ _s_ : Start draining.\n",space);
$write("%s____ __e : End draining.\n",space);
end
$write( "In %s Out %d %3s = %3s tail %d/%d head %d %s%s%s%s %s%s%s es %d in_sz %2d in x%h out x%h\n",
insert_req ? ( char_in == Char_EOS ? "E" : char_in ) : "-",
can_remove,
remove_req ? $sformatf("%3d",char_out) : " ",
!remove_req ? " " :
enc_start ?
( char_out == Char_escape ? "ESC" :
$sformatf("E-%1d",char_out-Char_escape) ) :
enc_middle ? $sformatf("x%02h",char_out) :
char_out >= 32 && char_out < 128 ? char_out
: $sformatf("x%02h",char_out),
b1.tail, b1.write_idx,
b1.head,
b1.start_encoding ? "s" : "_",
b1.now_encoding ? "n" : "_",
b1.end_encoding ? "e" : "_",
b1.use_encoding ? "u" : "_",
b1.have_outgoing_val_binary ? "f" : "_",
b1.start_draining ? "s" : "_",
b1.end_draining ? "e" : "_",
b1.tail_at_enc_start,
b1.incoming_n_bytes_ascii,
b1.incoming_val_binary, b1.outgoing_val_binary);
end
begin
automatic int in_pos = 0;
while ( in_pos < in_str_w_suffix.len() ) begin
@( negedge clk );
if ( {$random} & 'h1 && can_insert ) begin
char_in = in_str_w_suffix[in_pos++];
insert_req = 1;
end else begin
insert_req = 0;
end
end
@( negedge clk );
insert_req = 0;
insert_finished_cyc = cycle_num;
$write("Done feeding inputs.\n");
tb_insert_done = 1;
end
begin
bit [n_bytes_binary-1:0][7:0] buffer;
bytes_remaining = 0;
while ( insert_finished_cyc == 0
|| cycle_num < insert_finished_cyc + elts * 10 ) begin
@( negedge clk );
if ( can_remove
&& bytes_remaining == 0 && char_out == Char_EOS ) break;
if ( {$random} & 1 && can_remove ) begin
remove_req = 1;
out_size++;
enc_start = 0;
enc_middle = bytes_remaining > 0;
if ( bytes_remaining > 0 ) begin
buffer = ( buffer << 8 ) + char_out;
bytes_remaining--;
if ( bytes_remaining == 0 ) begin
out_str = {out_str,$sformatf("%0d",buffer)};
end
end else if ( char_out == Char_escape )
begin
enc_start = 1;
bytes_remaining =
char_out == Char_escape
? n_bytes_binary
: char_out - Char_escape ;
buffer = 0;
if ( bytes_remaining > n_bytes_binary ) err_oversize++;
end else begin
out_str = {out_str,char_out};
end
end else begin
remove_req = 0;
end
end
$write("Done gathering outputs.\n");
tb_remove_done = 1;
end
join
if ( err_oversize )
$write("** Error: %d encoded integers too long.\n", err_oversize);
if ( in_str != out_str )
$write("** Error: strings don't match.\n");
else
$write("Correct output, strings match. %s\n",
( in_str.len() == out_size ) ? "But no compression!" : "");
$write("In size %d bytes, out size %d bytes.\n",
in_str.len(), out_size);
$write("In - \"%s\"\nOut- \"%s\"\n",
in_str, out_str);
$finish(2);
end
endmodule
cadence
`ifdef XXX
Module Name Area Clock
Period
icomp_1cyc 4 4 272268 3378 Old overflow -> 108
icomp_1cyc 4 4 259632 2318 New ov. 117->109
icomp_2cyc 4 4 0 319452 2480
icomp_2cyc 4 4 1 361448 2250
`endif