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