#!/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/imm12 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/imm12 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/imm12 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/imm12 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/imm12 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/imm12 2/funct3 6/rs2 # jump back up to the top 6f/j 0/rd main/off21 """ import subv 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)) def pop_immediate(line, sizes, allow_offset=False): if isinstance(sizes, int): sizes = [sizes] for part in line: imm = subv.parse_immediate(part[1]) if imm and imm["size"] in sizes: line.remove(part) if not isinstance(part[0], str): imm["mode"] = "imm" return (part[0], subv.format_immediate(imm)) raise ValueError( "Expected an immediate with size {}".format("/".join(str(s) for s in sizes)) ) 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) 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_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_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 = pop_immediate(inputs, 12) assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs)) return [ (op[0], "i"), dest, funct, rs, imm, ] def validate_s(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"]) assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs)) return [ (op[0], "s"), base, offset, width, src, ] 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 = pop_immediate(inputs, [12, 13], allow_offset=True) assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs)) return [ (op[0], "b"), funct, rs1, rs2, offset, ] def validate_u(inputs): op = inputs.pop(0) rd = pop_register(inputs, ["rd", "dest"]) imm = pop_immediate(inputs, 20, allow_offset=True) assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs)) return [ (op[0], "u"), rd, imm, ] def validate_j(inputs): op = inputs.pop(0) dest = pop_register(inputs, ["rd", "dest"]) offset = pop_immediate(inputs, [21, 20], allow_offset=True) assert len(inputs) == 0, ValueError("Extra arguments: {}".format(inputs)) return [ (op[0], "j"), dest, offset, ] instr_map = { "opr": (validate_r, 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), "lui": (validate_u, 0x37), "auipc": (validate_u, 0x17), "jal": (validate_j, 0x6F), } @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)