Early micro assembler
authorGabe Black <gblack@eecs.umich.edu>
Thu, 31 May 2007 13:52:48 +0000 (13:52 +0000)
committerGabe Black <gblack@eecs.umich.edu>
Thu, 31 May 2007 13:52:48 +0000 (13:52 +0000)
src/arch/micro_asm.py:
    Micro assembler
src/arch/micro_asm_test.py:
    Test script for the micro assembler. This probably should go somewhere else eventually.

--HG--
extra : convert_revision : 277fdadec94763ae657f55f501704693b81e0015

src/arch/micro_asm.py [new file with mode: 0644]
src/arch/micro_asm_test.py [new file with mode: 0755]

diff --git a/src/arch/micro_asm.py b/src/arch/micro_asm.py
new file mode 100644 (file)
index 0000000..3d9e836
--- /dev/null
@@ -0,0 +1,433 @@
+# Copyright (c) 2003-2005 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Gabe Black
+
+import os
+import sys
+import re
+import string
+import traceback
+# get type names
+from types import *
+
+# Prepend the directory where the PLY lex & yacc modules are found
+# to the search path.
+sys.path[0:0] = [os.environ['M5_PLY']]
+
+from ply import lex
+from ply import yacc
+
+##########################################################################
+#
+# Base classes for use outside of the assembler
+#
+##########################################################################
+
+class Micro_Container(object):
+    def __init__(self, name):
+        self.microops = []
+        self.name = name
+        self.directives = {}
+        self.micro_classes = {}
+        self.labels = {}
+
+    def add_microop(self, microop):
+        self.microops.append(microop)
+
+    def __str__(self):
+        string = "%s:\n" % self.name
+        for microop in self.microops:
+            string += "  %s\n" % microop
+        return string
+
+class Macroop(Micro_Container):
+    pass
+
+class Rom(Micro_Container):
+    def __init__(self, name):
+        super(Rom, self).__init__(name)
+        self.externs = {}
+
+##########################################################################
+#
+# Support classes
+#
+##########################################################################
+
+class Label(object):
+    def __init__(self):
+        self.extern = False
+        self.name = ""
+
+class Block(object):
+    def __init__(self):
+        self.statements = []
+
+class Statement(object):
+    def __init__(self):
+        self.is_microop = False
+        self.is_directive = False
+
+class Microop(Statement):
+    def __init__(self):
+        super(Microop, self).__init__()
+        self.mnemonic = ""
+        self.labels = []
+        self.is_microop = True
+        self.params = ""
+
+class Directive(Statement):
+    def __init__(self):
+        super(Directive, self).__init__()
+        self.name = ""
+        self.is_directive = True
+
+##########################################################################
+#
+# Functions that handle common tasks
+#
+##########################################################################
+
+def print_error(message):
+    print
+    print "*** %s" % message
+    print
+
+def handle_statement(parser, container, statement):
+    if statement.is_microop:
+        try:
+            microop = eval('parser.microops[statement.mnemonic](%s)' %
+                    statement.params)
+        except:
+            print_error("Error creating microop object.")
+            raise
+        try:
+            for label in statement.labels:
+                container.labels[label.name] = microop
+                if label.extern:
+                    container.externs[label.name] = microop
+            container.add_microop(microop)
+        except:
+            print_error("Error adding microop.")
+            raise
+    elif statement.is_directive:
+        try:
+            eval('container.%s()' % statement.name)
+        except:
+            print_error("Error executing directive.")
+            print container.directives
+            raise
+    else:
+        raise Exception, "Didn't recognize the type of statement", statement
+
+##########################################################################
+#
+# Lexer specification
+#
+##########################################################################
+
+# Error handler.  Just call exit.  Output formatted to work under
+# Emacs compile-mode.  Optional 'print_traceback' arg, if set to True,
+# prints a Python stack backtrace too (can be handy when trying to
+# debug the parser itself).
+def error(lineno, string, print_traceback = False):
+    # Print a Python stack backtrace if requested.
+    if (print_traceback):
+        traceback.print_exc()
+    if lineno != 0:
+        line_str = "%d:" % lineno
+    else:
+        line_str = ""
+    sys.exit("%s %s" % (line_str, string))
+
+reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
+
+tokens = reserved + (
+        # identifier
+        'ID',
+        # arguments for microops and directives
+        'PARAMS',
+
+        'LPAREN', 'RPAREN',
+        'LBRACE', 'RBRACE',
+        #'COMMA',
+        'COLON', 'SEMI', 'DOT',
+        'NEWLINE'
+        )
+
+# New lines are ignored at the top level, but they end statements in the
+# assembler
+states = (
+    ('asm', 'exclusive'),
+    ('params', 'exclusive'),
+)
+
+reserved_map = { }
+for r in reserved:
+    reserved_map[r.lower()] = r
+
+def t_params_COLON(t):
+    r':'
+    t.lexer.begin('asm')
+    return t
+
+def t_asm_ID(t):
+    r'[A-Za-z_]\w*'
+    t.type = reserved_map.get(t.value, 'ID')
+    t.lexer.begin('params')
+    return t
+
+def t_ANY_ID(t):
+    r'[A-Za-z_]\w*'
+    t.type = reserved_map.get(t.value, 'ID')
+    return t
+
+def t_params_PARAMS(t):
+    r'([^\n;]|((?<=\\)[\n;]))+'
+    t.lineno += t.value.count('\n')
+    t.lexer.begin('asm')
+    return t
+
+def t_INITIAL_LBRACE(t):
+    r'\{'
+    t.lexer.begin('asm')
+    return t
+
+def t_asm_RBRACE(t):
+    r'\}'
+    t.lexer.begin('INITIAL')
+    return t
+
+def t_INITIAL_NEWLINE(t):
+    r'\n+'
+    t.lineno += t.value.count('\n')
+
+def t_asm_NEWLINE(t):
+    r'\n+'
+    t.lineno += t.value.count('\n')
+    return t
+
+def t_params_NEWLINE(t):
+    r'\n+'
+    t.lineno += t.value.count('\n')
+    t.lexer.begin('asm')
+    return t
+
+def t_params_SEMI(t):
+    r';'
+    t.lexer.begin('asm')
+    return t
+
+# Basic regular expressions to pick out simple tokens
+t_ANY_LPAREN = r'\('
+t_ANY_RPAREN = r'\)'
+#t_COMMA  = r','
+t_ANY_SEMI   = r';'
+t_ANY_DOT    = r'\.'
+
+t_ANY_ignore = ' \t\x0c'
+
+def t_ANY_error(t):
+    error(t.lineno, "illegal character '%s'" % t.value[0])
+    t.skip(1)
+
+##########################################################################
+#
+# Parser specification
+#
+##########################################################################
+
+# Start symbol for a file which may have more than one macroop or rom
+# specification.
+def p_file(t):
+    'file : opt_rom_or_macros'
+
+def p_opt_rom_or_macros_0(t):
+    'opt_rom_or_macros : '
+
+def p_opt_rom_or_macros_1(t):
+    'opt_rom_or_macros : rom_or_macros'
+
+def p_rom_or_macros_0(t):
+    'rom_or_macros : rom_or_macro'
+
+def p_rom_or_macros_1(t):
+    'rom_or_macros : rom_or_macros rom_or_macro'
+
+def p_rom_or_macro_0(t):
+    '''rom_or_macro : rom_block'''
+
+def p_rom_or_macro_1(t):
+    '''rom_or_macro : macroop_def'''
+
+# A block of statements
+def p_block(t):
+    'block : LBRACE statements RBRACE'
+    block = Block()
+    block.statements = t[2]
+    t[0] = block
+
+# Defines a section of microcode that should go in the current ROM
+def p_rom_block(t):
+    'rom_block : DEF ROM block SEMI'
+    for statement in t[3].statements:
+        handle_statement(t.parser, t.parser.rom, statement)
+    t[0] = t.parser.rom
+
+# Defines a macroop that jumps to an external label in the ROM
+def p_macroop_def_0(t):
+    'macroop_def : DEF MACROOP LPAREN ID RPAREN SEMI'
+    t[0] = t[4]
+
+# Defines a macroop that is combinationally generated
+def p_macroop_def_1(t):
+    'macroop_def : DEF MACROOP ID block SEMI'
+    try:
+        curop = t.parser.macro_type(t[3])
+    except TypeError:
+        print_error("Error creating macroop object.")
+        raise
+    for statement in t[4].statements:
+        handle_statement(t.parser, curop, statement)
+    t.parser.macroops.append(curop)
+
+def p_statements_0(t):
+    'statements : statement'
+    if t[1]:
+        t[0] = [t[1]]
+    else:
+        t[0] = []
+
+def p_statements_1(t):
+    'statements : statements statement'
+    if t[2]:
+        t[1].append(t[2])
+    t[0] = t[1]
+
+def p_statement(t):
+    'statement : content_of_statement end_of_statement'
+    t[0] = t[1]
+
+# A statement can be a microop or an assembler directive
+def p_content_of_statement_0(t):
+    '''content_of_statement : microop
+                            | directive'''
+    t[0] = t[1]
+
+def p_content_of_statement_1(t):
+    'content_of_statement : '
+    pass
+
+# Statements are ended by newlines or a semi colon
+def p_end_of_statement(t):
+    '''end_of_statement : NEWLINE
+                        | SEMI'''
+    pass
+
+def p_microop_0(t):
+    'microop : labels ID'
+    microop = Microop()
+    microop.labels = t[1]
+    microop.mnemonic = t[2]
+    t[0] = microop
+
+def p_microop_1(t):
+    'microop : ID'
+    microop = Microop()
+    microop.mnemonic = t[1]
+    t[0] = microop
+
+def p_microop_2(t):
+    'microop : labels ID PARAMS'
+    microop = Microop()
+    microop.labels = t[1]
+    microop.mnemonic = t[2]
+    microop.params = t[3]
+    t[0] = microop
+
+def p_microop_3(t):
+    'microop : ID PARAMS'
+    microop = Microop()
+    microop.mnemonic = t[1]
+    microop.params = t[2]
+    t[0] = microop
+
+def p_labels_0(t):
+    'labels : label'
+    t[0] = [t[1]]
+
+def p_labels_1(t):
+    'labels : labels label'
+    t[1].append(t[2])
+    t[0] = t[1]
+
+def p_label_0(t):
+    'label : ID COLON'
+    label = Label()
+    label.is_extern = False
+    label.text = t[1]
+    t[0] = label
+
+def p_label_1(t):
+    'label : EXTERN ID COLON'
+    label = Label()
+    label.is_extern = True
+    label.text = t[2]
+    t[0] = label
+
+def p_directive(t):
+    'directive : DOT ID'
+    directive = Directive()
+    directive.name = t[2]
+    t[0] = directive
+
+# Parse error handler.  Note that the argument here is the offending
+# *token*, not a grammar symbol (hence the need to use t.value)
+def p_error(t):
+    if t:
+        error(t.lineno, "syntax error at '%s'" % t.value)
+    else:
+        error(0, "unknown syntax error", True)
+
+class MicroAssembler(object):
+
+    def __init__(self, macro_type, microops, rom):
+        self.lexer = lex.lex()
+        self.parser = yacc.yacc()
+        self.parser.macro_type = macro_type
+        self.parser.macroops = []
+        self.parser.microops = microops
+        self.parser.rom = rom
+
+    def assemble(self, asm):
+        self.parser.parse(asm, lexer=self.lexer)
+        for macroop in self.parser.macroops:
+            print macroop
+        print self.parser.rom
+        macroops = self.parser.macroops
+        self.parser.macroops = []
+        return macroops
diff --git a/src/arch/micro_asm_test.py b/src/arch/micro_asm_test.py
new file mode 100755 (executable)
index 0000000..4a64356
--- /dev/null
@@ -0,0 +1,91 @@
+# Copyright (c) 2007 The Regents of The University of Michigan
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Gabe Black
+
+from micro_asm import MicroAssembler, Macroop, Rom
+
+class Bah(object):
+    def __init__(self):
+        self.mnemonic = "bah"
+
+class Bah_Tweaked(object):
+    def __init__(self):
+        self.mnemonic = "bah_tweaked"
+
+class Hoop(object):
+    def __init__(self, first_param, second_param):
+        self.mnemonic = "hoop_%s_%s" % (first_param, second_param)
+    def __str__(self):
+        return "%s" % self.mnemonic
+
+class Dah(object):
+    def __init__(self):
+        self.mnemonic = "dah"
+
+microops = {
+    "bah": Bah,
+    "hoop": Hoop,
+    "dah": Dah
+}
+
+class TestMacroop(Macroop):
+    def tweak(self):
+        microops["bah"] = Bah_Tweaked
+    def untweak(self):
+        microops["bah"] = Bah
+
+    def __init__(self, name):
+        super(TestMacroop, self).__init__(name)
+        self.directives = {
+            "tweak": self.tweak,
+            "untweak": self.untweak
+        }
+
+assembler = MicroAssembler(TestMacroop, microops, Rom('main ROM'))
+
+testAssembly = '''
+def rom {
+    goo: bah
+    extern la: hoop 4*8, "a"
+};
+
+def macroop squishy {
+    .tweak
+    bah
+    .untweak
+    bah
+    dah
+    .tweak
+};
+
+def macroop squashy {
+    bah
+};
+
+def macroop (bar);
+'''
+assembler.assemble(testAssembly)