<commit_before>/*************************************************************************
*
* bfload - loads xilinx bitfile into mesa 5i20 board FPGA
*
* Copyright (C) 2007 <NAME> (jmkasunich at fastmail dot fm)
* portions based on m5i20cfg by <NAME>
*
**************************************************************************

This program is free software; you can redistribute it and/or
modify it under the terms of version 2 of the GNU General
Public License as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 USA

THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
harming persons must have provisions for completely removing power
from all motors, etc, before persons enter any danger area.  All
machinery must be designed to comply with local and national safety
codes, and the authors of this software can not, and do not, take
any responsibility for such compliance.

This code was written as part of the EMC HAL project.  For more
information, go to www.linuxcnc.org.

**************************************************************************

Info about programming the 5i20:

Board wiring (related to programming):

 unused				<-- 9030 pin 156, GPIO2, bit  8 reg 0x54
 FPGA pin 104 = DONE		--> 9030 pin 157, GPIO3, bit 11 reg 0x54
 FGPA pin 107 = /INIT		--> 9030 pin 137, GPIO4, bit 14 reg 0x54
 STATUS LED CR11 (low = on)	<-- 9030 pin 136, GPIO5, bit 17 reg 0x54
 unused				<-- 9030 pin 135, GPIO6, bit 20 reg 0x54
 FPGA pin 161 = /WRITE		<-- 9030 pin 134, GPIO7, but 23 reg 0x54
 FPGA pin 106 = /PROGRAM,	<-- 9030 pin  94, GPIO8, bit 26 reg 0x54
 FPGA pin 155 = CCLK		<-> FPGA pin 182 (LCLK 33MHz)
 FPGA pin 160 = /CS		<-- /LWR (local bus write strobe)
 FPGA pin 153 = D0		<-> LAD0 (local data bus)
 FPGA pin 146 = D1		<-> LAD1 (local data bus)
 FPGA pin 142 = D2		<-> LAD2 (local data bus)
 FPGA pin 135 = D3		<-> LAD3 (local data bus)
 FPGA pin 126 = D4		<-> LAD4 (local data bus)
 FPGA pin 119 = D5		<-> LAD5 (local data bus)
 FPGA pin 115 = D6		<-> LAD6 (local data bus)
 FPGA pin 108 = D7		<-> LAD7 (local data bus)

Programming sequence:

 set /PROGRAM low for 300nS minimum (resets chip and starts clearing memory)
 /INIT and DONE go low
 set /PROGRAM high
 wait for /INIT to go high (100uS max, when done clearing memory)
 set /WRITE low
 send data bytes (each byte strobes /CS low)
 the last few bytes in the file are dummies, which provide the clocks
 needed to let the device come out of config mode and begin running
 if a CRC error is detected, /INIT will go low
 DONE will go high during the dummy bytes at the end of the file
 set /WRITE high

**************************************************************************

Info about programming the 5i22:

Board wiring (related to programming):

 FPGA pin R15 = DONE		--> 9054 pin 159, USERI/
 FGPA pin U10 = /INIT		--> not accessible from the PC
 FPGA pin V3  = RD_WR/		<-- pulled low?
 FPGA pin E5  = /PROGRAM,	<-- 9054 pin 154, USERO/
 FPGA pin T15 = CCLK		<-> FPGA pin ??? (LCLK 33MHz???)
 FPGA pin V2  = /CS		<-- /LWR (local bus write strobe)
 FPGA pin T12 = D0		<-> LAD0 (local data bus)
 FPGA pin R12 = D1		<-> LAD1 (local data bus)
 FPGA pin N11 = D2		<-> LAD2 (local data bus)
 FPGA pin P11 = D3		<-> LAD3 (local data bus)
 FPGA pin U9  = D4		<-> LAD4 (local data bus)
 FPGA pin V9  = D5		<-> LAD5 (local data bus)
 FPGA pin R7  = D6		<-> LAD6 (local data bus)
 FPGA pin T7  = D7		<-> LAD7 (local data bus)

Programming sequence:

 set /PROGRAM low for 300nS minimum (resets chip and starts clearing memory)
 /INIT and DONE go low (verify that DONE is low, /INIT is inaccessible)
 set /PROGRAM high
 wait at least 100uS for clearing memory (INIT/ goes high but can't sense it)
 set /WRITE low (maybe already pulled low?)
 send data bytes (each byte strobes /CS low)
 the last few bytes in the file are dummies, which provide the clocks
 needed to let the device come out of config mode and begin running
 if a CRC error is detected, /INIT will go low (can't tell) and DONE will
 not go high.
 DONE will go high during the dummy bytes at the end of the file if all is OK
 set /WRITE high (or maybe leave alone)

*************************************************************************/

