#let colors = ( address: rgb(228, 103, 103), label: rgb(119, 97, 204), op: rgb(235, 105, 40) ) #let opcodes = ( "lui": 0b0110111, "auipc": 0b0010111, "jal": 0b1101111, "jalr": 0b1100111, "beq": 0b1100011, "bne": 0b1100011, "blt": 0b1100011, "bge": 0b1100011, "bltu": 0b1100011, "bgeu": 0b1100011, "lb": 0b0000011, "lh": 0b0000011, "lw": 0b0000011, "lbu": 0b0000011, "lhu": 0b0000011, "sb": 0b0100011, "sh": 0b0100011, "sw": 0b0100011, "addi": 0b0010011, "slti": 0b0010011, "sltiu": 0b0010011, "xori": 0b0010011, "ori": 0b0010011, "andi": 0b0010011, "slli": 0b0010011, "srli": 0b0010011, "srai": 0b0010011, "add": 0b0110011, "sub": 0b0110011, "sll": 0b0110011, "slt": 0b0110011, "sltu": 0b0110011, "xor": 0b0110011, "srl": 0b0110011, "sra": 0b0110011, "or": 0b0110011, "and": 0b0110011, ) #let types = ( "lui": "U", "auipc": "U", "jal": "J", "jalr": "I", "beq": "B", "bne": "B", "blt": "B", "bge": "B", "bltu": "B", "bgeu": "B", "lb": "L", "lh": "L", "lw": "L", "lbu": "L", "lhu": "L", "sb": "S", "sh": "S", "sw": "S", "addi": "I", "slti": "I", "sltiu": "I", "xori": "I", "ori": "I", "andi": "I", "slli": "I", "srli": "I", "srai": "I", "add": "R", "sub": "R", "sll": "R", "slt": "R", "sltu": "R", "xor": "R", "srl": "R", "sra": "R", "or": "R", "and": "R", ) #let funct3 = ( "jalr": 0b000, "beq": 0b000, "bne": 0b001, "blt": 0b100, "bge": 0b101, "bltu": 0b110, "bgeu": 0b111, "lb": 0b000, "lh": 0b001, "lw": 0b010, "lbu": 0b100, "lhu": 0b101, "sb": 0b000, "sh": 0b001, "sw": 0b010, "addi": 0b000, "slti": 0b010, "sltiu": 0b011, "xori": 0b100, "ori": 0b110, "andi": 0b111, "slli": 0b001, "srli": 0b101, "srai": 0b101, "add": 0b000, "sub": 0b000, "sll": 0b001, "slt": 0b010, "sltu": 0b011, "xor": 0b100, "srl": 0b101, "sra": 0b101, "or": 0b110, "and": 0b111, ) #let funct7 = ( "slli": 0b0000000, "srli": 0b0000000, "srai": 0b0100000, "add": 0b0000000, "sub": 0b0100000, "sll": 0b0000000, "slt": 0b0000000, "sltu": 0b0000000, "xor": 0b0000000, "srl": 0b0000000, "sra": 0b0100000, "or": 0b0000000, "and": 0b0000000, ) #let regs = ( "zero": 0, "ra": 1, "sp": 2, "gp": 3, "tp": 4, "t0": 5, "t1": 6, "t2": 7, "s0": 8, "fp": 8, "s1": 9, "a0": 10, "a1": 11, "a2": 12, "a3": 13, "a4": 14, "a5": 15, "a6": 16, "a7": 17, "s2": 18, "s3": 19, "s4": 20, "s5": 21, "s6": 22, "s7": 23, "s8": 24, "s9": 25, "s10": 26, "s11": 27, "t3": 28, "t4": 29, "t5": 30, "t6": 31 ) #let zfill(string, length, c: "0") = { let res = c * length + string return res.slice(-length) } #let int-to-bin(value, bits) = { if value < 0 { let m = (1).bit-lshift(bits) - 1 let v = str((-value).bit-xor(m) + 1, base: 2) return zfill(v, bits, c: "1") } let v = str(value, base: 2) return zfill(v, bits) } #let parse-reg(value) = { let number = value.last() if not value.starts-with("x") { number = regs.at(value) } return int(number) } #let parse-imm(value) = { let imm = eval(value) assert(type(imm) == int) return imm } #let parse-addr(value) = { let matches = value.match(regex("(.*)\((.*)\)")) let imm = matches.captures.first() let reg = matches.captures.last() imm = parse-imm(imm) reg = parse-reg(reg) return (reg, imm) } #let parse-args(instr, instr-type, args) = { let parsed = (:) let r1 = parse-reg(args.first()) if instr-type in ("R", "I", "L", "U", "J") { parsed.insert("rd", r1) } else if instr-type == "B" { parsed.insert("rs1", r1) } else if instr-type == "S" { parsed.insert("rs2", r1) } let r2 = none let r3 = none let imm = none if instr-type == "R" { r2 = parse-reg(args.at(1)) r3 = parse-reg(args.at(2)) } else if instr-type in ("I", "B") { r2 = parse-reg(args.at(1)) imm = parse-imm(args.at(2)) } else if instr-type in ("U", "J") { imm = parse-imm(args.at(1)) } else if instr-type in ("L", "S") { (r2, imm) = parse-addr(args.at(1)) } if instr-type == "R" { parsed.insert("rs1", r2) parsed.insert("rs2", r3) } else if instr-type in ("I", "L", "S") { parsed.insert("rs1", r2) parsed.insert("imm", imm) } else if instr-type == "B" { parsed.insert("rs2", r2) parsed.insert("imm", imm) } else if instr-type in ("U", "J") { parsed.insert("imm", imm) } return parsed } #let format-imm(imm, sections) = { let value = 0 for section in sections { if type(section) == int { let bit = imm.bit-rshift(section).bit-and(1) value = value.bit-lshift(1).bit-or(bit) } else { let (start, end) = section if start > end { (start, end) = (end, start) } let span = end - start + 1 let mask = (1).bit-lshift(span) - 1 let bits = imm.bit-rshift(start).bit-and(mask) value = value.bit-lshift(span).bit-or(bits) } } return value } #let instr-to-bin(instruction) = { let (instr, ..args) = instruction.split(" ") args = args.join("").split(",").map(a => a.trim()) instr = lower(instr) let parts = () let parts-names = () let opcode = opcodes.at(instr) let instr-type = types.at(instr) let code = 0 let parsed-args = parse-args(instr, instr-type, args) let imm = parsed-args.at("imm", default: none) let add-part(code, parts, part, bits) = { code = code.bit-lshift(bits).bit-or(part) parts.push(int-to-bin(part, bits)) return (code, parts) } if instr in ("slli", "srli", "srai") { let f7 = funct7.at(instr) (code, parts) = add-part(code, parts, f7, 7) parts-names.push("funct7") let v = format-imm(imm, ((4,0),)) (code, parts) = add-part(code, parts, v, 5) parts-names.push("shamt") } else if instr-type == "R" { let v = funct7.at(instr) parts-names.push("funct7") (code, parts) = add-part(code, parts, v, 7) } else if instr-type in ("I", "L") { let v = format-imm(imm, ((11,0),)) (code, parts) = add-part(code, parts, v, 12) parts-names.push("imm[11:0]") } else if instr-type == "S" { let v = format-imm(imm, ((11, 5),)) (code, parts) = add-part(code, parts, v, 7) parts-names.push("imm[11:5]") } else if instr-type == "B" { let v = format-imm(imm, (12, (10, 5))) (code, parts) = add-part(code, parts, v, 7) parts-names.push("imm[12|10:5]") } else if instr-type == "U" { let v = format-imm(imm, ((19, 0),)) (code, parts) = add-part(code, parts, v, 20) parts-names.push("imm[31:12]") } else if instr-type == "J" { let v = format-imm(imm, (20, (10, 1), 11, (19, 12))) (code, parts) = add-part(code, parts, v, 20) parts-names.push("imm[20|10:1|11|19:12]") } if instr-type in ("R", "S", "B") { (code, parts) = add-part(code, parts, parsed-args.rs2, 5) parts-names.push("rs2") } if instr-type in ("R", "I", "L", "S", "B") { (code, parts) = add-part(code, parts, parsed-args.rs1, 5) parts-names.push("rs1") (code, parts) = add-part(code, parts, funct3.at(instr), 3) parts-names.push("funct3") } if instr-type in ("R", "I", "L", "U", "J") { (code, parts) = add-part(code, parts, parsed-args.rd, 5) parts-names.push("rd") } else if instr-type == "S" { let v = format-imm(imm, ((4, 0),)) (code, parts) = add-part(code, parts, v, 5) parts-names.push("imm[4:0]") } else if instr-type == "B" { let v = format-imm(imm, ((4, 1), 11)) (code, parts) = add-part(code, parts, v, 5) parts-names.push("imm[4:1|11]") } (code, parts) = add-part(code, parts, opcode, 7) parts-names.push("opcode") return ( instruction: instruction, parts: parts, names: parts-names, code: code ) } #let color-instruction(line) = { let res = "" if line.starts-with("0x") { let (addr, ..rest) = line.split(" ") line = rest.join(" ").trim() res += text(raw(addr), fill: colors.address) + " " } if ":" in line { let label (label, line) = line.split(":") line = line.trim() res += text(raw(label), fill: colors.label) + ": " if line.len() == 0 { return res } } let (op, ..args) = line.split(" ") args = args.join("").split(",").map(a => a.trim()) args = args.map(a => raw(a)) res += text(raw(op), fill: colors.op) + " " res += args.join(", ") return res } #let explain-instruction-encoding(instruction) = { let encoded = instr-to-bin(instruction) let parts = encoded.parts let names = encoded.names let code = encoded.code parts.zip(names).map(((v, n)) => $underbrace(#raw(v), #n)$).join(" ") [ \= #raw(instruction) \= 0x#raw(zfill(str(code, base: 16), 8))] } #let replace-label-refs(instr, labels, addr) = { let (op, ..args) = instr.split(" ") args = args.join("").split(",").map(a => a.trim()) if op in ("beq", "bne", "blt", "bge", "bltu", "bgeu", "jal") { let label = args.last() let target = labels.at(label) let offset = target - addr args.last() = str(offset) } return op + " " + args.join(", ") } #let riscv-to-bin(riscv, start-address: 0) = { let src = riscv if type(riscv) == raw or type(riscv) == content { src = riscv.text } let lines = src.split("\n") .map(l => l.split("#").first().trim()) .filter(l => l.len() != 0) let labels = (:) let addr = start-address let lines2 = () let addresses = () for line in lines { let set-addr = none if line.starts-with("0x") { let rest (set-addr, ..rest) = line.split(" ") line = rest.join(" ") addr = eval(set-addr) } if ":" in line { let label (label, line) = line.split(":") labels.insert(label, addr) line = line.trim() if line.len() == 0 { continue } } lines2.push(line.trim()) addresses.push(addr) if set-addr == none { addr += 4 } } let cells = () for (line, addr) in lines2.zip(addresses) { let instr = replace-label-refs(line, labels, addr) let encoded = instr-to-bin(instr) let addr-txt = "0x" + zfill(str(addr, base: 16), 8) let code-txt = "0x" + zfill(str(encoded.code, base: 16), 8) addr-txt = raw(addr-txt) addr-txt = text(addr-txt, fill: colors.address) let label = "" if addr in labels.values() { label = labels.pairs().filter(p => p.last() == addr).first().first() label += ":" } label = text(raw(label), fill: colors.label) cells.push(addr-txt) cells.push(label) cells.push(raw(code-txt)) cells.push(color-instruction(line)) } table( align: left + horizon, stroke: none, columns: 4, inset: 5pt, ..cells ) }