Skip to content

Commit

Permalink
Refactor RISC-V instruction models
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Urbanczyk committed Mar 27, 2024
1 parent 6128533 commit a4ff061
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 158 deletions.
2 changes: 1 addition & 1 deletion coreblocks/frontend/decoder/rvc.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def _quadrant_2(self) -> list[DecodedInstr]:
shamt = Cat(self.instr_in[2:7], self.instr_in[12])
ldsp_imm = Cat(C(0, 3), self.instr_in[5:7], self.instr_in[12], self.instr_in[2:5], C(0, 3))
lwsp_imm = Cat(C(0, 2), self.instr_in[4:7], self.instr_in[12], self.instr_in[2:4], C(0, 4))
sdsp_imm = Cat(C(0, 3), self.instr_in[10:13], self.instr_in[7:10], C(0, 2))
sdsp_imm = Cat(C(0, 3), self.instr_in[10:13], self.instr_in[7:10], C(0, 3))
swsp_imm = Cat(C(0, 2), self.instr_in[9:13], self.instr_in[7:9], C(0, 4))

slli = (
Expand Down
289 changes: 158 additions & 131 deletions coreblocks/params/instr.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
from abc import abstractmethod, ABC
"""
Based on riscv-python-model by Stefan Wallentowitz
https://github.com/wallento/riscv-python-model
"""

from dataclasses import dataclass
from abc import ABC
from enum import Enum
from typing import Optional

from amaranth.hdl import ValueCastable
from amaranth import *

from transactron.utils import ValueLike, int_to_signed
from transactron.utils import ValueLike
from coreblocks.params.isa_params import *
from coreblocks.frontend.decoder.isa import *


__all__ = [
"RISCVInstr",
"RTypeInstr",
"ITypeInstr",
"STypeInstr",
Expand All @@ -20,154 +30,171 @@
]


@dataclass(frozen=True, kw_only=True)
class Field:
name: str
base: int | list[int]
size: int | list[int]

signed: bool = False
offset: int = 0
static_value: Optional[Value] = None

def get_base(self) -> list[int]:
if isinstance(self.base, int):
return [self.base]
return self.base

def get_size(self) -> list[int]:
if isinstance(self.size, int):
return [self.size]
return self.size


class RISCVInstr(ABC, ValueCastable):
@abstractmethod
def pack(self) -> Value:
pass
field_opcode = Field(name="opcode", base=0, size=7)

def __init__(self, **kwargs):
for field in kwargs:
fname = "field_" + field
assert fname in dir(self), "Invalid field {} for {}".format(fname, self.__name__)
setattr(self, field, kwargs[field])

@classmethod
def get_fields(cls) -> list[Field]:
return [getattr(cls, member) for member in dir(cls) if member.startswith("field_")]

def encode(self) -> int:
const = Const.cast(self.as_value())
return const.value # type: ignore

def __setattr__(self, key, value):
fname = "field_{}".format(key)

if fname not in dir(self):
super().__setattr__(key, value)
return

field = getattr(self, fname)
if field.static_value is not None:
raise AttributeError("Can't overwrite the static value of a field.")

expected_shape = Shape(width=sum(field.get_size()) + field.offset, signed=field.signed)

field_val: Value = C(0)
if isinstance(value, Enum):
field_val = Const(value.value, expected_shape)
elif isinstance(value, int):
field_val = Const(value, expected_shape)
else:
field_val = Value.cast(value)

if field_val.shape().width != expected_shape.width:
raise AttributeError(
f"Expected width of the value: {expected_shape.width}, given: {field_val.shape().width}"
)
if field_val.shape().signed and not expected_shape.signed:
raise AttributeError(
f"Expected signedness of the value: {expected_shape.signed}, given: {field_val.shape().signed}"
)

self.__dict__[key] = field_val

@ValueCastable.lowermethod
def as_value(self):
return self.pack()
def as_value(self) -> Value:
parts: list[tuple[int, Value]] = []

for field in self.get_fields():
value: Value = C(0)
if field.static_value is not None:
value = field.static_value
else:
value = getattr(self, field.name)

base = field.get_base()
size = field.get_size()

def shape(self):
offset = field.offset
for i in range(len(base)):
parts.append((base[i], value[offset : offset + size[i]]))
offset += size[i]

parts.sort()
return Cat([part[1] for part in parts])

def shape(self) -> Shape:
return self.as_value().shape()


class RTypeInstr(RISCVInstr):
def __init__(
self,
opcode: ValueLike,
rd: ValueLike,
funct3: ValueLike,
rs1: ValueLike,
rs2: ValueLike,
funct7: ValueLike,
):
self.opcode = Value.cast(opcode)
self.rd = Value.cast(rd)
self.funct3 = Value.cast(funct3)
self.rs1 = Value.cast(rs1)
self.rs2 = Value.cast(rs2)
self.funct7 = Value.cast(funct7)

def pack(self) -> Value:
return Cat(C(0b11, 2), self.opcode, self.rd, self.funct3, self.rs1, self.rs2, self.funct7)

@staticmethod
def encode(opcode: int, rd: int, funct3: int, rs1: int, rs2: int, funct7: int):
return int(f"{funct7:07b}{rs2:05b}{rs1:05b}{funct3:03b}{rd:05b}{opcode:05b}11", 2)


class ITypeInstr(RISCVInstr):
def __init__(self, opcode: ValueLike, rd: ValueLike, funct3: ValueLike, rs1: ValueLike, imm: ValueLike):
self.opcode = Value.cast(opcode)
self.rd = Value.cast(rd)
self.funct3 = Value.cast(funct3)
self.rs1 = Value.cast(rs1)
self.imm = Value.cast(imm)

def pack(self) -> Value:
return Cat(C(0b11, 2), self.opcode, self.rd, self.funct3, self.rs1, self.imm)

@staticmethod
def encode(opcode: int, rd: int, funct3: int, rs1: int, imm: int):
imm = int_to_signed(imm, 12)
return int(f"{imm:012b}{rs1:05b}{funct3:03b}{rd:05b}{opcode:05b}11", 2)


class STypeInstr(RISCVInstr):
def __init__(self, opcode: ValueLike, imm: ValueLike, funct3: ValueLike, rs1: ValueLike, rs2: ValueLike):
self.opcode = Value.cast(opcode)
self.imm = Value.cast(imm)
self.funct3 = Value.cast(funct3)
self.rs1 = Value.cast(rs1)
self.rs2 = Value.cast(rs2)

def pack(self) -> Value:
return Cat(C(0b11, 2), self.opcode, self.imm[0:5], self.funct3, self.rs1, self.rs2, self.imm[5:12])

@staticmethod
def encode(opcode: int, imm: int, funct3: int, rs1: int, rs2: int):
imm = int_to_signed(imm, 12)
imm_str = f"{imm:012b}"
return int(f"{imm_str[5:12]:07b}{rs2:05b}{rs1:05b}{funct3:03b}{imm_str[0:5]:05b}{opcode:05b}11", 2)


class BTypeInstr(RISCVInstr):
def __init__(self, opcode: ValueLike, imm: ValueLike, funct3: ValueLike, rs1: ValueLike, rs2: ValueLike):
self.opcode = Value.cast(opcode)
self.imm = Value.cast(imm)
self.funct3 = Value.cast(funct3)
self.rs1 = Value.cast(rs1)
self.rs2 = Value.cast(rs2)

def pack(self) -> Value:
return Cat(
C(0b11, 2),
self.opcode,
self.imm[11],
self.imm[1:5],
self.funct3,
self.rs1,
self.rs2,
self.imm[5:11],
self.imm[12],
)
class InstructionFunct3Type(RISCVInstr):
field_funct3 = Field(name="funct3", base=12, size=3)

@staticmethod
def encode(opcode: int, imm: int, funct3: int, rs1: int, rs2: int):
imm = int_to_signed(imm, 13)
imm_str = f"{imm:013b}"
return int(
f"{imm_str[12]:01b}{imm_str[5:11]:06b}{rs2:05b}{rs1:05b}{funct3:03b}{imm_str[1:5]:04b}"
+ f"{imm_str[11]:01b}{opcode:05b}11",
2,
)

class InstructionFunct5Type(RISCVInstr):
field_funct5 = Field(name="funct5", base=27, size=5)

class UTypeInstr(RISCVInstr):
def __init__(self, opcode: ValueLike, rd: ValueLike, imm: ValueLike):
self.opcode = Value.cast(opcode)
self.rd = Value.cast(rd)
self.imm = Value.cast(imm)

def pack(self) -> Value:
return Cat(C(0b11, 2), self.opcode, self.rd, self.imm[12:])
class InstructionFunct7Type(RISCVInstr):
field_funct7 = Field(name="funct7", base=25, size=7)


class RTypeInstr(InstructionFunct3Type, InstructionFunct7Type):
field_rd = Field(name="rd", base=7, size=5)
field_rs1 = Field(name="rs1", base=15, size=5)
field_rs2 = Field(name="rs2", base=20, size=5)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)

