aboutsummaryrefslogtreecommitdiffstats
path: root/validate.py
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-07-09 10:58:14 +0000
committers-ol <s+removethis@s-ol.nu>2021-07-09 12:39:12 +0000
commit6368fb586f3d5d70528915369397281ccfd00dbe (patch)
treedc60783429b8f1c831649c5aa7804161883bc023 /validate.py
parentremove outdated disasm.sh (diff)
downloadsubv-6368fb586f3d5d70528915369397281ccfd00dbe.tar.gz
subv-6368fb586f3d5d70528915369397281ccfd00dbe.zip
wip more validation logic
Diffstat (limited to 'validate.py')
-rwxr-xr-xvalidate.py422
1 files changed, 335 insertions, 87 deletions
diff --git a/validate.py b/validate.py
index 09c37a5..bdf8f95 100755
--- a/validate.py
+++ b/validate.py
@@ -12,23 +12,23 @@ no missing arguments.
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 48/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 65/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 6c/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 6c/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 6f/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # 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
+... 13/opi 6/rd/t1 0/subop/add 0/rs/x0 0a/imm12
+... 23/store 2/width/word 5/rs/t0 0/off12 6/rs/t1
... # jump back up to the top
... 6f/jal 0/rd/x0 main/off21
... '''[1:-1]))))
@@ -38,22 +38,22 @@ main:
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/imm12 2/funct3 6/rs2
+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 5/rs1 0/imm12 2/funct3 6/rs2
+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 5/rs1 0/imm12 2/funct3 6/rs2
+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 5/rs1 0/imm12 2/funct3 6/rs2
+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 5/rs1 0/imm12 2/funct3 6/rs2
+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 5/rs1 0/imm12 2/funct3 6/rs2
+23/s 2/funct3 5/rs1 0/imm12 6/rs2
# jump back up to the top
6f/j 0/rd main/off21
"""
@@ -61,16 +61,46 @@ main:
import subv
-def pop_piece(line, labels):
+def pop_part(line, labels, NAME="part"):
for part in line:
if part[1] in labels:
line.remove(part)
- return (part[0], labels[0])
+ return (part[0], labels[0]) + part[2:]
- raise ValueError("Expected a part with labels {}".format(labels))
+ raise ValueError("Expected a {} with label {}".format(NAME, "/".join(labels)))
-def pop_immediate(line, sizes, allow_offset=False):
+def validate_part(line, labels, MAP, NAME="part"):
+ part = pop_part(line, labels, NAME)
+
+ MNEMONICS = "\nvalid mnemonics are: " + ", ".join(
+ subv.format_part((MAP[name], labels[0], name)) for name in MAP
+ )
+
+ if len(part) != 3:
+ raise ValueError(
+ "{} part {} needs a value mnemonic".format(NAME, subv.format_part(part))
+ + MNEMONICS
+ )
+
+ value, _, name = part
+
+ if name not in MAP:
+ raise ValueError("Unknown {} mnemonic '{}'".format(NAME, name) + MNEMONICS)
+
+ if value != MAP[name]:
+ raise ValueError(
+ "{} code doesn't match mnemonic (got {}, expected {})".format(
+ NAME,
+ subv.format_part(part),
+ subv.format_part((MAP[name],) + part[1:]),
+ )
+ )
+
+ return part
+
+
+def pop_immediate(line, sizes, modes=["imm"]):
if isinstance(sizes, int):
sizes = [sizes]
@@ -78,8 +108,16 @@ def pop_immediate(line, sizes, allow_offset=False):
imm = subv.parse_immediate(part[1])
if imm and imm["size"] in sizes:
line.remove(part)
+
+ if imm["mode"] not in modes:
+ raise ValueError(
+ "Expected immediate {} to use mode {}".format(
+ subv.format_part(part), "/".join(modes)
+ )
+ )
+
if not isinstance(part[0], str):
- imm["mode"] = "imm"
+ imm["mode"] = modes[0]
return (part[0], subv.format_immediate(imm))
@@ -88,111 +126,321 @@ def pop_immediate(line, sizes, allow_offset=False):
)
+def validate_empty(inputs):
+ if len(inputs) != 0:
+ raise ValueError("Extra arguments: {}".format(inputs))
+
+
REG_NAMES = "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 = {name: i for i, name in enumerate(REG_NAMES.split(","))}
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)
+def validate_reg(line, labels):
+ """pop a register and validate its mnemonic.
+
+ >>> validate_reg([(5, 'rd', 't0')], ['rd', 'dest'])
+ (5, 'rd')
+ >>> validate_reg([(5, 'rd', 'x5')], ['rd', 'dest'])
+ (5, 'rd')
+ >>> validate_reg([(5, 'dest', 'x5')], ['rd', 'dest'])
+ (5, 'rd')
+
+ >>> validate_reg([(5, 'rd')], ['rd']) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: register part 5/rd needs a value mnemonic
+ valid mnemonics are: 0/rd/zero, 1/rd/ra, 2/rd/sp, ...
+ >>> validate_reg([(5, 'rd', 'xxx')], ['rd']) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: Unknown register mnemonic 'xxx'
+ valid mnemonics are: 0/rd/zero, 1/rd/ra, 2/rd/sp, ...
+ >>> validate_reg([(3, 'rd', 't0')], ['rd'])
+ Traceback (most recent call last):
+ ...
+ ValueError: register code doesn't match mnemonic (got 3/rd/t0, expected 5/rd/t0)
+ >>> validate_reg([(5, 'rd')], ['rs'])
+ Traceback (most recent call last):
+ ...
+ ValueError: Expected a register with label rs
+ >>> validate_reg([], ['rd', 'dest'])
+ Traceback (most recent call last):
+ ...
+ ValueError: Expected a register with label rd/dest
+ """
+
+ return validate_part(line, labels, REG_NAMES, "register")[:2]
+
+
+COMP_OP_NAMES = {
+ "add": 0,
+ "sub": 0,
+ "xor": 4,
+ "or": 6,
+ "and": 7,
+ "sll": 1,
+ "slt": 2,
+ "sltu": 3,
+ "srl": 5,
+ "sra": 5,
+}
- value = part[0]
- if value < 0 or value > 31:
- raise ValueError(
- "Invalid register value {} (expected 0...31)".format(value)
- )
+STORE_WIDTHS = {
+ # ISA names
+ # byte
+ "b": 0,
+ "byte": 0,
+ # halfword
+ "h": 1,
+ "half": 1,
+ # word
+ "w": 2,
+ "word": 2,
+}
- 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))
+LOAD_WIDTHS = STORE_WIDTHS | {
+ # unsigned byte
+ "bu": 4,
+ "ubyte": 4,
+ # unsigned halfword
+ "hu": 5,
+ "uhalf": 5,
+}
- return (part[0], labels[0])
+BRANCH_NAMES = {
+ # equal
+ "beq": 0,
+ "eq": 0,
+ "==": 0,
+ # not equal
+ "bne": 1,
+ "ne": 1,
+ "!=": 1,
+ # lower than
+ "blt": 4,
+ "lt": 4,
+ "<": 4,
+ # greater/equal than
+ "bge": 5,
+ "ge": 5,
+ ">=": 5,
+ # unsigned lower than
+ "bltu": 6,
+ "ltu": 6,
+ "<u": 6,
+ # unsigned greater/equal than
+ "bgeu": 7,
+ "geu": 7,
+ ">=u": 7,
+}
- raise ValueError("Expected a register with labels {}".format(labels))
+SYSTEM_OP_NAMES = {
+ "csrrw": 1, # read/write
+ "csrrs": 2, # read/set,
+ "csrrc": 3, # read/clear,
+ "csrrwi": 5, # read/write immediate
+ "csrrsi": 6, # read/set, immediate
+ "csrrci": 7, # read/clear, immediate
+}
-def validate_r(inputs):
+def validate_opr(inputs):
+ """
+ validate integer register-register operations.
+
+ >>> validate_opr(subv.parse('33/opr 0/mode/norm 0/subop/add 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ [(51, 'r'), (5, 'rd'), (0, 'funct3'), (10, 'rs1'), (11, 'rs2'), (0, 'funct7')]
+ >>> validate_opr(subv.parse('33/opr 20/mode/alt 0/subop/sub 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ [(51, 'r'), (5, 'rd'), (0, 'funct3'), (10, 'rs1'), (11, 'rs2'), (32, 'funct7')]
+ >>> validate_opr(subv.parse('33/opr 20/mode/alt 5/subop/sra 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ [(51, 'r'), (5, 'rd'), (5, 'funct3'), (10, 'rs1'), (11, 'rs2'), (32, 'funct7')]
+
+ >>> validate_opr(subv.parse('33/opr 20/mode/alt 0/subop/add 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ Traceback (most recent call last):
+ ...
+ ValueError: comp-op 0/funct3/add needs to go with comp-mode 0/funct7/norm
+ >>> validate_opr(subv.parse('33/opr 0/mode/norm 0/subop/sub 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ Traceback (most recent call last):
+ ...
+ ValueError: comp-op 0/funct3/sub needs to go with comp-mode 20/funct7/alt
+ >>> validate_opr(subv.parse('33/opr 0/mode/norm 5/subop/sra 5/rd/t0 a/rs/a0 b/rs/a1')['instr'])
+ Traceback (most recent call last):
+ ...
+ ValueError: comp-op 5/funct3/sra needs to go with comp-mode 20/funct7/alt
+ """
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"])
+ dest = validate_reg(inputs, ["rd", "dest"])
+ rs1 = validate_reg(inputs, ["rs1", "rs", "src1", "src"])
+ rs2 = validate_reg(inputs, ["rs2", "rs", "src2", "src"])
+
+ funct3 = validate_part(
+ inputs, ["funct3", "funct", "subop"], COMP_OP_NAMES, NAME="comp-op"
+ )
+ funct7 = validate_part(
+ inputs, ["funct7", "mode"], {"norm": 0, "alt": 32}, NAME="comp-mode"
+ )
+ cop = funct3[2]
+
+ expect_mode = "alt" if cop in ["sub", "sra"] else "norm"
+ expect_val = 32 if expect_mode == "alt" else 0
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ if funct7[2] != expect_mode:
+ raise ValueError(
+ "comp-op {} needs to go with comp-mode {}".format(
+ subv.format_part(funct3),
+ subv.format_part((expect_val, "funct7", expect_mode)),
+ )
+ )
+
+ validate_empty(inputs)
return [
(op[0], "r"),
dest,
- funct,
+ funct3[:2],
rs1,
rs2,
+ funct7[:2],
]
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])
+ dest = validate_reg(inputs, ["rd", "dest"])
+ funct = pop_part(inputs, ["funct3", "funct", "subop"])
+ src = validate_reg(inputs, ["rs", "rs1"])
imm = pop_immediate(inputs, 12)
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ validate_empty(inputs)
+
+ return [
+ (op[0], "i"),
+ dest,
+ funct[:2],
+ src,
+ imm,
+ ]
+
+
+def validate_jalr(inputs):
+ op = inputs.pop(0)
+
+ dest = validate_reg(inputs, ["rd", "dest"])
+ funct = pop_part(inputs, ["funct3", "funct", "subop"])
+ src = validate_reg(inputs, ["rs", "rs1", "base"])
+ offset = pop_immediate(inputs, 12, ["imm", "off"])
+
+ if funct[0] != 0:
+ raise ValueError("Expected 0/funct3, got {}".format(subv.format_part(funct)))
+
+ validate_empty(inputs)
return [
(op[0], "i"),
dest,
- funct,
- rs,
+ funct[:2],
+ src,
+ offset,
+ ]
+
+
+def validate_csr(inputs):
+ """
+ validate system operations.
+
+ >>> validate_csr(subv.parse('73/system 0/dest/x0 1/subop/csrrw 5/rs/t0 afe/csr12')['instr'])
+ [(115, 'i'), (0, 'rd'), (1, 'funct3'), (5, 'rs'), (2814, 'imm12')]
+ >>> validate_csr(subv.parse('73/system 0/dest/x0 5/subop/csrrwi 7/imm5 afe/csr12')['instr'])
+ [(115, 'i'), (0, 'rd'), (5, 'funct3'), (7, 'imm5'), (2814, 'imm12')]
+
+ >>> validate_csr(subv.parse('73/system 0/dest/x0 1/subop/csrrw 7/imm5 afe/csr12')['instr'])
+ Traceback (most recent call last):
+ ...
+ ValueError: Expected a register with label rs/rs1/src
+ >>> validate_csr(subv.parse('73/system 0/dest/x0 5/subop/csrrwi 5/rs/t0 afe/csr12')['instr'])
+ Traceback (most recent call last):
+ ...
+ ValueError: Expected an immediate with size 5
+ """
+ op = inputs.pop(0)
+
+ dest = validate_reg(inputs, ["rd", "dest"])
+ funct = validate_part(
+ inputs, ["funct3", "funct", "subop"], SYSTEM_OP_NAMES, "system-op"
+ )
+ imm = pop_immediate(inputs, 12, ["imm", "csr"])
+
+ if funct[2][-1] == "i":
+ src = pop_immediate(inputs, 5)
+ else:
+ src = validate_reg(inputs, ["rs", "rs1", "src"])
+
+ validate_empty(inputs)
+
+ return [
+ (op[0], "i"),
+ dest,
+ funct[:2],
+ src,
imm,
]
-def validate_s(inputs):
+def validate_load(inputs):
+ op = inputs.pop(0)
+
+ dest = validate_reg(inputs, ["rd", "dest"])
+ width = validate_part(inputs, ["funct3", "funct", "width"], LOAD_WIDTHS, "width")
+ base = validate_reg(inputs, ["rs", "base"])
+ offset = pop_immediate(inputs, 12, ["imm", "off"])
+
+ validate_empty(inputs)
+
+ return [
+ (op[0], "i"),
+ dest,
+ width[:2],
+ base,
+ offset,
+ ]
+
+
+def validate_store(inputs):
op = inputs.pop(0)
- base = pop_register(inputs, ["rs1", "rs", "base"])
- offset = pop_immediate(inputs, 12, allow_offset=True)
- width = pop_piece(inputs, ["funct3", "funct", "width"])
- src = pop_register(inputs, ["rs2", "rs", "src"])
+ width = validate_part(inputs, ["funct3", "funct", "width"], STORE_WIDTHS, "width")
+ base = validate_reg(inputs, ["rs1", "rs", "base"])
+ offset = pop_immediate(inputs, 12, ["imm", "off"])
+ src = validate_reg(inputs, ["rs2", "rs", "src"])
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ validate_empty(inputs)
return [
(op[0], "s"),
+ width[:2],
base,
offset,
- width,
src,
]
-def validate_b(inputs):
+def validate_branch(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 = pop_immediate(inputs, [12, 13], allow_offset=True)
+ funct = validate_part(
+ inputs, ["funct3", "funct", "subop"], BRANCH_NAMES, "branch-op"
+ )
+ rs1 = validate_reg(inputs, ["rs1", "rs", "src1", "src"])
+ rs2 = validate_reg(inputs, ["rs2", "rs", "src2", "src"])
+ offset = pop_immediate(inputs, [12, 13], ["imm", "off"])
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ validate_empty(inputs)
return [
(op[0], "b"),
- funct,
+ funct[:2],
rs1,
rs2,
offset,
@@ -201,10 +449,10 @@ def validate_b(inputs):
def validate_u(inputs):
op = inputs.pop(0)
- rd = pop_register(inputs, ["rd", "dest"])
- imm = pop_immediate(inputs, 20, allow_offset=True)
+ rd = validate_reg(inputs, ["rd", "dest"])
+ imm = pop_immediate(inputs, 20, ["imm", "off"])
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ validate_empty(inputs)
return [
(op[0], "u"),
@@ -216,10 +464,10 @@ def validate_u(inputs):
def validate_j(inputs):
op = inputs.pop(0)
- dest = pop_register(inputs, ["rd", "dest"])
- offset = pop_immediate(inputs, [21, 20], allow_offset=True)
+ dest = validate_reg(inputs, ["rd", "dest"])
+ offset = pop_immediate(inputs, [21, 20], ["imm", "off"])
- assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs))
+ validate_empty(inputs)
return [
(op[0], "j"),
@@ -229,13 +477,13 @@ def validate_j(inputs):
instr_map = {
- "opr": (validate_r, 0x33),
+ "opr": (validate_opr, 0x33),
"opi": (validate_i, 0x13),
- "load": (validate_i, 0x03),
- "jalr": (validate_i, 0x67),
- "system": (validate_i, 0x73),
- "store": (validate_s, 0x23),
- "branch": (validate_b, 0x63),
+ "jalr": (validate_jalr, 0x67),
+ "system": (validate_csr, 0x73),
+ "load": (validate_load, 0x03),
+ "store": (validate_store, 0x23),
+ "branch": (validate_branch, 0x63),
"lui": (validate_u, 0x37),
"auipc": (validate_u, 0x17),
"jal": (validate_j, 0x6F),