git.s-ol.nu subv / master survey.py
master

Tree @master (Download .tar.gz)

survey.py @master

51091f7
18870fe
 
 
 
 
 
 
 
 
218a4f2
18870fe
218a4f2
6368fb5
18870fe
218a4f2
6368fb5
18870fe
218a4f2
6368fb5
18870fe
218a4f2
6368fb5
18870fe
218a4f2
6368fb5
18870fe
218a4f2
6368fb5
18870fe
218a4f2
18870fe
 
848e84b
 
218a4f2
848e84b
218a4f2
6368fb5
848e84b
218a4f2
6368fb5
848e84b
218a4f2
6368fb5
848e84b
218a4f2
6368fb5
848e84b
218a4f2
6368fb5
848e84b
218a4f2
6368fb5
848e84b
ae0e0c0
c2be219
 
 
 
 
 
 
a9192c6
c2be219
218a4f2
 
c2be219
218a4f2
 
c2be219
218a4f2
 
 
 
c2be219
 
 
 
 
218a4f2
c2be219
218a4f2
 
c2be219
218a4f2
 
c2be219
218a4f2
 
ae0e0c0
 
18870fe
d298144
661a792
d298144
12ca589
661a792
18870fe
 
 
218a4f2
 
 
 
 
ae0e0c0
218a4f2
ae0e0c0
218a4f2
ae0e0c0
218a4f2
ae0e0c0
218a4f2
ae0e0c0
218a4f2
ae0e0c0
218a4f2
ae0e0c0
218a4f2
 
18870fe
 
12ca589
218a4f2
 
 
 
18870fe
12ca589
b404af9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18870fe
218a4f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ae0e0c0
51091f7
661a792
0658187
51091f7
 
18870fe
12ca589
f899824
661a792
d298144
51091f7
 
661a792
 
 
 
12ca589
661a792
 
218a4f2
 
 
661a792
 
12ca589
51091f7
12ca589
 
 
661a792
 
 
 
 
 
f899824
 
218a4f2
 
 
 
 
 
f899824
 
51091f7
12ca589
 
661a792
 
12ca589
 
218a4f2
12ca589
 
848e84b
 
 
661a792
12ca589
 
661a792
12ca589
 
51091f7
661a792
 
51091f7
661a792
51091f7
 
#!/usr/bin/env python3
""" survey.py
Resolves label references and substitutes them with literal values.

>>> from io import StringIO
>>> # doctest: +REPORT_NDIFF
... print(subv.join_all(survey(StringIO('''
... == code 0x80000000
... main:
... # load 0x10010000 (UART0) into t0
... 37/u 5/rd 10010/imm20
... # store 0x48 (H) in UART0+0
... 13/i 6/rd 0/funct3 0/rs 48/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # store 0x65 (e) in UART0+0
... 13/i 6/rd 0/funct3 0/rs 65/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # store 0x6c (l) in UART0+0
... 13/i 6/rd 0/funct3 0/rs 6c/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # store 0x6c (l) in UART0+0
... 13/i 6/rd 0/funct3 0/rs 6c/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # store 0x6f (o) in UART0+0
... 13/i 6/rd 0/funct3 0/rs 6f/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # store 0x0a (\\\\n) in UART0+0
... 13/i 6/rd 0/funct3 0/rs a/imm12
... 23/s 2/funct3 5/rs1 0/imm12 6/rs2
... # jump back up to the top
... 6f/j 0/rd main/off21
... '''[1:-1]))))
== code 0x80000000
# main:
# load 0x10010000 (UART0) into t0
37/u 5/rd 10010/imm20
# store 0x48 (H) in UART0+0
13/i 6/rd 0/funct3 0/rs 48/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# store 0x65 (e) in UART0+0
13/i 6/rd 0/funct3 0/rs 65/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# store 0x6c (l) in UART0+0
13/i 6/rd 0/funct3 0/rs 6c/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# store 0x6c (l) in UART0+0
13/i 6/rd 0/funct3 0/rs 6c/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# store 0x6f (o) in UART0+0
13/i 6/rd 0/funct3 0/rs 6f/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# store 0x0a (\\n) in UART0+0
13/i 6/rd 0/funct3 0/rs a/imm12
23/s 2/funct3 5/rs1 0/imm12 6/rs2
# jump back up to the top
6f/j 0/rd -34/imm21

>>> # doctest: +REPORT_NDIFF
... print(subv.join_all(survey(StringIO('''
... == code 0x80000000
... # repeatedly print "Hi\\\\n"
... main:
... # load 0x10010000 (UART0) into t0
... 37/u 5/rd/t0 10010/imm20
... # store 0x48 (H) in UART0+0
... 13/i 0/subop/add 6/rd/t1 0/rs/x0 48/imm12
... 23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
... # store 0x69 (i) in UART0+0
... 13/i 0/subop/add 6/rd/t1 0/rs/x0 69/imm12
... 23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
... # store 0x0a (\\\\n) in UART0+0
... 13/i 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12
... 23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
... 17/u 7/rd/t2 main/off20hi
... 67/i 0/subop 0/rd/x0 7/rs/t2 main+4/off12lo
... '''[1:-1]))))
== code 0x80000000
# repeatedly print "Hi\\n"
# main:
# load 0x10010000 (UART0) into t0
37/u 5/rd/t0 10010/imm20
# store 0x48 (H) in UART0+0
13/i 0/subop/add 6/rd/t1 0/rs/x0 48/imm12
23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
# store 0x69 (i) in UART0+0
13/i 0/subop/add 6/rd/t1 0/rs/x0 69/imm12
23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
# store 0x0a (\\n) in UART0+0
13/i 0/subop/add 6/rd/t1 0/rs/x0 a/imm12
23/s 2/subop/word 5/rs/t0 6/rs/t1 0/off12
17/u 7/rd/t2 0/imm20
67/i 0/subop 0/rd/x0 7/rs/t2 -1c/imm12
"""
import subv