//#define _GNU_SOURCE /* getline() */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/stat.h>
#include <sys/types.h>

#include <linux/types.h>

#include "upci.h"
#include "bitfile.h"


/************************************************************************/
#define MASK(x)			(1<<(x))	/* gets a bit in position (x) */
#define CHECK_W(w,x)	((w&MASK(x))==MASK(x))	/* true if bit x in w is set */

/* I/O registers */
#define CTRL_STAT_OFFSET	0x0054	/* 9030 GPIO register (region 1) */


//
// the LAS?BRD registers are in the PLX 9030
// HostMot2 firmware needs the #READY bit (0x2) set in order to work, but
// some older eeproms on the 5i20 (and maybe other cards) dont set them
// right, but we can detect the problem and fix it up
//

#define LAS0BRD_OFFSET 0x28
#define LAS1BRD_OFFSET 0x2C
#define LAS2BRD_OFFSET 0x30
#define LAS3BRD_OFFSET 0x34

#define LASxBRD_READY 0x2


 /* bit number in 9030 GPIO register */
#define GPIO_3_MASK		(1<<11)	/* GPIO 3 */
#define DONE_MASK		(1<<11)	/* GPIO 3 */
#define _INIT_MASK		(1<<14)	/* GPIO 4 */
#define _LED_MASK		(1<<17)	/* GPIO 5 */
#define GPIO_6_MASK		(1<<20)	/* GPIO 6 */
#define _WRITE_MASK		(1<<23)	/* GPIO 7 */
#define _PROGRAM_MASK		(1<<26)	/* GPIO 8 */

/* Exit codes */
#define EC_OK    0   /* Exit OK. */
#define EC_BADCL 100 /* Bad command line. */
#define EC_HDW   101 /* Some sort of hardware failure on the 5I20. */
#define EC_FILE  102 /* File error of some sort. */
#define EC_SYS   103 /* Beyond our scope. */

/* I/O register indices.
*/
#define CTRL_STAT_OFFSET_5I22	0x006C /* 5I22 32-bit control/status register. */

 /* bit number in 9054 GPIO register */
 /* yes, the direction control bits are not in the same order as the I/O bits */
#define DONE_MASK_5I22			(1<<17)	/* GPI */
#define _PROGRAM_MASK_5I22		(1<<16)	/* GPO, active low */
#define DONE_ENABLE_5I22		(1<<18) /* GPI direction control, 1=input */
#define _PROG_ENABLE_5I22		(1<<19) /* GPO direction control, 1=output */

/* how long should we wait for DONE when programming 9054-based cards */
#define DONE_WAIT_5I22			20000

/************************************************************************/


//
// this data structure describes a board we know how to program
//

struct board_info {
    char *board_type;
    char *chip_type;
    unsigned short vendor_id;
    unsigned short device_id;
    unsigned short ss_vendor_id;
    unsigned short ss_device_id;
    int fpga_pci_region;
    int upci_devnum;
    int (*program_funct) (struct board_info *bd, struct bitfile_chunk *ch);
};


//
// these are the functions that actually program the FPGA
//

static int program_5i20_fpga(struct board_info *bd, struct bitfile_chunk *ch);
static int program_5i22_fpga(struct board_info *bd, struct bitfile_chunk *ch);


// 
// this array describes all the boards we know how to program
//

struct board_info board_info_table[] = {

    {
        .board_type = "5i20",
        .chip_type = "2s200pq208",
        .vendor_id = 0x10B5,
        .device_id = 0x9030,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3131,
        .fpga_pci_region = 5,
        .upci_devnum = 0,
        .program_funct = program_5i20_fpga
    },

    {
        .board_type = "5i22-1M",
        .chip_type = "3s1000fg320",
        .vendor_id = 0x10B5,
        .device_id = 0x9054,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3132,
        .fpga_pci_region = 3,
        .upci_devnum = 0,
        .program_funct = program_5i22_fpga
    },

