from .instructions import Instruction, InstructionSet from ctypes import c_uint32 RV32I = InstructionSet() class R(Instruction): funct3 = None funct7 = None opcode = None def __init__(self, rd, rs1, rs2): self.rd = rd self.rs1 = rs1 self.rs2 = rs2 def compile(self): # TODO: ensure sizes and convert register names to number... return c_uint32( (self.funct7 << 25) +\ (self.rs2 << 20) +\ (self.rs1 << 15) +\ (self.funct3 << 12) +\ (self.rd << 7) +\ self.opcode ) class I(Instruction): funct3 = None opcode = None def __init__(self, rd, rs, imm): self.rd = rd self.rs = rs self.imm = imm def compile(self): return c_uint32( (self.imm << 20) +\ (self.rs << 15) +\ (self.funct3 << 12) +\ (self.rd << 7) +\ self.opcode ) class S(Instruction): funct3 = None opcode = None def __init__(self, rs1, rs2, imm): self.rs1 = rs1 self.rs2 = rs2 self.imm = imm def compile(self): imm_0_4 = self.imm & 0x1F imm_5_11 = (self.imm & 0xFE0)>>5 return c_uint32( (imm_5_11 << 25) +\ (self.rs2 << 20) +\ (self.rs1 << 15) +\ (self.funct3 << 12) +\ (imm_0_4 << 7) +\ self.opcode ) class B(Instruction): funct3 = None opcode = None def __init__(self, rs1, rs2, imm): self.rs1 = rs1 self.rs2 = rs2 self.imm = imm def compile(self): # NOTE: The lowest bit of the imm is always 0 because instructions # are at least 16 bits wide, so it's not used, that's why this # instruction looks that weird imm_11 = (self.imm & 0b0100000000000)>>11 imm_12 = (self.imm & 0b1000000000000)>>12 imm_1_4 = (self.imm & 0b0000000011110)>>1 imm_5_10 = (self.imm & 0b0011111100000)>>5 return c_uint32( (imm_12 << 30) +\ (imm_5_10 << 25) +\ (self.rs2 << 20) +\ (self.rs1 << 15) +\ (self.funct3 << 12) +\ (imm_1_4 << 8) +\ (imm_11 << 7) +\ self.opcode ) class U(Instruction): opcode = None def __init__(self, rd, imm): self.rd = rd self.imm = imm def compile(self): # NOTE: U Type is for AUIPC and LUI that only use the high part of the # immediate imm_12_32 = (self.imm & 0xFFFFF000)>>12 return c_uint32( (imm_12_32 << 12) +\ (self.rd << 7) +\ self.opcode ) class J(Instruction): opcode = None def __init__(self, rd, imm): self.rd = rd self.imm = imm def compile(self): # NOTE: Jumps are also weird imm_20 = (self.imm & 0x100000)>>20 imm_12_19 = (self.imm & 0x0FF000)>>12 imm_11 = (self.imm & 0x000800)>>8 imm_1_10 = (self.imm & 0x0007FE)>>1 return c_uint32( (imm_20 << 31) +\ (imm_1_10 << 21) +\ (imm_11 << 20) +\ (imm_12_19 << 12) +\ (self.rd << 7) +\ self.opcode ) @RV32I.instruction class lui(U): name = "lui" opcode = 0b0110111 @RV32I.instruction class auipc(U): name = "auipc" opcode = 0b0010111 @RV32I.instruction class jal(J): name = "jal" opcode = 0b1101111 def execute(self, pc): # TODO # - Save current pc in rd # - Make pc from `imm` # - Return new pc return pc @RV32I.instruction class jalr(I): name = "jalr" opcode = 0b1100111 funct3 = 0b000 @RV32I.instruction class beq(B): name = "beq" opcode = 0b1100011 funct3 = 0b000 @RV32I.instruction class bne(B): name = "bne" opcode = 0b1100011 funct3 = 0b001 @RV32I.instruction class blt(B): name = "blt" opcode = 0b1100011 funct3 = 0b100 @RV32I.instruction class bge(B): name = "bge" opcode = 0b1100011 funct3 = 0b101 @RV32I.instruction class bltu(B): name = "bltu" opcode = 0b1100011 funct3 = 0b110 @RV32I.instruction class bgeu(B): name = "bgeu" opcode = 0b1100011 funct3 = 0b111 @RV32I.instruction class lb(I): name = "lb" opcode = 0b0000011 funct3 = 0b000 @RV32I.instruction class lh(I): name = "lh" opcode = 0b0000011 funct3 = 0b001 @RV32I.instruction class lw(I): name = "lw" opcode = 0b0000011 funct3 = 0b010 @RV32I.instruction class lbu(I): name = "lbu" opcode = 0b0000011 funct3 = 0b100 @RV32I.instruction class lhu(I): name = "lhu" opcode = 0b0000011 funct3 = 0b101 @RV32I.instruction class sb(S): name = "sb" opcode = 0b0100011 funct3 = 0b000 @RV32I.instruction class sh(S): name = "sh" opcode = 0b0100011 funct3 = 0b001 @RV32I.instruction class sw(S): name = "sw" opcode = 0b0100011 funct3 = 0b010 @RV32I.instruction class addi(I): name = "addi" opcode = 0b0010011 funct3 = 0b000 def execute(self, pc): # TODO return pc + self.size @RV32I.instruction class slti(I): name = "slti" opcode = 0b0010011 funct3 = 0b010 @RV32I.instruction class sltiu(I): name = "sltiu" opcode = 0b0010011 funct3 = 0b011 @RV32I.instruction class xori(I): name = "xori" opcode = 0b0010011 funct3 = 0b100 @RV32I.instruction class ori(I): name = "ori" opcode = 0b0010011 funct3 = 0b110 @RV32I.instruction class andi(I): name = "andi" opcode = 0b0010011 funct3 = 0b111 class ShiftImm(I): # NOTE: This is an special type used for shifting operations because they # have 7 bits left after the maximum shift (5bits -> 32 rotations) # they can apply. # In RV64I they can indicate rotation with 1 bit more (64 rotations) so # they use a funct6 instead. funct7 = None funct3 = None opcode = None def __init__(self, rd, rs, imm): self.rd = rd self.rs = rs self.imm = imm def compile(self): return c_uint32( (self.funct7 << 25) +\ (self.imm << 20) +\ (self.rs << 15) +\ (self.funct3 << 12) +\ (self.rd << 7) +\ self.opcode ) @RV32I.instruction class slli(ShiftImm): name = "slli" opcode = 0b0010011 funct3 = 0b001 funct7 = 0b0000000 @RV32I.instruction class srli(ShiftImm): name = "srli" opcode = 0b0010011 funct3 = 0b101 funct7 = 0b0000000 @RV32I.instruction class srai(ShiftImm): name = "srai" opcode = 0b0010011 funct3 = 0b101 funct7 = 0b0100000 @RV32I.instruction class add(R): name = "add" opcode = 0b0110011 funct3 = 0b000 funct7 = 0b0000000 def execute(self, pc): # TODO return pc + self.size @RV32I.instruction class sub(R): name = "sub" opcode = 0b0110011 funct3 = 0b000 funct7 = 0b0100000 @RV32I.instruction class sll(R): name = "sll" opcode = 0b0110011 funct3 = 0b001 funct7 = 0b0000000 @RV32I.instruction class slt(R): name = "slt" opcode = 0b0110011 funct3 = 0b010 funct7 = 0b0000000 @RV32I.instruction class sltu(R): name = "sltu" opcode = 0b0110011 funct3 = 0b011 funct7 = 0b0000000 @RV32I.instruction class xor(R): name = "xor" opcode = 0b0110011 funct3 = 0b100 funct7 = 0b0000000 @RV32I.instruction class srl(R): name = "srl" opcode = 0b0110011 funct3 = 0b101 funct7 = 0b0000000 @RV32I.instruction class sra(R): name = "sra" opcode = 0b0110011 funct3 = 0b101 funct7 = 0b0100000 @RV32I.instruction class _or(R): name = "or" opcode = 0b0110011 funct3 = 0b110 funct7 = 0b0000000 @RV32I.instruction class _and(R): name = "and" opcode = 0b0110011 funct3 = 0b111 funct7 = 0b0000000 @RV32I.instruction class ecall(I): name = "ecall" opcode = 0b1110011 funct3 = 0b000 def __init__(self): # NOTE: ecall is a I-type instruction but doesn't get any arg and sets # every field to 0 self.rd = 0b00000 self.rs = 0b00000 self.imm = 0b000000000000 @RV32I.instruction class ebreak(I): name = "ebreak" opcode = 0b1110011 funct3 = 0b000 def __init__(self): # NOTE: ebreak is a I-type instruction but doesn't get any arg and pre- # -sets every field to a fixed value self.rd = 0b00000 self.rs = 0b00000 self.imm = 0b000000000001 @RV32I.pseudoinstruction class j(J): name = "j" def __new__(cls, imm): return jal("x0", imm) if __name__ == "__main__": print(RV32I) print(RV32I.instructions)