/*
 * Copyright (c) 2003-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagcam.c 13453 2010-12-02 23:30:08Z jomi.gregory $
 */

/* Endace headers. */
#include "dagapi.h"
#include "dag_platform.h"
#include "dagreg.h"
#include "dagutil.h"
#include "dagclarg.h"

#if HAVE_EDITLINE
#include <editline/readline.h>
#include <histedit.h>
#endif /* HAVE_EDITLINE */

#include <string.h>

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagcam.c 13453 2010-12-02 23:30:08Z jomi.gregory $";
static const char* const kRevisionString = "$Revision: 13453 $";


#define MAXARGV 64
static char *dgetline(char *prompt);

#define DEFAULT_DAGNAME "/dev/dagiom0"
#define DEFAULT_DAGCAM_HISTORY ".dagcam_history"
#define DEFAULT_DAGCAM_RCFILE ".rc_filename_buf"

static char dagnamebuf[DAGNAME_BUFSIZE] = "dag0";;
static char dagname[DAGNAME_BUFSIZE];
static uint8_t* dagiom;
static volatile uint32_t *csr;
static volatile uint32_t *srch;
static volatile uint32_t *regs;

#define COMMAND_BUFSIZE 128
#define FILENAME_BUFSIZE 128
static char command_buf[COMMAND_BUFSIZE] = "";
static char history_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGCAM_HISTORY;
static char rc_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGCAM_RCFILE;

static int dagstream;

static int dagfd;
static int main_return_value = EXIT_SUCCESS;

static int uHistoryCount;
static int uInteractive = 1; /* the default is to go interactive */
static int uHaveReadCommandLine = 0;
static int uReadFromKeyboard = 1;
static int uSaveHistory = 1;

static uint32_t prototype = 0;
static uint32_t wide_bus = 0;
static uint32_t cam_busy = 1;
static char white[] = " \t\n";

enum {
	
	CLA_HELP,
	CLA_VERSION,
	CLA_VERBOSE,
	CLA_DEVICE,
	CLA_INTERACTIVE,
	CLA_HISTFILE,
	CLA_NOHIST,
	CLA_RCFILE,
	CLA_CMDLINE,
	CLA_PROTOTYPE
};

static void
print_version(void)
{
	printf("dagcam (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}
/*****************************************************************************/

static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagcam - Endace DAG Coprocessor SiberCAM shell.\n");
	printf("Usage: dagcam [-hvin] [-d device] [-t histfile] [-r rcfile] [-c cmdline] [file]\n");
	dagclarg_display_usage(clarg, stdout);
}

static void dag_read_history(const char* filename);
static void dag_write_history(const char* filename);
static void dagcam(void);


int
dagcam_main(int argc,  char *argv[])
{
	dag_reg_t cam_register_base_info[DAG_REG_MAX_ENTRIES];
	dag_reg_t cam_search_register_info[DAG_REG_MAX_ENTRIES];
	ClArgPtr clarg;
	FILE *errorfile = NULL;
	int argindex;
	int code;
	int clarg_result;

	dagutil_set_progname("dagcam");

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(dagnamebuf, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", dagnamebuf, strerror(errno));
	}


				/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char* const *) argv);

	dagclarg_add(clarg, "display help (this page).", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	dagclarg_add(clarg, "display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add_string(clarg, "DAG device to use. default: dag0.", "--device", 'd', "device", dagnamebuf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "interactive, even when reading from a file or command line", "--interactive", 'i', CLA_INTERACTIVE);
	dagclarg_add_string(clarg, "read history file (default is .dagcam_history)", "--history", 't', "histfile", history_filename_buf, FILENAME_BUFSIZE, CLA_HISTFILE);
	dagclarg_add(clarg, "no history (turn off reading and writing .dagcam_history)", "--nohistory", 'n', CLA_NOHIST);
	dagclarg_add_string(clarg, "read initial commands from rcfile (default is .rc_filename_buf)", "--rcfile", 'r', "rcfile", rc_filename_buf, FILENAME_BUFSIZE, CLA_RCFILE);
	dagclarg_add_string(clarg, "execute cmdline (instead of stdin processing)", "--cmdline", 'c', "cmdline", command_buf, COMMAND_BUFSIZE, CLA_CMDLINE);
	dagclarg_add(clarg, "prototype", "--ptype", 'p', CLA_PROTOTYPE);

	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;

			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;

			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;

			case CLA_DEVICE:
				if (-1 == dag_parse_name(dagnamebuf, dagname, DAGNAME_BUFSIZE, &dagstream))
				{
					dagutil_panic("dag_parse_name(%s): %s\n", dagnamebuf, strerror(errno));
				}
				break;

			case CLA_INTERACTIVE:
				uInteractive = 1;
				break;

			case CLA_NOHIST:
				uSaveHistory = 0;
				break;

			case CLA_PROTOTYPE:
				prototype = 1;
				break;

			case CLA_HISTFILE:
			case CLA_RCFILE:
			case CLA_CMDLINE:
				break;

			default:
			/* Unknown option. */
				dagutil_error("unknown option %s\n", argv[argindex]); 
				print_usage(clarg);
				return EXIT_FAILURE;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}


	if (-1 == clarg_result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);

		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}



	argv = (char **)dagclarg_get_unprocessed_args(clarg, &argc);

	/*
	 * This is the case when a command line argument is taken
	 * as the input when dagcam running as shell interpreter.
	 */
	if (argc)
	{
		if (freopen(argv[0], "r", stdin) != stdin)
			dagutil_panic("cannot open %s: %s\n", argv[0], strerror(errno));
		argc--;
		argv++;
	}

	if (argc)
		dagutil_warning("excessive command line arguments: '%s'%s\n", argv[0], (argc > 1) ? " and further" : "");

	/* A single command line command to execute. */
	if (strlen(command_buf) > 0)
	{
		uReadFromKeyboard = 1;
		uInteractive = 0;
		uSaveHistory = 0;
	}

	/*
	 * Multiple commands, either file or interactive
	 */
	if (0 == isatty(fileno(stdin)))
	{
		uInteractive = 0;
	}
	
#if HAVE_EDITLINE
	using_history();
#endif /* HAVE_EDITLINE */

	/*dagfd = open(dagname, O_RDWR);
	if (-1 == dagfd)
		dagutil_panic("cannot open %s: %s\n", dagname, strerror(errno));

	dagiom = mmap(NULL, 64 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, dagfd, 0);
	if (dagiom == MAP_FAILED)
		dagutil_panic("mmap %s: %s\n", dagname, strerror(errno));*/

	dagfd = dag_open(dagname);
	if (dagfd < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	dagiom = dag_iom(dagfd);
	if (dagiom == NULL)
	{
		fprintf(stderr, "dagcam main(): dag_iom() failed: %s\n", strerror(errno));
		return 0;
	}

	/* Find the CAM I/O locations. */
	if (dag_reg_find((char*) dagiom, DAG_REG_CAM_MAINT, cam_register_base_info) < 0)
	{
		dagutil_panic("failed to find CAM maintenance registers: %s\n", strerror(errno));
	}

	if (dag_reg_find((char*) dagiom, DAG_REG_CAM_SEARCH, cam_search_register_info) < 0)
	{
		dagutil_panic("failed to find CAM search registers: %s\n", strerror(errno));
	}

	srch = (volatile uint32_t*) (dagiom + cam_search_register_info[0].addr);
	regs = (volatile uint32_t*) (dagiom + cam_register_base_info[0].addr);
	csr = (volatile uint32_t*) (dagiom + cam_register_base_info[0].addr + 0x80);
	wide_bus = ((*csr >> 8) & 0x01) ? 1 : 0;

	/*
	 * Initialization
	 */
	if (1 == uInteractive)
	{
		int safed;
		FILE *fp;

		/* Display short greeting */
		printf("Dagcam alive. Horrible Prototype Hacks %sabled.\n", (prototype) ? "en" : "dis");
		printf("Cam currently in %u-bit mode.\n", (wide_bus + 1) * 18);

		/*
		 * Read in rc file without closing stdin, stdout is untouched.
		 * Since readline is not used here we don't have any problems.
		 */
		safed = dup(STDIN_FILENO);
		if (safed < 0)
			dagutil_panic("cannot duplicate stdin: %s\n", strerror(errno));
		
		if (freopen(rc_filename_buf, "r", stdin) != stdin)
		{
			/*
			 * Probably doesn't exist
			 */
			(void) fclose(stdin);	/* paranoia ? */
		}
		else
		{
			dagcam();
			if (fclose(stdin) < 0)
				dagutil_panic("cannot fclose %s on stdin: %s\n", rc_filename_buf, strerror(errno));
		}
		
		if (dup(safed) != STDIN_FILENO)
			dagutil_panic("cannot replicate stdin: %s\n", strerror(errno));

		fp = fdopen(STDIN_FILENO, "r");
		
		if (fp == NULL)
			dagutil_panic("fdopen on old stdin failed: %s\n", strerror(errno));

		*stdin = *fp; /* interesting to see that this works */
		
#if HAVE_EDITLINE
		clear_history();
#endif /* HAVE_EDITLINE */

		uHaveReadCommandLine = 1;
	}
	else
	{
		uSaveHistory = 0;
	}

	dag_read_history(history_filename_buf);
	
#if HAVE_EDITLINE
	uHistoryCount = where_history();
#endif /* HAVE_EDITLINE */

	dagcam();
	
	dag_write_history(history_filename_buf);

	return EXIT_SUCCESS;
}


static void
dag_read_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	(void) read_history(filename);
#endif /* HAVE_EDITLINE */
}


static void
dag_write_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	if (write_history(filename))
	{
		dagutil_warning("couldn't save history file %s: %s\n", filename, strerror(errno));
	}
#endif /* HAVE_EDITLINE */
}


