read qsf file and display assigments from there
[x300-pci] / svf2xsvf.py
1 #!/usr/bin/python3
2
3 # Copyright 2008, SoftPLC Corporation  http://softplc.com
4 # Dick Hollenbeck dick@softplc.com
5
6
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.
11 #
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.
16 #
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
23
24
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.
28 #
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.
35 #
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
38 # building it.
39
40
41 import re
42 import sys
43 import struct
44
45
46 # There are both ---<Lexer>--- and ---<Parser>--- sections to this program
47
48
49 if len( sys.argv ) < 3:
50     print("usage %s <svf_filename> <xsvf_filename>" % sys.argv[0])
51     exit(1)
52
53
54 inputFilename = sys.argv[1]
55 outputFilename = sys.argv[2]
56
57 doCOMMENTs = True       # Save XCOMMENTs in the output xsvf file
58 #doCOMMENTs = False       # Save XCOMMENTs in the output xsvf file
59
60 # pick your file encoding
61 file_encoding = 'ISO-8859-1'
62 #file_encoding = 'utf-8'
63
64
65 xrepeat = 0             # argument to XREPEAT, gives retry count for masked compares
66
67
68 #-----< Lexer >---------------------------------------------------------------
69
70 StateBin = (RESET,IDLE,
71     DRSELECT,DRCAPTURE,DRSHIFT,DREXIT1,DRPAUSE,DREXIT2,DRUPDATE,
72     IRSELECT,IRCAPTURE,IRSHIFT,IREXIT1,IRPAUSE,IREXIT2,IRUPDATE) = range(16)
73
74 # Any integer index into this tuple will be equal to its corresponding StateBin value
75 StateTxt = ("RESET","IDLE",
76     "DRSELECT","DRCAPTURE","DRSHIFT","DREXIT1","DRPAUSE","DREXIT2","DRUPDATE",
77     "IRSELECT","IRCAPTURE","IRSHIFT","IREXIT1","IRPAUSE","IREXIT2","IRUPDATE")
78
79
80 (XCOMPLETE,XTDOMASK,XSIR,XSDR,XRUNTEST,hole0,hole1,XREPEAT,XSDRSIZE,XSDRTDO,
81     XSETSDRMASKS,XSDRINC,XSDRB,XSDRC,XSDRE,XSDRTDOB,XSDRTDOC,
82     XSDRTDOE,XSTATE,XENDIR,XENDDR,XSIR2,XCOMMENT,XWAIT,XWAITSTATE,
83     LCOUNT,LDELAY,LSDR,XTRST) = range(29)
84
85 #Note: LCOUNT, LDELAY, and LSDR are Lattice extensions to SVF and provide a way to loop back
86 # and check a completion status, essentially waiting on a part until it signals that it is done.
87 # For example below: loop 25 times, each time through the loop do a LDELAY (same as a true RUNTEST)
88 # and exit loop when LSDR compares match.
89 """
90 LCOUNT  25;
91 ! Step to DRPAUSE give 5 clocks and wait for 1.00e+000 SEC.
92 LDELAY  DRPAUSE 5 TCK   1.00E-003 SEC;
93 ! Test for the completed status. Match means pass.
94 ! Loop back to LDELAY line if not match and loop count less than 25.
95 LSDR  1 TDI  (0)
96         TDO  (1);
97 """
98
99 #XTRST is an opcode Xilinx seemed to have missed and it comes from the SVF TRST statement.
100
101 LineNumber = 1
102
103 def s_ident(scanner, token): return ("ident", token.upper(), LineNumber)
104
105 def s_hex(scanner, token):
106     global LineNumber
107     LineNumber = LineNumber + token.count('\n')
108     token = ''.join(token.split())
109     return ("hex", token[1:-1], LineNumber)
110
111 def s_int(scanner, token): return ("int", int(token), LineNumber)
112 def s_float(scanner, token): return ("float", float(token), LineNumber)
113 #def s_comment(scanner, token): return ("comment", token, LineNumber)
114 def s_semicolon(scanner, token): return ("semi", token, LineNumber)
115
116 def s_nl(scanner,token):
117     global LineNumber
118     LineNumber = LineNumber + 1
119     #print( 'LineNumber=', LineNumber, file=sys.stderr )
120     return None
121
122 #2.00E-002
123
124 scanner = re.Scanner([
125     (r"[a-zA-Z]\w*", s_ident),
126 #    (r"[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?", s_float),
127     (r"[-+]?[0-9]+(([.][0-9eE+-]*)|([eE]+[-+]?[0-9]+))", s_float),
128     (r"\d+", s_int),
129     (r"\(([0-9a-fA-F]|\s)*\)", s_hex),
130     (r"(!|//).*$", None),
131     (r";", s_semicolon),
132     (r"\n",s_nl),
133     (r"\s*", None),
134     ],
135     re.MULTILINE
136     )
137
138 # open the file using the given encoding
139 file = open( sys.argv[1], encoding=file_encoding )
140
141 # read all svf file input into string "input"
142 input = file.read()
143
144 file.close()
145
146 # Lexer:
147 # create a list of tuples containing (tokenType, tokenValue, LineNumber)
148 tokens = scanner.scan( input )[0]
149
150 input = None    # allow gc to reclaim memory holding file
151
152 #for tokenType, tokenValue, ln in tokens: print( "line %d: %s" % (ln, tokenType), tokenValue )
153
154
155 #-----<parser>-----------------------------------------------------------------
156
157 tokVal = tokType = tokLn = None
158
159 tup = iter( tokens )
160
161 def nextTok():
162     """
163     Function to read the next token from tup into tokType, tokVal, tokLn (linenumber)
164     which are globals.
165     """
166     global tokType, tokVal, tokLn, tup
167     tokType, tokVal, tokLn = tup.__next__()
168
169
170 class ParseError(Exception):
171     """A class to hold a parsing error message"""
172     def __init__(self, linenumber, token, message):
173         self.linenumber = linenumber
174         self.token = token
175         self.message = message
176     def __str__(self):
177         global inputFilename
178         return "Error in file \'%s\' at line %d near token %s\n %s" % (
179                    inputFilename, self.linenumber, repr(self.token), self.message)
180
181
182 class MASKSET(object):
183     """
184     Class MASKSET holds a set of bit vectors, all of which are related, will all
185     have the same length, and are associated with one of the seven shiftOps:
186     HIR, HDR, TIR, TDR, SIR, SDR, LSDR. One of these holds a mask, smask, tdi, tdo, and a
187     size.
188     """
189     def __init__(self, name):
190         self.empty()
191         self.name = name
192
193     def empty(self):
194         self.mask = bytearray()
195         self.smask = bytearray()
196         self.tdi = bytearray()
197         self.tdo = bytearray()
198         self.size = 0
199
200     def syncLengths( self, sawTDI, sawTDO, sawMASK, sawSMASK, newSize ):
201         """
202         Set all the lengths equal in the event some of the masks were
203         not seen as part of the last change set.
204         """
205         if self.size == newSize:
206             return
207
208         if newSize == 0:
209             self.empty()
210             return
211
212         # If an SIR was given without a MASK(), then use a mask of all zeros.
213         # this is not consistent with the SVF spec, but it makes sense because
214         # it would be odd to be testing an instruction register read out of a
215         # tap without giving a mask for it.  Also, lattice seems to agree and is
216         # generating SVF files that comply with this philosophy.
217         if self.name == 'SIR' and not sawMASK:
218             self.mask = bytearray( newSize )
219
220         if newSize != len(self.mask):
221             self.mask = bytearray( newSize )
222             if self.name == 'SDR':  # leave mask for HIR,HDR,TIR,TDR,SIR zeros
223                 for i in range( newSize ):
224                     self.mask[i] = 1
225
226         if newSize != len(self.tdo):
227             self.tdo = bytearray( newSize )
228
229         if newSize != len(self.tdi):
230             self.tdi = bytearray( newSize )
231
232         if newSize != len(self.smask):
233             self.smask = bytearray( newSize )
234
235         self.size = newSize
236 #-----</MASKSET>-----
237
238
239 def makeBitArray( hexString, bitCount ):
240     """
241     Converts a packed sequence of hex ascii characters into a bytearray where
242     each element in the array holds exactly one bit. Only "bitCount" bits are
243     scanned and these must be the least significant bits in the hex number. That
244     is, it is legal to have some unused bits in the must significant hex nibble
245     of the input "hexString". The string is scanned starting from the backend,
246     then just before returning we reverse the array. This way the append()
247     method can be used, which I assume is faster than an insert.
248     """
249     global tokLn
250     a = bytearray()
251     length = bitCount
252     hexString = list(hexString)
253     hexString.reverse()
254     #print(hexString)
255     for c in hexString:
256         if length <= 0:
257             break;
258         c = int(c, 16)
259         for mask in [1,2,4,8]:
260             if length <= 0:
261                 break;
262             length = length - 1
263             a.append( (c & mask) != 0 )
264     if length > 0:
265         raise ParseError( tokLn, hexString, "Insufficient hex characters for given length of %d" % bitCount )
266     a.reverse()
267     #print(a)
268     return a
269
270
271 def makeXSVFbytes( bitarray ):
272     """
273     Make a bytearray which is contains the XSVF bits which will be written
274     directly to disk.  The number of bytes needed is calculated from the size
275     of the argument bitarray.
276     """
277     bitCount = len(bitarray)
278     byteCount = (bitCount+7)//8
279     ba = bytearray( byteCount )
280     firstBit = (bitCount % 8) - 1
281     if firstBit == -1:
282         firstBit = 7
283     bitNdx = 0
284     for byteNdx in range(byteCount):
285         mask = 1<<firstBit
286         byte = 0
287         while mask:
288             if bitarray[bitNdx]:
289                 byte |= mask;
290             mask = mask >> 1
291             bitNdx = bitNdx + 1
292         ba[byteNdx] = byte
293         firstBit = 7
294     return ba
295
296
297 def writeComment( outputFile, shiftOp_linenum, shiftOp ):
298     """
299     Write an XCOMMENT record to outputFile
300     """
301     comment = "%s @%d\0" % (shiftOp, shiftOp_linenum)   # \0 is terminating nul
302     ba = bytearray(1)
303     ba[0] = XCOMMENT
304     ba += comment.encode()
305     outputFile.write( ba )
306
307
308 def combineBitVectors( trailer, meat, header ):
309     """
310     Combine the 3 bit vectors comprizing a transmission.  Since the least
311     significant bits are sent first, the header is put onto the list last so
312     they are sent first from that least significant position.
313     """
314     ret = bytearray()
315     ret.extend( trailer )
316     ret.extend( meat )
317     ret.extend( header )
318     return ret
319
320
321 def writeRUNTEST( outputFile, run_state, end_state, run_count, min_time, tokenTxt ):
322     """
323     Write the output for the SVF RUNTEST command.
324     run_count - the number of clocks
325     min_time - the number of seconds
326     tokenTxt - either RUNTEST or LDELAY
327     """
328     # convert from secs to usecs
329     min_time = int( min_time * 1000000)
330
331     # the SVF RUNTEST command does NOT map to the XSVF XRUNTEST command.  Check the SVF spec, then
332     # read the XSVF command.   They are not the same.  Use an XSVF XWAITSTATE to
333     # implement the required behavior of the SVF RUNTEST command.
334     if doCOMMENTs:
335         writeComment( output, tokLn, tokenTxt )
336
337     if tokenTxt == 'RUNTEST':
338         obuf = bytearray(11)
339         obuf[0] = XWAITSTATE
340         obuf[1] = run_state
341         obuf[2] = end_state
342         struct.pack_into(">i", obuf, 3, run_count )  # big endian 4 byte int to obuf
343         struct.pack_into(">i", obuf, 7, min_time )   # big endian 4 byte int to obuf
344         outputFile.write( obuf )
345     else:   # == 'LDELAY'
346         obuf = bytearray(10)
347         obuf[0] = LDELAY
348         obuf[1] = run_state
349         # LDELAY has no end_state
350         struct.pack_into(">i", obuf, 2, run_count )  # big endian 4 byte int to obuf
351         struct.pack_into(">i", obuf, 6, min_time )   # big endian 4 byte int to obuf
352         outputFile.write( obuf )
353
354
355 output = open( outputFilename, mode='wb' )
356
357 hir = MASKSET('HIR')
358 hdr = MASKSET('HDR')
359 tir = MASKSET('TIR')
360 tdr = MASKSET('TDR')
361 sir = MASKSET('SIR')
362 sdr = MASKSET('SDR')
363
364
365 expecting_eof = True
366
367
368 # one of the commands that take the shiftParts after the length, the parse
369 # template for all of these commands is identical
370 shiftOps = ('SDR', 'SIR', 'LSDR', 'HDR', 'HIR', 'TDR', 'TIR')
371
372 # the order must correspond to shiftOps, this holds the MASKSETS.  'LSDR' shares sdr with 'SDR'
373 shiftSets = (sdr, sir, sdr, hdr, hir, tdr, tir )
374
375 # what to expect as parameters to a shiftOp, i.e. after a SDR length or SIR length
376 shiftParts = ('TDI', 'TDO', 'MASK', 'SMASK')
377
378 # the set of legal states which can trail the RUNTEST command
379 run_state_allowed = ('IRPAUSE', 'DRPAUSE', 'RESET', 'IDLE')
380
381 enddr_state_allowed = ('DRPAUSE', 'IDLE')
382 endir_state_allowed = ('IRPAUSE', 'IDLE')
383
384 trst_mode_allowed = ('ON', 'OFF', 'Z', 'ABSENT')
385
386 enddr_state = IDLE
387 endir_state = IDLE
388
389 frequency =     1.00e+006 # HZ;
390
391 # change detection for xsdrsize and xtdomask
392 xsdrsize = -1           # the last one sent, send only on change
393 xtdomask = bytearray()  # the last one sent, send only on change
394
395
396 # we use a number of single byte writes for the XSVF command below
397 cmdbuf = bytearray(1)
398
399
400 # Save the XREPEAT setting into the file as first thing.
401 obuf = bytearray(2)
402 obuf[0] = XREPEAT
403 obuf[1] = xrepeat
404 output.write( obuf )
405
406
407 try:
408     while 1:
409         expecting_eof = True
410         nextTok()
411         expecting_eof = False
412         # print( tokType, tokVal, tokLn )
413
414         if tokVal in shiftOps:
415             shiftOp_linenum = tokLn
416             shiftOp = tokVal
417
418             set = shiftSets[shiftOps.index(shiftOp)]
419
420             # set flags false, if we see one later, set that one true later
421             sawTDI = sawTDO = sawMASK = sawSMASK = False
422
423             nextTok()
424             if tokType != 'int':
425                 raise ParseError( tokLn, tokVal, "Expecting 'int' giving %s length, got '%s'" % (shiftOp, tokType) )
426             length = tokVal
427
428             nextTok()
429
430             while tokVal != ';':
431                 if tokVal not in shiftParts:
432                     raise ParseError( tokLn, tokVal, "Expecting TDI, TDO, MASK, SMASK, or ';'")
433                 shiftPart = tokVal
434
435                 nextTok()
436
437                 if tokType != 'hex':
438                     raise ParseError( tokLn, tokVal, "Expecting hex bits" )
439                 bits = makeBitArray( tokVal, length )
440
441                 if shiftPart == 'TDI':
442                     sawTDI = True
443                     set.tdi = bits
444
445                 elif shiftPart == 'TDO':
446                     sawTDO = True
447                     set.tdo = bits
448
449                 elif shiftPart == 'MASK':
450                     sawMASK = True
451                     set.mask = bits
452
453                 elif shiftPart == 'SMASK':
454                     sawSMASK = True
455                     set.smask = bits
456
457                 nextTok()
458
459             set.syncLengths( sawTDI, sawTDO, sawMASK, sawSMASK, length )
460
461             # process all the gathered parameters and generate outputs here
462             if shiftOp == 'SIR':
463                 if doCOMMENTs:
464                     writeComment( output, shiftOp_linenum, 'SIR' )
465
466                 tdi = combineBitVectors( tir.tdi, sir.tdi, hir.tdi )
467                 if len(tdi) > 255:
468                     obuf = bytearray(3)
469                     obuf[0] = XSIR2
470                     struct.pack_into( ">h", obuf, 1, len(tdi) )
471                 else:
472                     obuf = bytearray(2)
473                     obuf[0] = XSIR
474                     obuf[1] = len(tdi)
475                 output.write( obuf )
476                 obuf = makeXSVFbytes( tdi )
477                 output.write( obuf )
478
479             elif shiftOp == 'SDR':
480                 if doCOMMENTs:
481                     writeComment( output, shiftOp_linenum, shiftOp )
482
483                 if not sawTDO:
484                     # pass a zero filled bit vector for the sdr.mask
485                     mask = combineBitVectors( tdr.mask, bytearray(sdr.size), hdr.mask )
486                     tdi  = combineBitVectors( tdr.tdi,  sdr.tdi,  hdr.tdi )
487
488                     if xsdrsize != len(tdi):
489                         xsdrsize = len(tdi)
490                         cmdbuf[0] = XSDRSIZE
491                         output.write( cmdbuf )
492                         obuf = bytearray(4)
493                         struct.pack_into( ">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
494                         output.write( obuf )
495
496                     if xtdomask != mask:
497                         xtdomask = mask
498                         cmdbuf[0] = XTDOMASK
499                         output.write( cmdbuf )
500                         obuf = makeXSVFbytes( mask )
501                         output.write( obuf )
502
503                     cmdbuf[0] = XSDR
504                     output.write( cmdbuf )
505                     obuf = makeXSVFbytes( tdi )
506                     output.write( obuf )
507
508                 else:
509                     mask = combineBitVectors( tdr.mask, sdr.mask, hdr.mask )
510                     tdi  = combineBitVectors( tdr.tdi,  sdr.tdi,  hdr.tdi )
511                     tdo  = combineBitVectors( tdr.tdo,  sdr.tdo,  hdr.tdo )
512
513                     if xsdrsize != len(tdi):
514                         xsdrsize = len(tdi)
515                         cmdbuf[0] = XSDRSIZE
516                         output.write( cmdbuf )
517                         obuf = bytearray(4)
518                         struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
519                         output.write( obuf )
520
521                     if xtdomask != mask:
522                         xtdomask = mask
523                         cmdbuf[0] = XTDOMASK
524                         output.write( cmdbuf )
525                         obuf = makeXSVFbytes( mask )
526                         output.write( obuf )
527
528                     cmdbuf[0] = XSDRTDO
529                     output.write( cmdbuf )
530                     obuf = makeXSVFbytes( tdi )
531                     output.write( obuf )
532                     obuf = makeXSVFbytes( tdo )
533                     output.write( obuf )
534                     #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
535
536             elif shiftOp == 'LSDR':
537                 if doCOMMENTs:
538                     writeComment( output, shiftOp_linenum, shiftOp )
539
540                 mask = combineBitVectors( tdr.mask, sdr.mask, hdr.mask )
541                 tdi  = combineBitVectors( tdr.tdi,  sdr.tdi,  hdr.tdi )
542                 tdo  = combineBitVectors( tdr.tdo,  sdr.tdo,  hdr.tdo )
543
544                 if xsdrsize != len(tdi):
545                     xsdrsize = len(tdi)
546                     cmdbuf[0] = XSDRSIZE
547                     output.write( cmdbuf )
548                     obuf = bytearray(4)
549                     struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
550                     output.write( obuf )
551
552                 if xtdomask != mask:
553                     xtdomask = mask
554                     cmdbuf[0] = XTDOMASK
555                     output.write( cmdbuf )
556                     obuf = makeXSVFbytes( mask )
557                     output.write( obuf )
558
559                 cmdbuf[0] = LSDR
560                 output.write( cmdbuf )
561                 obuf = makeXSVFbytes( tdi )
562                 output.write( obuf )
563                 obuf = makeXSVFbytes( tdo )
564                 output.write( obuf )
565                 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
566
567         elif tokVal == 'RUNTEST' or tokVal == 'LDELAY':
568             # e.g. from lattice tools:
569             # "RUNTEST  IDLE    5 TCK   1.00E-003 SEC;"
570             saveTok = tokVal
571             nextTok()
572             min_time = 0
573             run_count = 0
574             max_time = 600  # ten minutes
575             if tokVal in run_state_allowed:
576                 run_state = StateTxt.index(tokVal)
577                 end_state = run_state  # bottom of page 17 of SVF spec
578                 nextTok()
579             if tokType != 'int' and tokType != 'float':
580                 raise ParseError( tokLn, tokVal, "Expecting 'int' or 'float' after RUNTEST [run_state]")
581             timeval = tokVal;
582             nextTok()
583             if tokVal != 'TCK' and tokVal != 'SEC' and tokVal != 'SCK':
584                 raise ParseError( tokLn, tokVal, "Expecting 'TCK' or 'SEC' or 'SCK' after RUNTEST [run_state] (run_count|min_time)")
585             if tokVal == 'TCK' or tokVal == 'SCK':
586                 run_count = int( timeval )
587             else:
588                 min_time = timeval
589             nextTok()
590             if tokType == 'int' or tokType == 'float':
591                 min_time = tokVal
592                 nextTok()
593                 if tokVal != 'SEC':
594                     raise ParseError( tokLn, tokVal, "Expecting 'SEC' after RUNTEST [run_state] run_count min_time")
595                 nextTok()
596             if tokVal == 'MAXIMUM':
597                 nextTok()
598                 if tokType != 'int' and tokType != 'float':
599                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM")
600                 max_time = tokVal
601                 nextTok()
602                 if tokVal != 'SEC':
603                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM max_time")
604                 nextTok()
605             if tokVal == 'ENDSTATE':
606                 nextTok()
607                 if tokVal not in run_state_allowed:
608                     raise ParseError( tokLn, tokVal, "Expecting 'run_state' after RUNTEST .... ENDSTATE")
609                 end_state = StateTxt.index(tokVal)
610                 nextTok()
611             if tokVal != ';':
612                 raise ParseError( tokLn, tokVal, "Expecting ';' after RUNTEST ....")
613             print( "run_count=", run_count, "min_time=", min_time,
614                 "max_time=", max_time, "run_state=", run_state, "end_state=", end_state )
615             writeRUNTEST( output, run_state, end_state, run_count, min_time, saveTok )
616
617         elif tokVal == 'LCOUNT':
618             nextTok()
619             if tokType != 'int':
620                 raise ParseError( tokLn, tokVal, "Expecting integer 'count' after LCOUNT")
621             loopCount = tokVal
622             nextTok()
623             if tokVal != ';':
624                 raise ParseError( tokLn, tokVal, "Expecting ';' after LCOUNT count")
625             if doCOMMENTs:
626                 writeComment( output, tokLn, 'LCOUNT' )
627             obuf = bytearray(5)
628             obuf[0] = LCOUNT
629             struct.pack_into(">i", obuf, 1, loopCount )  # big endian 4 byte int to obuf
630             output.write( obuf )
631
632         elif tokVal == 'ENDDR':
633             nextTok()
634             if tokVal not in enddr_state_allowed:
635                 raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after ENDDR. (one of: DRPAUSE, IDLE)")
636             enddr_state = StateTxt.index(tokVal)
637             nextTok()
638             if tokVal != ';':
639                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDDR stable_state")
640             if doCOMMENTs:
641                 writeComment( output, tokLn, 'ENDDR' )
642             obuf = bytearray(2)
643             obuf[0] = XENDDR
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 enddr_state == DRPAUSE else 0
648             output.write( obuf )
649
650         elif tokVal == 'ENDIR':
651             nextTok()
652             if tokVal not in endir_state_allowed:
653                 raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after ENDIR. (one of: IRPAUSE, IDLE)")
654             endir_state = StateTxt.index(tokVal)
655             nextTok()
656             if tokVal != ';':
657                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDIR stable_state")
658             if doCOMMENTs:
659                 writeComment( output, tokLn, 'ENDIR' )
660             obuf = bytearray(2)
661             obuf[0] = XENDIR
662             # Page 10 of the March 1999 SVF spec shows that RESET is also allowed here.
663             # Yet the XSVF spec has no provision for that, and uses a non-standard, i.e.
664             # boolean argument to XENDDR which only handles two of the 3 intended states.
665             obuf[1] = 1 if endir_state == IRPAUSE else 0
666             output.write( obuf )
667
668         elif tokVal == 'STATE':
669             nextTok()
670             ln = tokLn
671             while tokVal != ';':
672                 if tokVal not in StateTxt:
673                     raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after STATE")
674                 stable_state = StateTxt.index( tokVal )
675
676                 if doCOMMENTs and ln != -1:
677                     writeComment( output, ln, 'STATE' )
678                     ln = -1     # save comment only once
679
680                 obuf = bytearray(2)
681                 obuf[0] = XSTATE
682                 obuf[1] = stable_state
683                 output.write( obuf )
684                 nextTok()
685
686         elif tokVal == 'FREQUENCY':
687             nextTok()
688             if tokVal != ';':
689                 if tokType != 'int' and tokType != 'float':
690                     raise ParseError( tokLn, tokVal, "Expecting 'cycles HZ' after FREQUENCY")
691                 frequency = tokVal
692                 nextTok()
693                 if tokVal != 'HZ':
694                     raise ParseError( tokLn, tokVal, "Expecting 'HZ' after FREQUENCY cycles")
695                 nextTok()
696                 if tokVal != ';':
697                     raise ParseError( tokLn, tokVal, "Expecting ';' after FREQUENCY cycles HZ")
698
699         elif tokVal == 'TRST':
700             nextTok()
701             if tokVal not in trst_mode_allowed:
702                 raise ParseError( tokLn, tokVal, "Expecting 'ON|OFF|Z|ABSENT' after TRST")
703             trst_mode = tokVal
704             nextTok()
705             if tokVal != ';':
706                 raise ParseError( tokLn, tokVal, "Expecting ';' after TRST trst_mode")
707             if doCOMMENTs:
708                 writeComment( output, tokLn, 'TRST %s' % trst_mode )
709             obuf = bytearray( 2 )
710             obuf[0] = XTRST
711             obuf[1] = trst_mode_allowed.index( trst_mode )  # use the index as the binary argument to XTRST opcode
712             output.write( obuf )
713
714         else:
715             raise ParseError( tokLn, tokVal, "Unknown token '%s'" % tokVal)
716
717 except StopIteration:
718     if not expecting_eof:
719         print( "Unexpected End of File at line ", tokLn )
720
721 except ParseError as pe:
722     print( "\n", pe )
723
724 finally:
725     # print( "closing file" )
726     cmdbuf[0] = XCOMPLETE
727     output.write( cmdbuf )
728     output.close()
729