905 lines
25 KiB
C
905 lines
25 KiB
C
//
|
|
// Author: Wolfgang Spraul <wspraul@q-ag.de>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version
|
|
// 3 of the License, or (at your option) any later version.
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#define PROGRAM_REVISION "2012-06-01"
|
|
|
|
// 120 MB max bitstream size is enough for now
|
|
#define BITSTREAM_READ_PAGESIZE 4096
|
|
#define BITSTREAM_READ_MAXPAGES 30000 // 120 MB max bitstream
|
|
|
|
__inline uint16_t __swab16(uint16_t x)
|
|
{
|
|
return (((x & 0x00ffU) << 8) | \
|
|
((x & 0xff00U) >> 8)); \
|
|
}
|
|
|
|
__inline uint32_t __swab32(uint32_t x)
|
|
{
|
|
return (((x & 0x000000ffUL) << 24) | \
|
|
((x & 0x0000ff00UL) << 8) | \
|
|
((x & 0x00ff0000UL) >> 8) | \
|
|
((x & 0xff000000UL) >> 24)); \
|
|
}
|
|
|
|
#define __be32_to_cpu(x) __swab32((uint32_t)(x))
|
|
#define __be16_to_cpu(x) __swab16((uint16_t)(x))
|
|
#define __cpu_to_be32(x) __swab32((uint32_t)(x))
|
|
#define __cpu_to_be16(x) __swab16((uint16_t)(x))
|
|
|
|
#define __le32_to_cpu(x) ((uint32_t)(x))
|
|
#define __le16_to_cpu(x) ((uint16_t)(x))
|
|
#define __cpu_to_le32(x) ((uint32_t)(x))
|
|
#define __cpu_to_le16(x) ((uint16_t)(x))
|
|
|
|
void help();
|
|
|
|
//
|
|
// xc6 configuration registers, documentation in ug380, page90
|
|
//
|
|
|
|
enum {
|
|
CRC = 0, FAR_MAJ, FAR_MIN, FDRI, FDRO, CMD, CTL, MASK, STAT, LOUT, COR1,
|
|
COR2, PWRDN_REG, FLR, IDCODE, CWDT, HC_OPT_REG, CSBO = 18,
|
|
GENERAL1, GENERAL2, GENERAL3, GENERAL4, GENERAL5, MODE_REG, PU_GWE,
|
|
PU_GTS, MFWR, CCLK_FREQ, SEU_OPT, EXP_SIGN, RDBK_SIGN, BOOTSTS,
|
|
EYE_MASK, CBC_REG
|
|
};
|
|
|
|
#define REG_R 0x01
|
|
#define REG_W 0x02
|
|
#define REG_RW (REG_R | REG_W)
|
|
|
|
typedef struct
|
|
{
|
|
char* name;
|
|
int rw; // REG_READ / REG_WRITE
|
|
} REG_INFO;
|
|
|
|
const REG_INFO xc6_regs[] =
|
|
{
|
|
[CRC] = {"CRC", REG_W},
|
|
[FAR_MAJ] = {"FAR_MAJ", REG_W}, // frame address register block and major
|
|
[FAR_MIN] = {"FAR_MIN", REG_W}, // frame address register minor
|
|
[FDRI] = {"FDRI", REG_W}, // frame data input
|
|
[FDRO] = {"FDRO", REG_R}, // frame data output
|
|
[CMD] = {"CMD", REG_RW}, // command
|
|
[CTL] = {"CTL", REG_RW}, // control
|
|
[MASK] = {"MASK", REG_RW}, // control mask
|
|
[STAT] = {"STAT", REG_R}, // status
|
|
[LOUT] = {"LOUT", REG_W}, // legacy output for serial daisy-chain
|
|
[COR1] = {"COR1", REG_RW}, // configuration option 1
|
|
[COR2] = {"COR2", REG_RW}, // configuration option 2
|
|
[PWRDN_REG] = {"PWRDN_REG", REG_RW}, // power-down option register
|
|
[FLR] = {"FLR", REG_W}, // frame length register
|
|
[IDCODE] = {"IDCODE", REG_RW}, // product IDCODE
|
|
[CWDT] = {"CWDT", REG_RW}, // configuration watchdog timer
|
|
[HC_OPT_REG] = {"HC_OPT_REG", REG_RW}, // house clean option register
|
|
[CSBO] = {"CSBO", REG_W}, // CSB output for parallel daisy-chaining
|
|
[GENERAL1] = {"GENERAL1", REG_RW}, // power-up self test or loadable
|
|
// program addr
|
|
[GENERAL2] = {"GENERAL2", REG_RW}, // power-up self test or loadable
|
|
// program addr and new SPI opcode
|
|
[GENERAL3] = {"GENERAL3", REG_RW}, // golden bitstream address
|
|
[GENERAL4] = {"GENERAL4", REG_RW}, // golden bitstream address and new
|
|
// SPI opcode
|
|
[GENERAL5] = {"GENERAL5", REG_RW}, // user-defined register for
|
|
// fail-safe scheme
|
|
[MODE_REG] = {"MODE_REG", REG_RW}, // reboot mode
|
|
[PU_GWE] = {"PU_GWE", REG_W}, // GWE cycle during wake-up from suspend
|
|
[PU_GTS] = {"PU_GTS", REG_W}, // GTS cycle during wake-up from suspend
|
|
[MFWR] = {"MFWR", REG_W}, // multi-frame write register
|
|
[CCLK_FREQ] = {"CCLK_FREQ", REG_W}, // CCLK frequency select for
|
|
// master mode
|
|
[SEU_OPT] = {"SEU_OPT", REG_RW}, // SEU frequency, enable and status
|
|
[EXP_SIGN] = {"EXP_SIGN", REG_RW}, // expected readback signature for
|
|
// SEU detect
|
|
[RDBK_SIGN] = {"RDBK_SIGN", REG_W}, // readback signature for readback
|
|
// cmd and SEU
|
|
[BOOTSTS] = {"BOOTSTS", REG_R}, // boot history register
|
|
[EYE_MASK] = {"EYE_MASK", REG_RW}, // mask pins for multi-pin wake-up
|
|
[CBC_REG] = {"CBC_REG", REG_W} // initial CBC value register
|
|
};
|
|
|
|
// The highest 4 bits are the binary revision and not
|
|
// used when performing IDCODE verification.
|
|
// ug380, Configuration Sequence, page 78
|
|
typedef struct
|
|
{
|
|
char* name;
|
|
uint32_t code;
|
|
} IDCODES;
|
|
|
|
const IDCODES idcodes[] =
|
|
{
|
|
{"XC6SLX4", 0x04000093},
|
|
{"XC6SLX9", 0x04001093},
|
|
{"XC6SLX16", 0x04002093},
|
|
{"XC6SLX25", 0x04004093},
|
|
{"XC6SLX25T", 0x04024093},
|
|
{"XC6SLX45", 0x04008093},
|
|
{"XC6SLX45T", 0x04028093},
|
|
{"XC6SLX75", 0x0400E093},
|
|
{"XC6SLX75T", 0x0402E093},
|
|
{"XC6SLX100", 0x04011093},
|
|
{"XC6SLX100T", 0x04031093},
|
|
{"XC6SLX150", 0x0401D093}
|
|
};
|
|
|
|
// CMD register - ug380, page 92
|
|
enum {
|
|
CMD_NULL = 0, CMD_WCFG, CMD_MFW, CMD_LFRM, CMD_RCFG, CMD_START,
|
|
CMD_RCRC = 7, CMD_AGHIGH, CMD_GRESTORE = 10, CMD_SHUTDOWN,
|
|
CMD_DESYNC = 13, CMD_IPROG
|
|
};
|
|
|
|
const char* cmds[] =
|
|
{
|
|
[CMD_NULL] = "NULL",
|
|
[CMD_WCFG] = "WCFG",
|
|
[CMD_MFW] = "MFW",
|
|
[CMD_LFRM] = "LFRM",
|
|
[CMD_RCFG] = "RCFG",
|
|
[CMD_START] = "START",
|
|
[CMD_RCRC] = "RCRC",
|
|
[CMD_AGHIGH] = "AGHIGH",
|
|
[CMD_GRESTORE] = "GRESTORE",
|
|
[CMD_SHUTDOWN] = "SHUTDOWN",
|
|
[CMD_DESYNC] = "DESYNC",
|
|
[CMD_IPROG] = "IPROG"
|
|
};
|
|
|
|
static const char* bitstr(uint32_t value, int digits)
|
|
{
|
|
static char str[2 /* "0b" */ + 32 + 1 /* '\0' */];
|
|
int i;
|
|
|
|
str[0] = '0';
|
|
str[1] = 'b';
|
|
for (i = 0; i < digits; i++)
|
|
str[digits-i+1] = (value & (1<<i)) ? '1' : '0';
|
|
str[digits+2] = 0;
|
|
return str;
|
|
}
|
|
|
|
void hexdump(const uint8_t* data, int len)
|
|
{
|
|
int i, j;
|
|
i = 0;
|
|
while (i < len) {
|
|
printf("@%05x %02x", i, data[i]);
|
|
for (j = 1; (j < 8) && (i + j < len); j++) {
|
|
if (i + j >= len) break;
|
|
printf(" %02x", data[i+j]);
|
|
}
|
|
printf("\n");
|
|
i += 8;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
uint8_t* bit_data = 0;
|
|
FILE* bitf = 0;
|
|
uint32_t bit_cur, bit_eof, cmd_len, u32, u16_off;
|
|
uint16_t str_len, u16, packet_hdr_type, packet_hdr_opcode;
|
|
uint16_t packet_hdr_register, packet_hdr_wordcount;
|
|
int info = 0; // whether to print #I info messages (offsets and others)
|
|
char* bit_path = 0;
|
|
int i, j;
|
|
|
|
//
|
|
// parse command line
|
|
//
|
|
|
|
if (argc < 2) {
|
|
help();
|
|
return EXIT_SUCCESS;
|
|
}
|
|
for (i = 1; i < argc; i++) {
|
|
if (!strcmp(argv[i], "--help")) {
|
|
help();
|
|
return EXIT_SUCCESS;
|
|
}
|
|
if (!strcmp(argv[i], "--version")) {
|
|
printf("%s\n", PROGRAM_REVISION);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
if (!strcmp(argv[i], "--info")) {
|
|
info = 1;
|
|
} else {
|
|
bit_path = argv[i];
|
|
if (argc > i+1) { // only 1 path supported
|
|
help();
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
if (!bit_path) { // shouldn't get here, just in case
|
|
help();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// read .bit into memory
|
|
//
|
|
|
|
bit_data = malloc(BITSTREAM_READ_MAXPAGES * BITSTREAM_READ_PAGESIZE);
|
|
if (!bit_data) {
|
|
fprintf(stderr, "#E Cannot allocate %i bytes for bitstream.\n",
|
|
BITSTREAM_READ_MAXPAGES * BITSTREAM_READ_PAGESIZE);
|
|
goto fail;
|
|
}
|
|
if (!(bitf = fopen(bit_path, "rb"))) {
|
|
fprintf(stderr, "#E Error opening %s.\n", bit_path);
|
|
goto fail;
|
|
}
|
|
bit_eof = 0;
|
|
while (bit_eof < BITSTREAM_READ_MAXPAGES * BITSTREAM_READ_PAGESIZE) {
|
|
size_t num_read = fread(&bit_data[bit_eof], sizeof(uint8_t),
|
|
BITSTREAM_READ_PAGESIZE, bitf);
|
|
bit_eof += num_read;
|
|
if (num_read != BITSTREAM_READ_PAGESIZE)
|
|
break;
|
|
}
|
|
fclose(bitf);
|
|
if (bit_eof >= BITSTREAM_READ_MAXPAGES * BITSTREAM_READ_PAGESIZE) {
|
|
fprintf(stderr, "#E Bitstream size above maximum of "
|
|
"%i bytes.\n", BITSTREAM_READ_MAXPAGES *
|
|
BITSTREAM_READ_PAGESIZE);
|
|
goto fail;
|
|
}
|
|
|
|
//
|
|
// header
|
|
//
|
|
|
|
printf("bit2txt_format 1\n");
|
|
|
|
// offset 0 - magic header
|
|
if (bit_eof < 13) {
|
|
fprintf(stderr, "#E File size %i below minimum of 13 bytes.\n",
|
|
bit_eof);
|
|
goto fail;
|
|
}
|
|
printf("hex");
|
|
for (i = 0; i < 13; i++)
|
|
printf(" %.02x", bit_data[i]);
|
|
printf("\n");
|
|
|
|
// 4 strings 'a' - 'd', 16-bit length
|
|
bit_cur = 13;
|
|
for (i = 'a'; i <= 'd'; i++) {
|
|
if (bit_eof < bit_cur + 3) {
|
|
fprintf(stderr, "#E Unexpected EOF at %i.\n", bit_eof);
|
|
goto fail;
|
|
}
|
|
if (bit_data[bit_cur] != i) {
|
|
fprintf(stderr, "#E Expected string code '%c', got "
|
|
"'%c'.\n", i, bit_data[bit_cur]);
|
|
goto fail;
|
|
}
|
|
str_len = __be16_to_cpu(*(uint16_t*)&bit_data[bit_cur + 1]);
|
|
if (bit_eof < bit_cur + 3 + str_len) {
|
|
fprintf(stderr, "#E Unexpected EOF at %i.\n", bit_eof);
|
|
goto fail;
|
|
}
|
|
if (bit_data[bit_cur + 3 + str_len - 1] != 0) {
|
|
fprintf(stderr, "#E z-terminated string ends with %0xh"
|
|
".\n", bit_data[bit_cur + 3 + str_len - 1]);
|
|
goto fail;
|
|
}
|
|
printf("header_str_%c %s\n", i, &bit_data[bit_cur + 3]);
|
|
bit_cur += 3 + str_len;
|
|
}
|
|
|
|
//
|
|
// commands
|
|
//
|
|
|
|
if (bit_cur + 5 > bit_eof) goto fail_eof;
|
|
if (bit_data[bit_cur] != 'e') {
|
|
fprintf(stderr, "#E Expected string code 'e', got '%c'.\n",
|
|
bit_data[bit_cur]);
|
|
goto fail;
|
|
}
|
|
cmd_len = __be32_to_cpu(*(uint32_t*)&bit_data[bit_cur + 1]);
|
|
bit_cur += 5;
|
|
if (bit_cur + cmd_len > bit_eof) goto fail_eof;
|
|
if (bit_cur + cmd_len < bit_eof) {
|
|
printf("#W Unexpected continuation after offset "
|
|
"%i.\n", bit_cur + 5 + cmd_len);
|
|
}
|
|
|
|
// hex-dump everything until 0xAA (sync word: 0xAA995566)
|
|
if (bit_cur >= bit_eof) goto fail_eof;
|
|
if (bit_data[bit_cur] != 0xAA) {
|
|
printf("hex");
|
|
while (bit_cur < bit_eof && bit_data[bit_cur] != 0xAA) {
|
|
printf(" %.02x", bit_data[bit_cur]);
|
|
bit_cur++; if (bit_cur >= bit_eof) goto fail_eof;
|
|
}
|
|
printf("\n");
|
|
}
|
|
if (bit_cur + 4 > bit_eof) goto fail_eof;
|
|
if (info) printf("#I sync word at offset 0x%x.\n", bit_cur);
|
|
u32 = __be32_to_cpu(*(uint32_t*)&bit_data[bit_cur]);
|
|
bit_cur += 4;
|
|
if (u32 != 0xAA995566) {
|
|
fprintf(stderr, "#E Unexpected sync word 0x%x.\n", u32);
|
|
goto fail;
|
|
}
|
|
printf("sync_word\n");
|
|
|
|
while (bit_cur < bit_eof) {
|
|
|
|
// packet header: ug380, Configuration Packets (p88)
|
|
if (info) printf("#I Packet header at off 0x%x.\n", bit_cur);
|
|
|
|
if (bit_cur + 2 > bit_eof) goto fail_eof;
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[bit_cur]);
|
|
u16_off = bit_cur; bit_cur += 2;
|
|
|
|
// 3 bits: 001 = Type 1; 010 = Type 2
|
|
packet_hdr_type = (u16 & 0xE000) >> 13;
|
|
|
|
if (packet_hdr_type != 1 && packet_hdr_type != 2) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected packet type "
|
|
"%u.\n", u16_off, u16, packet_hdr_type);
|
|
goto fail;
|
|
}
|
|
|
|
// 2 bits: 00 = noop; 01 = read; 10 = write; 11 = reserved
|
|
packet_hdr_opcode = (u16 & 0x1800) >> 11;
|
|
if (packet_hdr_opcode == 3) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected packet opcode "
|
|
"3.\n", u16_off, u16);
|
|
goto fail;
|
|
}
|
|
if (packet_hdr_opcode == 0) { // noop
|
|
if (packet_hdr_type != 1)
|
|
printf("#W 0x%x=0x%x Unexpected packet"
|
|
" type %u noop.\n", u16_off,
|
|
u16, packet_hdr_type);
|
|
if (u16 & 0x07FF)
|
|
printf("#W 0x%x=0x%x Unexpected noop "
|
|
"header.\n", u16_off, u16);
|
|
printf("noop\n");
|
|
continue;
|
|
}
|
|
|
|
// Now we must look at a Type 1 read or write command
|
|
packet_hdr_register = (u16 & 0x07E0) >> 5;
|
|
packet_hdr_wordcount = u16 & 0x001F;
|
|
if (bit_cur + packet_hdr_wordcount*2 > bit_eof) goto fail_eof;
|
|
bit_cur += packet_hdr_wordcount*2;
|
|
|
|
// Check whether register and r/w action on register
|
|
// looks valid.
|
|
|
|
if (packet_hdr_type == 1) {
|
|
if (packet_hdr_register >= sizeof(xc6_regs)
|
|
/ sizeof(xc6_regs[0])
|
|
|| xc6_regs[packet_hdr_register].name[0] == 0) {
|
|
printf("#W 0x%x=0x%x unknown T1 reg %u, "
|
|
"skipping %d words.\n", u16_off, u16,
|
|
packet_hdr_register, packet_hdr_wordcount);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == IDCODE) {
|
|
if (packet_hdr_wordcount != 2) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected IDCODE"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u32 = __be32_to_cpu(*(uint32_t*)&bit_data[u16_off+2]);
|
|
for (i = 0; i < sizeof(idcodes)/sizeof(idcodes[0]); i++) {
|
|
if ((u32 & 0x0FFFFFFF) == idcodes[i].code) {
|
|
printf("T1 IDCODE %s\n", idcodes[i].name);
|
|
break;
|
|
}
|
|
}
|
|
if (i >= sizeof(idcodes)/sizeof(idcodes[0]))
|
|
printf("#W Unknown IDCODE 0x%x.\n", u32);
|
|
else if (u32 & 0xF0000000)
|
|
printf("#W Unexpected revision bits in IDCODE 0x%x.\n", u32);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == CMD) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected CMD"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
if (u16 >= sizeof(cmds) / sizeof(cmds[0])
|
|
|| cmds[u16][0] == 0)
|
|
printf("#W 0x%x=0x%x Unknown CMD.\n",
|
|
u16_off, u16);
|
|
else
|
|
printf("T1 CMD %s\n", cmds[u16]);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == FLR) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected FLR"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 FLR %u\n", u16);
|
|
// There are 3 types of frames. Type0 (clb, ioi
|
|
// and special blocks), type1 (bram) and type2
|
|
// (iob). The size of a type0 and type1 is
|
|
// fixed, only the size of a type2 (iob) is
|
|
// specified with the FLR register.
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == COR1) {
|
|
int unexpected_clk11 = 0;
|
|
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected COR1"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 COR1");
|
|
if (u16 & 0x8000) {
|
|
printf(" DRIVE_AWAKE");
|
|
u16 &= ~0x8000;
|
|
}
|
|
if (u16 & 0x0010) {
|
|
printf(" CRC_BYPASS");
|
|
u16 &= ~0x0010;
|
|
}
|
|
if (u16 & 0x0008) {
|
|
printf(" DONE_PIPE");
|
|
u16 &= ~0x0008;
|
|
}
|
|
if (u16 & 0x0004) {
|
|
printf(" DRIVE_DONE");
|
|
u16 &= ~0x0004;
|
|
}
|
|
if (u16 & 0x0003) {
|
|
if (u16 & 0x0002) {
|
|
if (u16 & 0x0001)
|
|
unexpected_clk11 = 1;
|
|
printf(" SSCLKSRC=TCK");
|
|
} else
|
|
printf(" SSCLKSRC=UserClk");
|
|
u16 &= ~0x0003;
|
|
}
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
if (unexpected_clk11)
|
|
printf("#W Unexpected SSCLKSRC 11.\n");
|
|
// Reserved bits 14:5 should be 0110111000
|
|
// according to documentation.
|
|
if (u16 != 0x3700)
|
|
printf("#W Expected reserved 0x%x, got 0x%x.\n", 0x3700, u16);
|
|
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == COR2) {
|
|
int unexpected_done_cycle = 0;
|
|
int unexpected_lck_cycle = 0;
|
|
unsigned cycle;
|
|
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected COR2"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 COR2");
|
|
if (u16 & 0x8000) {
|
|
printf(" RESET_ON_ERROR");
|
|
u16 &= ~0x8000;
|
|
}
|
|
|
|
// DONE_CYCLE
|
|
cycle = (u16 & 0x0E00) >> 9;
|
|
printf(" DONE_CYCLE=%s", bitstr(cycle, 3));
|
|
if (!cycle || cycle == 7)
|
|
unexpected_done_cycle = 1;
|
|
u16 &= ~0x0E00;
|
|
|
|
// LCK_CYCLE
|
|
cycle = (u16 & 0x01C0) >> 6;
|
|
printf(" LCK_CYCLE=%s", bitstr(cycle, 3));
|
|
if (!cycle)
|
|
unexpected_lck_cycle = 1;
|
|
u16 &= ~0x01C0;
|
|
|
|
// GTS_CYCLE
|
|
cycle = (u16 & 0x0038) >> 3;
|
|
printf(" GTS_CYCLE=%s", bitstr(cycle, 3));
|
|
u16 &= ~0x0038;
|
|
|
|
// GWE_CYCLE
|
|
cycle = u16 & 0x0007;
|
|
printf(" GWE_CYCLE=%s", bitstr(cycle, 3));
|
|
u16 &= ~0x0007;
|
|
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
if (unexpected_done_cycle)
|
|
printf("#W Unexpected DONE_CYCLE %s.\n",
|
|
bitstr((u16 & 0x01C0) >> 6, 3));
|
|
if (unexpected_lck_cycle)
|
|
printf("#W Unexpected LCK_CYCLE 0b000.\n");
|
|
// Reserved bits 14:12 should be 000
|
|
// according to documentation.
|
|
if (u16)
|
|
printf("#W Expected reserved 0, got 0x%x.\n", u16);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == FAR_MAJ
|
|
&& packet_hdr_wordcount == 2) {
|
|
uint16_t maj, min;
|
|
int unexpected_blk_bit4 = 0;
|
|
|
|
maj = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
min = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+4]);
|
|
printf("T1 FAR_MAJ");
|
|
|
|
// BLK
|
|
u16 = (maj & 0xF000) >> 12;
|
|
printf(" BLK=%u", u16);
|
|
if (u16 > 7)
|
|
unexpected_blk_bit4 = 1;
|
|
// ROW
|
|
u16 = (maj & 0x0F00) >> 8;
|
|
printf(" ROW=%u", u16);
|
|
// MAJOR
|
|
u16 = maj & 0x00FF;
|
|
printf(" MAJOR=%u", u16);
|
|
// Block RAM
|
|
u16 = (min & 0xC000) >> 14;
|
|
printf(" BRAM=%u", u16);
|
|
// MINOR
|
|
u16 = min & 0x03FF;
|
|
printf(" MINOR=%u", u16);
|
|
|
|
if (min & 0x3C00)
|
|
printf(" 0x%x", min & 0x3C00);
|
|
printf("\n");
|
|
|
|
if (unexpected_blk_bit4)
|
|
printf("#W Unexpected BLK bit 4 set.\n");
|
|
// Reserved min bits 13:10 should be 000
|
|
// according to documentation.
|
|
if (min & 0x3C00)
|
|
printf("#W Expected reserved 0, got 0x%x.\n", (min & 0x3C00) > 10);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == MFWR) {
|
|
uint32_t first_dword, second_dword;
|
|
|
|
if (packet_hdr_wordcount != 4) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected "
|
|
"MFWR wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
first_dword = __be32_to_cpu(*(uint32_t*)&bit_data[u16_off+2]);
|
|
second_dword = __be32_to_cpu(*(uint32_t*)&bit_data[u16_off+6]);
|
|
if (first_dword || second_dword) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected "
|
|
"MFWR data 0x%x 0x%x.\n", u16_off,
|
|
u16, first_dword, second_dword);
|
|
goto fail;
|
|
}
|
|
printf("T1 MFWR\n");
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == CTL) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected CTL"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 CTL");
|
|
if (u16 & 0x0040) {
|
|
printf(" DECRYPT");
|
|
u16 &= ~0x0040;
|
|
}
|
|
if (u16 & 0x0020) {
|
|
if (u16 & 0x0010)
|
|
printf(" SBITS=NO_RW");
|
|
else
|
|
printf(" SBITS=NO_READ");
|
|
u16 &= ~0x0030;
|
|
} else if (u16 & 0x0010) {
|
|
printf(" SBITS=ICAP_READ");
|
|
u16 &= ~0x0010;
|
|
}
|
|
if (u16 & 0x0008) {
|
|
printf(" PERSIST");
|
|
u16 &= ~0x0008;
|
|
}
|
|
if (u16 & 0x0004) {
|
|
printf(" USE_EFUSE_KEY");
|
|
u16 &= ~0x0004;
|
|
}
|
|
if (u16 & 0x0002) {
|
|
printf(" CRC_EXTSTAT_DISABLE");
|
|
u16 &= ~0x0002;
|
|
}
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
// bit0 is reserved as 1, and we have seen
|
|
// bit7 on as well.
|
|
if (u16 != 0x81)
|
|
printf("#W Expected reserved 0x%x, got 0x%x.\n", 0x0081, u16);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == MASK) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected MASK"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 MASK");
|
|
if (u16 & 0x0040) {
|
|
printf(" DECRYPT");
|
|
u16 &= ~0x0040;
|
|
}
|
|
if ((u16 & 0x0030) == 0x0030) {
|
|
printf(" SECURITY");
|
|
u16 &= ~0x0030;
|
|
}
|
|
if (u16 & 0x0008) {
|
|
printf(" PERSIST");
|
|
u16 &= ~0x0008;
|
|
}
|
|
if (u16 & 0x0004) {
|
|
printf(" USE_EFUSE_KEY");
|
|
u16 &= ~0x0004;
|
|
}
|
|
if (u16 & 0x0002) {
|
|
printf(" CRC_EXTSTAT_DISABLE");
|
|
u16 &= ~0x0002;
|
|
}
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
// It seems bit7 and bit0 are always masked in.
|
|
if (u16 != 0x81)
|
|
printf("#W Expected reserved 0x%x, got 0x%x.\n", 0x0081, u16);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == PWRDN_REG) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected PWRDN_REG"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 PWRDN_REG");
|
|
if (u16 & 0x4000) {
|
|
printf(" EN_EYES");
|
|
u16 &= ~0x4000;
|
|
}
|
|
if (u16 & 0x0020) {
|
|
printf(" FILTER_B");
|
|
u16 &= ~0x0020;
|
|
}
|
|
if (u16 & 0x0010) {
|
|
printf(" EN_PGSR");
|
|
u16 &= ~0x0010;
|
|
}
|
|
if (u16 & 0x0004) {
|
|
printf(" EN_PWRDN");
|
|
u16 &= ~0x0004;
|
|
}
|
|
if (u16 & 0x0001) {
|
|
printf(" KEEP_SCLK");
|
|
u16 &= ~0x0001;
|
|
}
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
// Reserved bits 13:6 should be 00100010
|
|
// according to documentation.
|
|
if (u16 != 0x0880)
|
|
printf("#W Expected reserved 0x%x, got 0x%x.\n", 0x0880, u16);
|
|
continue;
|
|
}
|
|
if (packet_hdr_register == HC_OPT_REG) {
|
|
if (packet_hdr_wordcount != 1) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected HC_OPT_REG"
|
|
" wordcount %u.\n", u16_off,
|
|
u16, packet_hdr_wordcount);
|
|
goto fail;
|
|
}
|
|
u16 = __be16_to_cpu(*(uint16_t*)&bit_data[u16_off+2]);
|
|
u16_off+=2;
|
|
printf("T1 HC_OPT_REG");
|
|
if (u16 & 0x0040) {
|
|
printf(" INIT_SKIP");
|
|
u16 &= ~0x0040;
|
|
}
|
|
if (u16)
|
|
printf(" 0x%x", u16);
|
|
printf("\n");
|
|
// Reserved bits 5:0 should be 011111
|
|
// according to documentation.
|
|
if (u16 != 0x001F)
|
|
printf("#W Expected reserved 0x%x, got 0x%x.\n", 0x001F, u16);
|
|
continue;
|
|
}
|
|
printf("#W T1 %s (%u words)",
|
|
xc6_regs[packet_hdr_register].name,
|
|
packet_hdr_wordcount);
|
|
for (i = 0; (i < 8) && (i < packet_hdr_wordcount); i++)
|
|
printf(" 0x%x", __be16_to_cpu(*(uint16_t*)
|
|
&bit_data[u16_off+2+i*2]));
|
|
printf("\n");
|
|
continue;
|
|
}
|
|
|
|
// packet type must be 2 here
|
|
if (packet_hdr_wordcount != 0) {
|
|
printf("#W 0x%x=0x%x Unexpected Type 2 "
|
|
"wordcount.\n", u16_off, u16);
|
|
}
|
|
if (packet_hdr_register != FDRI) {
|
|
fprintf(stderr, "#E 0x%x=0x%x Unexpected Type 2 "
|
|
"register.\n", u16_off, u16);
|
|
goto fail;
|
|
}
|
|
if (bit_cur + 4 > bit_eof) goto fail_eof;
|
|
u32 = __be32_to_cpu(*(uint32_t*)&bit_data[bit_cur]);
|
|
bit_cur += 4;
|
|
|
|
printf("T2 FDRI\n");
|
|
if (bit_cur + 2*u32 > bit_eof) goto fail_eof;
|
|
if (!u32)
|
|
printf("#W Unexpected Type2 length 0.\n");
|
|
|
|
// If the data size is a multiple of frame size (1 frame
|
|
// is 130 bytes), then hexdump frame by frame.
|
|
// Total frames on xc6slx4 is 2020. 505 per row (18
|
|
// columns (majors) with varying counts), 4 rows.
|
|
// Then there are an extra 5 frames before data starts (?)
|
|
if ((2*u32) % 130 == 0 || 2*u32 > 2025*130) {
|
|
int max_frames = 2*u32 / 130;
|
|
if (max_frames > 2025)
|
|
max_frames = 2025;
|
|
|
|
for (i = 0; i < max_frames; i++) {
|
|
int first64_all_zero, first64_all_one;
|
|
int last64_all_zero, last64_all_one;
|
|
uint16_t middle_word;
|
|
|
|
middle_word = __be16_to_cpu(*(uint16_t*)
|
|
&bit_data[bit_cur+i*130+64]);
|
|
first64_all_zero = 1; first64_all_one = 1;
|
|
last64_all_zero = 1; last64_all_one = 1;
|
|
|
|
for (j = 0; j < 64; j++) {
|
|
if (bit_data[bit_cur+i*130+j] != 0)
|
|
first64_all_zero = 0;
|
|
if (bit_data[bit_cur+i*130+j] != 0xff)
|
|
first64_all_one = 0;
|
|
if (!first64_all_zero && !first64_all_one)
|
|
break;
|
|
}
|
|
for (j = 66; j < 130; j++) {
|
|
if (bit_data[bit_cur+i*130+j] != 0)
|
|
last64_all_zero = 0;
|
|
if (bit_data[bit_cur+i*130+j] != 0xff)
|
|
last64_all_one = 0;
|
|
if (!last64_all_zero && !last64_all_one)
|
|
break;
|
|
}
|
|
if (first64_all_zero && !middle_word
|
|
&& last64_all_zero)
|
|
printf("frame_130 all_0\n");
|
|
else if (first64_all_one
|
|
&& (middle_word == 0xFFFF) &&
|
|
last64_all_one)
|
|
printf("frame_130 all_1\n");
|
|
else {
|
|
|
|
if (first64_all_zero)
|
|
printf("frame_64 all_0\n");
|
|
else if (first64_all_one)
|
|
printf("frame_64 all_1\n");
|
|
else {
|
|
printf("frame_64");
|
|
for (j = 0; j < 64; j++)
|
|
printf(" %02x", bit_data[bit_cur+i*130+j]);
|
|
printf("\n");
|
|
}
|
|
|
|
printf("frame_2 0x%04x\n", __be16_to_cpu(
|
|
*(uint16_t*) &bit_data[bit_cur+i*130+64]));
|
|
|
|
if (last64_all_zero)
|
|
printf("frame_64 all_0\n");
|
|
else if (last64_all_one)
|
|
printf("frame_64 all_1\n");
|
|
else {
|
|
printf("frame_64");
|
|
for (j = 66; j < 130; j++)
|
|
printf(" %02x", bit_data[bit_cur+i*130+j]);
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
if (2*u32 > 2025*130) {
|
|
int dump_len = 2*u32 - 2025*130;
|
|
printf("#D hexdump offset %xh, len 0x%x (%i)\n",
|
|
bit_cur+2025*130, dump_len, dump_len);
|
|
hexdump(&bit_data[bit_cur+2025*130], dump_len);
|
|
}
|
|
} else {
|
|
printf("#D hexdump offset %xh, len 0x%x (%i)\n",
|
|
bit_cur, 2*u32, 2*u32);
|
|
hexdump(&bit_data[bit_cur], 2*u32);
|
|
}
|
|
bit_cur += u32*2;
|
|
|
|
if (bit_cur + 4 > bit_eof) goto fail_eof;
|
|
u32 = __be32_to_cpu(*(uint32_t*)&bit_data[bit_cur]);
|
|
printf("#W 0x%x=0x%x Ignoring Auto-CRC.\n", bit_cur, u32);
|
|
bit_cur += 4;
|
|
}
|
|
free(bit_data);
|
|
return EXIT_SUCCESS;
|
|
|
|
fail_eof:
|
|
fprintf(stderr, "#E Unexpected EOF.\n");
|
|
fail:
|
|
free(bit_data);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
void help()
|
|
{
|
|
printf("\n"
|
|
"bit2txt %s - convert FPGA bitstream to text\n"
|
|
"(c) 2012 Wolfgang Spraul <wspraul@q-ag.de>\n"
|
|
"\n"
|
|
"bit2txt [options] <path to .bit file>\n"
|
|
" --help print help message\n"
|
|
" --version print version number\n"
|
|
" --info add extra info to output (marked #I)\n"
|
|
" <path to .bit file> bitstream to print on stdout\n"
|
|
"\n", PROGRAM_REVISION);
|
|
}
|