Created
March 2, 2026 11:44
-
-
Save leighleighleigh/141a27f3c4a36a0d72e8fda1142dbd69 to your computer and use it in GitHub Desktop.
Custom instruction encoder script for ESP32-S3 RISCV ULP co-processor.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| /** | |
| * This header file defines custom instructions for interrupt handling on the | |
| * ULP RISC-V. The architecture of the processor and therefore, the interrupt | |
| * handling is based on the PicoRV32 CPU. Details about the operations are | |
| * available at https://github.com/YosysHQ/picorv32#custom-instructions-for-irq-handling | |
| */ | |
| /* Define encoding for all general purpose RISC-V registers */ | |
| #define regnum_zero 0 | |
| #define regnum_ra 1 | |
| #define regnum_sp 2 | |
| #define regnum_gp 3 | |
| #define regnum_tp 4 | |
| #define regnum_t0 5 | |
| #define regnum_t1 6 | |
| #define regnum_t2 7 | |
| #define regnum_s0 8 | |
| #define regnum_s1 9 | |
| #define regnum_a0 10 | |
| #define regnum_a1 11 | |
| #define regnum_a2 12 | |
| #define regnum_a3 13 | |
| #define regnum_a4 14 | |
| #define regnum_a5 15 | |
| #define regnum_a6 16 | |
| #define regnum_a7 17 | |
| #define regnum_s2 18 | |
| #define regnum_s3 19 | |
| #define regnum_s4 20 | |
| #define regnum_s5 21 | |
| #define regnum_s6 22 | |
| #define regnum_s7 23 | |
| #define regnum_s8 24 | |
| #define regnum_s9 25 | |
| #define regnum_s10 26 | |
| #define regnum_s11 27 | |
| #define regnum_t3 28 | |
| #define regnum_t4 29 | |
| #define regnum_t5 30 | |
| #define regnum_t6 31 | |
| /* Define encoding for special interrupt handling registers, viz., q0, q1, q2 and q3 */ | |
| #define regnum_q0 0 | |
| #define regnum_q1 1 | |
| #define regnum_q2 2 | |
| #define regnum_q3 3 | |
| /* All custom interrupt handling instructions follow the standard R-type instruction format from RISC-V ISA | |
| * with the same opcode of custom0 (0001011). | |
| */ | |
| #define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ | |
| .word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) | |
| /** | |
| * Instruction: getq rd, qs | |
| * Description: This instruction copies the value of Qx into a general purpose register rd | |
| */ | |
| #define getq_insn(_rd, _qs) \ | |
| r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) | |
| /** | |
| * Instruction: setq qd, rs | |
| * Description: This instruction copies the value of general purpose register rs to Qx | |
| */ | |
| #define setq_insn(_qd, _rs) \ | |
| r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) | |
| /** | |
| * Instruction: retirq | |
| * Description: This instruction copies the value of Q0 to CPU PC, and renables interrupts | |
| */ | |
| #define retirq_insn() \ | |
| r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) | |
| /** | |
| * Instruction: maskirq rd, rs | |
| * Description: This instruction copies the value of the register IRQ Mask to the register rd, and copies the value | |
| * of register rs to to IRQ mask. | |
| */ | |
| #define maskirq_insn(_rd, _rs) \ | |
| r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) | |
| """ | |
| from argparse import ArgumentParser | |
| from enum import Enum, IntEnum, StrEnum | |
| class Register(IntEnum): | |
| zero = 0 | |
| ra = 1 | |
| sp = 2 | |
| gp = 3 | |
| tp = 4 | |
| t0 = 5 | |
| t1 = 6 | |
| t2 = 7 | |
| s0 = 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 | |
| def __str__(self) -> str: | |
| return self.name.replace("Register", "") | |
| class QRegister(IntEnum): | |
| q0 = 0 | |
| q1 = 1 | |
| q2 = 2 | |
| q3 = 3 | |
| def __str__(self) -> str: | |
| return self.name.replace("QRegister", "") | |
| def parse_reg(val: str): | |
| return Register._member_map_[val] | |
| def parse_qreg(val: str): | |
| return QRegister._member_map_[val] | |
| def r_type_insn(f7: int, rs2: int, rs1: int, f3: int, rd: int, opc: int) -> str: | |
| return f".word 0x{(((f7) << 25) | ((rs2) << 20) | ((rs1) << 15) | ((f3) << 12) | ((rd) << 7) | ((opc) << 0)):08x}" | |
| def getq_insn(rd: Register, qs: QRegister) -> str: | |
| return r_type_insn(0b0000000, 0, qs.value, 0b100, rd.value, 0b0001011) | |
| def setq_insn(qd: QRegister, rs: Register) -> str: | |
| return r_type_insn(0b0000001, 0, rs.value, 0b010, qd.value, 0b0001011) | |
| def retirq_insn() -> str: | |
| return r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) | |
| def maskirq_insn(rd : Register, rs : QRegister) -> str: | |
| return r_type_insn(0b0000011, 0, rs.value, 0b110, rd.value, 0b0001011) | |
| if __name__ == "__main__": | |
| parser = ArgumentParser() | |
| subparsers = parser.add_subparsers(help="Instruction name",required=True,dest="instr") | |
| getq_parser = subparsers.add_parser("getq") | |
| setq_parser = subparsers.add_parser("setq") | |
| retirq_parser = subparsers.add_parser("retirq") | |
| maskirq_parser = subparsers.add_parser("maskirq") | |
| getq_parser.add_argument("rd", type=parse_reg, choices=list(Register)) | |
| getq_parser.add_argument("qs", type=parse_qreg, choices=list(QRegister)) | |
| setq_parser.add_argument("qd", type=parse_qreg, choices=list(QRegister)) | |
| setq_parser.add_argument("rs", type=parse_reg, choices=list(Register)) | |
| maskirq_parser.add_argument("rd", type=parse_reg, choices=list(Register)) | |
| maskirq_parser.add_argument("rs", type=parse_reg, choices=list(Register)) | |
| opts = parser.parse_args() | |
| print(opts) | |
| match opts.instr: | |
| case "getq": | |
| print(getq_insn(opts.rd, opts.qs)) | |
| case "setq": | |
| print(setq_insn(opts.qd, opts.rs)) | |
| case "retirq": | |
| print(retirq_insn()) | |
| case "maskirq": | |
| print(maskirq_insn(opts.rd,opts.rs)) | |
| case _: | |
| pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment