micro_asm.py revision 4507:487b70cfd58d
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 29import os 30import sys 31import re 32import string 33import traceback 34# get type names 35from types import * 36 37# Prepend the directory where the PLY lex & yacc modules are found 38# to the search path. 39sys.path[0:0] = [os.environ['M5_PLY']] 40 41from ply import lex 42from ply import yacc 43 44########################################################################## 45# 46# Base classes for use outside of the assembler 47# 48########################################################################## 49 50class 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 67class Combinational_Macroop(Micro_Container): 68 pass 69 70class Rom_Macroop(object): 71 def __init__(self, name, target): 72 self.name = name 73 self.target = target 74 75class 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 86class Label(object): 87 def __init__(self): 88 self.extern = False 89 self.name = "" 90 91class Block(object): 92 def __init__(self): 93 self.statements = [] 94 95class Statement(object): 96 def __init__(self): 97 self.is_microop = False 98 self.is_directive = False 99 self.params = "" 100 101class Microop(Statement): 102 def __init__(self): 103 super(Microop, self).__init__() 104 self.mnemonic = "" 105 self.labels = [] 106 self.is_microop = True 107 108class 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 120def print_error(message): 121 print 122 print "*** %s" % message 123 print 124 125def 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). 162def 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 172reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN') 173 174tokens = 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 188states = ( 189 ('asm', 'exclusive'), 190 ('params', 'exclusive'), 191) 192 193reserved_map = { } 194for r in reserved: 195 reserved_map[r.lower()] = r 196 197def t_ANY_COMMENT(t): 198 r'\#[^\n]*(?=\n)' 199 #print "t_ANY_COMMENT %s" % t.value 200 201def t_ANY_MULTILINECOMMENT(t): 202 r'/\*([^/]|((?<!\*)/))*\*/' 203 #print "t_ANY_MULTILINECOMMENT %s" % t.value 204 205def t_params_COLON(t): 206 r':' 207 t.lexer.begin('asm') 208 #print "t_params_COLON %s" % t.value 209 return t 210 211def 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 218def 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 224def 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 231def t_INITIAL_LBRACE(t): 232 r'\{' 233 t.lexer.begin('asm') 234 #print "t_INITIAL_LBRACE %s" % t.value 235 return t 236 237def t_asm_RBRACE(t): 238 r'\}' 239 t.lexer.begin('INITIAL') 240 #print "t_asm_RBRACE %s" % t.value 241 return t 242 243def t_INITIAL_NEWLINE(t): 244 r'\n+' 245 t.lineno += t.value.count('\n') 246 #print "t_INITIAL_NEWLINE %s" % t.value 247 248def 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 254def 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 261def 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 268t_ANY_LPAREN = r'\(' 269t_ANY_RPAREN = r'\)' 270t_ANY_SEMI = r';' 271t_ANY_DOT = r'\.' 272 273t_ANY_ignore = ' \t\x0c' 274 275def 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. 287def p_file(t): 288 'file : opt_rom_or_macros' 289 290def p_opt_rom_or_macros_0(t): 291 'opt_rom_or_macros : ' 292 293def p_opt_rom_or_macros_1(t): 294 'opt_rom_or_macros : rom_or_macros' 295 296def p_rom_or_macros_0(t): 297 'rom_or_macros : rom_or_macro' 298 299def p_rom_or_macros_1(t): 300 'rom_or_macros : rom_or_macros rom_or_macro' 301 302def p_rom_or_macro_0(t): 303 '''rom_or_macro : rom_block''' 304 305def p_rom_or_macro_1(t): 306 '''rom_or_macro : macroop_def''' 307 308# A block of statements 309def 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 316def 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 326def 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 336def 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 347def p_statements_0(t): 348 'statements : statement' 349 if t[1]: 350 t[0] = [t[1]] 351 else: 352 t[0] = [] 353 354def p_statements_1(t): 355 'statements : statements statement' 356 if t[2]: 357 t[1].append(t[2]) 358 t[0] = t[1] 359 360def 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 365def p_content_of_statement_0(t): 366 '''content_of_statement : microop 367 | directive''' 368 t[0] = t[1] 369 370def p_content_of_statement_1(t): 371 'content_of_statement : ' 372 pass 373 374# Statements are ended by newlines or a semi colon 375def p_end_of_statement(t): 376 '''end_of_statement : NEWLINE 377 | SEMI''' 378 pass 379 380def 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 387def p_microop_1(t): 388 'microop : ID' 389 microop = Microop() 390 microop.mnemonic = t[1] 391 t[0] = microop 392 393def 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 401def 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 408def p_labels_0(t): 409 'labels : label' 410 t[0] = [t[1]] 411 412def p_labels_1(t): 413 'labels : labels label' 414 t[1].append(t[2]) 415 t[0] = t[1] 416 417def 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 424def 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 431def p_directive_0(t): 432 'directive : DOT ID' 433 directive = Directive() 434 directive.name = t[2] 435 t[0] = directive 436 437def 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) 446def 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 452class 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 474