/* http://neil.franklin.ch/Projects/VirtexTools/vd.c */
/*   - VirtexDump, textual dump of LUTs+FFs/IO-FFs/BRAMdatas */
/*     and entire GCLKs/CLBs/IOBs/BRAMs/DLLs from Virtex bitstreams */
/*   presently (Milestone 1) only CLBs (LUTs+FFs and entire CLBs) intended */
/* author Neil Franklin, last modification 2002.09.22 */


/* ------ prerequisites section */

/* to really understand this code you should */
/*   have an understanding of the basics of */
/*     designing logic circuits, such as with [74|74LS|74F]xx(x) chips */
/*     basics of how programmable logic works, particularly FPGAs */
/*   have read the specifics from the */
/*     Xilinx Virtex 2.5V FPGAs (XCV00) data sheet */
/*       http://www.xilinx.com/partinfo/ds003.pdf */
/*     Xilinx Virtex-E 1.8V FPGAs (XCV00E) data sheet */
/*       http://www.xilinx.com/partinfo/ds022.pdf */
/*     Xilinx Virtex-E 1.8V Extended Memory FPGAs (XCV00E) data sheet */
/*       http://www.xilinx.com/partinfo/ds025.pdf */
/*     Xilinx Spartan[tm]-II data sheet (XC2S00) */
/*       http://www.xilinx.com/partinfo/ds001.htm */
/*     Xilinx Spartan[tm]-IIE data sheet (XC2S00E) */
/*       http://www.xilinx.com/partinfo/ds077.htm */
/*     Xilinx Application Note XAPP138, Virtex Configuration and Readback */
/*       http://www.xilinx.com/xapp/xapp138.pdf */
/*     Xilinx XAPP151, Virtex Series Configuration Architecture User Guide */
/*       http://www.xilinx.com/xapp/xapp151.pdf */


/* ------ C includes section */

/* the usual suspects, may not be actually used */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>


/* ------ our own "include" section */

/* at present no .h file, more work to download 2 files as no .tar file yet */
/* this will most likely disappear when I start with libvirtex and virtex.h */

/* my typical typing savers, oops some are already in Linux types.h */
typedef unsigned char uchar;
/*typedef unsigned short ushort;*/
/*typedef unsigned int uint;*/
/*typedef unsigned long ulong;*/

/* prototypes */
int main(int argc, char *argv[], char *env[]);

void getoptions(char *argv[]);
void helpoptions(int error);

void readbitfile(void);
char *readfileheader(void);
char *readstreamheader(void);
char *setmodelfromconfigsize(void);
char *allocframes(void);
char *readmainframes(void);

void dumpoutfile(void);
void setclbrange(void);

void dumpmemoryzzclbs(void);
void dumpmemoryzzclbslis(int Col0, int Sli0, int Col1, int Sli1);
void dumpmemoryzzclb(int Col0, int Sli0, int Col1, int Sli1, int Row,
  int MemWidth, int MemBit);

void dumpmemoryclbs(void);
void dumpmemoryclbslis(int Col, int Sli);
void dumpmemoryclb(int Col, int Sli, int Row, int Lut,
  int MemWidth, int MemBit);

void formatmemory(int MemWidth, char *MemWord[], int Words,
  char *HeaderLine[], int Lines);

void dumpentireclbs(void);
void dumpentireclb(int Col, int Row);

void dumpclbs(void);
void dumpclbslis(int Col, int Sli);
void dumpclb(int Col, int Sli, int Row, int Lut);

/* constants for selecting slices and LUTs in dump*() */
/* slice 1 is left(!) in the CLB, see XAPP page 11, table 7, formula MNA */
#define SLI0 0
#define SLI1 1
#define SLIL SLI1
#define SLIR SLI0
#define LUTF 0
#define LUTG 1


/* ------ define the Virtex series section */

/* assumptions on series differences: */
/* Virtex: JBits makes code for these, so my reverse engineering is for these
   will be supported from beginning on */
/* Virtex-E: XAPP151 documented bit positions (LUTs, FFs, IO-FFs, BRAMs)
   are identical, bitmap sizes also, different BRAM arrangement is in XAPP151
   will be supported when someone wants the extra code
   "entire mode" data sheet description of CLBs and BRAMs looks same
   DLLs and IOBs can do more, need more PIPs?, added or changed PIPs?
   will be supported after I can get an bitstream to cross compare */
/* Virtex-ME: most of the data sheet is verbatim Virtex-E
   seems to be just more memory, no other change
   XAPP151 documents differences in BRAM arrangement, more like normal Virtex
   will be supported as soon as Virtex-E is supported */
/* Sparten-II: are equivalent-size compatible with Virtex, according to Xilinx
   will be supported from beginning on, as just more sizes */
/* Sparten-IIE: seems to be Virtex-E but with only 2 BRAM columns
   will be supported tentatively when Virtex-E is in
   will be definitive only after I get an bitstream to cross compare */
/* Virtex-II and Virtex-IIpro: totally different architecture, no JBits for it
   and I have no BGA processing ability anyway, no support intended at all */

enum VirtexSeries {
  none       = 0,
  virtex     = 1,
  virtex_e   = 2,
  virtex_em  = 3,
  spartan_2  = 4,
  spartan_2e = 5 };


/* ------ define various Virtex chip architecture constants section */

/* horizontal arrangement */

/* these constants are from the data sheets */
#define VirtexSlicesPerClb    2
#define VirtexIosPerIobNS     2

/* these constants are from the data sheets */
#define VirtexCenterColumns   1
#define VirtexGclkColumns     VirtexCenterColumns
/*      VirtexClbColumns      is model dependant from VirtexModels */
/*      VirtexIobNSColumns    is model dependant = VirtexClbColumns */
#define VirtexIobWEColumns    2
#define VirtexPadIobWEColumns VirtexIobWEColumns
/*      VirtexBramColumns     is model dependant from VirtexModels */
/*      VirtexDllColumns      is model dependant = VirtexBramColumns */

/* these constants are from XAPP151, page7 */
#define VirtexCenterFrames    8
#define VirtexGclkFrames      VirtexCenterFrames
#define VirtexClbFrames       48
#define VirtexIobNSFrames     VirtexClbFrames
#define VirtexIobWEFrames     54
#define VirtexPadIobWEFrames  VirtexIobWEFrames
#define VirtexBramCFrames     27
#define VirtexBramDFrames     64
#define VirtexBramFrames      (VirtexBramCFrames+VirtexBramDFrames)
#define VirtexDllFrames       VirtexBramFrames


/* vertical arrangement */

/* these constants are from the data sheets */
#define VirtexLutsPerSlice    2
#define VirtexIosPerIobWE     3
#define VirtexClbsPerBram     4

/* these constants are from the data sheets */
/*      VirtexMiddleRows      is model dependant from VirtexModels */
#define VirtexTopBotRows      2

/* these constants are from XAPP151, page7 */
#define VirtexMiddleBits      18
#define VirtexTopBotBits      18


/* ------ define various Virtex chip model dependant constants section */

/* chip model dependant constants, so they are not scattered over code */
struct VirtexModel {

  /* given data, set here */
  char* Name;
  enum VirtexSeries Series;

  int ClbColumns;
  int ClbRows;
  int BramColumns;

  int BramSpacing;

  /* computed data, calculated later */
  int IobNSColumns;
  int DllColumns;
  /* MiddleRows not given, because ClbRows given for easier understanding */
  int MiddleRows;

  int FrameBits;
  int FrameWords;
  int MainFrames; };

/* declaration of actual devices, this data is from the data sheets */
struct VirtexModel VirtexModels[] = {

  { "XCV50",    virtex,     24,  16,  2,  0 },
  { "XCV100",   virtex,     30,  20,  2,  0 },
  { "XCV150",   virtex,     36,  24,  2,  0 },
  { "XCV200",   virtex,     42,  28,  2,  0 },
  { "XCV300",   virtex,     48,  32,  2,  0 },
  { "XCV400",   virtex,     60,  40,  2,  0 },
  { "XCV600",   virtex,     72,  48,  2,  0 },
  { "XCV800",   virtex,     84,  56,  2,  0 },
  { "XCV1000",  virtex,     96,  64,  2,  0 },

#ifdef RISK_BROKEN_CHIP
  { "XCV50E",   virtex_e,   24,  16,  4,  6 },
  { "XCV100E",  virtex_e,   30,  20,  4, 12 },
  { "XCV200E",  virtex_e,   42,  28,  4, 12 },
  { "XCV300E",  virtex_e,   48,  32,  4, 12 },
  { "XCV400E",  virtex_e,   60,  40,  4, 12 },
  { "XCV600E",  virtex_e,   72,  48,  6, 12 },
  { "XCV1000E", virtex_e,   96,  64,  6, 12 },
  { "XCV1600E", virtex_e,  108,  72,  8, 12 },
  { "XCV2000E", virtex_e,  120,  80,  8, 12 },
  { "XCV2600E", virtex_e,  138,  92,  8, 12 },
  { "XCV3200E", virtex_e,  156, 104,  8, 12 },

  { "XCV405E",  virtex_em,  60,  40, 14,  4 },
  { "XCV812E",  virtex_em,  84,  56, 20,  4 },
#endif

  { "XC2S15",   spartan_2,  12,   8,  2,  0 },
  { "XC2S30",   spartan_2,  18,  12,  2,  0 },
  { "XC2S50",   spartan_2,  24,  16,  2,  0 },
  { "XC2S100",  spartan_2,  30,  20,  2,  0 },
  { "XC2S150",  spartan_2,  36,  24,  2,  0 },
  { "XC2S200",  spartan_2,  42,  28,  2,  0 },

#ifdef RISK_BROKEN_CHIP
  { "XC2S50E",  spartan_2e, 24,  16,  2,  0 },
  { "XC2S100E", spartan_2e, 30,  20,  2,  0 },
  { "XC2S150E", spartan_2e, 36,  24,  2,  0 },
  { "XC2S200E", spartan_2e, 42,  28,  2,  0 },
  { "XC2S300E", spartan_2e, 48,  32,  2,  0 },
#endif

  { NULL,       none,        0,   0,  0,  0 } };


/* ------ main program control section */

int main(int argc, char *argv[], char *env[]) {

  /* straight forward, really */
  getoptions(argv);
  readbitfile();
  dumpoutfile();

  exit(0); }


/* ------ get command line options section */

char *OptProgName;

#define OptLongFormat 0
#define OptShortFormat 1
#define OptQuietFormat 2
int OptFormat = OptLongFormat;

#define OptWithFf 0
#define OptNoFf 1
int OptFf = OptWithFf;

#define OptWithLut 0
#define OptNoLut 1
int OptLut = OptWithLut;

#define OptNoEntire 0
#define OptDoEntire 1
int OptEntire = OptNoEntire;

#define OptNoMemory 0
#define OptDoMemory 1
int OptMemory = OptNoMemory;

#define OptNoZigzag 0
#define OptDoZigzag 1
int OptZigzag = OptNoZigzag;

#define OptNormalOperation 0
#define OptVerboseOperation 1
int OptOperation = OptNormalOperation;

/* NULL for no range given, use entire chip */
char *OptRange = NULL;

/* NULL for no base given, use binary */
char *OptMemoryBase = NULL;

