diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-07-09 10:58:14 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-07-09 12:39:12 +0000 |
| commit | 6368fb586f3d5d70528915369397281ccfd00dbe (patch) | |
| tree | dc60783429b8f1c831649c5aa7804161883bc023 /validate.py | |
| parent | remove outdated disasm.sh (diff) | |
| download | subv-6368fb586f3d5d70528915369397281ccfd00dbe.tar.gz subv-6368fb586f3d5d70528915369397281ccfd00dbe.zip | |
wip more validation logic
Diffstat (limited to 'validate.py')
| -rwxr-xr-x | validate.py | 422 |
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), |
