micro_asm.py revision 4508
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 75 def __str__(self): 76 return "%s: %s\n" % (self.name, self.target) 77 78class 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 89class Label(object): 90 def __init__(self): 91 self.extern = False 92 self.name = "" 93 94class Block(object): 95 def __init__(self): 96 self.statements = [] 97 98class Statement(object): 99 def __init__(self): 100 self.is_microop = False 101 self.is_directive = False 102 self.params = "" 103 104class Microop(Statement): 105 def __init__(self): 106 super(Microop, self).__init__() 107 self.mnemonic = "" 108 self.labels = [] 109 self.is_microop = True 110 111class 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 123def print_error(message): 124 print 125 print "*** %s" % message 126 print 127 128def 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). 165def 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 175reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN') 176 177tokens = 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 191states = ( 192 ('asm', 'exclusive'), 193 ('params', 'exclusive'), 194) 195 196reserved_map = { } 197for r in reserved: 198 reserved_map[r.lower()] = r 199 200def t_ANY_COMMENT(t): 201 r'\#[^\n]*(?=\n)' 202 #print "t_ANY_COMMENT %s" % t.value 203 204def t_ANY_MULTILINECOMMENT(t): 205 r'/\*([^/]|((?<!\*)/))*\*/' 206 #print "t_ANY_MULTILINECOMMENT %s" % t.value 207 208def t_params_COLON(t): 209 r':' 210 t.lexer.begin('asm') 211 #print "t_params_COLON %s" % t.value 212 return t 213 214def 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 221def 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 227def 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 234def t_INITIAL_LBRACE(t): 235 r'\{' 236 t.lexer.begin('asm') 237 #print "t_INITIAL_LBRACE %s" % t.value 238 return t 239 240def t_asm_RBRACE(t): 241 r'\}' 242 t.lexer.begin('INITIAL') 243 #print "t_asm_RBRACE %s" % t.value 244 return t 245 246def t_INITIAL_NEWLINE(t): 247 r'\n+' 248 t.lineno += t.value.count('\n') 249 #print "t_INITIAL_NEWLINE %s" % t.value 250 251def 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 257def 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 264def 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 271t_ANY_LPAREN = r'\(' 272t_ANY_RPAREN = r'\)' 273t_ANY_SEMI = r';' 274t_ANY_DOT = r'\.' 275 276t_ANY_ignore = ' \t\x0c' 277 278def 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. 290def p_file(t): 291 'file : opt_rom_or_macros' 292 293def p_opt_rom_or_macros_0(t): 294 'opt_rom_or_macros : ' 295 296def p_opt_rom_or_macros_1(t): 297 'opt_rom_or_macros : rom_or_macros' 298 299def p_rom_or_macros_0(t): 300 'rom_or_macros : rom_or_macro' 301 302def p_rom_or_macros_1(t): 303 'rom_or_macros : rom_or_macros rom_or_macro' 304 305def p_rom_or_macro_0(t): 306 '''rom_or_macro : rom_block''' 307 308def p_rom_or_macro_1(t): 309 '''rom_or_macro : macroop_def''' 310 311# A block of statements 312def 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 319def 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 329def 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 339def 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 350def p_statements_0(t): 351 'statements : statement' 352 if t[1]: 353 t[0] = [t[1]] 354 else: 355 t[0] = [] 356 357def p_statements_1(t): 358 'statements : statements statement' 359 if t[2]: 360 t[1].append(t[2]) 361 t[0] = t[1] 362 363def 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 368def p_content_of_statement_0(t): 369 '''content_of_statement : microop 370 | directive''' 371 t[0] = t[1] 372 373def p_content_of_statement_1(t): 374 'content_of_statement : ' 375 pass 376 377# Statements are ended by newlines or a semi colon 378def p_end_of_statement(t): 379 '''end_of_statement : NEWLINE 380 | SEMI''' 381 pass 382 383def 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 390def p_microop_1(t): 391 'microop : ID' 392 microop = Microop() 393 microop.mnemonic = t[1] 394 t[0] = microop 395 396def 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 404def 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 411def p_labels_0(t): 412 'labels : label' 413 t[0] = [t[1]] 414 415def p_labels_1(t): 416 'labels : labels label' 417 t[1].append(t[2]) 418 t[0] = t[1] 419 420def 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 427def 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 434def p_directive_0(t): 435 'directive : DOT ID' 436 directive = Directive() 437 directive.name = t[2] 438 t[0] = directive 439 440def 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) 449def 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 455class 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 477