/* NULL for no file given, use stdout */
char *OptOutfileName = NULL;

/* NULL for no file given, use stdin */
char *OptBitfileName = NULL;

void getoptions(char *argv[]) {

  OptProgName = *argv++;

  while (*argv != NULL && (*argv)[0] == '-') {
    /* it is a bunch options, modify fitting Opt* variables */

    char *option;
    /* can be -xyz multiple options */
    for (option = (*argv)+1; *option != 0; option++) {

      if (*option == 's') {
        /* -s (short format) */
        OptFormat = OptShortFormat; }

      else if (*option == 'q') {
        /* -q (quiet format) */
        OptFormat = OptQuietFormat; }

      else if (*option == 'l') {
        /* -l (luts only) */
        OptFf = OptNoFf; }

      else if (*option == 'f') {
        /* -f (flipflops only) */
        OptLut = OptNoLut; }

      else if (*option == 'e') {
        /* -e (entire bits) */
        OptEntire = OptDoEntire; }

      else if (*option == 'm') {
        /* -m (memory output) */
        OptMemory = OptDoMemory; }

      else if (*option == 'z') {
        /* -z (zigzag memory) */
        OptZigzag = OptDoZigzag; }

      else if (*option == 'v') {
        /* -v (verbose operation) */
        OptOperation = OptVerboseOperation; }

      else if (*option == 'r') {
        /* -r (range): next argument must be range description, take it  */
        argv++;
        OptRange = *argv; }

      else if (*option == 't') {
        /* -t (type): next argument must be base description, take it  */
        argv++;
        OptMemoryBase = *argv; }

      else if (*option == 'o') {
        /* -o (output set): next argument must be outfile name, take it */
        argv++;
        OptOutfileName = *argv; }

      else if (*option == 'h') {
        /* -h (help): output help option text, and abort then */
        helpoptions(0); }

      else {
        /* unknown option given, give help and abort with error */
        fprintf(stderr, "%s: Error: unknown option: -%c\n\n",
          OptProgName, *option);
        helpoptions(1); } }

    argv++; }

  if (*argv != NULL) {
    /* starting with non-digit, must be an bit file name */
    OptBitfileName = *argv++; }

  if (*argv != NULL) {
    /* there should be no params any more, give help and abort with error */
    fprintf(stderr, "%s: Error: too many parameters at: %s\n\n",
      OptProgName, *argv);
    helpoptions(1); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "*** %s: running in verbose mode ***\n", OptProgName); }

  return; }


/* ------ output help options text section */

void helpoptions(int error) {

  FILE *Output;

  if (error) {
    Output = stderr; }
  else {
    Output = stdout; }

  fprintf(Output,
    "Usage: %s [-sqlfemzvh] [-r range] [-t base[byte]] [-o file] [bitfile]\n",
    OptProgName);
  fprintf(Output, "\n");
  fprintf(Output, "Options:\n");
  fprintf(Output,
    "  -s            short format: data+address, no headers/labels\n");
  fprintf(Output,
    "  -q            quiet format: just data, no head/label/address\n");
  fprintf(Output,
    "                (default is data with headers/labels/addresses)\n");
  fprintf(Output,
    "  -l            only LUTs: dump only LUTs, suppress FFs (def both)\n");
  fprintf(Output,
    "  -f            only flipflops: dump only FFs, suppress LUTs\n");
  fprintf(Output,
    "  -e            entire elment: CLB as bit pattern, not just LUTs+FFs\n");
  fprintf(Output,
    "  -m            memory: LUTs as LUT-RAMs, rows merged and rotated\n");
  fprintf(Output,
    "  -z            zigzag: LUT-RAM32s bit0/2/.. first sli, 1/3/.. second\n");
  fprintf(Output,
    "  -v            verbose operation: running activity report to stderr\n");
  fprintf(Output,
    "  -h            help: display this help text, then abort\n");
  fprintf(Output,
    "  -r spec       restrict range to only: [horizontal][/vertical]\n");
  fprintf(Output,
    "                horizontal: [left][-[right]], with left<right\n");
  fprintf(Output,
    "                vertical: [bottom][-[top]], with bottom<top\n");
  fprintf(Output,
    "                left, right: column[slice];  bottom, top: row[LUT]\n");
  fprintf(Output,
    "                column, row: number 0 to size-1 (from left or bottom)\n");
  fprintf(Output,
    "                slice: L (left=1) or R (right=0);  LUT: F or G\n");
  fprintf(Output,
    "  -t base[byte] type: of base of memory output (default bI)\n");
  fprintf(Output,
    "                base: a ascii, b binary, d decimal, o octal, h hex\n");
  fprintf(Output,
    "                byte: width number 1 to n or I=word, C=8, S=16, L=32\n");
  fprintf(Output,
    "  -o file       output: send to file (default stdout)\n");
  fprintf(Output, "\n");
  fprintf(Output,
    "bitfile:        read bitstream from this file (default stdin)\n");

  /* and abort, do not run actual program, neither after -h nor after error */
  exit(error); }


/* ------ read bits from bit file section */

/* expects: OptBitfileName set to filename or NULL for stdin */
/* leaves: Bitfile opened, read in, and closed */

FILE *Bitfile;

void readbitfile(void) {

  char *serror;
  int ierror;

  if (OptBitfileName) {
    if (OptOperation == OptVerboseOperation) {
      fprintf(stderr, "reading from bitfile: %s\n", OptBitfileName); }
    /* we open with "b" for binary, because else some systems do CRLF->LF */
    Bitfile = fopen(OptBitfileName, "rb");
    if (!Bitfile) {
      fprintf(stderr, "%s: %s\n", strerror(errno), OptBitfileName);
      exit(1); } }
  else {
    if (OptOperation == OptVerboseOperation) {
      fprintf(stderr, "reading from standard input\n"); }
    Bitfile = stdin; }

  /* read the various sections for the bitfile */
  serror = readfileheader();
  if (serror) {
    fclose(Bitfile);
    fprintf(stderr, "%s\n", serror);
    exit(1); }
  serror = readstreamheader();
  if (serror) {
    fclose(Bitfile);
    fprintf(stderr, "%s\n", serror);
    exit(1); }
  serror = setmodelfromconfigsize();
  if (serror) {
    fclose(Bitfile);
    fprintf(stderr, "%s\n", serror);
    exit(1); }
  serror = allocframes();
  if (serror) {
    fclose(Bitfile);
    fprintf(stderr, "%s\n", serror);
    exit(1); }
  serror = readmainframes();
  if (serror) {
    fclose(Bitfile);
    fprintf(stderr, "%s\n", serror);
    exit(1); }
  /* at present we only read until the main frames are in */
  /*   ignore BRAM data frames and bitstream end */
  /*   we will need these when we start processing BRAMs and for writing */

  ierror = fclose(Bitfile);
  if (ierror) {
    fprintf(stderr, "%s: %s\n", strerror(errno), OptBitfileName);
    exit(1); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "finished reading\n"); }

  return; }


/* ------ read .bit file header section */

/* expects: open file, file pointer at nothing yet read */
/* leaves: file header read, file pointer ready to read bitstream */

/* we test here really picky, to make absolutely sure we are processing */
/* and real FPGA .bit file, to prevent random bits ending up in an FPGA */
/* which would destroy the chip due to internal shorted circuits! */

/* format from .../comp.arch.fpga/20010828_download_bitstream_to_FPGA */
/*   corrected error 2->4 bytes for field 0x65 */
/* the example data is from the JBits null300.bit file */

ushort FileheaderLength; /* 0x0009 */
ulong  FileheaderLong1;  /* 0x0ff00ff0 */
ulong  FileheaderLong2;  /* 0x0ff00ff0 */
uchar  FileheaderZero;   /* 0x00 */

ushort KeyLength;        /* 0x0001 */

uchar  DesignnameKey;    /* 0x61 */
ushort DesignnameLength; /* 0x000c */
char   *Designname;      /* "null300.ncd" */

uchar  PartnameKey;      /* 0x62 */
ushort PartnameLength;   /* 0x000a */
char   *Partname;        /* "v300BG352" */

uchar  GendateKey;       /* 0x63 */
ushort GendateLength;    /* 0x000a */
char   *Gendate;         /* "2001/3/23" */

uchar  GentimeKey;       /* 0x64 */
ushort GentimeLength;    /* 0x0009 */
char   *Gentime;         /* "11:43:57" */

uchar  BitstreamKey;     /* 0x65 */
ulong  BitstreamLength;  /* 0x00035760 */