static uint64_t
mangle_entry(uint64_t ind)
{
	uint64_t outd;

	if (!prototype)
		return ind;

	/* we have to manipulate as follows: */
	/* ind   33  32   8   4  all_other_bits */
	/* outd   8   4  33  32  unchanged      */
	outd = (ind & 0xcfffffeefLL) |	/* unchanged bits */
		((ind & 0x000000010LL) << 28) |	/* SD04 shift to SD32 */
		((ind & 0x000000100LL) << 25) |	/* SD08 shift to SD33 */
		((ind & 0x200000000LL) >> 25) |	/* SD33 shift to SD08 */
		((ind & 0x100000000LL) >> 28);	/* SD32 shift to SD04 */

	if (dagutil_get_verbosity())
		printf("Mangled: in 0x%.9"PRIx64" out 0x%.9"PRIx64"\n", ind, outd);

	return outd;
}


static uint32_t TO_MAX = 1000;


static void
write_csr(uint32_t rd_cnt, uint32_t wr_cnt, uint32_t go)
{
	uint32_t timeout = 1000;
	uint32_t data;

	data = ((rd_cnt & 0x0f) << 24) | ((wr_cnt & 0x0f) << 20) | ((wide_bus & 1) << 8) | ((cam_busy & 1) << 4) | (go & 1);

	dagutil_nanosleep(2000);
	timeout = TO_MAX;
	while (!((*csr >> 13) & 1) && timeout > 0)
	{
		timeout--;
		dagutil_nanosleep(2000);
	}
	if (timeout < 1)
		fprintf(stdout, "*** CSR: Pre Timeout waiting for rdy ***\n");

	*csr = data;
	dagutil_nanosleep(2000);
	if (go & 1)
	{
		timeout = TO_MAX;
		while (!((*csr >> 13) & 1) && timeout > 0)
		{
			timeout--;
			dagutil_nanosleep(2000);
		}
		if (timeout < 1)
			fprintf(stdout, "*** CSR: Post Timeout waiting for rdy ***\n");

		dagutil_nanosleep(2000);
		if (dagutil_get_verbosity() != 0 && (*csr) & 0x08)
		{
			if ((((*csr) >> 16) & 0x0f) != (((*csr) >> 24) & 0x0f))
				fprintf(stdout, "*** CSR: nread %.1x != aread %.1x ***\n", (((*csr) >> 24) & 0x0f), (((*csr) >> 16) & 0x0f));
			printf("CSR_FSM: %c%c%c%c%c TO:%c RDY:%c BV: %c\n", ((*csr) & 0x02) ? 'i' : '_', ((*csr) & 0x04) ? 'b' : '_', ((*csr) & 0x20) ? 'W' : '_', ((*csr) & 0x40) ? 'R' : '_', ((*csr) & 0x80) ? 'w' : '_', ((*csr) & 0x8000) ? '1' : '0', ((*csr) & 0x2000) ? '1' : '0', ((*csr) & 0x1000) ? '1' : '0');
		}
	}

	data = ((wide_bus & 1) << 8) | ((cam_busy & 1) << 4);
	*csr = data;
}


static void
write_reg(uint32_t addr, uint64_t value)
{
	uint32_t lowbits;
	uint32_t highbits;

	addr = (addr & 0x07) * 2;

	lowbits = (uint32_t) (value & 0xffffffffL);
	highbits = (uint32_t) ((value >> 32) & 0x0f);
	/*
	 * XXX: Fails with any of the three nanonaps missing
	 * Fails with all three nanonaps at 200
	 * "looks good" with all three nanonaps at 500
	 */
	dagutil_nanosleep(500);
	*(regs + addr) = lowbits;
	dagutil_nanosleep(500);
	*(regs + addr + 1) = highbits;
	dagutil_nanosleep(500);
}


static void
cmd_help(int argc, char *argv[])
{
	if (argc > 1)
	{							/* any argument prints help message. Lazy parsing. */
		printf("help\n\
	Lists available commands.\n");
		return;
	}
	/*
	 * The beginning of most of these lines are TAB characters.
	 * Please ensure your editor does not chew on the tabs.
	 */
	/*
	 * Wouldn't it be nice if this data fit on one 80x25 screen?
	 * Maybe we need to put pauses every 20 lines for slow readers?
	 */
	printf("\
Usage:\n\
	help         - this page\n\
	quit         - exit program\n\
	csr          - prints the contents of the Control Status Register\n\
	busy         - Set or clear the FPGA cam_busy bit\n\
	regs         - prints the contents of the Result Registers\n\
	reset        - Forces a hardware reset of the CAM. Must be followed with an \"init\" to be useful.\n\
	init         - Initialises the CAM. Performs many operations until the cam is ready for learning and searching.\n\
	init_ip      - Initialises the CAM for operation for IP Filtering.\n\
	invalidate   - Sets all entries in the CAM to be Invalid.\n\
	revision     - return the contents of the Query Revision Register\n\
	{rw}config   - reads/writes the Configuration Register\n\
	{rw}thread   - reads/writes the Thread Configuration Register\n\
	{rw}learn    - reads/writes the Learning Configuration Register\n\
	{rw}gmr      - reads/writes  36-bits of a Global Mask Registers\n\
	{rw}gmr72    - reads/writes  72-bits of a Global Mask Registers\n\
	{rw}gmr144   - reads/writes 144-bits of a Global Mask Registers\n\
	{rw}emr      - reads/writes  36-bits of a Entry Mask Registers\n\
	{rw}emr72    - reads/writes  72-bits of a Entry Mask Registers\n\
	{rw}emr144   - reads/writes 144-bits of a Entry Mask Registers\n\
	{rw}bdr      - reads/writes the Background Data Register\n\
	{rw}nfa      - reads/writes the Next Free Address Register\n\
	{rw}entry    - reads/writes a  36-bit entry in the CAM memory space\n\
	{rw}entry72  - reads/writes a  72-bit entry in the CAM memory space\n\
	{rw}entry144 - reads/writes a 144-bit entry in the CAM memory space\n\
	sentry       - changes the Valid/Invalid flag on one entry in the CAM memory space. Does not alter the data value\n\
	bentry       - burst writes several entries in the CAM memory space with the contents of the Background Data Register\n\
\n\
	{rw}<command> means preface command with either r for read, or w for write.\n\
	\"<command> ?\" returns the arguments needed for that command, e.g.\n\
	\"rentry ?\" will describe how to use the rentry command.\n\
\n\
");
}


static void
cmd_quit(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf(
			"quit\n"
			"Quits the shell and returns you to your mundane existence.\n");
		return;
	}
	printf("\tGoodbye\n");
	exit(EXIT_SUCCESS);
}


static void
cmd_verbose(int argc, char *argv[])
{
	if ((argc > 1 && argv[1][0] == '?') || (argc > 2))
	{
		printf("verbose [n]\n\
	[n] : if n = 1, enable verbosity. if n = 0 disable verbosity.\
	      if n not specified, print current status.\n");
		return;
	}

	if (argc > 1)
	{
		/* set verbosity */
		if (argv[1][0] == '1')
		{
			dagutil_inc_verbosity();
		}
	}
	else
	{
        /* print verbosity */
		printf("\tVerbose Debugging is %s\n", (dagutil_get_verbosity()) ? "Enabled" : "disabled");
	}
}


static void
cmd_busy(int argc, char *argv[])
{
	if ((argc > 1 && argv[1][0] == '?') || (argc > 2))
	{
		printf("busy [n]\n\
	[n] : if n = 1, flag cam as \"busy\". if n = 0 flag cam as not busy.\
	      if n not specified, print current status of the busy flag.\n\
	A \"busy\" cam does not perform searches.\n");
		return;
	}
	if (argc > 1)
	{
		/* set busy */
		cam_busy = (argv[1][0] == '1');

		/* update busy but don't perform any operation */
		write_csr(0, 0, 0);
	}
	else
	{
		/* print busy */
		cam_busy = ((*csr) & 16) ? 1 : 0;
		printf("\tThe cam is%sbusy\n", (cam_busy) ? " " : " not ");
	}
}

static void
cmd_csr(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("csr\n\
	displays the contents of the Control Status Register, pretty-printed\n");
		return;
	}

	printf("*csr\t0x%.8x rst=%u nread=%u nwrite=%u\n", *csr, (*csr >> 31) & 1, (*csr >> 24) & 0xf, (*csr >> 20) & 0xf);
	printf("\ttimo=%u rdy=%u bufvld=%u bus=%s busy=%u\n", (*csr >> 15) & 1, (*csr >> 13) & 1, (*csr >> 12) & 1, ((*csr >> 8) & 1) ? "36b" : "18b", (*csr >> 4) & 1);
	if ((*csr) & 0x08)			/* Debugging Enabled in image */
		printf("\tFSM: %c%c%c%c%c aread=%u\n", ((*csr) & 0x02) ? 'i' : '_', ((*csr) & 0x04) ? 'b' : '_', ((*csr) & 0x20) ? 'W' : '_', ((*csr) & 0x40) ? 'R' : '_', ((*csr) & 0x80) ? 'w' : '_', (*csr >> 16) & 0xf);
}


static void
cmd_regs(int argc, char *argv[])
{
	int i;
	int j;
	uint32_t d;

	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("regs\n\
	displays the contents of the eight Results Registers, containing the\n\
	results from the last read from the CAM.\n");
		return;
	}
	for (i = 0; i < 16; i++)
	{
		d = regs[i];
		for (j = 0; j < 1000; j++)
		{
			if (d != regs[i])
			{
				printf("*** REGS Fail i%i j%i***\n", i, j);
			}
		}
	}

	printf("\t0x%.1x%.8x 0x%.1x%.8x\n", regs[1] & 0x0f, regs[0], regs[3] & 0x0f, regs[2]);
	printf("\t0x%.1x%.8x 0x%.1x%.8x\n", regs[5] & 0x0f, regs[4], regs[7] & 0x0f, regs[6]);
	printf("\t0x%.1x%.8x 0x%.1x%.8x\n", regs[9] & 0x0f, regs[8], regs[11] & 0x0f, regs[10]);
	printf("\t0x%.1x%.8x 0x%.1x%.8x\n", regs[13] & 0x0f, regs[12], regs[15] & 0x0f, regs[14]);
}


static void
cmd_revision(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("revision\n\
	Returns the IEEE 1149.1 TAP ID code, 0x4000222f\n");
		return;
	}

	write_reg(0, 0x0000f0000LL);
	write_csr(1, 1, 1);
	printf("\tREV=0x%.8x\n", regs[0]);
}


