Initial commit
authorMilo Kim <milo.kim@ti.com>
Fri, 17 Apr 2015 19:43:11 +0000 (04:43 +0900)
committerMilo Kim <milo.kim@ti.com>
Fri, 17 Apr 2015 19:43:11 +0000 (04:43 +0900)
_lasm_cmd_parser.py [new file with mode: 0644]
_lasm_utils.py [new file with mode: 0644]
lasm.py [new file with mode: 0644]

diff --git a/_lasm_cmd_parser.py b/_lasm_cmd_parser.py
new file mode 100644 (file)
index 0000000..6ee46b8
--- /dev/null
@@ -0,0 +1,712 @@
+#
+# Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions an
+# limitations under the License.
+#
+#
+# LASM Command Parser
+#
+#   Search a command and convert into 16bit code
+#   Written based on documents below
+#     - www.ti.com/lit/ds/symlink/lp5523.pdf
+#     - www.ti.com/lit/an/snva664/snva664.pdf
+#
+
+import _lasm_utils as util
+
+# Return type
+SUCCESS = 0
+FAILURE = CMD_NOT_FOUND = -1
+
+
+# Display error
+# Format: 'function name' 'error message'
+def put_error(message):
+    func_name = util.get_caller_func_name()
+    util.put_error(func_name, message)
+    return FAILURE
+
+
+# Segment command is used for program engine selection (1,2,3)
+# Save start SRAM address to calculate label offset
+def segment(name):
+    args = name.split()
+    if len(args) != 1:
+        put_error('must have one directive')
+        return FAILURE
+
+    util.add_segment_list(args[0])
+    return SUCCESS
+
+
+def mux_map_start(label):
+    args = label.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    code = util.get_address_of_label(args[0], '')
+    if code == util.INVALID_ADDR:
+        return FAILURE
+
+    MUX_MAP_START = 0x9C00
+    util.put_code(MUX_MAP_START | code)
+    return SUCCESS
+
+
+def mux_map_next(val):
+    util.put_code(0x9D80)
+    return SUCCESS
+
+
+def mux_map_prev(val):
+    util.put_code(0x9DC0)
+    return SUCCESS
+
+
+def mux_ld_next(val):
+    util.put_code(0x9D81)
+    return SUCCESS
+
+
+def mux_ld_prev(val):
+    util.put_code(0x9DC1)
+    return SUCCESS
+
+
+def mux_ld_addr(label):
+    args = label.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    code = util.get_address_of_label(args[0], '')
+    if code == util.INVALID_ADDR:
+        return FAILURE
+
+    MUX_LD_ADDR = 0x9F00
+    util.put_code(MUX_LD_ADDR | code)
+    return SUCCESS
+
+
+def mux_map_addr(label):
+    args = label.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    code = util.get_address_of_label(args[0], '')
+    if code == util.INVALID_ADDR:
+        return FAILURE
+
+    MUX_MAP_ADDR = 0x9F80
+    util.put_code(MUX_MAP_ADDR | code)
+    return SUCCESS
+
+
+def mux_sel(val):
+    args = val.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    MAX_LEDS = 9
+    GPO = 16
+    led = int(args[0])
+    if (led < 0 or (led > MAX_LEDS and led is not GPO)):
+        put_error('invalid argument')
+        return FAILURE
+
+    MUX_SEL = 0x9D00
+    util.put_code(MUX_SEL | led)
+    return SUCCESS
+
+
+def mux_clr(val):
+    util.put_code(0x9D00)
+    return SUCCESS
+
+
+def mux_ld_start(label):
+    args = label.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    code = util.get_address_of_label(args[0], '')
+    if code == util.INVALID_ADDR:
+        return FAILURE
+
+    MUX_LD_START = 0x9E00
+    util.put_code(MUX_LD_START | code)
+    return SUCCESS
+
+
+def mux_ld_end(label):
+    args = label.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    code = util.get_address_of_label(args[0], '')
+    if code == util.INVALID_ADDR:
+        return FAILURE
+
+    MUX_LD_END = 0x9C80
+    util.put_code(MUX_LD_END | code)
+    return SUCCESS
+
+
+def set_pwm(val):
+    args = val.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    # Format: set_pwm <number>
+    #         set_pwm 'ra' or 'rb' or 'rc' or 'rd'
+    variables = {'ra': 0, 'rb': 1, 'rc': 2, 'rd': 3}
+    index_var = variables.get(args[0])
+    if index_var is None:
+        SET_PWM = 0x4000
+        code = util.format_number(args[0])
+        util.put_code(SET_PWM | code)
+        return SUCCESS
+
+    SET_PWM = 0x8460
+    util.put_code(SET_PWM | index_var)
+    return SUCCESS
+
+
+def wait(val):
+    args = val.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+
+    PRESCALE0 = 0.015625
+    PRESCALE1 = 0.5
+    MASTER_FREQ = 32768
+    PRESCALE0_CLK = MASTER_FREQ / 16
+    PRESCALE1_CLK = MASTER_FREQ / 512
+    MAXCODE = 0x1F
+    PRESCALE_CODE = 0x40
+
+    time = float(args[0])
+
+    if time < 0:
+        put_error('time should be positive value')
+        return FAILURE
+
+    if time < PRESCALE0:
+        code = min(int(round(time * PRESCALE0_CLK, 0)), MAXCODE)
+        code = code << 1
+    elif time < PRESCALE1:
+        code = min(int(round(time * PRESCALE1_CLK, 0)), MAXCODE)
+        code = PRESCALE_CODE | (code << 1)
+    else:
+        put_error('time should be less than 0.48')
+        return FAILURE
+
+    util.put_code(code << 8)
+    return SUCCESS
+
+
+# Format: ramp <time>, <pwm>
+#         ramp <variable1>, <prescale>, <variable2>
+#              variable1 = 'ra' or 'rb' or 'rc' or 'rd'
+#              prescale  = 'pre=0' or 'pre=1'
+#              variable2 = '+ra' or '+rb' or '+rc' or '+rd' or
+#                          '-ra' or '-rb' or '-rc' or '-rd'
+def ramp(val):
+    args = val.split(',')
+    if len(args) == 2:        # format : ramp <time>, <pwm>
+        # Remove space and check whether it is digit or not
+        if util.is_float(args[0].split()[0]):
+            time = float(args[0].split()[0])
+            pwm = int(args[1].split()[0])
+
+            if time < 0:
+                put_error('time should be positive value')
+                return FAILURE
+
+            if (pwm < -255 or pwm > 255):
+                put_error('should be -255 <= pwm <= 255')
+                return FAILURE
+
+            if pwm == 0:
+                put_error('should not zero')
+                return FAILURE
+
+            PRESCALE0 = 0.015625
+            PRESCALE1 = 0.5
+            MASTER_FREQ = 32768
+            PRESCALE0_CLK = MASTER_FREQ / 16
+            PRESCALE1_CLK = MASTER_FREQ / 512
+            MAXCODE = 0x1F
+            PRESCALE_CODE = 0x40
+
+            time = time / abs(pwm)
+            if time < PRESCALE0:
+                code = min(int(round(time * PRESCALE0_CLK, 0)), MAXCODE)
+                code = code << 1
+            elif time < PRESCALE1:
+                code = min(int(round(time * PRESCALE1_CLK, 0)), MAXCODE)
+                code = PRESCALE_CODE | (code << 1)
+            else:
+                put_error('time should be less than 0.48')
+                return FAILURE
+
+            NEGATIVE = 0x01
+            if pwm < 0:
+                code = code | NEGATIVE
+
+            util.put_code((code << 8) | abs(pwm))
+            return SUCCESS
+        else:
+            put_error('invalid value')
+            return FAILURE
+    elif len(args) == 3:        # format : ramp <var1>, <prescale>, <var2>
+        # Step time
+        var1 = args[0].split()[0]
+        variables = {'ra': 0x0, 'rb': 0x04, 'rc': 0x08, 'rd': 0x0C}
+        step_time = variables.get(var1)
+        if step_time is None:
+            put_error('%s is invalid argument' % var1)
+            return FAILURE
+
+        # Prescale
+        if args[1].split()[0] == 'pre=0':
+            prescale = 0 << 4
+        elif args[1].split()[0] == 'pre=1':
+            prescale = 1 << 4
+        else:
+            put_error('%s is invalid argument' % prescale)
+            return FAILURE
+
+        # Sign bit and number of increments
+        var2 = args[2].split()[0]
+        variables = {'+ra': 0x00, '+rb': 0x01, '+rc': 0x02, '+rd': 0x03,
+                     '-ra': 0x10, '-rb': 0x11, '-rc': 0x12, '-rd': 0x13}
+        sign_and_inc = variables.get(var2)
+        if sign_and_inc is None:
+            put_error('%s is invalid argument' % var2)
+            return FAILURE
+
+        RAMP = 0x8400
+        util.put_code(RAMP | prescale | sign_and_inc | step_time)
+        return SUCCESS
+
+    put_error('invalid arguments')
+    return FAILURE
+
+
+# Convert ds argument to offset
+def get_offset_size(val):
+    args = val.split()
+    if len(args) != 1:
+        return -1
+    return util.format_number(args[0])
+
+
+def ds(val):
+    size = get_offset_size(val)
+    if size < 0:
+        put_error('must have one argument')
+        return FAILURE
+
+    for i in range(0, size):
+        util.put_code(0x0000)
+    return SUCCESS
+
+
+def dw(val):
+    args = val.split()
+    if len(args) != 1:
+        put_error('must have one argument')
+        return FAILURE
+    code = util.format_number(args[0])
+    util.put_code(code)
+    return SUCCESS
+
+
+# Format: branch <number>, label
+#         branch 'ra' or 'rb' or 'rc' or 'rd', label
+def branch(val):
+    args = val.split(',')
+    if len(args) != 2:
+        put_error('must have two arguments')
+        return FAILURE
+
+    # Remove space and check whether it is digit or not
+    if args[0].split()[0].isdigit():
+        loop_count = int(args[0].split()[0])
+        if (loop_count < 0 or loop_count > 64):
+            put_error('0 <= loop count <= 63')
+            return FAILURE
+
+        label = args[1].split()[0]
+        step_no = util.get_offset_address_of_label(label)
+        if step_no == util.INVALID_ADDR:
+            return FAILURE
+
+        BRANCH = 0xA000
+        word = (BRANCH | (loop_count << 7) | (step_no & 0x7F))
+    else:
+        variables = {'ra': 0, 'rb': 1, 'rc': 2, 'rd': 3}
+        index_var = variables.get(args[0].split()[0])
+        if index_var is None:
+            put_error('%s is invalid argument' % args[0].split()[0])
+            return FAILURE
+
+        label = args[1].split()[0]
+        step_no = util.get_offset_address_of_label(label)
+        if step_no == util.INVALID_ADDR:
+            return FAILURE
+
+        BRANCH = 0x8600
+        word = (BRANCH | (step_no << 2) | index_var)
+
+    util.put_code(word)
+    return SUCCESS
+
+
+def reset(val):
+    util.put_code(0x0000)
+    return SUCCESS
+
+
+def interrupt(val):
+    util.put_code(0xC400)
+    return SUCCESS
+
+
+def end(val):
+    args = val.split()
+    if len(args) == 0:    # format: 'end'
+        util.put_code(0xC000)
+        return SUCCESS
+
+    args = args[0].split(',')
+    num_args = len(args)
+    if num_args == 1:        # format: 'end i' or 'end r'
+        arg = args[0]
+        if arg == 'i':
+            util.put_code(0xD000)
+            return SUCCESS
+        elif arg == 'r':
+            util.put_code(0xC800)
+            return SUCCESS
+    elif num_args == 2:        # format: 'end i,r' or 'end r,i'
+        arg1 = args[0]
+        arg2 = args[1]
+        if (arg1 == 'i' and arg2 == 'r') or (arg1 == 'r' and arg2 == 'i'):
+            util.put_code(0xD800)
+            return SUCCESS
+
+    put_error('invalid argument')
+    return FAILURE
+
+
+# Generate trigger code
+def get_trigger_code(val):
+    code = 0
+    if (val.find('1') > 0):
+        code |= 0x01
+    if (val.find('2') > 0):
+        code |= 0x02
+    if (val.find('3') > 0):
+        code |= 0x04
+    if (val.find('e') > 0):
+        code |= 0x20
+
+    return code
+
+
+# Format: trigger w{source1 | source 2 .. }
+#         trigger s{source1 | source 2 .. }
+#         trigger w{source1 | source 2 .. }, s{source1 | source 2 .. }
+#         trigger s{source1 | source 2 .. }, w{source1 | source 2 .. }
+#         trigger w{source1 | source 2 .. } s{source1 | source 2 .. }
+#         trigger s{source1 | source 2 .. } w{source1 | source 2 .. }
+def trigger(val):
+    WAIT = 'w{'
+    START = 's{'
+    w_code = 0
+    s_code = 0
+
+    idx_wait = val.find(WAIT)
+    if idx_wait > 0:
+        w_code = get_trigger_code(val[idx_wait:])
+
+    idx_start = val.find(START)
+    if idx_start > 0:
+        s_code = get_trigger_code(val[idx_start:])
+
+    if idx_wait < 0 and idx_start < 0:
+        put_error('invalid argument')
+        return FAILURE
+
+    TRIGGER = 0xE000
+    util.put_code(TRIGGER | (w_code << 7) | (s_code << 1))
+    return SUCCESS
+
+
+# Common function for jne, jl, jge and je
+# Format: <command> var1, var2, label(address)
+def jump(val, action):
+    args = val.split(',')
+    if len(args) != 3:
+        put_error('must have three arguments')
+        return FAILURE
+
+    # Check the 1st and 2nd argument are variable or not
+    var1 = args[0].split()[0]
+    var2 = args[1].split()[0]
+    variables = {'ra': 0, 'rb': 1, 'rc': 2, 'rd': 3}
+    index_var1 = variables.get(var1)
+    if index_var1 is None:
+        put_error('%s is invalid argument' % var1)
+        return FAILURE
+
+    index_var2 = variables.get(var2)
+    if index_var2 is None:
+        put_error('%s is invalid argument' % var2)
+        return FAILURE
+
+    # Check the 3rd is label or not
+    var3 = args[2].split()[0]
+    addr = util.get_address_of_label(var3, "")
+    if addr == util.INVALID_ADDR:
+        return FAILURE
+
+    cmds = {'jne': 0x8800, 'jl': 0x8A00, 'jge': 0x8C00, 'je': 0x8E00}
+    code = cmds.get(action)
+    if code is None:
+        put_error('%s is invalid argument' % code)
+        return FAILURE
+
+    offset = abs(addr - util.get_sram_address()) - 1
+    word = (code | (offset << 4) | (index_var1 << 2) | index_var2)
+    util.put_code(word)
+    return SUCCESS
+
+
+def jne(val):
+    return jump(val, 'jne')
+
+
+def jl(val):
+    return jump(val, 'jl')
+
+
+def jge(val):
+    return jump(val, 'jge')
+
+
+def je(val):
+    return jump(val, 'je')
+
+
+def ld(val):
+    args = val.split(',')
+    if len(args) != 2:
+        put_error('must have two arguments')
+        return FAILURE
+
+    # Check the 1st argument is variable or not
+    var = args[0].split()[0]
+    target = {'ra': 0, 'rb': 1, 'rc': 2}
+    index = target.get(var)
+    if index is None:
+        put_error('%s is invalid argument' % var)
+        return FAILURE
+
+    # Check the 2nd argument is valid or not
+    val = util.format_number(args[1].split()[0])
+    if val < 0 or val > 255:
+        put_error('0 <= value <=255')
+        return FAILURE
+
+    LD = 0x9000
+    word = (LD | (index << 10) | val)
+    util.put_code(word)
+    return SUCCESS
+
+
+# Common function for 'add' and 'sub'
+# Two formats supported
+#   <command> var, value
+#   <command> var1, var2, var3
+def add_or_sub(val, action):
+    args = val.split(',')
+    if len(args) == 2:          # format: add var, value
+        # Check the 1st argument is variable or not
+        var = args[0].split()[0]
+        target = {'ra': 0, 'rb': 1, 'rc': 2}
+        index = target.get(var)
+        if index is None:
+            put_error('%s is invalid argument' % var)
+            return FAILURE
+
+        # Check the 2nd argument is valid or not
+        val = util.format_number(args[1].split()[0])
+        if (val < 0 or val > 255):
+            put_error('0 <= value <=255')
+            return FAILURE
+
+        cmds = {'add': 0x9100, 'sub': 0x9200}
+        code = cmds.get(action)
+        if code is None:
+            put_error('%s is invalid argument' % code)
+            return FAILURE
+
+        word = (code | (index << 10) | val)
+        util.put_code(word)
+    elif len(args) == 3:        # format: add var1, var2, var3
+        # Check the 1st argument is variable or not
+        var1 = args[0].split()[0]
+        target = {'ra': 0, 'rb': 1, 'rc': 2}
+        index_var1 = target.get(var1)
+        if index_var1 is None:
+            put_error('%s is invalid argument' % var1)
+            return FAILURE
+
+        # Check the others are variable or not
+        var2 = args[1].split()[0]
+        var3 = args[2].split()[0]
+        others = {'ra': 0, 'rb': 1, 'rc': 2, 'rd': 3}
+        index_var2 = others.get(var2)
+        if index_var2 is None:
+            put_error('%s is invalid argument' % var2)
+            return FAILURE
+        index_var3 = others.get(var3)
+        if index_var3 is None:
+            put_error('%s is invalid argument' % var3)
+            return FAILURE
+
+        cmds = {'add': 0x9300, 'sub': 0x9310}
+        code = cmds.get(action)
+        if code is None:
+            put_error('%s is invalid argument' % code)
+            return FAILURE
+
+        word = (code | (index_var1 << 10) | (index_var2 << 2) | index_var3)
+        util.put_code(word)
+    else:
+        put_error('invalid arguments')
+        return FAILURE
+
+    return SUCCESS
+
+
+def add(val):
+    return add_or_sub(val, "add")
+
+
+def sub(val):
+    return add_or_sub(val, "sub")
+
+
+# Command map
+# Format: { 'command':function }
+# If 'command' is found, then function is called by lookup_command() in case
+# the action is 'generate_code'
+commands = {
+            '.segment': segment,
+            'mux_map_start': mux_map_start,
+            'mux_map_next': mux_map_next,
+            'mux_map_prev': mux_map_prev,
+            'mux_ld_next': mux_ld_next,
+            'mux_ld_prev': mux_ld_prev,
+            'mux_ld_addr': mux_ld_addr,
+            'mux_map_addr': mux_map_addr,
+            'mux_sel': mux_sel,
+            'mux_clr': mux_clr,
+            'mux_ld_start': mux_ld_start,
+            'mux_ld_end': mux_ld_end,
+            'set_pwm': set_pwm,
+            'wait': wait,
+            'ramp': ramp,
+            'ds': ds,
+            'dw': dw,
+            'branch': branch,
+            'rst': reset,
+            'int': interrupt,
+            'end': end,
+            'trigger': trigger,
+            'jne': jne,
+            'jl': jl,
+            'jge': jge,
+            'je': je,
+            'ld': ld,
+            'add': add,
+            'sub': sub,
+            }
+
+
+# Command Parser
+#
+# Format: <command> <argument1> .. <argumentsN>
+# To ignore spaces, split() is used
+def lookup_command(cmd, action):
+    # Skip checking if the command is space character
+    if len(cmd.split()) == 0:
+        return SUCCESS
+
+    for key, func in commands.iteritems():
+        if cmd.split()[0] == key:
+            # Two actions are allowed when the command is matched
+            # - create_label: just increase SRAM address for next use
+            # - generate_code: code is generated by each function call
+            if action == 'create_label':
+                # .segment command does not require SRAM address increment
+                if key == '.segment':
+                    return SUCCESS
+
+                # ds command needs to update SRAM size offset
+                if key == 'ds':
+                    size = get_offset_size(cmd.split(key)[1])
+                    if size < 0:
+                        put_error("wrong size information")
+                        return FAILURE
+                    for x in range(0, size):
+                        util.inc_sram_address()
+                    return SUCCESS
+
+                # Other commands, just increase SRAM address
+                util.inc_sram_address()
+                return SUCCESS
+            elif action == 'generate_code':
+                arg = cmd.split(key)[1]
+                return func(arg)
+
+    put_error("%s command is not found" % cmd)
+    return CMD_NOT_FOUND
+
+
+# Line format
+#
+# COMMAND COMMAND ARGUMENTS(S)
+# LABEL:    COMMAND ARGUMENT(S)
+def parse_line(string, action):
+    LABEL = ":"
+    index = string.find(LABEL)
+    if index > 0:    # LABEL format
+        if action == 'create_label':
+            util.add_label_list(string[:index])
+        ret = lookup_command(string[index + 1:], action)
+    else:              # COMMAND format
+        ret = lookup_command(string, action)
+
+    return ret
diff --git a/_lasm_utils.py b/_lasm_utils.py
new file mode 100644 (file)
index 0000000..7f3e54c
--- /dev/null
@@ -0,0 +1,294 @@
+#
+# Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions an
+# limitations under the License.
+#
+#
+# Utility functions for LASM
+#
+#   Common functions used in main entry and command parser
+#
+
+import inspect
+
+segment_addr = 0
+sram_addr = 0
+MAX_SRAM_SIZE = 96
+
+segments = {}        # { "segment":address}
+labels = {}          # { "key":address }
+errors = {}          # { command: error }
+codes = []           # output program code
+INVALID_ADDR = 0xFF
+
+LF = '\n'
+CRLF = '\r\n'
+
+
+#
+# Error handling functions
+#
+
+# Make error format
+def put_error(command, message):
+    errors.update({command: message})
+
+
+# Print out errors
+def report_error(ln, line):
+    for cmd, msg in errors.iteritems():
+        print """
+        Error! Line number: %d
+        %s
+        %s %s
+        """ % (ln, line, cmd, msg)
+
+
+#
+# Segment functions
+#
+
+# Initialize segment list
+def init_segment():
+    global segment_addr, segments
+    segment_addr = 0
+    segments = {}
+
+
+# Add segment structure
+# Format: { segment name:address }
+def add_segment_list(name):
+    global segment_addr
+    segment_addr = get_sram_address()
+    segments.update({name: segment_addr})
+
+
+# Return the address of the latest segment
+def get_segment_address():
+    global segment_addr
+    return segment_addr
+
+
+#
+# SRAM functions
+#
+
+# Initialize SRAM
+def init_sram_address():
+    global sram_addr
+    sram_addr = 0
+
+
+# Increase SRAM address
+def inc_sram_address():
+    global sram_addr
+    sram_addr += 1
+
+
+# Get SRAM address
+def get_sram_address():
+    global sram_addr
+    return sram_addr
+
+
+#
+# Program engine code functions
+#
+
+# Initialize code
+def init_code():
+    global codes
+    codes = []
+
+
+# Put word bytes
+def put_code(word):
+    codes.append((word & 0xFF00) >> 8)
+    codes.append(word & 0x00FF)
+    inc_sram_address()
+
+
+#
+# Label functions
+#
+
+# Initialize label list
+def init_label_list():
+    global labels
+    labels = {}
+
+
+# Add label information
+# Format: { label name:address }
+def add_label_list(key):
+    addr = get_sram_address()
+    labels.update({key: addr})
+
+
+# Get address of label
+# Invalid address returns if the label is not found
+def get_address_of_label(label, func_name):
+    for key in labels:
+        if label == key:
+            return labels.get(key)
+
+    if not func_name:
+        func_name = get_caller_func_name()
+
+    put_error(func_name, 'can not find %s' % label)
+    return INVALID_ADDR
+
+
+# Calculate offset address
+# Returns (label address - segment address)
+def get_offset_address_of_label(key):
+    addr = get_address_of_label(key, get_caller_func_name())
+    if addr == INVALID_ADDR:
+        return INVALID_ADDR
+
+    offset = addr - get_segment_address()
+    if offset < 0:
+        return INVALID_ADDR
+
+    return offset
+
+
+# Trace the stack to get name of the caller
+def get_caller_func_name():
+    return inspect.stack()[2][3]
+
+
+#
+# Output file generation functions
+#
+
+# Convert value to binary type - fill with zeros
+def bin_format(val, bit):
+    return '{:b}'.format(val).zfill(bit)
+
+
+# Convert value to hex type - fill with zero
+def hex_format(val):
+    return '{:X}'.format(val).zfill(2)
+
+
+# Generate binary file
+# Format:
+#       segment code
+#       sram code (up to 96 * 2 byte)
+def generate_bin(src):
+    fname = src[:-3] + 'bin'
+    f = open(fname, 'w')
+
+    # Update segment code
+    for item in segments:
+        bits = bin_format(segments.get(item), 8)
+        f.write(bits)
+        f.write(LF)
+
+    # Fill the default segment value
+    MAX_SEGMENTS = 3
+    DEFAULT_SEG_VAL = MAX_SRAM_SIZE - 1
+    for x in range(len(segments), MAX_SEGMENTS):
+        bits = bin_format(DEFAULT_SEG_VAL, 8)
+        f.write(bits)
+        f.write(LF)
+
+    # Update word code
+    count = 0
+    sram_size = 0
+    for x in codes:
+        bits = bin_format(x, 8)
+        f.write(bits)
+        count += 1
+        if count % 2 == 0:
+            f.write(LF)
+            sram_size += 1
+
+    # Fill zero for last area
+    for n in range(sram_size, MAX_SRAM_SIZE):
+        f.write(bin_format(0, 16))
+        f.write(LF)
+
+    f.close()
+
+
+# Generate hex file
+# Format:
+#       sram code (up to 96 * 2 byte)
+#       @ <segment address> <segment name>
+def generate_hex(src):
+    fname = src[:-3] + 'hex'
+    f = open(fname, 'w')
+
+    # Update byte code
+    count = 0
+    sram_size = 0
+    for x in codes:
+        f.write(hex_format(x))
+        f.write(' ')
+        count += 1
+        if count % 2 == 0:
+            sram_size += 1
+        if count % 16 == 0:
+            f.write(CRLF)
+
+    # Fill zero for last area
+    for n in range(sram_size, MAX_SRAM_SIZE):
+        for a in range(0, 2):        # make two bytes
+            f.write(hex_format(0))
+            f.write(' ')
+            count += 1
+            if count % 16 == 0:
+                f.write(CRLF)
+
+    # Update segment code
+    for item in segments:
+        code = hex_format(segments.get(item))
+        form = "@ %s %s" % (code, item)
+        f.write(form)
+        f.write(CRLF)
+
+    f.close()
+
+
+#
+# Other utils
+#
+
+# Get number type by reading suffix
+def number_type(val):
+    return val[-1]
+
+
+# Check the value is float type or not
+def is_float(val):
+    try:
+        float(val)
+        return True
+    except ValueError:
+        return False
+
+
+# XXXXb or XXXXB is binary
+# XXXXh or XXXXH is hex
+# XXXX is decimal
+def format_number(val):
+    t = number_type(val)
+    if t == "b" or t == "B":        # binary
+        code = int(val[:-1], 2)
+    elif t == "h" or t == "H":      # hex
+        code = int(val[:-1], 16)
+    else:                           # decimal
+        code = int(val, 10)
+    return code
\ No newline at end of file
diff --git a/lasm.py b/lasm.py
new file mode 100644 (file)
index 0000000..94adfb3
--- /dev/null
+++ b/lasm.py
@@ -0,0 +1,123 @@
+#
+# Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions an
+# limitations under the License.
+#
+#
+# LASM (LP5523 Assembler) Main Entry
+#
+#   LASM is used for generating program code which runs LP5523 chip
+#
+#   (input)             (output)
+#   *.src -> lasm.py -> *.bin and *.hex
+#
+
+import os
+import sys
+import _lasm_cmd_parser as cp
+import _lasm_utils as util
+
+NUM_ARGS = 3
+COMMENT = ";"
+
+
+# Check whether the line starts with comment or not
+def is_comment(line):
+    if line[0] == COMMENT:
+        return True
+    else:
+        return False
+
+
+# Get line string by removing comment
+def readline_without_comment(line):
+    index = line.find(COMMENT)
+    if index > 0:
+        line = line[:index]
+
+    return line
+
+
+def error(argv):
+    print """
+    Argument error: %s
+    Usage: python lasm.py -f <src file name>
+           python lasm.py -d <directory name>
+    """ % str(argv)
+
+
+# Initialize data structures
+def init_data():
+    util.init_label_list()
+    util.init_segment()
+    util.init_code()
+
+
+# Parse *.src file by each line
+def process_from_file(src):
+    init_data()
+    # Add label info first and then generate code
+    for action in ['create_label', 'generate_code']:
+        util.init_sram_address()
+        ln = 0     # line number
+
+        # Read line
+        for line in open(src):
+            ln += 1
+            if is_comment(line):
+                continue
+            line = readline_without_comment(line)
+            err = cp.parse_line(line, action)
+            if err < 0:
+                util.report_error(ln, line)
+                break
+
+        if err < 0:
+            return False
+
+    # Generate output files
+    util.generate_bin(src)
+    util.generate_hex(src)
+    return True
+
+
+# Parse all .src files under a directory
+def process_from_directory(dir):
+    for f in os.listdir(dir):
+        fullpath = dir + f
+        if os.path.isfile(fullpath) and f[-3:] == 'src':
+            err = process_from_file(fullpath)
+            if err < 0:
+                return False
+
+    return True
+
+
+def main():
+    if len(sys.argv) != NUM_ARGS:
+        error(sys.argv)
+        exit()
+
+    flag = sys.argv[1]
+    if flag == '-f':            # file
+        if process_from_file(sys.argv[2]) is False:
+            exit()
+    elif flag == '-d':          # directory
+        if process_from_directory(sys.argv[2]) is False:
+            exit()
+    else:
+        error(sys.argv)
+        exit()
+
+if __name__ == "__main__":
+    main()