char *readfileheader(void) {

  if (!Bitfile) {
    fprintf(stderr, "BUGCHECK readfileheader() FATAL: no file to read\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "reading bitfile header\n"); }

  fread(&FileheaderLength, sizeof(short), 1, Bitfile);
  /* the bitfile is big-endian, just like network, so we can use ntoh*() */
  FileheaderLength = ntohs(FileheaderLength);
  if (FileheaderLength != 9) {
    return ("expected file header length not 9, not a bitstream file"); }
  fread(&FileheaderLong1, sizeof(ulong), 1, Bitfile);
  FileheaderLong1 = ntohl(FileheaderLong1);
  fread(&FileheaderLong2, sizeof(ulong), 1, Bitfile);
  FileheaderLong2 = ntohl(FileheaderLong2);
  fread(&FileheaderZero, sizeof(uchar), 1, Bitfile);
  if (FileheaderLong1 != 0x0FF00FF0 || FileheaderLong2 != 0x0FF00FF0 ||
      FileheaderZero != 0x00) {
    return ("expected file header not 4 0FF0 plus 00, not a bitstream file"); }

  fread(&KeyLength, sizeof(ushort), 1, Bitfile);
  KeyLength = ntohs(KeyLength);
  if (KeyLength != 1) {
    return ("key length not 1, not a bitstream file"); }

  fread(&DesignnameKey, sizeof(uchar), 1, Bitfile);
  if (DesignnameKey != 0x61) {
    return ("expected design name key missing, not a bitstream file"); }
  fread(&DesignnameLength, sizeof(ushort), 1, Bitfile);
  DesignnameLength = ntohs(DesignnameLength);
  if (DesignnameLength == 0) {
    return ("expected design name length nonzero, not a bitstream file"); }
  Designname = (char *)malloc(DesignnameLength);
  fread(Designname, sizeof(char), DesignnameLength, Bitfile);

  fread(&PartnameKey, sizeof(uchar), 1, Bitfile);
  if (PartnameKey != 0x62) {
    return ("expected part name key missing, not a bitstream file"); }
  fread(&PartnameLength, sizeof(ushort), 1, Bitfile);
  PartnameLength = ntohs(PartnameLength);
  if (PartnameLength == 0) {
    return ("expected part name length nonzero, not a bitstream file"); }
  Partname = (char *)malloc(PartnameLength);
  fread(Partname, sizeof(char), PartnameLength, Bitfile);

  fread(&GendateKey, sizeof(uchar), 1, Bitfile);
  if (GendateKey != 0x63) {
    return ("expected generation date key missing, not a bitstream file"); }
  fread(&GendateLength, sizeof(ushort), 1, Bitfile);
  GendateLength = ntohs(GendateLength);
  if (GendateLength == 0) {
    return ("expected generation date length nonzero, not a bitstream file"); }
  Gendate = (char *)malloc(GendateLength);
  fread(Gendate, sizeof(char), GendateLength, Bitfile);

  fread(&GentimeKey, sizeof(uchar), 1, Bitfile);
  if (GentimeKey != 0x64) {
    return ("expected generation time key missing, not a bitstream file"); }
  fread(&GentimeLength, sizeof(ushort), 1, Bitfile);
  GentimeLength = ntohs(GentimeLength);
  if (GentimeLength == 0) {
    return ("expected generation time length nonzero, not a bitstream file"); }
  Gentime = (char *)malloc(GentimeLength);
  fread(Gentime, sizeof(char), GentimeLength, Bitfile);

  fread(&BitstreamKey, sizeof(uchar), 1, Bitfile);
  if (BitstreamKey != 0x65) {
    return ("expected bitstream key missing, not a bitstream file"); }
  fread(&BitstreamLength, sizeof(ulong), 1, Bitfile);
  BitstreamLength = ntohl(BitstreamLength);
  if (BitstreamLength == 0) {
    return ("expected bitstream length nonzero, not a bitstream file"); }

  return(NULL); }


/* ------ read bitstream header section */

/* expects: header read, file pointer ready to read bitstream */
/* leaves: bitstream header read, file pointer ready to read frames */

/* still being really picky, to make absolutely sure we are processing */
/* an standard bitstream, we assume this is .bit file, but format may change */

/* format from XAPP138, pages 20-22 */

ulong StreamDummy;       /* 0xFFFFFFFF, so that sync contains first non-ones */
ulong StreamSync;        /* 0xAA995566, this triggers interpretation */

ulong StreamRcrcCmd;     /* 0x30008001, command register */
ulong StreamRcrc;        /* 0x00000007, reset CRC counter */

ulong StreamFlenReg;     /* 0x30016001, frame length, with fill, wo pipeline */
ulong StreamFlen;        /* 0x000000??, XV300 example 0x00000014 */

ulong StreamConfoptReg;  /* 0x30012001, configuration options */
ulong StreamConfopt;     /* 0x????????, JBits uses 0x00803f2d */

ulong StreamMaskReg;     /* 0x3000C001, control register mask */
ulong StreamMask;        /* 0x00000000, JBits uses 0x000001c6 */

ulong StreamSwitchCmd;   /* 0x30008001, command register */
ulong StreamSwitch;      /* 0x00000009, switch Cclk freq */

ulong StreamFaddrReg;    /* 0x30002001, frame address */
ulong StreamFaddr;       /* 0x00000000, from the beginning */

ulong StreamWriteCmd;    /* 0x30008001, command register */
ulong StreamWrite;       /* 0x00000001, write config data */

ulong StreamFdatinReg;   /* 0x30004000, frame data input */
ulong StreamFdatinLen;   /* 0x5???????, XV300 example 0x5000cb07 */


char *readstreamheader(void) {

  if (!Bitfile) {
    fprintf(stderr, "BUGCHECK readstreamheader() FATAL: no file to read\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "reading bitstream header\n"); }

  fread(&StreamDummy, sizeof(ulong), 1, Bitfile);
  StreamDummy = ntohl(StreamDummy);
  if (StreamDummy != 0xFFFFFFFF) {
    return ("expected stream dummy missing, not a standard bitstream"); }
  fread(&StreamSync, sizeof(ulong), 1, Bitfile);
  StreamSync = ntohl(StreamSync);
  if (StreamSync != 0xAA995566) {
    return ("expected stream synchron missing, not a standard bitstream"); }

  fread(&StreamRcrcCmd, sizeof(ulong), 1, Bitfile);
  StreamRcrcCmd = ntohl(StreamRcrcCmd);
  fread(&StreamRcrc, sizeof(ulong), 1, Bitfile);
  StreamRcrc = ntohl(StreamRcrc);
  if (StreamRcrcCmd != 0x30008001 || StreamRcrc != 0x00000007) {
    return ("expected stream reset CRC missing, not a standard bitstream"); }

  fread(&StreamFlenReg, sizeof(ulong), 1, Bitfile);
  StreamFlenReg = ntohl(StreamFlenReg);
  fread(&StreamFlen, sizeof(ulong), 1, Bitfile);
  StreamFlen = ntohl(StreamFlen);
  if (StreamFlenReg != 0x30016001 || StreamFlen == 0x00000000) {
    return ("expected stream frame len nonzero, not a standard bitstream"); }

  fread(&StreamConfoptReg, sizeof(ulong), 1, Bitfile);
  StreamConfoptReg = ntohl(StreamConfoptReg);
  fread(&StreamConfopt, sizeof(ulong), 1, Bitfile);
  StreamConfopt = ntohl(StreamConfopt);
  if (StreamConfoptReg != 0x30012001) {
    return ("expected stream config opts missing, not a standard bitstream"); }

  fread(&StreamMaskReg, sizeof(ulong), 1, Bitfile);
  StreamMaskReg = ntohl(StreamMaskReg);
  fread(&StreamMask, sizeof(ulong), 1, Bitfile);
  StreamMask = ntohl(StreamMask);
  if (StreamMaskReg != 0x3000C001) {
    return ("expected stream ctrl mask missing, not a standard bitstream"); }

  fread(&StreamSwitchCmd, sizeof(ulong), 1, Bitfile);
  StreamSwitchCmd = ntohl(StreamSwitchCmd);
  fread(&StreamSwitch, sizeof(ulong), 1, Bitfile);
  StreamSwitch = ntohl(StreamSwitch);
  if (StreamSwitchCmd != 0x30008001 || StreamSwitch != 0x00000009) {
    return ("expected stream clock freq missing, not a standard bitstream"); }

  fread(&StreamFaddrReg, sizeof(ulong), 1, Bitfile);
  StreamFaddrReg = ntohl(StreamFaddrReg);
  fread(&StreamFaddr, sizeof(ulong), 1, Bitfile);
  StreamFaddr = ntohl(StreamFaddr);
  if (StreamFaddrReg != 0x30002001 || StreamFaddr != 0x00000000) {
    return ("expected stream frame addr zero, not a standard bitstream"); }

  fread(&StreamWriteCmd, sizeof(ulong), 1, Bitfile);
  StreamWriteCmd = ntohl(StreamWriteCmd);
  fread(&StreamWrite, sizeof(ulong), 1, Bitfile);
  StreamWrite = ntohl(StreamWrite);
  if (StreamWriteCmd != 0x30008001 || StreamWrite != 0x00000001) {
    return ("expected stream write cmd missing, not a standard bitstream"); }

  fread(&StreamFdatinReg, sizeof(ulong), 1, Bitfile);
  StreamFdatinReg = ntohl(StreamFdatinReg);
  fread(&StreamFdatinLen, sizeof(ulong), 1, Bitfile);
  StreamFdatinLen = ntohl(StreamFdatinLen);
  if (StreamFdatinReg != 0x30004000 || StreamFdatinLen == 0x00000000) {
    return ("expected stream data input nonzero, not a standard bitstream"); }

  return (NULL); }


/* ------ set Virtex model from config size section */

/* expects: StreamFdatinLen set so that we can find fitting Model */
/* leaves: Model set pointing to model dependant constants */

/* format from XAPP138, page 22, last row in table */

/* the actual device we are using for selecting parameters */
struct VirtexModel *Model;


char *setmodelfromconfigsize(void) {

  /* size of main config section identifies model */
  long StreamWords;

  if (!StreamFdatinLen) {
    fprintf(stderr, "BUGCHECK setmodelfromconfigsize() FATAL: no d length\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "determining model being used\n"); }

  /* extract length from type 2 packet header, see XAPP138, page 21 */
  StreamWords = StreamFdatinLen & 0x07FFFFFF;

  for (Model = VirtexModels; Model->Name != NULL; Model++) {

    long ModelWords;

    /* fill out this models computed data */
    Model->IobNSColumns   = Model->ClbColumns;
    Model->DllColumns     = Model->BramColumns;
    Model->MiddleRows     = Model->ClbRows;

    /* calculate main section size for this model */

    /* these formulas are from XAPP151, page 5 */
    Model->FrameBits = Model->MiddleRows*VirtexMiddleBits+
                       VirtexTopBotRows*VirtexTopBotBits;
    /* round up to next number dividable by 32, then divide, add 1 pipeline */
    Model->FrameWords = (((Model->FrameBits+31)&0xFFFFFFE0)/32)+1;

    /* these formulas are from XAPP151, pages 3 - 5 */
    Model->MainFrames = VirtexCenterColumns*VirtexCenterFrames+
                        Model->ClbColumns*VirtexClbFrames+
                        VirtexIobWEColumns*VirtexIobWEFrames+
                        /* BRAM data not in main frames, see XAPP151, page 3 */
                        /* so here use BramCFrames, not BramFrames */
                        Model->BramColumns*VirtexBramCFrames+
                        /* and the pipelining frame */
                        1;

    ModelWords = Model->MainFrames*Model->FrameWords;

    if (ModelWords == StreamWords) {
      /* we have found right model, dont search further */
      break; } }

  /* we ran out of models to test */
  if (Model->Name == NULL) {
    return ("stream word count doesn't fit a known model, not a bitstream"); }

  return (NULL); }


/* ------ allocate memory for bitstream frame data section */

/* expects: Model set so that model dependant constants are known */
/*   also tests for not already allocated, to prevent memory leak */
/* leaves: memory ready for reading in frames data */

/* format from XAPP151, pages 3-5 */

/* data will be stored using one "ulong" for each frame of an CLB/etc */
/*   placed as Bitmap*[0..columns-1][0..rows-1][0..frames-1] */
/*   so that all frames of one column/row position are together */
/*   and all rows of one column are together */
/* C does not allow variable size arrays, so use heap memory and pointers */
/*   will require explicit pointer arithmetic everywhere */
ulong *BitmapCenter, *BitmapGclk;
ulong *BitmapClb,    *BitmapIobNS;
ulong *BitmapIobWE,  *BitmapPadIobWE;
ulong *BitmapBram,   *BitmapDll;


char *allocframes(void) {

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK allocframes() FATAL: model has not been set\n");
    abort(); }

  if (BitmapCenter || BitmapGclk     || BitmapClb  || BitmapIobNS ||
      BitmapIobWE  || BitmapPadIobWE || BitmapBram || BitmapDll) {
    fprintf(stderr, "BUGCHECK allocframes() FATAL: mem already allocated\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "readying memory to store config data\n"); }

  BitmapCenter = (ulong *)calloc(VirtexCenterColumns*
                                 Model->MiddleRows*
                                 VirtexCenterFrames, sizeof(ulong));
  if (!BitmapCenter) {
    return ("failled to allocate memory for Center bits"); }

  BitmapGclk = (ulong *)calloc(VirtexGclkColumns*
                               VirtexTopBotRows*
                               VirtexGclkFrames, sizeof(ulong));
  if (!BitmapGclk) {
    return ("failled to allocate memory for GCLK bits"); }


  BitmapClb = (ulong *)calloc(Model->ClbColumns*
                              Model->MiddleRows*
                              VirtexClbFrames, sizeof(ulong));
  if (!BitmapClb) {
    return ("failled to allocate memory for CLB bits"); }

  BitmapIobNS = (ulong *)calloc(Model->IobNSColumns*
                                VirtexTopBotRows*
                                VirtexIobNSFrames, sizeof(ulong));
  if (!BitmapIobNS) {
    return ("failled to allocate memory for IOB N/S bits"); }


  BitmapIobWE = (ulong *)calloc(VirtexIobWEColumns*
                                Model->MiddleRows*
                                VirtexIobWEFrames, sizeof(ulong));
  if (!BitmapIobWE) {
    return ("failled to allocate memory for IOB W/E bits"); }

  BitmapPadIobWE = (ulong *)calloc(VirtexPadIobWEColumns*
                                   VirtexTopBotRows*
                                   VirtexPadIobWEFrames, sizeof(ulong));
  if (!BitmapPadIobWE) {
    return ("failled to allocate memory for IOB W/E padding bits"); }


  /* for consistency with other rows and fitting into array of longs */
  /*   BRAMs 72 bits/frame are stored split as 4 quaters of 18 bits/frame */
  /*   as rows are direct after each other, these quaters follow each other */
  /*     so that gives 27*18+64*18+27*18+64*18+27*18+64*18+27*18+64*18 bits */
  /*     so then use BRAM row no * 4 as base and then frame indexes 0..363 */
  /*   for output then correctly as 4*((27+64)*18) underneath each other */

  /* space is allocated for BRAM control and BRAM data frames in one go */
  BitmapBram = (ulong *)calloc(Model->BramColumns*
                               Model->MiddleRows*
                               VirtexBramFrames, sizeof(ulong));
  if (!BitmapBram) {
    return ("failled to allocate memory for BRAM bits"); }

  /* like for BRAMs space for DLLs and the padding above BRAM data in one go */
  /*   BRAM data padding has no effect on DLL indexing, as it is after it */
  BitmapDll = (ulong *)calloc(Model->DllColumns*
                              VirtexTopBotRows*
                              VirtexDllFrames, sizeof(ulong));
  if (!BitmapDll) {
    return ("failled to allocate memory for DLL bits"); }


  return (NULL); }


/* ------ read bitstream main frame data section */

/* expects: bitstream header read, file pointer ready to read frames */
/*   and also Model set so that model dependant constants are known */
/* leaves: bitstream main frames data read, file pointer ready for BRAMd */

/* by here we must assume this to be a valid bitstream, trust rest of bits */
/* as we have no ability to do any syntactic checks any more, Banzaaaaii!!! */

/* format from XAPP151, pages 3-5 */

/* Xilinx thinks big endian, so we use the first/top/big/MSB 18 bits */
/*   mask for extracting top 18 bits from 32 */
#define BitsInUlong 32
#define BitsMask    0xFFFFC000


char *readmainframes(void) {

  /* for stepping through frames */
  int Frame;
  /* temporary space for one frame from file, then extract to Bitmap* arrays */
  ulong *FileFrame;

  if (!Bitfile) {
    fprintf(stderr, "BUGCHECK readmainframes() FATAL: no file to read\n");
    abort(); }

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK readmainframes() FATAL: model not been set\n");
    abort(); }

  if (!(BitmapCenter && BitmapGclk     && BitmapClb  && BitmapIobNS &&
        BitmapIobWE  && BitmapPadIobWE && BitmapBram && BitmapDll)) {
    fprintf(stderr, "BUGCHECK readmainframes() FATAL: no memory for frame\n");
    abort(); }

  FileFrame = (ulong *)calloc(Model->FrameWords, sizeof(ulong));
  if (!FileFrame) {
    return ("failled to allocate temporary memory for frame from file"); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "reading main config frames\n"); }


  for (Frame = 0; Frame < Model->MainFrames; Frame++) {

    /* running through frame from bitstream */
    ulong *Bits;

    /* running through target Bitmap*s */
    ulong *Middle, *TopBot;
    int StepMiddle, StepTopBot;
    ulong *FirstMiddle, *FirstTopBot;

    /* controlling when to jump Bitmap* and deinterleave in one Bitmap* */
    int FirstClbFrame, FirstIobWEFrame, FirstBramCFrame;
    int NextClbColFrame, NextIobWEColFrame, NextBramCColFrame;

    /* taking frame appart for putting into target words */
    int BitsLeft, Row;


    /* fetch an frame and get it ready to be taken apart */

    fread(FileFrame, sizeof(ulong), Model->FrameWords, Bitfile);
    for (Bits = FileFrame; Bits < FileFrame+Model->FrameWords; Bits++) {
      *Bits = ntohl(*Bits); }

    /* check if pipeline frame empty, don't store, so break the for () loop */
    if (Frame == Model->MainFrames-1) {
      for (Bits = FileFrame; Bits < FileFrame+Model->FrameWords; Bits++){
        if (*Bits != 0) {
          return ("Pipelining Frame bits not empty, not a bitstream"); } }
      break; }


    /* find target addresses (base and step) to write frame to */
    /*   this is complicated by wanting all words of an row together */
    /*     data will be placed Bitmap*[0..columns-1][0..rows-1][0..frames-1] */
    /*   is further complicated by different jump after column in row done */
    /*   and further by CLB columns not arriving ordered left to right */
    /*   and even further by stepping Center->Clb->IobWE->Bram sections */

    /* step target through groups, hardcoded stepping of Virtex/Spartan-II */
    /*   for Virtex-E/Virtex-EM/Spartan-IIE it will need different code */

    /* first do Center frames */
    if (Frame == 0) {
      /* frames have rows from top to bottom, we want then bottom to top */
      /*   for row indexing, so start at last row address and count down */
      Middle = BitmapCenter+(Model->MiddleRows-1)*VirtexCenterFrames;
      TopBot = BitmapGclk+(VirtexTopBotRows-1)*VirtexGclkFrames;
      /* jump downwards over space for frames of one row */
      StepMiddle = StepTopBot = -VirtexCenterFrames;
      /* when to step to processing CLB frames */
      FirstClbFrame = VirtexCenterFrames;
      /* zero these out because else they have random content */
      FirstIobWEFrame = 0; FirstBramCFrame = 0;
      NextClbColFrame = 0; NextIobWEColFrame = 0; NextBramCColFrame = 0; }

    /* after Center do CLB frames */
    else if (Frame == FirstClbFrame || Frame == NextClbColFrame) {
      /* physical is bitstream ordering:  cols .. 4 2 center 1 3 .. cols-1 */
      /*   so they start half way over chip (cols/2) and then zizag outwards */
      int PhysColumn = (Frame-FirstClbFrame)/VirtexClbFrames+1;
      /* logical is usage ordering:  from left to right: 0 1 2 .. cols-1 */
      /*   if 1,3,.. add half (auto-losing 0.5), if 2,4,.. subtract half */
      int LogColumn = (PhysColumn&1) ? Model->ClbColumns/2+PhysColumn/2
                                     : Model->ClbColumns/2-PhysColumn/2;
      /* note that C automagically multiplies "+xxx" with sizeof(ulong) */
      Middle = BitmapClb+LogColumn*Model->MiddleRows*VirtexClbFrames+
        (Model->MiddleRows-1)*VirtexClbFrames;
      TopBot = BitmapIobNS+LogColumn*VirtexTopBotRows*VirtexIobNSFrames+
        (VirtexTopBotRows-1)*VirtexIobNSFrames;
      StepMiddle = StepTopBot = -VirtexClbFrames;
      /* dont set when last column, else no fall through to FirstIobWEFrame */
      NextClbColFrame = (PhysColumn<Model->ClbColumns)
                        ? Frame+VirtexClbFrames : 0;
      FirstIobWEFrame = FirstClbFrame+Model->ClbColumns*VirtexClbFrames; }

    /* after CLBs do IOB W/E frames */
    else if (Frame == FirstIobWEFrame || Frame == NextIobWEColFrame) {
      /* these are in bitstream 2 .. rest here .. 1 */
      int PhysColumn = (Frame-FirstIobWEFrame)/VirtexIobWEFrames+1;
      int LogColumn = (PhysColumn&1) ? VirtexIobWEColumns/2+PhysColumn/2
                                     : VirtexIobWEColumns/2-PhysColumn/2;
      Middle = BitmapIobWE+LogColumn*Model->MiddleRows*VirtexIobWEFrames+
        (Model->MiddleRows-1)*VirtexIobWEFrames;
      TopBot = BitmapPadIobWE+LogColumn*VirtexTopBotRows*VirtexPadIobWEFrames+
        (VirtexTopBotRows-1)*VirtexPadIobWEFrames;
      StepMiddle = StepTopBot = -VirtexIobWEFrames;
      NextIobWEColFrame = (PhysColumn<VirtexIobWEColumns)
                          ? Frame+VirtexIobWEFrames : 0;
      FirstBramCFrame = FirstIobWEFrame+VirtexIobWEColumns*VirtexIobWEFrames; }

    /* after W/E IOBs do BRAM control frames */
    else if (Frame == FirstBramCFrame || Frame == NextBramCColFrame) {
      /* these are in bitstream .. 2 .. rest here .. 1 .. */
      int PhysColumn = (Frame-FirstBramCFrame)/VirtexBramCFrames+1;
      int LogColumn = (PhysColumn&1) ? Model->BramColumns/2+PhysColumn/2
                                     : Model->BramColumns/2-PhysColumn/2;
      Middle = BitmapBram+LogColumn*Model->MiddleRows*VirtexBramFrames+
	(Model->MiddleRows-1)*VirtexBramFrames;
      TopBot = BitmapDll+LogColumn*VirtexTopBotRows*VirtexDllFrames+
	(VirtexTopBotRows-1)*VirtexDllFrames;
      StepMiddle = StepTopBot = -VirtexBramFrames;
      NextBramCColFrame = (PhysColumn<Model->BramColumns)
                          ? Frame+VirtexBramCFrames : 0;
      /* no calculating First*Frame for pipeline, as it is last frame */ }

    /* still in same columns (any type of column) frames */
    else {
      /* first row of next frame one word further then first of last frame */
      Middle = FirstMiddle+1; TopBot = FirstTopBot+1; }

    /* remember new first row for next round of above calculation */
    FirstMiddle = Middle; FirstTopBot = TopBot;


    /* extract the config bits and transfer them to the target addresses */

    Bits = FileFrame;

    /* top 1 row, allways starts frame with bits alligned, no shifting*/
    *TopBot = *Bits&BitsMask;
    BitsLeft = BitsInUlong-VirtexTopBotBits;
    /* jump over space used for rest of words of this row, to next row */
    TopBot += StepTopBot;

    /* middle "MiddleRows" rows, may be missaligned, shift or even get next */
    for (Row = 0; Row < Model->MiddleRows; Row ++) {
      if (BitsLeft >= VirtexMiddleBits) {
        *Middle = (*Bits<<(BitsInUlong-BitsLeft))&BitsMask;
        BitsLeft = BitsLeft-VirtexMiddleBits; }
      else if (BitsLeft == 0) {
        /* fix for broken i386 "sall" (shift arith left long) instruction */
        /* for BitsLeft == 0, shift by 32, does nothing instead of giving 0 */
        Bits++;
        *Middle = *Bits&BitsMask;
        BitsLeft = BitsInUlong-VirtexMiddleBits; }
      else {
        ulong FirstPart = *Bits<<(BitsInUlong-BitsLeft);
        Bits++;
        *Middle = FirstPart|(*Bits>>BitsLeft)&BitsMask;
        BitsLeft = BitsLeft+BitsInUlong-VirtexMiddleBits; }
      Middle += StepMiddle; }

    /* bottom 1 row, no need to update BitsLeft, as reset for next frame */
    if (BitsLeft >= VirtexTopBotBits) {
      *TopBot = *Bits<<(BitsInUlong-BitsLeft); }
    else if (BitsLeft == 0) {
      /* fix for broken i386 "sall" (shift arith left long) instruction */
      Bits++;
      *TopBot = *Bits&BitsMask; }
    else {
      ulong FirstPart = (*Bits<<(BitsInUlong-BitsLeft))&BitsMask;
      Bits++;
      *TopBot = FirstPart|(*Bits>>BitsLeft)&BitsMask; }
    /* may be superflouos, but consistent with Middle and does no damage */
    TopBot += StepTopBot;


    /* check if pipelining word empty, allways needs to be got, don't store */
    Bits++;
    if (*Bits != 0) {
      return ("Pipelining word not empty, not a bitstream"); } }


  /* this was only temporary, will be re-*alloc()ed if needed again */
  free(FileFrame);

  return (NULL); }


/* ------ dump bits to output file section */

/* expects: Model set so that model dependant constants are known */
/*   and also bitstream main frames data read */
/*   and OptBitfileName set to filename or NULL for stdout */
/* leaves: Outfile opened, dumped to, and closed */

FILE *Outfile;

void dumpoutfile(void) {

  int ierror;

  if (OptOutfileName) {
    if (OptOperation == OptVerboseOperation) {
      fprintf(stderr, "dumping to outfile %s\n", OptOutfileName); }
    Outfile = fopen(OptOutfileName, "w");
    if (!Outfile) {
      fprintf(stderr, "%s: %s\n", strerror(errno), OptOutfileName);
      exit(1); } }
  else {
    if (OptOperation == OptVerboseOperation) {
      fprintf(stderr, "dumping to standard output\n"); }
    Outfile = stdout; }

  /* *fixme*: at present we only support dumping CLBs */

  /* select what rage we want to see, and do the actual dumping */
  setclbrange();
  /* select type of dumping, from more special to least special */
  if (OptMemory == OptDoMemory) {
    if (OptZigzag == OptDoZigzag) {
      dumpmemoryzzclbs(); }
    else {
      dumpmemoryclbs(); } }
  else if (OptEntire == OptDoEntire) {
    dumpentireclbs(); }
  else {
    dumpclbs(); }

  ierror = fclose(Outfile);
  if (ierror) {
    fprintf(stderr, "%s: %s\n", strerror(errno), OptOutfileName);
    exit(1); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "finished dumping\n"); } }


/* ------ set range of CLBs to dump section */

/* expects: Model set so that model dependant constants are known */
/*   and OptRange set to string representing desired range */
/* leaves: OptRange parsed into [First|Last][Col|Sli|Row|Lut] */

#define SLIBOTH -1
#define LUTBOTH -1

int FirstCol, LastCol, Col;
int FirstSli, LastSli, OnlySli, Sli;
int FirstRow, LastRow, Row;
int FirstLut, LastLut, OnlyLut, Lut;

void setclbrange(void) {

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK setclbrange() FATAL: model not been set\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "setting range for dumping CLBs\n"); }

  /* if no range given, dump entire chip, yes this is large or even massive */
  FirstCol = 0; LastCol = Model->ClbColumns-1;
  FirstSli = SLIL; LastSli = SLIR; OnlySli = SLIBOTH;
  FirstRow = 0; LastRow = Model->ClbRows-1;
  FirstLut = LUTF; LastLut = LUTG; OnlyLut = LUTBOTH;

  /* if range is given, evaluate it and reduce output to that */
  if (OptRange != NULL) {
    /* prevent loosing OptRange, even though we presently do not reuse it */
    char *Range = OptRange;

    /* a full range string looks like this: 0L-mR/0F-nG */
    if (*Range != '/') {
      /* user is giving columns, else leave entire width */

      if (*Range != '-') {
        /* is giving column beginning, else leave default left */
        LastCol = FirstCol = (int)strtoul(Range, &Range, 10);
        if (*Range == 'L') {
          OnlySli = SLIL; Range++; }
        if (*Range == 'R') {
          FirstSli = SLIR; Range++; } }

      if (*Range == '-') {
        Range++;
        /* is giving column end, else just leave one column */
        if (*Range == 0 || *Range == '/') {
          /* has given beginning and '-' but no end: set back to right */
          LastCol = Model->ClbColumns-1; LastSli = SLIR; }

        else {
          /* is giving column end, else just wants one column, multi row */
          LastCol = (int)strtoul(Range, &Range, 10);
          if (*Range == 'L') {
            LastSli = SLIL; Range++; }
          if (*Range == 'R') {
            OnlySli = SLIR; Range++; } } } }

    if (*Range == '/') {
      Range++;
      /* user is giving rows, else leave entire hight */

      if (*Range != '-') {
        /* is giving row beginning, else leave bottom */
        LastRow = FirstRow = (int)strtoul(Range, &Range, 10);
        if (*Range == 'F') {
          OnlyLut = LUTF; Range++; }
        if (*Range == 'G') {
          FirstLut = LUTG; Range++; } }

      if (*Range == '-') {
        Range++;
        /* is giving row end, else just leave one row */
        if (*Range == 0) {
          /* has given beginning and '-' but no end: set back to top */
          LastRow = Model->ClbRows-1; LastLut = LUTG; }

        else {
          /* is giving row end, else just wants one row, multi column */
          LastRow = (int)strtoul(Range, &Range, 10);
          if (*Range == 'F') {
            LastLut = LUTF; Range++; }
          if (*Range == 'G') {
            OnlyLut = LUTG; Range++; } } } }

    if (*Range != 0) {
      fprintf(stderr, "%s: Error: unknown char in range at: %s\n\n",
        OptProgName, Range);
      helpoptions(1); }

    /* test if we are still on the chip */
    if (FirstCol > Model->ClbColumns-1) {
      fprintf(stderr, "%s: Error: first column out of range (%i-%i): %i\n",
        OptProgName, 0, Model->ClbColumns-1, FirstCol);
      exit(1); }
    if (LastCol > Model->ClbColumns-1) {
      fprintf(stderr, "%s: Error: last column out of range (%i-%i): %i\n",
        OptProgName, 0, Model->ClbColumns-1, LastCol);
      exit(1); }
    if (FirstRow > Model->ClbRows-1) {
      fprintf(stderr, "%s: Error: first row out of range (%i-%i): %i\n",
        OptProgName, 0, Model->ClbRows-1, FirstRow);
      exit(1); }
    if (LastRow > Model->ClbRows-1) {
      fprintf(stderr, "%s: Error: last row out of range (%i-%i): %i\n",
        OptProgName, 0, Model->ClbRows-1, LastRow);
      exit(1); } } }


/* ------ dump group of CLBs as zigzagged memory to outfile section */

/* just a driver to repeatedly call dumpzzmemoryclb() */

void dumpmemoryzzclbs(void) {

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "dumping CLB data as zigzagged wordwise memory\n"); }

  /* interleave halves of CLBs (Sli) with CLBs (Col), same as dumpclbs() */
  /* but here we always have pairs of slices, one for 0/2/.., one for 1/3/.. */
  if (OnlySli == SLIBOTH) {
    if (FirstSli == SLIL) {
      /* a L and an R slice */
      for (Col = FirstCol; Col <= LastCol; Col++) {
        dumpmemoryzzclbslis(Col, SLIL, Col, SLIR);
        if (Col != LastCol) {
          fprintf(Outfile, "\n"); } } }
    else {
      /* a R and an L slice, LastCol has only R, L is from LastCol+1 */
      for (Col = FirstCol; Col <= LastCol; Col++) {
        dumpmemoryzzclbslis(Col, SLIR, Col+1, SLIL);
        if (Col != LastCol) {
          fprintf(Outfile, "\n"); } } } }
  else {
    /* cut down to only showing one slice per CLB, so 2 CLB per memory */
    for (Col = FirstCol; Col <= LastCol; Col+=2) {
      dumpmemoryzzclbslis(Col, OnlySli, Col+1, OnlySli);
      /* empty line after, only if another CLB coming */
      if (Col != LastCol) {
        fprintf(Outfile, "\n"); } } } }


