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