1 //-----------------------------------------------------------------------------
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // Compression tool for FPGA config files. Compress several *.bit files at
9 // compile time. Decompression is done at run time (see fpgaloader.c).
10 // This uses the zlib library tuned to this specific case. The small file sizes
11 // allow to use "insane" parameters for optimum compression ratio.
12 //-----------------------------------------------------------------------------
23 #define MAX(a,b) ((a)>(b)?(a):(b))
26 #define COMPRESS_LEVEL 9 // use best possible compression
27 #define COMPRESS_WINDOW_BITS 15 // default = max = 15 for a window of 2^15 = 32KBytes
28 #define COMPRESS_MEM_LEVEL 9 // determines the amount of memory allocated during compression. Default = 8.
29 /* COMPRESS_STRATEGY can be
30 Z_DEFAULT_STRATEGY (the default),
31 Z_FILTERED (more huffmann, less string matching),
32 Z_HUFFMAN_ONLY (huffman only, no string matching)
33 Z_RLE (distances limited to one)
34 Z_FIXED (prevents the use of dynamic Huffman codes)
37 #define COMPRESS_STRATEGY Z_DEFAULT_STRATEGY
38 // zlib tuning parameters:
39 #define COMPRESS_GOOD_LENGTH 258
40 #define COMPRESS_MAX_LAZY 258
41 #define COMPRESS_MAX_NICE_LENGTH 258
42 #define COMPRESS_MAX_CHAIN 8192
44 #define HARDNESTED_TABLE_SIZE (sizeof(uint32_t) * ((1L<<19)+1))
46 static void usage(void)
48 fprintf(stdout
, "Usage: fpga_compress <infile1> <infile2> ... <infile_n> <outfile>\n");
49 fprintf(stdout
, " Combine n FPGA bitstream files and compress them into one.\n\n");
50 fprintf(stdout
, " fpga_compress -v <infile1> <infile2> ... <infile_n> <outfile>\n");
51 fprintf(stdout
, " Extract Version Information from FPGA bitstream files and write it to <outfile>\n\n");
52 fprintf(stdout
, " fpga_compress -d <infile> <outfile>\n");
53 fprintf(stdout
, " Decompress <infile>. Write result to <outfile>\n\n");
54 fprintf(stdout
, " fpga_compress -t <infile> <outfile>\n");
55 fprintf(stdout
, " Compress hardnested table <infile>. Write result to <outfile>\n\n");
59 static voidpf
fpga_deflate_malloc(voidpf opaque
, uInt items
, uInt size
)
61 return malloc(items
*size
);
65 static void fpga_deflate_free(voidpf opaque
, voidpf address
)
71 static bool all_feof(FILE *infile
[], uint8_t num_infiles
)
73 for (uint16_t i
= 0; i
< num_infiles
; i
++) {
74 if (!feof(infile
[i
])) {
83 int zlib_compress(FILE *infile
[], uint8_t num_infiles
, FILE *outfile
, bool hardnested_mode
)
89 z_stream compressed_fpga_stream
;
91 if (hardnested_mode
) {
92 fpga_config
= malloc(num_infiles
* HARDNESTED_TABLE_SIZE
);
94 fpga_config
= malloc(num_infiles
* FPGA_CONFIG_SIZE
);
96 // read the input files. Interleave them into fpga_config[]
100 if (i
>= num_infiles
* (hardnested_mode
?HARDNESTED_TABLE_SIZE
:FPGA_CONFIG_SIZE
)) {
101 if (hardnested_mode
) {
102 fprintf(stderr
, "Input file too big (> %zu bytes). This is probably not a hardnested bitflip state table.\n", HARDNESTED_TABLE_SIZE
);
104 fprintf(stderr
, "Input files too big (total > %zu bytes). These are probably not PM3 FPGA config files.\n", num_infiles
*FPGA_CONFIG_SIZE
);
106 for(uint16_t j
= 0; j
< num_infiles
; j
++) {
110 return(EXIT_FAILURE
);
113 for(uint16_t j
= 0; j
< num_infiles
; j
++) {
114 for(uint16_t k
= 0; k
< FPGA_INTERLEAVE_SIZE
; k
++) {
115 c
= (uint8_t)fgetc(infile
[j
]);
116 if (!feof(infile
[j
])) {
117 fpga_config
[i
++] = c
;
118 } else if (num_infiles
> 1) {
119 fpga_config
[i
++] = '\0';
124 } while (!all_feof(infile
, num_infiles
));
126 // initialize zlib structures
127 compressed_fpga_stream
.next_in
= fpga_config
;
128 compressed_fpga_stream
.avail_in
= i
;
129 compressed_fpga_stream
.zalloc
= fpga_deflate_malloc
;
130 compressed_fpga_stream
.zfree
= fpga_deflate_free
;
131 compressed_fpga_stream
.opaque
= Z_NULL
;
132 ret
= deflateInit2(&compressed_fpga_stream
,
135 COMPRESS_WINDOW_BITS
,
139 // estimate the size of the compressed output
140 uint32_t outsize_max
= deflateBound(&compressed_fpga_stream
, compressed_fpga_stream
.avail_in
);
141 uint8_t *outbuf
= malloc(outsize_max
);
142 compressed_fpga_stream
.next_out
= outbuf
;
143 compressed_fpga_stream
.avail_out
= outsize_max
;
146 ret
= deflateTune(&compressed_fpga_stream
,
147 COMPRESS_GOOD_LENGTH
,
149 COMPRESS_MAX_NICE_LENGTH
,
154 ret
= deflate(&compressed_fpga_stream
, Z_FINISH
);
157 fprintf(stdout
, "compressed %u input bytes to %lu output bytes\n", i
, compressed_fpga_stream
.total_out
);
159 if (ret
!= Z_STREAM_END
) {
160 fprintf(stderr
, "Error in deflate(): %i %s\n", ret
, compressed_fpga_stream
.msg
);
162 deflateEnd(&compressed_fpga_stream
);
163 for(uint16_t j
= 0; j
< num_infiles
; j
++) {
169 return(EXIT_FAILURE
);
172 for (i
= 0; i
< compressed_fpga_stream
.total_out
; i
++) {
173 fputc(outbuf
[i
], outfile
);
177 deflateEnd(&compressed_fpga_stream
);
178 for(uint16_t j
= 0; j
< num_infiles
; j
++) {
185 return(EXIT_SUCCESS
);
190 int zlib_decompress(FILE *infile
, FILE *outfile
)
192 #define DECOMPRESS_BUF_SIZE 1024
193 uint8_t outbuf
[DECOMPRESS_BUF_SIZE
];
194 uint8_t inbuf
[DECOMPRESS_BUF_SIZE
];
197 z_stream compressed_fpga_stream
;
199 // initialize zlib structures
200 compressed_fpga_stream
.next_in
= inbuf
;
201 compressed_fpga_stream
.avail_in
= 0;
202 compressed_fpga_stream
.next_out
= outbuf
;
203 compressed_fpga_stream
.avail_out
= DECOMPRESS_BUF_SIZE
;
204 compressed_fpga_stream
.zalloc
= fpga_deflate_malloc
;
205 compressed_fpga_stream
.zfree
= fpga_deflate_free
;
206 compressed_fpga_stream
.opaque
= Z_NULL
;
208 ret
= inflateInit2(&compressed_fpga_stream
, 0);
211 if (compressed_fpga_stream
.avail_in
== 0) {
212 compressed_fpga_stream
.next_in
= inbuf
;
215 int32_t c
= fgetc(infile
);
217 inbuf
[i
++] = c
& 0xFF;
218 compressed_fpga_stream
.avail_in
++;
222 } while (i
< DECOMPRESS_BUF_SIZE
);
225 ret
= inflate(&compressed_fpga_stream
, Z_SYNC_FLUSH
);
227 if (ret
!= Z_OK
&& ret
!= Z_STREAM_END
) {
231 if (compressed_fpga_stream
.avail_out
== 0) {
232 for (uint16_t i
= 0; i
< DECOMPRESS_BUF_SIZE
; i
++) {
233 fputc(outbuf
[i
], outfile
);
235 compressed_fpga_stream
.avail_out
= DECOMPRESS_BUF_SIZE
;
236 compressed_fpga_stream
.next_out
= outbuf
;
238 } while (ret
== Z_OK
);
240 if (ret
== Z_STREAM_END
) { // reached end of input
242 while (compressed_fpga_stream
.avail_out
< DECOMPRESS_BUF_SIZE
) {
243 fputc(outbuf
[i
++], outfile
);
244 compressed_fpga_stream
.avail_out
++;
248 return(EXIT_SUCCESS
);
250 fprintf(stderr
, "Error. Inflate() returned error %i, %s", ret
, compressed_fpga_stream
.msg
);
253 return(EXIT_FAILURE
);
259 /* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence
260 * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01
261 * After that the format is 1 byte section type (ASCII character), 2 byte length
262 * (big endian), <length> bytes content. Except for section 'e' which has 4 bytes
265 static int bitparse_find_section(FILE *infile
, char section_name
, unsigned int *section_length
)
268 #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section
269 uint16_t numbytes
= 0;
270 while(numbytes
< MAX_FPGA_BIT_STREAM_HEADER_SEARCH
) {
271 char current_name
= (char)fgetc(infile
);
273 if(current_name
< 'a' || current_name
> 'e') {
274 /* Strange section name, abort */
277 unsigned int current_length
= 0;
278 switch(current_name
) {
280 /* Four byte length field */
281 current_length
+= fgetc(infile
) << 24;
282 current_length
+= fgetc(infile
) << 16;
284 default: /* Fall through, two byte length field */
285 current_length
+= fgetc(infile
) << 8;
286 current_length
+= fgetc(infile
) << 0;
290 if(current_name
!= 'e' && current_length
> 255) {
291 /* Maybe a parse error */
295 if(current_name
== section_name
) {
297 *section_length
= current_length
;
302 for (uint16_t i
= 0; i
< current_length
&& numbytes
< MAX_FPGA_BIT_STREAM_HEADER_SEARCH
; i
++) {
312 static int FpgaGatherVersion(FILE *infile
, char* infile_name
, char *dst
, int len
)
314 unsigned int fpga_info_len
;
315 char tempstr
[40] = {0x00};
319 for (uint16_t i
= 0; i
< FPGA_BITSTREAM_FIXED_HEADER_SIZE
; i
++) {
320 if (fgetc(infile
) != bitparse_fixed_header
[i
]) {
321 fprintf(stderr
, "Invalid FPGA file. Aborting...\n\n");
322 return(EXIT_FAILURE
);
326 strncat(dst
, basename(infile_name
), len
-1);
327 // if (bitparse_find_section(infile, 'a', &fpga_info_len)) {
328 // for (uint16_t i = 0; i < fpga_info_len; i++) {
329 // char c = (char)fgetc(infile);
330 // if (i < sizeof(tempstr)) {
334 // strncat(dst, tempstr, len-1);
336 strncat(dst
, " built", len
-1);
337 if (bitparse_find_section(infile
, 'b', &fpga_info_len
)) {
338 strncat(dst
, " for ", len
-1);
339 for (uint16_t i
= 0; i
< fpga_info_len
; i
++) {
340 char c
= (char)fgetc(infile
);
341 if (i
< sizeof(tempstr
)) {
345 strncat(dst
, tempstr
, len
-1);
347 if (bitparse_find_section(infile
, 'c', &fpga_info_len
)) {
348 strncat(dst
, " on ", len
-1);
349 for (uint16_t i
= 0; i
< fpga_info_len
; i
++) {
350 char c
= (char)fgetc(infile
);
351 if (i
< sizeof(tempstr
)) {
355 strncat(dst
, tempstr
, len
-1);
357 if (bitparse_find_section(infile
, 'd', &fpga_info_len
)) {
358 strncat(dst
, " at ", len
-1);
359 for (uint16_t i
= 0; i
< fpga_info_len
; i
++) {
360 char c
= (char)fgetc(infile
);
361 if (i
< sizeof(tempstr
)) {
365 strncat(dst
, tempstr
, len
-1);
371 static void print_version_info_preamble(FILE *outfile
, int num_infiles
) {
372 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
373 fprintf(outfile
, "// piwi, 2018\n");
374 fprintf(outfile
, "//\n");
375 fprintf(outfile
, "// This code is licensed to you under the terms of the GNU GPL, version 2 or,\n");
376 fprintf(outfile
, "// at your option, any later version. See the LICENSE.txt file for the text of\n");
377 fprintf(outfile
, "// the license.\n");
378 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
379 fprintf(outfile
, "// Version information on fpga images\n");
380 fprintf(outfile
, "//\n");
381 fprintf(outfile
, "// This file is generated by fpga_compress. Don't edit!\n");
382 fprintf(outfile
, "//-----------------------------------------------------------------------------\n");
383 fprintf(outfile
, "\n");
384 fprintf(outfile
, "\n");
385 fprintf(outfile
, "const int fpga_bitstream_num = %d;\n", num_infiles
);
386 fprintf(outfile
, "const char* const fpga_version_information[%d] = {\n", num_infiles
);
390 static int generate_fpga_version_info(FILE *infile
[], char *infile_names
[], int num_infiles
, FILE *outfile
) {
392 char version_string
[80] = "";
394 print_version_info_preamble(outfile
, num_infiles
);
396 for (int i
= 0; i
< num_infiles
; i
++) {
397 FpgaGatherVersion(infile
[i
], infile_names
[i
], version_string
, sizeof(version_string
));
398 fprintf(outfile
, "\t\"%s\"", version_string
);
399 if (i
!= num_infiles
-1) {
400 fprintf(outfile
, ",");
402 fprintf(outfile
,"\n");
405 fprintf(outfile
, "};\n");
411 int main(int argc
, char **argv
)
417 if (argc
== 1 || argc
== 2) {
419 return(EXIT_FAILURE
);
422 if (!strcmp(argv
[1], "-d")) { // Decompress
424 infiles
= calloc(1, sizeof(FILE*));
427 return(EXIT_FAILURE
);
429 infiles
[0] = fopen(argv
[2], "rb");
430 if (infiles
[0] == NULL
) {
431 fprintf(stderr
, "Error. Cannot open input file %s\n\n", argv
[2]);
432 return(EXIT_FAILURE
);
434 outfile
= fopen(argv
[3], "wb");
435 if (outfile
== NULL
) {
436 fprintf(stderr
, "Error. Cannot open output file %s\n\n", argv
[3]);
437 return(EXIT_FAILURE
);
439 return zlib_decompress(infiles
[0], outfile
);
441 } else { // Compress or gemerate version info
443 bool hardnested_mode
= false;
444 bool generate_version_file
= false;
445 int num_input_files
= 0;
446 if (!strcmp(argv
[1], "-t")) { // compress one hardnested table
449 return(EXIT_FAILURE
);
451 hardnested_mode
= true;
453 } else if (!strcmp(argv
[1], "-v")) { // generate version info
454 generate_version_file
= true;
455 num_input_files
= argc
-3;
456 } else { // compress 1..n fpga files
457 num_input_files
= argc
-2;
460 infiles
= calloc(num_input_files
, sizeof(FILE*));
461 infile_names
= calloc(num_input_files
, sizeof(char*));
462 for (uint16_t i
= 0; i
< num_input_files
; i
++) {
463 infile_names
[i
] = argv
[i
+((hardnested_mode
|| generate_version_file
)?2:1)];
464 infiles
[i
] = fopen(infile_names
[i
], "rb");
465 if (infiles
[i
] == NULL
) {
466 fprintf(stderr
, "Error. Cannot open input file %s\n\n", infile_names
[i
]);
467 return(EXIT_FAILURE
);
470 outfile
= fopen(argv
[argc
-1], "wb");
471 if (outfile
== NULL
) {
472 fprintf(stderr
, "Error. Cannot open output file %s\n\n", argv
[argc
-1]);
473 return(EXIT_FAILURE
);
475 if (generate_version_file
) {
476 if (generate_fpga_version_info(infiles
, infile_names
, num_input_files
, outfile
)) {
477 return(EXIT_FAILURE
);
480 return zlib_compress(infiles
, num_input_files
, outfile
, hardnested_mode
);