d9d9d1b21f5db13c3de87f0f06603c64e449eb2a
[gem5.git] / src / arch / micro_asm.py
1 # Copyright (c) 2003-2005 The Regents of The University of Michigan
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #
27 # Authors: Gabe Black
28
29 import os
30 import sys
31 import re
32 import string
33 import traceback
34 # get type names
35 from types import *
36
37 # Prepend the directory where the PLY lex & yacc modules are found
38 # to the search path.
39 sys.path[0:0] = [os.environ['M5_PLY']]
40
41 from ply import lex
42 from ply import yacc
43
44 ##########################################################################
45 #
46 # Base classes for use outside of the assembler
47 #
48 ##########################################################################
49
50 class Micro_Container(object):
51 def __init__(self, name):
52 self.microops = []
53 self.name = name
54 self.directives = {}
55 self.micro_classes = {}
56 self.labels = {}
57
58 def add_microop(self, microop):
59 self.microops.append(microop)
60
61 def __str__(self):
62 string = "%s:\n" % self.name
63 for microop in self.microops:
64 string += " %s\n" % microop
65 return string
66
67 class Combinational_Macroop(Micro_Container):
68 pass
69
70 class Rom_Macroop(object):
71 def __init__(self, name, target):
72 self.name = name
73 self.target = target
74
75 class Rom(Micro_Container):
76 def __init__(self, name):
77 super(Rom, self).__init__(name)
78 self.externs = {}
79
80 ##########################################################################
81 #
82 # Support classes
83 #
84 ##########################################################################
85
86 class Label(object):
87 def __init__(self):
88 self.extern = False
89 self.name = ""
90
91 class Block(object):
92 def __init__(self):
93 self.statements = []
94
95 class Statement(object):
96 def __init__(self):
97 self.is_microop = False
98 self.is_directive = False
99 self.params = ""
100
101 class Microop(Statement):
102 def __init__(self):
103 super(Microop, self).__init__()
104 self.mnemonic = ""
105 self.labels = []
106 self.is_microop = True
107
108 class Directive(Statement):
109 def __init__(self):
110 super(Directive, self).__init__()
111 self.name = ""
112 self.is_directive = True
113
114 ##########################################################################
115 #
116 # Functions that handle common tasks
117 #
118 ##########################################################################
119
120 def print_error(message):
121 print
122 print "*** %s" % message
123 print
124
125 def handle_statement(parser, container, statement):
126 if statement.is_microop:
127 try:
128 microop = eval('parser.microops[statement.mnemonic](%s)' %
129 statement.params)
130 except:
131 print_error("Error creating microop object.")
132 raise
133 try:
134 for label in statement.labels:
135 container.labels[label.name] = microop
136 if label.extern:
137 container.externs[label.name] = microop
138 container.add_microop(microop)
139 except:
140 print_error("Error adding microop.")
141 raise
142 elif statement.is_directive:
143 try:
144 eval('container.directives[statement.name](%s)' % statement.params)
145 except:
146 print_error("Error executing directive.")
147 print container.directives
148 raise
149 else:
150 raise Exception, "Didn't recognize the type of statement", statement
151
152 ##########################################################################
153 #
154 # Lexer specification
155 #
156 ##########################################################################
157
158 # Error handler. Just call exit. Output formatted to work under
159 # Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
160 # prints a Python stack backtrace too (can be handy when trying to
161 # debug the parser itself).
162 def error(lineno, string, print_traceback = False):
163 # Print a Python stack backtrace if requested.
164 if (print_traceback):
165 traceback.print_exc()
166 if lineno != 0:
167 line_str = "%d:" % lineno
168 else:
169 line_str = ""
170 sys.exit("%s %s" % (line_str, string))
171
172 reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
173
174 tokens = reserved + (
175 # identifier
176 'ID',
177 # arguments for microops and directives
178 'PARAMS',
179
180 'LPAREN', 'RPAREN',
181 'LBRACE', 'RBRACE',
182 'COLON', 'SEMI', 'DOT',
183 'NEWLINE'
184 )
185
186 # New lines are ignored at the top level, but they end statements in the
187 # assembler
188 states = (
189 ('asm', 'exclusive'),
190 ('params', 'exclusive'),
191 )
192
193 reserved_map = { }
194 for r in reserved:
195 reserved_map[r.lower()] = r
196
197 def t_ANY_COMMENT(t):
198 r'\#[^\n]*(?=\n)'
199 #print "t_ANY_COMMENT %s" % t.value
200
201 def t_ANY_MULTILINECOMMENT(t):
202 r'/\*([^/]|((?<!\*)/))*\*/'
203 #print "t_ANY_MULTILINECOMMENT %s" % t.value
204
205 def t_params_COLON(t):
206 r':'
207 t.lexer.begin('asm')
208 #print "t_params_COLON %s" % t.value
209 return t
210
211 def t_asm_ID(t):
212 r'[A-Za-z_]\w*'
213 t.type = reserved_map.get(t.value, 'ID')
214 t.lexer.begin('params')
215 #print "t_asm_ID %s" % t.value
216 return t
217
218 def t_ANY_ID(t):
219 r'[A-Za-z_]\w*'
220 t.type = reserved_map.get(t.value, 'ID')
221 #print "t_ANY_ID %s" % t.value
222 return t
223
224 def t_params_PARAMS(t):
225 r'([^\n;]|((?<=\\)[\n;]))+'
226 t.lineno += t.value.count('\n')
227 t.lexer.begin('asm')
228 #print "t_params_PARAMS %s" % t.value
229 return t
230
231 def t_INITIAL_LBRACE(t):
232 r'\{'
233 t.lexer.begin('asm')
234 #print "t_INITIAL_LBRACE %s" % t.value
235 return t
236
237 def t_asm_RBRACE(t):
238 r'\}'
239 t.lexer.begin('INITIAL')
240 #print "t_asm_RBRACE %s" % t.value
241 return t
242
243 def t_INITIAL_NEWLINE(t):
244 r'\n+'
245 t.lineno += t.value.count('\n')
246 #print "t_INITIAL_NEWLINE %s" % t.value
247
248 def t_asm_NEWLINE(t):
249 r'\n+'
250 t.lineno += t.value.count('\n')
251 #print "t_asm_NEWLINE %s" % t.value
252 return t
253
254 def t_params_NEWLINE(t):
255 r'\n+'
256 t.lineno += t.value.count('\n')
257 t.lexer.begin('asm')
258 #print "t_params_NEWLINE %s" % t.value
259 return t
260
261 def t_params_SEMI(t):
262 r';'
263 t.lexer.begin('asm')
264 #print "t_params_SEMI %s" % t.value
265 return t
266
267 # Basic regular expressions to pick out simple tokens
268 t_ANY_LPAREN = r'\('
269 t_ANY_RPAREN = r'\)'
270 t_ANY_SEMI = r';'
271 t_ANY_DOT = r'\.'
272
273 t_ANY_ignore = ' \t\x0c'
274
275 def t_ANY_error(t):
276 error(t.lineno, "illegal character '%s'" % t.value[0])
277 t.skip(1)
278
279 ##########################################################################
280 #
281 # Parser specification
282 #
283 ##########################################################################
284
285 # Start symbol for a file which may have more than one macroop or rom
286 # specification.
287 def p_file(t):
288 'file : opt_rom_or_macros'
289
290 def p_opt_rom_or_macros_0(t):
291 'opt_rom_or_macros : '
292
293 def p_opt_rom_or_macros_1(t):
294 'opt_rom_or_macros : rom_or_macros'
295
296 def p_rom_or_macros_0(t):
297 'rom_or_macros : rom_or_macro'
298
299 def p_rom_or_macros_1(t):
300 'rom_or_macros : rom_or_macros rom_or_macro'
301
302 def p_rom_or_macro_0(t):
303 '''rom_or_macro : rom_block'''
304
305 def p_rom_or_macro_1(t):
306 '''rom_or_macro : macroop_def'''
307
308 # A block of statements
309 def p_block(t):
310 'block : LBRACE statements RBRACE'
311 block = Block()
312 block.statements = t[2]
313 t[0] = block
314
315 # Defines a section of microcode that should go in the current ROM
316 def p_rom_block(t):
317 'rom_block : DEF ROM block SEMI'
318 if not t.parser.rom:
319 print_error("Rom block found, but no Rom object specified.")
320 raise TypeError, "Rom block found, but no Rom object was specified."
321 for statement in t[3].statements:
322 handle_statement(t.parser, t.parser.rom, statement)
323 t[0] = t.parser.rom
324
325 # Defines a macroop that jumps to an external label in the ROM
326 def p_macroop_def_0(t):
327 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
328 if not t.parser.rom_macroop_type:
329 print_error("ROM based macroop found, but no ROM macroop class was specified.")
330 raise TypeError, "ROM based macroop found, but no ROM macroop class was specified."
331 macroop = t.parser.rom_macroop_type(t[3], t[5])
332 t[0] = macroop
333
334
335 # Defines a macroop that is combinationally generated
336 def p_macroop_def_1(t):
337 'macroop_def : DEF MACROOP ID block SEMI'
338 try:
339 curop = t.parser.macro_type(t[3])
340 except TypeError:
341 print_error("Error creating macroop object.")
342 raise
343 for statement in t[4].statements:
344 handle_statement(t.parser, curop, statement)
345 t.parser.macroops[t[3]] = curop
346
347 def p_statements_0(t):
348 'statements : statement'
349 if t[1]:
350 t[0] = [t[1]]
351 else:
352 t[0] = []
353
354 def p_statements_1(t):
355 'statements : statements statement'
356 if t[2]:
357 t[1].append(t[2])
358 t[0] = t[1]
359
360 def p_statement(t):
361 'statement : content_of_statement end_of_statement'
362 t[0] = t[1]
363
364 # A statement can be a microop or an assembler directive
365 def p_content_of_statement_0(t):
366 '''content_of_statement : microop
367 | directive'''
368 t[0] = t[1]
369
370 def p_content_of_statement_1(t):
371 'content_of_statement : '
372 pass
373
374 # Statements are ended by newlines or a semi colon
375 def p_end_of_statement(t):
376 '''end_of_statement : NEWLINE
377 | SEMI'''
378 pass
379
380 def p_microop_0(t):
381 'microop : labels ID'
382 microop = Microop()
383 microop.labels = t[1]
384 microop.mnemonic = t[2]
385 t[0] = microop
386
387 def p_microop_1(t):
388 'microop : ID'
389 microop = Microop()
390 microop.mnemonic = t[1]
391 t[0] = microop
392
393 def p_microop_2(t):
394 'microop : labels ID PARAMS'
395 microop = Microop()
396 microop.labels = t[1]
397 microop.mnemonic = t[2]
398 microop.params = t[3]
399 t[0] = microop
400
401 def p_microop_3(t):
402 'microop : ID PARAMS'
403 microop = Microop()
404 microop.mnemonic = t[1]
405 microop.params = t[2]
406 t[0] = microop
407
408 def p_labels_0(t):
409 'labels : label'
410 t[0] = [t[1]]
411
412 def p_labels_1(t):
413 'labels : labels label'
414 t[1].append(t[2])
415 t[0] = t[1]
416
417 def p_label_0(t):
418 'label : ID COLON'
419 label = Label()
420 label.is_extern = False
421 label.text = t[1]
422 t[0] = label
423
424 def p_label_1(t):
425 'label : EXTERN ID COLON'
426 label = Label()
427 label.is_extern = True
428 label.text = t[2]
429 t[0] = label
430
431 def p_directive_0(t):
432 'directive : DOT ID'
433 directive = Directive()
434 directive.name = t[2]
435 t[0] = directive
436
437 def p_directive_1(t):
438 'directive : DOT ID PARAMS'
439 directive = Directive()
440 directive.name = t[2]
441 directive.params = t[3]
442 t[0] = directive
443
444 # Parse error handler. Note that the argument here is the offending
445 # *token*, not a grammar symbol (hence the need to use t.value)
446 def p_error(t):
447 if t:
448 error(t.lineno, "syntax error at '%s'" % t.value)
449 else:
450 error(0, "unknown syntax error", True)
451
452 class MicroAssembler(object):
453
454 def __init__(self, macro_type, microops,
455 rom = None, rom_macroop_type = None):
456 self.lexer = lex.lex()
457 self.parser = yacc.yacc()
458 self.parser.macro_type = macro_type
459 self.parser.macroops = {}
460 self.parser.microops = microops
461 self.parser.rom = rom
462 self.parser.rom_macroop_type = rom_macroop_type
463
464 def assemble(self, asm):
465 self.parser.parse(asm, lexer=self.lexer)
466 # Begin debug printing
467 for macroop in self.parser.macroops.values():
468 print macroop
469 print self.parser.rom
470 # End debug printing
471 macroops = self.parser.macroops
472 self.parser.macroops = {}
473 return macroops