void dumpmemoryzzclbslis(int Col0, int Sli0, int Col1, int Sli1) {

  int MemWidth, MemBit;
  /* always 1 bit per slice, 2 slices parallel */
  MemWidth = (LastRow-FirstRow+1)*2;
  /* we start at top/left/high/MSB */
  MemBit = MemWidth-1;

  for (Row = LastRow; Row >= FirstRow; Row--) {
    /* 2 bits per Row, MemBit selects from/to */
    dumpmemoryzzclb(Col0, Sli0, Col1, Sli1, Row, MemWidth, MemBit--);
    dumpmemoryzzclb(Col0, Sli0, Col1, Sli1, Row, MemWidth, MemBit--); } }


/* ------ dump CLB (actually 2 slices) as zigzag memory to outfile section */

/* expects: Model set so that model dependant constants are known */
/*   and also bitstream main frames data read */
/* leaves: same state, but output has lines generated from CLB */

/* CLB layout of LUT-RAM bits, rest see dumpclb()   */
/* Slice 1/L                              Slice 0/R */
/* FEDCBA9876543210................0123456789ABCDEF */

/* Xilinx thinks big endian, so we use the first/top/big/MSB 18 bits */
/*   masks for extracting LUT bits from 18 (or 32) */
#define LutGBitMask 0x20000000
#define LutFBitMask 0x10000000