    {
        .board_type = "5i22-1.5M",
        .chip_type = "3s1500fg320",
        .vendor_id = 0x10B5,
        .device_id = 0x9054,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3131,
        .fpga_pci_region = 3,
        .upci_devnum = 0,
        .program_funct = program_5i22_fpga
    }

};


static void errmsg(const char *funct, const char *fmt, ...);
static int parse_cmdline(unsigned argc, char *argv[]);
static __u8 bit_reverse (__u8 data);
static int write_fpga_ram(struct board_info *bd, struct bitfile_chunk *ch);

/* globals to pass data from command line parser to main */
static char *config_file_name;
static int card_number;


struct bitfile *open_bitfile_or_die(char *filename) {
    struct bitfile *bf;
    int r;

    printf ( "Reading '%s'...\n", filename);

    bf = bitfile_read(filename);
    if (bf == NULL) {
	errmsg(__func__, "reading bitstream file '%s'", filename );
	exit(EC_FILE);
    }

    r = bitfile_validate_xilinx_info(bf);
    if (r != 0) {
	errmsg(__func__, "not a valid Xilinx bitfile");
	exit(EC_FILE);
    }

    bitfile_print_xilinx_info(bf);

    return bf;
}


/***********************************************************************/

int main(int argc, char *argv[])
{
    struct upci_dev_info info;
    int tablesize, n, retval;
    struct bitfile *bf;
    struct bitfile_chunk *ch;
    char *chip;
    struct board_info board;

    /* if we are setuid, drop privs until needed */
    seteuid(getuid());
    if ( parse_cmdline(argc, argv) != 0 ) {
	errmsg(__func__,"command line error" );
	return EC_BADCL;
    }

    bf = open_bitfile_or_die(config_file_name);

    /* chunk 'b' has the target device */
    ch = bitfile_find_chunk(bf, 'b', 0);
    chip = (char *)(ch->body);

    /* scan board specs table looking for a board that uses
	the chip for which this bitfile was targeted */
    tablesize = sizeof(board_info_table) / sizeof(struct board_info);
    n = 0;
    while ( strcmp(board_info_table[n].chip_type, chip ) != 0 ) {
	n++;
	if ( n >= tablesize ) {
	    errmsg(__func__,"bitfile is targeted for a '%s' FPGA,\n"
		"                 but no supported board uses that device", ch->body );
	    return EC_FILE;
	}
    }

    /* copy board data from table to local struct */
    board = board_info_table[n];
    printf ( "Board type:      %s\n", board.board_type );

    /* chunk 'e' has the bitstream */
    ch = bitfile_find_chunk(bf, 'e', 0);

    /* now deal with the hardware */
    printf ( "Searching for board...\n" );
    retval = upci_scan_bus();
    if ( retval < 0 ) {
	errmsg(__func__,"PCI bus data missing" );
	return EC_SYS;
    }

    info.vendor_id = board.vendor_id;
    info.device_id = board.device_id;
    info.ss_vendor_id = board.ss_vendor_id;
    info.ss_device_id = board.ss_device_id;
    info.instance = card_number;

    /* find the matching device */
    board.upci_devnum = upci_find_device(&info);
    if ( board.upci_devnum < 0 ) {
	errmsg(__func__, "%s board #%d not found",
	    board.board_type, info.instance );
	return EC_HDW;
    }

    upci_print_device_info(board.upci_devnum);

    printf ( "Loading configuration into %s board...\n", board.board_type );
    retval = board.program_funct(&board, ch);
    if ( retval != 0 ) {
	errmsg(__func__, "configuration did not load");
	return EC_HDW;
    }

    /* do we need to HAL driver data to the FGPA RAM? */
    ch = bitfile_find_chunk(bf, 'r', 0);
    if ( ch != NULL ) {
	/* yes */
	printf ( "Writing data to FPGA RAM\n" );
	retval = write_fpga_ram(&board, ch);
	if ( retval != 0 ) {
	    errmsg(__func__, "RAM data could not be loaded" );
	    return EC_HDW;
	}
    }

    upci_reset();
    printf( "Finished!\n" );
    return 0;
}