def observe(word, pc, map):
    """resolve a label reference.

    >>> observe((3, '3'), 0, {})
    (3, '3')
    >>> subv.format_part(observe(('label', 'imm32'), 0, {'label': 0x1234}))
    '1234/imm32'
    >>> subv.format_part(observe(('label-2', 'imm20',), 0, {'label': 0x1234}))
    '1232/imm20'
    >>> subv.format_part(observe(('label', 'off32'), 0x1000, {'label': 0x1234}))
    '234/imm32'
    >>> subv.format_part(observe(('label+4', 'off32'), 0x1000, {'label': 0x1234}))
    '238/imm32'
    >>> subv.format_part(observe(('label', 'off32'), 0x1234, {'label': 0x1000}))
    '-234/imm32'
    >>> subv.format_part(observe(('label', 'off20hi'), 0x1000, {'label': 0x3234}))
    '2/imm20'
    >>> subv.format_part(observe(('label', 'off12lo'), 0x1000, {'label': 0x3234}))
    '234/imm12'
    >>> subv.format_part(observe(('label', 'off20hi'), 0x3234, {'label': 0x1000}))
    '-2/imm20'
    >>> subv.format_part(observe(('label', 'off12lo'), 0x3234, {'label': 0x1000}))
    '-234/imm12'

    >>> observe(('label', 'imm32'), 0, {})
    Traceback (most recent call last):
        ...
    ValueError: undefined label 'label'
    >>> observe(('label', 'imm8'), 0, {'label': 0x1234})
    Traceback (most recent call last):
        ...
    ValueError: label reference out of range (8 bit): 'label' (0x1234)
    """
    if not subv.is_reference(word):
        if len(word) != 2:
            return word

        ref = subv.parse_immediate(word[1])
        if not ref or "split" not in ref:
            return word

        value = word[0]

        sgn = -1 if value < 0 else 1
        value *= sgn

        if ref["split"] == "hi" and ref["size"] == 20:
            value = value >> 12
        elif ref["split"] == "lo" and ref["size"] == 12:
            value = value & 0xFFF
        else:
            raise ValueError(
                "unknown label split mode {mode}{size}{split}".format(**ref)
            )

        return (value, "imm{size}".format(**ref))

    ref = subv.parse_reference(word)

    if ref["label"] not in map:
        raise ValueError("undefined label '{}'".format(ref["label"]))

    value = map[ref["label"]] + ref["offset"]

    if ref["mode"] == "off":
        value -= pc

    if "split" in ref:
        sgn = -1 if value < 0 else 1
        value *= sgn

        if ref["split"] == "hi" and ref["size"] == 20:
            value = value >> 12
        elif ref["split"] == "lo" and ref["size"] == 12:
            value = value & 0xFFF
        else:
            raise ValueError(
                "unknown label split mode {mode}{size}{split}".format(**ref)
            )

        value *= sgn

    if value > 0:
        if value >= (1 << ref["size"]):
            raise ValueError(
                "label reference out of range ({size} bit): '{label}' (0x{:x})".format(
                    value, **ref
                )
            )
    else:
        if value < -(1 << (ref["size"] - 1)):
            raise ValueError(
                "label reference out of range ({size} bit): '{label}' (-0x{:x})".format(
                    abs(value), **ref
                )
            )

    return (value, "imm{size}".format(**ref))


@subv.with_parsed_lines
def survey(iter):
    queue = []
    map = {}
    addr, bitcount = -1, 0
    for segment, line in iter:
        line["addr"] = addr
        queue.append(line)

        # step forward addr
        type = line["type"]
        if type == "segment":
            addr, bitcount = line["segment"][1], 0
        elif type == "label":
            assert bitcount == 0, "label isn't byte aligned"
            map[line["label"]] = addr
        elif type == "instr":
            if len(line["instr"]) == 0:
                continue

            if segment == "data":
                for part in line["instr"]:
                    bitcount += int(part[1])

                if bitcount >= 8:
                    addr += bitcount // 8
                    bitcount = bitcount % 8
            elif segment == "code":
                assert (
                    bitcount == 0
                ), "instruction isn't byte aligned ({} bitcount left)".format(
                    8 - bitcount
                )
                assert addr % 2 == 0, "instruction isn't 2-byte aligned"

                op = line["instr"][0]
                instr_size = 0
                if op[1] in "usirjb":
                    addr += 4
                else:
                    raise ValueError("Unknown instruction format '{}'".format(op[1]))
            else:
                raise ValueError("unknown segment '{}'".format(segment))

    for i, line in enumerate(queue, start=1):
        try:
            type = line["type"]
            if type == "instr":
                instr = []
                for part in line[type]:
                    instr.append(observe(part, line["addr"], map))
                line[type] = instr
                yield subv.format(line)
            elif type == "label":
                yield "# {}".format(line["raw"])
            else:
                yield line["raw"]
        except Exception as e:
            msg = "failed to survey line"
            msg = msg + "\n{}:{}:  {}".format("(unknown)", i, subv.format(line))
            msg = msg + "\nparsed as {}".format(subv.dump(line))
            raise subv.SubVException(msg) from e


if __name__ == "__main__":
    import sys

    for line in survey(sys.stdin):
        print(line)