void dumpmemoryzzclb(int Col0, int Sli0, int Col1, int Sli1, int Row,
  int MemWidth, int MemBit) {

  ulong *Clb0, *Clb1;
  int HeaderLoop, LutAddress;
  char *HeaderLine[3], *MemWord[32];

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: model not been set\n");
    abort(); }

  if (!(BitmapCenter && BitmapGclk     && BitmapClb  && BitmapIobNS &&
        BitmapIobWE  && BitmapPadIobWE && BitmapBram && BitmapDll)) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: no mem for frames\n");
    abort(); }

  if (Col < 0 || Col > Model->ClbColumns-1) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: column out of range\n");
    abort(); }

  if (!(Sli == SLIL || Sli == SLIR)) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: slice out of range\n");
    abort(); }

  if (Row < 0 || Row > Model->ClbRows-1) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: row out of range\n");
    abort(); }

  if (!Outfile) {
    fprintf(stderr, "BUGCHECK dumpmemoryzzclb() FATAL: no file to dump to\n");
    abort(); }

  /* frome here on are our VirtexClbFrames of frames */
  Clb0 = BitmapClb+(Col0*Model->MiddleRows+Row)*VirtexClbFrames;
  Clb1 = BitmapClb+(Col1*Model->MiddleRows+Row)*VirtexClbFrames;

  if (MemBit == MemWidth-1) {
    /* begin of first time through */

    for (HeaderLoop = 0; HeaderLoop < 3; HeaderLoop++) {
      HeaderLine[HeaderLoop] = malloc(MemWidth+1);
      /* zero terminate the strings, will be filled by indexed writes */
      HeaderLine[HeaderLoop][MemWidth] = '\0'; }
    for (LutAddress = 0; LutAddress < 32; LutAddress++) {
      MemWord[LutAddress] = malloc(MemWidth+1);
      MemWord[LutAddress][MemWidth] = '\0'; } }


  if (OptFormat == OptLongFormat) {
    char RowString[4];
    snprintf(RowString, sizeof(RowString), "%03i", Row);
    /* 0 = hundreds, 1 = tens, 2 = ones */
    HeaderLine[0][MemWidth-(MemBit+1)] = RowString[0];
    HeaderLine[1][MemWidth-(MemBit+1)] = RowString[1];
    HeaderLine[2][MemWidth-(MemBit+1)] = RowString[2]; }

  /* transpose LUT bits, from Addr hor / LutNo ver to BitNo hor / Addr ver */
  for (LutAddress = 0; LutAddress < 16; LutAddress++) {
    /* first 16 addresses come from F LUTs */
    if ((MemBit&1) == 0) {
      /* bit 0/2/... come from Col0/Sli0 */
      int LutFrameNo = (Sli0 == SLIR) ? 32+LutAddress : 15-LutAddress;
      /* un-invert the LUT bits, as they are inverted in bitstream */
      char BitChar = (*(Clb0+LutFrameNo) & LutFBitMask) ? '0' : '1';
      MemWord[LutAddress][MemWidth-(MemBit+1)] = BitChar; }
    else {
      /* bit 1/3/... come from Col1/Sli1 */
      int LutFrameNo = (Sli1 == SLIR) ? 32+LutAddress : 15-LutAddress;
      char BitChar = (*(Clb1+LutFrameNo) & LutFBitMask) ? '0' : '1';
      MemWord[LutAddress][MemWidth-(MemBit+1)] = BitChar; } }

  for (LutAddress = 0; LutAddress < 16; LutAddress++) {
    /* second 16 addresses come from G LUTs */
    if ((MemBit&1) == 0) {
      int LutFrameNo = (Sli0 == SLIR) ? 32+LutAddress : 15-LutAddress;
      char BitChar = (*(Clb0+LutFrameNo) & LutGBitMask) ? '0' : '1';
      MemWord[LutAddress+16][MemWidth-(MemBit+1)] = BitChar; }
    else {
      int LutFrameNo = (Sli1 == SLIR) ? 32+LutAddress : 15-LutAddress;
      char BitChar = (*(Clb1+LutFrameNo) & LutGBitMask) ? '0' : '1';
      MemWord[LutAddress+16][MemWidth-(MemBit+1)] = BitChar; } }


  if (MemBit == 0) {
    /* end of last time through */

    if (OptMemoryBase != NULL) {
      /* user wants the output formatted, not just raw bits */
      if (OptFormat == OptLongFormat) {
        formatmemory(MemWidth, MemWord, 32, HeaderLine, 3); }
      else {
        formatmemory(MemWidth, MemWord, 32, NULL, 0); } }

    if (OptFormat == OptLongFormat) {
      /* output stats (what we are dumping), column and slice */
      fprintf(Outfile, "Col/Sli: %i%c+%i%c, Data:\n",
        Col0, (Sli0 == SLIR) ? 'R' : 'L', Col1, (Sli1 == SLIR) ? 'R' : 'L');
      /* output which row we are in */
      fprintf(Outfile, "   %s\n", HeaderLine[0]);
      fprintf(Outfile, "   %s\n", HeaderLine[1]);
      fprintf(Outfile, "   %s\n", HeaderLine[2]);
      /* output separator, full line of HeaderLine/MemWord width, reuse [0] */
      memset(HeaderLine[0], '-', strlen(HeaderLine[0]));
      fprintf(Outfile, "  .%s\n", HeaderLine[0]); }

    if (OptFormat == OptShortFormat) {
      /* short stats, just column/slice number and then data without headers */
      fprintf(Outfile, "%03i%1c+%03i%1c/%03i-%03i:\n",
              Col0, (Sli0 == SLIR) ? 'R' : 'L',
              Col1, (Sli1 == SLIR) ? 'R' : 'L',
              LastRow, FirstRow); }

    for (LutAddress = 0; LutAddress < 32; LutAddress++) {
      if (OptFormat == OptLongFormat) {
        /* number addresses */
        fprintf(Outfile, "%2i|", LutAddress); }
      fprintf(Outfile, "%s\n", MemWord[LutAddress]); }

    for (HeaderLoop = 0; HeaderLoop < 3; HeaderLoop++) {
      free(HeaderLine[HeaderLoop]); }
    for (LutAddress = 0; LutAddress < 32; LutAddress++) {
      free(MemWord[LutAddress]); } } }