void list_devices(void) {
    int r;
    int pci_index;

    int num_pci_devices;
    int num_boards;

    r = upci_scan_bus();
    if (r < 0) {
        errmsg(__func__,"upci error scanning bus");
        exit(EC_SYS);
    }

    num_pci_devices = r;
    num_boards = sizeof(board_info_table) / sizeof(struct board_info);

    // printf("%d pci devices, %d known boards\n", num_pci_devices, num_boards);

    for (pci_index = 0; pci_index < num_pci_devices; pci_index ++) {
        struct upci_dev_info p;
        int board_index;

        upci_get_device_info(&p, pci_index);

        // see if this pci device is in the board table
        for (board_index = 0; board_index < num_boards; board_index ++) {
            struct board_info *board = &board_info_table[board_index];

            if (board->vendor_id != p.vendor_id) continue;
            if (board->device_id != p.device_id) continue;
            if (board->ss_vendor_id != p.ss_vendor_id) continue;
            if (board->ss_device_id != p.ss_device_id) continue;

            printf("%s at PCI %02x:%02x.%x\n", board->board_type, p.bus, p.dev, p.func);
        }
    }

    exit(EC_OK);
}


/************************************************************************/

static void errmsg(const char *funct, const char *fmt, ...)
{
    va_list vp;

    va_start(vp, fmt);
    fprintf(stderr, "ERROR in %s(): ", funct);
    vfprintf(stderr, fmt, vp);
    fprintf(stderr, "\n");
}


static void usage(void) {
    printf("\n");
    printf("bfload <filename> [<card>]\n");
    printf("\n");
    printf("    <filename> - name of bitfile\n");
    printf("    <cardnum>  - card number (default is 0)\n");
    printf("\n");
    printf("Loads an FPGA configuration from a bitfile into a\n");
    printf("Mesa 5i20 or 5i22 FPGA.  If the bitfile contains HAL driver\n");
    printf("config data, writes that data to the FPGA's RAM.\n");
    printf("The type of card is deduced from the FPGA type info in the bitfile.\n");
    printf("Card types are numbered independently\n");
    printf("\n");
}


int program(char *device_type, char *device_id, char *filename) {
    struct bitfile *bf;
    struct board_info *board;
    int board_num;

    int num_boards;
    int i;

    bf = open_bitfile_or_die(filename);

    // find device_type:device_id
    num_boards = sizeof(board_info_table) / sizeof(struct board_info);
    board = NULL;
    for (i = 0; i < num_boards; i ++) {
        if (strcmp(board_info_table[i].board_type, device_type) == 0) {
            board = &board_info_table[i];
            break;
        }
    }
    if (board == NULL) {
        printf("board type '%s' is unknown\n", device_type);
        return -1;
    }

    printf("found board:\n");
    printf("    board_type '%s'\n", board->board_type);
    printf("    chip_type '%s'\n", board->chip_type);
    printf("    PCI ID %04x:%04x %04x:%04x\n", board->vendor_id, board->device_id, board->ss_vendor_id, board->ss_vendor_id);

    if (device_id == NULL) {
        board_num = 0;
    } else {
        char *endp;
        board_num = strtol(device_id, &endp, 0);
        if (*endp != '\0') {
            printf("error parsing board number from '%s'\n", device_id);
            return -1;
        }
    }
    printf("should program %s (%d) with %s\n", device_type, board_num, filename);
    printf("but new-style command-lines are not supported yet\n");

    return -1;
}


