]> git.sur5r.net Git - openocd/blob - tools/xsvf_tools/svf2xsvf.py
20080db3049e1c04efd1d779e1ec38f32752ecc5
[openocd] / tools / xsvf_tools / svf2xsvf.py
1 #!/usr/bin/python3.0
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 xrepeat = 0             # argument to XREPEAT, gives retry count for masked compares
61
62
63 #-----< Lexer >---------------------------------------------------------------
64
65 StateBin = (RESET,IDLE,
66     DRSELECT,DRCAPTURE,DRSHIFT,DREXIT1,DRPAUSE,DREXIT2,DRUPDATE,
67     IRSELECT,IRCAPTURE,IRSHIFT,IREXIT1,IRPAUSE,IREXIT2,IRUPDATE) = range(16)
68
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")
73
74
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)
78
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.
83 """
84 LCOUNT  25;
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.
89 LSDR  1 TDI  (0)
90         TDO  (1);
91 """
92
93 LineNumber = 1
94
95 def s_ident(scanner, token): return ("ident", token.upper(), LineNumber)
96
97 def s_hex(scanner, token):
98     global LineNumber
99     LineNumber = LineNumber + token.count('\n')
100     token = ''.join(token.split())
101     return ("hex", token[1:-1], LineNumber)
102
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)
107
108 def s_nl(scanner,token):
109     global LineNumber
110     LineNumber = LineNumber + 1
111     #print( 'LineNumber=', LineNumber, file=sys.stderr )
112     return None
113
114 #2.00E-002
115
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),
120     (r"\d+", s_int),
121     (r"\(([0-9a-fA-F]|\s)*\)", s_hex),
122     (r"(!|//).*$", None),
123     (r";", s_semicolon),
124     (r"\n",s_nl),
125     (r"\s*", None),
126     ],
127     re.MULTILINE
128     )
129
130 # read all svf file input into string "input"
131 input = open( sys.argv[1] ).read()
132
133 # Lexer:
134 # create a list of tuples containing (tokenType, tokenValue, LineNumber)
135 tokens = scanner.scan( input )[0]
136
137 input = None    # allow gc to reclaim memory holding file
138
139 #for tokenType, tokenValue, ln in tokens: print( "line %d: %s" % (ln, tokenType), tokenValue )
140
141
142 #-----<parser>-----------------------------------------------------------------
143
144 tokVal = tokType = tokLn = None
145
146 tup = iter( tokens )
147
148 def nextTok():
149     """
150     Function to read the next token from tup into tokType, tokVal, tokLn (linenumber)
151     which are globals.
152     """
153     global tokType, tokVal, tokLn, tup
154     tokType, tokVal, tokLn = tup.__next__()
155
156
157 class ParseError(Exception):
158     """A class to hold a parsing error message"""
159     def __init__(self, linenumber, token, message):
160         self.linenumber = linenumber
161         self.token = token
162         self.message = message
163     def __str__(self):
164         global inputFilename
165         return "Error in file \'%s\' at line %d near token %s\n %s" % (
166                    inputFilename, self.linenumber, repr(self.token), self.message)
167
168
169 class MASKSET(object):
170     """
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
174     size.
175     """
176     def __init__(self, name):
177         self.empty()
178         self.name = name
179
180     def empty(self):
181         self.mask = bytearray()
182         self.smask = bytearray()
183         self.tdi = bytearray()
184         self.tdo = bytearray()
185         self.size = 0
186
187     def syncLengths( self, sawTDI, sawTDO, sawMASK, sawSMASK, newSize ):
188         """
189         Set all the lengths equal in the event some of the masks were
190         not seen as part of the last change set.
191         """
192         if self.size == newSize:
193             return
194
195         if newSize == 0:
196             self.empty()
197             return
198
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 )
206
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 ):
211                     self.mask[i] = 1
212
213         if newSize != len(self.tdo):
214             self.tdo = bytearray( newSize )
215
216         if newSize != len(self.tdi):
217             self.tdi = bytearray( newSize )
218
219         if newSize != len(self.smask):
220             self.smask = bytearray( newSize )
221
222         self.size = newSize
223 #-----</MASKSET>-----
224
225
226 def makeBitArray( hexString, bitCount ):
227     """
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.
235     """
236     global tokLn
237     a = bytearray()
238     length = bitCount
239     hexString = list(hexString)
240     hexString.reverse()
241     #print(hexString)
242     for c in hexString:
243         if length <= 0:
244             break;
245         c = int(c, 16)
246         for mask in [1,2,4,8]:
247             if length <= 0:
248                 break;
249             length = length - 1
250             a.append( (c & mask) != 0 )
251     if length > 0:
252         raise ParseError( tokLn, hexString, "Insufficient hex characters for given length of %d" % bitCount )
253     a.reverse()
254     #print(a)
255     return a
256
257
258 def makeXSVFbytes( bitarray ):
259     """
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.
263     """
264     bitCount = len(bitarray)
265     byteCount = (bitCount+7)//8
266     ba = bytearray( byteCount )
267     firstBit = (bitCount % 8) - 1
268     if firstBit == -1:
269         firstBit = 7
270     bitNdx = 0
271     for byteNdx in range(byteCount):
272         mask = 1<<firstBit
273         byte = 0
274         while mask:
275             if bitarray[bitNdx]:
276                 byte |= mask;
277             mask = mask >> 1
278             bitNdx = bitNdx + 1
279         ba[byteNdx] = byte
280         firstBit = 7
281     return ba
282
283
284 def writeComment( outputFile, shiftOp_linenum, shiftOp ):
285     """
286     Write an XCOMMENT record to outputFile
287     """
288     comment = "%s @%d\0" % (shiftOp, shiftOp_linenum)   # \0 is terminating nul
289     ba = bytearray(1)
290     ba[0] = XCOMMENT
291     ba += comment.encode()
292     outputFile.write( ba )
293
294
295 def combineBitVectors( trailer, meat, header ):
296     """
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.
300     """
301     ret = bytearray()
302     ret.extend( trailer )
303     ret.extend( meat )
304     ret.extend( header )
305     return ret
306
307
308 def writeRUNTEST( outputFile, run_state, end_state, run_count, min_time, tokenTxt ):
309     """
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
314     """
315     # convert from secs to usecs
316     min_time = int( min_time * 1000000)
317
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.
321     if doCOMMENTs:
322         writeComment( output, tokLn, tokenTxt )
323
324     if tokenTxt == 'RUNTEST':
325         obuf = bytearray(11)
326         obuf[0] = XWAITSTATE
327         obuf[1] = run_state
328         obuf[2] = end_state
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 )
332     else:   # == 'LDELAY'
333         obuf = bytearray(10)
334         obuf[0] = LDELAY
335         obuf[1] = run_state
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 )
340
341
342 output = open( outputFilename, mode='wb' )
343
344 hir = MASKSET('HIR')
345 hdr = MASKSET('HDR')
346 tir = MASKSET('TIR')
347 tdr = MASKSET('TDR')
348 sir = MASKSET('SIR')
349 sdr = MASKSET('SDR')
350
351
352 expecting_eof = True
353
354
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')
358
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 )
361
362 # what to expect as parameters to a shiftOp, i.e. after a SDR length or SIR length
363 shiftParts = ('TDI', 'TDO', 'MASK', 'SMASK')
364
365 # the set of legal states which can trail the RUNTEST command
366 run_state_allowed = ('IRPAUSE', 'DRPAUSE', 'RESET', 'IDLE')
367
368 enddr_state_allowed = ('DRPAUSE', 'IDLE')
369 endir_state_allowed = ('IRPAUSE', 'IDLE')
370
371 enddr_state = IDLE
372 endir_state = IDLE
373
374 frequency =     1.00e+006 # HZ;
375
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
379
380
381 # we use a number of single byte writes for the XSVF command below
382 cmdbuf = bytearray(1)
383
384
385 # Save the XREPEAT setting into the file as first thing.
386 obuf = bytearray(2)
387 obuf[0] = XREPEAT
388 obuf[1] = xrepeat
389 output.write( obuf )
390
391
392 try:
393     while 1:
394         expecting_eof = True
395         nextTok()
396         expecting_eof = False
397         # print( tokType, tokVal, tokLn )
398
399         if tokVal in shiftOps:
400             shiftOp_linenum = tokLn
401             shiftOp = tokVal
402
403             set = shiftSets[shiftOps.index(shiftOp)]
404
405             # set flags false, if we see one later, set that one true later
406             sawTDI = sawTDO = sawMASK = sawSMASK = False
407
408             nextTok()
409             if tokType != 'int':
410                 raise ParseError( tokLn, tokVal, "Expecting 'int' giving %s length, got '%s'" % (shiftOp, tokType) )
411             length = tokVal
412
413             nextTok()
414
415             while tokVal != ';':
416                 if tokVal not in shiftParts:
417                     raise ParseError( tokLn, tokVal, "Expecting TDI, TDO, MASK, SMASK, or ';'")
418                 shiftPart = tokVal
419
420                 nextTok()
421
422                 if tokType != 'hex':
423                     raise ParseError( tokLn, tokVal, "Expecting hex bits" )
424                 bits = makeBitArray( tokVal, length )
425
426                 if shiftPart == 'TDI':
427                     sawTDI = True
428                     set.tdi = bits
429
430                 elif shiftPart == 'TDO':
431                     sawTDO = True
432                     set.tdo = bits
433
434                 elif shiftPart == 'MASK':
435                     sawMASK = True
436                     set.mask = bits
437
438                 elif shiftPart == 'SMASK':
439                     sawSMASK = True
440                     set.smask = bits
441
442                 nextTok()
443
444             set.syncLengths( sawTDI, sawTDO, sawMASK, sawSMASK, length )
445
446             # process all the gathered parameters and generate outputs here
447             if shiftOp == 'SIR':
448                 if doCOMMENTs:
449                     writeComment( output, shiftOp_linenum, 'SIR' )
450
451                 tdi = combineBitVectors( tir.tdi, sir.tdi, hir.tdi )
452                 if len(tdi) > 255:
453                     obuf = bytearray(3)
454                     obuf[0] = XSIR2
455                     struct.pack_into( ">h", obuf, 1, len(tdi) )
456                 else:
457                     obuf = bytearray(2)
458                     obuf[0] = XSIR
459                     obuf[1] = len(tdi)
460                 output.write( obuf )
461                 obuf = makeXSVFbytes( tdi )
462                 output.write( obuf )
463
464             elif shiftOp == 'SDR':
465                 if doCOMMENTs:
466                     writeComment( output, shiftOp_linenum, shiftOp )
467
468                 if not sawTDO:
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 )
472
473                     if xsdrsize != len(tdi):
474                         xsdrsize = len(tdi)
475                         cmdbuf[0] = XSDRSIZE
476                         output.write( cmdbuf )
477                         obuf = bytearray(4)
478                         struct.pack_into( ">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
479                         output.write( obuf )
480
481                     if xtdomask != mask:
482                         xtdomask = mask
483                         cmdbuf[0] = XTDOMASK
484                         output.write( cmdbuf )
485                         obuf = makeXSVFbytes( mask )
486                         output.write( obuf )
487
488                     cmdbuf[0] = XSDR
489                     output.write( cmdbuf )
490                     obuf = makeXSVFbytes( tdi )
491                     output.write( obuf )
492
493                 else:
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 )
497
498                     if xsdrsize != len(tdi):
499                         xsdrsize = len(tdi)
500                         cmdbuf[0] = XSDRSIZE
501                         output.write( cmdbuf )
502                         obuf = bytearray(4)
503                         struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
504                         output.write( obuf )
505
506                     if xtdomask != mask:
507                         xtdomask = mask
508                         cmdbuf[0] = XTDOMASK
509                         output.write( cmdbuf )
510                         obuf = makeXSVFbytes( mask )
511                         output.write( obuf )
512
513                     cmdbuf[0] = XSDRTDO
514                     output.write( cmdbuf )
515                     obuf = makeXSVFbytes( tdi )
516                     output.write( obuf )
517                     obuf = makeXSVFbytes( tdo )
518                     output.write( obuf )
519                     #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
520
521             elif shiftOp == 'LSDR':
522                 if doCOMMENTs:
523                     writeComment( output, shiftOp_linenum, shiftOp )
524
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 )
528
529                 if xsdrsize != len(tdi):
530                     xsdrsize = len(tdi)
531                     cmdbuf[0] = XSDRSIZE
532                     output.write( cmdbuf )
533                     obuf = bytearray(4)
534                     struct.pack_into(">i", obuf, 0, xsdrsize )  # big endian 4 byte int to obuf
535                     output.write( obuf )
536
537                 if xtdomask != mask:
538                     xtdomask = mask
539                     cmdbuf[0] = XTDOMASK
540                     output.write( cmdbuf )
541                     obuf = makeXSVFbytes( mask )
542                     output.write( obuf )
543
544                 cmdbuf[0] = LSDR
545                 output.write( cmdbuf )
546                 obuf = makeXSVFbytes( tdi )
547                 output.write( obuf )
548                 obuf = makeXSVFbytes( tdo )
549                 output.write( obuf )
550                 #print( "len(tdo)=", len(tdo), "len(tdr.tdo)=", len(tdr.tdo), "len(sdr.tdo)=", len(sdr.tdo), "len(hdr.tdo)=", len(hdr.tdo) )
551
552         elif tokVal == 'RUNTEST' or tokVal == 'LDELAY':
553             # e.g. from lattice tools:
554             # "RUNTEST  IDLE    5 TCK   1.00E-003 SEC;"
555             saveTok = tokVal
556             nextTok()
557             min_time = 0
558             run_count = 0
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
563                 nextTok()
564             if tokType != 'int' and tokType != 'float':
565                 raise ParseError( tokLn, tokVal, "Expecting 'int' or 'float' after RUNTEST [run_state]")
566             timeval = tokVal;
567             nextTok()
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 )
572             else:
573                 min_time = timeval
574             nextTok()
575             if tokType == 'int' or tokType == 'float':
576                 min_time = tokVal
577                 nextTok()
578                 if tokVal != 'SEC':
579                     raise ParseError( tokLn, tokVal, "Expecting 'SEC' after RUNTEST [run_state] run_count min_time")
580                 nextTok()
581             if tokVal == 'MAXIMUM':
582                 nextTok()
583                 if tokType != 'int' and tokType != 'float':
584                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM")
585                 max_time = tokVal
586                 nextTok()
587                 if tokVal != 'SEC':
588                     raise ParseError( tokLn, tokVal, "Expecting 'max_time' after RUNTEST [run_state] min_time SEC MAXIMUM max_time")
589                 nextTok()
590             if tokVal == 'ENDSTATE':
591                 nextTok()
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)
595                 nextTok()
596             if 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 )
601
602         elif tokVal == 'LCOUNT':
603             nextTok()
604             if tokType != 'int':
605                 raise ParseError( tokLn, tokVal, "Expecting integer 'count' after LCOUNT")
606             loopCount = tokVal
607             nextTok()
608             if tokVal != ';':
609                 raise ParseError( tokLn, tokVal, "Expecting ';' after LCOUNT count")
610             if doCOMMENTs:
611                 writeComment( output, tokLn, 'LCOUNT' )
612             obuf = bytearray(5)
613             obuf[0] = LCOUNT
614             struct.pack_into(">i", obuf, 1, loopCount )  # big endian 4 byte int to obuf
615             output.write( obuf )
616
617         elif tokVal == 'ENDDR':
618             nextTok()
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)
622             nextTok()
623             if tokVal != ';':
624                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDDR stable_state")
625             if doCOMMENTs:
626                 writeComment( output, tokLn, 'ENDDR' )
627             obuf = bytearray(2)
628             obuf[0] = XENDDR
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
633             output.write( obuf )
634
635         elif tokVal == 'ENDIR':
636             nextTok()
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)
640             nextTok()
641             if tokVal != ';':
642                 raise ParseError( tokLn, tokVal, "Expecting ';' after ENDIR stable_state")
643             if doCOMMENTs:
644                 writeComment( output, tokLn, 'ENDIR' )
645             obuf = bytearray(2)
646             obuf[0] = XENDIR
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
651             output.write( obuf )
652
653         elif tokVal == 'STATE':
654             nextTok()
655             ln = tokLn
656             while tokVal != ';':
657                 if tokVal not in StateTxt:
658                     raise ParseError( tokLn, tokVal, "Expecting 'stable_state' after STATE")
659                 stable_state = StateTxt.index( tokVal )
660
661                 if doCOMMENTs and ln != -1:
662                     writeComment( output, ln, 'STATE' )
663                     ln = -1     # save comment only once
664
665                 obuf = bytearray(2)
666                 obuf[0] = XSTATE
667                 obuf[1] = stable_state
668                 output.write( obuf )
669                 nextTok()
670
671         elif tokVal == 'FREQUENCY':
672             nextTok()
673             if tokVal != ';':
674                 if tokType != 'int' and tokType != 'float':
675                     raise ParseError( tokLn, tokVal, "Expecting 'cycles HZ' after FREQUENCY")
676                 frequency = tokVal
677                 nextTok()
678                 if tokVal != 'HZ':
679                     raise ParseError( tokLn, tokVal, "Expecting 'HZ' after FREQUENCY cycles")
680                 nextTok()
681                 if tokVal != ';':
682                     raise ParseError( tokLn, tokVal, "Expecting ';' after FREQUENCY cycles HZ")
683
684         else:
685             raise ParseError( tokLn, tokVal, "Unknown token '%s'" % tokVal)
686
687 except StopIteration:
688     if not expecting_eof:
689         print( "Unexpected End of File at line ", tokLn )
690
691 except ParseError as pe:
692     print( "\n", pe )
693
694 finally:
695     # print( "closing file" )
696     cmdbuf[0] = XCOMPLETE
697     output.write( cmdbuf )
698     output.close()
699