/* ------ dump group of CLBs as memory to outfile section */

/* just a driver to repeatedly call dumpmemoryclb() */

void dumpmemoryclbs(void) {

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "dumping CLB data as wordwise memory\n"); }

  /* interleave halves of CLBs (Sli) with CLBs (Col), same as dumpclbs() */
  for (Col = FirstCol; Col <= LastCol; Col++) {
    if (OnlySli == SLIBOTH) {
      /* left slice */
      if (!(Col == FirstCol && FirstSli == SLIR)) {
        dumpmemoryclbslis(Col, SLIL); }
      /* empty line inbetween, only if both */
      if (!(Col == FirstCol && FirstSli == SLIR &&
            Col == LastCol && LastSli == SLIL)) {
        fprintf(Outfile, "\n"); }
      /* right slice */
      if (!(Col == LastCol && LastSli == SLIL)) {
        dumpmemoryclbslis(Col, SLIR); }
      /* empty line after, only if another CLB coming */
      if (!(Col == LastCol)) {
        fprintf(Outfile, "\n"); } }
    else {
      /* cut down to only showing one slice per CLB */
      dumpmemoryclbslis(Col, OnlySli);
      /* empty line after, only if another CLB coming */
      if (Col != LastCol) {
        fprintf(Outfile, "\n"); } } } }


void dumpmemoryclbslis(int Col, int Sli) {

  int MemWidth, MemBit;
  if (OnlyLut == LUTBOTH) {
    MemWidth = (LastRow-FirstRow+1)*2;
    if (LastLut == LUTF) {
      MemWidth--; }
    if (FirstLut == LUTG) {
      MemWidth--; } }
  else {
    MemWidth = LastRow-FirstRow+1; }
  /* we start at top/left/high/MSB */
  MemBit = MemWidth-1;

  for (Row = LastRow; Row >= FirstRow; Row--) {
    if (OnlyLut == LUTBOTH) {
      /* dump G if range not restricted to end with F, mem MSB F not G */
      if (!(Row == LastRow && LastLut == LUTF)) {
        dumpmemoryclb(Col, Sli, Row, LUTG, MemWidth, MemBit--); }
      /* dump F if range not restricted to start with G, mem LSB G not F */
      if (!(Row == FirstRow && FirstLut == LUTG)) {
        dumpmemoryclb(Col, Sli, Row, LUTF, MemWidth, MemBit--); } }
    else {
      dumpmemoryclb(Col, Sli, Row, OnlyLut, MemWidth, MemBit--); } } }


/* ------ dump CLB (actually quater CLB = LUT) as memory to outfile section */

/* expects: Model set so that model dependant constants are known */
/*   and also bitstream main frames data read */
/* leaves: same state, but output has lines generated from CLB */

/* CLB layout of LUT-RAM bits, rest see dumpclb()   */
/* Slice 1/L                              Slice 0/R */
/* FEDCBA9876543210................0123456789ABCDEF */

/* Xilinx thinks big endian, so we use the first/top/big/MSB 18 bits */
/*   masks for extracting LUT bits from 18 (or 32) */
#define LutGBitMask 0x20000000
#define LutFBitMask 0x10000000

void dumpmemoryclb(int Col, int Sli, int Row, int Lut,
  int MemWidth, int MemBit) {

  ulong *Clb, LutBitMask;
  int HeaderLoop, LutAddress;
  char *HeaderLine[4], *MemWord[16];

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: model not been set\n");
    abort(); }

  if (!(BitmapCenter && BitmapGclk     && BitmapClb  && BitmapIobNS &&
        BitmapIobWE  && BitmapPadIobWE && BitmapBram && BitmapDll)) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: no memory for frames\n");
    abort(); }

  if (Col < 0 || Col > Model->ClbColumns-1) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: column out of range\n");
    abort(); }

  if (!(Sli == SLIL || Sli == SLIR)) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: slice out of range\n");
    abort(); }

  if (Row < 0 || Row > Model->ClbRows-1) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: row out of range\n");
    abort(); }

  if (!(Lut == LUTF || Lut == LUTG)) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: LUT out of range\n");
    abort(); }

  if (!Outfile) {
    fprintf(stderr, "BUGCHECK dumpmemoryclb() FATAL: no file to dump to\n");
    abort(); }

  /* frome here on are our VirtexClbFrames of frames */
  Clb = BitmapClb+(Col*Model->MiddleRows+Row)*VirtexClbFrames;

  if (MemBit == MemWidth-1) {
    /* begin of first time through */

    for (HeaderLoop = 0; HeaderLoop < 4; HeaderLoop++) {
      HeaderLine[HeaderLoop] = malloc(MemWidth+1);
      /* zero terminate the strings, will be filled by indexed writes */
      HeaderLine[HeaderLoop][MemWidth] = '\0'; }
    for (LutAddress = 0; LutAddress < 16; LutAddress++) {
      MemWord[LutAddress] = malloc(MemWidth+1);
      MemWord[LutAddress][MemWidth] = '\0'; } }


  if (OptFormat == OptLongFormat) {
    char RowString[4];
    snprintf(RowString, sizeof(RowString), "%03i", Row);
    /* 0 = hundreds, 1 = tens, 2 = ones */
    HeaderLine[0][MemWidth-(MemBit+1)] = RowString[0];
    HeaderLine[1][MemWidth-(MemBit+1)] = RowString[1];
    HeaderLine[2][MemWidth-(MemBit+1)] = RowString[2];
    HeaderLine[3][MemWidth-(MemBit+1)] = (Lut == LUTG) ? 'G' : 'F'; }

  /* which LUT bits we are interested in */
  LutBitMask = (Lut == LUTG) ? LutGBitMask: LutFBitMask;
  /* transpose LUT bits, from Addr hor / LutNo ver to BitNo hor / Addr ver */
  for (LutAddress = 0; LutAddress < 16; LutAddress++) {
    int LutFrameNo = (Sli == SLIR) ? 32+LutAddress : 15-LutAddress;
    /* un-invert the LUT bits, as they are inverted in bitstream */
    char BitChar = (*(Clb+LutFrameNo) & LutBitMask) ? '0' : '1';
    MemWord[LutAddress][MemWidth-(MemBit+1)] = BitChar; }


  if (MemBit == 0) {
    /* end of last time through */

    if (OptMemoryBase != NULL) {
      /* user wants the output formatted, not just raw bits */
      if (OptFormat == OptLongFormat) {
        formatmemory(MemWidth, MemWord, 16, HeaderLine, 4); }
      else {
        formatmemory(MemWidth, MemWord, 16, NULL, 0); } }

    if (OptFormat == OptLongFormat) {
      /* output stats (what we are dumping), column and slice */
      fprintf(Outfile, "Col/Sli: %i%c, Data:\n",
        Col, (Sli == SLIR) ? 'R' : 'L');
      /* output which row we are in */
      fprintf(Outfile, "   %s\n", HeaderLine[0]);
      fprintf(Outfile, "   %s\n", HeaderLine[1]);
      fprintf(Outfile, "   %s\n", HeaderLine[2]);
      /* output which LUT we are in */
      fprintf(Outfile, "   %s\n", HeaderLine[3]);
      /* output separator, full line of HeaderLine/MemWord width, reuse [0] */
      memset(HeaderLine[0], '-', strlen(HeaderLine[0]));
      fprintf(Outfile, "  .%s\n", HeaderLine[0]); }

    if (OptFormat == OptShortFormat) {
      /* short stats, just column/slice number and then data without headers */
      fprintf(Outfile, "%03i%1c/%03i%1c-%03i%1c:\n",
              Col,      (Sli == SLIR) ? 'R' : 'L',
              LastRow,  (LastLut == LUTG) ? 'G' : 'F',
              FirstRow, (FirstLut == LUTG) ? 'G' : 'F'); }

    for (LutAddress = 0; LutAddress < 16; LutAddress++) {
      if (OptFormat == OptLongFormat) {
        /* number addresses */
        fprintf(Outfile, "%2i|", LutAddress); }
      fprintf(Outfile, "%s\n", MemWord[LutAddress]); }

    for (HeaderLoop = 0; HeaderLoop < 4; HeaderLoop++) {
      free(HeaderLine[HeaderLoop]); }
    for (LutAddress = 0; LutAddress < 16; LutAddress++) {
      free(MemWord[LutAddress]); } } }


/* ------ format memory output section */

/* expects: OptMemoryBase set to string representing desired format */
/*   MemWord pointing to Words long array of strings, char 0/1 per bit */
/*   HeaderLine pointing to Lines long array of strings, one char per bit */
/* leaves: MemWord data reformatted, HeaderLine fitting format selected */

void formatmemory(int MemWidth, char *MemWord[], int Words,
  char *HeaderLine[], int Lines) {

  char *BaseOpt = OptMemoryBase, FormatChar = *BaseOpt, Format[10];
  int DigitLength, ByteLength = MemWidth, DigitPerByte;
  int Word, Line;

  if (OptMemoryBase == NULL) {
    fprintf(stderr, "BUGCHECK formatmemory() FATAL: no format to parse\n");
    abort(); }

  if (MemWord == NULL || Words == 0) {
    fprintf(stderr, "BUGCHECK formatmemory() FATAL: no memory to format\n");
    abort(); }

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "formatting memory for output\n"); }


  /* if an "od" user types c, convert him to a, we have no special c case */
  if      (*BaseOpt == 'c') { FormatChar = *BaseOpt = 'a'; }
  /* if an "od" user types u, convert him to d, d is already unsigned */
  else if (*BaseOpt == 'u') { FormatChar = *BaseOpt = 'd'; }

  /* for h we use x internally, but externally h is more obvious for hex */
  else if (*BaseOpt == 'h') { FormatChar = *BaseOpt = 'x'; }

  if      (*BaseOpt == 'a') { DigitLength = 8; BaseOpt++; }
  else if (*BaseOpt == 'b') { DigitLength = 1; BaseOpt++; }
  else if (*BaseOpt == 'd') { DigitLength = 0; BaseOpt++; }
  else if (*BaseOpt == 'o') { DigitLength = 3; BaseOpt++; }
  else if (*BaseOpt == 'x') { DigitLength = 4; BaseOpt++; }
  else {
    fprintf(stderr, "%s: Error: unknown base type at: %s\n\n",
      OptProgName, BaseOpt);
    helpoptions(1); }


  /* char format usually used to view bytes as characters, usually 8bit/char */
  if      (FormatChar == 'a') { ByteLength = 8; }

  if      (*BaseOpt == 'C') { ByteLength = 8;        BaseOpt++; }
  else if (*BaseOpt == 'I') { ByteLength = MemWidth; BaseOpt++; }
  else if (*BaseOpt == 'L') { ByteLength = 32;       BaseOpt++; }
  else if (*BaseOpt == 'S') { ByteLength = 16;       BaseOpt++; }
  else if (isdigit(*BaseOpt)) {
    ByteLength = (int)strtoul(BaseOpt, &BaseOpt, 10); }

  if (*BaseOpt != 0) {
    fprintf(stderr, "%s: Error: unknown char in base type at: %s\n\n",
      OptProgName, BaseOpt);
    helpoptions(1); }

  if (ByteLength > MemWidth) {
    fprintf(stderr, "%s: Error: byte wider than word is senseless: %i>%i\n",
      OptProgName, ByteLength, MemWidth);
    exit(1); }

  if (FormatChar == 'a' && ByteLength < 7) {
    fprintf(stderr, "%s: Error: byte too short to contain ASCII: %i<7\n",
      OptProgName, ByteLength);
    exit(1); }


  /* calc how much chars space we need for an byte */
  if (FormatChar == 'a') {
    /* characters shown 0..31+127 3 char ASCII name, rest char and 2 blank */
    DigitPerByte = 3; }
  else if (FormatChar == 'd') {
    /* for decimal we can only use an table lookup */
    /*   would be add 1 every ln10/ln2 = 3.3219 steps, shudder */
    /*   no I am not calculating ln10/ln2 in integer */
    /*   table for 1-32 bit wide bytes, larger are not converted anyway */
    int DecDigitPerByte[] = { 1,1,1,2,2,2,3,3, 3,4,4,4,4,5,5,5,
                              6,6,6,7,7,7,7,8, 8,8,9,9,9,10,10,10 };
    DigitPerByte = DecDigitPerByte[ByteLength-1]; }
  else {
    DigitPerByte = ByteLength/DigitLength;
    /* no exact fit, % rounds downwards, we want upwards */
    if (DigitPerByte*DigitLength < ByteLength) {
      DigitPerByte++; } }
  sprintf(Format, "%%0%i%c", DigitPerByte, FormatChar);


  for (Word = 0; Word < Words; Word++) {
    char *From = MemWord[Word], Buffer[1024], *To = Buffer;

    /* separate bytes with spaces */
    while (From+ByteLength < MemWord[Word]+MemWidth) {
      strncpy(To, From, ByteLength);
      From += ByteLength; To += ByteLength;
      *To++ = ' '; }
    if (From < MemWord[Word]+MemWidth) {
      /* remaining bits, less than ByteLength */
      strncpy(To, From, MemWord[Word]+MemWidth-From);
      To += MemWord[Word]+MemWidth-From; }
    else {
      /* undo blank after last byte */
      To--; }
    *To = 0;

    /* realloc MemWord[] to proper size and copy Buffer to it */
    MemWord[Word] = realloc(MemWord[Word], strlen(Buffer)+1);
    strcpy(MemWord[Word], Buffer);


    /* convert binary number to other forms */
    if (FormatChar != 'b') {
      /* use of sprintf and "ulong" sets limit, alternative lots of code */
      if (ByteLength > 32) {
        fprintf(stderr, "%s: Error: byte wider 32(long) not convertable: %i\n",
          OptProgName, ByteLength);
        exit(1); }
      From = MemWord[Word]; To = Buffer;
      while (From < MemWord[Word]+MemWidth) {
        ulong Byte = strtoul(From, &From, 2);
        if (FormatChar == 'a') {
          /* only one character/byte, multi lot of work, unlikely to be used */
          Byte &= 0xFF;
          /* control chars converted to names, else they kill terminal */
          if      ((Byte&0x7F) < 32) {
            char *Ctrl[] = { "NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL",
                             "BS ","HT ","LF ","VT ","FF ","CR ","SO ","SI ",
                             "DLE","DC1","DC2","DC3","DC4","NAK","SYM","ETB",
                             "CAN","EM ","SUB","ESC","FS ","GS ","RS ","US " };
            To += sprintf(To, "%3s", Ctrl[Byte&0x7F]); }
          else if ((Byte&0x7F) == 0x7F) {
            To += sprintf(To, "DEL");  }
          else {
            To += sprintf(To, "%c  ", Byte); } }
        else {
          To += sprintf(To, Format, Byte); }
        if (*From == ' ') {
          *To++ = *From++; } }
      *To = 0;

      /* realloc MemWord[] to proper size and copy Buffer to it */
      MemWord[Word] = realloc(MemWord[Word], strlen(Buffer)+1);
      strcpy(MemWord[Word], Buffer); } }


  /* no header to format, abort rest of formatmemory() */
  if (HeaderLine == NULL || Lines == 0) {
    return; }

  for (Line = 0; Line < Lines; Line++) {
    char *From = HeaderLine[Line], Buffer[1024], *To = Buffer;

    /* header insert identical spaces between bytes, copy rest */
    while (From+ByteLength < HeaderLine[Line]+MemWidth) {
      strncpy(To, From, ByteLength);
      From += ByteLength; To += ByteLength;
      *To++ = ' '; }
    if (From < HeaderLine[Line]+MemWidth) {
      strncpy(To, From, HeaderLine[Line]+MemWidth-From);
      To += HeaderLine[Line]+MemWidth-From; }
    else {
      To--; }
    *To = 0;

    HeaderLine[Line] = realloc(HeaderLine[Line], strlen(Buffer)+1);
    strcpy(HeaderLine[Line], Buffer);


    /* header, copy header of 1st bit of bitgroup that makes one char */
    if (FormatChar != 'b') {
      From = HeaderLine[Line]; To = Buffer;
      while (From < HeaderLine[Line]+MemWidth) {
        int InsertBlank;
        /* one header column per byte, better would be one per digit */
        /*   but that would be a lot of work, because of byte<n*digit */
        *To++ = *From; From += ByteLength;
        for (InsertBlank = 0; InsertBlank < DigitPerByte-1; InsertBlank++) {
          *To++ = ' '; }
        /* make sure we did not overrun the memory word width */
        if (From < HeaderLine[Line]+MemWidth && *From == ' ') {
          *To++ = *From++; } }
      *To = 0;

      /* realloc HeaderLine[] to proper size and copy Buffer to it */
      HeaderLine[Line] = realloc(HeaderLine[Line], strlen(Buffer)+1);
      strcpy(HeaderLine[Line], Buffer); } } }


/* ------ dump group of entire CLBs to outfile section */

/* just a driver to repeatedly call dumpentireclb() */

void dumpentireclbs(void) {

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "dumping entire CLB data\n"); }

  /* "entire" mode simply ignores *Sli and *Lut, as all 4 quaters are in one */
  for (Col = FirstCol; Col <= LastCol; Col ++) {
    for (Row = FirstRow; Row <= LastRow; Row ++) {
      dumpentireclb(Col, Row);

      if (!(Col == LastCol && Row == LastRow)) {
        fprintf(Outfile, "\n"); } } } }


/* ------ dump single entire CLB to outfile section */

/* expects: Model set so that model dependant constants are known */
/*   and also bitstream main frames data read */
/* leaves: same state, but output has lines generated from CLB */

/* Xilinx thinks big endian, so we use the first/top/big/MSB 18 bits */
/*   masks for extracting top 18 bits from 32 */
#define TopBitMask 0x80000000
#define BotBitMask 0x00004000