// try to execute a new-style programming command
// new style is: CardType[:CardID]=FileName
int parse_program_command(char *cmd) {
    char *filename;

    char *device_type;
    char *device_id;

    struct stat stat_buf;
    int r;

    printf("trying to parse '%s' as a new-style command-line...\n", cmd);

    // first parse out the filename
    filename = strchr(cmd, '=');
    if (filename == NULL) return -1;<commit_msg>locate the requested PCI board<commit_after>/*************************************************************************
*
* bfload - loads xilinx bitfile into mesa 5i20 board FPGA
*
* Copyright (C) 2007 <NAME> (jmkasunich at fastmail dot fm)
* portions based on m5i20cfg by <NAME>
*
**************************************************************************

This program is free software; you can redistribute it and/or
modify it under the terms of version 2 of the GNU General
Public License as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 USA

THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
harming persons must have provisions for completely removing power
from all motors, etc, before persons enter any danger area.  All
machinery must be designed to comply with local and national safety
codes, and the authors of this software can not, and do not, take
any responsibility for such compliance.

This code was written as part of the EMC HAL project.  For more
information, go to www.linuxcnc.org.

**************************************************************************

Info about programming the 5i20:

Board wiring (related to programming):

 unused				<-- 9030 pin 156, GPIO2, bit  8 reg 0x54
 FPGA pin 104 = DONE		--> 9030 pin 157, GPIO3, bit 11 reg 0x54
 FGPA pin 107 = /INIT		--> 9030 pin 137, GPIO4, bit 14 reg 0x54
 STATUS LED CR11 (low = on)	<-- 9030 pin 136, GPIO5, bit 17 reg 0x54
 unused				<-- 9030 pin 135, GPIO6, bit 20 reg 0x54
 FPGA pin 161 = /WRITE		<-- 9030 pin 134, GPIO7, but 23 reg 0x54
 FPGA pin 106 = /PROGRAM,	<-- 9030 pin  94, GPIO8, bit 26 reg 0x54
 FPGA pin 155 = CCLK		<-> FPGA pin 182 (LCLK 33MHz)
 FPGA pin 160 = /CS		<-- /LWR (local bus write strobe)
 FPGA pin 153 = D0		<-> LAD0 (local data bus)
 FPGA pin 146 = D1		<-> LAD1 (local data bus)
 FPGA pin 142 = D2		<-> LAD2 (local data bus)
 FPGA pin 135 = D3		<-> LAD3 (local data bus)
 FPGA pin 126 = D4		<-> LAD4 (local data bus)
 FPGA pin 119 = D5		<-> LAD5 (local data bus)
 FPGA pin 115 = D6		<-> LAD6 (local data bus)
 FPGA pin 108 = D7		<-> LAD7 (local data bus)

Programming sequence:

 set /PROGRAM low for 300nS minimum (resets chip and starts clearing memory)
 /INIT and DONE go low
 set /PROGRAM high
 wait for /INIT to go high (100uS max, when done clearing memory)
 set /WRITE low
 send data bytes (each byte strobes /CS low)
 the last few bytes in the file are dummies, which provide the clocks
 needed to let the device come out of config mode and begin running
 if a CRC error is detected, /INIT will go low
 DONE will go high during the dummy bytes at the end of the file
 set /WRITE high

**************************************************************************

Info about programming the 5i22:

Board wiring (related to programming):

 FPGA pin R15 = DONE		--> 9054 pin 159, USERI/
 FGPA pin U10 = /INIT		--> not accessible from the PC
 FPGA pin V3  = RD_WR/		<-- pulled low?
 FPGA pin E5  = /PROGRAM,	<-- 9054 pin 154, USERO/
 FPGA pin T15 = CCLK		<-> FPGA pin ??? (LCLK 33MHz???)
 FPGA pin V2  = /CS		<-- /LWR (local bus write strobe)
 FPGA pin T12 = D0		<-> LAD0 (local data bus)
 FPGA pin R12 = D1		<-> LAD1 (local data bus)
 FPGA pin N11 = D2		<-> LAD2 (local data bus)
 FPGA pin P11 = D3		<-> LAD3 (local data bus)
 FPGA pin U9  = D4		<-> LAD4 (local data bus)
 FPGA pin V9  = D5		<-> LAD5 (local data bus)
 FPGA pin R7  = D6		<-> LAD6 (local data bus)
 FPGA pin T7  = D7		<-> LAD7 (local data bus)

Programming sequence:

 set /PROGRAM low for 300nS minimum (resets chip and starts clearing memory)
 /INIT and DONE go low (verify that DONE is low, /INIT is inaccessible)
 set /PROGRAM high
 wait at least 100uS for clearing memory (INIT/ goes high but can't sense it)
 set /WRITE low (maybe already pulled low?)
 send data bytes (each byte strobes /CS low)
 the last few bytes in the file are dummies, which provide the clocks
 needed to let the device come out of config mode and begin running
 if a CRC error is detected, /INIT will go low (can't tell) and DONE will
 not go high.
 DONE will go high during the dummy bytes at the end of the file if all is OK
 set /WRITE high (or maybe leave alone)

*************************************************************************/

//#define _GNU_SOURCE /* getline() */

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/stat.h>
#include <sys/types.h>

#include <linux/types.h>

#include "upci.h"
#include "bitfile.h"


/************************************************************************/
#define MASK(x)			(1<<(x))	/* gets a bit in position (x) */
#define CHECK_W(w,x)	((w&MASK(x))==MASK(x))	/* true if bit x in w is set */

/* I/O registers */
#define CTRL_STAT_OFFSET	0x0054	/* 9030 GPIO register (region 1) */


//
// the LAS?BRD registers are in the PLX 9030
// HostMot2 firmware needs the #READY bit (0x2) set in order to work, but
// some older eeproms on the 5i20 (and maybe other cards) dont set them
// right, but we can detect the problem and fix it up
//

#define LAS0BRD_OFFSET 0x28
#define LAS1BRD_OFFSET 0x2C
#define LAS2BRD_OFFSET 0x30
#define LAS3BRD_OFFSET 0x34

#define LASxBRD_READY 0x2


 /* bit number in 9030 GPIO register */
#define GPIO_3_MASK		(1<<11)	/* GPIO 3 */
#define DONE_MASK		(1<<11)	/* GPIO 3 */
#define _INIT_MASK		(1<<14)	/* GPIO 4 */
#define _LED_MASK		(1<<17)	/* GPIO 5 */
#define GPIO_6_MASK		(1<<20)	/* GPIO 6 */
#define _WRITE_MASK		(1<<23)	/* GPIO 7 */
#define _PROGRAM_MASK		(1<<26)	/* GPIO 8 */

/* Exit codes */
#define EC_OK    0   /* Exit OK. */
#define EC_BADCL 100 /* Bad command line. */
#define EC_HDW   101 /* Some sort of hardware failure on the 5I20. */
#define EC_FILE  102 /* File error of some sort. */
#define EC_SYS   103 /* Beyond our scope. */

/* I/O register indices.
*/
#define CTRL_STAT_OFFSET_5I22	0x006C /* 5I22 32-bit control/status register. */

 /* bit number in 9054 GPIO register */
 /* yes, the direction control bits are not in the same order as the I/O bits */
#define DONE_MASK_5I22			(1<<17)	/* GPI */
#define _PROGRAM_MASK_5I22		(1<<16)	/* GPO, active low */
#define DONE_ENABLE_5I22		(1<<18) /* GPI direction control, 1=input */
#define _PROG_ENABLE_5I22		(1<<19) /* GPO direction control, 1=output */

/* how long should we wait for DONE when programming 9054-based cards */
#define DONE_WAIT_5I22			20000

/************************************************************************/


//
// this data structure describes a board we know how to program
//

struct board_info {
    char *board_type;
    char *chip_type;
    unsigned short vendor_id;
    unsigned short device_id;
    unsigned short ss_vendor_id;
    unsigned short ss_device_id;
    int fpga_pci_region;
    int upci_devnum;
    int (*program_funct) (struct board_info *bd, struct bitfile_chunk *ch);
};


//
// these are the functions that actually program the FPGA
//

static int program_5i20_fpga(struct board_info *bd, struct bitfile_chunk *ch);
static int program_5i22_fpga(struct board_info *bd, struct bitfile_chunk *ch);


// 
// this array describes all the boards we know how to program
//

struct board_info board_info_table[] = {

    {
        .board_type = "5i20",
        .chip_type = "2s200pq208",
        .vendor_id = 0x10B5,
        .device_id = 0x9030,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3131,
        .fpga_pci_region = 5,
        .upci_devnum = 0,
        .program_funct = program_5i20_fpga
    },

    {
        .board_type = "5i22-1M",
        .chip_type = "3s1000fg320",
        .vendor_id = 0x10B5,
        .device_id = 0x9054,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3132,
        .fpga_pci_region = 3,
        .upci_devnum = 0,
        .program_funct = program_5i22_fpga
    },

    {
        .board_type = "5i22-1.5M",
        .chip_type = "3s1500fg320",
        .vendor_id = 0x10B5,
        .device_id = 0x9054,
        .ss_vendor_id = 0x10B5,
        .ss_device_id = 0x3131,
        .fpga_pci_region = 3,
        .upci_devnum = 0,
        .program_funct = program_5i22_fpga
    }

};


static void errmsg(const char *funct, const char *fmt, ...);
static int parse_cmdline(unsigned argc, char *argv[]);
static __u8 bit_reverse (__u8 data);
static int write_fpga_ram(struct board_info *bd, struct bitfile_chunk *ch);

/* globals to pass data from command line parser to main */
static char *config_file_name;
static int card_number;


struct bitfile *open_bitfile_or_die(char *filename) {
    struct bitfile *bf;
    int r;

    printf ( "Reading '%s'...\n", filename);

    bf = bitfile_read(filename);
    if (bf == NULL) {
	errmsg(__func__, "reading bitstream file '%s'", filename );
	exit(EC_FILE);
    }

    r = bitfile_validate_xilinx_info(bf);
    if (r != 0) {
	errmsg(__func__, "not a valid Xilinx bitfile");
	exit(EC_FILE);
    }

    bitfile_print_xilinx_info(bf);

    return bf;
}


/***********************************************************************/

int main(int argc, char *argv[])
{
    struct upci_dev_info info;
    int tablesize, n, retval;
    struct bitfile *bf;
    struct bitfile_chunk *ch;
    char *chip;
    struct board_info board;

    /* if we are setuid, drop privs until needed */
    seteuid(getuid());
    if ( parse_cmdline(argc, argv) != 0 ) {
	errmsg(__func__,"command line error" );
	return EC_BADCL;
    }

    bf = open_bitfile_or_die(config_file_name);

    /* chunk 'b' has the target device */
    ch = bitfile_find_chunk(bf, 'b', 0);
    chip = (char *)(ch->body);

    /* scan board specs table looking for a board that uses
	the chip for which this bitfile was targeted */
    tablesize = sizeof(board_info_table) / sizeof(struct board_info);
    n = 0;
    while ( strcmp(board_info_table[n].chip_type, chip ) != 0 ) {
	n++;
	if ( n >= tablesize ) {
	    errmsg(__func__,"bitfile is targeted for a '%s' FPGA,\n"
		"                 but no supported board uses that device", ch->body );
	    return EC_FILE;
	}
    }

    /* copy board data from table to local struct */
    board = board_info_table[n];
    printf ( "Board type:      %s\n", board.board_type );

    /* chunk 'e' has the bitstream */
    ch = bitfile_find_chunk(bf, 'e', 0);

    /* now deal with the hardware */
    printf ( "Searching for board...\n" );
    retval = upci_scan_bus();
    if ( retval < 0 ) {
	errmsg(__func__,"PCI bus data missing" );
	return EC_SYS;
    }

    info.vendor_id = board.vendor_id;
    info.device_id = board.device_id;
    info.ss_vendor_id = board.ss_vendor_id;
    info.ss_device_id = board.ss_device_id;
    info.instance = card_number;

    /* find the matching device */
    board.upci_devnum = upci_find_device(&info);
    if ( board.upci_devnum < 0 ) {
	errmsg(__func__, "%s board #%d not found",
	    board.board_type, info.instance );
	return EC_HDW;
    }

    upci_print_device_info(board.upci_devnum);

    printf ( "Loading configuration into %s board...\n", board.board_type );
    retval = board.program_funct(&board, ch);
    if ( retval != 0 ) {
	errmsg(__func__, "configuration did not load");
	return EC_HDW;
    }

    /* do we need to HAL driver data to the FGPA RAM? */
    ch = bitfile_find_chunk(bf, 'r', 0);
    if ( ch != NULL ) {
	/* yes */
	printf ( "Writing data to FPGA RAM\n" );
	retval = write_fpga_ram(&board, ch);
	if ( retval != 0 ) {
	    errmsg(__func__, "RAM data could not be loaded" );
	    return EC_HDW;
	}
    }

    upci_reset();
    printf( "Finished!\n" );
    return 0;
}


void list_devices(void) {
    int r;
    int pci_index;

    int num_pci_devices;
    int num_boards;

    r = upci_scan_bus();
    if (r < 0) {
        errmsg(__func__,"upci error scanning bus");
        exit(EC_SYS);
    }

    num_pci_devices = r;
    num_boards = sizeof(board_info_table) / sizeof(struct board_info);

    // printf("%d pci devices, %d known boards\n", num_pci_devices, num_boards);

    for (pci_index = 0; pci_index < num_pci_devices; pci_index ++) {
        struct upci_dev_info p;
        int board_index;

        upci_get_device_info(&p, pci_index);

        // see if this pci device is in the board table
        for (board_index = 0; board_index < num_boards; board_index ++) {
            struct board_info *board = &board_info_table[board_index];

            if (board->vendor_id != p.vendor_id) continue;
            if (board->device_id != p.device_id) continue;
            if (board->ss_vendor_id != p.ss_vendor_id) continue;
            if (board->ss_device_id != p.ss_device_id) continue;

            printf("%s at PCI %02x:%02x.%x\n", board->board_type, p.bus, p.dev, p.func);
        }
    }

    exit(EC_OK);
}


/************************************************************************/

static void errmsg(const char *funct, const char *fmt, ...)
{
    va_list vp;

    va_start(vp, fmt);
    fprintf(stderr, "ERROR in %s(): ", funct);
    vfprintf(stderr, fmt, vp);
    fprintf(stderr, "\n");
}


static void usage(void) {
    printf("\n");
    printf("bfload <filename> [<card>]\n");
    printf("\n");
    printf("    <filename> - name of bitfile\n");
    printf("    <cardnum>  - card number (default is 0)\n");
    printf("\n");
    printf("Loads an FPGA configuration from a bitfile into a\n");
    printf("Mesa 5i20 or 5i22 FPGA.  If the bitfile contains HAL driver\n");
    printf("config data, writes that data to the FPGA's RAM.\n");
    printf("The type of card is deduced from the FPGA type info in the bitfile.\n");
    printf("Card types are numbered independently\n");
    printf("\n");
}


int program(char *device_type, char *device_id, char *filename) {
    struct bitfile *bf;
    struct board_info *board;
    int board_num;
    struct upci_dev_info info;

    int num_boards;
    int i, r;

    bf = open_bitfile_or_die(filename);

    // find device_type:device_id
    num_boards = sizeof(board_info_table) / sizeof(struct board_info);
    board = NULL;
    for (i = 0; i < num_boards; i ++) {
        if (strcmp(board_info_table[i].board_type, device_type) == 0) {
            board = &board_info_table[i];
            break;
        }
    }
    if (board == NULL) {
        printf("board type '%s' is unknown\n", device_type);
        return -1;
    }

    printf("found board:\n");
    printf("    board_type '%s'\n", board->board_type);
    printf("    chip_type '%s'\n", board->chip_type);
    printf("    PCI ID %04x:%04x %04x:%04x\n", board->vendor_id, board->device_id, board->ss_vendor_id, board->ss_vendor_id);

    if (device_id == NULL) {
        board_num = 0;
    } else {
        char *endp;
        board_num = strtol(device_id, &endp, 0);
        if (*endp != '\0') {
            printf("error parsing board number from '%s'\n", device_id);
            return -1;
        }
    }


    //
    // find the PCI board
    //

    r = upci_scan_bus();
    if (r < 0) {
	errmsg(__func__, "PCI bus data missing");
	return EC_SYS;
    }

    info.vendor_id = board->vendor_id;
    info.device_id = board->device_id;
    info.ss_vendor_id = board->ss_vendor_id;
    info.ss_device_id = board->ss_device_id;
    info.instance = board_num;

    board->upci_devnum = upci_find_device(&info);
    if (board->upci_devnum < 0) {
	errmsg(__func__, "%s board #%d not found",
	    device_type, board_num);
	return EC_HDW;
    }

    upci_print_device_info(board->upci_devnum);

    printf("but new-style command-lines are not supported yet\n");

    return -1;
}


// try to execute a new-style programming command
// new style is: CardType[:CardID]=FileName
int parse_program_command(char *cmd) {
    char *filename;

    char *device_type;
    char *device_id;

    struct stat stat_buf;
    int r;

    printf("trying to parse '%s' as a new-style command-line...\n", cmd);

    // first parse out the filename
    filename = strchr(cmd, '=');
    if (filename == NULL) return -1;
{'fraction_non_alphanumeric': 0.15000621658585106, 'fraction_numerical': 0.03381822702971528, 'mean_word_length': 3.0392969240426866, 'pattern_counts': {'":': 0, '<': 149, '<?xml version=': 0, '>': 112, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3182404', 'n_tokens_mistral': 11786, 'n_tokens_neox': 10742, 'n_words': 3668}