mirror of https://github.com/YosysHQ/icestorm.git
Added icecompr
This commit is contained in:
parent
a140056324
commit
ff02cd753c
|
|
@ -0,0 +1,5 @@
|
|||
icecompr
|
||||
iceuncompr
|
||||
example_?k.compr
|
||||
example_?k.ok
|
||||
example_?k.uncompr
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
all: icecompr iceuncompr
|
||||
|
||||
test: example_1k.ok example_8k.ok
|
||||
|
||||
icecompr: icecompr.cc
|
||||
clang++ -o icecompr -Wall -Wextra -std=c++11 icecompr.cc
|
||||
|
||||
iceuncompr: iceuncompr.c
|
||||
clang -o iceuncompr -Wall -Wextra iceuncompr.c
|
||||
|
||||
%.compr: %.bin icecompr
|
||||
./icecompr -v $< $@
|
||||
|
||||
|
||||
%.uncompr: %.compr iceuncompr
|
||||
./iceuncompr $< $@
|
||||
|
||||
%.ok: %.uncompr %.bin
|
||||
cmp $^
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
rm -f icecompr iceuncompr
|
||||
rm -f example_1k.compr example_8k.compr
|
||||
rm -f example_1k.uncompr example_8k.uncompr
|
||||
rm -f example_1k.ok example_8k.ok
|
||||
|
||||
.SECONDARY:
|
||||
.PHONY: all test clean
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
A simple compression algorithm for iCE40 bit-streams
|
||||
====================================================
|
||||
|
||||
This directory contains tools for compressing and uncompressing
|
||||
iCE40 bit-streams. The motivation is to reduce the bandwidth
|
||||
requirements for bit-stream upload.
|
||||
|
||||
Note that iCE40 FPGAs can not uncompress this compressed bit-streams!
|
||||
Uncompression must be performed by e.g. a uC on the FPGA board.
|
||||
|
||||
This compression algorithm uses the fact that most bits in an iCE40
|
||||
bit-stream are cleared.
|
||||
|
||||
The bit-stream is encoded as the distances between set bits (i.e.
|
||||
the length of runs of ZERO bits between two ONE bits). This sequence
|
||||
of integers is stored using a simple prefix code to spend fewer bits
|
||||
on smaller (more frequent) numbers.
|
||||
|
||||
The algorithm includes an escape-mechanism to mix uncompressed binary
|
||||
data with compressed bit-streams. This is useful when the bit-stream
|
||||
contains sections that do not compress well with this algorithm, for
|
||||
example in BRAM initialization data.
|
||||
|
||||
The compressed bitstream starts with the ASCII string "ICECOMPR", i.e.
|
||||
the hex values 0x49434543 and 0x4f4d5052 (stored as big-endian numbers).
|
||||
|
||||
After the 8 bytes magic the compressed bitstream is a stream of the
|
||||
following opcodes that must be interpreted by the decompressor. The
|
||||
notation ZERO and ONE is used for zero and one bits. The notation foo[N]
|
||||
is used for an N bit value "foo", transmitted MSB first.
|
||||
|
||||
ONE count[2]
|
||||
ZERO ONE count[5]
|
||||
ZERO ZERO ONE count[8]
|
||||
ZERO ZERO ZERO ZERO ONE count[23]
|
||||
output count ZERO bits followed by a single ONE bit
|
||||
|
||||
ZERO ZERO ZERO ONE count[6] data[count]
|
||||
output the count data bits followed by a single ONE bit
|
||||
|
||||
ZERO ZERO ZERO ZERO ZERO count[23]
|
||||
output count ZERO bits and stop decompressing. (end of file)
|
||||
|
||||
The program "icecompr" (C++11, ISC license) contains an implementation of a
|
||||
compressor and a decompressor. The decompressor in this program however is
|
||||
only used for integrity checking the compressed bit-stream.
|
||||
|
||||
The program "iceuncompr" (plain C, public domain) contains an implementation
|
||||
of a stand-alone decompressor. Simply copy&paste this implementation into
|
||||
your uC firmware.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* IceCompr -- A simple compressor for iCE40 bit-streams
|
||||
*
|
||||
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
static void push_int_bits(std::vector<bool> &outbits, int value, int bits)
|
||||
{
|
||||
while (bits-- > 0)
|
||||
outbits.push_back((value >> bits) & 1);
|
||||
}
|
||||
|
||||
static void push_zero_bits(std::vector<bool> &outbits, int bits)
|
||||
{
|
||||
while (bits-- > 0)
|
||||
outbits.push_back(false);
|
||||
}
|
||||
|
||||
static int decode_int_from_bits(const std::vector<bool> &inbits, int &cursor, int bits)
|
||||
{
|
||||
int ret = 0;
|
||||
while (bits-- > 0)
|
||||
if (inbits.at(cursor++))
|
||||
ret |= 1 << bits;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ice_compress(std::vector<bool> &outbits, const std::vector<bool> &inbits)
|
||||
{
|
||||
int opcode_stats_d4 = 0;
|
||||
int opcode_stats_d32 = 0;
|
||||
int opcode_stats_d256 = 0;
|
||||
int opcode_stats_raw = 0;
|
||||
int opcode_stats_d8M = 0;
|
||||
int opcode_stats_end = 0;
|
||||
|
||||
std::vector<int> deltas;
|
||||
int numzeros = 0;
|
||||
|
||||
for (auto bit : inbits)
|
||||
{
|
||||
if (bit) {
|
||||
deltas.push_back(numzeros);
|
||||
numzeros = 0;
|
||||
} else {
|
||||
numzeros++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < int(deltas.size()); i++)
|
||||
{
|
||||
int raw_len = 0;
|
||||
int compr_len = 0;
|
||||
int best_compr_raw_diff = -1;
|
||||
int best_compr_raw_idx = -1;
|
||||
int best_compr_raw_len = -1;
|
||||
|
||||
for (int j = 0; j+i < int(deltas.size()); j++)
|
||||
{
|
||||
int delta = deltas.at(i + j);
|
||||
raw_len += delta + 1;
|
||||
|
||||
if (delta < 4)
|
||||
compr_len += 3;
|
||||
else if (delta < 32)
|
||||
compr_len += 7;
|
||||
else if (delta < 256)
|
||||
compr_len += 11;
|
||||
else
|
||||
compr_len += 26;
|
||||
|
||||
if (compr_len - raw_len < std::max(best_compr_raw_diff - 4, 0) || raw_len > 64)
|
||||
break;
|
||||
|
||||
if (compr_len - raw_len > best_compr_raw_diff) {
|
||||
best_compr_raw_diff = compr_len - raw_len;
|
||||
best_compr_raw_idx = j;
|
||||
best_compr_raw_len = raw_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_compr_raw_diff > 9)
|
||||
{
|
||||
opcode_stats_raw++;
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(true);
|
||||
push_int_bits(outbits, best_compr_raw_len-1, 6);
|
||||
|
||||
for (int j = 0; j <= best_compr_raw_idx; j++) {
|
||||
int delta = deltas.at(i + j);
|
||||
for (int k = 0; k < delta; k++)
|
||||
outbits.push_back(false);
|
||||
if (j < best_compr_raw_idx)
|
||||
outbits.push_back(true);
|
||||
}
|
||||
|
||||
i += best_compr_raw_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
int delta = deltas.at(i);
|
||||
|
||||
if (delta < 4) {
|
||||
opcode_stats_d4++;
|
||||
outbits.push_back(true);
|
||||
push_int_bits(outbits, delta, 2);
|
||||
} else
|
||||
if (delta < 32) {
|
||||
opcode_stats_d32++;
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(true);
|
||||
push_int_bits(outbits, delta, 5);
|
||||
} else
|
||||
if (delta < 256) {
|
||||
opcode_stats_d256++;
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(true);
|
||||
push_int_bits(outbits, delta, 8);
|
||||
} else {
|
||||
opcode_stats_d8M++;
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(true);
|
||||
push_int_bits(outbits, delta, 23);
|
||||
}
|
||||
}
|
||||
|
||||
opcode_stats_end++;
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
outbits.push_back(false);
|
||||
push_int_bits(outbits, numzeros, 23);
|
||||
|
||||
if (verbose > 1) {
|
||||
fprintf(stderr, "opcode d4 %5d\n", opcode_stats_d4);
|
||||
fprintf(stderr, "opcode d32 %5d\n", opcode_stats_d32);
|
||||
fprintf(stderr, "opcode d256 %5d\n", opcode_stats_d256);
|
||||
fprintf(stderr, "opcode raw %5d\n", opcode_stats_raw);
|
||||
fprintf(stderr, "opcode d8M %5d\n", opcode_stats_d8M);
|
||||
fprintf(stderr, "opcode end %5d\n", opcode_stats_end);
|
||||
}
|
||||
}
|
||||
|
||||
void ice_uncompress(std::vector<bool> &outbits, const std::vector<bool> &inbits)
|
||||
{
|
||||
int cursor = 0;
|
||||
|
||||
while (cursor < int(inbits.size()))
|
||||
{
|
||||
if (inbits.at(cursor++)) {
|
||||
int zeros = decode_int_from_bits(inbits, cursor, 2);
|
||||
push_zero_bits(outbits, zeros);
|
||||
outbits.push_back(true);
|
||||
} else
|
||||
if (inbits.at(cursor++)) {
|
||||
int zeros = decode_int_from_bits(inbits, cursor, 5);
|
||||
push_zero_bits(outbits, zeros);
|
||||
outbits.push_back(true);
|
||||
} else
|
||||
if (inbits.at(cursor++)) {
|
||||
int zeros = decode_int_from_bits(inbits, cursor, 8);
|
||||
push_zero_bits(outbits, zeros);
|
||||
outbits.push_back(true);
|
||||
} else
|
||||
if (inbits.at(cursor++)) {
|
||||
int raw_len = decode_int_from_bits(inbits, cursor, 6);
|
||||
while (raw_len--)
|
||||
outbits.push_back(inbits.at(cursor++));
|
||||
outbits.push_back(true);
|
||||
} else
|
||||
if (inbits.at(cursor++)) {
|
||||
int zeros = decode_int_from_bits(inbits, cursor, 23);
|
||||
push_zero_bits(outbits, zeros);
|
||||
outbits.push_back(true);
|
||||
} else {
|
||||
int zeros = decode_int_from_bits(inbits, cursor, 23);
|
||||
push_zero_bits(outbits, zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void help()
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: icecompr [-v] [input-file [output-file]]\n");
|
||||
printf("\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *input_file = stdin;
|
||||
FILE *output_file = stdout;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "v")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
default:
|
||||
help();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
input_file = fopen(argv[optind], "rb");
|
||||
if (input_file == NULL) {
|
||||
fprintf(stderr, "Failed to open input file `%s': %s\n", argv[optind], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
output_file = fopen(argv[optind], "wb");
|
||||
if (output_file == NULL) {
|
||||
fprintf(stderr, "Failed to open output file `%s': %s\n", argv[optind], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (optind != argc)
|
||||
help();
|
||||
|
||||
std::vector<bool> original_bits;
|
||||
int count_set_bits = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int byte = fgetc(input_file);
|
||||
|
||||
if (byte < 0)
|
||||
break;
|
||||
|
||||
// MSB first
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
bool bit = (byte >> i) & 1;
|
||||
if (bit) count_set_bits++;
|
||||
original_bits.push_back(bit);
|
||||
}
|
||||
}
|
||||
|
||||
int uncompressed_size = original_bits.size();
|
||||
|
||||
if (verbose > 0) {
|
||||
fprintf(stderr, "Percentage of set bits: %.2f%%\n", (100.0*count_set_bits) / uncompressed_size);
|
||||
fprintf(stderr, "Uncompressed size: %8d bits\n", uncompressed_size);
|
||||
}
|
||||
|
||||
std::vector<bool> compressed_bits;
|
||||
ice_compress(compressed_bits, original_bits);
|
||||
|
||||
int compressed_size = compressed_bits.size();
|
||||
|
||||
if (verbose > 0) {
|
||||
fprintf(stderr, "Compressed size: %8d bits\n", compressed_size);
|
||||
fprintf(stderr, "Space savings: %.2f%%\n", 100 - (100.0*compressed_size) / uncompressed_size);
|
||||
}
|
||||
|
||||
std::vector<bool> uncompressed_bits;
|
||||
ice_uncompress(uncompressed_bits, compressed_bits);
|
||||
|
||||
bool check_ok = original_bits == uncompressed_bits;
|
||||
|
||||
if (verbose > 0 || !check_ok) {
|
||||
fprintf(stderr, "Integrity check: %s\n", check_ok ? "OK" : "ERROR");
|
||||
if (!check_ok)
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(output_file, "ICECOMPR");
|
||||
for (int i = 0; i < int(compressed_bits.size()); i += 8) {
|
||||
int value = 0;
|
||||
for (int j = 0; j < 8 && i+j < int(compressed_bits.size()); j++)
|
||||
if (compressed_bits.at(i+j))
|
||||
value |= 1 << (7-j);
|
||||
fputc(value, output_file);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
FILE *input_file;
|
||||
FILE *output_file;
|
||||
|
||||
int read_bitcounter;
|
||||
int read_buffer;
|
||||
|
||||
int write_bitcounter;
|
||||
int write_buffer;
|
||||
|
||||
static int read_bit()
|
||||
{
|
||||
if (read_bitcounter == 0) {
|
||||
read_bitcounter = 8;
|
||||
read_buffer = fgetc(input_file);
|
||||
}
|
||||
|
||||
read_bitcounter--;
|
||||
return (read_buffer >> read_bitcounter) & 1;
|
||||
}
|
||||
|
||||
static void write_bit(int value)
|
||||
{
|
||||
write_bitcounter--;
|
||||
|
||||
if (value)
|
||||
write_buffer |= 1 << write_bitcounter;
|
||||
|
||||
if (write_bitcounter == 0) {
|
||||
fputc(write_buffer, output_file);
|
||||
write_bitcounter = 8;
|
||||
write_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int read_int(int bits)
|
||||
{
|
||||
int ret = 0;
|
||||
while (bits-- > 0)
|
||||
if (read_bit())
|
||||
ret |= 1 << bits;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void write_zeros(int bits)
|
||||
{
|
||||
while (bits-- > 0)
|
||||
write_bit(0);
|
||||
}
|
||||
|
||||
int ice_uncompress()
|
||||
{
|
||||
read_bitcounter = 0;
|
||||
read_buffer = 0;
|
||||
|
||||
write_bitcounter = 8;
|
||||
write_buffer = 0;
|
||||
|
||||
int magic1_ok = read_int(32) == 0x49434543;
|
||||
int magic2_ok = read_int(32) == 0x4f4d5052;
|
||||
|
||||
if (!magic1_ok || !magic2_ok) {
|
||||
fprintf(stderr, "Missing ICECOMPR magic. Abort!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (read_bit()) {
|
||||
write_zeros(read_int(2));
|
||||
write_bit(1);
|
||||
} else
|
||||
if (read_bit()) {
|
||||
write_zeros(read_int(5));
|
||||
write_bit(1);
|
||||
} else
|
||||
if (read_bit()) {
|
||||
write_zeros(read_int(8));
|
||||
write_bit(1);
|
||||
} else
|
||||
if (read_bit()) {
|
||||
int n = read_int(6);
|
||||
while (n--)
|
||||
write_bit(read_bit());
|
||||
write_bit(1);
|
||||
} else
|
||||
if (read_bit()) {
|
||||
write_zeros(read_int(23));
|
||||
write_bit(1);
|
||||
} else {
|
||||
write_zeros(read_int(23));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void help()
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: iceuncompr [input-file [output-file]]\n");
|
||||
printf("\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
input_file = stdin;
|
||||
output_file = stdout;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "v")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'v':
|
||||
break;
|
||||
default:
|
||||
help();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
input_file = fopen(argv[optind], "rb");
|
||||
if (input_file == NULL) {
|
||||
fprintf(stderr, "Failed to open input file `%s': %s\n", argv[optind], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
output_file = fopen(argv[optind], "wb");
|
||||
if (output_file == NULL) {
|
||||
fprintf(stderr, "Failed to open output file `%s': %s\n", argv[optind], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (optind != argc)
|
||||
help();
|
||||
|
||||
if (optind != argc)
|
||||
help();
|
||||
|
||||
return ice_uncompress();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue