Extending Jtagulator Code with SVF Player? - in progress
cheezus
Posts: 298
in Propeller 1
I finally had a chance to play with the codebase and it's very nice! I plugged in a board and it identified it and spit out a bunch of useful info. Now what I'd LIKE to do is load a SVF or xsvf from SD card and program to the target device. Seems pretty simple, but then again so am I most of the time.
The output from Jtagulator looks like this
Other programmers seem to prefer the xsvf files but I can generate either for my target very easily. Since I can open SVF files in notepad ++ I'm thinking this will be the easier way to go. Looking further into a file we get an initialization, followed by checking for read / write protect.
The next part is where I get lost, I think it's bypassing any other devices in the chain?
Now the next part is pretty obvious, the bitstream itself. I won't post the long, repeating garbage just a sampling.
And then this init / bypass sequence again.
Then it verifies the image, I guess
Again, just a small sample of the actual bitstream. Which ends with some housekeeping.
PropJTAG has these methods, should cover it. It's just a matter of learning how to parse the svf file.
I know any assumptions i make are going to come back to bite me in the end. If I really get lucky, it might support something other than the target chip. At some point, at some time. This seems like a fun project but with how cheap jtag programmers can be had I'm not going to stress on it. :cool:
The output from Jtagulator looks like this
▒
UU LLL
JJJ TTTTTTT AAAAA GGGGGGGGGGG UUUU LLL AAAAA TTTTTTTT OOOOOOO RRRRRRRRR
JJJJ TTTTTTT AAAAAA GGGGGGG UUUU LLL AAAAAA TTTTTTTT OOOOOOO RRRRRRRR
JJJJ TTTT AAAAAAA GGG UUU UUUU LLL AAA AAA TTT OOOO OOO RRR RRR
JJJJ TTTT AAA AAA GGG GGG UUUU UUUU LLL AAA AAA TTT OOO OOO RRRRRRR
JJJJ TTTT AAA AA GGGGGGGGG UUUUUUUU LLLLLLLL AAAA TTT OOOOOOOOO RRR RRR
JJJ TTTT AAA AA GGGGGGGGG UUUUUUUU LLLLLLLLL AAA TTT OOOOOOOOO RRR RRR
JJJ TT GGG AAA RR RRR
JJJ GG AA RRR
JJJ G A RR
Welcome to JTAGulator. Press 'H' for available commands.
Warning: Use of this tool may affect target system behavior!
JTAG> v
Current target I/O voltage: Undefined
Enter new target I/O voltage (1.2 - 3.3, 0 for off): 3.3
New target I/O voltage set: 3.3
Ensure VADJ is NOT connected to target!
JTAG> i
Enter starting channel [0]: 0
Enter ending channel [8]: 8
Possible permutations: 504
Bring channels LOW between each permutation? [Y/n]: y
Enter length of time for channels to remain LOW (in ms, 1 - 1000) [100]:
Enter length of time after channels return HIGH before proceeding (in ms, 1 - 1000) [100]:
Press spacebar to begin (any other key to abort)...
JTAGulating! Press any key to abort...
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TDI: N/A
TDO: 6
TCK: 4
TMS: 5
Device ID #1: 0000 0110111001011100 00001001001 1 (0x06E5C093)
----------------------------------------------------------------------------------------------------------------------------------------
IDCODE scan complete.
JTAG> d
TDI not needed to retrieve Device ID.
Enter TDO pin [6]:
Enter TCK pin [4]:
Enter TMS pin [5]:
Device ID #1: 0000 0110111001011100 00001001001 1 (0x06E5C093)
-> Manufacturer ID: 0x049
-> Part Number: 0x6E5C
-> Version: 0x0
IDCODE listing complete.
JTAG> y
Enter TDI pin [0]: 7
Enter TDO pin [6]:
Enter TCK pin [4]:
Enter TMS pin [5]:
Ignore single-bit Data Registers? [Y/n]: y
Instruction Register (IR) length: 8
Possible instructions: 256
Press spacebar to begin (any other key to abort)...
JTAGulating! Press any key to abort...
IR: 00000000 (0x00) -> DR: 192
IR: 00000001 (0x01) -> DR: 32
IR: 00000010 (0x02) -> DR: 192
IR: 00000011 (0x03) -> DR: 192
-
IR: 00010001 (0x11) -> DR: 281
IR: 00010010 (0x12) -> DR: 281
IR: 00010011 (0x13) -> DR: 281
IR: 00010100 (0x14) -> DR: 281
IR: 00010101 (0x15) -> DR: 281
IR: 00010110 (0x16) -> DR: 192
------
IR: 11000000 (0xC0) -> DR: 281
--
IR: 11100100 (0xE4) -> DR: 281
-
IR: 11100110 (0xE6) -> DR: 281
IR: 11100111 (0xE7) -> DR: 281
IR: 11101000 (0xE8) -> DR: 281
IR: 11101001 (0xE9) -> DR: 281
IR: 11101010 (0xEA) -> DR: 281
-
IR: 11101101 (0xED) -> DR: 281
IR: 11101110 (0xEE) -> DR: 281
-
IR: 11110000 (0xF0) -> DR: 281
-
IR: 11111101 (0xFD) -> DR: 32
IR/DR discovery complete.
Other programmers seem to prefer the xsvf files but I can generate either for my target very easily. Since I can open SVF files in notepad ++ I'm thinking this will be the easier way to go. Looking further into a file we get an initialization, followed by checking for read / write protect.
TRST OFF; ENDIR IDLE; ENDDR IDLE; STATE RESET; STATE IDLE; FREQUENCY 1E6 HZ; //Operation: Program -p 0 -e -v TIR 0 ; HIR 0 ; TDR 0 ; HDR 0 ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ; //Loading device with 'idcode' instruction. SIR 8 TDI (01) SMASK (ff) ; SDR 32 TDI (00000000) SMASK (ffffffff) TDO (f6e5f093) MASK (0fff8fff) ; //Check for Read/Write Protect. SIR 8 TDI (ff) TDO (01) MASK (03) ; //Boundary Scan Chain Contents //Position 1: xc2c64a TIR 0 ; HIR 0 ; TDR 0 ; HDR 0 ; TIR 0 ; HIR 0 ; TDR 0 ; HDR 0 ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ; //Loading device with 'idcode' instruction. SIR 8 TDI (01) ; SDR 32 TDI (00000000) TDO (f6e5f093) ; //Check for Read/Write Protect. SIR 8 TDI (ff) TDO (01) MASK (03) ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ;
The next part is where I get lost, I think it's bypassing any other devices in the chain?
// Loading devices with 'enable' or 'bypass' instruction. SIR 8 TDI (e8) ; // Loading devices with 'erase' or 'bypass' instruction. ENDIR IRPAUSE; SIR 8 TDI (ed) SMASK (ff) ; ENDIR IDLE; STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRPAUSE; RUNTEST DRPAUSE 20 TCK; STATE IDLE; RUNTEST IDLE 100000 TCK; STATE DRPAUSE; RUNTEST DRPAUSE 5000 TCK; RUNTEST IDLE 1 TCK; // Loading devices with 'init' or 'bypass' instruction. ENDIR IRPAUSE; SIR 8 TDI (f0) SMASK (ff) ; STATE IDLE; RUNTEST IDLE 20 TCK; // Loading devices with 'init' or 'bypass' instruction. ENDIR IRPAUSE; SIR 8 TDI (f0) SMASK (ff) ; STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; RUNTEST 800 TCK; ENDIR IDLE; // Loading devices with 'conld' or 'bypass' instruction. SIR 8 TDI (c0) ; RUNTEST 100 TCK; // Loading devices with 'conld' or 'bypass' instruction. SIR 8 TDI (c0) ; RUNTEST 100 TCK; // Loading devices with 'enable' or 'bypass' instruction. SIR 8 TDI (e8) ;
Now the next part is pretty obvious, the bitstream itself. I won't post the long, repeating garbage just a sampling.
// Programming. // Loading devices with 'program' instruction. ENDIR IRPAUSE; SIR 8 TDI (ea) ; SDR 281 TDI (0003deab2aaaabffffffffefbeffffffffebfe7d7ffffffffffffffffffffffb7efefa0f) SMASK (01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; ENDIR IDLE; RUNTEST 10000 TCK; SDR 281 TDI (0102417dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (0182f97dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefb81) ; RUNTEST 10000 TCK; SDR 281 TDI (0083df7dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (00c241fffffffffffffffff7bffffffffffffe3e5fffffffffffffffffffffffffffce7d) ; RUNTEST 10000 TCK; SDR 281 TDI (01c2f97dfdf7dffffffffffffffffffffffdf67ebffffffffffffffffffffffffffbff81) ; RUNTEST 10000 TCK; SDR 281 TDI (0143df7dfedffffffffffffffffffffffffffe6e5fffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (004241ffffffffffffffffffffffffffffffff7f5fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (0062f97dfdf7dffffffffffffffffffffffffe7e79ffffffffffffffffffffefbefefb01) ; RUNTEST 10000 TCK; SDR 281 TDI (0163df7dfdf7dffffffffffffffffffffffffe7e79ffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (01e2417dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (00e2f97dfdf7edfffffffffffffffffffffffe667fffffffffffffffffffffefbefefb01) ; RUNTEST 10000 TCK; SDR 281 TDI (00a3df2dacb2cbfffffffff7defffffffffffe1e7ffffffffbefbfffefedb34d34d6d20f) ;
And then this init / bypass sequence again.
// Programming. // Loading devices with 'program' instruction. ENDIR IRPAUSE; SIR 8 TDI (ea) ; SDR 281 TDI (0003deab2aaaabffffffffefbeffffffffebfe7d7ffffffffffffffffffffffb7efefa0f) SMASK (01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; ENDIR IDLE; RUNTEST 10000 TCK; SDR 281 TDI (0102417dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (0182f97dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefb81) ; RUNTEST 10000 TCK; SDR 281 TDI (0083df7dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (00c241fffffffffffffffff7bffffffffffffe3e5fffffffffffffffffffffffffffce7d) ; RUNTEST 10000 TCK; SDR 281 TDI (01c2f97dfdf7dffffffffffffffffffffffdf67ebffffffffffffffffffffffffffbff81) ; RUNTEST 10000 TCK; SDR 281 TDI (0143df7dfedffffffffffffffffffffffffffe6e5fffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (004241ffffffffffffffffffffffffffffffff7f5fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (0062f97dfdf7dffffffffffffffffffffffffe7e79ffffffffffffffffffffefbefefb01) ; RUNTEST 10000 TCK; SDR 281 TDI (0163df7dfdf7dffffffffffffffffffffffffe7e79ffffffffffffffffffffefbefefa0f) ; RUNTEST 10000 TCK; SDR 281 TDI (01e2417dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa7d) ; RUNTEST 10000 TCK; SDR 281 TDI (00e2f97dfdf7edfffffffffffffffffffffffe667fffffffffffffffffffffefbefefb01) ; RUNTEST 10000 TCK; SDR 281 TDI (00a3df2dacb2cbfffffffff7defffffffffffe1e7ffffffffbefbfffefedb34d34d6d20f) ;
Then it verifies the image, I guess
// Verification. // Loading device with a 'verify' instruction. ENDIR IRPAUSE; SIR 8 TDI (ee) ; ENDDR DRPAUSE; SDR 7 TDI (00) SMASK (7f) ; ENDIR IDLE; RUNTEST DRPAUSE 20 TCK; ENDDR IDLE; RUNTEST IDLE 100 TCK; SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03deab2aaaabffffffffefbeffffffffebfe7d7ffffffffffffffffffffffb7efefa0f) MASK ( 03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; RUNTEST 100 TCK; ENDDR DRPAUSE; SDR 7 TDI (40) SMASK (7f) ; RUNTEST DRPAUSE 20 TCK; ENDDR IDLE; RUNTEST IDLE 100 TCK; SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02417dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa7d) MASK ( 03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; RUNTEST 100 TCK; ENDDR DRPAUSE; SDR 7 TDI (60) SMASK (7f) ; RUNTEST DRPAUSE 20 TCK; ENDDR IDLE; RUNTEST IDLE 100 TCK; SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (02f97dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefb81) MASK ( 03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; RUNTEST 100 TCK; ENDDR DRPAUSE; SDR 7 TDI (20) SMASK (7f) ; RUNTEST DRPAUSE 20 TCK; ENDDR IDLE; RUNTEST IDLE 100 TCK; SDR 274 TDI (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) TDO (03df7dfdf7dffffffffffffffffffffffffe7e1fffffffffffffffffffffefbefefa0f) MASK ( 03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; RUNTEST 100 TCK;
Again, just a small sample of the actual bitstream. Which ends with some housekeeping.
//Loading device with 'conld' instruction. SIR 8 TDI (c0) ; RUNTEST IDLE 100 TCK; //Loading device with 'enable' instruction. SIR 8 TDI (e8) ; // Setting Done bit ... // Loading device with a 'program' instruction. ENDIR IRPAUSE; SIR 8 TDI (ea) ; SDR 281 TDI (0017fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) SMASK (01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) ; ENDIR IDLE; RUNTEST 10000 TCK; SIR 8 TDI (f0) SMASK (ff) ; STATE IDLE; RUNTEST IDLE 20 TCK; ENDIR IRPAUSE; SIR 8 TDI (f0) SMASK (ff) ; STATE IREXIT2 IRUPDATE DRSELECT DRCAPTURE DREXIT1 DRUPDATE IDLE; RUNTEST 800 TCK; ENDIR IDLE; //Loading device with 'conld' instruction. SIR 8 TDI (c0) ; RUNTEST IDLE 100 TCK; //Loading device with 'idcode' instruction. SIR 8 TDI (01) ; SDR 32 TDI (00000000) SMASK (ffffffff) TDO (f6e5f093) MASK (0fff8fff) ; //Check for Done bit. SIR 8 TDI (ff) TDO (05) MASK (07) ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ; TIR 0 ; HIR 0 ; TDR 0 ; HDR 0 ; SIR 8 TDI (ff) ; SDR 1 TDI (00) SMASK (01) ;
PropJTAG has these methods, should cover it. It's just a matter of learning how to parse the svf file.
PUB Config(tdi_pin, tdo_pin, tck_pin, tms_pin, tck_speed) PUB Detect_Devices : num PUB Detect_IR_Length : num PUB Detect_DR_Length(value) : num PUB Bypass_Test(num, bPattern) : value PUB Get_Device_IDs(num, idptr) PUB Send_Instruction(instruction, num_bits) : ret_value PUB Send_Data(data, num_bits) : ret_value PUB Restore_Idle
I know any assumptions i make are going to come back to bite me in the end. If I really get lucky, it might support something other than the target chip. At some point, at some time. This seems like a fun project but with how cheap jtag programmers can be had I'm not going to stress on it. :cool:

Comments
The code is pretty ugly right now and it's not flexible at all. But it's a start!
*edit -
I spent some time getting the multi-line source working as well as better handling of comments. I'm updating the code below to reflect the improvements. I think reading from card and filling a command buffer with a valid command is working right so now it's just figuring out how to parse the command!
PRI Program_JTAG : z| s,r,c,t '' to program from sd card we should discover device and info first, then try to mount the sd card pst.Str(String(CR, LF, LF)) sd.FATEngineStart(SD_basepin, SD_basepin+1, SD_basepin+2, SD_basepin+3, -1, -1, -1, -1, -1) sd.mountPartition(0) ' mount the sd card s := \sd.openfile(@fn,"R") if sd.partitionerror '? ifnot ? ' failed to find the file pst.Str(s) pst.CharIn return s := 0 ' sd counter r := 0 ' cmdbuffer counter t := 0 ' total number of bytes z := sd.filesize ' size of the file repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks sd.readdata(@sdbuffer,512) ' read from SD card repeat s from 0 to 511 c := byte[@sdbuffer][s] ' get the next byte if t =< z ' might read in one more 512 byte block to ensure gets any remainder if c == 13 or c == 10 or c == ";" or r > maxcommandstring ' end of a line so new string, or string too long '' handle buffer overrun if r >maxcommandstring pst.str(string("buffer overrun")) return '' this is the end of a comment, don't parse if (c == 13 or c == 10) and (byte[@commandbuffer][0] == "/" and byte[@commandbuffer][1] == "/" ) or (byte[@commandbuffer][0] == "!") r:= 0 '' parse command if c == ";" byte[@commandbuffer][r] := 0 ' end of string terminator ParseJtagCommand r := 0 ' start at beginning of cmdbuffer elseif c > 31 and c < 127 byte[@commandbuffer][r] := c ' store if a valid character r +=1 t +=1 ' number of bytes read sd.closeFile PRI ParseJtagCommand ' just blast a line out for now pst.str(@commandbuffer) pst.Str(String(CR, LF)) 'pst.CharInAnd I'm pretty much stuck here trying to figure out how to move through the TAP states properly. I guess it's a bit more complicated than I thought it was and all the code I've found handling this is C++ which I don't understand at all
CON ' TAP ENUMERATIONS RESET = 15 IDLE = 0 #1,DRSELECT, DRCAPTURE, DRSHIFT, DREXIT1, DRPAUSE, DREXIT2, DRUPDATE #8,IRSELECT, IRCAPTURE, IRSHIFT, IREXIT1, IRPAUSE, IREXIT2, IRUPDATE VAR long SIR_EXIT, SDR_EXIT, CURRENT_STATE PUB Set_Tap_Mode(mode) { State switching ... All TAPs are in the same state, and that state changes on TCK transitions. This JTAG state machine is part of the JTAG spec, and includes sixteen states. There are six "stable states" where keeping TMS stable prevents the state from changing. Returns false if mode changed, true if error } result := false IF CURRENT_STATE == mode RETURN '' if we are already in the right state, do nothing if mode == RESET outa[TMS] := 1 ' TMS high repeat 5 TCK_Pulse CURRENT_STATE := RESET return if (CURRENT_STATE == RESET) and (mode == IDLE) outa[TMS] := 0 TCK_Pulse CURRENT_STATE := IDLE return if (CURRENT_STATE == IDLE) and (mode == DRPAUSE) outa[TMS] := 1 TCK_Pulse ' Go to Select DR Scan outa[TMS] := 0 TCK_Pulse ' Go to Capture DR outa[TMS] := 0 TCK_Pulse ' Go to Shift DR outa[TMS] := 1 TCK_Pulse ' Go to exit1 DR outa[TMS] := 0 TCK_Pulse ' Go to pause DR CURRENT_STATE := DRPAUSE return if (CURRENT_STATE == DRPAUSE) and (mode == IDLE) outa[TMS] := 1 TCK_Pulse ' pause to exit2 outa[TMS] := 1 TCK_Pulse ' exit2 to update outa[TMS] := 0 TCK_Pulse ' update to idle CURRENT_STATE := DRPAUSE return if (CURRENT_STATE == IRPAUSE) and (mode == IREXIT2 ) outa[TMS] := 1 TCK_Pulse ' pause to exit2 CURRENT_STATE := IREXIT2 return if (CURRENT_STATE == IREXIT2) and (mode == IRUPDATE ) outa[TMS] := 1 TCK_Pulse ' exit2 to update CURRENT_STATE := IRUPDATE return if (CURRENT_STATE == IRUPDATE) and (mode == DRSELECT ) outa[TMS] := 1 TCK_Pulse ' update to drselect CURRENT_STATE := DRSELECT return if (CURRENT_STATE == DRSELECT) and (mode == DRCAPTURE ) outa[TMS] := 0 TCK_Pulse ' drselect to drcapture CURRENT_STATE := DRCAPTURE return if (CURRENT_STATE == DRCAPTURE) and (mode == DREXIT1 ) outa[TMS] := 1 TCK_Pulse ' drcapture to drexit CURRENT_STATE := DREXIT1 return if (CURRENT_STATE == DREXIT1) and (mode == DRPAUSE ) outa[TMS] := 0 TCK_Pulse ' update to drselect CURRENT_STATE := DRSELECT return if (CURRENT_STATE == DREXIT1) and (mode == DRUPDATE ) outa[TMS] := 1 TCK_Pulse ' drselect to drcapture CURRENT_STATE := DRUPDATE return if (CURRENT_STATE == DRUPDATE) and (mode == IDLE ) outa[TMS] := 0 TCK_Pulse ' drcapture to drexit CURRENT_STATE := DREXIT1 return return true ' we had an errorI think that using a case statement to load a bit pattern for TMS and number of clocks would be much better but I tried to do this first and only succeeded at confusing myself.
This is what's added to the Jtagulator Main object.
PRI Program_JTAG pst.Str(String(CR, LF, LF)) ' mount the sd card result := \sd.openfile(@fn,"R") if sd.partitionerror '? ifnot ? ' failed to find the file pst.Str(result) pst.CharIn return ifnot BYPASS_Scan ' DO BYPASS Scan to find pins pst.str(string(13,10,"Scan failed, no pins setup. aborting")) sd.closeFile return pst.str(string(13,10,"Doing SVF",13,10)) jtag.Restore_Idle doSvf jtag.Restore_Idle sd.closeFile pst.str(string(13,10,"SVF done",13,10)) PRI doSvf : z| s,r,c,t, d '' to program from sd card we should discover device and info first, then try to mount the sd card d := 0 s := 0 ' sd counter r := 0 ' cmdbuffer counter t := 0 ' total number of bytes z := sd.filesize ' size of the file repeat (z >> 9) +1 ' include any remainder as +1, do blocks of 512 bytes each as natural size of SD blocks sd.readdata(@sdbuffer,512) ' read from SD card repeat s from 0 to 511 c := byte[@sdbuffer][s] ' get the next byte if t =< z ' might read in one more 512 byte block to ensure gets any remainder if c == 13 or c == 10 or c == ";" or r > maxcommandstring ' end of a line so new string, or string too long if r >maxcommandstring ' handle buffer overrun pst.str(string("buffer overrun")) return if (c == 13 or c == 10) and (byte[@commandbuffer][0] == "/" and byte[@commandbuffer][1] == "/" ) or (byte[@commandbuffer][0] == "!") { '' this is the end of a comment, don't parse but maybe output to term pst.str(@commandbuffer) pst.Str(String(CR, LF)) } r:= 0 if c == ";" ' parse command byte[@commandbuffer][r] := 0 ' end of string terminator ParseJtagCommand StatusUpdate r := 0 ' start at beginning of cmdbuffer elseif c > 31 and c < 127 byte[@commandbuffer][r] := c ' store if a valid character r +=1 t +=1 ' number of bytes read PRI StatusUpdate ' pst.char(13) ' if d == 47 or d == 0 ' result := 92 pst.char("-") ' if d == 92 ' result := 47 ' pst.char(92) PRI dotest(numOfClocks) ' pst.Str(string(13,10,"Run Test Clocks ")) ' pst.dec(numOfClocks) repeat numOfClocks jtag.TCK_Pulse DAT Commands ENDDR byte "ENDDR",0 ' Specifies default end state for DR scan operations. ENDIR byte "ENDIR",0 ' Specifies default end state for IR scan operations. FREQUENCY byte "FREQUENCY",0 ' Specifies maximum test clock frequency for IEEE 1149.1 bus operations. HDR byte "HDR",0 ' (Header Data Register) Specifies a header pattern that is prepended to the beginning of subsequent DR scan operations. HIR byte "HIR",0 ' (Header Instruction Register) Specifies a header pattern that is prepended to the beginning of subsequent IR scan operations. PIO byte "PIO",0 ' (Parallel Input/Output) Specifies a parallel test pattern. PIOMAP byte "PIOMAP",0 ' (Parallel Input/Output Map) Maps PIO column positions to a logical pin. RUNTEST byte "RUNTEST",0 ' Forces the IEEE 1149.1 bus to a run state for a specified number of clocks or a specified time period. SDR byte "SDR",0 ' (Scan Data Register) Performs an IEEE 1149.1 Data Register scan. SIR byte "SIR",0 ' (Scan Instruction Register) Performs an IEEE 1149.1 Instruction Register scan. - STATE byte "STATE",0 ' Forces the IEEE 1149.1 bus to a specified stable state. TDR byte "TDR",0 ' (Trailer Data Register) Specifies a trailer pattern that is appended to the end of subsequent DR scan operations. TIR byte "TIR",0 ' (Trailer Instruction Register) Specifies a trailer pattern that is appended to the end of subsequent IR scan operations. TRST byte "TRST",0 ' (Test ReSeT) Controls the optional Test Reset line. DAT States RESET byte "RESET",0 IDLE byte "IDLE",0 DRSELECT byte "DRSELECT",0 DRCAPTURE byte "DRCAPTURE",0 DRSHIFT byte "DRSHIFT",0 DREXIT1 byte "DREXIT1",0 DRPAUSE byte "DRPAUSE",0 DREXIT2 byte "DREXIT2",0 DRUPDATE byte "DRUPDATE",0 IRSELECT byte "IRSELECT",0 IRCAPTURE byte "IRCAPTURE",0 IRSHIFT byte "IRSHIFT",0 IREXIT1 byte "IREXIT1",0 IRPAUSE byte "IRPAUSE",0 IREXIT2 byte "IREXIT2",0 IRUPDATE byte "IRUPDATE",0 var byte sdbuffer[512] byte commandbuffer[maxcommandstring +1] dat fn byte "file.svf", 0The section that parses the command buffer works okay I guess.
PRI ParseJtagCommand |t, s, n, i ,p1, p2, p3, p4, p5, p6, p7, p8, p9,d1[9],d2[9],d3[9],d4[9] s := strsize(@commandbuffer) ' save this string size for later n := 0 ' n is num of parameters i := @commandbuffer ' t,i are temps repeat ' parse string to parameter list t := str.replaceCharacter(i, " ", 0) if t <> 0 and strsize(t) >0 long[@p1][n++] := t i := t while t ifnot str.stringCompareCI(@commandbuffer, @TRST) ' mess with reset line? RETURN ifnot str.stringCompareCI(@commandbuffer, @TIR) ' tail ir RETURN ifnot str.stringCompareCI(@commandbuffer, @HIR) ' head ir RETURN ifnot str.stringCompareCI(@commandbuffer, @TDR) ' tail dr RETURN ifnot str.stringCompareCI(@commandbuffer, @HDR) ' head dr RETURN ifnot str.stringCompareCI(@commandbuffer, @ENDIR) ' end IR with idle or irpause ifnot str.stringCompareCI(p1, @IDLE) t := jtag#IDLE elseifnot str.stringCompareCI(p1, @IRPAUSE) t:= jtag#IRPAUSE jtag.Set_ENDIR(t) return ifnot str.stringCompareCI(@commandbuffer, @ENDDR) ' end DR with idle or drpause ifnot str.stringCompareCI(p1, @IDLE) t := jtag#IDLE elseifnot str.stringCompareCI(p1, @DRPAUSE) t:= jtag#DRPAUSE jtag.Set_ENDDR(t) return ifnot str.stringCompareCI(@commandbuffer, @STATE) ' set state or through explicit repeat i from 0 to n-1 '' this could go through a bunch of modes if strsize(long[@p1][i]) >0 t := 32 t := ChangeState(long[@p1][i]) return ifnot str.stringCompareCI(@commandbuffer, @RUNTEST) repeat i from 0 to n-1 '' this could go through a bunch of modes if strsize( t:= long[@p1][i]) >0 ifnot byte[t] < ":" ' if numbers we don't need to set mode ChangeState(t) dotest(str.decimalToInteger(long[@p1][i+1])) return else dotest(str.decimalToInteger(t)) return ifnot str.stringCompareCI(@commandbuffer, @SIR) ' sir always 8 bits ' pst.str(string(13,10,"SIR")) ' jtag.Send_Instruction($FF, str.decimalToInteger(p1)) 'just send bypass for test jtag.Send_Instruction($FF, 0) 'just send bypass for test ifnot str.stringCompareCI(@commandbuffer, @SDR) ' ' pst.str(string(13,10,"SDR")) ' jtag.Send_Data($FF, str.decimalToInteger(p1)) jtag.Send_Data($FF, 0)This mess of code handles the STATE command, and while it seems to work... it's UGLY.
Pri ChangeState(statePtr) |t t := 32 ifnot str.stringCompareCI(statePtr, @IDLE) ' set state or through explicit 'pst.str(@IDLE) t:= jtag.Set_Tap_Mode(jtag#IDLE) elseifnot str.stringCompareCI(statePtr, @RESET) ' set state or through explicit 'pst.str(@RESET) t:= jtag.Set_Tap_Mode(jtag#RESET) elseifnot str.stringCompareCI(statePtr, @IREXIT2) ' set state or through explicit 'pst.str( @IREXIT2) t:= jtag.Set_Tap_Mode(jtag#IREXIT2) elseifnot str.stringCompareCI(statePtr, @IRUPDATE) ' set state or through explicit 'pst.str(@IRUPDATE) t:= jtag.Set_Tap_Mode(jtag#IRUPDATE) elseifnot str.stringCompareCI(statePtr, @DRSELECT) ' set state or through explicit 'pst.str(@DRSELECT) t:= jtag.Set_Tap_Mode(jtag#DRSELECT) elseifnot str.stringCompareCI(statePtr, @DRCAPTURE) ' set state or through explicit ' pst.str(@DRCAPTURE) t:= jtag.Set_Tap_Mode(jtag#DRCAPTURE) elseifnot str.stringCompareCI(statePtr, @DREXIT1) ' set state or through explicit 'pst.str(@DREXIT1) t:= jtag.Set_Tap_Mode(jtag#DREXIT1) elseifnot str.stringCompareCI(statePtr, @DRPAUSE) ' set state or through explicit 'pst.str(@DRPAUSE) t:= jtag.Set_Tap_Mode(jtag#DRPAUSE) elseifnot str.stringCompareCI(statePtr, @DRUPDATE) ' set state or through explicit 'pst.str(@DRUPDATE) t:= jtag.Set_Tap_Mode(jtag#DRUPDATE) '' these aren't used for test bitstream { elseifnot str.stringCompareCI(@p1+i, @IRSELECT) ' set state or through explicit pst.str(string("IRSELECT",13,10)) elseifnot str.stringCompareCI(@p1+i, @IRCAPTURE) ' set state or through explicit pst.str(string("IRCAPTURE",13,10)) elseifnot str.stringCompareCI(@p1+i, @IRSHIFT) ' set state or through explicit pst.str(string("IRSHIFT",13,10)) elseifnot str.stringCompareCI(@p1+i, @IREXIT1) ' set state or through explicit pst.str(string("IREXIT1",13,10)) elseifnot str.stringCompareCI(@p1+i, @IRPAUSE) ' set state or through explicit pst.str(string("IRPAUSE",13,10)) elseifnot str.stringCompareCI(@p1+i, @DRSHIFT) ' set state or through explicit pst.str(string("DRSHIFT",13,10)) elseifnot str.stringCompareCI(@p1+i, @DREXIT2) ' set state or through explicit pst.str(string("DREXIT2",13,10)) } ' ' pst.str(long[@p1][i]) ' pst.str(string(13,10)) 'pst.dec(t) if t == false ' pst.str(String(" good")) 'return ' mode updated properly elseif t == 32 pst.str(string(13,10,"state update not parsed ")) pst.str(statePtr) else pst.str(string(13,10,"state update not handled ")) pst.str(statePtr) pst.str(string(" from ")) if t == 1 pst.str(string("IDLE")) if t == 2 pst.str(string("DRSELECT")) if t == 3 pst.str(string("DRCAPTURE")) if t == 4 pst.str(string("DRSHIFT")) if t == 5 pst.str(string("DREXIT1")) if t == 6 pst.str(string("DRPAUSE")) if t == 7 pst.str(string("DREXIT2")) if t == 8 pst.str(string("DRUPDATE")) if t == 9 pst.str(string("IRSELECT")) if t == 10 pst.str(string("IRCAPTURE")) if t == 11 pst.str(string("IRSHIFT")) if t == 12 pst.str(string("IREXIT1")) if t == 13 pst.str(string("IRPAUSE")) if t == 14 pst.str(string("IREXIT2")) if t == 15 pst.str(string("IRUPDATE")) if t == 16 pst.str(string("RESET")) '' irpause to idle was saying from idle to idle?PUB Set_Tap_Mode(mode) { State switching ... All TAPs are in the same state, and that state changes on TCK transitions. This JTAG state machine is part of the JTAG spec, and includes sixteen states. There are six "stable states" where keeping TMS stable prevents the state from changing. This expects to be called from RESET, IDLE, IRPAUSE OR DRPAUSE Returns false if mode changed, true if error } result := false '' IF CURRENT_STATE == mode return '' if we are already in the right state, do nothing if mode == RESET outa[TMS] := 1 ' TMS high repeat 5 TCK_Pulse CURRENT_STATE := RESET return if (CURRENT_STATE == RESET) and (mode == IDLE) outa[TMS] := 0 TCK_Pulse CURRENT_STATE := IDLE return if (CURRENT_STATE == IDLE) and (mode == DRPAUSE) outa[TMS] := 1 TCK_Pulse ' Go to Select DR Scan outa[TMS] := 0 TCK_Pulse ' Go to Capture DR outa[TMS] := 0 TCK_Pulse ' Go to Shift DR outa[TMS] := 1 TCK_Pulse ' Go to exit1 DR outa[TMS] := 0 TCK_Pulse ' Go to pause DR CURRENT_STATE := DRPAUSE return if (CURRENT_STATE == DRPAUSE) and (mode == IDLE) outa[TMS] := 1 TCK_Pulse ' pause to exit2 outa[TMS] := 1 TCK_Pulse ' exit2 to update outa[TMS] := 0 TCK_Pulse ' update to idle CURRENT_STATE := IDLE return if (CURRENT_STATE == IRPAUSE) and (mode == IDLE) outa[TMS] := 1 TCK_Pulse ' pause to exit2 outa[TMS] := 1 TCK_Pulse ' exit2 to update outa[TMS] := 0 TCK_Pulse ' update to idle CURRENT_STATE := IDLE return if (CURRENT_STATE == IRPAUSE) and (mode == IREXIT2 ) outa[TMS] := 1 TCK_Pulse ' pause to exit2 CURRENT_STATE := IREXIT2 return if (CURRENT_STATE == IREXIT2) and (mode == IRUPDATE ) outa[TMS] := 1 TCK_Pulse ' exit2 to update CURRENT_STATE := IRUPDATE return if (CURRENT_STATE == IRUPDATE) and (mode == DRSELECT ) outa[TMS] := 1 TCK_Pulse ' update to drselect CURRENT_STATE := DRSELECT return if (CURRENT_STATE == DRSELECT) and (mode == DRCAPTURE ) outa[TMS] := 0 TCK_Pulse ' drselect to drcapture CURRENT_STATE := DRCAPTURE return if (CURRENT_STATE == DRCAPTURE) and (mode == DREXIT1 ) outa[TMS] := 1 TCK_Pulse ' drcapture to drexit CURRENT_STATE := DREXIT1 return if (CURRENT_STATE == DREXIT1) and (mode == DRPAUSE ) outa[TMS] := 0 TCK_Pulse ' exit1 to pause CURRENT_STATE := DRPAUSE return if (CURRENT_STATE == DREXIT1) and (mode == DRUPDATE ) outa[TMS] := 1 TCK_Pulse ' exit1 to update CURRENT_STATE := DRUPDATE return if (CURRENT_STATE == DRUPDATE) and (mode == IDLE ) outa[TMS] := 0 TCK_Pulse ' update to idle CURRENT_STATE := IDLE return return mode +1 ' we had an error so return currrent mode +1 since idle is 0I think I have the right idea creating a table, there's just gotta be a better way to use them. Right now it's brute force using compare current and new state, ugly like I said. I think it should work though, or at least I hope.
I do have a bug I can't find with debugging the state change but it only seems to show up on unsupported modes. I can cause it by removing the Send_Instruction or Send_Data call which changes from the current state to the IR or DR PAUSE state. IR PAUSE to DR PAUSE or the other way around. I'm not going to spend too much time chasing this one, just make a note for now in case I stumble across it later. Sometimes the hardest part of programming is deciding how to handle bad input
I'm implementing just enough to program the coolrunner ii devices I use right now and they seem pretty simple. It's been a real interesting exercise trying to implement and hopefully someone else will find it useful when I'm done. Things are shaping up pretty nicely and while they could be improved (a lot)… lipstick on a pig at this point.
I am kinda stuck on the last problem to solve, or at least I hope it's the last major problem. The built in send/receive can handle up to 32 bit transmissions, but I need to send almost 300 bits!! I've worked it out to fit in 9 longs, data in, data out and mask (I'm ignoring SMASK, I figure I'm ignoring enough other stuff right now couldn't hurt to ignore one more thing). This is a total of 27 longs! Okay, shouldn't be TOO scary right? I guess where I'm getting confused is they're kinda oddball numbers, 274 bits and 281. I'll probably just end up brute forcing this like the rest.
I'm going to put this here in case I break things again
PRI doSvf : z| s,r,c,t ' pst.Str(String(CR, LF, LF)) s := \sd.openfile(@fn,"R") ' save abort if sd.partitionerror ' file system error pst.Str(s) ' debug error pst.CharIn ' wait for key return ' and exit jtag.Config(8, 9, 10, 11, 22) '' !! DUMMY CONFIG SO WE DON'T OUTPUT DATA WHILE TEST !! pst.str(string(13,10,"Doing SVF",13,10)) s := 0 ' sd counter r := 0 ' cmdbuffer counter t := 0 ' total number of bytes z := sd.filesize ' size of the file repeat (z >> 9) +1 ' include any remainder as +1, blocks of 512 bytes [s][/s]as natural size of SD blocks sd.readdata(@sdbuffer,512) ' read from SD card repeat s from 0 to 511 ' do 512 byte block c := byte[@sdbuffer][s] ' get the next byte if t =< z ' might read in one more 512 byte block to ensure gets any remainder if c == 13 or c == 10 or c == ";" or r > maxcmdstr ' end of a line or string too long if r >maxcmdstr ' handle buffer overrun pst.str(string("buffer overrun")) return if (c == 13) and ((byte[@commandbuffer][0] == "/" and byte[@commandbuffer][1] == "/" ) or (byte[@commandbuffer][0] == "!")) byte[@commandbuffer][r+1] := 0 byte[@commandbuffer][r] := 0 ' end of string terminator r:= 0 if c == ";" ' parse command byte[@commandbuffer][r] := 0 ' end of string terminator ParseJtagCommand r := 0 ' start at beginning of cmdbuffer elseif c > 31 and c < 127 byte[@commandbuffer][r] := c ' store if a valid character r +=1 t +=1 ' number of bytes read jtag.Restore_Idle sd.closeFile pst.str(string(13,10,"SVF done",13,10)) PRI ParseJtagCommand |t, n, i ,p1, p2, p3, p4, p5, p6, p7, p8, p9 ',d n := 0 ' n is num of parameters i := @commandbuffer ' t,i are temps repeat 'parse string to parameter list t := str.replaceCharacter(i, " ", 0) if t <> 0 and strsize(t) > 0 ' remove trailing space if t -1 == i ' remove leading space, don't advance long[@p1][n-1] := t else long[@p1][n++] := t i := t while t ifnot str.stringCompareCS(@commandbuffer,@FREQUENCY) 'set the frequency RETURN ifnot str.stringCompareCS(@commandbuffer,@TRST) ' mess with reset line? RETURN ifnot str.stringCompareCS(@commandbuffer,@TIR) ' tail ir RETURN ifnot str.stringCompareCS(@commandbuffer,@HIR) ' head ir RETURN ifnot str.stringCompareCS(@commandbuffer,@TDR) ' tail dr RETURN ifnot str.stringCompareCS(@commandbuffer,@HDR) ' head dr RETURN DoJtagCommand(@commandbuffer,n,@p1) PRI DoJtagCommand(c,n,p) | t, ck, i,tdi, tdo, mask, isTdo, isMask ifnot str.stringCompareCS(c, @ENDIR) ' end IR with idle or irpause ifnot str.stringCompareCS(long[p], @IDLE) t := jtag#IDLE elseifnot str.stringCompareCS(long[p], @IRPAUSE) t:= jtag#IRPAUSE jtag.Set_ENDIR(t) return ifnot str.stringCompareCS(c, @ENDDR) ' end DR with idle or drpause ifnot str.stringCompareCS(long[p], @IDLE) t := jtag#IDLE elseifnot str.stringCompareCS(long[p], @DRPAUSE) t:= jtag#DRPAUSE jtag.Set_ENDDR(t) return ifnot str.stringCompareCS(c, @STATE) ' set state or through explicit repeat t from 0 to n-1 ChangeState(long[p][t]) return ifnot str.stringCompareCS(c, @RUNTEST) if byte[long[p]] < ":" Dotest(str.decimalToInteger(long[p])) return else ' if numbers we don't need to set mode ChangeState(long[p]) dotest(str.decimalToInteger(long[p][1])) return ck := str.decimalToInteger(long[p]) ifnot str.stringCompareCS(c, @SIR) ' sir always 8 bits, don't bother checking but keep in debugging code!! tdi := nu.FromStr(long[p][2], nu#HEX) isTdo := false isMask := false if n == 5 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true if n == 7 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true ifnot str.stringCompareCS(long[p][5], string("MASK")) mask := nu.FromStr(long[p][6], nu#HEX) isMask := true elseifnot str.stringCompareCS(long[p][5], string("TDO")) tdo := nu.FromStr(long[p][6], nu#HEX) isTdo := true if n == 9 if str.stringCompareCS(long[p][3], string("SMASK")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][5], string("TDO")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][7], string("MASK")) pst.str(string("bad data format")) return tdo := nu.FromStr(long[p][6], nu#HEX) mask := nu.FromStr(long[p][8], nu#HEX) isTdo := true isMask := true result := jtag.Send_Instruction(tdi ,ck) if isTdo result ^= tdo if isMask result &= mask ifnot result '' DEBUG SUCESS OR FAIL HERE!! pst.str(String("SIR COMPARE FAIL")) ifnot str.stringCompareCS(c, @SDR) '' dummy, just to update DR state if (ck := str.decimalToInteger(long[p])) > 32 ' num of clocks jtag.Send_Data($FF, 1) return tdi := nu.FromStr(long[p][2], nu#HEX) isTdo := false isMask := false if n == 5 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true if n == 7 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true ifnot str.stringCompareCS(long[p][5], string("MASK")) mask := nu.FromStr(long[p][6], nu#HEX) isMask := true elseifnot str.stringCompareCS(long[p][5], string("TDO")) tdo := nu.FromStr(long[p][6], nu#HEX) isTdo := true if n == 9 if str.stringCompareCS(long[p][3], string("SMASK")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][5], string("TDO")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][7], string("MASK")) pst.str(string("bad data format")) return tdo := nu.FromStr(long[p][6], nu#HEX) mask := nu.FromStr(long[p][8], nu#HEX) isTdo := true isMask := true result :=jtag.Send_Data($ff,1) if isTdo result ^= tdo if isMask result &= mask PRI dotest(numOfClocks) repeat numOfClocks jtag.TCK_Pulse PRI ChangeState(statePtr) |t t := 32 ifnot str.stringCompareCS(statePtr, @IDLE) t:= jtag.Set_Tap_Mode(jtag#IDLE) elseifnot str.stringCompareCS(statePtr, @RESET) t:= jtag.Set_Tap_Mode(jtag#RESET) elseifnot str.stringCompareCS(statePtr, @IREXIT2) t:= jtag.Set_Tap_Mode(jtag#IREXIT2) elseifnot str.stringCompareCS(statePtr, @IRUPDATE) t:= jtag.Set_Tap_Mode(jtag#IRUPDATE) elseifnot str.stringCompareCS(statePtr, @DRSELECT) t:= jtag.Set_Tap_Mode(jtag#DRSELECT) elseifnot str.stringCompareCS(statePtr, @DRCAPTURE) t:= jtag.Set_Tap_Mode(jtag#DRCAPTURE) elseifnot str.stringCompareCS(statePtr, @DREXIT1) t:= jtag.Set_Tap_Mode(jtag#DREXIT1) elseifnot str.stringCompareCS(statePtr, @DRPAUSE) t:= jtag.Set_Tap_Mode(jtag#DRPAUSE) elseifnot str.stringCompareCS(statePtr, @DRUPDATE) t:= jtag.Set_Tap_Mode(jtag#DRUPDATE) if t == false return ' mode updated properly elseif t == 32 pst.str(string(13,10,"state update not parsed ")) pst.str(statePtr) else pst.str(string(13,10,"state update not handled ")) DAT Commands ENDDR byte "ENDDR",0 ' Specifies default end state for DR scan operations. ENDIR byte "ENDIR",0 ' Specifies default end state for IR scan operations. FREQUENCY byte "FREQUENCY",0 ' Specifies maximum test clock frequency for IEEE 1149.1 bus operations. HDR byte "HDR",0 ' (Header Data Register) Specifies a header pattern that is prepended to the beginning of subsequent DR scan operations. HIR byte "HIR",0 ' (Header Instruction Register) Specifies a header pattern that is prepended to the beginning of subsequent IR scan operations. PIO byte "PIO",0 ' (Parallel Input/Output) Specifies a parallel test pattern. PIOMAP byte "PIOMAP",0 ' (Parallel Input/Output Map) Maps PIO column positions to a logical pin. RUNTEST byte "RUNTEST",0 ' Forces the IEEE 1149.1 bus to a run state for a specified number of clocks or a specified time period. SDR byte "SDR",0 ' (Scan Data Register) Performs an IEEE 1149.1 Data Register scan. SIR byte "SIR",0 ' (Scan Instruction Register) Performs an IEEE 1149.1 Instruction Register scan. - STATE byte "STATE",0 ' Forces the IEEE 1149.1 bus to a specified stable state. TDR byte "TDR",0 ' (Trailer Data Register) Specifies a trailer pattern that is appended to the end of subsequent DR scan operations. TIR byte "TIR",0 ' (Trailer Instruction Register) Specifies a trailer pattern that is appended to the end of subsequent IR scan operations. TRST byte "TRST",0 ' (Test ReSeT) Controls the optional Test Reset line. DAT States RESET byte "RESET",0 IDLE byte "IDLE",0 DRSELECT byte "DRSELECT",0 DRCAPTURE byte "DRCAPTURE",0 DRSHIFT byte "DRSHIFT",0 DREXIT1 byte "DREXIT1",0 DRPAUSE byte "DRPAUSE",0 DREXIT2 byte "DREXIT2",0 DRUPDATE byte "DRUPDATE",0 IRSELECT byte "IRSELECT",0 IRCAPTURE byte "IRCAPTURE",0 IRSHIFT byte "IRSHIFT",0 IREXIT1 byte "IREXIT1",0 IRPAUSE byte "IRPAUSE",0 IREXIT2 byte "IREXIT2",0 IRUPDATE byte "IRUPDATE",0That's common, you just define a bit-counter and decide some convention of which end you unpack from.
Attached is a simple test SVF file for an Atmel CPLD, which has a length of 326, if you are interested in other vendor test cases.
The problem is I'm over/under thinking the ASCII characters vs pure bits and shifting data out LSB and in MSB. I guess knowing a max number of bits to support would be a good idea. I know what my test case is but I'll have to do some research as what's a the best place to start.
Here's the existing code for handling the actual shift:
PRI Shift_Array(array, num_bits) : ret_value | i { Shifts an array of bits into the TAP while reading data back out. This method is called when the TAP state machine is in the Shift_DR or Shift_IR state. } ret_value := 0 repeat i from 1 to num_bits outa[TDI] := array & 1 ' Output data to target, LSB first array >>= 1 ret_value <<= 1 ret_value |= ina[TDO] ' Receive data, shift order depends on target if (i == num_bits) ' If at final bit... outa[TMS] := 1 ' Go to Exit1 TCK_PulseThe one place where this code will break is with a buffer over-run with greater lengths. It generates an error message to the terminal but keeps going. Right now from thinking about it 326 bits would break the 9 long bitfield code I was working on. Making it variable bitlength would be nice but I'm at a loss right now.
Another thing that would be nice to have are some switches to "turn off" output functions, and turn on debugging code, just to test parsing the file. Maybe I can add that if I get everything else working.
This is going to be painfully slow, once it does work. I've got some ideas to improve performance but I think one big performance hit comes from parsing an ascii file. XSVF would be way more efficient. The other one is a result of doing all the pin control in spin. I think there's a few ways to go about generating an asm engine without handwriting code. It would be a really neat display of the multi-core capability to assign a cog one portion of the task of programming and handle it all inline, in parallel.
I've saved that AMTEL bitstream, i'll see if it parses and what it generates. Thanks for that. Will be nice to see if it can handle other vendors once working. I thought I was going to give up working on the state control code but I made it through. The amtel file will probably break that too by manually cycling through the ir register state.
PRI StringToPlist(ptrToStr) :nOfParm |t, i ' return number of parameters found nOfParm := 0 ' is num of parameters i := ptrToStr ' t,i are temps repeat 'parse string to parameter list t := str.replaceCharacter(i, " ", 0) if t <> 0 and strsize(t) > 0 ' remove trailing space if t -1 == i ' remove leading space, don't advance long[@ptr1][nOfParm-1] := t else long[@ptr1][:nOfParm++] := t i := t while tThe 4 supported commands are decoded in this block. I keep telling myself there's a way to handle the string compare with a list. These work great with the parsing code above.
PRI DoJtagCommand(c,n) | t, ck, i,tdi, tdo, mask, isTdo, isMask ifnot str.stringCompareCS(c, @ENDIR) ' end IR with idle or irpause ifnot str.stringCompareCS(long[p], @IDLE) t := jtag#IDLE elseifnot str.stringCompareCS(long[p], @IRPAUSE) t:= jtag#IRPAUSE jtag.Set_ENDIR(t) return ifnot str.stringCompareCS(c, @ENDDR) ' end DR with idle or drpause ifnot str.stringCompareCS(long[p], @IDLE) t := jtag#IDLE elseifnot str.stringCompareCS(long[p], @DRPAUSE) t:= jtag#DRPAUSE jtag.Set_ENDDR(t) return ifnot str.stringCompareCS(c, @STATE) ' set state or through explicit repeat t from 0 to n-1 ChangeState(long[p][t]) return ifnot str.stringCompareCS(c, @RUNTEST) if byte[long[p]] < ":" Dotest(str.decimalToInteger(long[p])) return else ' if numbers we don't need to set mode ChangeState(long[p]) dotest(str.decimalToInteger(long[p][1])) returnThe next part is for SIR (Instruction register, always 8 bits) and although not tested it should work. But this ugliness just gets uglier when trying to handle the large data. I'm going to try to get through this but the $15 board from china is looking more attractive by the minute. The whole idea of waiting till April is the only thing stopping me at this point.
ck := str.decimalToInteger(long[p]) ifnot str.stringCompareCS(c, @SIR) ' sir always 8 bits tdi := nu.FromStr(long[p][2], nu#HEX) isTdo := false isMask := false if n == 5 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true if n == 7 ifnot str.stringCompareCS(long[p][3], string("TDO")) tdo := nu.FromStr(long[p][4], nu#HEX) isTdo := true ifnot str.stringCompareCS(long[p][5], string("MASK")) mask := nu.FromStr(long[p][6], nu#HEX) isMask := true elseifnot str.stringCompareCS(long[p][5], string("TDO")) tdo := nu.FromStr(long[p][6], nu#HEX) isTdo := true if n == 9 if str.stringCompareCS(long[p][3], string("SMASK")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][5], string("TDO")) pst.str(string("bad data format")) return if str.stringCompareCS(long[p][7], string("MASK")) pst.str(string("bad data format")) return tdo := nu.FromStr(long[p][6], nu#HEX) mask := nu.FromStr(long[p][8], nu#HEX) isTdo := true isMask := true result := jtag.Send_Instruction(tdi ,ck) if isTdo result ^= tdo if isMask result &= mask ifnot result '' DEBUG SUCESS OR FAIL HERE!! pst.str(String("SIR COMPARE FAIL"))I tried getting some captures of the wire last time I hooked up my PC-III programmer. Something wasn't right and although I was able to identify the CPLD, it didn't pass the programming stage. I didn't realize I was going to have this hard of a time. Should probably have looked at XSVF instead!
Any sage advice greatly appreciated!