static void
cmd_invalidate(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("invalidate\n\
	Marks every entry in the CAM memory space as invalid.\n");
		return;
	}

	write_reg(0, 0x000400000LL);
	write_csr(0, 1, 1);
}


static void
cmd_reset(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("reset\n\
	Issues a hardware reset to the CAM. The contents of any\n\
	Register / Memory Space location after a reset is unpredictable.\n\
	Must be followed by an \"init\" command before the CAM is usable.\n\
	(init also performs a reset, so reset is a kinda pointless operation)\n");
		return;
	}
	*csr = ((1 << 31) | (1 << 4));	/* RST and BUSY */
	wide_bus = 0;
	cam_busy = 1;
}


static void
cmd_init(int argc, char *argv[])
{
	int make_wide = 0;

	if (argc > 1)
	{
		if (argv[1][0] == '3' && argv[1][1] == '6')
		{
			make_wide = 1;
		}
		else
		{
			printf("init [36]\n\
	36 - initialise cam in 36-bit maintenance port mode (default is\n\
	     18-bit maintenance port mode)\n\
	Resets the CAM and loads all registers as appropriate for ATM: 36-bit\
	searches, learn enabled, etc.\n");
			return;
		}
	}

	*csr = ((1 << 31) | (1 << 4));	/* RST and BUSY */
	cam_busy = 1;
	wide_bus = 0;

	/* load config */
	write_reg(0, 0x008000030LL);
	write_reg(1, 0x000000010LL | (make_wide << 3));
	write_csr(0, 2, 1);
	dagutil_nanosleep(6500);				/* paranoia */
	wide_bus = make_wide;

	/* learn config */
	write_reg(0, 0x000020000LL);
	write_reg(1, 0x0000000e0LL);
	write_reg(2, 0x000000000LL);
	write_reg(3, 0x00000ffffLL);
	write_csr(0, 4, 1);

	/* thread config */
	write_reg(0, 0x000040000LL);
	write_reg(1, 0x000000000LL);
	write_csr(0, 2, 1);

	/* init nfa-bist */
	write_reg(0, 0x000f00000LL);
	write_reg(1, 0x000000808LL);
	write_reg(2, 0x10000000bLL);
	write_csr(0, 3, 1);
	dagutil_nanosleep(6500);				/* paranoia */

	/* clear nfa-bist */
	write_reg(0, 0x000f00000LL);
	write_reg(1, 0x000000000LL);
	write_reg(2, 0x000000000LL);
	write_csr(0, 3, 1);

	/* core reset to invalidate entire table */
	write_reg(0, 0x000400000LL);
	write_csr(0, 1, 1);

	/* load EMR[0] to support ATM searches */
	write_reg(0, 0x000220000LL);
	write_reg(1, mangle_entry(0x8fffffff8LL));	/* NNI, usr/mgmnt bit */
	write_csr(0, 2, 1);

	/* load EMR[1] to support ATM NFA Updates */
	write_reg(0, 0x000220001LL);
	write_reg(1, mangle_entry(0x800000000LL));	/* match any entry */
	write_csr(0, 2, 1);
	dagutil_nanosleep(2000);

	/* load GMR[0] to support ATM searches
	 * (GMR[0] is used by firmware) */
	write_reg(0, 0x000200000LL);
	write_reg(1, mangle_entry(0x8fffffff8LL));	/* NNI, usr/mgmnt bit */
	write_csr(0, 2, 1);

	/* load GMR[1] to support ATM NFA Updates
	 * (GMR[1] is used by firmware for this purpose) */
	write_reg(0, 0x000200001LL);
	write_reg(1, mangle_entry(0x800000000LL));	/* match any entry */
	write_csr(0, 2, 1);
	dagutil_nanosleep(2000);

	/* load NFA to support learning for ATM */
	write_reg(0, 0x000280000LL);
	write_reg(1, 0x000000000LL);
	write_csr(0, 2, 1);

	/* Load BDR with the bit pattern needed for NFA update to succeed */
	write_reg(0, 0x000240000LL);
	write_reg(1, mangle_entry(0x800000000LL));
	write_csr(0, 2, 1);
	dagutil_nanosleep(2000);

	/* burst-write BDR to all memory entries */
	write_reg(0, 0x000890001LL);	/* invalid, GMR[1] */
	write_reg(1, 0x000000000LL);	/* saddr = 0 */
	write_reg(2, 0x000010000LL);	/* count = 65536 */
	write_csr(0, 3, 1);
	dagutil_nanosleep(2000);

	/*
	 * CAM should now be all set to run searches on ATM cells
	 */

	/* blank the array */
	write_reg(0, 0x000000000LL);
	write_reg(1, 0x000000000LL);
	write_reg(2, 0x000000000LL);
	write_reg(3, 0x000000000LL);

	/* update busy but don't perform any operation */
	cam_busy = 0;
	write_csr(0, 0, 0);
	dagutil_nanosleep(2000);
}


static void
cmd_init_ip(int argc, char *argv[])
{
	int make_wide = 0;

	if (argc > 1)
	{
		if (argv[1][0] == '3' && argv[1][1] == '6')
		{
			make_wide = 1;
		}
		else
		{
			printf("init [36]\n\
        36 - initialise cam in 36-bit maintenance port mode (default is\n\
             18-bit maintenance port mode)\n\
        Resets the CAM and loads all registers as appropriate for ATM: 36-bit\
        searches, learn enabled, etc.\n");
			return;
		}
	}

	*csr = ((1 << 31) | (1 << 4));	/* RST and BUSY */
	cam_busy = 1;
	wide_bus = 0;

	/* load config */
	write_reg(0, 0x008000030LL);
	write_reg(1, 0x000000410LL);
	write_csr(0, 2, 1);
	dagutil_nanosleep(6500);				/* paranoia */
	wide_bus = make_wide;

	/* thread config */
	write_reg(0, 0x000040000LL);
	write_reg(1, 0x000000000LL);
	write_csr(0, 2, 1);

	/* core reset to invalidate entire table */
	write_reg(0, 0x000400000LL);
	write_csr(0, 1, 1);

	/* Load GMR[0] for 144-bit searches */
	write_reg(0, 0x000200020LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, mangle_entry(0xfffffffffLL));
	write_reg(3, mangle_entry(0xfffffffffLL));
	write_reg(4, mangle_entry(0xfffffffffLL));
	write_csr(0, 5, 1);

	/* Load GMR[1] for 72-bit searches */
	write_reg(0, 0x000200011LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, mangle_entry(0xfffffffffLL));
	write_reg(3, 0x000000000LL);
	write_reg(4, 0x000000000LL);
	write_csr(0, 3, 1);

	/* Load GMR[2] for 36-bit searches */
	write_reg(0, 0x000200002LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, 0x000000000LL);
	write_csr(0, 2, 1);

	/* Load GMR[3] for 36-bit searches */
	write_reg(0, 0x000200003LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_csr(0, 2, 1);

	/* Load GMR[4] for 36-bit searches */
	write_reg(0, 0x000200004LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_csr(0, 2, 1);

	/* Load GMR[8] for 72-bit searches */
	write_reg(0, 0x000200018LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, mangle_entry(0xfffffffffLL));
	write_csr(0, 3, 1);

	/* Load EMR[0] for 144-bit searches */
	write_reg(0, 0x000220020LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, mangle_entry(0xfffffffffLL));
	write_reg(3, mangle_entry(0xfffffffffLL));
	write_reg(4, mangle_entry(0xfffffffffLL));
	write_csr(0, 5, 1);

	/* Load EMR[1] for 72-bit searches */
	write_reg(0, 0x000220011LL);
	write_reg(1, mangle_entry(0xfffffffffLL));
	write_reg(2, mangle_entry(0xfffffffffLL));
	write_reg(3, 0x000000000LL);
	write_reg(4, 0x000000000LL);
	write_csr(0, 3, 1);

	/* Load EMR[2] for 36-bit searches */
	write_reg(0, 0x000220002LL);
	write_reg(1, mangle_entry(0x000fff000LL));
	write_reg(2, 0x000000000LL);
	write_csr(0, 2, 1);

	/*
	 * CAM should now be all set to run searches on ATM cells
	 */

	/* blank the array */
	write_reg(0, 0x000000000LL);
	write_reg(1, 0x000000000LL);
	write_reg(2, 0x000000000LL);
	write_reg(3, 0x000000000LL);
	write_reg(4, 0x000000000LL);

	/* update busy but don't perform any operation */
	cam_busy = 0;
	write_csr(0, 0, 0);
	dagutil_nanosleep(2000);

}


static void
cmd_config(int argc, char *argv[])
{
	uint32_t rw;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (1 + rw) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 2 */
		if (rw)
			printf("wconfig <data>\n\
	data = 36-bit value to write to the Configuration Register.\n");
		else
			printf("rconfig \n\
	displays the 36-bit contents of the Configuration Register\n");
		return;
	}

	if (rw)
		data = strtoull(argv[1], NULL, 16);

	data &= 0xfffffffffLL;

	if (rw)
	{
		/* write config reg */
		write_reg(0, 0x000000030LL);
		write_reg(1, data);
		write_csr(0, 2, 1);
		dagutil_nanosleep(6500);			/* paranoia */

		wide_bus = (data & 8) ? 1 : 0;
	}
	else
	{
		/* read config reg */
		write_reg(0, 0x000010000LL);
		write_csr(1, 1, 1);
		printf("\tConfig =0x%.1x%.8x\n", regs[1] & 0x0f, regs[0]);
	}
}


static void
cmd_thread(int argc, char *argv[])
{
	uint32_t rw;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (1 + rw) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 2 */
		if (rw)
			printf("wthread <data>\n\
	data = 36-bit value to write to the Thread Configuration Register.\n");
		else
			printf("rthread \n\
	displays the 36-bit contents of the Thread Configuration Register\n");
		return;
	}

	if (rw)
		data = strtoull(argv[1], NULL, 16);

	data &= 0xfffffffffLL;

	if (rw)
	{
		/* write thread reg */
		write_reg(0, 0x000040000LL);
		write_reg(1, data);
		write_csr(0, 2, 1);
	}
	else
	{
		/* read thread reg */
		write_reg(0, 0x000050000LL);
		write_csr(1, 1, 1);
		printf("\tThread =0x%.1x%.8x\n", regs[1] & 0x0f, regs[0]);
	}
}


