aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-07-06 10:03:57 +0000
committers-ol <s+removethis@s-ol.nu>2021-07-06 10:04:36 +0000
commit912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2 (patch)
tree3c62b2a7db782bd6c142a2fdb37d232f31f16eb1
parentupdate ex.subv to work with new pipeline (diff)
downloadsubv-912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2.tar.gz
subv-912b5df8aef99aa00848d2a87bb5e5cebc7ff6b2.zip
fix and finish new pipeline
-rw-r--r--bits.py321
-rwxr-xr-xelf.py8
-rwxr-xr-xpack.py15
-rw-r--r--subv.py31
-rwxr-xr-xvalidate.py5
5 files changed, 36 insertions, 344 deletions
diff --git a/bits.py b/bits.py
index a40c363..bba325c 100644
--- a/bits.py
+++ b/bits.py
@@ -1,102 +1,4 @@
-import re
-from operator import __and__
-from functools import reduce
-
-
-class WordBase(object):
- def slice_allowempty(self, range):
- if not isinstance(range, slice):
- range = slice(range, range)
- hi, lo = range.start, range.stop
-
- if hi < lo:
- return empty
-
- return self[range]
-
- def __and__(self, other):
- if isinstance(other, WordBase):
- return BitsUnion(self, other)
- raise NotImplementedError()
-
-
-class BitsUnion(WordBase):
- def __init__(self, *parts):
- self.parts = parts
-
- def __repr__(self):
- return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts))
-
- def as_value(self, **kwargs):
- """reduce down to a single Bitfield value.
-
- >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value()
- Bitfield(0x1234, 16)
- >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).as_value(labels={'test': 0xf34f})
- Bitfield(0x1234, 16)
- """
- return reduce(__and__, (part.as_value(**kwargs) for part in self.parts))
-
- @property
- def size(self):
- """get the total size.
-
- >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size
- 16
- >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4))).size
- 16
- """
- return sum(part.size for part in self.parts)
-
- def __getitem__(self, range):
- """slice a union given hi and lo bit indices.
-
- >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4]
- Bitfield(0x23, 8)
- >>> BitsUnion(Bitfield(0x12, 8), LabelRef("test", 0, "imm", slice(11, 4)))[11:4]
- BitsUnion(Bitfield(0x2, 4), LabelRef('test', 0, ('imm', 32), 11:8))
- """
- if not isinstance(range, slice):
- range = slice(range, range)
- hi, lo = range.start, range.stop
-
- size = self.size
- if hi < lo:
- raise ValueError("cant slice reverse range")
- elif lo < 0 or hi >= size:
- raise ValueError(
- "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
- )
-
- i = size
- result = Bitfield(0, 0)
- for part in self.parts:
- p_size = part.size
- i -= p_size
- if i > hi:
- # still above hi
- continue
-
- r_hi = min(p_size - 1, hi - i)
- r_lo = max(0, lo - i)
-
- result = result & part[r_hi:r_lo]
-
- if i <= lo:
- # past lo
- break
-
- return result
-
- def __and__(self, other):
- if isinstance(other, BitsUnion):
- return BitsUnion(*self.parts, *other.parts)
- elif isinstance(other, WordBase):
- return BitsUnion(*self.parts, other)
- raise NotImplementedError()
-
-
-class Bitfield(WordBase):
+class Bitfield(object):
def __init__(self, val, size, extra=()):
if val < 0:
min = -(1 << (size - 1))
@@ -152,9 +54,6 @@ class Bitfield(WordBase):
base = (digits - len(base)) * "0" + base
return "/".join([base, *self.extra, str(self.size)])
- def as_value(self, **kwargs):
- return self
-
def __getitem__(self, range):
"""slice a bitfield given hi and lo bit indices.
@@ -197,6 +96,16 @@ class Bitfield(WordBase):
val = (self.val >> lo) & ((1 << size) - 1)
return Bitfield(val, size)
+ def slice_allowempty(self, range):
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ if hi < lo:
+ return empty
+
+ return self[range]
+
def __and__(self, other):
"""concatenate multiple bitfields.
@@ -207,152 +116,11 @@ class Bitfield(WordBase):
"""
if isinstance(other, Bitfield):
return Bitfield(self.val << other.size | other.val, self.size + other.size)
- elif isinstance(other, WordBase):
- return BitsUnion(self, other)
raise NotImplementedError()
global_slice = slice
-
-class LabelRef(WordBase):
- mode_re = re.compile(r"^(imm|off)(\d+)?$")
-
- def __init__(self, label, offset, mode, slice=None):
- if isinstance(mode, str):
- match = LabelRef.mode_re.match(mode)
- assert match, ValueError("invalid label reference tag")
- mode = (match.group(1), int(match.group(2) or 32))
-
- self.label = label
- self.mode = mode
- self.slice = slice or global_slice(self.mode[1] - 1, 0)
- self.offset = offset
-
- def __repr__(self):
- """format for debugging.
-
- >>> LabelRef("label", 0, ("imm", 32), slice(31,31))
- LabelRef('label', 0, ('imm', 32), 31:31)
- >>> LabelRef("label", 4, ("imm", 32), slice(11,0))
- LabelRef('label', 4, ('imm', 32), 11:0)
- >>> LabelRef("test", -4, "off32")
- LabelRef('test', -4, ('off', 32), 31:0)
- >>> LabelRef("test", 0, "off")
- LabelRef('test', 0, ('off', 32), 31:0)
- """
- return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(
- self.label, self.offset, self.mode, self.slice
- )
-
- def __str__(self):
- """format as a word.
-
- >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31)))
- 'label/imm32/[31:31]'
- >>> str(LabelRef("label", 0, ("imm", 32), slice(11,0)))
- 'label/imm32/[11:0]'
- >>> str(LabelRef("test", 4, "off32"))
- 'test+4/off32/[31:0]'
- >>> str(LabelRef("test", -12, "off"))
- 'test-12/off32/[31:0]'
- """
- offset = ""
- if self.offset != 0:
- offset = "{:+}".format(self.offset)
-
- return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(
- self.label, offset, self.mode, self.slice
- )
-
- @property
- def size(self):
- return self.slice.start - self.slice.stop + 1
-
- def as_value(self, labels={}, **kwargs):
- """resolve the reference.
-
- >>> labels = {'test': 0xabcd1234, 'main': 0x8000}
- >>> LabelRef("test", 0, "imm").as_value(labels=labels)
- Bitfield(0xabcd1234, 32)
- >>> LabelRef("test", 0, "imm", slice(15, 0)).as_value(labels=labels)
- Bitfield(0x1234, 16)
- >>> LabelRef("test", 0, "imm", slice(31, 12)).as_value(labels=labels)
- Bitfield(0xabcd1, 20)
- >>> LabelRef("test", 4, "imm", slice(15, 0)).as_value(labels=labels)
- Bitfield(0x1238, 16)
- >>> LabelRef("test", 4, "imm", slice(15, 4)).as_value(labels=labels)
- Bitfield(0x123, 12)
- >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x6000)
- Bitfield(0x2000, 32)
- >>> LabelRef("main", 0, "off").as_value(labels=labels, pc=0x800a)
- Bitfield(0xfffffff6, 32)
- >>> LabelRef("main", 0, "off", slice(7, 0)).as_value(labels=labels, pc=0x8012)
- Bitfield(0xee, 8)
- >>> LabelRef("main", 0, "off8").as_value(labels=labels, pc=0x8012)
- Bitfield(0xee, 8)
- >>> LabelRef("main", 0, "off4").as_value(labels=labels, pc=0x8012)
- Traceback (most recent call last):
- ...
- ValueError: value -18 out of i4 range [-8;7]
- >>> LabelRef("main", 4, "off", slice(7, 0)).as_value(labels=labels, pc=0x70fa)
- Bitfield(0xa, 8)
- """
- if self.label not in labels:
- raise ValueError("undefined label '{}'".format(self.label))
-
- value = labels[self.label] + self.offset
-
- if self.mode[0] == "off":
- assert "pc" in kwargs, TypeError(
- "'pc' is required to resolve offset-references"
- )
- value = value - kwargs["pc"]
-
- field = Bitfield(value, self.mode[1])
- return field[self.slice]
-
- def __getitem__(self, range):
- """slice a label using hi and lo bit indices.
-
- >>> label = LabelRef("label", 0, "imm32", slice(31,0))
- >>> label.size
- 32
- >>> label[31]
- LabelRef('label', 0, ('imm', 32), 31:31)
- >>> label[11:0]
- LabelRef('label', 0, ('imm', 32), 11:0)
- >>> label[11:0].size
- 12
- >>> label[12:1]
- LabelRef('label', 0, ('imm', 32), 12:1)
- >>> label = LabelRef("abc", 0, "off32", slice(31,12))
- >>> label.size
- 20
- >>> label[9:0]
- LabelRef('abc', 0, ('off', 32), 21:12)
- >>> label[9:2]
- LabelRef('abc', 0, ('off', 32), 21:14)
- >>> label[20:8]
- Traceback (most recent call last):
- ...
- ValueError: slice [20:8] out of range of abc/off32/[31:12]
- """
- if not isinstance(range, slice):
- range = slice(range, range)
- hi, lo = range.start, range.stop
-
- if hi < lo:
- raise ValueError("cant slice reverse range")
- elif lo < 0 or hi >= self.size:
- raise ValueError(
- "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)
- )
-
- new_slice = slice(self.slice.stop + hi, self.slice.stop + lo)
- return LabelRef(self.label, self.offset, self.mode, new_slice)
-
-
empty = Bitfield(0, 0)
@@ -396,22 +164,21 @@ def i(num, bits):
>>> i(-128, 8)
Bitfield(0x80, 8)
- >>> i(128, 8)
+ >>> i(256, 8)
Traceback (most recent call last):
...
- ValueError: value 128 (i9) too large for i8 field [-128;127]
+ ValueError: value 256 (u9) too large for u8 field
>>> i(-129, 8)
Traceback (most recent call last):
...
- ValueError: value -129 (i9) too large for i8 field [-128;127]
+ ValueError: value -129 (i9) too small for i8 field (min -128)
"""
min = -(1 << (bits - 1))
- max = -1 - min
- if num > max or num < min:
+ if num < min:
raise ValueError(
- "value {} (i{}) too large for i{} field [{};{}]".format(
- num, num.bit_length() + 1, bits, min, max
+ "value {} (i{}) too small for i{} field (min {})".format(
+ num, num.bit_length() + 1, bits, min
)
)
@@ -432,57 +199,3 @@ def from_part(part):
val = int(part[0])
size = part[1]
return Bitfield(val, int(size))
-
-
-global_slice = slice
-ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$")
-slice_re = re.compile(r"^\[(\d+):(\d+)]$")
-
-
-def ref(val, default_slice=None):
- """add a default slice spec to labels if missing.
-
- >>> ref(('label', 'imm12'))
- LabelRef('label', 0, ('imm', 12), 11:0)
- >>> ref(('label', 'imm12'), slice(12, 1))
- LabelRef('label', 0, ('imm', 12), 12:1)
- >>> ref(('label-4', 'imm12'), slice(12, 1))
- LabelRef('label', -4, ('imm', 12), 12:1)
- >>> ref(('label', 'imm12', '[14:3]'), slice(12, 1))
- LabelRef('label', 0, ('imm', 12), 14:3)
- >>> ref(('label+4', 'imm12', '[14:3]'), slice(12, 1))
- LabelRef('label', 4, ('imm', 12), 14:3)
- >>> ref(('label', 'imm12', '[14:3]'), slice(12, 1))
- LabelRef('label', 0, ('imm', 12), 14:3)
- >>> ref(('label', 'imm12', '[31:0]'), slice(11, 0))
- Traceback (most recent call last):
- ...
- AssertionError: expected 12 bit slice, got label/imm12/[31:0]
- """
- slice = None
- if len(val) == 3:
- label_offset, mode, slice = val
- elif len(val) == 2:
- label_offset, mode = val
- else:
- raise ValueError("expected label reference, got {}".format(val))
-
- match = ref_re.match(label_offset)
- assert match, ValueError("invalid label reference")
- label, offset = match.group(1), int(match.group(2) or 0)
-
- if isinstance(slice, str):
- match = slice_re.match(slice)
- assert match, ValueError("invalid slice syntax")
- a, b = match.groups()
- slice = global_slice(int(a), int(b))
-
- ref = LabelRef(label, offset, mode, slice or default_slice)
-
- if default_slice:
- expected = default_slice.start - default_slice.stop + 1
- assert expected == ref.size, ValueError(
- "expected {} bit slice, got {}".format(expected, ref)
- )
-
- return ref
diff --git a/elf.py b/elf.py
index 0db1138..776dd91 100755
--- a/elf.py
+++ b/elf.py
@@ -6,7 +6,7 @@ import subv
Takes a packed & surveyed hex stream (with section headers) and packs it into a
simulatable ELF file that can be run as a kernel:
- qemu-system-riscv32 -nographic -machine sifive_u -nographic -kernel out.elf
+ qemu-system-riscv32 -nographic -machine sifive_u -bios none -kernel out.elf
sifive-u has lots of memory mapped peripherals, see
https://github.com/qemu/qemu/blob/master/hw/riscv/sifive_u.c#L66-L83
@@ -71,9 +71,9 @@ def write_program_header(segment, c, start):
flags = 5 if segment["name"] == "code" else 6
if offset % align != start % align:
- print("{:02x} {:02x}".format(offset, offset % align))
- print("{:02x} {:02x}".format(start, start % align))
- raise ValueError("improper alignment")
+ extra = "\n{:02x} {:02x}".format(offset, offset % align)
+ extra += "\n{:02x} {:02x}".format(start, start % align)
+ raise ValueError("improper alignment:" + extra)
c += wi(0x1, 4) # p_type
c += wi(offset, 4) # p_offset
diff --git a/pack.py b/pack.py
index 4a21ab5..930c120 100755
--- a/pack.py
+++ b/pack.py
@@ -69,13 +69,9 @@ def pack(iter):
buf = bits.empty
def flush_bytes():
- tmp = out[:]
+ line = " ".join("{:02x}".format(b.val) for b in out)
out.clear()
- return {
- "type": "instr",
- "instr": tmp,
- "comment": None,
- }
+ return line
last_segment = None
for segment, line in iter:
@@ -93,15 +89,18 @@ def pack(iter):
while buf.size >= 8:
byte = buf[7:0]
buf = buf.slice_allowempty(slice(buf.size - 1, 8))
- out.append((byte.val,))
+ out.append(byte)
if len(out) == 4:
- yield subv.format(flush_bytes())
+ yield flush_bytes()
last_segment = segment
assert buf.size == 0, "segment '{}' end isn't byte-aligned".format(segment)
+ if len(out) > 0:
+ yield flush_bytes()
+
if __name__ == "__main__":
import sys
diff --git a/subv.py b/subv.py
index 8871625..c514e34 100644
--- a/subv.py
+++ b/subv.py
@@ -7,6 +7,7 @@ num = re.compile(r"^\d+$")
ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$")
immediate_re = re.compile(r"^(imm|off)(\d+)(hi|lo)?$")
+
# parsing
def parse_part(part):
"""parse a literal with attached metadata.
@@ -113,7 +114,7 @@ def parse_reference(part):
"""parse a sliced-label part.
>>> parse_reference(('lbl', 'imm32'))
- {'label': 'lbl', 'mode': 'imm', 'size': 32, offset: 0}
+ {'label': 'lbl', 'mode': 'imm', 'size': 32, 'offset': 0}
>>> parse_reference(('lbl+0', 'off32'))
{'label': 'lbl', 'mode': 'off', 'size': 32, 'offset': 0}
>>> parse_reference(('lbl+2', 'imm32'))
@@ -274,30 +275,6 @@ def untag(part, expect=None):
return part[0]
-def format_reference(ref):
- """opposite of parse_reference..
-
- >>> format_reference({'label': 'lbl', 'mode': 'imm', 'size': 32, 'meta': ()})
- ('lbl', 'imm32')
- >>> format_reference({'label': 'lbl', 'offset': 0, 'mode': 'imm', 'size': 32, 'meta': ()})
- ('lbl+0', 'imm32')
- >>> format_reference({'label': 'lbl', 'offset': 2, 'mode': 'imm', 'size': 32, 'meta': ()})
- ('lbl+2', 'imm32')
- >>> format_reference({'label': 'x', 'mode': 'off', 'size': 12, 'meta': (), 'hi': 11, 'lo': 0})
- ('x[11:0]', 'off12')
- >>> format_reference({'label': 'x', 'offset': -16, 'mode': 'off', 'size': 12, 'meta': (), 'hi': 11, 'lo': 0})
- ('x-16[11:0]', 'off12')
- >>> format_reference({'label': 'x', 'offset': -16, 'mode': 'off', 'size': 12, 'meta': ('extra', 'stuff'), 'hi': 11, 'lo': 0})
- ('x-16[11:0]', 'off12', 'extra', 'stuff')
- """
- label = ref["label"]
- if "offset" in ref:
- label += "{:+}".format(ref["offset"])
- if "hi" in ref:
- label += "[{hi}:{lo}]".format(**ref)
- return (label, "{mode}{size}".format(**ref), *ref["meta"])
-
-
def format_immediate(imm):
tag = "{mode}{size}".format(**imm)
if "split" in imm:
@@ -327,7 +304,7 @@ def format_part(part):
>>> format_part(('$label:suff', 'tag'))
'$label:suff/tag'
"""
- if isinstance(part, bits.WordBase):
+ if isinstance(part, bits.Bitfield):
return str(part)
elif not is_reference(part):
first = "{:x}".format(part[0])
@@ -351,6 +328,8 @@ def format(line):
if line["comment"]:
packed = packed + " # " + line["comment"]
return packed
+ elif type == "empty":
+ return line["raw"]
else:
raise NotImplementedError("type {}".format(type))
diff --git a/validate.py b/validate.py
index 84bb036..09c37a5 100755
--- a/validate.py
+++ b/validate.py
@@ -193,8 +193,8 @@ def validate_b(inputs):
return [
(op[0], "b"),
funct,
- src1,
- src2,
+ rs1,
+ rs2,
offset,
]
@@ -233,6 +233,7 @@ instr_map = {
"opi": (validate_i, 0x13),
"load": (validate_i, 0x03),
"jalr": (validate_i, 0x67),
+ "system": (validate_i, 0x73),
"store": (validate_s, 0x23),
"branch": (validate_b, 0x63),
"lui": (validate_u, 0x37),