2 # SPDX-License-Identifier: GPL-2.0-or-later
4 # Copyright 2008, SoftPLC Corporation http://softplc.com
5 # Dick Hollenbeck dick@softplc.com
7 # A python program to convert an SVF file to an XSVF file. There is an
8 # option to include comments containing the source file line number from the origin
9 # SVF file before each outputted XSVF statement.
11 # We deviate from the XSVF spec in that we introduce a new command called
12 # XWAITSTATE which directly flows from the SVF RUNTEST command. Unfortunately
13 # XRUNSTATE was ill conceived and is not used here. We also add support for the
14 # three Lattice extensions to SVF: LCOUNT, LDELAY, and LSDR. The xsvf file
15 # generated from this program is suitable for use with the xsvf player in
16 # OpenOCD with my modifications to xsvf.c.
18 # This program is written for python 3.0, and it is not easy to change this
19 # back to 2.x. You may find it easier to use python 3.x even if that means
28 # There are both ---<Lexer>--- and ---<Parser>--- sections to this program
31 if len( sys
.argv
) < 3:
32 print("usage %s <svf_filename> <xsvf_filename>" % sys
.argv
[0])
36 inputFilename
= sys
.argv
[1]
37 outputFilename
= sys
.argv
[2]
39 doCOMMENTs
= True # Save XCOMMENTs in the output xsvf file
40 #doCOMMENTs = False # Save XCOMMENTs in the output xsvf file
42 # pick your file encoding
43 file_encoding
= 'ISO-8859-1'
44 #file_encoding = 'utf-8'
47 xrepeat
= 0 # argument to XREPEAT, gives retry count for masked compares
50 #-----< Lexer >---------------------------------------------------------------
52 StateBin
= (RESET
,IDLE
,
53 DRSELECT
,DRCAPTURE
,DRSHIFT
,DREXIT1
,DRPAUSE
,DREXIT2
,DRUPDATE
,
54 IRSELECT
,IRCAPTURE
,IRSHIFT
,IREXIT1
,IRPAUSE
,IREXIT2
,IRUPDATE
) = range(16)
56 # Any integer index into this tuple will be equal to its corresponding StateBin value
57 StateTxt
= ("RESET","IDLE",
58 "DRSELECT","DRCAPTURE","DRSHIFT","DREXIT1","DRPAUSE","DREXIT2","DRUPDATE",
59 "IRSELECT","IRCAPTURE","IRSHIFT","IREXIT1","IRPAUSE","IREXIT2","IRUPDATE")
62 (XCOMPLETE
,XTDOMASK
,XSIR
,XSDR
,XRUNTEST
,hole0
,hole1
,XREPEAT
,XSDRSIZE
,XSDRTDO
,
63 XSETSDRMASKS
,XSDRINC
,XSDRB
,XSDRC
,XSDRE
,XSDRTDOB
,XSDRTDOC
,
64 XSDRTDOE
,XSTATE
,XENDIR
,XENDDR
,XSIR2
,XCOMMENT
,XWAIT
,XWAITSTATE
,
65 LCOUNT
,LDELAY
,LSDR
,XTRST
) = range(29)
67 #Note: LCOUNT, LDELAY, and LSDR are Lattice extensions to SVF and provide a way to loop back
68 # and check a completion status, essentially waiting on a part until it signals that it is done.
69 # For example below: loop 25 times, each time through the loop do a LDELAY (same as a true RUNTEST)
70 # and exit loop when LSDR compares match.
73 ! Step to DRPAUSE give 5 clocks and wait for 1.00e+000 SEC.
74 LDELAY DRPAUSE 5 TCK 1.00E-003 SEC;
75 ! Test for the completed status. Match means pass.
76 ! Loop back to LDELAY line if not match and loop count less than 25.
81 #XTRST is an opcode Xilinx seemed to have missed and it comes from the SVF TRST statement.
85 def s_ident(scanner
, token
): return ("ident", token
.upper(), LineNumber
)
87 def s_hex(scanner
, token
):
89 LineNumber
= LineNumber
+ token
.count('\n')
90 token
= ''.join(token
.split())
91 return ("hex", token
[1:-1], LineNumber
)
93 def s_int(scanner
, token
): return ("int", int(token
), LineNumber
)
94 def s_float(scanner
, token
): return ("float", float(token
), LineNumber
)
95 #def s_comment(scanner, token): return ("comment", token, LineNumber)
96 def s_semicolon(scanner
, token
): return ("semi", token
, LineNumber
)
98 def s_nl(scanner
,token
):
100 LineNumber
= LineNumber
+ 1
101 #print( 'LineNumber=', LineNumber, file=sys.stderr )
106 scanner
= re
.Scanner([
107 (r
"[a-zA-Z]\w*", s_ident
),
108 # (r"[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?", s_float),
109 (r
"[-+]?[0-9]+(([.][0-9eE+-]*)|([eE]+[-+]?[0-9]+))", s_float
),
111 (r
"\(([0-9a-fA-F]|\s)*\)", s_hex
),
112 (r
"(!|//).*$", None),
120 # open the file using the given encoding
121 file = open( sys
.argv
[1], encoding
=file_encoding
)
123 # read all svf file input into string "input"
129 # create a list of tuples containing (tokenType, tokenValue, LineNumber)
130 tokens
= scanner
.scan( input )[0]
132 input = None # allow gc to reclaim memory holding file
134 #for tokenType, tokenValue, ln in tokens: print( "line %d: %s" % (ln, tokenType), tokenValue )
137 #-----<parser>-----------------------------------------------------------------
139 tokVal
= tokType
= tokLn
= None
145 Function to read the next token from tup into tokType, tokVal, tokLn (linenumber)
148 global tokType
, tokVal
, tokLn
, tup
149 tokType
, tokVal
, tokLn
= tup
.__next
__()
152 class ParseError(Exception):
153 """A class to hold a parsing error message"""
154 def __init__(self
, linenumber
, token
, message
):
155 self
.linenumber
= linenumber
157 self
.message
= message
160 return "Error in file \'%s\' at line %d near token %s\n %s" % (
161 inputFilename
, self
.linenumber
, repr(self
.token
), self
.message
)
164 class MASKSET(object):
166 Class MASKSET holds a set of bit vectors, all of which are related, will all
167 have the same length, and are associated with one of the seven shiftOps:
168 HIR, HDR, TIR, TDR, SIR, SDR, LSDR. One of these holds a mask, smask, tdi, tdo, and a
171 def __init__(self
, name
):
176 self
.mask
= bytearray()
177 self
.smask
= bytearray()
178 self
.tdi
= bytearray()
179 self
.tdo
= bytearray()
182 def syncLengths( self
, sawTDI
, sawTDO
, sawMASK
, sawSMASK
, newSize
):
184 Set all the lengths equal in the event some of the masks were
185 not seen as part of the last change set.
187 if self
.size
== newSize
:
194 # If an SIR was given without a MASK(), then use a mask of all zeros.
195 # this is not consistent with the SVF spec, but it makes sense because
196 # it would be odd to be testing an instruction register read out of a
197 # tap without giving a mask for it. Also, lattice seems to agree and is
198 # generating SVF files that comply with this philosophy.
199 if self
.name
== 'SIR' and not sawMASK
:
200 self
.mask
= bytearray( newSize
)
202 if newSize
!= len(self
.mask
):
203 self
.mask
= bytearray( newSize
)
204 if self
.name
== 'SDR': # leave mask for HIR,HDR,TIR,TDR,SIR zeros
205 for i
in range( newSize
):
208 if newSize
!= len(self
.tdo
):
209 self
.tdo
= bytearray( newSize
)
211 if newSize
!= len(self
.tdi
):
212 self
.tdi
= bytearray( newSize
)
214 if newSize
!= len(self
.smask
):
215 self
.smask
= bytearray( newSize
)
218 #-----</MASKSET>-----
221 def makeBitArray( hexString
, bitCount
):
223 Converts a packed sequence of hex ascii characters into a bytearray where
224 each element in the array holds exactly one bit. Only "bitCount" bits are
225 scanned and these must be the least significant bits in the hex number. That
226 is, it is legal to have some unused bits in the must significant hex nibble
227 of the input "hexString". The string is scanned starting from the backend,
228 then just before returning we reverse the array. This way the append()
229 method can be used, which I assume is faster than an insert.
234 hexString
= list(hexString
)
241 for mask
in [1,2,4,8]:
245 a
.append( (c
& mask
) != 0 )
247 raise ParseError( tokLn
, hexString
, "Insufficient hex characters for given length of %d" % bitCount
)
253 def makeXSVFbytes( bitarray
):
255 Make a bytearray which is contains the XSVF bits which will be written
256 directly to disk. The number of bytes needed is calculated from the size
257 of the argument bitarray.
259 bitCount
= len(bitarray
)
260 byteCount
= (bitCount
+7)//8
261 ba
= bytearray( byteCount
)
262 firstBit
= (bitCount
% 8) - 1
266 for byteNdx
in range(byteCount
):
279 def writeComment( outputFile
, shiftOp_linenum
, shiftOp
):
281 Write an XCOMMENT record to outputFile
283 comment
= "%s @%d\0" % (shiftOp
, shiftOp_linenum
) # \0 is terminating nul
286 ba
+= comment
.encode()
287 outputFile
.write( ba
)
290 def combineBitVectors( trailer
, meat
, header
):
292 Combine the 3 bit vectors comprizing a transmission. Since the least
293 significant bits are sent first, the header is put onto the list last so
294 they are sent first from that least significant position.
297 ret
.extend( trailer
)
303 def writeRUNTEST( outputFile
, run_state
, end_state
, run_count
, min_time
, tokenTxt
):
305 Write the output for the SVF RUNTEST command.
306 run_count - the number of clocks
307 min_time - the number of seconds
308 tokenTxt - either RUNTEST or LDELAY
310 # convert from secs to usecs
311 min_time
= int( min_time
* 1000000)
313 # the SVF RUNTEST command does NOT map to the XSVF XRUNTEST command. Check the SVF spec, then
314 # read the XSVF command. They are not the same. Use an XSVF XWAITSTATE to
315 # implement the required behavior of the SVF RUNTEST command.
317 writeComment( output
, tokLn
, tokenTxt
)
319 if tokenTxt
== 'RUNTEST':
324 struct
.pack_into(">i", obuf
, 3, run_count
) # big endian 4 byte int to obuf
325 struct
.pack_into(">i", obuf
, 7, min_time
) # big endian 4 byte int to obuf
326 outputFile
.write( obuf
)
331 # LDELAY has no end_state
332 struct
.pack_into(">i", obuf
, 2, run_count
) # big endian 4 byte int to obuf
333 struct
.pack_into(">i", obuf
, 6, min_time
) # big endian 4 byte int to obuf
334 outputFile
.write( obuf
)
337 output
= open( outputFilename
, mode
='wb' )
350 # one of the commands that take the shiftParts after the length, the parse
351 # template for all of these commands is identical
352 shiftOps
= ('SDR', 'SIR', 'LSDR', 'HDR', 'HIR', 'TDR', 'TIR')
354 # the order must correspond to shiftOps, this holds the MASKSETS. 'LSDR' shares sdr with 'SDR'
355 shiftSets
= (sdr
, sir
, sdr
, hdr
, hir
, tdr
, tir
)
357 # what to expect as parameters to a shiftOp, i.e. after a SDR length or SIR length
358 shiftParts
= ('TDI', 'TDO', 'MASK', 'SMASK')
360 # the set of legal states which can trail the RUNTEST command
361 run_state_allowed
= ('IRPAUSE', 'DRPAUSE', 'RESET', 'IDLE')
363 enddr_state_allowed
= ('DRPAUSE', 'IDLE')
364 endir_state_allowed
= ('IRPAUSE', 'IDLE')
366 trst_mode_allowed
= ('ON', 'OFF', 'Z', 'ABSENT')
371 frequency
= 1.00e+006 # HZ;
373 # change detection for xsdrsize and xtdomask
374 xsdrsize
= -1 # the last one sent, send only on change
375 xtdomask
= bytearray() # the last one sent, send only on change
378 # we use a number of single byte writes for the XSVF command below
379 cmdbuf
= bytearray(1)
382 # Save the XREPEAT setting into the file as first thing.
393 expecting_eof
= False
394 # print( tokType, tokVal, tokLn )
396 if tokVal
in shiftOps
:
397 shiftOp_linenum
= tokLn
400 set = shiftSets
[shiftOps
.index(shiftOp
)]
402 # set flags false, if we see one later, set that one true later
403 sawTDI
= sawTDO
= sawMASK
= sawSMASK
= False
407 raise ParseError( tokLn
, tokVal
, "Expecting 'int' giving %s length, got '%s'" % (shiftOp
, tokType
) )
413 if tokVal
not in shiftParts
:
414 raise ParseError( tokLn
, tokVal
, "Expecting TDI, TDO, MASK, SMASK, or ';'")
420 raise ParseError( tokLn
, tokVal
, "Expecting hex bits" )
421 bits
= makeBitArray( tokVal
, length
)
423 if shiftPart
== 'TDI':
427 elif shiftPart
== 'TDO':
431 elif shiftPart
== 'MASK':
435 elif shiftPart
== 'SMASK':
441 set.syncLengths( sawTDI
, sawTDO
, sawMASK
, sawSMASK
, length
)
443 # process all the gathered parameters and generate outputs here
446 writeComment( output
, shiftOp_linenum
, 'SIR' )
448 tdi
= combineBitVectors( tir
.tdi
, sir
.tdi
, hir
.tdi
)
452 struct
.pack_into( ">h", obuf
, 1, len(tdi
) )
458 obuf
= makeXSVFbytes( tdi
)
461 elif shiftOp
== 'SDR':
463 writeComment( output
, shiftOp_linenum
, shiftOp
)
466 # pass a zero filled bit vector for the sdr.mask
467 mask
= combineBitVectors( tdr
.mask
, bytearray(sdr
.size
), hdr
.mask
)
468 tdi
= combineBitVectors( tdr
.tdi
, sdr
.tdi
, hdr
.tdi
)
470 if xsdrsize
!= len(tdi
):
473 output
.write( cmdbuf
)
475 struct
.pack_into( ">i", obuf
, 0, xsdrsize
) # big endian 4 byte int to obuf
481 output
.write( cmdbuf
)
482 obuf
= makeXSVFbytes( mask
)
486 output
.write( cmdbuf
)
487 obuf
= makeXSVFbytes( tdi
)
491 mask
= combineBitVectors( tdr
.mask
, sdr
.mask
, hdr
.mask
)
492 tdi
= combineBitVectors( tdr
.tdi
, sdr
.tdi
, hdr
.tdi
)
493 tdo
= combineBitVectors( tdr
.tdo
, sdr
.tdo
, hdr
.tdo
)
495 if xsdrsize
!= len(tdi
):
498 output
.write( cmdbuf
)
500 struct
.pack_into(">i", obuf
, 0, xsdrsize
) # big endian 4 byte int to obuf
506 output
.write( cmdbuf
)
507 obuf
= makeXSVFbytes( mask
)
511 output
.write( cmdbuf
)
512 obuf
= makeXSVFbytes( tdi
)
514 obuf
= makeXSVFbytes( tdo
)
516 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
518 elif shiftOp
== 'LSDR':
520 writeComment( output
, shiftOp_linenum
, shiftOp
)
522 mask
= combineBitVectors( tdr
.mask
, sdr
.mask
, hdr
.mask
)
523 tdi
= combineBitVectors( tdr
.tdi
, sdr
.tdi
, hdr
.tdi
)
524 tdo
= combineBitVectors( tdr
.tdo
, sdr
.tdo
, hdr
.tdo
)
526 if xsdrsize
!= len(tdi
):
529 output
.write( cmdbuf
)
531 struct
.pack_into(">i", obuf
, 0, xsdrsize
) # big endian 4 byte int to obuf
537 output
.write( cmdbuf
)
538 obuf
= makeXSVFbytes( mask
)
542 output
.write( cmdbuf
)
543 obuf
= makeXSVFbytes( tdi
)
545 obuf
= makeXSVFbytes( tdo
)
547 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
549 elif tokVal
== 'RUNTEST' or tokVal
== 'LDELAY':
550 # e.g. from lattice tools:
551 # "RUNTEST IDLE 5 TCK 1.00E-003 SEC;"
556 max_time
= 600 # ten minutes
557 if tokVal
in run_state_allowed
:
558 run_state
= StateTxt
.index(tokVal
)
559 end_state
= run_state
# bottom of page 17 of SVF spec
561 if tokType
!= 'int' and tokType
!= 'float':
562 raise ParseError( tokLn
, tokVal
, "Expecting 'int' or 'float' after RUNTEST [run_state]")
565 if tokVal
!= 'TCK' and tokVal
!= 'SEC' and tokVal
!= 'SCK':
566 raise ParseError( tokLn
, tokVal
, "Expecting 'TCK' or 'SEC' or 'SCK' after RUNTEST [run_state] (run_count|min_time)")
567 if tokVal
== 'TCK' or tokVal
== 'SCK':
568 run_count
= int( timeval
)
572 if tokType
== 'int' or tokType
== 'float':
576 raise ParseError( tokLn
, tokVal
, "Expecting 'SEC' after RUNTEST [run_state] run_count min_time")
578 if tokVal
== 'MAXIMUM':
580 if tokType
!= 'int' and tokType
!= 'float':
581 raise ParseError( tokLn
, tokVal
, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM")
585 raise ParseError( tokLn
, tokVal
, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM max_time")
587 if tokVal
== 'ENDSTATE':
589 if tokVal
not in run_state_allowed
:
590 raise ParseError( tokLn
, tokVal
, "Expecting 'run_state' after RUNTEST .... ENDSTATE")
591 end_state
= StateTxt
.index(tokVal
)
594 raise ParseError( tokLn
, tokVal
, "Expecting ';' after RUNTEST ....")
595 # print( "run_count=", run_count, "min_time=", min_time,
596 # "max_time=", max_time, "run_state=", State[run_state], "end_state=", State[end_state] )
597 writeRUNTEST( output
, run_state
, end_state
, run_count
, min_time
, saveTok
)
599 elif tokVal
== 'LCOUNT':
602 raise ParseError( tokLn
, tokVal
, "Expecting integer 'count' after LCOUNT")
606 raise ParseError( tokLn
, tokVal
, "Expecting ';' after LCOUNT count")
608 writeComment( output
, tokLn
, 'LCOUNT' )
611 struct
.pack_into(">i", obuf
, 1, loopCount
) # big endian 4 byte int to obuf
614 elif tokVal
== 'ENDDR':
616 if tokVal
not in enddr_state_allowed
:
617 raise ParseError( tokLn
, tokVal
, "Expecting 'stable_state' after ENDDR. (one of: DRPAUSE, IDLE)")
618 enddr_state
= StateTxt
.index(tokVal
)
621 raise ParseError( tokLn
, tokVal
, "Expecting ';' after ENDDR stable_state")
623 writeComment( output
, tokLn
, 'ENDDR' )
626 # Page 10 of the March 1999 SVF spec shows that RESET is also allowed here.
627 # Yet the XSVF spec has no provision for that, and uses a non-standard, i.e.
628 # boolean argument to XENDDR which only handles two of the 3 intended states.
629 obuf
[1] = 1 if enddr_state
== DRPAUSE
else 0
632 elif tokVal
== 'ENDIR':
634 if tokVal
not in endir_state_allowed
:
635 raise ParseError( tokLn
, tokVal
, "Expecting 'stable_state' after ENDIR. (one of: IRPAUSE, IDLE)")
636 endir_state
= StateTxt
.index(tokVal
)
639 raise ParseError( tokLn
, tokVal
, "Expecting ';' after ENDIR stable_state")
641 writeComment( output
, tokLn
, 'ENDIR' )
644 # Page 10 of the March 1999 SVF spec shows that RESET is also allowed here.
645 # Yet the XSVF spec has no provision for that, and uses a non-standard, i.e.
646 # boolean argument to XENDDR which only handles two of the 3 intended states.
647 obuf
[1] = 1 if endir_state
== IRPAUSE
else 0
650 elif tokVal
== 'STATE':
654 if tokVal
not in StateTxt
:
655 raise ParseError( tokLn
, tokVal
, "Expecting 'stable_state' after STATE")
656 stable_state
= StateTxt
.index( tokVal
)
658 if doCOMMENTs
and ln
!= -1:
659 writeComment( output
, ln
, 'STATE' )
660 ln
= -1 # save comment only once
664 obuf
[1] = stable_state
668 elif tokVal
== 'FREQUENCY':
671 if tokType
!= 'int' and tokType
!= 'float':
672 raise ParseError( tokLn
, tokVal
, "Expecting 'cycles HZ' after FREQUENCY")
676 raise ParseError( tokLn
, tokVal
, "Expecting 'HZ' after FREQUENCY cycles")
679 raise ParseError( tokLn
, tokVal
, "Expecting ';' after FREQUENCY cycles HZ")
681 elif tokVal
== 'TRST':
683 if tokVal
not in trst_mode_allowed
:
684 raise ParseError( tokLn
, tokVal
, "Expecting 'ON|OFF|Z|ABSENT' after TRST")
688 raise ParseError( tokLn
, tokVal
, "Expecting ';' after TRST trst_mode")
690 writeComment( output
, tokLn
, 'TRST %s' % trst_mode
)
691 obuf
= bytearray( 2 )
693 obuf
[1] = trst_mode_allowed
.index( trst_mode
) # use the index as the binary argument to XTRST opcode
697 raise ParseError( tokLn
, tokVal
, "Unknown token '%s'" % tokVal
)
699 except StopIteration:
700 if not expecting_eof
:
701 print( "Unexpected End of File at line ", tokLn
)
703 except ParseError
as pe
:
707 # print( "closing file" )
708 cmdbuf
[0] = XCOMPLETE
709 output
.write( cmdbuf
)