static void
cmd_learn(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t saddr = 0;
	uint32_t faddr = 0;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (1 + (rw * 3)) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 4 */
		if (rw)
			printf("wlearn <data> <saddr> <faddr>\n\
	data = 36-bit value to write to the Learning Configuration Register\n\
	saddr = 16-bit value to use as the Learning Start Address\n\
	faddr = 16-bit value to use as the Learning Finish Address\n\
	In operation, learning will occur for all address from saddr to\n\
	faddr, inclusive.\n");
		else
			printf("rlearn \n\
	displays the 36-bit contents of the Learning Configuration Register,\n\
	the 16-bit contents of the Learning Start Address (saddr)\n\
	and the 16-bit contents of the Learning Finish Address (faddr)\n\
	In operation, learning will occur for all address from saddr to\n\
	faddr, inclusive.\n");
		return;
	}

	if (rw)
	{
		data = strtoull(argv[1], NULL, 16);
		saddr = strtoull(argv[2], NULL, 16);
		faddr = strtoull(argv[3], NULL, 16);
	}

	data &= 0xfffffffffLL;
	saddr &= 0xffff;
	faddr &= 0xffff;

	if (rw)
	{
		/* write learn reg */
		write_reg(0, 0x000020000LL);
		write_reg(1, data);
		write_reg(2, saddr);
		write_reg(3, faddr);
		write_csr(0, 4, 1);
	}
	else
	{
		/* read learn reg */
		write_reg(0, 0x000030000LL);
		write_csr(3, 1, 1);
		printf("\tLearn  =0x%.1x%.8x saddr=0x%.4x faddr=0x%.4x\n", regs[1] & 0x0f, regs[0], regs[2] & 0xffff, regs[4] & 0xffff);
	}
}


static void
cmd_gmr(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw) || argv[1][0] == '?')
	{							/* read needs 2, write needs 3 */
		if (rw)
			printf("wgmr <addr> <data>\n\
	addr = 4-bit value selecting which GMR to write to\n\
	data = 36-bit value to write to the selected Global Mask Register\n");
		else
			printf("rgmr <addr> \n\
	addr = 4-bit value selecting which GMR to read from\n\
	displays the 36-bit contents of the selected Global Mask Register\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
		data = strtoull(argv[2], NULL, 16);

	addr &= 0xf;
	data &= 0xfffffffffLL;
	data = mangle_entry(data);

	if (rw)
	{
		/* write gmr */
		write_reg(0, 0x000200000LL | addr);
		write_reg(1, data);
		write_csr(0, 2, 1);
	}
	else
	{
		/* read gmr */
		write_reg(0, 0x000210000LL | addr);
		write_csr(1, 1, 1);
		data = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data = mangle_entry(data);
		printf("\tGMR[%2u]=0x%.9"PRIx64"\n", addr, data);
	}
}


static void
cmd_gmr72(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data_0 = 0;
	uint64_t data_1 = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw * 2) || argv[1][0] == '?')
	{							
        /* read needs 2, write needs 4 */
		if (rw)
		{
			printf("wgmr72 <addr> <data_0> <data_1>\n\
	addr = 4-bit value selecting which GMR to write to\n\
	data_0 = Lower 36-bit value to write to the selected Entry Mask Register\n\
	data_1 = Upper 36-bit value to write to the selected Entry Mask Register\n");
		}
		else
		{
			printf("rgmr72 <addr> \n\
	addr = 4-bit value selecting which GMR to read from\n\
	displays the two 36-bit contents of the selected Entry Mask Register\n");
		}

		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
	{
		data_0 = strtoull(argv[2], NULL, 16);
		data_1 = strtoull(argv[3], NULL, 16);
	}

	addr &= 0xf;
	data_0 &= 0xfffffffffLL;
	data_1 &= 0xfffffffffLL;
	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);

	if (rw)
	{
		/* write gmr */
		write_reg(0, 0x000220010LL | addr);
		write_reg(1, data_0);
		write_reg(2, data_1);
		write_csr(0, 3, 1);
	}
	else
	{
		/* read gmr */
		write_reg(0, 0x000230010LL | addr);
		write_csr(2, 1, 1);
		data_0 = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data_1 = ((uint64_t) (regs[3] & 0x0f) << 32) | regs[2];
		data_0 = mangle_entry(data_0);
		data_1 = mangle_entry(data_1);
		printf("\tGMR72[%2u]=0x%.9"PRIx64" 0x%.9"PRIx64"\n", addr, data_0, data_1);
	}
}


static void
cmd_gmr144(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data_0 = 0;
	uint64_t data_1 = 0;
	uint64_t data_2 = 0;
	uint64_t data_3 = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw * 4) || argv[1][0] == '?')
	{							/* read needs 2, write needs 6 */
		if (rw)
			printf("wgmr144 <addr> <data_0> <data_1> <data_2> <data_3>\n\
	addr = 4-bit value selecting which GMR to write to\n\
	data_0 = Lower  36-bit value to write to the selected Entry Mask Register\n\
	data_1 = Second 36-bit value to write to the selected Entry Mask Register\n\
	data_2 = Third  36-bit value to write to the selected Entry Mask Register\n\
	data_3 = Upper  36-bit value to write to the selected Entry Mask Register\n");
		else
			printf("rgmr144 <addr> \n\
	addr = 4-bit value selecting which GMR to read from\n\
	displays the four 36-bit contents of the selected Entry Mask Register\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
	{
		data_0 = strtoull(argv[2], NULL, 16);
		data_1 = strtoull(argv[3], NULL, 16);
		data_2 = strtoull(argv[4], NULL, 16);
		data_3 = strtoull(argv[5], NULL, 16);
	}

	addr &= 0xf;
	data_0 &= 0xfffffffffLL;
	data_1 &= 0xfffffffffLL;
	data_2 &= 0xfffffffffLL;
	data_3 &= 0xfffffffffLL;
	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);
	data_2 = mangle_entry(data_2);
	data_3 = mangle_entry(data_3);

	if (rw)
	{
		/* write gmr */
		write_reg(0, 0x000220020LL | addr);
		write_reg(1, data_0);
		write_reg(2, data_1);
		write_reg(3, data_2);
		write_reg(4, data_3);
		write_csr(0, 5, 1);
	}
	else
	{
		/* read gmr */
		write_reg(0, 0x000230020LL | addr);
		write_csr(4, 1, 1);
		data_0 = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data_1 = ((uint64_t) (regs[3] & 0x0f) << 32) | regs[2];
		data_2 = ((uint64_t) (regs[5] & 0x0f) << 32) | regs[4];
		data_3 = ((uint64_t) (regs[7] & 0x0f) << 32) | regs[6];
		data_0 = mangle_entry(data_0);
		data_1 = mangle_entry(data_1);
		data_2 = mangle_entry(data_2);
		data_3 = mangle_entry(data_3);
		printf("\tGMR144[%2u]=0x%.9"PRIx64" 0x%.9"PRIx64" 0x%.9"PRIx64" 0x%.9"PRIx64"\n", addr, data_0, data_1, data_2, data_3);
	}
}


static void
cmd_emr(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw) || argv[1][0] == '?')
	{							/* read needs 2, write needs 3 */
		if (rw)
			printf("wemr <addr> <data>\n\
	addr = 4-bit value selecting which EMR to write to\n\
	data = 36-bit value to write to the selected Entry Mask Register\n");
		else
			printf("remr <addr> \n\
	addr = 4-bit value selecting which EMR to read from\n\
	displays the 36-bit contents of the selected Entry Mask Register\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
		data = strtoull(argv[2], NULL, 16);

	addr &= 0xf;
	data &= 0xfffffffffLL;
	data = mangle_entry(data);

	if (rw)
	{
		/* write emr */
		write_reg(0, 0x000220000LL | addr);
		write_reg(1, data);
		write_csr(0, 2, 1);
	}
	else
	{
		/* read emr */
		write_reg(0, 0x000230000LL | addr);
		write_csr(1, 1, 1);
		data = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		/* data =  (uint64_t)regs[0]; */
		/* data = mangle_entry( data ); */
		printf("\tEMR[%2u]=0x%.9"PRIx64"\n", addr, data);
	}
}


static void
cmd_emr72(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data_0 = 0;
	uint64_t data_1 = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw * 2) || argv[1][0] == '?')
	{							/* read needs 2, write needs 4 */
		if (rw)
			printf("wemr72 <addr> <data_0> <data_1>\n\
	addr = 4-bit value selecting which EMR to write to\n\
	data_0 = Lower 36-bit value to write to the selected Entry Mask Register\n\
	data_1 = Upper 36-bit value to write to the selected Entry Mask Register\n");
		else
			printf("remr72 <addr> \n\
	addr = 4-bit value selecting which EMR to read from\n\
	displays the two 36-bit contents of the selected Entry Mask Register\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
	{
		data_0 = strtoull(argv[2], NULL, 16);
		data_1 = strtoull(argv[3], NULL, 16);
	}

	addr &= 0xf;
	data_0 &= 0xfffffffffLL;
	data_1 &= 0xfffffffffLL;
	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);

	if (rw)
	{
		/* write emr */
		write_reg(0, 0x000220010LL | addr);
		write_reg(1, data_0);
		write_reg(2, data_1);
		write_csr(0, 3, 1);
	}
	else
	{
		/* read emr */
		write_reg(0, 0x000230010LL | addr);
		write_csr(2, 1, 1);
		data_0 = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data_1 = ((uint64_t) (regs[3] & 0x0f) << 32) | regs[2];
		data_0 = mangle_entry(data_0);
		data_1 = mangle_entry(data_1);
		printf("\tEMR72[%2u]=0x%.9"PRIx64" 0x%.9"PRIx64"\n", addr, data_0, data_1);
	}
}

