/* lib-jtag.c. Basic test of Or1ksim library JTAG interface.
|
/* lib-jtag.c. Basic test of Or1ksim library JTAG interface.
|
|
|
Copyright (C) 1999-2006 OpenCores
|
Copyright (C) 1999-2006 OpenCores
|
Copyright (C) 2010 Embecosm Limited
|
Copyright (C) 2010 Embecosm Limited
|
|
|
Contributors various OpenCores participants
|
Contributors various OpenCores participants
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
|
|
This program is free software; you can redistribute it and/or modify it
|
This program is free software; you can redistribute it and/or modify it
|
under the terms of the GNU General Public License as published by the Free
|
under the terms of the GNU General Public License as published by the Free
|
Software Foundation; either version 3 of the License, or (at your option)
|
Software Foundation; either version 3 of the License, or (at your option)
|
any later version.
|
any later version.
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
more details.
|
more details.
|
|
|
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
with this program. If not, see <http: www.gnu.org/licenses/>. */
|
with this program. If not, see <http: www.gnu.org/licenses/>. */
|
|
|
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
This code is commented throughout for use with Doxygen.
|
This code is commented throughout for use with Doxygen.
|
--------------------------------------------------------------------------*/
|
--------------------------------------------------------------------------*/
|
|
|
#include <errno.h>
|
#include <errno.h>
|
#include <stddef.h>
|
#include <stddef.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
#include "or1ksim.h"
|
#include "or1ksim.h"
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Dump a JTAG register
|
/*!Dump a JTAG register
|
|
|
Prefix with the supplied string and add a newline afterwards.
|
Prefix with the supplied string and add a newline afterwards.
|
|
|
@param[in] prefix Prefix string to print out
|
@param[in] prefix Prefix string to print out
|
@param[in] jreg The JTAG register
|
@param[in] jreg The JTAG register
|
@param[in] num_bytes The number of bytes in the register */
|
@param[in] num_bytes The number of bytes in the register */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static void
|
static void
|
dump_jreg (const char *prefix,
|
dump_jreg (const char *prefix,
|
unsigned char *jreg,
|
unsigned char *jreg,
|
int num_bytes)
|
int num_bytes)
|
{
|
{
|
int i;
|
int i;
|
|
|
printf ("%s: ", prefix);
|
printf ("%s: ", prefix);
|
|
|
/* Dump each byte in turn */
|
/* Dump each byte in turn */
|
for (i = num_bytes - 1; i >=0; i--)
|
for (i = num_bytes - 1; i >=0; i--)
|
{
|
{
|
printf ("%02x", jreg[i]);
|
printf ("%02x", jreg[i]);
|
}
|
}
|
|
|
printf ("\n");
|
printf ("\n");
|
|
|
} /* dump_jreg () */
|
} /* dump_jreg () */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Convert a hex char into its value.
|
/*!Convert a hex char into its value.
|
|
|
@param[in] c The char to convert
|
@param[in] c The char to convert
|
|
|
@return The value represented by the char, or -1 if it's not a valid
|
@return The value represented by the char, or -1 if it's not a valid
|
char. */
|
char. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static int
|
static int
|
hexch2val (char c)
|
hexch2val (char c)
|
{
|
{
|
switch (c)
|
switch (c)
|
{
|
{
|
case '0': return 0;
|
case '0': return 0;
|
case '1': return 1;
|
case '1': return 1;
|
case '2': return 2;
|
case '2': return 2;
|
case '3': return 3;
|
case '3': return 3;
|
case '4': return 4;
|
case '4': return 4;
|
case '5': return 5;
|
case '5': return 5;
|
case '6': return 6;
|
case '6': return 6;
|
case '7': return 7;
|
case '7': return 7;
|
case '8': return 8;
|
case '8': return 8;
|
case '9': return 9;
|
case '9': return 9;
|
|
|
case 'a': case 'A': return 10;
|
case 'a': case 'A': return 10;
|
case 'b': case 'B': return 11;
|
case 'b': case 'B': return 11;
|
case 'c': case 'C': return 12;
|
case 'c': case 'C': return 12;
|
case 'd': case 'D': return 13;
|
case 'd': case 'D': return 13;
|
case 'e': case 'E': return 14;
|
case 'e': case 'E': return 14;
|
case 'f': case 'F': return 15;
|
case 'f': case 'F': return 15;
|
|
|
default:
|
default:
|
return -1;
|
return -1;
|
}
|
}
|
} /* hexch2val () */
|
} /* hexch2val () */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Shift a JTAG register.
|
/*!Shift a JTAG register.
|
|
|
Almost all this code is common between the instruction and data
|
Almost all this code is common between the instruction and data
|
registers. All that varies is the library function called and the error
|
registers. All that varies is the library function called and the error
|
message if anything goes wrong. So we common things up here.
|
message if anything goes wrong. So we common things up here.
|
|
|
@param[in] type 'D' if this is a data register, 'I' if an instruction
|
@param[in] type 'D' if this is a data register, 'I' if an instruction
|
register.
|
register.
|
@param[in] next_jreg Offset into argv of the next JTAG register length
|
@param[in] next_jreg Offset into argv of the next JTAG register length
|
field.
|
field.
|
@param[in] argc argc from the main program (for checking next_jref).
|
@param[in] argc argc from the main program (for checking next_jref).
|
@param[in] argv argv from the main program.
|
@param[in] argv argv from the main program.
|
|
|
@return 1 (TRUE) on success, 0 (FALSE) on failure. */
|
@return 1 (TRUE) on success, 0 (FALSE) on failure. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
static int
|
static int
|
process_jreg (const char type,
|
process_jreg (const char type,
|
int next_jreg,
|
int next_jreg,
|
int argc,
|
int argc,
|
char *argv[])
|
char *argv[])
|
{
|
{
|
const char *long_name = ('D' == type) ? "data" : "instruction";
|
const char *long_name = ('D' == type) ? "data" : "instruction";
|
|
|
/* Do we have the arg (length and value)? */
|
/* Do we have the arg (length and value)? */
|
if ((next_jreg + 1) > argc)
|
if ((next_jreg + 1) > argc)
|
{
|
{
|
printf ("ERROR: no %s register found.\n", long_name);
|
printf ("ERROR: no %s register found.\n", long_name);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Get the length field */
|
/* Get the length field */
|
int bit_len = strtol (argv[next_jreg++], NULL, 0);
|
int bit_len = strtol (argv[next_jreg++], NULL, 0);
|
|
|
if (0 == bit_len)
|
if (0 == bit_len)
|
{
|
{
|
printf ("ERROR: invalid register length\n");
|
printf ("ERROR: invalid register length\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Is the reg an exact number of bytes? */
|
/* Is the reg an exact number of bytes? */
|
char *hex_str = argv[next_jreg];
|
char *hex_str = argv[next_jreg];
|
int num_chars = strlen (hex_str);
|
int num_chars = strlen (hex_str);
|
int num_bytes = (bit_len + 7) / 8;
|
int num_bytes = (bit_len + 7) / 8;
|
|
|
if (num_chars > (2 * num_bytes))
|
if (num_chars > (2 * num_bytes))
|
{
|
{
|
printf ("Warning: Too many digits for register: truncated.\n");
|
printf ("Warning: Too many digits for register: truncated.\n");
|
}
|
}
|
|
|
/* Allocate and clear space */
|
/* Allocate and clear space */
|
unsigned char *jreg = malloc (num_bytes);
|
unsigned char *jreg = malloc (num_bytes);
|
|
|
if (NULL == jreg)
|
if (NULL == jreg)
|
{
|
{
|
printf ("ERROR: malloc for %s register failed.\n", long_name);
|
printf ("ERROR: malloc for %s register failed.\n", long_name);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
memset (jreg, 0, num_bytes);
|
memset (jreg, 0, num_bytes);
|
|
|
/* Initialize the register. The hex presentation is MS byte of the string on
|
/* Initialize the register. The hex presentation is MS byte of the string on
|
the left (i.e. at offset 0), but the internal representation is LS byte
|
the left (i.e. at offset 0), but the internal representation is LS byte
|
at the lowest address. */
|
at the lowest address. */
|
int i;
|
int i;
|
|
|
for (i = num_chars - 1; i >= 0; i--)
|
for (i = num_chars - 1; i >= 0; i--)
|
{
|
{
|
int dig_num = num_chars - 1 - i; /* Which digit */
|
int dig_num = num_chars - 1 - i; /* Which digit */
|
int dig_val = hexch2val (hex_str[i]);
|
int dig_val = hexch2val (hex_str[i]);
|
|
|
if (dig_val < 0)
|
if (dig_val < 0)
|
{
|
{
|
printf ("ERROR: %c not valid hex digit.\n", hex_str[i]);
|
printf ("ERROR: %c not valid hex digit.\n", hex_str[i]);
|
free (jreg);
|
free (jreg);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* MS digits are the odd numbered ones */
|
/* MS digits are the odd numbered ones */
|
jreg[dig_num / 2] |= (0 == (dig_num % 2)) ? dig_val : dig_val << 4;
|
jreg[dig_num / 2] |= (0 == (dig_num % 2)) ? dig_val : dig_val << 4;
|
}
|
}
|
|
|
/* Note what we are doing */
|
/* Note what we are doing */
|
dump_jreg (" shifting in", jreg, num_bytes);
|
dump_jreg (" shifting in", jreg, num_bytes);
|
|
|
double t;
|
double t;
|
|
|
if ('D' == type)
|
if ('D' == type)
|
{
|
{
|
t = or1ksim_jtag_shift_dr (jreg, bit_len);
|
t = or1ksim_jtag_shift_dr (jreg, bit_len);
|
}
|
}
|
else
|
else
|
{
|
{
|
t = or1ksim_jtag_shift_ir (jreg, bit_len);
|
t = or1ksim_jtag_shift_ir (jreg, bit_len);
|
}
|
}
|
|
|
dump_jreg (" shifted out", jreg, num_bytes);
|
dump_jreg (" shifted out", jreg, num_bytes);
|
printf (" time taken %.12fs\n", t);
|
printf (" time taken %.12fs\n", t);
|
|
|
free (jreg);
|
free (jreg);
|
return 1; /* Completed successfully */
|
return 1; /* Completed successfully */
|
|
|
} /* process_jreg () */
|
} /* process_jreg () */
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
/*!Main program
|
/*!Main program
|
|
|
Build an or1ksim program using the library which loads a program and config
|
Build an or1ksim program using the library which loads a program and config
|
from the command line which will drive JTAG.
|
from the command line which will drive JTAG.
|
|
|
lib-jtag <config-file> <image> <jtype> [<bitlen> <reg>]
|
lib-jtag <config-file> <image> <jtype> [<bitlen> <reg>]
|
[<jtype> [<bitlen> <reg>]] ...
|
[<jtype> [<bitlen> <reg>]] ...
|
|
|
- config-file An Or1ksim configuration file.
|
- config-file An Or1ksim configuration file.
|
- image A OpenRISC binary image to load into Or1ksim
|
- image A OpenRISC binary image to load into Or1ksim
|
- jtype One of 'R' (JTAG reset), 'I' (JTAG instruction register) or
|
- jtype One of 'R' (JTAG reset), 'I' (JTAG instruction register) or
|
'D' (JTAG data register).
|
'D' (JTAG data register).
|
- bitlen If jtype is 'D' or 'I', the number of bits in the JTAG
|
- bitlen If jtype is 'D' or 'I', the number of bits in the JTAG
|
register.
|
register.
|
- reg If jtype is 'D' or 'I', a JTAG register specified in
|
- reg If jtype is 'D' or 'I', a JTAG register specified in
|
hex. Specified LS digit on the right, and leading zeros may
|
hex. Specified LS digit on the right, and leading zeros may
|
be omitted.
|
be omitted.
|
|
|
The target program is run in bursts of 1ms execution, and the type of
|
The target program is run in bursts of 1ms execution, and the type of
|
return (OK, hit breakpoint) noted. Between each burst of execution, the
|
return (OK, hit breakpoint) noted. Between each burst of execution, the
|
next register is submitted to the corresponding Or1ksim JTAG interface
|
next register is submitted to the corresponding Or1ksim JTAG interface
|
function, and the resulting register (for 'I' and 'D') noted.
|
function, and the resulting register (for 'I' and 'D') noted.
|
|
|
@param[in] argc Number of elements in argv
|
@param[in] argc Number of elements in argv
|
@param[in] argv Vector of program name and arguments
|
@param[in] argv Vector of program name and arguments
|
|
|
@return Return code for the program. */
|
@return Return code for the program. */
|
/* --------------------------------------------------------------------------*/
|
/* --------------------------------------------------------------------------*/
|
int
|
int
|
main (int argc,
|
main (int argc,
|
char *argv[])
|
char *argv[])
|
{
|
{
|
/* Check we have minimum number of args. */
|
/* Check we have minimum number of args. */
|
if (argc < 4)
|
if (argc < 4)
|
{
|
{
|
printf ("usage: lib-jtag <config-file> <image> <jtype> [<bitlen> <reg>] "
|
printf ("usage: lib-jtag <config-file> <image> <jtype> [<bitlen> <reg>] "
|
"[<jtype> [<bitlen> <reg>]] ...\n");
|
"[<jtype> [<bitlen> <reg>]] ...\n");
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
/* Dummy argv array to pass arguments to or1ksim_init. Varies depending on
|
|
whether an image file is specified. */
|
|
int dummy_argc;
|
|
char *dummy_argv[5];
|
|
|
|
dummy_argv[0] = "libsim";
|
|
dummy_argv[1] = "-q";
|
|
dummy_argv[2] = "-f";
|
|
dummy_argv[3] = argv[1];
|
|
dummy_argv[4] = argv[2];
|
|
|
|
dummy_argc = 5;
|
|
|
/* Initialize the program. Put the initialization message afterwards, or it
|
/* Initialize the program. Put the initialization message afterwards, or it
|
will get swamped by the Or1ksim header. */
|
will get swamped by the Or1ksim header. */
|
if (0 == or1ksim_init (argv[1], argv[2], NULL, NULL, NULL))
|
if (0 == or1ksim_init (dummy_argc, dummy_argv, NULL, NULL, NULL))
|
{
|
{
|
printf ("Initalization succeeded.\n");
|
printf ("Initalization succeeded.\n");
|
}
|
}
|
else
|
else
|
{
|
{
|
printf ("Initalization failed.\n");
|
printf ("Initalization failed.\n");
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Run repeatedly for 1 millisecond until we have processed all JTAG
|
/* Run repeatedly for 1 millisecond until we have processed all JTAG
|
registers */
|
registers */
|
int next_jreg = 3; /* Offset to next JTAG register */
|
int next_jreg = 3; /* Offset to next JTAG register */
|
|
|
do
|
do
|
{
|
{
|
switch (or1ksim_run (1.0e-3))
|
switch (or1ksim_run (1.0e-3))
|
{
|
{
|
case OR1KSIM_RC_OK:
|
case OR1KSIM_RC_OK:
|
printf ("Execution step completed OK.\n");
|
printf ("Execution step completed OK.\n");
|
break;
|
break;
|
|
|
case OR1KSIM_RC_BRKPT:
|
case OR1KSIM_RC_BRKPT:
|
printf ("Execution step completed with breakpoint.\n");
|
printf ("Execution step completed with breakpoint.\n");
|
break;
|
break;
|
|
|
default:
|
default:
|
printf ("ERROR: run failed.\n");
|
printf ("ERROR: run failed.\n");
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Process the next arg appropriately. */
|
/* Process the next arg appropriately. */
|
switch (argv[next_jreg++][0])
|
switch (argv[next_jreg++][0])
|
{
|
{
|
case 'R':
|
case 'R':
|
printf ("Resetting JTAG.\n");
|
printf ("Resetting JTAG.\n");
|
or1ksim_jtag_reset ();
|
or1ksim_jtag_reset ();
|
break;
|
break;
|
|
|
case 'I':
|
case 'I':
|
printf ("Shifting instruction register.\n");
|
printf ("Shifting instruction register.\n");
|
|
|
if (process_jreg ('I', next_jreg, argc, argv))
|
if (process_jreg ('I', next_jreg, argc, argv))
|
{
|
{
|
next_jreg += 2;
|
next_jreg += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
return 1; /* Something went wrong */
|
return 1; /* Something went wrong */
|
}
|
}
|
|
|
break;
|
break;
|
|
|
case 'D':
|
case 'D':
|
printf ("Shifting data register.\n");
|
printf ("Shifting data register.\n");
|
|
|
if (process_jreg ('D', next_jreg, argc, argv))
|
if (process_jreg ('D', next_jreg, argc, argv))
|
{
|
{
|
next_jreg += 2;
|
next_jreg += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
return 1; /* Something went wrong */
|
return 1; /* Something went wrong */
|
}
|
}
|
|
|
break;
|
break;
|
|
|
default:
|
default:
|
printf ("ERROR: unknown JTAG request type.\n");
|
printf ("ERROR: unknown JTAG request type.\n");
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
while (next_jreg < argc);
|
while (next_jreg < argc);
|
|
|
/* A little longer to allow response to last upcall to be handled. */
|
/* A little longer to allow response to last upcall to be handled. */
|
switch (or1ksim_run (1.0e-3))
|
switch (or1ksim_run (1.0e-3))
|
{
|
{
|
case OR1KSIM_RC_OK:
|
case OR1KSIM_RC_OK:
|
printf ("Execution step completed OK.\n");
|
printf ("Execution step completed OK.\n");
|
break;
|
break;
|
|
|
case OR1KSIM_RC_BRKPT:
|
case OR1KSIM_RC_BRKPT:
|
printf ("Execution step completed with breakpoint.\n");
|
printf ("Execution step completed with breakpoint.\n");
|
break;
|
break;
|
|
|
default:
|
default:
|
printf ("ERROR: run failed.\n");
|
printf ("ERROR: run failed.\n");
|
return 1;
|
return 1;
|
}
|
}
|
|
|
printf ("Test completed successfully.\n");
|
printf ("Test completed successfully.\n");
|
return 0;
|
return 0;
|
|
|
} /* main () */
|
} /* main () */
|
|
|