3 # Copyright 2008, SoftPLC Corporation http://softplc.com
4 # Dick Hollenbeck dick@softplc.com
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, you may find one here:
19 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 # or you may search the http://www.gnu.org website for the version 2 license,
21 # or you may write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 # A python program to convert an SVF file to an XSVF file. There is an
26 # option to include comments containing the source file line number from the origin
27 # SVF file before each outputted XSVF statement.
29 # We deviate from the XSVF spec in that we introduce a new command called
30 # XWAITSTATE which directly flows from the SVF RUNTEST command. Unfortunately
31 # XRUNSTATE was ill conceived and is not used here. We also add support for the
32 # three Lattice extensions to SVF: LCOUNT, LDELAY, and LSDR. The xsvf file
33 # generated from this program is suitable for use with the xsvf player in
34 # OpenOCD with my modifications to xsvf.c.
36 # This program is written for python 3.0, and it is not easy to change this
37 # back to 2.x. You may find it easier to use python 3.x even if that means
46 # There are both ---<Lexer>--- and ---<Parser>--- sections to this program
49 if len( sys.argv ) < 3:
50 print("usage %s <svf_filename> <xsvf_filename>" % sys.argv[0])
54 inputFilename = sys.argv[1]
55 outputFilename = sys.argv[2]
57 doCOMMENTs = True # Save XCOMMENTs in the output xsvf file
58 #doCOMMENTs = False # Save XCOMMENTs in the output xsvf file
60 xrepeat = 0 # argument to XREPEAT, gives retry count for masked compares
63 #-----< Lexer >---------------------------------------------------------------
65 StateBin = (RESET,IDLE,
66 DRSELECT,DRCAPTURE,DRSHIFT,DREXIT1,DRPAUSE,DREXIT2,DRUPDATE,
67 IRSELECT,IRCAPTURE,IRSHIFT,IREXIT1,IRPAUSE,IREXIT2,IRUPDATE) = range(16)
69 # Any integer index into this tuple will be equal to its corresponding StateBin value
70 StateTxt = ("RESET","IDLE",
71 "DRSELECT","DRCAPTURE","DRSHIFT","DREXIT1","DRPAUSE","DREXIT2","DRUPDATE",
72 "IRSELECT","IRCAPTURE","IRSHIFT","IREXIT1","IRPAUSE","IREXIT2","IRUPDATE")
75 (XCOMPLETE,XTDOMASK,XSIR,XSDR,XRUNTEST,hole0,hole1,XREPEAT,XSDRSIZE,XSDRTDO,
76 XSETSDRMASKS,XSDRINC,XSDRB,XSDRC,XSDRE,XSDRTDOB,XSDRTDOC,
77 XSDRTDOE,XSTATE,XENDIR,XENDDR,XSIR2,XCOMMENT,XWAIT,XWAITSTATE,LCOUNT,LDELAY,LSDR) = range(28)
79 #Note: LCOUNT, LDELAY, and LSDR are Lattice extensions to SVF and provide a way to loop back
80 # and check a completion status, essentially waiting on a part until it signals that it is done.
81 # For example below: loop 25 times, each time through the loop do a LDELAY (same as a true RUNTEST)
82 # and exit loop when LSDR compares match.
85 ! Step to DRPAUSE give 5 clocks and wait for 1.00e+000 SEC.
86 LDELAY DRPAUSE 5 TCK 1.00E-003 SEC;
87 ! Test for the completed status. Match means pass.
88 ! Loop back to LDELAY line if not match and loop count less than 25.
95 def s_ident(scanner, token): return ("ident", token.upper(), LineNumber)
97 def s_hex(scanner, token):
99 LineNumber = LineNumber + token.count('\n')
100 token = ''.join(token.split())
101 return ("hex", token[1:-1], LineNumber)
103 def s_int(scanner, token): return ("int", int(token), LineNumber)
104 def s_float(scanner, token): return ("float", float(token), LineNumber)
105 #def s_comment(scanner, token): return ("comment", token, LineNumber)
106 def s_semicolon(scanner, token): return ("semi", token, LineNumber)
108 def s_nl(scanner,token):
110 LineNumber = LineNumber + 1
111 #print( 'LineNumber=', LineNumber, file=sys.stderr )
116 scanner = re.Scanner([
117 (r"[a-zA-Z]\w*", s_ident),
118 # (r"[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?", s_float),
119 (r"[-+]?[0-9]+(([.][0-9eE+-]*)|([eE]+[-+]?[0-9]+))", s_float),
121 (r"\(([0-9a-fA-F]|\s)*\)", s_hex),
122 (r"(!|//).*$", None),
130 # read all svf file input into string "input"
131 input = open( sys.argv[1] ).read()
134 # create a list of tuples containing (tokenType, tokenValue, LineNumber)
135 tokens = scanner.scan( input )[0]
137 input = None # allow gc to reclaim memory holding file
139 #for tokenType, tokenValue, ln in tokens: print( "line %d: %s" % (ln, tokenType), tokenValue )
142 #-----<parser>-----------------------------------------------------------------
144 tokVal = tokType = tokLn = None
150 Function to read the next token from tup into tokType, tokVal, tokLn (linenumber)
153 global tokType, tokVal, tokLn, tup
154 tokType, tokVal, tokLn = tup.__next__()
157 class ParseError(Exception):
158 """A class to hold a parsing error message"""
159 def __init__(self, linenumber, token, message):
160 self.linenumber = linenumber
162 self.message = message
165 return "Error in file \'%s\' at line %d near token %s\n %s" % (
166 inputFilename, self.linenumber, repr(self.token), self.message)
169 class MASKSET(object):
171 Class MASKSET holds a set of bit vectors, all of which are related, will all
172 have the same length, and are associated with one of the seven shiftOps:
173 HIR, HDR, TIR, TDR, SIR, SDR, LSDR. One of these holds a mask, smask, tdi, tdo, and a
176 def __init__(self, name):
181 self.mask = bytearray()
182 self.smask = bytearray()
183 self.tdi = bytearray()
184 self.tdo = bytearray()
187 def syncLengths( self, sawTDI, sawTDO, sawMASK, sawSMASK, newSize ):
189 Set all the lengths equal in the event some of the masks were
190 not seen as part of the last change set.
192 if self.size == newSize:
199 # If an SIR was given without a MASK(), then use a mask of all zeros.
200 # this is not consistent with the SVF spec, but it makes sense because
201 # it would be odd to be testing an instruction register read out of a
202 # tap without giving a mask for it. Also, lattice seems to agree and is
203 # generating SVF files that comply with this philosophy.
204 if self.name == 'SIR' and not sawMASK:
205 self.mask = bytearray( newSize )
207 if newSize != len(self.mask):
208 self.mask = bytearray( newSize )
209 if self.name == 'SDR': # leave mask for HIR,HDR,TIR,TDR,SIR zeros
210 for i in range( newSize ):
213 if newSize != len(self.tdo):
214 self.tdo = bytearray( newSize )
216 if newSize != len(self.tdi):
217 self.tdi = bytearray( newSize )
219 if newSize != len(self.smask):
220 self.smask = bytearray( newSize )
223 #-----</MASKSET>-----
226 def makeBitArray( hexString, bitCount ):
228 Converts a packed sequence of hex ascii characters into a bytearray where
229 each element in the array holds exactly one bit. Only "bitCount" bits are
230 scanned and these must be the least significant bits in the hex number. That
231 is, it is legal to have some unused bits in the must significant hex nibble
232 of the input "hexString". The string is scanned starting from the backend,
233 then just before returning we reverse the array. This way the append()
234 method can be used, which I assume is faster than an insert.
239 hexString = list(hexString)
246 for mask in [1,2,4,8]:
250 a.append( (c & mask) != 0 )
252 raise ParseError( tokLn, hexString, "Insufficient hex characters for given length of %d" % bitCount )
258 def makeXSVFbytes( bitarray ):
260 Make a bytearray which is contains the XSVF bits which will be written
261 directly to disk. The number of bytes needed is calculated from the size
262 of the argument bitarray.
264 bitCount = len(bitarray)
265 byteCount = (bitCount+7)//8
266 ba = bytearray( byteCount )
267 firstBit = (bitCount % 8) - 1
271 for byteNdx in range(byteCount):
284 def writeComment( outputFile, shiftOp_linenum, shiftOp ):
286 Write an XCOMMENT record to outputFile
288 comment = "%s @%d\0" % (shiftOp, shiftOp_linenum) # \0 is terminating nul
291 ba += comment.encode()
292 outputFile.write( ba )
295 def combineBitVectors( trailer, meat, header ):
297 Combine the 3 bit vectors comprizing a transmission. Since the least
298 significant bits are sent first, the header is put onto the list last so
299 they are sent first from that least significant position.
302 ret.extend( trailer )
308 def writeRUNTEST( outputFile, run_state, end_state, run_count, min_time, tokenTxt ):
310 Write the output for the SVF RUNTEST command.
311 run_count - the number of clocks
312 min_time - the number of seconds
313 tokenTxt - either RUNTEST or LDELAY
315 # convert from secs to usecs
316 min_time = int( min_time * 1000000)
318 # the SVF RUNTEST command does NOT map to the XSVF XRUNTEST command. Check the SVF spec, then
319 # read the XSVF command. They are not the same. Use an XSVF XWAITSTATE to
320 # implement the required behavior of the SVF RUNTEST command.
322 writeComment( output, tokLn, tokenTxt )
324 if tokenTxt == 'RUNTEST':
329 struct.pack_into(">i", obuf, 3, run_count ) # big endian 4 byte int to obuf
330 struct.pack_into(">i", obuf, 7, min_time ) # big endian 4 byte int to obuf
331 outputFile.write( obuf )
336 # LDELAY has no end_state
337 struct.pack_into(">i", obuf, 2, run_count ) # big endian 4 byte int to obuf
338 struct.pack_into(">i", obuf, 6, min_time ) # big endian 4 byte int to obuf
339 outputFile.write( obuf )
342 output = open( outputFilename, mode='wb' )
355 # one of the commands that take the shiftParts after the length, the parse
356 # template for all of these commands is identical
357 shiftOps = ('SDR', 'SIR', 'LSDR', 'HDR', 'HIR', 'TDR', 'TIR')
359 # the order must correspond to shiftOps, this holds the MASKSETS. 'LSDR' shares sdr with 'SDR'
360 shiftSets = (sdr, sir, sdr, hdr, hir, tdr, tir )
362 # what to expect as parameters to a shiftOp, i.e. after a SDR length or SIR length
363 shiftParts = ('TDI', 'TDO', 'MASK', 'SMASK')
365 # the set of legal states which can trail the RUNTEST command
366 run_state_allowed = ('IRPAUSE', 'DRPAUSE', 'RESET', 'IDLE')
368 enddr_state_allowed = ('DRPAUSE', 'IDLE')
369 endir_state_allowed = ('IRPAUSE', 'IDLE')
374 frequency = 1.00e+006 # HZ;
376 # change detection for xsdrsize and xtdomask
377 xsdrsize = -1 # the last one sent, send only on change
378 xtdomask = bytearray() # the last one sent, send only on change
381 # we use a number of single byte writes for the XSVF command below
382 cmdbuf = bytearray(1)
385 # Save the XREPEAT setting into the file as first thing.
396 expecting_eof = False
397 # print( tokType, tokVal, tokLn )
399 if tokVal in shiftOps:
400 shiftOp_linenum = tokLn
403 set = shiftSets[shiftOps.index(shiftOp)]
405 # set flags false, if we see one later, set that one true later
406 sawTDI = sawTDO = sawMASK = sawSMASK = False
410 raise ParseError( tokLn, tokVal, "Expecting 'int' giving %s length, got '%s'" % (shiftOp, tokType) )
416 if tokVal not in shiftParts:
417 raise ParseError( tokLn, tokVal, "Expecting TDI, TDO, MASK, SMASK, or ';'")
423 raise ParseError( tokLn, tokVal, "Expecting hex bits" )
424 bits = makeBitArray( tokVal, length )
426 if shiftPart == 'TDI':
430 elif shiftPart == 'TDO':
434 elif shiftPart == 'MASK':
438 elif shiftPart == 'SMASK':
444 set.syncLengths( sawTDI, sawTDO, sawMASK, sawSMASK, length )
446 # process all the gathered parameters and generate outputs here
449 writeComment( output, shiftOp_linenum, 'SIR' )
451 tdi = combineBitVectors( tir.tdi, sir.tdi, hir.tdi )
455 struct.pack_into( ">h", obuf, 1, len(tdi) )
461 obuf = makeXSVFbytes( tdi )
464 elif shiftOp == 'SDR':
466 writeComment( output, shiftOp_linenum, shiftOp )
469 # pass a zero filled bit vector for the sdr.mask
470 mask = combineBitVectors( tdr.mask, bytearray(sdr.size), hdr.mask )
471 tdi = combineBitVectors( tdr.tdi, sdr.tdi, hdr.tdi )
473 if xsdrsize != len(tdi):
476 output.write( cmdbuf )
478 struct.pack_into( ">i", obuf, 0, xsdrsize ) # big endian 4 byte int to obuf
484 output.write( cmdbuf )
485 obuf = makeXSVFbytes( mask )
489 output.write( cmdbuf )
490 obuf = makeXSVFbytes( tdi )
494 mask = combineBitVectors( tdr.mask, sdr.mask, hdr.mask )
495 tdi = combineBitVectors( tdr.tdi, sdr.tdi, hdr.tdi )
496 tdo = combineBitVectors( tdr.tdo, sdr.tdo, hdr.tdo )
498 if xsdrsize != len(tdi):
501 output.write( cmdbuf )
503 struct.pack_into(">i", obuf, 0, xsdrsize ) # big endian 4 byte int to obuf
509 output.write( cmdbuf )
510 obuf = makeXSVFbytes( mask )
514 output.write( cmdbuf )
515 obuf = makeXSVFbytes( tdi )
517 obuf = makeXSVFbytes( tdo )
519 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
521 elif shiftOp == 'LSDR':
523 writeComment( output, shiftOp_linenum, shiftOp )
525 mask = combineBitVectors( tdr.mask, sdr.mask, hdr.mask )
526 tdi = combineBitVectors( tdr.tdi, sdr.tdi, hdr.tdi )
527 tdo = combineBitVectors( tdr.tdo, sdr.tdo, hdr.tdo )
529 if xsdrsize != len(tdi):
532 output.write( cmdbuf )
534 struct.pack_into(">i", obuf, 0, xsdrsize ) # big endian 4 byte int to obuf
540 output.write( cmdbuf )
541 obuf = makeXSVFbytes( mask )
545 output.write( cmdbuf )
546 obuf = makeXSVFbytes( tdi )
548 obuf = makeXSVFbytes( tdo )
550 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
552 elif tokVal == 'RUNTEST' or tokVal == 'LDELAY':
553 # e.g. from lattice tools:
554 # "RUNTEST IDLE 5 TCK 1.00E-003 SEC;"
559 max_time = 600 # ten minutes
560 if tokVal in run_state_allowed:
561 run_state = StateTxt.index(tokVal)
562 end_state = run_state # bottom of page 17 of SVF spec
564 if tokType != 'int' and tokType != 'float':
565 raise ParseError( tokLn, tokVal, "Expecting 'int' or 'float' after RUNTEST [run_state]")
568 if tokVal != 'TCK' and tokVal != 'SEC' and tokVal != 'SCK':
569 raise ParseError( tokLn, tokVal, "Expecting 'TCK' or 'SEC' or 'SCK' after RUNTEST [run_state] (run_count|min_time)")
570 if tokVal == 'TCK' or tokVal == 'SCK':
571 run_count = int( timeval )
575 if tokType == 'int' or tokType == 'float':
579 raise ParseError( tokLn, tokVal, "Expecting 'SEC' after RUNTEST [run_state] run_count min_time")
581 if tokVal == 'MAXIMUM':
583 if tokType != 'int' and tokType != 'float':
584 raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM")
588 raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM max_time")
590 if tokVal == 'ENDSTATE':
592 if tokVal not in run_state_allowed:
593 raise ParseError( tokLn, tokVal, "Expecting 'run_state' after RUNTEST .... ENDSTATE")
594 end_state = StateTxt.index(tokVal)
597 raise ParseError( tokLn, tokVal, "Expecting ';' after RUNTEST ....")
598 # print( "run_count=", run_count, "min_time=", min_time,
599 # "max_time=", max_time, "run_state=", State[run_state], "end_state=", State[end_state] )
600 writeRUNTEST( output, run_state, end_state, run_count, min_time, saveTok )
602 elif tokVal == 'LCOUNT':
605 raise ParseError( tokLn, tokVal, "Expecting integer 'count' after LCOUNT")
609 raise ParseError( tokLn, tokVal, "Expecting ';' after LCOUNT count")
611 writeComment( output, tokLn, 'LCOUNT' )
614 struct.pack_into(">i", obuf, 1, loopCount ) # big endian 4 byte int to obuf
617 elif tokVal == 'ENDDR':
619 if tokVal not in enddr_state_allowed:
620 raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after ENDDR. (one of: DRPAUSE, IDLE)")
621 enddr_state = StateTxt.index(tokVal)
624 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDDR stable_state")
626 writeComment( output, tokLn, 'ENDDR' )
629 # Page 10 of the March 1999 SVF spec shows that RESET is also allowed here.
630 # Yet the XSVF spec has no provision for that, and uses a non-standard, i.e.
631 # boolean argument to XENDDR which only handles two of the 3 intended states.
632 obuf[1] = 1 if enddr_state == DRPAUSE else 0
635 elif tokVal == 'ENDIR':
637 if tokVal not in endir_state_allowed:
638 raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after ENDIR. (one of: IRPAUSE, IDLE)")
639 endir_state = StateTxt.index(tokVal)
642 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDIR stable_state")
644 writeComment( output, tokLn, 'ENDIR' )
647 # Page 10 of the March 1999 SVF spec shows that RESET is also allowed here.
648 # Yet the XSVF spec has no provision for that, and uses a non-standard, i.e.
649 # boolean argument to XENDDR which only handles two of the 3 intended states.
650 obuf[1] = 1 if endir_state == IRPAUSE else 0
653 elif tokVal == 'STATE':
657 if tokVal not in StateTxt:
658 raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after STATE")
659 stable_state = StateTxt.index( tokVal )
661 if doCOMMENTs and ln != -1:
662 writeComment( output, ln, 'STATE' )
663 ln = -1 # save comment only once
667 obuf[1] = stable_state
671 elif tokVal == 'FREQUENCY':
674 if tokType != 'int' and tokType != 'float':
675 raise ParseError( tokLn, tokVal, "Expecting 'cycles HZ' after FREQUENCY")
679 raise ParseError( tokLn, tokVal, "Expecting 'HZ' after FREQUENCY cycles")
682 raise ParseError( tokLn, tokVal, "Expecting ';' after FREQUENCY cycles HZ")
685 raise ParseError( tokLn, tokVal, "Unknown token '%s'" % tokVal)
687 except StopIteration:
688 if not expecting_eof:
689 print( "Unexpected End of File at line ", tokLn )
691 except ParseError as pe:
695 # print( "closing file" )
696 cmdbuf[0] = XCOMPLETE
697 output.write( cmdbuf )