static void
cmd_emr144(int argc, char *argv[])
{
	uint32_t rw;
	uint32_t addr = 0;
	uint64_t data_0 = 0;
	uint64_t data_1 = 0;
	uint64_t data_2 = 0;
	uint64_t data_3 = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (2 + rw * 4) || argv[1][0] == '?')
	{							/* read needs 2, write needs 6 */
		if (rw)
			printf("wemr144 <addr> <data_0> <data_1> <data_2> <data_3>\n\
	addr = 4-bit value selecting which EMR to write to\n\
	data_0 = Lower  36-bit value to write to the selected Entry Mask Register\n\
	data_1 = Second 36-bit value to write to the selected Entry Mask Register\n\
	data_2 = Third  36-bit value to write to the selected Entry Mask Register\n\
	data_3 = Upper  36-bit value to write to the selected Entry Mask Register\n");
		else
			printf("remr144 <addr> \n\
	addr = 4-bit value selecting which EMR to read from\n\
	displays the four 36-bit contents of the selected Entry Mask Register\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);
	if (rw)
	{
		data_0 = strtoull(argv[2], NULL, 16);
		data_1 = strtoull(argv[3], NULL, 16);
		data_2 = strtoull(argv[4], NULL, 16);
		data_3 = strtoull(argv[5], NULL, 16);
	}

	addr &= 0xf;
	data_0 &= 0xfffffffffLL;
	data_1 &= 0xfffffffffLL;
	data_2 &= 0xfffffffffLL;
	data_3 &= 0xfffffffffLL;
	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);
	data_2 = mangle_entry(data_2);
	data_3 = mangle_entry(data_3);

	if (rw)
	{
		/* write emr */
		write_reg(0, 0x000220020LL | addr);
		write_reg(1, data_0);
		write_reg(2, data_1);
		write_reg(3, data_2);
		write_reg(4, data_3);
		write_csr(0, 5, 1);
	}
	else
	{
		/* read emr */
		write_reg(0, 0x000230020LL | addr);
		write_csr(4, 1, 1);
		data_0 = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data_1 = ((uint64_t) (regs[3] & 0x0f) << 32) | regs[2];
		data_2 = ((uint64_t) (regs[5] & 0x0f) << 32) | regs[4];
		data_3 = ((uint64_t) (regs[7] & 0x0f) << 32) | regs[6];
		data_0 = mangle_entry(data_0);
		data_1 = mangle_entry(data_1);
		data_2 = mangle_entry(data_2);
		data_3 = mangle_entry(data_3);
		printf("\tEMR144[%2u]=0x%.9"PRIx64" 0x%.9"PRIx64" 0x%.9"PRIx64" 0x%.9"PRIx64"\n", addr, data_0, data_1, data_2, data_3);
	}
}

static void
cmd_bdr(int argc, char *argv[])
{
	uint32_t rw;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (1 + rw) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 2 */
		if (rw)
			printf("wbdr <data>\n\
	data = 36-bit value to write to the Background Data Register.\n");
		else
			printf("rbdr \n\
	displays the 36-bit contents of the Background Data Register\n");
		return;
	}

	if (rw)
		data = strtoull(argv[1], NULL, 16);

	data &= 0xfffffffffLL;
	data = mangle_entry(data);

	if (rw)
	{
		/* write bdr */
		write_reg(0, 0x000240000LL);
		write_reg(1, data);
		write_csr(0, 2, 1);
	}
	else
	{
		/* read bdr */
		write_reg(0, 0x000250000LL);
		write_csr(1, 1, 1);
		data = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
		data = mangle_entry(data);
		printf("\tBDR   =0x%.9"PRIx64"\n", data);
	}

}

static void
cmd_nfa(int argc, char *argv[])
{
	uint32_t rw;
	uint64_t data = 0;

	rw = (argv[0][0] == 'w');

	if (argc < (1 + rw) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 2 */
		if (rw)
			printf("wnfa <data>\n\
	data = 16-bit value to write to the Next Free Address Register.\n");
		else
			printf("rnfa \n\
	displays the 16-bit contents of the Next Free Address Register\n");
		return;
	}

	if (rw)
		data = strtoull(argv[1], NULL, 16);
	data &= 0xffffLL;

	if (rw)
	{
		/* write nfa */
		write_reg(0, 0x000280000LL);
		write_reg(1, data);
		write_csr(0, 2, 1);
	}
	else
	{
		/* read nfa */
		write_reg(0, 0x000290000LL);
		write_csr(1, 1, 1);
		printf("\tNFA   =0x%.4x\n", regs[0] & 0xffff);
	}

}

static void
cmd_bentry(int argc, char *argv[])
{
	int valid;
	uint32_t mask;
	uint32_t addr;
	uint32_t cnt;

	if (argc < 5 || argv[1][0] == '?')
	{
		printf("bentry <valid> <mask> <addr> <count>\n\
	valid = either \"valid\" or \"invalid\" to set the altered entries\n\
	to Valid or Invalid respectively. May be shortened to 'v' or 'i'\n\
	mask = 4-bit value selecting which EMR to mask the BDR against\n\
	addr = 16-bit value selecting which memory location to start at\n\
	count = Number of entries to write, 1 to 65536 inclusive\n");
		return;
	}

	valid = (argv[1][0] == 'v');
	mask = strtoul(argv[2], NULL, 16);
	addr = strtoul(argv[3], NULL, 16);
	cnt = strtoul(argv[4], NULL, 16);

	mask &= 0xf;				/* 4 bits */
	addr &= 0xffff;				/* 16 bits */
	if (cnt > 65536)
		cnt = 65536;

	if (cnt > 0)
	{							/* no point writing zero entries */
		write_reg(0, 0x000890000LL | (valid << 8) | mask);
		write_reg(1, addr);
		write_reg(2, cnt);
		write_csr(0, 3, 1);
	}
}


static void
cmd_sentry(int argc, char *argv[])
{
	int valid;
	uint32_t addr;

	if (argc < 3 || argv[1][0] == '?')
	{
		printf("sentry <valid> <addr>\n\
	valid = either \"valid\" or \"invalid\" to set the altered entry\n\
	to Valid or Invalid respectively. May be shortened to 'v' or 'i'\n\
	addr = 16-bit value selecting which memory location to alter\n\
	Marks the selected CAM Memory entry as either Valid or Invalid.\n\
	Does not alter the 36-bit mask or data values at that location.\n");
		return;
	}

	valid = (argv[1][0] == 'v');
	addr = strtoul(argv[2], NULL, 16);

	addr &= 0xffff;				/* 16 bits */

	write_reg(0, 0x000820000LL | (valid << 8));
	write_reg(1, addr);
	write_csr(0, 2, 1);
}


static void
cmd_wentry(int argc, char *argv[])
{
	int valid;
	uint32_t mask;
	uint32_t addr;
	uint64_t data;

	if (argc < 5 || argv[1][0] == '?')
	{
		printf("wentry <valid> <mask> <addr> <data>\n\
	valid = either \"valid\" or \"invalid\" to set the altered entry\n\
	to Valid or Invalid respectively. May be shortened to 'v' or 'i'\n\
	mask = 4-bit value selecting which EMR to mask the data against\n\
	addr = 16-bit value selecting which memory location to write to\n\
	data = 36-bit value to store at the selected memory location\n");
		return;
	}

	valid = (argv[1][0] == 'v');
	mask = strtoul(argv[2], NULL, 16);
	addr = strtoul(argv[3], NULL, 16);
	data = strtoull(argv[4], NULL, 16);

	mask &= 0xf;				/* 4 bits */
	addr &= 0xffff;				/* 16 bits */
	data &= 0xfffffffffLL;		/* 36 bits */

	data = mangle_entry(data);

	write_reg(0, 0x000800000LL | (valid << 8) | mask);
	write_reg(1, addr);
	write_reg(2, data);
	write_csr(0, 3, 1);
}


static void
cmd_wentry72(int argc, char *argv[])
{
	int valid;
	uint32_t mask;
	uint32_t addr;
	uint64_t data_0;
	uint64_t data_1;

	if (argc < 6 || argv[1][0] == '?')
	{
		printf("wentry <valid> <mask> <addr> <data>\n\
        valid = either \"valid\" or \"invalid\" to set the altered entry\n\
        to Valid or Invalid respectively. May be shortened to 'v' or 'i'\n\
        mask = 4-bit value selecting which EMR to mask the data against\n\
        addr = 16-bit value selecting which memory location to write to\n\
        data = 36-bit value to store at the selected memory location\n");
		return;
	}

	valid = (argv[1][0] == 'v');
	mask = strtoul(argv[2], NULL, 16);
	addr = strtoul(argv[3], NULL, 16);
	data_0 = strtoull(argv[4], NULL, 16);
	data_1 = strtoull(argv[5], NULL, 16);

	mask &= 0xf;				/*  4 bits */
	addr &= 0xffff;				/* 16 bits */
	data_0 &= 0xfffffffffLL;	/* 36 bits */
	data_1 &= 0xfffffffffLL;	/* 36 bits */

	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);

	write_reg(0, 0x000800010LL | (valid << 8) | mask);
	write_reg(1, addr);
	write_reg(2, data_0);
	write_reg(3, data_1);
	write_csr(0, 4, 1);
}


static void
cmd_wentry144(int argc, char *argv[])
{
	int valid;
	uint32_t mask;
	uint32_t addr;
	uint64_t data_0;
	uint64_t data_1;
	uint64_t data_2;
	uint64_t data_3;

	if (argc < 8 || argv[1][0] == '?')
	{
		printf("wentry <valid> <mask> <addr> <data>\n\
        valid = either \"valid\" or \"invalid\" to set the altered entry\n\
        to Valid or Invalid respectively. May be shortened to 'v' or 'i'\n\
        mask = 4-bit value selecting which EMR to mask the data against\n\
        addr = 16-bit value selecting which memory location to write to\n\
        data = 36-bit value to store at the selected memory location\n");
		return;
	}

	valid = (argv[1][0] == 'v');
	mask = strtoul(argv[2], NULL, 16);
	addr = strtoul(argv[3], NULL, 16);
	data_0 = strtoull(argv[4], NULL, 16);
	data_1 = strtoull(argv[5], NULL, 16);
	data_2 = strtoull(argv[6], NULL, 16);
	data_3 = strtoull(argv[7], NULL, 16);

	mask &= 0xf;				/*  4 bits */
	addr &= 0xffff;				/* 16 bits */
	data_0 &= 0xfffffffffLL;	/* 36 bits */
	data_1 &= 0xfffffffffLL;	/* 36 bits */
	data_2 &= 0xfffffffffLL;	/* 36 bits */
	data_3 &= 0xfffffffffLL;	/* 36 bits */

	data_0 = mangle_entry(data_0);
	data_1 = mangle_entry(data_1);
	data_2 = mangle_entry(data_2);
	data_3 = mangle_entry(data_3);

	write_reg(0, 0x000800020LL | (valid << 8) | mask);
	write_reg(1, addr);
	write_reg(2, data_0);
	write_reg(3, data_1);
	write_reg(4, data_2);
	write_reg(5, data_3);
	write_csr(0, 6, 1);
}


