aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-28 15:22:11 +0000
committers-ol <s-ol@users.noreply.github.com>2020-05-28 15:22:11 +0000
commit51091f7355a89aab6347523f3e3e53ed328b4dfc (patch)
treec3a11ca809ba075a8e2b37c217c6ed6278905ff2
parenttests, cleanup, verify.py (diff)
downloadsubv-51091f7355a89aab6347523f3e3e53ed328b4dfc.tar.gz
subv-51091f7355a89aab6347523f3e3e53ed328b4dfc.zip
survey, pack
-rw-r--r--README.md10
-rw-r--r--format.py254
-rw-r--r--pack.py74
-rw-r--r--riscv.py4
-rw-r--r--subx.py19
-rw-r--r--survey.py137
-rw-r--r--test.subv24
-rw-r--r--verify.py186
8 files changed, 516 insertions, 192 deletions
diff --git a/README.md b/README.md
index 19f3496..b3d718b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ SubV
This is a wip clone of [SubX][mu] for the RISC-V RV31I base ISA.
- $ ./test.py | ./elf.py > out.elf
+ $ <label_test.subv ./format.py | ./survey.py | ./pack.py | ./elf.py > out.elf
$ ./qemu.sh out.elf
Pipeline
@@ -11,8 +11,12 @@ Pipeline
back to front:
-- `elf`: takes `hex.subx`-style input, outputs an ELF file
-- `survey`:
+- `elf.py`: takes hex bytes and segment headers, outputs an ELF file
+- `pack.py`: packs bitfields (`3/3 1/1 f/4`) into hex bytes (`fb`)
+- `survey.py`: replaces label references by their addresses
+- `format.py`: checks op-arguments and chops and orders arguments into ISA formats
+
+and now back to front with a little example:
Debugging
---------
diff --git a/format.py b/format.py
new file mode 100644
index 0000000..b63a854
--- /dev/null
+++ b/format.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python3
+from subx import clean, classify, parse_segment, parse_instr, is_lref, untag, format_instr
+from riscv import u, i, bit_concat, bit_slice
+
+instr_map = {
+ 'opr': ('r', 0x33),
+ 'load': ('i', 0x03),
+ 'opi': ('i', 0x13),
+ 'jalr': ('i', 0x67),
+ 'store': ('s', 0x23),
+ 'branch': ('b', 0x63),
+ 'lui': ('u', 0x37),
+ 'auipc': ('u', 0x17),
+ 'jal': ('j', 0x6f),
+}
+
+def pack_u(instr):
+ (op, rd, imm) = instr
+ op = u(untag(op), 7)
+ rd = u(untag(rd, 'rd'), 5)
+ if not is_lref(imm):
+ imm = i(untag(imm, 'imm20'), 20)
+
+ return [op, rd, imm]
+
+def pack_i(instr):
+ (op, sub, rd, rs, imm) = instr
+ op = u(untag(op), 7)
+ sub = u(untag(sub, 'subop'), 3)
+ rd = u(untag(rd, 'rd'), 5)
+ rs = u(untag(rs, 'rs'), 5)
+ if not is_lref(imm):
+ imm = i(untag(imm, 'imm12'), 12)
+
+ return [op, rd, sub, rs, imm]
+
+def pack_s(instr):
+ (op, sub, rs1, rs2, imm) = instr
+ op = u(untag(op), 7)
+ sub = u(untag(sub, 'subop'), 3)
+ rs1 = u(untag(rs1, 'rs'), 5)
+ rs2 = u(untag(rs2, 'rs'), 5)
+ if not is_lref(imm):
+ imm = i(untag(imm, 'off12'), 12)
+ imm_lo = bit_slice(imm, 4, 0)
+ imm_hi = bit_slice(imm, 11, 5)
+ else:
+ label = untag(imm, 'off12')
+ imm_lo = (label, 5, 'off12')
+ imm_hi = (label, 7, 'off12>>5')
+ return [op, imm_lo, sub, rs1, rs2, imm_hi]
+
+def pack_j(instr):
+ (op, rd, imm) = instr
+ op = u(untag(op), 7)
+ rd = u(untag(rd, 'rd'), 5)
+
+ if not is_lref(imm):
+ imm = i(untag(imm, 'off21'), 21)
+ if imm & 0b1 == 1:
+ raise ArgumentError("J-type offsets have to be halfword-aligned")
+ imm_lo = bit_slice(imm, 10, 1)
+ imm_11 = bit_slice(imm, 11, 11)
+ imm_hi = bit_slice(imm, 19, 12)
+ imm_20 = bit_slice(imm, 20, 20)
+ else:
+ label = untag(imm, 'off21')
+ imm_lo = (label, 10, 'off21>>1')
+ imm_11 = (label, 1, 'off21>>11')
+ imm_hi = (label, 8, 'off21>>12')
+ imm_20 = (label, 1, 'off21>>20')
+
+ return [op, rd, imm_hi, imm_11, imm_lo, imm_20]
+
+def pack_b(instr):
+ (op, sub, rs1, rs2, imm) = instr
+ op = u(untag(op), 7)
+ sub = u(untag(sub, 'subop'), 3)
+ rs1 = u(untag(rs1, 'rs'), 5)
+ rs2 = u(untag(rs2, 'rs'), 5)
+ if not is_lref(imm):
+ imm = i(untag(imm, 'off13'), 13)
+ if imm & 0b1 == 1:
+ raise ArgumentError("B-type offsets have to be halfword-aligned")
+ imm_lo = bit_slice(imm, 4, 1)
+ imm_md = bit_slice(imm, 10, 5)
+ imm_11 = bit_slice(imm, 11, 11)
+ imm_12 = bit_slice(imm, 12, 12)
+ else:
+ label = untag(imm, 'off13')
+ imm_lo = (label, 4, 'off13>>1')
+ imm_md = (label, 6, 'off13>>5')
+ imm_11 = (label, 1, 'off13>>11')
+ imm_12 = (label, 1, 'off13>>12')
+
+ return [op, imm_11, imm_lo, sub, rs1, rs2, imm_md, imm_12]
+
+def format(iter):
+ for line in iter:
+ line = clean(line)
+ if line == '':
+ continue
+
+ type = classify(line)
+
+ if type == 'segment' or type == 'label':
+ yield line
+ else:
+ instr = parse_instr(line)
+ op = instr[0]
+ if len(op) != 2:
+ raise ValueError("instruction without op label")
+
+ (op, label) = op
+ if label not in instr_map:
+ raise ValueError("unknown instruction label: {}".format(label))
+ (format, expected) = instr_map[label]
+ if op != expected:
+ raise ValueError("opcode {} doesn't match label {} (expected {})"
+ .format(op, label, expected))
+
+ if format == 'u':
+ out = pack_u(instr)
+ elif format == 'i':
+ out = pack_i(instr)
+ elif format == 's':
+ out = pack_s(instr)
+ elif format == 'j':
+ out = pack_j(instr)
+ else:
+ raise NotImplementedError()
+
+ yield format_instr(out)
+
+if __name__ == '__main__':
+ import sys
+ for line in format(sys.stdin):
+ print(line)
+
+import unittest
+class TestPackers(unittest.TestCase):
+ def test_pack_u(self):
+ final = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')])
+ self.assertEqual(
+ final,
+ [(0x37, 7), (0x5, 5), (0x10010, 20)]
+ )
+ self.assertEqual(bit_concat(*final)[1], 32)
+
+ label = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')])
+ self.assertEqual(
+ label,
+ [(0x37, 7), (0x5, 5), ('pos', 'imm20')]
+ )
+
+ def test_pack_i(self):
+ final = pack_i([
+ (0x13, 'opi'),
+ (0, 'subop', 'add'),
+ (6, 'rd', 't1'),
+ (0, 'rs', 'x0'),
+ (0x65, 'imm12'),
+ ])
+ self.assertEqual(
+ final,
+ [(0x13, 7), (6, 5), (0, 3), (0, 5), (0x65, 12)]
+ )
+ self.assertEqual(bit_concat(*final)[1], 32)
+
+ def test_pack_s(self):
+ final = pack_s([
+ (0x23, 'store'),
+ (2, 'subop', 'word'),
+ (5, 'rs', 't0'),
+ (6, 'rs', 't1'),
+ (0, 'off12'),
+ ])
+ self.assertEqual(
+ final,
+ [(0x23, 7), (0, 5), (2, 3), (5, 5), (6, 5), (0, 7)]
+ )
+ self.assertEqual(bit_concat(*final)[1], 32)
+
+ label = pack_s([
+ (0x23, 'store'),
+ (2, 'subop', 'word'),
+ (5, 'rs', 't0'),
+ (6, 'rs', 't1'),
+ ('home', 'off12'),
+ ])
+ self.assertEqual(label, [
+ (0x23, 7),
+ ('home', 5, 'off12'),
+ (2, 3),
+ (5, 5),
+ (6, 5),
+ ('home', 7, 'off12>>5'),
+ ])
+
+class TestE2E(unittest.TestCase):
+ def test_e2e(self):
+ from io import StringIO
+ from textwrap import dedent
+ inp = dedent('''\
+ == code 0x80000000
+ main:
+ # load 0x10010000 (UART0) into t0
+ 37/lui 5/rd/t0 0x10010/imm20
+ # store 0x48 (H) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 48/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x65 (e) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 65/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6c (l) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6c (l) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6f (o) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6f/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x0a (\\n) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # jump back up to the top
+ 6f/jal 0/rd/x0 main/off21
+ ''')
+
+ out = dedent('''\
+ == code 0x80000000
+ main:
+ 37/7 05/5 10010/20
+ 13/7 06/5 00/3 00/5 48/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 65/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6f/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 0a/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20
+ ''')
+
+ got = ''
+ for line in format(StringIO(inp)):
+ got += line + '\n'
+
+ self.assertEqual(got, out)
diff --git a/pack.py b/pack.py
new file mode 100644
index 0000000..aba29a3
--- /dev/null
+++ b/pack.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+from riscv import byteify, bit_concat
+from subx import clean, classify, parse_bits, format_instr
+
+def pack(iter):
+ for line in iter:
+ line = clean(line)
+ if line == '':
+ continue
+
+ type = classify(line)
+
+ if type == 'segment' or type == 'label':
+ yield line
+ else:
+ fields = parse_bits(line)
+ total = bit_concat(*fields)
+
+ if total[1] != 32:
+ raise ValueError("instruction parts do not add up to 32 bit!")
+
+ yield format_instr(byteify(total))
+
+if __name__ == '__main__':
+ import sys
+ for line in pack(sys.stdin):
+ print(line)
+
+import unittest
+class TestE2E(unittest.TestCase):
+ def test_e2e(self):
+ from io import StringIO
+ from textwrap import dedent
+ inp = dedent('''\
+ == code 0x80000000
+ 37/7 05/5 10010/20
+ 13/7 06/5 00/3 00/5 48/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 65/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6f/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 0a/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 6f/7 00/5 ff/8 01/1 3e6/10 1/1
+ ''')
+
+ out = dedent('''\
+ == code 0x80000000
+ b7 02 01 10
+ 13 03 80 04
+ 23 a0 62 00
+ 13 03 50 06
+ 23 a0 62 00
+ 13 03 c0 06
+ 23 a0 62 00
+ 13 03 c0 06
+ 23 a0 62 00
+ 13 03 f0 06
+ 23 a0 62 00
+ 13 03 a0 00
+ 23 a0 62 00
+ 6f f0 df fc
+ ''')
+
+ got = ''
+ for line in pack(StringIO(inp)):
+ got += line + '\n'
+
+ self.assertEqual(got, out)
diff --git a/riscv.py b/riscv.py
index 245d60d..238a06e 100644
--- a/riscv.py
+++ b/riscv.py
@@ -160,6 +160,10 @@ class TestHelpers(unittest.TestCase):
byteify((0x12345678, 32)),
[(0x78,), (0x56,), (0x34,), (0x12,)]
)
+ self.assertEqual(
+ byteify((0x4801813, 32)),
+ [(0x13,), (0x18,), (0x80,), (0x04,)]
+ )
class TestFormats(unittest.TestCase):
def test_format_u(self):
diff --git a/subx.py b/subx.py
index ed3b184..3266403 100644
--- a/subx.py
+++ b/subx.py
@@ -13,16 +13,29 @@ def parse_instr(line):
parts = [parse_part(part) for part in parts if part != '']
return parts
+def parse_bits(line):
+ parts = []
+ for part in white.split(line):
+ if part == '':
+ continue
+
+ val, size = parse_part(part)
+ parts.append((val, int(size)))
+ return parts
+
def parse_segment(line):
parts = white.split(line)
return (parts[1], int(parts[2], 16))
+def parse_label(line):
+ return line[:-1]
+
def is_lref(part):
return isinstance(part[0], str)
-def unlabel(part, expect=None):
+def untag(part, expect=None):
if expect and part[1] != expect:
- raise ValueError("expected {} to be labelled {}", part, expect)
+ raise ValueError("expected {} to be labelled {}".format(part, expect))
return part[0]
def format_part(part):
@@ -38,7 +51,7 @@ def format_instr(inst, comment=None):
return packed
def clean(line):
- return line.strip().split('#')[0]
+ return line.strip().split('#')[0].rstrip()
def classify(line):
if line.startswith('=='): # segment
diff --git a/survey.py b/survey.py
new file mode 100644
index 0000000..51f1136
--- /dev/null
+++ b/survey.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+from subx import clean, classify, parse_segment, parse_label, parse_instr, format_instr, is_lref
+from riscv import u, i, bit_concat, bit_slice
+import re
+
+def scan(iter):
+ addr = None
+ map = {}
+ for line in iter:
+ if line['type'] == 'label':
+ map[line['label']] = line['addr']
+ return map
+
+tag_re = re.compile(r'^(imm|off)(\d+)(?:>>(\d+))?$')
+def observe(part, rel_addr, map):
+ if is_lref(part):
+ label, trim, tag = part
+ trim = int(trim)
+
+ if label not in map:
+ raise ValueError("label not found: {} ({})".format(label, part))
+ addr = map[label]
+
+ mode, size, shift = tag_re.match(tag).groups()
+ size = int(size)
+ shift = int(shift)
+
+ if mode == 'imm':
+ addr = u(addr, size)
+ else:
+ addr = i(addr - rel_addr, size)
+
+ part = bit_slice(addr, trim+shift-1, shift)
+
+ return part
+
+def survey(iter):
+ # buffer in everything first, we need two passes
+ queue = []
+ addr = -1
+ for line in iter:
+ # parse the line
+ line = clean(line)
+ if line == '':
+ continue
+
+ type = classify(line)
+ if type == 'segment':
+ parsed = parse_segment(line)
+ elif type == 'label':
+ parsed = parse_label(line)
+ elif type == 'instr':
+ parsed = parse_instr(line)
+
+ queue.append({
+ 'type': type,
+ 'raw': line,
+ 'addr': addr,
+ type: parsed,
+ })
+
+ # step forward addr
+ if type == 'segment':
+ segment, addr = parsed
+ elif type == 'instr':
+ bits = 0
+ for part in parsed:
+ bits += int(part[1])
+
+ if bits % 8 != 0:
+ raise ValueError("SubV lines should be byte aligned!")
+
+ addr += bits // 8
+
+ map = scan(queue)
+ for line in queue:
+ if line['type'] == 'instr':
+ instr = []
+ for part in line['instr']:
+ observed = observe(part, line['addr'], map)
+ instr.append(observed)
+ yield format_instr(instr)
+ elif line['type'] == 'segment':
+ yield line['raw']
+
+if __name__ == '__main__':
+ import sys
+ for line in survey(sys.stdin):
+ print(line)
+
+import unittest
+class TestE2E(unittest.TestCase):
+ def test_e2e(self):
+ from io import StringIO
+ from textwrap import dedent
+ inp = dedent('''\
+ == code 0x80000000
+ main:
+ 37/7 05/5 10010/20
+ 13/7 06/5 00/3 00/5 48/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 65/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6f/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 0a/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20
+ ''')
+
+ out = dedent('''\
+ == code 0x80000000
+ 37/7 05/5 10010/20
+ 13/7 06/5 00/3 00/5 48/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 65/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6c/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 6f/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 13/7 06/5 00/3 00/5 0a/12
+ 23/7 00/5 02/3 05/5 06/5 00/7
+ 6f/7 00/5 ff/8 01/1 3e6/10 01/1
+ ''')
+
+ got = ''
+ for line in survey(StringIO(inp)):
+ got += line + '\n'
+
+ self.assertEqual(got, out)
diff --git a/test.subv b/test.subv
new file mode 100644
index 0000000..f1c73fd
--- /dev/null
+++ b/test.subv
@@ -0,0 +1,24 @@
+== code 0x80000000
+main:
+ # load 0x10010000 (UART0) into t0
+ 37/lui 5/rd/t0 0x10010/imm20
+ # store 0x48 (H) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 48/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x65 (e) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 65/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6c (l) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6c (l) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x6f (o) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6f/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # store 0x0a (\n) in UART0+0
+ 13/opi 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12
+ 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/off12
+ # jump back up to the top
+ 6f/jal 0/rd/x0 main/off21
diff --git a/verify.py b/verify.py
deleted file mode 100644
index 8499da5..0000000
--- a/verify.py
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/env python3
-from subx import clean, classify, parse_segment, parse_instr, is_lref, unlabel, format_instr
-from riscv import u, i, bit_concat
-
-"""
-"""
-
-instr_map = {
- 'opr': ('r', 0x33),
- 'load': ('i', 0x03),
- 'opi': ('i', 0x13),
- 'jalr': ('i', 0x67),
- 'store': ('s', 0x23),
- 'branch': ('b', 0x63),
- 'lui': ('u', 0x37),
- 'auipc': ('u', 0x17),
- 'jal': ('j', 0x6f),
-}
-
-def pack_u(instr):
- (op, rd, imm) = instr
- op = u(unlabel(op), 7)
- rd = u(unlabel(rd, 'rd'), 5)
- if not is_lref(imm):
- imm = i(unlabel(imm, 'imm20'), 20)
-
- return [op, rd, imm]
-
-def pack_i(instr):
- (op, sub, rd, rs, imm) = instr
- op = u(unlabel(op), 7)
- sub = u(unlabel(sub, 'subop'), 3)
- rd = u(unlabel(rd, 'rd'), 5)
- rs = u(unlabel(rs, 'rs'), 5)
- if not is_lref(imm):
- imm = i(unlabel(imm, 'imm12'), 12)
-
- return [op, sub, rd, rs, imm]
-
-def pack_s(instr):
- (op, sub, rs1, rs2, imm) = instr
- op = u(unlabel(op), 7)
- sub = u(unlabel(sub, 'subop'), 3)
- rs1 = u(unlabel(rs1, 'rs'), 5)
- rs2 = u(unlabel(rs2, 'rs'), 5)
- if not is_lref(imm):
- imm = i(unlabel(imm, 'disp12'), 12)
-
- return [op, sub, rs1, rs2, imm]
-
-def pack_j(instr):
- (op, rd, imm) = instr
- op = u(unlabel(op), 7)
- rd = u(unlabel(rd, 'rd'), 5)
- if not is_lref(imm):
- imm = i(unlabel(imm, 'disp20u'), 20)
-
- return [op, rd, imm]
-
-def pack(iter):
- for line in iter:
- line = clean(line)
- if line == '':
- continue
-
- type = classify(line)
-
- if type == 'segment' or type == 'label':
- yield line
- else:
- instr = parse_instr(line)
- op = instr[0]
- if len(op) != 2:
- raise ValueError("instruction without op label")
-
- (op, label) = op
- if label not in instr_map:
- raise ValueError("unknown instruction label: {}".format(label))
- (format, expected) = instr_map[label]
- if op != expected:
- raise ValueError("opcode {} doesn't match label {} (expected {})"
- .format(op, label, expected))
-
- out = None
- if format == 'u':
- out = pack_u(instr)
- elif format == 'i':
- out = pack_i(instr)
- elif format == 's':
- out = pack_s(instr)
- elif format == 'j':
- out = pack_j(instr)
- else:
- raise NotImplementedError()
-
- yield format_instr(out)
-
-if __name__ == '__main__':
- import sys
- pack(sys.stdin)
-
-import unittest
-class TestPackers(unittest.TestCase):
- def test_pack_u(self):
- final = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), (0x10010, 'imm20')])
- self.assertEqual(
- final,
- [(0x37, 7), (0x5, 5), (0x10010, 20)]
- )
- self.assertEqual(bit_concat(*final)[1], 32)
-
- label = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')])
- self.assertEqual(
- label,
- [(0x37, 7), (0x5, 5), ('pos', 'imm20')]
- )
-
- def test_pack_i(self):
- final = pack_i([
- (0x13, 'opi'),
- (0, 'subop', 'add'),
- (6, 'rd', 't1'),
- (0, 'rs', 'x0'),
- (0x65, 'imm12'),
- ])
- self.assertEqual(
- final,
- [(0x13, 7), (0, 3), (6, 5), (0, 5), (0x65, 12)]
- )
- self.assertEqual(bit_concat(*final)[1], 32)
-
-from io import StringIO
-from textwrap import dedent
-class TestE2E(unittest.TestCase):
- def test_e2e(self):
- inv = dedent('''\
- == code 0x80000000
- main:
- # load 0x10010000 (UART0) into t0
- 37/lui 5/rd/t0 0x10010/imm20
- # store 0x48 (H) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 48/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # store 0x65 (e) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 65/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # store 0x6c (l) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # store 0x6c (l) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6c/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # store 0x6f (o) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 6f/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # store 0x0a (\\n) in UART0+0
- 13/opi 0/subop/add 6/rd/t1 0/rs/x0 0a/imm12
- 23/store 2/subop/word 5/rs/t0 6/rs/t1 0/disp12
- # jump back up to the top
- 6f/jal 0/rd/x0 main/disp20u
- ''')
-
- out = dedent('''\
- == code 0x80000000
- main:
- 37/7 05/5 10010/20
- 13/7 00/3 06/5 00/5 48/12
- 23/7 02/3 05/5 06/5 00/12
- 13/7 00/3 06/5 00/5 65/12
- 23/7 02/3 05/5 06/5 00/12
- 13/7 00/3 06/5 00/5 6c/12
- 23/7 02/3 05/5 06/5 00/12
- 13/7 00/3 06/5 00/5 6c/12
- 23/7 02/3 05/5 06/5 00/12
- 13/7 00/3 06/5 00/5 6f/12
- 23/7 02/3 05/5 06/5 00/12
- 13/7 00/3 06/5 00/5 0a/12
- 23/7 02/3 05/5 06/5 00/12
- 6f/7 00/5 main/disp20u
- ''')
-
- got = ''
- for line in pack(StringIO(inv)):
- got += line + '\n'
-
- self.assertEqual(got, out)