Merge zizzer:/bk/newmem
[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 Macroop(Micro_Container):
68 pass
69
70 class Rom(Micro_Container):
71 def __init__(self, name):
72 super(Rom, self).__init__(name)
73 self.externs = {}
74
75 ##########################################################################
76 #
77 # Support classes
78 #
79 ##########################################################################
80
81 class Label(object):
82 def __init__(self):
83 self.extern = False
84 self.name = ""
85
86 class Block(object):
87 def __init__(self):
88 self.statements = []
89
90 class Statement(object):
91 def __init__(self):
92 self.is_microop = False
93 self.is_directive = False
94 self.params = ""
95
96 class Microop(Statement):
97 def __init__(self):
98 super(Microop, self).__init__()
99 self.mnemonic = ""
100 self.labels = []
101 self.is_microop = True
102
103 class Directive(Statement):
104 def __init__(self):
105 super(Directive, self).__init__()
106 self.name = ""
107 self.is_directive = True
108
109 ##########################################################################
110 #
111 # Functions that handle common tasks
112 #
113 ##########################################################################
114
115 def print_error(message):
116 print
117 print "*** %s" % message
118 print
119
120 def handle_statement(parser, container, statement):
121 if statement.is_microop:
122 try:
123 microop = eval('parser.microops[statement.mnemonic](%s)' %
124 statement.params)
125 except:
126 print_error("Error creating microop object.")
127 raise
128 try:
129 for label in statement.labels:
130 container.labels[label.name] = microop
131 if label.extern:
132 container.externs[label.name] = microop
133 container.add_microop(microop)
134 except:
135 print_error("Error adding microop.")
136 raise
137 elif statement.is_directive:
138 try:
139 eval('container.directives[statement.name](%s)' % statement.params)
140 except:
141 print_error("Error executing directive.")
142 print container.directives
143 raise
144 else:
145 raise Exception, "Didn't recognize the type of statement", statement
146
147 ##########################################################################
148 #
149 # Lexer specification
150 #
151 ##########################################################################
152
153 # Error handler. Just call exit. Output formatted to work under
154 # Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
155 # prints a Python stack backtrace too (can be handy when trying to
156 # debug the parser itself).
157 def error(lineno, string, print_traceback = False):
158 # Print a Python stack backtrace if requested.
159 if (print_traceback):
160 traceback.print_exc()
161 if lineno != 0:
162 line_str = "%d:" % lineno
163 else:
164 line_str = ""
165 sys.exit("%s %s" % (line_str, string))
166
167 reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
168
169 tokens = reserved + (
170 # identifier
171 'ID',
172 # arguments for microops and directives
173 'PARAMS',
174
175 'LPAREN', 'RPAREN',
176 'LBRACE', 'RBRACE',
177 'COLON', 'SEMI', 'DOT',
178 'NEWLINE'
179 )
180
181 # New lines are ignored at the top level, but they end statements in the
182 # assembler
183 states = (
184 ('asm', 'exclusive'),
185 ('params', 'exclusive'),
186 )
187
188 reserved_map = { }
189 for r in reserved:
190 reserved_map[r.lower()] = r
191
192 def t_ANY_COMMENT(t):
193 r'\#[^\n]*(?=\n)'
194 #print "t_ANY_COMMENT %s" % t.value
195
196 def t_ANY_MULTILINECOMMENT(t):
197 r'/\*([^/]|((?<!\*)/))*\*/'
198 #print "t_ANY_MULTILINECOMMENT %s" % t.value
199
200 def t_params_COLON(t):
201 r':'
202 t.lexer.begin('asm')
203 #print "t_params_COLON %s" % t.value
204 return t
205
206 def t_asm_ID(t):
207 r'[A-Za-z_]\w*'
208 t.type = reserved_map.get(t.value, 'ID')
209 t.lexer.begin('params')
210 #print "t_asm_ID %s" % t.value
211 return t
212
213 def t_ANY_ID(t):
214 r'[A-Za-z_]\w*'
215 t.type = reserved_map.get(t.value, 'ID')
216 #print "t_ANY_ID %s" % t.value
217 return t
218
219 def t_params_PARAMS(t):
220 r'([^\n;]|((?<=\\)[\n;]))+'
221 t.lineno += t.value.count('\n')
222 t.lexer.begin('asm')
223 #print "t_params_PARAMS %s" % t.value
224 return t
225
226 def t_INITIAL_LBRACE(t):
227 r'\{'
228 t.lexer.begin('asm')
229 #print "t_INITIAL_LBRACE %s" % t.value
230 return t
231
232 def t_asm_RBRACE(t):
233 r'\}'
234 t.lexer.begin('INITIAL')
235 #print "t_asm_RBRACE %s" % t.value
236 return t
237
238 def t_INITIAL_NEWLINE(t):
239 r'\n+'
240 t.lineno += t.value.count('\n')
241 #print "t_INITIAL_NEWLINE %s" % t.value
242
243 def t_asm_NEWLINE(t):
244 r'\n+'
245 t.lineno += t.value.count('\n')
246 #print "t_asm_NEWLINE %s" % t.value
247 return t
248
249 def t_params_NEWLINE(t):
250 r'\n+'
251 t.lineno += t.value.count('\n')
252 t.lexer.begin('asm')
253 #print "t_params_NEWLINE %s" % t.value
254 return t
255
256 def t_params_SEMI(t):
257 r';'
258 t.lexer.begin('asm')
259 #print "t_params_SEMI %s" % t.value
260 return t
261
262 # Basic regular expressions to pick out simple tokens
263 t_ANY_LPAREN = r'\('
264 t_ANY_RPAREN = r'\)'
265 t_ANY_SEMI = r';'
266 t_ANY_DOT = r'\.'
267
268 t_ANY_ignore = ' \t\x0c'
269
270 def t_ANY_error(t):
271 error(t.lineno, "illegal character '%s'" % t.value[0])
272 t.skip(1)
273
274 ##########################################################################
275 #
276 # Parser specification
277 #
278 ##########################################################################
279
280 # Start symbol for a file which may have more than one macroop or rom
281 # specification.
282 def p_file(t):
283 'file : opt_rom_or_macros'
284
285 def p_opt_rom_or_macros_0(t):
286 'opt_rom_or_macros : '
287
288 def p_opt_rom_or_macros_1(t):
289 'opt_rom_or_macros : rom_or_macros'
290
291 def p_rom_or_macros_0(t):
292 'rom_or_macros : rom_or_macro'
293
294 def p_rom_or_macros_1(t):
295 'rom_or_macros : rom_or_macros rom_or_macro'
296
297 def p_rom_or_macro_0(t):
298 '''rom_or_macro : rom_block'''
299
300 def p_rom_or_macro_1(t):
301 '''rom_or_macro : macroop_def'''
302
303 # A block of statements
304 def p_block(t):
305 'block : LBRACE statements RBRACE'
306 block = Block()
307 block.statements = t[2]
308 t[0] = block
309
310 # Defines a section of microcode that should go in the current ROM
311 def p_rom_block(t):
312 'rom_block : DEF ROM block SEMI'
313 for statement in t[3].statements:
314 handle_statement(t.parser, t.parser.rom, statement)
315 t[0] = t.parser.rom
316
317 # Defines a macroop that jumps to an external label in the ROM
318 def p_macroop_def_0(t):
319 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
320 t[0] = t[4]
321
322 # Defines a macroop that is combinationally generated
323 def p_macroop_def_1(t):
324 'macroop_def : DEF MACROOP ID block SEMI'
325 try:
326 curop = t.parser.macro_type(t[3])
327 except TypeError:
328 print_error("Error creating macroop object.")
329 raise
330 for statement in t[4].statements:
331 handle_statement(t.parser, curop, statement)
332 t.parser.macroops[t[3]] = curop
333
334 def p_statements_0(t):
335 'statements : statement'
336 if t[1]:
337 t[0] = [t[1]]
338 else:
339 t[0] = []
340
341 def p_statements_1(t):
342 'statements : statements statement'
343 if t[2]:
344 t[1].append(t[2])
345 t[0] = t[1]
346
347 def p_statement(t):
348 'statement : content_of_statement end_of_statement'
349 t[0] = t[1]
350
351 # A statement can be a microop or an assembler directive
352 def p_content_of_statement_0(t):
353 '''content_of_statement : microop
354 | directive'''
355 t[0] = t[1]
356
357 def p_content_of_statement_1(t):
358 'content_of_statement : '
359 pass
360
361 # Statements are ended by newlines or a semi colon
362 def p_end_of_statement(t):
363 '''end_of_statement : NEWLINE
364 | SEMI'''
365 pass
366
367 def p_microop_0(t):
368 'microop : labels ID'
369 microop = Microop()
370 microop.labels = t[1]
371 microop.mnemonic = t[2]
372 t[0] = microop
373
374 def p_microop_1(t):
375 'microop : ID'
376 microop = Microop()
377 microop.mnemonic = t[1]
378 t[0] = microop
379
380 def p_microop_2(t):
381 'microop : labels ID PARAMS'
382 microop = Microop()
383 microop.labels = t[1]
384 microop.mnemonic = t[2]
385 microop.params = t[3]
386 t[0] = microop
387
388 def p_microop_3(t):
389 'microop : ID PARAMS'
390 microop = Microop()
391 microop.mnemonic = t[1]
392 microop.params = t[2]
393 t[0] = microop
394
395 def p_labels_0(t):
396 'labels : label'
397 t[0] = [t[1]]
398
399 def p_labels_1(t):
400 'labels : labels label'
401 t[1].append(t[2])
402 t[0] = t[1]
403
404 def p_label_0(t):
405 'label : ID COLON'
406 label = Label()
407 label.is_extern = False
408 label.text = t[1]
409 t[0] = label
410
411 def p_label_1(t):
412 'label : EXTERN ID COLON'
413 label = Label()
414 label.is_extern = True
415 label.text = t[2]
416 t[0] = label
417
418 def p_directive_0(t):
419 'directive : DOT ID'
420 directive = Directive()
421 directive.name = t[2]
422 t[0] = directive
423
424 def p_directive_1(t):
425 'directive : DOT ID PARAMS'
426 directive = Directive()
427 directive.name = t[2]
428 directive.params = t[3]
429 t[0] = directive
430
431 # Parse error handler. Note that the argument here is the offending
432 # *token*, not a grammar symbol (hence the need to use t.value)
433 def p_error(t):
434 if t:
435 error(t.lineno, "syntax error at '%s'" % t.value)
436 else:
437 error(0, "unknown syntax error", True)
438
439 class MicroAssembler(object):
440
441 def __init__(self, macro_type, microops, rom):
442 self.lexer = lex.lex()
443 self.parser = yacc.yacc()
444 self.parser.macro_type = macro_type
445 self.parser.macroops = {}
446 self.parser.microops = microops
447 self.parser.rom = rom
448
449 def assemble(self, asm):
450 self.parser.parse(asm, lexer=self.lexer)
451 # Begin debug printing
452 for macroop in self.parser.macroops.values():
453 print macroop
454 print self.parser.rom
455 # End debug printing
456 macroops = self.parser.macroops
457 self.parser.macroops = {}
458 return macroops