static void
cmd_rentry(int argc, char *argv[])
{
	uint32_t tag;
	uint32_t valid;
	uint32_t addr;
	uint64_t data;
	uint64_t mask;

	if (argc < 2 || argv[1][0] == '?')
	{
		printf("rentry <addr>\n\
	addr = 16-bit value selecting which memory location to read from\n\
	displays the 36-bit contents of the given CAM memory space location,\n\
	including the Valid flag and the 36-bit mask in use.\n");
		return;
	}

	addr = strtoul(argv[1], NULL, 16);

	addr &= 0xffff;				/* 16 bits */

	/* First, Valid */
	write_reg(0, 0x000830400LL);
	write_reg(1, addr);
	write_csr(1, 2, 1);
	dagutil_nanosleep(2000);
	valid = regs[0] & 1;
	tag = (regs[0] >> 1) & 3;
	dagutil_nanosleep(2000);

	/* Second, Mask */
	write_reg(0, 0x000830200LL);
	write_reg(1, addr);
	write_csr(1, 2, 1);
	dagutil_nanosleep(2000);
	mask = (regs[1] & 0x0f);
	dagutil_nanosleep(2000);
	mask = (mask << 32) | regs[0];
	mask = mangle_entry(mask);
	dagutil_nanosleep(2000);

	/* Third, Data */
	write_reg(0, 0x000830000LL);
	write_reg(1, addr);
	write_csr(1, 2, 1);
	dagutil_nanosleep(2000);
	data = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
	data = mangle_entry(data);

	/* Pretty-print all aspects of the entry read */
	printf("\tEntry[%.4x]=(%s, mask: 0x%.9"PRIx64", data: 0x%.9"PRIx64" tag: %u)\n", addr, (valid) ? "  valid" : "invalid", mask, data, tag & 3);
}


static uint32_t
i1_srch(uint64_t data, uint32_t op, uint32_t mask, uint32_t thdi)
{
	uint32_t d1;
	uint32_t t1;

	mask &= 0xf;				/* 4 bits */
	op &= 0xf;					/* 4 bits */
	thdi &= 0x3;				/* 2 bits */
	t1 = (mask << 8) | (op << 4) | (uint32_t) ((data >> 32) & 0x0f);
	if (!prototype)
		t1 |= (thdi << 12);
	d1 = data & 0xffffffffL;

	/* XXXX: July 2003
	 * Compiler likes to re-order these operations, collapsing the
	 * above 'tmp' from one 32-bit write to several 16 or 8 bit writes.
	 * Sticking in dagutil_nanosleeps stops it doing this. ha! take that!
	 * BAD compiler.
	 */
	dagutil_nanosleep(500);
	srch[0] = d1;
	dagutil_nanosleep(500);
	srch[1] = t1;
	dagutil_nanosleep(500);
	srch[2] = 0x01L;
	dagutil_nanosleep(2000);

	return srch[2];
}


enum
{
	BIT_VALID = 0x800,
	BIT_HIT = 0x040,
	BIT_MULT = 0x020,
	BIT_FULL = 0x010,
	BIT_NTBL = 0x008,
	BIT_NFAD = 0x004,
	BIT_DELD = 0x002,
	BIT_LRND = 0x001
};


static uint32_t
i2_srch(uint64_t data, uint32_t op, uint32_t mask, uint32_t thdi, uint32_t addr, uint32_t must_set, uint32_t must_clr, char *msg, uint32_t must_vld, uint32_t exp_thdo)
{
	uint32_t res;
	uint32_t res_addr;
	uint32_t ret = 0;
	char *test[] = { "LRND", "DELD", "NFAD", "NTBL",
		"FULL", "MULT", "HIT"
	};
	int i;

	res = i1_srch(data, op, mask, thdi);
	res_addr = (res >> 16);

	/* zeroth, sanity */
	if ((must_set | must_clr) != 0x7f)
	{
		fprintf(stdout, "Warning %10s: Bit patterns are incomplete. Missing: 0x%2x\n", msg, (~(must_set | must_clr)) & 0x7f);
	}

	/* first, expectations on valid */
	if (must_vld && 0 == (res & BIT_VALID))
	{
		fprintf(stdout, "Error %10s: VALID expected,     iaddr=%u\n", msg, addr);
		++ret;
	}
	if (0 == must_vld && (res & BIT_VALID))
	{
		fprintf(stdout, "Error %10s: VALID not expected, iaddr=%u\n", msg, addr);
		++ret;
	}
	if (0 == (res & BIT_VALID))	/* can't do any more tests on an invalid reply */
		return ret;

	/* second, check each result bit against expectations */
	for (i = 6; i >= 0; i--)
	{
		if ((must_set & (1 << i)) && 0 == (res & (1 << i)))
		{
			fprintf(stdout, "Error %10s: %5s expected,     iaddr=0x%.4x, data=0x%.9"PRIx64", caddr=0x%.4x\n", msg, test[i], addr, data, res_addr);
			++ret;
		}
		if ((must_clr & (1 << i)) && (res & (1 << i)))
		{
			fprintf(stdout, "Error %10s: %5s not expected, iaddr=0x%.4x, data=0x%.9"PRIx64", caddr=0x%.4x\n", msg, test[i], addr, data, res_addr);
			++ret;
		}
	}

	/* third, non-prototype, check THDO against expectation */
	if (prototype)				// debug: always skip THDO test.
		return ret;

	if ((res & 0x0780) >> 7 != exp_thdo)
	{
		fprintf(stdout, "Error %10s:  THDO exp: %x got %x, iaddr=0x%.4x, data=0x%.9"PRIx64", caddr=0x%.4x\n", msg, exp_thdo, (res & 0x0780) >> 7, addr, data, res_addr);
		++ret;
	}

	return ret;
}


static void
cmd_search(int argc, char *argv[])
{
	uint32_t op;
	uint32_t mask;
	uint32_t thdi;
	uint64_t data;
	uint32_t res;


	if (argc < 4 || argv[1][0] == '?')
	{
		printf("search <op> <mask> <data> [<thdi>]\n\
	op   = 4-bit value selecting what operation to perform\n\
	mask = 4-bit value selecting which GMR to mask the data against\n\
	data = 36-bit comparand to search for, the search key\n\
	thdi = 2-bit value to use as the THDI - ignored on prototype\n");
		return;
	}

	op = strtoul(argv[1], NULL, 16);
	mask = strtoul(argv[2], NULL, 16);
	data = strtoull(argv[3], NULL, 16);
	if (argc == 5)
		thdi = strtoull(argv[4], NULL, 16);
	else
		thdi = 0;

	res = i1_srch(data, op, mask, thdi);

	printf("\t(result=0x%.8x) : ", res);
	if (!(res & BIT_VALID))
	{
		printf("INVALID\n");
	}
	else
	{
		printf("addr=0x%.4x ", (res >> 16) & 0xffff);
		if ((res & BIT_HIT))
			printf("hit ");
		else
			printf("    ");
		if ((res & BIT_MULT))
			printf("mult ");
		else
			printf("     ");
		if ((res & BIT_FULL))
			printf("full ");
		else
			printf("     ");
		if ((res & BIT_NTBL))
			printf("ntbl ");
		else
			printf("     ");
		if ((res & BIT_NFAD))
			printf("nfad ");
		else
			printf("     ");
		if ((res & BIT_DELD))
			printf("deld ");
		else
			printf("     ");
		if ((res & BIT_LRND))
			printf("lrnd ");
		else
			printf("     ");
		if (argc == 5 && !prototype)
			printf("THDO=%x\n", (res >> 7) & 0x03);
		else
			printf("\n");
	}
}


static const uint32_t complete_max = 3450;
static uint32_t complete_cnt = 0;
static uint32_t num_err = 0;


static void
inc_cnt(int by)
{
	double tmp;

	complete_cnt += by;
	tmp = (complete_cnt < complete_max) ? complete_cnt : complete_max;
	tmp = (tmp / (double) complete_max) * 100.00;
	fprintf(stderr, "Progress: %3.2f%%", tmp);
	if (num_err == 1)
	{
		fprintf(stderr, " 1 error!\r");
	}
	else if (num_err > 1)
	{
		fprintf(stderr, " %u errors!\r", num_err);
	}
	else
	{
		fprintf(stderr, " no errors\r");
	}
	fflush(stderr);
}


static void
chk_cnt(void)
{
	fprintf(stderr, "Complete  100%%. ");

	if (num_err == 1)
	{
		fprintf(stderr, " FAIL! Total of 1 error! ");
	}
	else if (num_err > 1)
	{
		fprintf(stderr, " FAIL! Total of %u errors! ", num_err);
	}
	else
	{
		fprintf(stderr, " PASS! no errors. ");
	}

	if (complete_cnt != complete_max)
		fprintf(stderr, "New Maximum: %u. Please fix source\n", complete_cnt);
	else
		fprintf(stderr, "\n");
}


static void
i1_wmr(uint32_t addr, uint64_t data)
{
	write_reg(1, data); /* data */
	write_reg(0, 0x000200000LL | addr);	/* GMR */
	write_csr(0, 2, 1);
	write_reg(0, 0x000220000LL | addr);	/* EMR */
	write_csr(0, 2, 1);
}


const uint32_t VAL_MAX = 80;