void dumpentireclb(int Col, int Row) {

  int Bit, FrameNo;
  ulong *Clb, BitMask;

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK dumpentireclb() FATAL: model not been set\n");
    abort(); }

  if (!(BitmapCenter && BitmapGclk     && BitmapClb  && BitmapIobNS &&
        BitmapIobWE  && BitmapPadIobWE && BitmapBram && BitmapDll)) {
    fprintf(stderr, "BUGCHECK dumpentireclb() FATAL: no memory for frames\n");
    abort(); }

  if (Col < 0 || Col > Model->ClbColumns-1) {
    fprintf(stderr, "BUGCHECK dumpentireclb() FATAL: column out of range\n");
    abort(); }

  if (Row < 0 || Row > Model->ClbRows-1) {
    fprintf(stderr, "BUGCHECK dumpentireclb() FATAL: row out of range\n");
    abort(); }

  if (!Outfile) {
    fprintf(stderr, "BUGCHECK dumpentireclb() FATAL: no file to dump to\n");
    abort(); }

  /* frome here on are our VirtexClbFrames of frames */
  Clb = BitmapClb+(Col*Model->MiddleRows+Row)*VirtexClbFrames;

  if (OptFormat == OptLongFormat) {
    /* stats (what we are dumping) */
    fprintf(Outfile, "Col: %i, Row: %i CLB:\n", Col, Row);
    /* number bit columns */
    fprintf(Outfile,
            "   0         1         2         3         4        frame\n");
    fprintf(Outfile,
            "   012345678901234567890123456789012345678901234567 addr\n");
    fprintf(Outfile,
            "  .------------------------------------------------\n"); }

  if (OptFormat == OptShortFormat) {
    /* short stats, just data without headers */
    fprintf(Outfile, "%i/%i:\n", Col, Row); }

  /* actual data lines */
  /* one line per bit, top to bottom, shifting mask */
  BitMask = TopBitMask;

  for (Bit = 0; Bit < VirtexMiddleBits; Bit++) {
    char ClbBits[VirtexClbFrames+1];
    memset(ClbBits, 0, VirtexClbFrames+1);

    if (OptFormat == OptLongFormat) {
      /* number bit rows */
      fprintf(Outfile, "%2i|", Bit); }

    for (FrameNo = 0; FrameNo < VirtexClbFrames; FrameNo++) {
      ClbBits[FrameNo] = (*(Clb+FrameNo) & BitMask) ? '1' : '0'; }
    fprintf(Outfile, "%s\n", ClbBits);

    BitMask >>= 1; }

  if (OptFormat == OptLongFormat) {
    /* footer which completes the header */
    fprintf(Outfile, "bit addr\n"); } }


/* ------ dump group of CLBs to outfile section */

/* just a driver to repeatedly call dumpclb() */

void dumpclbs(void) {

  if (OptOperation == OptVerboseOperation) {
    fprintf(stderr, "dumping LUT and possibly FF data\n"); }

  /* interleave halves of CLBs (Sli) with CLBs (Col) */
  for (Col = FirstCol; Col <= LastCol; Col++) {
    if (OnlySli == SLIBOTH) {
      /* left slice */
      if (!(Col == FirstCol && FirstSli == SLIR)) {
        dumpclbslis(Col, SLIL); }
      /* right slice */
      if (!(Col == LastCol && LastSli == SLIL)) {
        dumpclbslis(Col, SLIR); } }
    else {
      /* cut down to only showing one slice per CLB */
      dumpclbslis(Col, OnlySli); } } }


void dumpclbslis(int Col, int Sli) {

  for (Row = FirstRow; Row <= LastRow; Row++) {
    if (OnlyLut == LUTBOTH) {
      if (!(Row == FirstRow && FirstLut == LUTG)) {
        dumpclb(Col, Sli, Row, LUTF); }
      if (!(Row == LastRow && LastLut == LUTF)) {
        dumpclb(Col, Sli, Row, LUTG); } }
    else {
      dumpclb(Col, Sli, Row, OnlyLut); } } }


/* ------ dump single CLB (actually quater CLB = LC) to outfile section */

/* expects: Model set so that model dependant constants are known */
/*   and also bitstream main frames data read */
/* leaves: same state, but output has lines generated from CLB */

/* CLB layout of the documented LUT and FF bits */
/* derived from the formulas in XAPP151, pages 11 + 12 */

/* . = unknown, 0..9A..F = LUT bits (inverted), XY = FF bits (true sense) */
/*    Slice 1/L                              Slice 0/R        */
/*                                                            */
/*    0         1         2         3         4        frame  */
/*    012345678901234567890123456789012345678901234567 addr   */
/*   .------------------------------------------------        */
/*  0|................................................        */
/*  1|..X.....Y..............................Y.....X.. FFs    */
/*  2|FEDCBA9876543210................0123456789ABCDEF G LUTs */
/*  3|FEDCBA9876543210................0123456789ABCDEF F LUTs */
/*  4|................................................        */
/*  5|................................................        */
/*  6|................................................        */
/*  7|................................................        */
/*  8|................................................        */
/*  9|................................................        */
/* 10|................................................        */
/* 11|................................................        */
/* 12|................................................        */
/* 13|................................................        */
/* 14|................................................        */
/* 15|................................................        */
/* 16|................................................        */
/* 17|................................................        */
/* bit addr                                                   */

/* Xilinx thinks big endian, so we use the first/top/big/MSB 18 bits */
/*   masks for extracting LUT and FF bits from 18 (or 32) */
#define FfBitMask   0x40000000
#define LutGBitMask 0x20000000
#define LutFBitMask 0x10000000

void dumpclb(int Col, int Sli, int Row, int Lut) {

  ulong *Clb, LutBitMask;
  uint LutVal, FfVal;
  int LutFrameNo, FfXFrameNo, FfYFrameNo, FfFrameNo;

  if (Model == NULL || Model->Name == NULL) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: model not been set\n");
    abort(); }

  if (!(BitmapCenter && BitmapGclk     && BitmapClb  && BitmapIobNS &&
        BitmapIobWE  && BitmapPadIobWE && BitmapBram && BitmapDll)) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: no memory for frames\n");
    abort(); }

  if (Col < 0 || Col > Model->ClbColumns-1) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: column out of range\n");
    abort(); }

  if (!(Sli == SLIL || Sli == SLIR)) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: slice out of range\n");
    abort(); }

  if (Row < 0 || Row > Model->ClbRows-1) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: row out of range\n");
    abort(); }

  if (!(Lut == LUTF || Lut == LUTG)) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: LUT out of range\n");
    abort(); }

  if (!Outfile) {
    fprintf(stderr, "BUGCHECK dumpclb() FATAL: no file to dump to\n");
    abort(); }

  /* frome here on are our VirtexClbFrames of frames */
  Clb = BitmapClb+(Col*Model->MiddleRows+Row)*VirtexClbFrames;

  /* which LUT bits we are interested in */
  LutBitMask = (Lut == LUTG) ? LutGBitMask: LutFBitMask;
  /* collect the LUT bits */
  LutVal = 0;
  /* needs an if, because test <= or >= not parameterable */
  if (Sli == SLIR) {
    for (LutFrameNo = 47; LutFrameNo >= 32; LutFrameNo--) {
      LutVal = LutVal<<1 | ((*(Clb+LutFrameNo) & LutBitMask) ? 1 : 0); } }
  else {
    for (LutFrameNo = 0;  LutFrameNo <= 15; LutFrameNo++) {
      LutVal = LutVal<<1 | ((*(Clb+LutFrameNo) & LutBitMask) ? 1 : 0); } }
  /* un-invert the LUT bits, as they are inverted in bitstream */
  LutVal = LutVal^0xFFFF;

  /* which FF bits we are interested in, FfBitMask is a given */
  FfXFrameNo = (Sli == SLIR) ? 45 : 2;
  FfYFrameNo = (Sli == SLIR) ? 39 : 8;
  FfFrameNo = (Lut == LUTG) ? FfYFrameNo : FfXFrameNo;
  FfVal = (*(Clb+FfFrameNo) & FfBitMask) ? 1 : 0;

  if (OptFormat == OptLongFormat) {
    /* long format of line for each LUT/FF pair: */
    /* Col: ccc, Slice: s, Row: rrr, LUT: l, Val: 0xvvvv, FF: f, Val: v */
    /* example for one (bottom left) LUT: */
    /* Col:   0, Slice: L, Row:   0, LUT: F, Val: 0x0000, FF: X, Val: 0 */
    fprintf(Outfile, "Col: %3i, Slice: %1c, Row: %3i",
            Col, (Sli == SLIR) ? 'R' : 'L', Row);
    if (OptLut == OptWithLut || OptFf == OptWithFf) {
      fprintf(Outfile, ", "); }
    if (OptLut == OptWithLut) {
      fprintf(Outfile, "LUT: %1c, Val: 0x%04x",
              (Lut == LUTG) ? 'G' : 'F', LutVal); }
    if (OptLut == OptWithLut && OptFf == OptWithFf) {
      fprintf(Outfile, ", "); }
    if (OptFf == OptWithFf) {
      fprintf(Outfile, "FF: %1c, Val: %d",
              (Lut == LUTG) ? 'Y' : 'X', FfVal); } }

  else if (OptFormat == OptShortFormat) {
    /* short format of line for each LUT/FF pair: */
    /* cccsrrrl: 0xvvvv, f: v */
    /* example for one (bottom left) LUT: */
    /* 000L000F: 0x0000, X: 0 */
    fprintf(Outfile, "%03i%1c%03i",
            Col, (Sli == SLIR) ? 'R' : 'L', Row);
    if (OptLut == OptWithLut) {
      fprintf(Outfile, "%1c: 0x%04x", (Lut == LUTG) ? 'G' : 'F', LutVal); }
    if (OptLut == OptWithLut && OptFf == OptWithFf) {
      fprintf(Outfile, ", "); }
    if (OptFf == OptWithFf) {
      fprintf(Outfile, "%1c: %d", (Lut == LUTG) ? 'Y' : 'X', FfVal); } }

  else {
    /* quiet format of line for each LUT/FF pair: */
    /* 0xvvvv v */
    /* example for one (bottom left) LUT: */
    /* 0x0000 0 */
    if (OptLut == OptWithLut) {
      fprintf(Outfile, "0x%04x", LutVal); }
    if (OptLut == OptWithLut && OptFf == OptWithFf) {
      fprintf(Outfile, " "); }
    if (OptFf == OptWithFf) {
      fprintf(Outfile, "%d", FfVal); } }

  fprintf(Outfile, "\n"); }


/* ------ display LUTs and FFs as graphic section */
/* not used in vd.c, for later in vv.c */

/* format for graphics for each CLB, intended for sqare pixels: */
/* placing the FF underneath the LUT gives actual Virtex 2/3 shape */
/* Sli1 Sli0          */
/*  =L   =R           */
/* ---------          */
/*  YY   YY |  Y FFs  */
/*  YY   YY |         */
/* 0123 0123|  G LUTs */
/* 4567 4567|         */
/* 89AB 89AB|         */
/* CDEF CDEF|         */
/*          |         */
/*  XX   XX |  X FFs  */
/*  XX   XX |         */
/* 0123 0123|  F LUTs */
/* 4567 4567|         */
/* 89AB 89AB|         */
/* CDEF CDEF|         */

