contrib: replace the GPLv2-or-later license tag
[fw/openocd] / contrib / xsvf_tools / svf2xsvf.py
1 #!/usr/bin/python3.0
2 # SPDX-License-Identifier: GPL-2.0-or-later
3
4 # Copyright 2008, SoftPLC Corporation  http://softplc.com
5 # Dick Hollenbeck dick@softplc.com
6
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.
10 #
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.
17 #
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
20 # building it.
21
22
23 import re
24 import sys
25 import struct
26
27
28 # There are both ---<Lexer>--- and ---<Parser>--- sections to this program
29
30
31 if len( sys.argv ) < 3:
32     print("usage %s <svf_filename> <xsvf_filename>" % sys.argv[0])
33     exit(1)
34
35
36 inputFilename = sys.argv[1]
37 outputFilename = sys.argv[2]
38
39 doCOMMENTs = True       # Save XCOMMENTs in the output xsvf file
40 #doCOMMENTs = False       # Save XCOMMENTs in the output xsvf file
41
42 # pick your file encoding
43 file_encoding = 'ISO-8859-1'
44 #file_encoding = 'utf-8'
45
46
47 xrepeat = 0             # argument to XREPEAT, gives retry count for masked compares
48
49
50 #-----< Lexer >---------------------------------------------------------------
51
52 StateBin = (RESET,IDLE,
53     DRSELECT,DRCAPTURE,DRSHIFT,DREXIT1,DRPAUSE,DREXIT2,DRUPDATE,
54     IRSELECT,IRCAPTURE,IRSHIFT,IREXIT1,IRPAUSE,IREXIT2,IRUPDATE) = range(16)
55
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")
60
61
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)
66
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.
71 """
72 LCOUNT  25;
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.
77 LSDR  1 TDI  (0)
78         TDO  (1);
79 """
80
81 #XTRST is an opcode Xilinx seemed to have missed and it comes from the SVF TRST statement.
82
83 LineNumber = 1
84
85 def s_ident(scanner, token): return ("ident", token.upper(), LineNumber)
86
87 def s_hex(scanner, token):
88     global LineNumber
89     LineNumber = LineNumber + token.count('\n')
90     token = ''.join(token.split())
91     return ("hex", token[1:-1], LineNumber)
92
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)
97
98 def s_nl(scanner,token):
99     global LineNumber
100     LineNumber = LineNumber + 1
101     #print( 'LineNumber=', LineNumber, file=sys.stderr )
102     return None
103
104 #2.00E-002
105
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),
110     (r"\d+", s_int),
111     (r"\(([0-9a-fA-F]|\s)*\)", s_hex),
112     (r"(!|//).*$", None),
113     (r";", s_semicolon),
114     (r"\n",s_nl),
115     (r"\s*", None),
116     ],
117     re.MULTILINE
118     )
119
120 # open the file using the given encoding
121 file = open( sys.argv[1], encoding=file_encoding )
122
123 # read all svf file input into string "input"
124 input = file.read()
125
126 file.close()
127
128 # Lexer:
129 # create a list of tuples containing (tokenType, tokenValue, LineNumber)
130 tokens = scanner.scan( input )[0]
131
132 input = None    # allow gc to reclaim memory holding file
133
134 #for tokenType, tokenValue, ln in tokens: print( "line %d: %s" % (ln, tokenType), tokenValue )
135
136
137 #-----<parser>-----------------------------------------------------------------
138
139 tokVal = tokType = tokLn = None
140
141 tup = iter( tokens )
142
143 def nextTok():
144     """
145     Function to read the next token from tup into tokType, tokVal, tokLn (linenumber)
146     which are globals.
147     """
148     global tokType, tokVal, tokLn, tup
149     tokType, tokVal, tokLn = tup.__next__()
150
151
152 class ParseError(Exception):
153     """A class to hold a parsing error message"""
154     def __init__(self, linenumber, token, message):
155         self.linenumber = linenumber
156         self.token = token
157         self.message = message
158     def __str__(self):
159         global inputFilename
160         return "Error in file \'%s\' at line %d near token %s\n %s" % (
161                    inputFilename, self.linenumber, repr(self.token), self.message)
162
163
164 class MASKSET(object):
165     """
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
169     size.
170     """
171     def __init__(self, name):
172         self.empty()
173         self.name = name
174
175     def empty(self):
176         self.mask = bytearray()
177         self.smask = bytearray()
178         self.tdi = bytearray()
179         self.tdo = bytearray()
180         self.size = 0
181
182     def syncLengths( self, sawTDI, sawTDO, sawMASK, sawSMASK, newSize ):
183         """
184         Set all the lengths equal in the event some of the masks were
185         not seen as part of the last change set.
186         """
187         if self.size == newSize:
188             return
189
190         if newSize == 0:
191             self.empty()
192             return
193
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 )
201
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 ):
206                     self.mask[i] = 1
207
208         if newSize != len(self.tdo):
209             self.tdo = bytearray( newSize )
210
211         if newSize != len(self.tdi):
212             self.tdi = bytearray( newSize )
213
214         if newSize != len(self.smask):
215             self.smask = bytearray( newSize )
216
217         self.size = newSize
218 #-----</MASKSET>-----
219
220
221 def makeBitArray( hexString, bitCount ):
222     """
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.
230     """
231     global tokLn
232     a = bytearray()
233     length = bitCount
234     hexString = list(hexString)
235     hexString.reverse()
236     #print(hexString)
237     for c in hexString:
238         if length <= 0:
239             break;
240         c = int(c, 16)
241         for mask in [1,2,4,8]:
242             if length <= 0:
243                 break;
244             length = length - 1
245             a.append( (c & mask) != 0 )
246     if length > 0:
247         raise ParseError( tokLn, hexString, "Insufficient hex characters for given length of %d" % bitCount )
248     a.reverse()
249     #print(a)
250     return a
251
252
253 def makeXSVFbytes( bitarray ):
254     """
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.
258     """
259     bitCount = len(bitarray)
260     byteCount = (bitCount+7)//8
261     ba = bytearray( byteCount )
262     firstBit = (bitCount % 8) - 1
263     if firstBit == -1:
264         firstBit = 7
265     bitNdx = 0
266     for byteNdx in range(byteCount):
267         mask = 1<<firstBit
268         byte = 0
269         while mask:
270             if bitarray[bitNdx]:
271                 byte |= mask;
272             mask = mask >> 1
273             bitNdx = bitNdx + 1
274         ba[byteNdx] = byte
275         firstBit = 7
276     return ba
277
278
279 def writeComment( outputFile, shiftOp_linenum, shiftOp ):
280     """
281     Write an XCOMMENT record to outputFile
282     """
283     comment = "%s @%d\0" % (shiftOp, shiftOp_linenum)   # \0 is terminating nul
284     ba = bytearray(1)
285     ba[0] = XCOMMENT
286     ba += comment.encode()
287     outputFile.write( ba )
288
289
290 def combineBitVectors( trailer, meat, header ):
291     """
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.
295     """
296     ret = bytearray()
297     ret.extend( trailer )
298     ret.extend( meat )
299     ret.extend( header )
300     return ret
301
302
303 def writeRUNTEST( outputFile, run_state, end_state, run_count, min_time, tokenTxt ):
304     """
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
309     """
310     # convert from secs to usecs
311     min_time = int( min_time * 1000000)
312
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.
316     if doCOMMENTs:
317         writeComment( output, tokLn, tokenTxt )
318
319     if tokenTxt == 'RUNTEST':
320         obuf = bytearray(11)
321         obuf[0] = XWAITSTATE
322         obuf[1] = run_state
323         obuf[2] = end_state
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 )
327     else:   # == 'LDELAY'
328         obuf = bytearray(10)
329         obuf[0] = LDELAY
330         obuf[1] = run_state
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 )
335
336
337 output = open( outputFilename, mode='wb' )
338
339 hir = MASKSET('HIR')
340 hdr = MASKSET('HDR')
341 tir = MASKSET('TIR')
342 tdr = MASKSET('TDR')
343 sir = MASKSET('SIR')
344 sdr = MASKSET('SDR')
345
346
347 expecting_eof = True
348
349
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')
353
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 )
356
357 # what to expect as parameters to a shiftOp, i.e. after a SDR length or SIR length
358 shiftParts = ('TDI', 'TDO', 'MASK', 'SMASK')
359
360 # the set of legal states which can trail the RUNTEST command
361 run_state_allowed = ('IRPAUSE', 'DRPAUSE', 'RESET', 'IDLE')
362
363 enddr_state_allowed = ('DRPAUSE', 'IDLE')
364 endir_state_allowed = ('IRPAUSE', 'IDLE')
365
366 trst_mode_allowed = ('ON', 'OFF', 'Z', 'ABSENT')
367
368 enddr_state = IDLE
369 endir_state = IDLE
370
371 frequency =     1.00e+006 # HZ;
372
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
376
377
378 # we use a number of single byte writes for the XSVF command below
379 cmdbuf = bytearray(1)
380
381
382 # Save the XREPEAT setting into the file as first thing.
383 obuf = bytearray(2)
384 obuf[0] = XREPEAT
385 obuf[1] = xrepeat
386 output.write( obuf )
387
388
389 try:
390     while 1:
391         expecting_eof = True
392         nextTok()
393         expecting_eof = False
394         # print( tokType, tokVal, tokLn )
395
396         if tokVal in shiftOps:
397             shiftOp_linenum = tokLn
398             shiftOp = tokVal
399
400             set = shiftSets[shiftOps.index(shiftOp)]
401
402             # set flags false, if we see one later, set that one true later
403             sawTDI = sawTDO = sawMASK = sawSMASK = False
404
405             nextTok()
406             if tokType != 'int':
407                 raise ParseError( tokLn, tokVal, "Expecting 'int' giving %s length, got '%s'" % (shiftOp, tokType) )
408             length = tokVal
409
410             nextTok()
411
412             while tokVal != ';':
413                 if tokVal not in shiftParts:
414                     raise ParseError( tokLn, tokVal, "Expecting TDI, TDO, MASK, SMASK, or ';'")
415                 shiftPart = tokVal
416
417                 nextTok()
418
419                 if tokType != 'hex':
420                     raise ParseError( tokLn, tokVal, "Expecting hex bits" )
421                 bits = makeBitArray( tokVal, length )
422
423                 if shiftPart == 'TDI':
424                     sawTDI = True
425                     set.tdi = bits
426
427                 elif shiftPart == 'TDO':
428                     sawTDO = True
429                     set.tdo = bits
430
431                 elif shiftPart == 'MASK':
432                     sawMASK = True
433                     set.mask = bits
434
435                 elif shiftPart == 'SMASK':
436                     sawSMASK = True
437                     set.smask = bits
438
439                 nextTok()
440
441             set.syncLengths( sawTDI, sawTDO, sawMASK, sawSMASK, length )
442
443             # process all the gathered parameters and generate outputs here
444             if shiftOp == 'SIR':
445                 if doCOMMENTs:
446                     writeComment( output, shiftOp_linenum, 'SIR' )
447
448                 tdi = combineBitVectors( tir.tdi, sir.tdi, hir.tdi )
449                 if len(tdi) > 255:
450                     obuf = bytearray(3)
451                     obuf[0] = XSIR2
452                     struct.pack_into( ">h", obuf, 1, len(tdi) )
453                 else:
454                     obuf = bytearray(2)
455                     obuf[0] = XSIR
456                     obuf[1] = len(tdi)
457                 output.write( obuf )
458                 obuf = makeXSVFbytes( tdi )
459                 output.write( obuf )
460
461             elif shiftOp == 'SDR':
462                 if doCOMMENTs:
463                     writeComment( output, shiftOp_linenum, shiftOp )
464
465                 if not sawTDO:
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 )
469
470                     if xsdrsize != len(tdi):
471                         xsdrsize = len(tdi)
472                         cmdbuf[0] = XSDRSIZE
473                         output.write( cmdbuf )
474                         obuf = bytearray(4)
475                         struct.pack_into( ">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
476                         output.write( obuf )
477
478                     if xtdomask != mask:
479                         xtdomask = mask
480                         cmdbuf[0] = XTDOMASK
481                         output.write( cmdbuf )
482                         obuf = makeXSVFbytes( mask )
483                         output.write( obuf )
484
485                     cmdbuf[0] = XSDR
486                     output.write( cmdbuf )
487                     obuf = makeXSVFbytes( tdi )
488                     output.write( obuf )
489
490                 else:
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 )
494
495                     if xsdrsize != len(tdi):
496                         xsdrsize = len(tdi)
497                         cmdbuf[0] = XSDRSIZE
498                         output.write( cmdbuf )
499                         obuf = bytearray(4)
500                         struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
501                         output.write( obuf )
502
503                     if xtdomask != mask:
504                         xtdomask = mask
505                         cmdbuf[0] = XTDOMASK
506                         output.write( cmdbuf )
507                         obuf = makeXSVFbytes( mask )
508                         output.write( obuf )
509
510                     cmdbuf[0] = XSDRTDO
511                     output.write( cmdbuf )
512                     obuf = makeXSVFbytes( tdi )
513                     output.write( obuf )
514                     obuf = makeXSVFbytes( tdo )
515                     output.write( obuf )
516                     #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
517
518             elif shiftOp == 'LSDR':
519                 if doCOMMENTs:
520                     writeComment( output, shiftOp_linenum, shiftOp )
521
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 )
525
526                 if xsdrsize != len(tdi):
527                     xsdrsize = len(tdi)
528                     cmdbuf[0] = XSDRSIZE
529                     output.write( cmdbuf )
530                     obuf = bytearray(4)
531                     struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
532                     output.write( obuf )
533
534                 if xtdomask != mask:
535                     xtdomask = mask
536                     cmdbuf[0] = XTDOMASK
537                     output.write( cmdbuf )
538                     obuf = makeXSVFbytes( mask )
539                     output.write( obuf )
540
541                 cmdbuf[0] = LSDR
542                 output.write( cmdbuf )
543                 obuf = makeXSVFbytes( tdi )
544                 output.write( obuf )
545                 obuf = makeXSVFbytes( tdo )
546                 output.write( obuf )
547                 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
548
549         elif tokVal == 'RUNTEST' or tokVal == 'LDELAY':
550             # e.g. from lattice tools:
551             # "RUNTEST  IDLE    5 TCK   1.00E-003 SEC;"
552             saveTok = tokVal
553             nextTok()
554             min_time = 0
555             run_count = 0
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
560                 nextTok()
561             if tokType != 'int' and tokType != 'float':
562                 raise ParseError( tokLn, tokVal, "Expecting 'int' or 'float' after RUNTEST [run_state]")
563             timeval = tokVal;
564             nextTok()
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 )
569             else:
570                 min_time = timeval
571             nextTok()
572             if tokType == 'int' or tokType == 'float':
573                 min_time = tokVal
574                 nextTok()
575                 if tokVal != 'SEC':
576                     raise ParseError( tokLn, tokVal, "Expecting 'SEC' after RUNTEST [run_state] run_count min_time")
577                 nextTok()
578             if tokVal == 'MAXIMUM':
579                 nextTok()
580                 if tokType != 'int' and tokType != 'float':
581                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM")
582                 max_time = tokVal
583                 nextTok()
584                 if tokVal != 'SEC':
585                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM max_time")
586                 nextTok()
587             if tokVal == 'ENDSTATE':
588                 nextTok()
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)
592                 nextTok()
593             if 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 )
598
599         elif tokVal == 'LCOUNT':
600             nextTok()
601             if tokType != 'int':
602                 raise ParseError( tokLn, tokVal, "Expecting integer 'count' after LCOUNT")
603             loopCount = tokVal
604             nextTok()
605             if tokVal != ';':
606                 raise ParseError( tokLn, tokVal, "Expecting ';' after LCOUNT count")
607             if doCOMMENTs:
608                 writeComment( output, tokLn, 'LCOUNT' )
609             obuf = bytearray(5)
610             obuf[0] = LCOUNT
611             struct.pack_into(">i", obuf, 1, loopCount )  # big endian 4 byte int to obuf
612             output.write( obuf )
613
614         elif tokVal == 'ENDDR':
615             nextTok()
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)
619             nextTok()
620             if tokVal != ';':
621                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDDR stable_state")
622             if doCOMMENTs:
623                 writeComment( output, tokLn, 'ENDDR' )
624             obuf = bytearray(2)
625             obuf[0] = XENDDR
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
630             output.write( obuf )
631
632         elif tokVal == 'ENDIR':
633             nextTok()
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)
637             nextTok()
638             if tokVal != ';':
639                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDIR stable_state")
640             if doCOMMENTs:
641                 writeComment( output, tokLn, 'ENDIR' )
642             obuf = bytearray(2)
643             obuf[0] = XENDIR
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
648             output.write( obuf )
649
650         elif tokVal == 'STATE':
651             nextTok()
652             ln = tokLn
653             while tokVal != ';':
654                 if tokVal not in StateTxt:
655                     raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after STATE")
656                 stable_state = StateTxt.index( tokVal )
657
658                 if doCOMMENTs and ln != -1:
659                     writeComment( output, ln, 'STATE' )
660                     ln = -1     # save comment only once
661
662                 obuf = bytearray(2)
663                 obuf[0] = XSTATE
664                 obuf[1] = stable_state
665                 output.write( obuf )
666                 nextTok()
667
668         elif tokVal == 'FREQUENCY':
669             nextTok()
670             if tokVal != ';':
671                 if tokType != 'int' and tokType != 'float':
672                     raise ParseError( tokLn, tokVal, "Expecting 'cycles HZ' after FREQUENCY")
673                 frequency = tokVal
674                 nextTok()
675                 if tokVal != 'HZ':
676                     raise ParseError( tokLn, tokVal, "Expecting 'HZ' after FREQUENCY cycles")
677                 nextTok()
678                 if tokVal != ';':
679                     raise ParseError( tokLn, tokVal, "Expecting ';' after FREQUENCY cycles HZ")
680
681         elif tokVal == 'TRST':
682             nextTok()
683             if tokVal not in trst_mode_allowed:
684                 raise ParseError( tokLn, tokVal, "Expecting 'ON|OFF|Z|ABSENT' after TRST")
685             trst_mode = tokVal
686             nextTok()
687             if tokVal != ';':
688                 raise ParseError( tokLn, tokVal, "Expecting ';' after TRST trst_mode")
689             if doCOMMENTs:
690                 writeComment( output, tokLn, 'TRST %s' % trst_mode )
691             obuf = bytearray( 2 )
692             obuf[0] = XTRST
693             obuf[1] = trst_mode_allowed.index( trst_mode )  # use the index as the binary argument to XTRST opcode
694             output.write( obuf )
695
696         else:
697             raise ParseError( tokLn, tokVal, "Unknown token '%s'" % tokVal)
698
699 except StopIteration:
700     if not expecting_eof:
701         print( "Unexpected End of File at line ", tokLn )
702
703 except ParseError as pe:
704     print( "\n", pe )
705
706 finally:
707     # print( "closing file" )
708     cmdbuf[0] = XCOMPLETE
709     output.write( cmdbuf )
710     output.close()