@staticmethod
def encode(opcode: int, rd: int, imm: int):
imm = int_to_signed(imm, 20)
return int(f"{imm:020b}{rd:05b}{opcode:05b}11", 2)

class ITypeInstr(InstructionFunct3Type):
field_rd = Field(name="rd", base=7, size=5)
field_rs1 = Field(name="rs1", base=15, size=5)
field_imm = Field(name="imm", base=20, size=12, signed=True)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)


class STypeInstr(InstructionFunct3Type):
field_rs1 = Field(name="rs1", base=15, size=5)
field_rs2 = Field(name="rs2", base=20, size=5)
field_imm = Field(name="imm", base=[7, 25], size=[5, 7], signed=True)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)


class BTypeInstr(InstructionFunct3Type):
field_rs1 = Field(name="rs1", base=15, size=5)
field_rs2 = Field(name="rs2", base=20, size=5)
field_imm = Field(name="imm", base=[8, 25, 7, 31], size=[4, 6, 1, 1], offset=1, signed=True)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)


class UTypeInstr(RISCVInstr):
field_rd = Field(name="rd", base=7, size=5)
field_imm = Field(name="imm", base=12, size=20, offset=12, signed=False)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)


class JTypeInstr(RISCVInstr):
def __init__(self, opcode: ValueLike, rd: ValueLike, imm: ValueLike):
self.opcode = Value.cast(opcode)
self.rd = Value.cast(rd)
self.imm = Value.cast(imm)

