My driver seems to be working now.
Please review and/or commit the attached diff and files.
Regards,
/Karl Hammar
diff --git a/urjtag/MAINTAINERS b/urjtag/MAINTAINERS
index 7caa5157..61c7955c 100644
--- a/urjtag/MAINTAINERS
+++ b/urjtag/MAINTAINERS
@@ -43,6 +43,11 @@ ARCOM CABLE DRIVER
F: src/tap/cable/arcom.c
S: Orphan
+ASPO CABLE DRIVER
+M: Karl Hammar <karl at aspodata.se>
+F: src/tap/cable/aspo.c
+S: Tested
+
AU1500 BUS DRIVER
F: src/bus/au1500.c
S: Orphan
diff --git a/urjtag/configure.ac b/urjtag/configure.ac
index c87b2385..ff9dfba6 100644
--- a/urjtag/configure.ac
+++ b/urjtag/configure.ac
@@ -655,6 +655,7 @@ URJ_DRIVER_SET([bus], [
# Enable cable drivers
URJ_DRIVER_SET([cable], [
arcom
+ aspo
byteblaster
dirtyjtag
dlc5
diff --git a/urjtag/doc/UrJTAG.txt b/urjtag/doc/UrJTAG.txt
index 94f32c3b..366c0a7b 100644
--- a/urjtag/doc/UrJTAG.txt
+++ b/urjtag/doc/UrJTAG.txt
@@ -206,6 +206,7 @@ See 'help cable' command for up-to-date info.
Parallel-port cables:
* Arcom JTAG Cable
+ * Aspo JTAG Cable (experimental)
http://aspodata.se/git/openhw/boards_other/isp/ pp_jtag_arm20.{sch,pcb}
* Altera ByteBlaster/ByteBlaster II/ByteBlasterMV Parallel Port Download Cable
* Xilinx DLC5 JTAG Parallel Cable III
* ETC EA253 JTAG Cable
diff --git a/urjtag/po/POTFILES.in b/urjtag/po/POTFILES.in
index 05e0c444..c2dceed1 100644
--- a/urjtag/po/POTFILES.in
+++ b/urjtag/po/POTFILES.in
@@ -107,6 +107,7 @@ src/svf/svf_bison.y
src/svf/svf.c
src/svf/svf_flex.l
src/tap/cable/arcom.c
+src/tap/cable/aspo.c
src/tap/cable/byteblaster.c
src/tap/cable.c
src/tap/cable/dlc5.c
diff --git a/urjtag/src/tap/Makefile.am b/urjtag/src/tap/Makefile.am
index 3f94f191..9917b2b5 100644
--- a/urjtag/src/tap/Makefile.am
+++ b/urjtag/src/tap/Makefile.am
@@ -57,6 +57,11 @@ libtap_la_SOURCES += \
cable/arcom.c
endif
+if ENABLE_CABLE_ASPO
+libtap_la_SOURCES += \
+ cable/aspo.c
+endif
+
if ENABLE_CABLE_BYTEBLASTER
libtap_la_SOURCES += \
cable/byteblaster.c
diff --git a/urjtag/src/tap/cable_list.h b/urjtag/src/tap/cable_list.h
index b321901c..a1e91ae0 100644
--- a/urjtag/src/tap/cable_list.h
+++ b/urjtag/src/tap/cable_list.h
@@ -30,6 +30,9 @@
#ifdef ENABLE_CABLE_ARCOM
_URJ_CABLE(arcom)
#endif
+#ifdef ENABLE_CABLE_ASPO
+_URJ_CABLE(aspo)
+#endif
#ifdef ENABLE_CABLE_BYTEBLASTER
_URJ_CABLE(byteblaster)
#endif
##############################
1. Intro
(All directory and file references are relative <urjtag-git top dir>/urjtag.)
All "cable" drivers lives in:
src/tap/cable
Currently theese parport drivers are available:
arcom.c
byteblaster.c
dlc5.c
ea253.c
ei012.c
keithkoep.c
lattice.c
minimal.c
mpcbdm.c
triton.c
wiggler.c
wiggler2.c
To write a new driver, you make a new file in that directory named by
convention by the driver name. In this case I'll describe the steps I
took to make the driver for the aspo adapter card (as of july 2019).
##############################
2. Build system, adding a driver.
(This section is valid for all cable driver, parport, usb and others.)
Before I describe the driver code, let's look into how to integrate
the .c file into the build system.
You have to update the following files:
MAINTAINERS
configure.ac
doc/UrJTAG.txt
po/POTFILES.in
src/tap/Makefile.am
src/tap/cable_list.h
2.1 MAINTAINERS
To be kind to others, please fill in the MAINTAINERS file, and keep it
sorted. I added the entry:
ASPO CABLE DRIVER
M: Karl Hammar <karl at aspodata.se>
F: src/tap/cable/aspo.c
S: Tested
between the "ARCOM CABLE DRIVER" and "AU1500 BUS DRIVER".
2.2 configure.ac
In configure.ac there is a list of all possible "cable" drivers around
line 655:
# Enable cable drivers
URJ_DRIVER_SET([cable], [
arcom
byteblaster
dirtyjtag
dlc5
ea253
... (omitted lines)
wiggler
xpc
],[
ep9307
jim
ts7800
],[
# automatically disable cable drivers when a required feature is not
available
... (omitted lines)
])
])
add your name there. This will make it possible to enable your driver
when you running autogen.sh and configure (look in config.h to make sure).
URJ_DRIVER_SET() have four parameters
1st is the driver set name
2nd is the list of drivers to enable by default
3rd is the list of drivers to disable by default
4th is some extra code to run before processing user list
you will probably add your driver name to the 2nd argument.
I added the line:
aspo
between the lines with arcom and byteblaster to keep the list sorted.
2.3 doc/UrJTAG.txt
Make a note here about your driver so others has a chance to know that
it exist. The best place is below line 202 under the headers:
==== Supported JTAG adapters/cables ====
See 'help cable' command for up-to-date info.
Parallel-port cables:
2.4 po/POTFILES.in
Add your driver source file in the list.
TODO:
Still don't know at this moment how to make gettext to update
po/urjtag.pot so translators can do their job properly.
2.5 src/tap/Makefile.am
Add, if-endif code to add your code if your driver is enabled.
I added:
if ENABLE_CABLE_ASPO
libtap_la_SOURCES += \
cable/aspo.c
endif
between the similar lines for ARCOM and BYTEBLASTER to keep the list
sorted.
2.6 src/tap/cable_list.h
Add your _URJ_CABLE() section here so your driver entry point can be
included. This file is then included in:
src/tap/cable.h
src/tap/cable.c
and via cpp hackery makes the driver entry points available.
As before, keep the list sorted
I added this section (between the ARCOM and BYTEBLASTER sections):
#ifdef ENABLE_CABLE_ASPO
_URJ_CABLE(aspo)
#endif
##############################
3. Driver code.
3.1 Test building
Copy a similar driver as your initial source file and replace the old
driver name with the new one.
In my case it amounted to:
sed -e 's/wiggler2/aspo/g' < src/tap/cable/wiggler2.c > src/tap/cable/aspo.c
and then change the first two text entries of the const
urj_cable_driver_t last in the file to say something else than the
original, just for testing.
Now, run
./autogen.sh
# ./configure done by autogen.sh; run it here with special options if needed
make
make install
Look at the make output and make sure you see your source compiling,
something like:
CC cable/aspo.lo
Test with jtag and verify that your driver is there with "help
cable" without any actual "cable" attached to the paralell port.
For me it worked out as (on linux using ppdev as the lowlevel driver):
$ jtag
UrJTAG 2018.09 #
Copyright (C) 2002, 2003 ETC s.r.o.
Copyright (C) 2007, 2008, 2009 Kolja Waschk and the respective authors
... (omitted lines)
jtag> help cable
Usage: cable DRIVER [DRIVER_OPTS]
Select JTAG cable type.
DRIVER name of cable
DRIVER_OPTS options for the selected cable
Type "cable DRIVER help" for info about options for cable DRIVER.
You can also use the driver "probe" to attempt autodetection.
List of supported cables:
ARCOM Arcom JTAG Cable
ASPO Aspo JTAG Cable (experimental)
ByteBlaster Altera ByteBlaster/ByteBlaster II/ByteBlasterMV Parallel Port
Download Cable
DLC5 Xilinx DLC5 JTAG Parallel Cable III
... (omitted lines)
DirtyJTAG DirtyJTAG STM32-based cable
jtag> cable aspo ppdev /dev/parport0
Initializing ppdev port /dev/parport0
jtag> cable ASPO ppdev /dev/parport0
Initializing ppdev port /dev/parport0
jtag> quit
As you can see, the driver name is case insensitive, the function that
finds the driver is urj_tap_cable_find() (in src/tap/cable.c) and it
uses strcasecmp().
3.2 The jtag command
When you run the command jtag, you run the code found in
src/apps/jtag/jtag.c, which, if run interactively, calls
jtag_readline_loop() -> jtag_readline_multiple_commands_support() ->
urj_parse_line() (which is found in src/global/parse.c) ->
urj_cmd_run() (src/cmd/cmd_cmd.c). urj_cmd_run() has two arguments,
chain and params. Chain is the found chain of tap controllers among
other things. Params is the command line entered, but tokenized.
The word chain is used in the combination JTAG chain in
doc/UrJTAG.txt.
urj_cmd_run() searches urj_cmds[] for the command to run. The
urj_cmds[] list is generated by Makefile from the files in src/cmd
that contaings a const urj_cmd_t urj_cmd_* variable.
So, if we enter quit to the command line, urj_cmd_run() finds "quit"
in urj_cmd_quit variable last in src/cmd/cmd_quit.c and runs
cmd_quit_run() since that is the run element of the urj_cmd_t struct.
The process is similar for other commands and you can find them all in
the src/cmd directory.
3.2.1 The jtag cable command
In 3.1 we entered the command line "cable aspo ppdev /dev/parport0".
By doing so urj_cmd_run() finds the data in urj_cmd_cable last in
src/cmd/cmd_cable.c and extracts the run member of that struct, i.e.
the function pointer cmd_cable_run. As seen in src/cmd/cmd_cable.c,
cmd_cable_run() does some checks and calls
urj_tap_chain_connect(chain, params[1], ¶ms[2]) (src/tap/chain.c),
params[1] is in this case is "aspo", and ¶ms[2] is a pointer to
{ "ppdev", "/dev/parport0" }.
It seems that everything hardware related goes through urj_tap_*()
functions, they reside in the usr/tap directory.
urj_tap_chain_connect() finds our driver (aspo), sees that it is a
parport driver, finds the parport devtype (ppdev) and calls
urj_tap_cable_parport_connect() (src/tap/cable.c).
For some reason, the urj_parport_driver_t (include/urjtag/parport.h)
struct doesn't contain a searchable string (e.g. "ppdev") like the
urj_cable_driver_t (struct URJ_CABLE_DRIVER, include/urjtag/cable.h),
and the search is done through a switch statement in
urj_cable_parport_devtype_string(). So first we search for an int and
then what driver struct matches that int.
urj_tap_cable_parport_connect() then allocs an urj_cable_t cable,
setting its .driver to a pointer to the driver, and runs the driver's
connect(), usually urj_tap_cable_generic_parport_connect()
(src/tap/cable/generic_parport.c) which finds the entry point (i.e. a
struct with function pointers) for devtype, and runs its connect() entry.
In our example, it is ppdev_connect(), which checks that we arn't
already using the device (/dev/parport0 in our example), and then sets
up a port_node_t (src/tap/parport.h) structure.
Back in urj_tap_cable_parport_connect(), it runs urj_tap_cable_start(),
which runs urj_tap_cable_init() (src/tap/cable.c) and
urj_tap_trst_reset() (src/tap/tap.c).
urj_tap_cable_init() sets up the urj_cable_t variable and calls the
cable drivers init (aspo_init() in our example), which is calling
ppdev_open() (in our example) which actually does open the device file.
3.3 driver documentation
3.3.1 driver entry point
Your driver is reached with the "const urj_cable_driver_t" struct,
usually found near the end of the driver code. The name of the struct
must match what comes out of the _URJ_CABLE(<driver name>) macro found
in src/tap/cable.[ch] and src/tap/cable_list.h. This macro is
differntly defined in the files src/tap/cable.[ch]:
cable.c:
#define _URJ_CABLE(cable) &urj_tap_cable_##cable##_driver,
cable.h:
#define _URJ_CABLE(cable) extern const urj_cable_driver_t
urj_tap_cable_##cable##_driver;
but the struct name is the same for booth theese to definitions
urj_tap_cable_##cable##_driver, i.e. if you used _URJ_CABLE(foo) in
the cable_list.h file, then you must define the entry point as:
const urj_cable_driver_t urj_tap_cable_foo_driver = {
... (omitted lines)
};
This type, urj_cable_driver_t, is defined in include/urjtag/cable.h,
it is a typedef near the top of the file:
typedef struct URJ_CABLE_DRIVER urj_cable_driver_t;
and the actual struct is defined in middle of the file:
struct URJ_CABLE_DRIVER
{
...
int (*parport) (urj_cable_t *cable, urj_cable_parport_devtype_t devtype,
int (*usb) (urj_cable_t *cable, const urj_param_t *params[]);
int (*other) (urj_cable_t *cable, const urj_param_t *params[]);
...
void (*disconnect) (urj_cable_t *cable);
void (*cable_free) (urj_cable_t *cable);
int (*init) (urj_cable_t *);
void (*done) (urj_cable_t *);
void (*set_frequency) (urj_cable_t *, uint32_t freq);
void (*clock) (urj_cable_t *, int, int, int);
int (*get_tdo) (urj_cable_t *);
int (*transfer) (urj_cable_t *, int, const char *, char *);
int (*set_signal) (urj_cable_t *, int, int);
int (*get_signal) (urj_cable_t *, urj_pod_sigsel_t);
void (*flush) (urj_cable_t *, urj_cable_flush_amount_t);
void (*help) (urj_log_level_t ll, const char *);
};
Your job as a driver writer is to fill in your struct variable with
the right set of functions for the operation of your hardware.
Most of theese entries are generic as seen in my example:
const urj_cable_driver_t urj_tap_cable_aspo_driver = {
"ASPO",
N_("Aspo JTAG Cable (experimental)"),
URJ_CABLE_DEVICE_PARPORT,
{ .parport = urj_tap_cable_generic_parport_connect, },
urj_tap_cable_generic_disconnect,
urj_tap_cable_generic_parport_free,
aspo_init,
urj_tap_cable_generic_parport_done,
urj_tap_cable_generic_set_frequency,
aspo_clock,
aspo_get_tdo,
urj_tap_cable_generic_transfer,
aspo_set_signal,
urj_tap_cable_generic_get_signal,
urj_tap_cable_generic_flush_one_by_one,
urj_tap_cable_generic_parport_help
};
The only thing I had to add is the init, clock, get_tdo and set_signal
entries and the name ("ASPO") and the description (N_("...")).
The N_(...) thing is defined in sysdep.h as
#define N_(s) gettext_noop(s)
and definition of gettext_noop() is found in include/urjtag/gettext.h.
3.3.2 Our first parameter, the urj_cable_t *cable
All function entries in the urj_cable_driver_t struct (except help) have a
"urj_cable_t *cable" first parameter, as can be seen from the extract below.
urj_cable_t is a struct URJ_CABLE (as seen in include/urjtag/types.h)
and that struct is declared in include/urjtag/cable.h, some 50 lines
below the struct URJ_CABLE_DRIVER declaration:
struct URJ_CABLE
{
const urj_cable_driver_t *driver;
union
{
urj_usbconn_t *usb;
urj_parport_t *port;
void *other;
} link;
void *params;
urj_chain_t *chain;
urj_cable_queue_info_t todo;
urj_cable_queue_info_t done;
uint32_t delay;
uint32_t frequency;
};
The variable cable is initialized as
urj_tap_cable_create():
cable = calloc()
cable->driver = driver (= your driver entry point)
urj_tap_cable_generic_parport_connect()
cable->link.port = port
cable->params = cable_params
cable->chain = NULL
urj_tap_cable_start()
chain->cable = cable
cable->delay = 0;
cable->frequency = 0;
urj_tap_cable_init()
cable->todo = {}
cable->done = {}
ppdev_parport_alloc()
cable->link.port = {}
your parport driver _open (ppdev_open() in this example)
open() and ioctl(,PPCLAIM)
your cable driver _init() (aspo_init() in this example)
PARAM_SIGNALS (cable) = ...
urj_tap_chain_connect()
chain->cable->chain = chain
Of all struct members, only cable->params is of main interest for the
cable driver writer. urj_tap_cable_generic_params_t and
PARAM_SIGNALS() are declared in src/tap/cable/generic.h, and they are
only used within the src/tap/cable directory. Its value is leaked out
by urj_tap_cable_generic_get_signal() which is the default
driver->get_signal() function, so we must provide for that value since
it is visible to the outside. That can be done by the default function
and types, by a custom one.
urj_tap_cable_get_signal() and urj_tap_cable_get_signal_late() via
urj_tap_chain_get_trst() and urj_tap_chain_get_pod_signal() is
used by the pod command (src/cmd/cmd_pod.c).
3.3.3 Talking to the hardware
Access to hw is done with the functions in src/tap/parport.c which are
shorthands for accessing the functions of the choosed parport handler
(ppdev in this example).
There are three registers on an ordinary paralell port: data, control
and status. Some bits in the control and status registers have
inverted signals to/from the connector pins, but theese functions
handles them so zero in an output bit given to thoose routines gives
you low voltage on the output pin and low on an input pin gives you a
zero on return on the corresponding bit.
3.3.4 Values to/from caller
The caller of the driver seems to treat tms/tdi/tck/trst/tdo values as:
1 => high voltage on jtag bus, 0 => low voltage on jtag bus. This is
guessed from the discussion below.
We have looked at driver->init() above. get_tdo() returns the TDO jtag
signal, and clock() and set_signal() sets the jtag signals.
So we must know if a clock(cable, 1, 1, 1) means that TMS and TDI
should be high (near Vcc) on the jtag bus on the target board, or low
and ditto for TRST and TCK.
>From urj_svf_force_reset_state() (src/svf/svf.c) we know that tms == 1
implies that the jtag signal TMS is high since five TMS high clocked in
puts the TAP controller in the Test-Logic-Reset state.
urj_tap_trst_reset() (src/tap/tap.c) gives us that trst == 1 implies
that jtag nTRST signal is high.
urj_tap_reset_bypass() (src/tap/tap.c) gives that tdi == 1 implies
that jtag TDI signal is high, BYPASS instruction is all 1's.
By looking in various cables implementation of clock(), I guess that
tck == 1 implies that the TCK signal is high on jtag bus.
/*
* $Id$
*
* Aspo JTAG Cable Driver
* Copyright (C) 2003 Ultra d.o.o.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Written by Marcel Telka <mar...@telka.sk>, 2002, 2003.
*
* Documentation:
* http://aspodata.se/git/openhw/boards_other/isp/ pp_jtag_arm20.{sch,pcb}
*
* Base on the code for the Macraigor WIGGLER code written by Marcel Telka.
* Modified by Matej Kupljen <matej.kupl...@ultra.si> to support
* the Modified WIGGLER JTAG cable. This has an additional pin, that is
* used for CPU reset. The schematic is based on the source code for the
* open source JTAG debugger for the PXA250 (255) processor, called Jelie
* <www.jelie.org>.
* Modified by Karl Hammar <k...@apodata.se> to support the JTAG cable at
* http://aspodata.se/git/openhw/boards_other/isp/ pp_jtag_arm20.{sch,pcb}
*
*/
#include <stdlib.h>
#include <sysdep.h>
#include <urjtag/cable.h>
#include <urjtag/parport.h>
#include <urjtag/chain.h>
#include "generic.h"
#include "generic_parport.h"
/* copied from <linux/parport.h>
GNU Free Documentation License, Version 1.1 or any later version
*/
#define PARPORT_CONTROL_STROBE 0x1
#define PARPORT_CONTROL_AUTOFD 0x2
#define PARPORT_CONTROL_INIT 0x4
#define PARPORT_CONTROL_SELECT 0x8
#define PARPORT_STATUS_ERROR 0x8
#define PARPORT_STATUS_SELECT 0x10
#define PARPORT_STATUS_PAPEROUT 0x20
#define PARPORT_STATUS_ACK 0x40
#define PARPORT_STATUS_BUSY 0x80
/* copied from openocd src/jtag/drivers/parport.c
* Copyright (C) 2005 by Dominic Rath *
* dominic.r...@gmx.de *
* *
* Copyright (C) 2008 by Spencer Oliver *
* s...@spen-soft.co.uk *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
...
*/
/* parallel port cable description
*/
struct cable {
const char *name;
uint8_t TDO_MASK; /* status port bit containing current TDO value */
uint8_t TRST_MASK; /* data port bit for TRST */
uint8_t TMS_MASK; /* data port bit for TMS */
uint8_t TCK_MASK; /* data port bit for TCK */
uint8_t TDI_MASK; /* data port bit for TDI */
uint8_t SRST_MASK; /* data port bit for SRST */
uint8_t OUTPUT_INVERT; /* data port bits that should be inverted */
uint8_t INPUT_INVERT; /* status port that should be inverted */
uint8_t PORT_INIT; /* initialize data port with this value */
uint8_t PORT_EXIT; /* de-initialize data port with this value */
uint8_t LED_MASK; /* data port bit for LED */
};
static const struct cable cables[] = {
/* name tdo trst tms tck tdi srst o_inv i_inv init exit led */
{ "wiggler", 0x80, 0x10, 0x02, 0x04, 0x08, 0x01, 0x01, 0x80, 0x80, 0x80, 0x00 },
{ "wiggler2", 0x80, 0x10, 0x02, 0x04, 0x08, 0x01, 0x01, 0x80, 0x80, 0x00, 0x20 },
{ "wiggler_ntrst_inverted", 0x80, 0x10, 0x02, 0x04, 0x08, 0x01, 0x11, 0x80, 0x80, 0x80, 0x00 },
{ "old_amt_wiggler", 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x11, 0x80, 0x80, 0x80, 0x00 },
{ "arm-jtag", 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x01, 0x80, 0x80, 0x80, 0x00 },
{ "chameleon", 0x80, 0x00, 0x04, 0x01, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 },
{ "dlc5", 0x10, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00 },
{ "triton", 0x80, 0x08, 0x04, 0x01, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 },
{ "lattice", 0x40, 0x10, 0x04, 0x02, 0x01, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00 },
{ "flashlink", 0x20, 0x10, 0x02, 0x01, 0x04, 0x20, 0x30, 0x20, 0x00, 0x00, 0x00 },
/* Altium Universal JTAG cable. Set the cable to Xilinx Mode and wire to target as follows:
HARD TCK - Target TCK
HARD TMS - Target TMS
HARD TDI - Target TDI
HARD TDO - Target TDO
SOFT TCK - Target TRST
SOFT TDI - Target SRST
*/
{ "altium", 0x10, 0x20, 0x04, 0x02, 0x01, 0x80, 0x00, 0x00, 0x10, 0x00, 0x08 },
{ "aspo", 0x10, 0x01, 0x04, 0x08, 0x02, 0x10, 0x17, 0x00, 0x17, 0x17, 0x00 },
{ NULL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
/* end copy */
/* Note about above:
all values ^= OUTPUT_INVERT, even PORT_INIT and PORT_EXIT on write
to hw register
no support for dongles/cables that uses the control port for any of
its output signals, nor for thoose that uses that data register for
input
urj_tap_cable_start() runs your drivers init function, where you
can set any initial value, BUT it then runs urj_tap_trst_reset()
which defeats the setting of an initial value
*/
/* some status and control pins are inverted in hw, see e.g.
https://en.wikipedia.org/wiki/Parallel_port#Port_addresses second table
https://web.archive.org/web/20120301022928/http://retired.beyondlogic.org/spp/parallel.pdf p.3
*/
#define STATUS_PORT_INVERSION ( PARPORT_STATUS_BUSY )
#define CONTROL_PORT_INVERSION ( PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT )
/*
* aspo Parallel port based JTAG dongle (#11 in above list)
*
* Design goal:
* pp idle at all zeroes (or not connected) => jtag idle at all high except TCK low
* target at 3.3V
*
* TDO -> select (status reg. 0x10, pin 13)
* DATA0 (0x01 pin 2) -> nTRST inverted
* DATA1 (0x02 pin 3) -> TDI inverted
* DATA2 (0x04 pin 4) -> TMS inverted
* DATA3 (0x08 pin 5) -> TCK
* DATA4 (0x10 pin 6) -> nSRST inverted
*
* All lines except TCK and TDO are inverted
*
* One could argue for nTRST to be non-inverted (i.e. low) to keep
* TAPs in the reset state when parport output is zero.
*/
struct pdata {
struct cable wiring;
unsigned char output; /* signal level of jtag bus, but with
paralell port output pin order */
};
static int
cable_connect (urj_cable_t *cable, urj_cable_parport_devtype_t devtype,
const char *devname, const urj_param_t *params[])
{
struct pdata *pdata;
int st;
/* Here we can handle possible params[] */
if ((st = urj_tap_cable_generic_parport_connect (cable, devtype, devname, (const urj_param_t **)NULL)) != URJ_STATUS_OK)
return st;
/* allocate our private data containg config and output pin state */
if (cable->params) { free(cable->params); }
pdata = (struct pdata *) malloc(sizeof (struct pdata));
if (!pdata) {
urj_error_set (URJ_ERROR_OUT_OF_MEMORY, _("malloc(%zd) fails"), sizeof (struct pdata *));
return URJ_STATUS_FAIL;
}
cable->params = pdata;
/* possible search cables[] for matching driver */
pdata->wiring = cables[11];
pdata->output = 0; /* real value set at first data write */
return URJ_STATUS_OK;
}
static int
cable_write(urj_cable_t *cable, unsigned char output)
{
struct pdata *pdata = (struct pdata *) cable->params;
output ^= pdata->wiring.OUTPUT_INVERT;
return urj_tap_parport_set_data (cable->link.port, output);
}
static int
cable_init (urj_cable_t *cable)
{
struct pdata *pdata = (struct pdata *) cable->params;
int st;
unsigned char output;
unsigned char unused_bits;
/* open/connect to device and check readability */
if (urj_tap_parport_open (cable->link.port) != URJ_STATUS_OK)
return URJ_STATUS_FAIL;
if (urj_tap_parport_get_data (cable->link.port) < 0)
return URJ_STATUS_FAIL;
if (urj_tap_parport_get_status (cable->link.port) < 0)
return URJ_STATUS_FAIL;
/* init output
only three cable drivers uses the control port
we just set it to zero, but we could possible set it to 0xff
*/
if (urj_tap_parport_set_control (cable->link.port, 0) != URJ_STATUS_OK)
return URJ_STATUS_FAIL;
output = pdata->wiring.PORT_INIT;
/* possible set unused pins high */
unused_bits = ~(pdata->wiring.TDO_MASK & pdata->wiring.TRST_MASK & pdata->wiring.TMS_MASK &
pdata->wiring.TCK_MASK & pdata->wiring.TDI_MASK & pdata->wiring.SRST_MASK );
(void) unused_bits;
/*output |= unused_bits;*/
if ((st=cable_write (cable, output)) != URJ_STATUS_OK) return st;
pdata->output = output; /* we could possible move all thoose inte cable_write() */
return URJ_STATUS_OK;
}
/*
http://www.jtagtest.com/pdf/ssya002c.pdf page 3-5:
The IEEE Std 1149.1 test bus uses both clock edges of TCK. TMS and
TDI are sampled on the rising edge of TCK, while TDO changes on the
falling edge of TCK.
*/
static void
cable_clock (urj_cable_t *cable, int tms, int tdi, int nn)
{
struct pdata *pdata = (struct pdata *) cable->params;
unsigned char output0;
unsigned char output1;
int ix;
if (!pdata) {
urj_error_set (URJ_ERROR_INVALID, _("pdata is null"));
return;
}
output0 = pdata->output;
if (tms) { output0 |= pdata->wiring.TMS_MASK; }
else { output0 &= ~pdata->wiring.TMS_MASK; }
if (tdi) { output0 |= pdata->wiring.TDI_MASK; }
else { output0 &= ~pdata->wiring.TDI_MASK; }
/* target TAP's reads TDI/TMS at rising edge of TCK */
output1 = output0;
output0 &= ~pdata->wiring.TCK_MASK;
output1 |= pdata->wiring.TCK_MASK;
for (ix = 0; ix < nn; ix++)
{
(void) cable_write (cable, output0);
urj_tap_cable_wait (cable);
(void) cable_write (cable, output1);
urj_tap_cable_wait (cable);
}
pdata->output = output1;
}
static int
cable_get_tdo (urj_cable_t *cable)
{
/* tested ok by cmd_pod_run() */
struct pdata *pdata = (struct pdata *) cable->params;
unsigned char output;
int status;
int tdo;
int st;
if (!pdata) {
urj_error_set (URJ_ERROR_INVALID, _("pdata is null"));
return -1;
}
/* TDO is set at the falling edge of TCK */
output = pdata->output & ~pdata->wiring.TCK_MASK;
if ((st=cable_write (cable, output)) != URJ_STATUS_OK) return st;
pdata->output = output;
urj_tap_cable_wait (cable);
if ((status = urj_tap_parport_get_status (cable->link.port)) < 0)
return status;
status ^= pdata->wiring.INPUT_INVERT;
tdo = status & pdata->wiring.TDO_MASK ? 1 : 0;
return tdo;
}
static int
cable_get_signal (urj_cable_t *cable, urj_pod_sigsel_t sig)
{
/* TODO: Don't know how to test this one.
used by urj_tap_chain_get_pod_signal() which only the python
binding uses
*/
/*
the default function (urj_tap_cable_generic_get_signal()) has:
return (((PARAM_SIGNALS (cable)) & sig) != 0) ? 1 : 0;
*/
struct pdata *pdata = (struct pdata *) cable->params;
unsigned char output;
int pod_signal;
if (!pdata) {
urj_error_set (URJ_ERROR_INVALID, _("pdata is null"));
return -1;
}
output = pdata->output;
/* convert from output pin state to what cmd_pod wants to see, urj_pod_sigsel_t values or'ed together */
pod_signal = 0;
if (output & pdata->wiring.TRST_MASK) { pod_signal |= URJ_POD_CS_TRST; }
if (output & pdata->wiring.TMS_MASK ) { pod_signal |= URJ_POD_CS_TMS; }
if (output & pdata->wiring.TCK_MASK ) { pod_signal |= URJ_POD_CS_TCK; }
if (output & pdata->wiring.TDI_MASK ) { pod_signal |= URJ_POD_CS_TDI; }
if (output & pdata->wiring.SRST_MASK) { pod_signal |= URJ_POD_CS_RESET; }
return (pod_signal & sig) ? 1 : 0;
}
static int
cable_set_signal (urj_cable_t *cable, int mask, int val)
{
/* tested ok by cmd_pod_run() */
/*
return value: the old pod_signal
*/
struct pdata *pdata = (struct pdata *) cable->params;
unsigned char output;
int pod_signal;
int st;
if (!pdata) {
urj_error_set (URJ_ERROR_INVALID, _("pdata is null"));
return -1;
}
/* convert from output pin state to what cmd_pod wants to see, urj_pod_sigsel_t values or'ed together */
output = pdata->output;
pod_signal = 0;
if (output & pdata->wiring.TRST_MASK) { pod_signal |= URJ_POD_CS_TRST; }
if (output & pdata->wiring.TMS_MASK ) { pod_signal |= URJ_POD_CS_TMS; }
if (output & pdata->wiring.TCK_MASK ) { pod_signal |= URJ_POD_CS_TCK; }
if (output & pdata->wiring.TDI_MASK ) { pod_signal |= URJ_POD_CS_TDI; }
if (output & pdata->wiring.SRST_MASK) { pod_signal |= URJ_POD_CS_RESET; }
/* only these can be modified */
/*mask &= (URJ_POD_CS_TDI | URJ_POD_CS_TCK | URJ_POD_CS_TMS | URJ_POD_CS_TRST);*/
mask &= (URJ_POD_CS_TDI | URJ_POD_CS_TCK | URJ_POD_CS_TMS | URJ_POD_CS_TRST | URJ_POD_CS_RESET);
if (mask != 0)
{
int sigs = (pod_signal & ~mask) | (val & mask);
output &= ~( pdata->wiring.TRST_MASK | pdata->wiring.TMS_MASK | pdata->wiring.TCK_MASK |
pdata->wiring.TDI_MASK | pdata->wiring.SRST_MASK );
if (sigs & URJ_POD_CS_TDI) { output |= pdata->wiring.TDI_MASK; }
if (sigs & URJ_POD_CS_TCK) { output |= pdata->wiring.TCK_MASK; }
if (sigs & URJ_POD_CS_TMS) { output |= pdata->wiring.TMS_MASK; }
if (sigs & URJ_POD_CS_TRST) { output |= pdata->wiring.TRST_MASK; }
if (sigs & URJ_POD_CS_RESET) { output |= pdata->wiring.SRST_MASK; }
if ((st=cable_write (cable, output)) != URJ_STATUS_OK) return st;
pdata->output = output;
}
return pod_signal;
}
const urj_cable_driver_t urj_tap_cable_aspo_driver = {
"ASPO",
N_("Aspo JTAG Cable (experimental)"),
URJ_CABLE_DEVICE_PARPORT,
{ .parport = cable_connect, },
urj_tap_cable_generic_disconnect,
urj_tap_cable_generic_parport_free,
cable_init,
urj_tap_cable_generic_parport_done,
urj_tap_cable_generic_set_frequency,
cable_clock,
cable_get_tdo,
urj_tap_cable_generic_transfer,
cable_set_signal,
cable_get_signal,
urj_tap_cable_generic_flush_one_by_one,
urj_tap_cable_generic_parport_help
};
_______________________________________________
UrJTAG-development mailing list
UrJTAG-development@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/urjtag-development