aboutsummaryrefslogtreecommitdiffstats
path: root/validate.py
diff options
context:
space:
mode:
Diffstat (limited to 'validate.py')
-rwxr-xr-xvalidate.py295
1 files changed, 295 insertions, 0 deletions
diff --git a/validate.py b/validate.py
new file mode 100755
index 0000000..452b4fa
--- /dev/null
+++ b/validate.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+""" validate.py
+Validates mnemonic labels match the numeric values given for opcodes,
+register codes, etc; normalizes argument order and verifies that there are
+no missing arguments.
+
+>>> from io import StringIO
+>>> # doctest: +REPORT_NDIFF
+... print(subv.join_all(validate(StringIO('''
+... == 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/width/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/width/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/width/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/width/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/width/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/width/word 5/rs/t0 6/rs/t1 0/off12
+... # jump back up to the top
+... 6f/jal 0/rd/x0 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 5/rs1 0/off12 2/funct3 6/rs2
+# store 0x65 (e) in UART0+0
+13/i 6/rd 0/funct3 0/rs 65/imm12
+23/s 5/rs1 0/off12 2/funct3 6/rs2
+# store 0x6c (l) in UART0+0
+13/i 6/rd 0/funct3 0/rs 6c/imm12
+23/s 5/rs1 0/off12 2/funct3 6/rs2
+# store 0x6c (l) in UART0+0
+13/i 6/rd 0/funct3 0/rs 6c/imm12
+23/s 5/rs1 0/off12 2/funct3 6/rs2
+# store 0x6f (o) in UART0+0
+13/i 6/rd 0/funct3 0/rs 6f/imm12
+23/s 5/rs1 0/off12 2/funct3 6/rs2
+# store 0x0a (\\n) in UART0+0
+13/i 6/rd 0/funct3 0/rs a/imm12
+23/s 5/rs1 0/off12 2/funct3 6/rs2
+# jump back up to the top
+6f/j 0/rd main/off21
+"""
+
+import subv
+import bits
+
+
+def pop_piece(line, labels):
+ for part in line:
+ if part[1] in labels:
+ line.remove(part)
+ return (part[0], labels[0])
+
+ raise ValueError("Expected a part with labels {}".format(labels))
+
+
+REG_NAMES = {}
+REG_NAMES.update(
+ {
+ name: i
+ for i, name in enumerate(
+ [
+ "zero",
+ "ra",
+ "sp",
+ "gp",
+ "tp",
+ "t0",
+ "t1",
+ "t2",
+ "s0",
+ "s1",
+ "a0",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "a6",
+ "a7",
+ "s2",
+ "s3",
+ "s4",
+ "s5",
+ "s6",
+ "s7",
+ "s8",
+ "s9",
+ "s10",
+ "s11",
+ "t3",
+ "t4",
+ "t5",
+ "t6",
+ ]
+ )
+ }
+)
+REG_NAMES.update({"x{}".format(i): i for i in range(32)})
+
+
+def pop_register(line, labels):
+ for part in line:
+ if part[1] in labels:
+ line.remove(part)
+
+ value = part[0]
+ if value < 0 or value > 31:
+ raise ValueError(
+ "Invalid register value {} (expected 0...31)".format(value)
+ )
+
+ if len(part) > 2:
+ name = part[2]
+ try:
+ assert value == REG_NAMES[name], ValueError(
+ "Invalid register value {} for '{}', expected {}".format(
+ value, name, REG_NAMES[name]
+ )
+ )
+ except KeyError:
+ raise ValueError("Unknown register name '{}'".format(name))
+
+ return (part[0], labels[0])
+
+ raise ValueError("Expected a register with labels {}".format(labels))
+
+
+def validate_u(inputs):
+ op = inputs.pop(0)
+ rd = pop_register(inputs, ["rd", "dest"])
+ imm = inputs.pop()
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "u"),
+ rd,
+ imm,
+ ]
+
+
+def validate_s(inputs):
+ op = inputs.pop(0)
+ width = pop_piece(inputs, ["funct3", "funct", "width"])
+ base = pop_register(inputs, ["rs1", "rs", "base"])
+ src = pop_register(inputs, ["rs2", "rs", "src"])
+ offset = inputs.pop()
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "s"),
+ base,
+ offset,
+ width,
+ src,
+ ]
+
+
+def validate_i(inputs):
+ op = inputs.pop(0)
+
+ rs_name = "src"
+ if op[1] in ["load", "jalr"]:
+ rs_name = "base"
+
+ dest = pop_register(inputs, ["rd", "dest"])
+ funct = pop_piece(inputs, ["funct3", "funct", "subop"])
+ rs = pop_register(inputs, ["rs", rs_name])
+ imm = inputs.pop()
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "i"),
+ dest,
+ funct,
+ rs,
+ imm,
+ ]
+
+
+def validate_r(inputs):
+ op = inputs.pop(0)
+
+ dest = pop_register(inputs, ["rd", "dest"])
+ funct = pop_piece(inputs, ["funct7", "funct", "subop"])
+ rs1 = pop_register(inputs, ["rs1", "rs", "src1", "src"])
+ rs2 = pop_register(inputs, ["rs2", "rs", "src2", "src"])
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "r"),
+ dest,
+ funct,
+ rs1,
+ rs2,
+ ]
+
+
+def validate_j(inputs):
+ op = inputs.pop(0)
+
+ dest = pop_register(inputs, ["rd", "dest"])
+ offset = inputs.pop()
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "j"),
+ dest,
+ offset,
+ ]
+
+
+def validate_b(inputs):
+ op = inputs.pop(0)
+
+ funct = pop_piece(inputs, ["funct3", "funct", "subop"])
+ rs1 = pop_register(inputs, ["rs1", "rs", "src1", "src"])
+ rs2 = pop_register(inputs, ["rs2", "rs", "src2", "src"])
+ offset = inputs.pop()
+
+ assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+
+ return [
+ (op[0], "b"),
+ funct,
+ src1,
+ src2,
+ offset,
+ ]
+
+
+instr_map = {
+ "opi": (validate_i, 0x13),
+ "opr": (validate_r, 0x33),
+ "load": (validate_i, 0x03),
+ "store": (validate_s, 0x23),
+ "jal": (validate_j, 0x6F),
+ "jalr": (validate_i, 0x67),
+ "branch": (validate_b, 0x63),
+ "lui": (validate_u, 0x37),
+ "auipc": (validate_u, 0x17),
+}
+
+
+@subv.with_parsed_lines
+def validate(iter):
+ for segment, line in iter:
+ if line["type"] == "instr" and segment == "code":
+ op = line["instr"][0]
+ assert len(op) == 2, "instruction without op label: {}".format(op)
+
+ (op, label) = op
+ if label not in instr_map:
+ raise ValueError("unknown op label: {}".format(label))
+ (validator, expected) = instr_map[label]
+ if op != expected:
+ raise ValueError(
+ "opcode {} doesn't match label {} (expected {})".format(
+ op, label, expected
+ )
+ )
+
+ line["instr"] = validator(line["instr"][:])
+ line = yield subv.format(line)
+ else:
+ line = yield line["raw"]
+
+
+if __name__ == "__main__":
+ import sys
+
+ for line in validate(sys.stdin):
+ print(line)