Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save leighleighleigh/141a27f3c4a36a0d72e8fda1142dbd69 to your computer and use it in GitHub Desktop.

Select an option

Save leighleighleigh/141a27f3c4a36a0d72e8fda1142dbd69 to your computer and use it in GitHub Desktop.
Custom instruction encoder script for ESP32-S3 RISCV ULP co-processor.
#!/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