uint64_t vals[] = {
	0x100000000LL, 0x200000000LL, 0x400000000LL, 0x800000000LL,
	0x010000000LL, 0x020000000LL, 0x040000000LL, 0x080000000LL,
	0x001000000LL, 0x002000000LL, 0x004000000LL, 0x008000000LL,
	0x000100000LL, 0x000200000LL, 0x000400000LL, 0x000800000LL,
	0x000010000LL, 0x000020000LL, 0x000040000LL, 0x000080000LL,
	0x000001000LL, 0x000002000LL, 0x000004000LL, 0x000008000LL,
	0x000000100LL, 0x000000200LL, 0x000000400LL, 0x000000800LL,
	0x000000010LL, 0x000000020LL, 0x000000040LL, 0x000000080LL,
	0x000000001LL, 0x000000002LL, 0x000000004LL, 0x000000008LL,
	0xeffffffffLL, 0xbffffffffLL, 0xdffffffffLL, 0x7ffffffffLL,
	0xfefffffffLL, 0xfbfffffffLL, 0xfdfffffffLL, 0xf7fffffffLL,
	0xffeffffffLL, 0xffbffffffLL, 0xffdffffffLL, 0xff7ffffffLL,
	0xfffefffffLL, 0xfffbfffffLL, 0xfffdfffffLL, 0xfff7fffffLL,
	0xffffeffffLL, 0xffffbffffLL, 0xffffdffffLL, 0xffff7ffffLL,
	0xfffffefffLL, 0xfffffbfffLL, 0xfffffdfffLL, 0xfffff7fffLL,
	0xffffffeffLL, 0xffffffbffLL, 0xffffffdffLL, 0xffffff7ffLL,
	0xfffffffefLL, 0xfffffffbfLL, 0xfffffffdfLL, 0xfffffff7fLL,
	0xffffffffeLL, 0xffffffffbLL, 0xffffffffdLL, 0xffffffff7LL,
	0x696969696LL, 0x969696969LL, 0x5a5a5a5a5LL, 0xa5a5a5a5aLL,
	0x242424242LL, 0x424242424LL, 0x818181818LL, 0x181818181LL,
};

// {0x0L, 0x5a5a5a5a5L, 0xa5a5a5a5aL, 0xfffffffffL};


static void
i2_bist_mr(uint32_t MAX_ERR, uint64_t mask, uint32_t shift)
{
	uint64_t data;
	uint64_t datao;
	uint32_t i;
	uint32_t j;

	/* Note well:
	 * We do NOT mangle the GMR/EMR entries at this point.
	 * The entries left in the GMR/EMR as a result of this bist are
	 * useless for search and learn on the prototype card.
	 */
	fprintf(stdout, "Testing GMR/EMR registers %u\n", shift);
	for (j = 0; j < VAL_MAX; j++)
	{
		if (num_err > MAX_ERR)
			return;

		data = vals[j];
		/* First we write the GMR / EMR, with varying data for each */
		for (i = 0; i < 16; i++)
		{
			i1_wmr(i, (data & mask) | (i << shift));
		}

		/* Now we read them, to make sure they are with the correct value.... */
		for (i = 0; i < 16; i++)
		{
			if (num_err > MAX_ERR)
				return;

			write_reg(0, 0x000210000LL | i);
			write_csr(1, 1, 1);
			datao = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];

			if (datao != ((data & mask) | (i << shift)))
			{
				fprintf(stdout, "GMR at address %2u is incorrect. Value is 0x%.1x%.8x expected was 0x%.9"PRIx64"\n", i, regs[1] & 0x0f, regs[0], (data & mask) | (i << shift));
				++num_err;
			}
		}

		inc_cnt(1);
		for (i = 0; i < 16; i++)
		{
			if (num_err > MAX_ERR)
				return;

			write_reg(0, 0x000230000LL | i);
			write_csr(1, 1, 1);
			datao = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
			if (datao != ((data & mask) | (i << shift)))
			{
				fprintf(stdout, "EMR at address %2u is incorrect. Value is 0x%.1x%.8x expected was 0x%.9"PRIx64"\n", i, regs[1] & 0x0f, regs[0], (data & mask) | (i << shift));
				++num_err;
			}
		}
		inc_cnt(1);
	}
}


static void
i2_bist_srch(uint32_t MAX_ERR, uint32_t o, uint64_t smask, uint32_t shift)
{
	uint32_t NENTRIES = 65536;
	uint32_t op;
	uint32_t mask;
	uint32_t thdi;
	uint64_t data;
	uint64_t datao;
	uint64_t proto_msk;
	uint32_t i;
	uint32_t j;
	uint32_t set;
	uint32_t clr;
	uint32_t zeroflag = 0;
	uint32_t oneflag = 0;

	proto_msk = (prototype) ? 0xcffffffffLL : 0xfffffffffLL;

	/* This is the BIST itself. It includes:
	   1. Learning 64K entries with vals after a suitable smask and shift
	   2. Searching for all learned entries, expecting a hit.
	   3. Deleting the entries, expecting a hit.
	   4. Searching for all entries, expecting a miss.
	   5. Repeating for the next value of "vals" */
	write_reg(0, 0x000240000LL);	/* load BDR */
	write_reg(1, mangle_entry(0x000000000LL));	/* BDR = zeroes */
	write_csr(0, 2, 1);

	for (j = 0; j < VAL_MAX; j++)
	{
		if ((vals[j] & smask) == 0)
		{
			if (zeroflag)
				continue;
			zeroflag = 1;
		}

		if ((vals[j] & smask) == (-1 & smask))
		{
			if (oneflag)
				continue;
			oneflag = 1;
		}

		/* Burst Invalidate all entries in CAM, so learn will work */
		write_reg(0, 0x000890000LL);	/* BDR -> memory */
		write_reg(1, 0x000000000LL);	/* saddr */
		write_reg(2, 0x000010000LL);	/* count */
		write_csr(0, 3, 1);
		write_reg(0, 0x000280000LL);	/* load NFA */
		write_reg(1, 0x000000000LL);	/* NFA = 0x0000 */
		write_csr(0, 2, 1);

		/*************************************************************/
		fprintf(stdout, "Starting learn  operation, iteration #%3u\n", j + o);
		op = 0x8; /* Learn operation */
		for (i = 0; i < NENTRIES; i++)
		{

			if (num_err > MAX_ERR)
				return;

			/* All masks are the same so we test them repetedly */
			mask = i % 16;
			thdi = i % 4;
			data = ((vals[j] & smask) | (i << shift)) & proto_msk;
			set = BIT_LRND | BIT_NTBL;
			clr = BIT_HIT | BIT_MULT | BIT_NFAD | BIT_DELD;
			if (i != 65534)
				clr |= BIT_FULL;
			else
				set |= BIT_FULL;

			num_err += i2_srch(data, op, mask, thdi, i, set, clr, "learn", 1, thdi);
		}
		inc_cnt(5);

		/*************************************************************/
		fprintf(stdout, "Starting search operation, iteration #%3u\n", j + o);
		op = 0x1; /* Search operation */
		for (i = 0; i < NENTRIES; i++)
		{
			if (num_err > MAX_ERR)
				return;

			/* All masks are the same so we test them repetedly */
			mask = i % 16;
			thdi = i % 4;
			data = ((vals[j] & smask) | (i << shift)) & proto_msk;
			set = BIT_HIT;
			clr = BIT_MULT | BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND;
			num_err += i2_srch(data, op, mask, thdi, i, set, clr, "search", 1, thdi);
		}
		inc_cnt(5);

		/*************************************************************/
		/* Search for Stuck Bits and data mismatches using "rentry"  */
		fprintf(stdout, "Starting rentry operation, iteration #%3u [0x%.9"PRIx64"]\n", j + o, (vals[j] & smask));
		for (i = 0; i < NENTRIES; i++)
		{
			if (num_err > MAX_ERR)
				return;

			data = ((vals[j] & smask) | (i << shift)) & proto_msk;

			write_reg(0, 0x000830000LL);
			write_reg(1, i & 0xffff);
			write_csr(1, 2, 1);

			datao = ((uint64_t) (regs[1] & 0x0f) << 32) | regs[0];
			datao = mangle_entry(datao);

			if (datao != data)
			{					
                /* Masks? */
				fprintf(stdout, "Error(compare): entry 0x%.4x wrote 0x%.9"PRIx64", read 0x%.9"PRIx64" %s\n", i, data, datao, (prototype) ? "(Prototype Mangled)" : "");
				++num_err;
			}
		}
		inc_cnt(5);

		/*************************************************************/
		fprintf(stdout, "Starting delete operation, iteration #%3u\n", j + o);
		op = 0xA;				/* Delete operation */
		for (i = 0; i < NENTRIES; i++)
		{
			if (num_err > MAX_ERR)
				return;

			/* All masks are the same so we test them repetedly */
			mask = i % 16;
			thdi = i % 4;
			data = ((vals[j] & smask) | (i << shift)) & proto_msk;
			set = BIT_HIT | BIT_NTBL | BIT_DELD;
			clr = BIT_MULT | BIT_FULL | BIT_NFAD | BIT_LRND;
			num_err += i2_srch(data, op, mask, thdi, i, set, clr, "search", 1, thdi);
		}

		inc_cnt(5);
	}
}


static void
i2_bist_msk(uint32_t MAX_ERR)
{
	uint32_t i;
	uint32_t j;

	/* Set up for msk bit checking.
	 * Set all entries in CAM to be invalid 0xf_ffff_ffff
	 * Set the first few entries to be valid - such that mult will
	 *  assert if they are searched for
	 * Set entry addr 1234 to be something different (and valid) - this is
	 *  our target.
	 * For each of the four msk wires, check stuck high and stuck low to
	 *  locate the target
	 */

	/*********************************************************************/
	/* Set up                                                            */
	i1_wmr(0, 0xfffffff00LL);	/* mask off target */
	i1_wmr(1, 0x0000000ffLL);	/* match only target */
	i1_wmr(2, 0xfffffffffLL);	/* match anything */
	write_reg(0, 0x000240000LL);	/* load BDR */
	write_reg(1, mangle_entry(0xfffffff00LL));	/* BDR = ones */
	write_csr(0, 2, 1);
	write_reg(0, 0x000890002LL);	/* BDR -> memory */
	write_reg(1, 0x000000000LL);	/* saddr */
	write_reg(2, 0x000010000LL);	/* count */
	write_csr(0, 3, 1);

	write_reg(0, 0x000820100LL);	/* set entry valid */
	write_reg(1, 0x000000000LL);	/* addr = 0 */
	write_csr(0, 2, 1);
	write_reg(1, 0x000000001LL);	/* addr = 1 */
	write_csr(0, 2, 1);
	write_reg(1, 0x000000002LL);	/* addr = 2 */
	write_csr(0, 2, 1);
	write_reg(0, 0x000800102LL);	/* write to entry, vld, msk=2 */
	write_reg(1, 0x000001234LL);	/* addr = 1234 */
	write_reg(2, 0x000000042LL);	/* data = 42 */
	write_csr(0, 3, 1);

	/* hit target */
	num_err += i2_srch(0x000000042LL, 0x1, 0x2, 0x0, 0x1234, BIT_HIT, BIT_MULT | BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "msk_hit", 1, 0x0);

	/* miss altogether */
	num_err += i2_srch(0x000000000LL, 0x1, 0x2, 0x0, 0xffff, 0, BIT_HIT | BIT_MULT | BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "msk_miss", 1, 0x0);

	/* hit multiple */
	num_err += i2_srch(0xfffffff00LL, 0x1, 0x2, 0x0, 0x0, BIT_HIT | BIT_MULT, BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "msk_mult", 1, 0x0);

	for (i = 0; i < 16; i++)
	{
		i1_wmr(i, 0xfffffff00LL);	/* mask off target */
	}
	num_err += i2_srch(0xfffffff00LL, 0x1, 0x0, 0x0, 0x0, BIT_HIT | BIT_MULT, BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "2msk_mult", 1, 0x0);

	inc_cnt(1);

	if (num_err > MAX_ERR)
		return;

	/*********************************************************************/
	/* One Hot                                                           */
	for (j = 0; j < 4; j++)
	{
		for (i = 0; i < 16; i++)
		{
			i1_wmr(i, 0xfffffffffLL);	/* mask off target */
		}
		i1_wmr((1 << j), 0x0000000ffLL);	/* match only target */
		num_err += i2_srch(0x000000042LL, 0x1, (1 << j), 0x1, (1 << j) | (j << 8), BIT_HIT, BIT_MULT | BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "msk_trgt1", 1, 0x1);

		inc_cnt(1);
	}

	if (num_err > MAX_ERR)
		return;

	/*********************************************************************/
	/* One Cold                                                          */
	for (j = 0; j < 4; j++)
	{
		for (i = 0; i < 16; i++)
		{
			i1_wmr(i, 0xfffffffffLL);	/* mask off target */
		}

		i1_wmr((~(1 << j)) & 0xf, 0x0000000ffLL);	/* match only target */
		num_err += i2_srch(0x000000042LL, 0x1, (~(1 << j)) & 0xf, 0x1, ((~(1 << j)) & 0xf) | (j << 8), BIT_HIT, BIT_MULT | BIT_FULL | BIT_NFAD | BIT_NTBL | BIT_DELD | BIT_LRND, "msk_trgt0", 1, 0x1);

		inc_cnt(1);
	}
}


static void
cmd_bist(int argc, char *argv[])
{
	uint32_t i;
	uint32_t MAX_ERR;
	uint64_t proto_msk;

	proto_msk = (prototype) ? 0xcffffffffLL : 0xfffffffffLL;
	num_err = 0;

	if ((argc > 1 && argv[1][0] == '?') || (argc > 2))
	{
		printf("bist <n>\n\
	Performs a software BIST to the CAM. Assumes the CAM has been initialiazed.\n\
        <n> - Number of errors to stop BIST after\n");
		return;
	}

	if (argc == 2)
		MAX_ERR = strtoul(argv[1], NULL, 16);
	else
		MAX_ERR = 8;

	/* First, test the EMRs and GMRs
	 * two iterations would probably suffice, but here we have nine
	 */
	for (i = 0; i < 36; i += 4)
	{
		i2_bist_mr(MAX_ERR, ~(0xf << i), i);
		if (num_err > MAX_ERR)
		{
			main_return_value = EXIT_FAILURE;
			return;
		}
	}

	/* Ensure we are not busy */
	cam_busy = 0;
	write_csr(0, 0, 0);

	/*
	 * Since the last value in the above code is not the one we actually
	 * want so we have to write again to the registers....
	 * This value is usable for learn and search on the prototype.
	 */
	for (i = 0; i < 16; i++)
	{
		i1_wmr(i, mangle_entry(proto_msk));
	}

	i2_bist_srch(MAX_ERR, 0, 0xfffff0000LL, 0);
	if (num_err > MAX_ERR)
	{
		main_return_value = EXIT_FAILURE;
		return;
	}

	i2_bist_srch(MAX_ERR, VAL_MAX, 0xf0000ffffLL, 16);
	if (num_err > MAX_ERR)
	{
		main_return_value = EXIT_FAILURE;
		return;
	}

	i2_bist_msk(MAX_ERR);
	if (num_err > MAX_ERR)
	{
		main_return_value = EXIT_FAILURE;
		return;
	}

	inc_cnt(1);
	chk_cnt();
	if (num_err > 0)
		main_return_value = EXIT_FAILURE;
}


typedef struct cmd
{
	char *name;
	void (*func) (int argc, char *argv[]);

} cmd_t;


static cmd_t cmdtab[] =
{
	{"help", cmd_help},
	{"?", cmd_help},
	{"quit", cmd_quit},
	{"verbose", cmd_verbose},
	{"busy", cmd_busy},
	{"csr", cmd_csr},
	{"regs", cmd_regs},
	{"reset", cmd_reset},
	{"init", cmd_init},
	{"init_ip", cmd_init_ip},
	{"invalidate", cmd_invalidate},
	{"revision", cmd_revision},
	{"rconfig", cmd_config},
	{"wconfig", cmd_config},
	{"rthread", cmd_thread},
	{"wthread", cmd_thread},
	{"rlearn", cmd_learn},
	{"wlearn", cmd_learn},
	{"rgmr", cmd_gmr},
	{"rgmr72", cmd_gmr72},
	{"rgmr144", cmd_gmr144},
	{"wgmr", cmd_gmr},
	{"wgmr72", cmd_gmr72},
	{"wgmr144", cmd_gmr144},
	{"remr", cmd_emr},
	{"remr72", cmd_emr72},
	{"remr144", cmd_emr144},
	{"wemr", cmd_emr},
	{"wemr72", cmd_emr72},
	{"wemr144", cmd_emr144},
	{"rbdr", cmd_bdr},
	{"wbdr", cmd_bdr},
	{"rnfa", cmd_nfa},
	{"wnfa", cmd_nfa},
	{"wentry", cmd_wentry},
	{"wentry72", cmd_wentry72},
	{"wentry144", cmd_wentry144},
	{"bentry", cmd_bentry},
	{"sentry", cmd_sentry},
	{"rentry", cmd_rentry},
	{"search", cmd_search},
	{"bist", cmd_bist},
	{NULL, 0},
};


static void
dagcam(void)
{
	char prompt[64];
	char* line;
	char* tok;
	char* remain;
	cmd_t* cmdp;
	char** ap;
	char* argv[MAXARGV];
    char *saveptr1, *saveptr2;
	int temp; 

	for (;;)
	{
		(void) sprintf(prompt, "dagcam:%d> ", uHistoryCount + 1);
		
		line = dgetline(prompt);
		if (line == NULL)
			break;

		/*
		 * It's a bit of a mess here, since we would like to
		 * parse the line only once for spaces and the first
		 * token but we need the line in complete form for
		 * history and shell commands.
		 */
		remain = line + strspn(line, white);
		if ((tok = strchr(remain, '#')) != NULL)
			*tok = '\0';

		if (*remain == '\0')
			goto done;

#if HAVE_EDITLINE
		add_history(remain);
#endif /* HAVE_EDITLINE */
		uHistoryCount++;
		for (tok = strtok_r(remain, ";", &saveptr1); tok != NULL; tok = strtok_r(NULL, ";", &saveptr1))
		{
			tok += strspn(tok, white);
			switch (*tok)
			{
				case '!':
					temp = system(tok + 1);
					continue;
					
				case '\0':
					continue;
					
				default:
					break;
			}

			for (ap = argv, *ap = strtok_r(tok, white, &saveptr2);
                 *ap != NULL; 
                 *ap = strtok_r(NULL, white, &saveptr2))
			{
				if (**ap != '\0')
					if (++ap >= &argv[MAXARGV])
					{
						*--ap = NULL;
						dagutil_warning("too many arguments for command %s\n", argv[0]);
						break;
					}
			}

# if 0
			printf("dagcam: %d args\n", ap - argv);
# endif

			for (cmdp = cmdtab; cmdp->name != NULL; cmdp++)
			{
				if (0 == strncmp(argv[0], cmdp->name, strlen(argv[0])))
				{
					(*cmdp->func) (ap - argv, argv);
					break;
				}
			}

			if (cmdp->name == NULL)
				printf("%s: unknown command\n", argv[0]);
		}

	done:
		dagutil_free(line);
	}
}


/*
 * This is a wrapper around readline which cannot handle changing
 * stdin in any way.
 */
static char *
dgetline(char *prompt)
{
	static char *buf;

	if (1 == uReadFromKeyboard)
	{
		if (0 < strlen(command_buf))
		{
			/* Use the -c parameter from the command line */
			if (0 == uHaveReadCommandLine)
			{
				uHaveReadCommandLine = 1;
				
				/* Return a malloc()d string so all strings returned by dgetline are malloc()d. */
				return (char*) dagutil_strdup((const char*) command_buf);
			}
			else
			{
				/* Already read the command line option */
				return NULL;
			}
		}
		else
		{
#if HAVE_EDITLINE
			/* Get the next line from readline(). */
			return readline(prompt);
#else
			/* Get the next line via standard I/O. */
			printf("%s", prompt);
#endif /* HAVE_EDITLINE */
		}
	}

	/* Note: do _not_ prompt or echo here. */
	buf = dagutil_malloc(BUFSIZ);
	if (buf == NULL)
		dagutil_panic("dgetline: out of memory\n");

	return fgets(buf, BUFSIZ, stdin);
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagcam_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
