Extending Jtagulator Code with SVF Player? - in progress

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
▒


                                    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

  • {reserved}
  • {reserved for completed code}
  • cheezuscheezus Posts: 146
    edited 2019-02-11 - 04:37:23
    I made some progress loading a SVF file and then dumping out the commands to the terminal, just to try to get something working. I think I've got the right idea now I just need to figure out how to handle multi-line statements. Once I get that working, it's just a matter of decoding the command and sending the right low-level function the right parameters, I think... I've already got it "throwing away" comments, maybe these could be sent to the terminal when actually programming so at least there's some output.

    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.CharIn
    
    

    And 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 :|
  • I've been working on how to cycle through the TAP states and I'm stuck using the brute force method right now. I know there's a much better way to do this but I'm scratching my head right now.
    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 error
    
    

    I 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.
  • cheezuscheezus Posts: 146
    edited 2019-02-12 - 05:41:56
    I've been chugging along, trying to solve the problems as I go. Most of it's been "brute force" method so not elegant.. I think I only have 1 major obstacle at this point and that's dealing with transfers greater than 32 bits. SDR has 1, 7, 32, 274 and 281bit packets. So I'll need to convert the long A$$ hex strings into 9 longs, and then send and receive multiple longs. Do some masking. Haven't really thought it through. I've done some refactoring and the code is looking better at least.

    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", 0    
    
    


    The 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?
    
  • One of the things that really needs work is this. It technically works but is ugggggggly.
    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 0
    
    
  • Not too clear on how the modes are represented, but if there are 4 modes and 16 states perhaps you could represent them as 0..3 for the modes and 0..15 for the states. That way you could shift the mode value left 4 and add it to the state to produce a value between 0..63 that could be used in a case statement.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • I've given up on improving the mode switching for now. The TAP state machine is really simple, with 6 steady states. There's 2 registers, instruction and data and a pretty simple flow control. The basics were already implemented but I had to add a bit of logic to the low level driver to handle it. There's a pretty good example at the top of page 2 in the Xilinx appnote I've been using for reference. https://www.xilinx.com/support/documentation/application_notes/xapp503.pdf

    I 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 :wink:

    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 :lol:
    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",0     
    
  • jmgjmg Posts: 13,108
    cheezus wrote: »
    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.

    That'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.

  • jmg wrote: »
    That'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_Pulse
    

    The 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. :lol:
  • I keep coming back to the way the svf file is parsed and it screams to be improved. The way things are parsed right now works pretty well for everything except sending "big data". I think need to check the command before creating my standard parameter list. I'm going through and trying to clean things up and break them into better modules. The code I have that parses a parameter list works but the way I handle leading and trailing spaces is kinda kludgy.
    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 t
    

    The 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]))
            return
    

    The 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!
Sign In or Register to comment.