def pack(self) -> Value:
return Cat(C(0b11, 2), self.opcode, self.rd, self.imm[12:20], self.imm[11], self.imm[1:11], self.imm[20])

@staticmethod
def encode(opcode: int, rd: int, imm: int):
imm = int_to_signed(imm, 21)
imm_str = f"{imm:021b}"
return int(
f"{imm_str[20]:01b}{imm_str[1:11]:010b}{imm_str[11]:01b}{imm_str[12:20]:08b}{rd:05b}{opcode:05b}11", 2
)
field_rd = Field(name="rd", base=7, size=5)
field_imm = Field(name="imm", base=[21, 20, 12, 31], size=[10, 1, 8, 1], offset=1, signed=True)

def __init__(self, opcode: ValueLike, **kwargs):
super().__init__(opcode=Cat(C(0b11, 2), opcode), **kwargs)

class IllegalInstr(RISCVInstr):
def __init__(self):
pass

def pack(self) -> Value:
return C(1).replicate(32) # Instructions with all bits set to 1 are reserved to be illegal.
class IllegalInstr(RISCVInstr):
field_illegal = Field(name="illegal", base=7, size=25, static_value=Cat(1).replicate(25))

@staticmethod
def encode(opcode: int, rd: int, imm: int):
return int("1" * 32, 2)
def __init__(self):
super().__init__(opcode=0b1111111)


class EBreakInstr(ITypeInstr):
Expand Down
Loading

0 comments on commit a4ff061

Please sign in to comment.