aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-28 16:28:07 +0000
committers-ol <s-ol@users.noreply.github.com>2020-05-28 16:28:07 +0000
commitd298144e6a5d6a0c52338100b73eadf4b33c5f09 (patch)
tree8b595d54677bfdcf58f0ec606f25c154712d6d71
parentAdd ex.* (diff)
downloadsubv-d298144e6a5d6a0c52338100b73eadf4b33c5f09.tar.gz
subv-d298144e6a5d6a0c52338100b73eadf4b33c5f09.zip
cleanup
-rw-r--r--README.md7
-rw-r--r--bits.py80
-rwxr-xr-xelf.py168
-rw-r--r--ex.format6
-rw-r--r--format.py113
-rw-r--r--pack.py76
-rw-r--r--riscv.py203
-rw-r--r--subv.py (renamed from subx.py)61
-rw-r--r--survey.py47
-rwxr-xr-xtest.py22
-rw-r--r--test_riscv.py3
11 files changed, 341 insertions, 445 deletions
diff --git a/README.md b/README.md
index 663f819..c8e931f 100644
--- a/README.md
+++ b/README.md
@@ -46,17 +46,22 @@ and now front to back with a little example:
`ex.format`: arguments sliced and diced and put into the ISA order.
== code 0x80000000
+ # repeatedly print "Hi\n"
main:
+ # load 0x10010000 (UART0) into t0
37/7 05/5 10010/20
+ # store 0x48 (H) in UART0+0
13/7 06/5 00/3 00/5 48/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x69 (i) in UART0+0
13/7 06/5 00/3 00/5 69/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x0a (\n) in UART0+0
13/7 06/5 00/3 00/5 0a/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # jump back up to the top
6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20
-
`ex.survey`: labels resolved, ready to be packed into hex format
== code 0x80000000
diff --git a/bits.py b/bits.py
new file mode 100644
index 0000000..162bcb0
--- /dev/null
+++ b/bits.py
@@ -0,0 +1,80 @@
+def u(num, bits):
+ if num < 0:
+ raise ValueError("negative value not allowed: {}".format(num))
+
+ if num.bit_length() > bits:
+ raise ValueError("value too large for u{} field: {} ({} bits)",
+ bits, num, num.bit_length())
+
+ return (num, bits)
+
+def i(num, bits):
+ if num < 0:
+ num = (1 << bits) + num
+
+ return u(num, bits)
+
+def from_part(part):
+ val, size = part
+ return (val, int(size))
+
+def concat(*parts):
+ val, size = 0, 0
+ for (pval, psize) in parts:
+ val = val | pval << size
+ size += psize
+ return (val, size)
+
+def slice(bits, top, bottom):
+ (val, size) = bits
+
+ if top < bottom:
+ raise ValueError("cant slice reverse range")
+ elif bottom < 0:
+ raise ValueError("negative slice index")
+ elif top >= size:
+ raise ValueError("cant slice [{}:{}] from {} bit value".format(top, bottom, size))
+
+ width = top - bottom + 1
+ val = (val >> bottom) & ((1 << width) - 1)
+ return (val, width)
+
+import unittest
+class TestHelpers(unittest.TestCase):
+ def test_concat(self):
+ self.assertEqual(
+ concat((0b10, 2), (0b00, 2)),
+ (0b0010, 4)
+ )
+ self.assertEqual(
+ concat((0b1, 1), (0b0110, 4), (0b110, 3)),
+ (0b11001101, 8)
+ )
+
+ def test_slice(self):
+ self.assertEqual(
+ slice((0x7f, 8), 7, 4),
+ (0x7, 4)
+ )
+ self.assertEqual(
+ slice((0b100, 3), 2, 2),
+ (0b1, 1)
+ )
+ with self.assertRaises(ValueError):
+ slice((0xf, 4), 4, 0)
+ with self.assertRaises(ValueError):
+ slice((0xf, 4), 2, 3)
+ with self.assertRaises(ValueError):
+ slice((0xf, 4), 3, -1)
+ self.assertEqual(
+ slice((0x12345678, 32), 7, 0),
+ (0x78, 8)
+ )
+ self.assertEqual(
+ slice((0x12345678, 32), 15, 8),
+ (0x56, 8)
+ )
+ self.assertEqual(
+ slice((0x12345678, 32), 23, 16),
+ (0x34, 8)
+ )
diff --git a/elf.py b/elf.py
index 919b690..ae81253 100755
--- a/elf.py
+++ b/elf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
import sys
-from subx import clean, classify, parse_segment, parse_instr
+import subv
"""
Takes a packed & surveyed hex stream (with section headers) and packs it into a
@@ -18,86 +18,92 @@ See full documentation here:
https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf
"""
-segments = []
-segment = None
-for line in sys.stdin:
- line = clean(line)
- type = classify(line)
-
- if type == 'segment':
- (name, addr) = parse_segment(line)
- segment = { 'name': name, 'addr': addr, 'content': [] }
- segments.append(segment)
- elif type == 'instr':
- if segment == None:
- raise ValueError("label or code outside of segment!")
-
- instr = parse_instr(line)
- segment['content'] += instr
- else:
- raise ValueError("elf input should contain only segments and instructions!")
-entrypoint = 0x80000000
-cursor = 0
-
def w(b):
- global cursor
- cursor += len(b)
- sys.stdout.buffer.write(b)
+ sys.stdout.buffer.write(b)
+ return len(b)
def wi(num, size=2):
- global cursor
- cursor += size
- sys.stdout.buffer.write(num.to_bytes(size, byteorder='little'))
-
-def padto(offset):
- missing = offset - cursor
- if missing < 0:
- raise ValueError("already past offset!")
- w(b'\x00' * missing)
-
-def write_elf_header():
- w(b"\x7fELF") # ELF magic
- wi(0x010101, 4) # 32bit, little endian
- wi(0, 8) # reserved
- wi(0x02) # e_type
- wi(0xf3) # e_machine
- wi(0x01, 4) # e_version
- wi(entrypoint, 4) # e_entry
- wi(0x34, 4) # e_phoff (Program Header offset)
- wi(0x00, 4) # e_shoff (Section Header offset, unused)
- wi(0x0004, 4) # e_flags
- wi(0x34) # e_ehsize
- wi(0x20) # e_phentsize
- wi(len(segments)) # e_phnum
- wi(0x28) # e_shentsize
- wi(0x0) # e_shnum
- wi(0x0) # e_shstrndx
-
-def write_program_header(segment):
- align = 0x1000
- offset = (1 + cursor // align) * align
- start = segment['addr']
- size = len(segment['content'])
- segment['offset'] = offset
-
- if offset % align != start % align:
- print("{:02x} {:02x}".format(offset, offset % align))
- print("{:02x} {:02x}".format(start, start % align))
- raise ValueError("improper alignment")
-
- wi(0x1, 4) # p_type
- wi(offset, 4) # p_offset
- wi(start, 4) # p_vaddr
- wi(start, 4) # p_paddr
- wi(size, 4) # p_filesz
- wi(size, 4) # p_memsz
- wi(0x5, 4) # p_flags, 0x5=rx, 0x6=rw
- wi(align, 4) # p_align
-
-write_elf_header()
-for seg in segments:
- write_program_header(seg)
-for seg in segments:
- padto(seg['offset'])
- for part in seg['content']:
- wi(part[0], 1)
+ sys.stdout.buffer.write(num.to_bytes(size, byteorder='little'))
+ return size
+
+def padto(offset, cursor):
+ missing = offset - cursor
+ if missing < 0:
+ raise ValueError("already past offset!")
+ w(b'\x00' * missing)
+ return offset
+
+def write_elf_header(segments):
+ c = 0
+
+ code = next(s for s in segments if s['name'] == 'code')
+ entrypoint = code['addr']
+
+ c += w(b"\x7fELF") # ELF magic
+ c += wi(0x010101, 4) # 32bit, little endian
+ c += wi(0, 8) # reserved
+ c += wi(0x02) # e_type
+ c += wi(0xf3) # e_machine
+ c += wi(0x01, 4) # e_version
+ c += wi(entrypoint, 4) # e_entry
+ c += wi(0x34, 4) # e_phoff (Program Header offset)
+ c += wi(0x00, 4) # e_shoff (Section Header offset, unused)
+ c += wi(0x0004, 4) # e_flags
+ c += wi(0x34) # e_ehsize
+ c += wi(0x20) # e_phentsize
+ c += wi(len(segments)) # e_phnum
+ c += wi(0x28) # e_shentsize
+ c += wi(0x0) # e_shnum
+ c += wi(0x0) # e_shstrndx
+ return c
+
+def write_program_header(segment, c):
+ align = 0x1000
+ offset = (1 + c // align) * align
+ start = segment['addr']
+ size = len(segment['content'])
+ segment['offset'] = offset
+
+ if offset % align != start % align:
+ print("{:02x} {:02x}".format(offset, offset % align))
+ print("{:02x} {:02x}".format(start, start % align))
+ raise ValueError("improper alignment")
+
+ c += wi(0x1, 4) # p_type
+ c += wi(offset, 4) # p_offset
+ c += wi(start, 4) # p_vaddr
+ c += wi(start, 4) # p_paddr
+ c += wi(size, 4) # p_filesz
+ c += wi(size, 4) # p_memsz
+ c += wi(0x5, 4) # p_flags, 0x5=rx, 0x6=rw
+ c += wi(align, 4) # p_align
+ return c
+
+if __name__ == "__main__":
+ segments = []
+ segment = None
+ for line in sys.stdin:
+ line = subv.parse(line)
+
+ if line['type'] == 'segment':
+ (name, addr) = line['segment']
+ segment = { 'name': name, 'addr': addr, 'content': [] }
+ segments.append(segment)
+ elif line['type'] == 'instr':
+ if segment == None:
+ raise ValueError("label or code outside of segment!")
+
+ segment['content'] += line['instr']
+ else:
+ raise ValueError("elf input should contain only segments and instructions!")
+
+ cursor = 0
+
+ c = write_elf_header(segments)
+ for seg in segments:
+ c = write_program_header(seg, c)
+
+ for seg in segments:
+ c = padto(seg['offset'], c)
+ for part in seg['content']:
+ c += wi(part[0], 1)
diff --git a/ex.format b/ex.format
index b0e818e..c9c68b8 100644
--- a/ex.format
+++ b/ex.format
@@ -1,10 +1,16 @@
== code 0x80000000
+# repeatedly print "Hi\n"
main:
+# load 0x10010000 (UART0) into t0
37/7 05/5 10010/20
+# store 0x48 (H) in UART0+0
13/7 06/5 00/3 00/5 48/12
23/7 00/5 02/3 05/5 06/5 00/7
+# store 0x69 (i) in UART0+0
13/7 06/5 00/3 00/5 69/12
23/7 00/5 02/3 05/5 06/5 00/7
+# store 0x0a (\n) in UART0+0
13/7 06/5 00/3 00/5 0a/12
23/7 00/5 02/3 05/5 06/5 00/7
+# jump back up to the top
6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20
diff --git a/format.py b/format.py
index b63a854..6a6a3b2 100644
--- a/format.py
+++ b/format.py
@@ -1,6 +1,6 @@
#!/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
+import subv
+import bits
instr_map = {
'opr': ('r', 0x33),
@@ -16,55 +16,55 @@ instr_map = {
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)
+ op = bits.u(subv.untag(op), 7)
+ rd = bits.u(subv.untag(rd, 'rd'), 5)
+ if not subv.is_lref(imm):
+ imm = bits.i(subv.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)
+ op = bits.u(subv.untag(op), 7)
+ sub = bits.u(subv.untag(sub, 'subop'), 3)
+ rd = bits.u(subv.untag(rd, 'rd'), 5)
+ rs = bits.u(subv.untag(rs, 'rs'), 5)
+ if not subv.is_lref(imm):
+ imm = bits.i(subv.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)
+ op = bits.u(subv.untag(op), 7)
+ sub = bits.u(subv.untag(sub, 'subop'), 3)
+ rs1 = bits.u(subv.untag(rs1, 'rs'), 5)
+ rs2 = bits.u(subv.untag(rs2, 'rs'), 5)
+ if not subv.is_lref(imm):
+ imm = bits.i(subv.untag(imm, 'off12'), 12)
+ imm_lo = bits.slice(imm, 4, 0)
+ imm_hi = bits.slice(imm, 11, 5)
else:
- label = untag(imm, 'off12')
+ label = subv.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)
+ op = bits.u(subv.untag(op), 7)
+ rd = bits.u(subv.untag(rd, 'rd'), 5)
- if not is_lref(imm):
- imm = i(untag(imm, 'off21'), 21)
+ if not subv.is_lref(imm):
+ imm = bits.i(subv.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)
+ imm_lo = bits.slice(imm, 10, 1)
+ imm_11 = bits.slice(imm, 11, 11)
+ imm_hi = bits.slice(imm, 19, 12)
+ imm_20 = bits.slice(imm, 20, 20)
else:
- label = untag(imm, 'off21')
+ label = subv.untag(imm, 'off21')
imm_lo = (label, 10, 'off21>>1')
imm_11 = (label, 1, 'off21>>11')
imm_hi = (label, 8, 'off21>>12')
@@ -74,20 +74,20 @@ def pack_j(instr):
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)
+ op = bits.u(subv.untag(op), 7)
+ sub = bits.u(subv.untag(sub, 'subop'), 3)
+ rs1 = bits.u(subv.untag(rs1, 'rs'), 5)
+ rs2 = bits.u(subv.untag(rs2, 'rs'), 5)
+ if not subv.is_lref(imm):
+ imm = bits.i(subv.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)
+ imm_lo = bits.slice(imm, 4, 1)
+ imm_md = bits.slice(imm, 10, 5)
+ imm_11 = bits.slice(imm, 11, 11)
+ imm_12 = bits.lice(imm, 12, 12)
else:
- label = untag(imm, 'off13')
+ label = subv.untag(imm, 'off13')
imm_lo = (label, 4, 'off13>>1')
imm_md = (label, 6, 'off13>>5')
imm_11 = (label, 1, 'off13>>11')
@@ -97,16 +97,10 @@ def pack_b(instr):
def format(iter):
for line in iter:
- line = clean(line)
- if line == '':
- continue
+ line = subv.parse(line)
- type = classify(line)
-
- if type == 'segment' or type == 'label':
- yield line
- else:
- instr = parse_instr(line)
+ if line['type'] == 'instr':
+ instr = line['instr']
op = instr[0]
if len(op) != 2:
raise ValueError("instruction without op label")
@@ -130,7 +124,9 @@ def format(iter):
else:
raise NotImplementedError()
- yield format_instr(out)
+ yield subv.format_instr(out)
+ else:
+ yield line['raw']
if __name__ == '__main__':
import sys
@@ -145,7 +141,7 @@ class TestPackers(unittest.TestCase):
final,
[(0x37, 7), (0x5, 5), (0x10010, 20)]
)
- self.assertEqual(bit_concat(*final)[1], 32)
+ self.assertEqual(bits.concat(*final)[1], 32)
label = pack_u([(0x37, 'lui'), (5, 'rd', 't0'), ('pos', 'imm20')])
self.assertEqual(
@@ -165,7 +161,7 @@ class TestPackers(unittest.TestCase):
final,
[(0x13, 7), (6, 5), (0, 3), (0, 5), (0x65, 12)]
)
- self.assertEqual(bit_concat(*final)[1], 32)
+ self.assertEqual(bits.concat(*final)[1], 32)
def test_pack_s(self):
final = pack_s([
@@ -179,7 +175,7 @@ class TestPackers(unittest.TestCase):
final,
[(0x23, 7), (0, 5), (2, 3), (5, 5), (6, 5), (0, 7)]
)
- self.assertEqual(bit_concat(*final)[1], 32)
+ self.assertEqual(bits.concat(*final)[1], 32)
label = pack_s([
(0x23, 'store'),
@@ -231,19 +227,27 @@ class TestE2E(unittest.TestCase):
out = dedent('''\
== code 0x80000000
main:
+ # load 0x10010000 (UART0) into t0
37/7 05/5 10010/20
+ # store 0x48 (H) in UART0+0
13/7 06/5 00/3 00/5 48/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x65 (e) in UART0+0
13/7 06/5 00/3 00/5 65/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x6c (l) in UART0+0
13/7 06/5 00/3 00/5 6c/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x6c (l) in UART0+0
13/7 06/5 00/3 00/5 6c/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x6f (o) in UART0+0
13/7 06/5 00/3 00/5 6f/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # store 0x0a (\\n) in UART0+0
13/7 06/5 00/3 00/5 0a/12
23/7 00/5 02/3 05/5 06/5 00/7
+ # jump back up to the top
6f/7 00/5 main/8/off21>>12 main/1/off21>>11 main/10/off21>>1 main/1/off21>>20
''')
@@ -251,4 +255,5 @@ class TestE2E(unittest.TestCase):
for line in format(StringIO(inp)):
got += line + '\n'
+ self.maxDiff = None
self.assertEqual(got, out)
diff --git a/pack.py b/pack.py
index aba29a3..f772de3 100644
--- a/pack.py
+++ b/pack.py
@@ -1,25 +1,31 @@
#!/usr/bin/env python3
-from riscv import byteify, bit_concat
-from subx import clean, classify, parse_bits, format_instr
+import subv
+import bits
-def pack(iter):
- for line in iter:
- line = clean(line)
- if line == '':
- continue
+def byteify(word):
+ (val, size) = word
+ if size != 32:
+ raise ValueError("Expected 32-bit word")
- type = classify(line)
+ b0 = bits.slice(word, 7, 0)
+ b1 = bits.slice(word, 15, 8)
+ b2 = bits.slice(word, 23, 16)
+ b3 = bits.slice(word, 31, 24)
+ return [b0[:1], b1[:1], b2[:1], b3[:1]]
- if type == 'segment' or type == 'label':
- yield line
- else:
- fields = parse_bits(line)
- total = bit_concat(*fields)
+def pack(iter):
+ for line in iter:
+ line = subv.parse(line)
+ if line['type'] == 'instr':
+ fields = [bits.from_part(p) for p in line['instr']]
+ total = bits.concat(*fields)
if total[1] != 32:
raise ValueError("instruction parts do not add up to 32 bit!")
- yield format_instr(byteify(total))
+ yield subv.format_instr(byteify(total))
+ else:
+ yield line['raw']
if __name__ == '__main__':
import sys
@@ -27,29 +33,41 @@ if __name__ == '__main__':
print(line)
import unittest
-class TestE2E(unittest.TestCase):
+class TestPack(unittest.TestCase):
+ def test_byteify(self):
+ self.assertEqual(
+ byteify((0x12345678, 32)),
+ [(0x78,), (0x56,), (0x34,), (0x12,)]
+ )
+ self.assertEqual(
+ byteify((0x4801813, 32)),
+ [(0x13,), (0x18,), (0x80,), (0x04,)]
+ )
+
def test_e2e(self):
from io import StringIO
from textwrap import dedent
inp = dedent('''\
+ # do some things
== 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
+ 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('''\
+ # do some things
== code 0x80000000
b7 02 01 10
13 03 80 04
diff --git a/riscv.py b/riscv.py
deleted file mode 100644
index 238a06e..0000000
--- a/riscv.py
+++ /dev/null
@@ -1,203 +0,0 @@
-def u(num, bits):
- if num < 0:
- raise ValueError("negative value not allowed: {}".format(num))
-
- if num.bit_length() > bits:
- raise ValueError("value too large for u{} field: {} ({} bits)",
- bits, num, num.bit_length())
-
- return (num, bits)
-
-def i(num, bits):
- if num < 0:
- num = (1 << bits) + num
-
- return u(num, bits)
-
-def bit_concat(*parts):
- val, size = 0, 0
- for (pval, psize) in parts:
- val = val | pval << size
- size += psize
- return (val, size)
-
-def bit_slice(bits, top, bottom):
- (val, size) = bits
-
- if top < bottom:
- raise ValueError("cant slice reverse range")
- elif bottom < 0:
- raise ValueError("negative slice index")
- elif top >= size:
- raise ValueError("cant slice [{}:{}] from {} bit value".format(top, bottom, size))
-
- width = top - bottom + 1
- val = (val >> bottom) & ((1 << width) - 1)
- return (val, width)
-
-def byteify(word):
- (val, size) = word
- if size != 32:
- raise ValueError("Expected 32-bit word")
-
- b0 = bit_slice(word, 7, 0)
- b1 = bit_slice(word, 15, 8)
- b2 = bit_slice(word, 23, 16)
- b3 = bit_slice(word, 31, 24)
- return [b0[:1], b1[:1], b2[:1], b3[:1]]
-
-def format_r(op, rd, r1, r2, funct3, funct7):
- # HH funct7 rs2 rs1 funct3 rd opcode LL
- return byteify(bit_concat(
- u(op, 7),
- u(rd, 5),
- u(funct3, 3),
- u(r1, 5),
- u(r2, 5),
- u(funct7, 7),
- ))
-
-def format_i(op, rd, r1, imm12, funct3):
- # HH imm[11:0] rs1 funct3 rd opcode LL
- return byteify(bit_concat(
- u(op, 7),
- u(rd, 5),
- u(funct3, 3),
- u(r1, 5),
- i(imm12, 12),
- ))
-
-def format_s(op, r1, r2, imm12, funct3):
- # HH imm[11:5] rs2 rs1 funct3 imm[4:0] opcode LL
- imm = i(imm12, 12)
- imm_lo = bit_slice(imm, 4, 0)
- imm_hi = bit_slice(imm, 11, 5)
- return byteify(bit_concat(
- u(op, 7),
- imm_lo,
- u(funct3, 3),
- u(r1, 5),
- u(r2, 5),
- imm_hi
- ))
-
- # imm12 = i(imm12, 12)
- # r1 = i(r1, 5, neg=False)
- # r2 = i(r2, 5, neg=False)
- #
- # b0 = (imm12 & 0x1) << 7 | op
- # b1 = (r1 & 0x1) << 7 | funct3 << 4 | (imm12 & 0x1f) >> 1
- # b2 = (r2 & 0xf) << 4 | r1 >> 1
- # b3 = (imm12 & 0xf0) | r2 >> 4
- # return [(b0,), (b1,), (b2,), (b3,)]
-
-def format_u(op, rd, imm20):
- # HH imm[31:12] rd opcode LL
- return byteify(bit_concat(
- u(op, 7),
- u(rd, 5),
- i(imm20, 20)
- ))
-
-def format_j(op, rd, imm20):
- # HH imm[20] imm[10:1] imm[11] imm[19:12] rd opcode LL
- imm = i(imm20, 20)
- imm_lo = bit_slice(imm, 9, 0)
- imm_10 = bit_slice(imm, 10, 10)
- imm_hi = bit_slice(imm, 18, 11)
- imm_19 = bit_slice(imm, 19, 19)
- return byteify(bit_concat(
- u(op, 7),
- u(rd, 5),
- imm_hi,
- imm_10,
- imm_lo,
- imm_19
- ))
-
-import unittest
-class TestHelpers(unittest.TestCase):
- def test_bit_concat(self):
- self.assertEqual(
- bit_concat((0b10, 2), (0b00, 2)),
- (0b0010, 4)
- )
- self.assertEqual(
- bit_concat((0b1, 1), (0b0110, 4), (0b110, 3)),
- (0b11001101, 8)
- )
-
- def test_bit_slice(self):
- self.assertEqual(
- bit_slice((0x7f, 8), 7, 4),
- (0x7, 4)
- )
- self.assertEqual(
- bit_slice((0b100, 3), 2, 2),
- (0b1, 1)
- )
- with self.assertRaises(ValueError):
- bit_slice((0xf, 4), 4, 0)
- with self.assertRaises(ValueError):
- bit_slice((0xf, 4), 2, 3)
- with self.assertRaises(ValueError):
- bit_slice((0xf, 4), 3, -1)
- self.assertEqual(
- bit_slice((0x12345678, 32), 7, 0),
- (0x78, 8)
- )
- self.assertEqual(
- bit_slice((0x12345678, 32), 15, 8),
- (0x56, 8)
- )
- self.assertEqual(
- bit_slice((0x12345678, 32), 23, 16),
- (0x34, 8)
- )
-
- def test_byteify(self):
- self.assertEqual(
- 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):
- self.assertEqual(
- format_u(0x37, 0x5, 0x10010),
- [(183,), (2,), (1,), (16,)]
- )
-
- def test_format_i(self):
- self.assertEqual(
- format_i(0x13, 5, 0, 72, 0x0),
- [(147,), (2,), (128,), (4,)]
- )
- self.assertEqual(
- format_i(0x13, 9, 2, 72, 0x3),
- [(147,), (52,), (129,), (4,)]
- )
-
- def test_format_s(self):
- self.assertEqual(
- format_s(0x23, 3, 4, 0, 0x2),
- [(35,), (160,), (65,), (0,)]
- )
- self.assertEqual(
- format_s(0x23, 2, 0, -4, 0x2),
- [(35,), (46,), (1,), (254,)]
- )
-
- def test_format_j(self):
- self.assertEqual(
- format_j(0x6f, 0, -26),
- [(111,), (240,), (223,), (252,)]
- )
- self.assertEqual(
- format_j(0x6f, 9, 0),
- [(239,), (4,), (0,), (0,)]
- )
diff --git a/subx.py b/subv.py
index 3266403..b53639d 100644
--- a/subx.py
+++ b/subv.py
@@ -2,6 +2,8 @@ import re
white = re.compile('[ \t\.\n]+')
hex = re.compile(r'^\-?(0x)?[0-9a-f]+$')
+# parsing
+
def parse_part(part):
part = part.split('/')
if hex.match(part[0]):
@@ -13,16 +15,6 @@ 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))
@@ -30,6 +22,44 @@ def parse_segment(line):
def parse_label(line):
return line[:-1]
+def classify(line):
+ if line == '':
+ return 'empty'
+ elif line.startswith('=='): # segment
+ return 'segment'
+ elif line.endswith(':'): # label
+ return 'label'
+ else:
+ return 'instr'
+
+def parse(line):
+ raw = line.strip()
+ split = raw.split('#', 1)
+ if len(split) == 1:
+ clean = raw
+ comment = None
+ else:
+ clean, comment = split
+
+ type = classify(clean)
+
+ if type == 'segment':
+ parsed = parse_segment(clean)
+ elif type == 'label':
+ parsed = parse_label(clean)
+ elif type == 'instr':
+ parsed = parse_instr(clean)
+ else:
+ parsed = None
+
+ return {
+ 'type': type,
+ 'raw': raw,
+ 'line': clean,
+ 'comment': comment,
+ type: parsed,
+ }
+
def is_lref(part):
return isinstance(part[0], str)
@@ -50,17 +80,6 @@ def format_instr(inst, comment=None):
packed = packed + ' # ' + comment
return packed
-def clean(line):
- return line.strip().split('#')[0].rstrip()
-
-def classify(line):
- if line.startswith('=='): # segment
- return 'segment'
- elif line.endswith(':'): # label
- return 'label'
- else:
- return 'instr'
-
import unittest
class TestParsing(unittest.TestCase):
def test_parse_part(self):
diff --git a/survey.py b/survey.py
index 51f1136..23fac27 100644
--- a/survey.py
+++ b/survey.py
@@ -1,8 +1,10 @@
#!/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 subv
+import bits
import re
+tag_re = re.compile(r'^(imm|off)(\d+)(?:>>(\d+))?$')
+
def scan(iter):
addr = None
map = {}
@@ -11,9 +13,8 @@ def scan(iter):
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):
+ if subv.is_lref(part):
label, trim, tag = part
trim = int(trim)
@@ -26,11 +27,11 @@ def observe(part, rel_addr, map):
shift = int(shift)
if mode == 'imm':
- addr = u(addr, size)
+ addr = bits.u(addr, size)
else:
- addr = i(addr - rel_addr, size)
+ addr = bits.i(addr - rel_addr, size)
- part = bit_slice(addr, trim+shift-1, shift)
+ part = bits.slice(addr, trim+shift-1, shift)
return part
@@ -39,32 +40,16 @@ def survey(iter):
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,
- })
+ line = subv.parse(line)
+ line['addr'] = addr
+ queue.append(line)
# step forward addr
- if type == 'segment':
- segment, addr = parsed
- elif type == 'instr':
+ if line['type'] == 'segment':
+ segment, addr = line['segment']
+ elif line['type'] == 'instr':
bits = 0
- for part in parsed:
+ for part in line['instr']:
bits += int(part[1])
if bits % 8 != 0:
@@ -79,7 +64,7 @@ def survey(iter):
for part in line['instr']:
observed = observe(part, line['addr'], map)
instr.append(observed)
- yield format_instr(instr)
+ yield subv.format_instr(instr)
elif line['type'] == 'segment':
yield line['raw']
diff --git a/test.py b/test.py
deleted file mode 100755
index 9934f7a..0000000
--- a/test.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python3
-from riscv import format_u, format_i, format_s, format_j
-from subx import format_instr
-
-if __name__ == "__main__":
- t0 = 0x5
- t1 = 0x6
- print("== code 0x80000000")
- print(format_instr(format_u(0x37, t0, 0x10010), "lui t0, 0x10010"))
- print(format_instr(format_i(0x13, t1, 0, 72, 0x0), "addi t1, x0, 72"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_i(0x13, t1, 0, 101, 0x0), "addi t1, x0, 101"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_i(0x13, t1, 0, 108, 0x0), "addi t1, x0, 108"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_i(0x13, t1, 0, 108, 0x0), "addi t1, x0, 108"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_i(0x13, t1, 0, 111, 0x0), "addi t1, x0, 111"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_i(0x13, t1, 0, 10, 0x0), "addi t1, x0, 10"))
- print(format_instr(format_s(0x23, t0, t1, 0, 0x2), "sw t1, 0(t0)"))
- print(format_instr(format_j(0x6f, 0, -26), "jal x0, -26"))
diff --git a/test_riscv.py b/test_riscv.py
deleted file mode 100644
index b945b36..0000000
--- a/test_riscv.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import unittest
-from riscv import bit_concat, bit_slice, byteify
-from riscv import format_u, format_i, format_s, format_j