cva6/perf-model/isa.py
Côme 7b3054156e
Some checks failed
bender-up-to-date / bender-up-to-date (push) Has been cancelled
ci / build-riscv-tests (push) Has been cancelled
ci / execute-riscv64-tests (push) Has been cancelled
ci / execute-riscv32-tests (push) Has been cancelled
Add CVA6 performance model (#2880)
2025-03-28 14:50:12 +01:00

574 lines
16 KiB
Python

# Copyright 2024 Thales Silicon Security
#
# Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
# You may obtain a copy of the License at https://solderpad.org/licenses/
#
# Original Author: Côme ALLART - Thales
"""
Represents the instruction set
"""
from dataclasses import dataclass
class Reg:
"""Constants to represent registers"""
# ABI names
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
# Register names
x0 = 0
x1 = 1
x2 = 2
x3 = 3
x4 = 4
x5 = 5
x6 = 6
x7 = 7
x8 = 8
x9 = 9
x10 = 10
x11 = 11
x12 = 12
x13 = 13
x14 = 14
x15 = 15
x16 = 16
x17 = 17
x18 = 18
x19 = 19
x20 = 20
x21 = 21
x22 = 22
x23 = 23
x24 = 24
x25 = 25
x26 = 26
x27 = 27
x28 = 28
x29 = 29
x30 = 30
x31 = 31
def sign_ext(imm, index, xlen=32):
"""
Sign extends a value
imm: value to sign extend
index: index of the sign bit of the value
len: target len for sign extended value
"""
imm_bits = index + 1
assert (imm >> imm_bits) == 0
neg = imm >> index
sext_bits = xlen - imm_bits
sext_ones = (1 << sext_bits) - 1
sext = neg * sext_ones << imm_bits
return sext | imm
@dataclass
class AddrFields:
"""Represents the data used to build a memory address"""
base_reg: int
offset: int
class Rtype:
"""R-type instructions"""
def __init__(self, instr):
self.funct7 = instr.bin >> 25
self.rs2 = (instr.bin >> 20) & 31
self.rs1 = (instr.bin >> 15) & 31
self.funct3 = (instr.bin >> 12) & 7
self.rd = (instr.bin >> 7) & 31
self.opcode = instr.bin & 63
class Itype:
"""I-type instructions"""
def __init__(self, instr):
self.rs1 = (instr.bin >> 15) & 31
self.funct3 = (instr.bin >> 12) & 7
self.rd = (instr.bin >> 7) & 31
self.opcode = instr.bin & 63
self.imm = sign_ext(instr.bin >> 20, 11)
class Stype:
"""S-type instructions"""
def __init__(self, instr):
self.rs2 = (instr.bin >> 20) & 31
self.rs1 = (instr.bin >> 15) & 31
self.funct3 = (instr.bin >> 12) & 7
self.opcode = instr.bin & 63
self.imm = sign_ext(
((instr.bin >> 25) << 5) \
| ((instr.bin >> 7) & 31)
, 11)
class Btype:
"""B-type instructions"""
def __init__(self, instr):
self.rs2 = (instr.bin >> 20) & 31
self.rs1 = (instr.bin >> 15) & 31
self.funct3 = (instr.bin >> 12) & 7
self.opcode = instr.bin & 63
self.imm = sign_ext(
((instr.bin >> 31) << 12) \
| (((instr.bin >> 7) & 1) << 11) \
| (((instr.bin >> 25) & 0x3f) << 5) \
| (((instr.bin >> 8) & 15) << 1)
, 12)
class Utype:
"""U-type instructions"""
def __init__(self, instr):
self.imm_31_12 = instr.bin >> 12
self.imm_4_0 = (instr.bin >> 7) & 31
self.rd = (instr.bin >> 7) & 31
self.opcode = instr.bin & 63
self.imm = self.imm_31_12 << 12
class Jtype:
"""J-type instructions"""
def __init__(self, instr):
self.rd = (instr.bin >> 7) & 31
self.opcode = instr.bin & 63
self.imm = sign_ext(
((instr.bin >> 31) << 20) \
| (((instr.bin >> 12) & 0xff) << 12) \
| (((instr.bin >> 20) & 1) << 11) \
| (((instr.bin >> 21) & 0x3ff) << 1)
, 20)
class MOItype:
"""Memory ordering instructions"""
def __init__(self, instr):
self.fm = instr.bin >> 28
self.PI = (instr.bin >> 27) & 1
self.PO = (instr.bin >> 26) & 1
self.PR = (instr.bin >> 25) & 1
self.PW = (instr.bin >> 24) & 1
self.SI = (instr.bin >> 23) & 1
self.SO = (instr.bin >> 22) & 1
self.SR = (instr.bin >> 21) & 1
self.SW = (instr.bin >> 20) & 1
self.rs1 = (instr.bin >> 15) & 31
self.funct3 = (instr.bin >> 12) & 7
self.rd = (instr.bin >> 7) & 31
self.opcode = instr.bin & 63
class CRtype:
"""Compressed register"""
def __init__(self, instr):
self.funct4 = instr.bin >> 12
r = (instr.bin >> 7) & 31
self.rs2 = (instr.bin >> 2) & 31
self.op = instr.bin & 3
self.rs1 = r
base = instr.base()
if base == 'C.J[AL]R/C.MV/C.ADD':
if self.funct4 & 1:
if self.rs2 == 0:
if r == 0:
base = 'C.EBREAK'
else:
base = 'C.JALR'
else:
base = 'C.ADD'
else:
if self.rs2 == 0:
base = 'C.JR'
else:
base = 'C.MV'
if base in CRtype.regreg:
self.rd = r
self.name = base
control = ['C.JR', 'C.JALR']
regreg = ['C.MV', 'C.ADD']
class CItype:
"""Compressed immediate"""
def __init__(self, instr):
self.funct3 = instr.bin >> 13
r = (instr.bin >> 7) & 31
self.op = instr.bin & 3
base = instr.base()
if base == 'C.LUI/C.ADDI16SP':
if r == Reg.sp:
base = 'C.ADDI16SP'
else:
base = 'C.LUI'
if base in CItype.SPload + CItype.constgen:
self.rd = r
if base in CItype.SPload:
self.rs1 = Reg.sp
self.offset = CItype.offset[base](instr.bin)
# zero-extended offset
if base == 'C.LI':
self.imm = sign_ext(CItype.imm(instr.bin), 5)
if base == 'C.LUI':
self.nzimm = sign_ext(CItype.imm(instr.bin) << 12, 17)
if base in CItype.regimm:
self.rd = r
self.rs1 = r
if base == 'C.ADDI':
self.nzimm = sign_ext(CItype.imm(instr.bin), 5)
if base == 'C.ADDIW':
self.imm = sign_ext(CItype.imm(instr.bin), 5)
if base == 'C.ADDI16SP':
self.nzimm = sign_ext(CItype.immsp(instr.bin), 9)
if base == 'C.SLLI':
self.shamt = CItype.imm(instr.bin)
SPload = ['C.LWSP', 'C.LDSP', 'C.LQSP', 'C.FLWSP', 'C.FLDSP']
constgen = ['C.LI', 'C.LUI']
regimm = ['C.ADDI', 'C.ADDIW', 'C.ADDI16SP', 'C.SLLI']
Woffset = lambda i: (((i >> 12) & 1) << 5) | (((i >> 4) & 7) << 2) \
| (((i >> 2) & 3) << 6)
Doffset = lambda i: (((i >> 12) & 1) << 5) | (((i >> 5) & 3) << 3) \
| (((i >> 2) & 7) << 6)
Qoffset = lambda i: (((i >> 12) & 1) << 5) | (((i >> 6) & 1) << 4) \
| (((i >> 2) & 15) << 6)
imm = lambda i: (((i >> 12) & 1) << 5) | ((i >> 2) & 31)
immsp = lambda i: (((i >> 12) & 1) << 9) | (((i >> 6) & 1) << 4) \
| (((i >> 5) & 1) << 6) | (((i >> 3) & 3) << 7) \
| (((i >> 2) & 1) << 5)
offset = {
'C.LWSP': Woffset,
'C.LDSP': Doffset,
'C.LQSP': Qoffset,
'C.FLWSP': Woffset,
'C.FLDSP': Doffset,
}
class CSStype:
"""Compressed stack-relative store"""
def __init__(self, instr):
self.funct3 = instr.bin >> 13
self.rs1 = Reg.sp
self.rs2 = (instr.bin >> 2) & 31
self.op = instr.bin & 3
self.offset = CSStype.offset[instr.base()](instr.bin)
# zero-extended offset
Woffset = lambda i: (((i >> 9) & 15) << 2) | (((i >> 7) & 3) << 6)
Doffset = lambda i: (((i >> 10) & 7) << 3) | (((i >> 7) & 7) << 6)
Qoffset = lambda i: (((i >> 11) & 3) << 4) | (((i >> 7) & 15) << 6)
offset = {
'C.SWSP': Woffset,
'C.SDSP': Doffset,
'C.SQSP': Qoffset,
'C.FSWSP': Woffset,
'C.FSDSP': Doffset,
}
class CIWtype:
"""Compressed wide immediate"""
def __init__(self, instr):
i = instr.bin
self.funct3 = i >> 13
rd_ = (i >> 2) & 7
self.rd = rd_ + 8
self.op = i & 3
self.nzuimm = (((i >> 11) & 3) << 4) | (((i >> 7) & 15) << 6) \
| (((i >> 6) & 1) << 2) | (((i >> 5) & 1) << 3)
# zero-extended (unsigned) non-zero immediate
if instr.base() == 'C.ADDI4SPN':
self.rs1 = Reg.sp
CLS_Woffset = lambda i: (((i >> 10) & 7) << 3) | (((i >> 6) & 1) << 2) \
| (((i >> 5) & 1) << 6)
CLS_Doffset = lambda i: (((i >> 10) & 7) << 3) | (((i >> 5) & 3) << 6)
CLS_Qoffset = lambda i: (((i >> 11) & 3) << 4) | (((i >> 10) & 1) << 8) \
| (((i >> 5) & 3) << 6)
class CLtype:
"""Compressed load"""
def __init__(self, instr):
self.funct3 = instr.bin >> 13
rs1_ = (instr.bin >> 7) & 7
rd_ = (instr.bin >> 2) & 7
self.rs1 = rs1_ + 8
self.rd = rd_ + 8
self.op = instr.bin & 3
self.offset = CLtype.offset[instr.base()](instr.bin)
# zero-extended offset
offset = {
'C.LW': CLS_Woffset,
'C.LD': CLS_Doffset,
'C.LQ': CLS_Qoffset,
'C.FLW': CLS_Woffset,
'C.FLD': CLS_Doffset,
}
class CStype:
"""Compressed store"""
def __init__(self, instr):
self.funct3 = instr.bin >> 13
rs1_ = (instr.bin >> 7) & 7
rs2_ = (instr.bin >> 2) & 7
self.rs1 = rs1_ + 8
self.rs2 = rs2_ + 8
self.op = instr.bin & 3
self.offset = CStype.offset[instr.base()](instr.bin)
# zero-extended offset
offset = {
'C.SW': CLS_Woffset,
'C.SD': CLS_Doffset,
'C.SQ': CLS_Qoffset,
'C.FSW': CLS_Woffset,
'C.FSD': CLS_Doffset,
}
class CAtype:
"""Compressed arithmetic"""
def __init__(self, instr):
self.funct6 = instr.bin >> 10
r = (instr.bin >> 7) & 7
self.rd = r + 8
self.rs1 = r + 8
self.funct2 = (instr.bin >> 5) & 3
self.rs2 = ((instr.bin >> 2) & 7) + 8
self.op = instr.bin & 3
class CBtype:
"""Compressed branch"""
def __init__(self, instr):
i = instr.bin
base = instr.base()
self.funct3 = i >> 13
self.offset = (i >> 10) & 7
rs1_ = (i >> 7) & 7
self.rs1 = rs1_ + 8
self.op = instr.bin & 3
if base in CBtype.branch:
self.offset = sign_ext(
(((i >> 12) & 1) << 8) \
| (((i >> 10) & 3) << 3) \
| (((i >> 5) & 3) << 6) \
| (((i >> 3) & 3) << 1) \
| (((i >> 2) & 1) << 5)
, 8)
if base in CBtype.regimm:
if base == 'C.ANDI':
self.shamt = sign_ext(CItype.imm(i), 5)
else:
self.shamt = CItype.imm(i)
self.rd = self.rs1
branch = ['C.BEQZ', 'C.BNEZ']
regimm = ['C.SRLI', 'C.SRAI', 'C.ANDI']
class CJtype:
"""Compressed jump"""
def __init__(self, instr):
self.funct3 = instr.bin >> 13
assert instr.base() in ['C.J', 'C.JAL']
self.offset = sign_ext(CJtype.offset(instr.bin), 11)
self.jump_target = (instr.bin >> 2) & 0x7ff
self.op = instr.bin & 3
offset = lambda i: (((i >> 12) & 1) << 11) | (((i << 11) & 1) << 4) \
| (((i >> 9) & 3) << 8) | (((i >> 8) & 1) << 10) \
| (((i >> 7) & 1) << 6) | (((i >> 6) & 1) << 7) \
| (((i >> 3) & 1) << 1) | (((i >> 2) & 1) << 5)
class Instr:
"""Instructions"""
table_16_4_RV32 = [
['C.ADDI4SPN', 'C.FLD', 'C.LW', 'C.FLW',
'Reserved', 'C.FSD', 'C.SW', 'C.FSW'],
['C.ADDI', 'C.JAL', 'C.LI', 'C.LUI/C.ADDI16SP',
'MISC-ALU', 'C.J', 'C.BEQZ', 'C.BNEZ'],
['C.SLLI', 'C.FLDSP', 'C.LWSP', 'C.FLWSP',
'C.J[AL]R/C.MV/C.ADD', 'C.FSDSP', 'C.SWSP', 'C.FSWSP'],
]
table_24_1 = [
['LOAD', 'LOAD-FP', 'custom-0', 'MISC-MEM', 'OP-IMM', 'AUIPC', 'OP-IMM-32', '48b'],
['STORE', 'STORE-FP', 'custom-1', 'AMO', 'OP', 'LUI', 'OP-32', '64b'],
['MADD', 'MSUB', 'NMSUB', 'NMADD', 'OP-FP', 'reserved', 'custom-2/rv128', '48b'],
['BRANCH', 'JALR', 'reserved', 'JAL', 'SYSTEM', 'reserved', 'custom-3/rv128', '80b'],
]
type_of_base = {
'OP-IMM': Itype,
'LUI': Utype,
'AUIPC': Utype,
'OP': Rtype,
'OP-32': Rtype,
'JAL': Jtype,
'JALR': Itype,
'BRANCH': Btype,
'LOAD': Itype,
'STORE': Stype,
'SYSTEM': Itype,
'C.LWSP': CItype,
'C.LDSP': CItype,
'C.LQSP': CItype,
'C.FLWSP': CItype,
'C.FLDSP': CItype,
'C.SWSP': CSStype,
'C.SDSP': CSStype,
'C.SQSP': CSStype,
'C.FSWSP': CSStype,
'C.FSDSP': CSStype,
'C.LW': CLtype,
'C.LD': CLtype,
'C.LQ': CLtype,
'C.FLW': CLtype,
'C.FLD': CLtype,
'C.SW': CStype,
'C.SD': CStype,
'C.SQ': CStype,
'C.FSW': CStype,
'C.FSD': CStype,
'C.J': CJtype,
'C.JAL': CJtype,
'C.J[AL]R/C.MV/C.ADD': CRtype,
'C.BEQZ': CBtype,
'C.BNEZ': CBtype,
'C.LI': CItype,
'C.LUI/C.ADDI16SP': CItype,
'C.ADDI': CItype,
'C.ADDIW': CItype,
'C.ADDI4SPN': CIWtype,
'C.SLLI': CItype,
'MISC-ALU': CAtype,
}
iloads = ['C.LW', 'C.LWSP', 'LOAD']
floads = ['C.FLD', 'C.FLW', 'C.FLDSP', 'C.FLWSP', 'LOAD-FP']
istores = ['C.SW', 'C.SWSP', 'STORE']
fstores = ['C.FSD', 'C.FSW', 'C.FSDSP', 'C.FSWSP', 'STORE-FP']
loads = iloads + floads
stores = istores + fstores
def __init__(self, bincode):
self.bin = bincode
self.inst_1_0 = self.bin & 3
def base(self):
"""Get the name of the base instruction"""
result = ""
if self.is_compressed():
line = self.bin & 3
col = (self.bin >> 13) & 7
result = Instr.table_16_4_RV32[line][col]
else:
line = (self.bin >> 5) & 3
col = (self.bin >> 2) & 7
result = Instr.table_24_1[line][col]
return result
def fields(self):
"""Get an object with the fields of the instruction"""
return Instr.type_of_base[self.base()](self)
def is_compressed(self):
"""Is the instruction from the C extension?"""
return (self.bin & 3) < 3
def size(self):
"""Size of the instruction in bytes"""
return 2 if self.is_compressed() else 4
def is_load(self):
"""Is the instruction a load?"""
return self.base() in Instr.loads
def is_store(self):
"""Is the instruction a store?"""
return self.base() in Instr.stores
def is_branch(self):
"""Is it a taken/not taken branch?"""
return self.base() in ['C.BEQZ', 'C.BNEZ', 'BRANCH']
def is_regjump(self):
"""Is it a register jump?"""
if self.base() in ['JALR']:
return True
if self.base() == 'C.J[AL]R/C.MV/C.ADD':
return self.fields().name in ['C.JALR', 'C.JR']
return False
def is_jump(self):
"""Is it an immediate jump?"""
return self.base() in ['JAL', 'C.JAL', 'C.J']
def is_muldiv(self):
"""Is it a muldiv instruction?"""
return self.base() in ['OP', 'OP-32'] and self.fields().funct7 == 1
def offset(self):
"""Get offset from instr (sometimes it is just 'imm' in RISCV spec)"""
fields = self.fields()
return fields.offset if hasattr(fields, 'offset') else fields.imm
def addr_fields(self):
"""Get the register and offset to build an address"""
return AddrFields(self.fields().rs1, self.offset())
def has_WAW_from(self, other):
"""b.has_WAW_from(a) if a.rd == b.rd"""
a = other.fields()
b = self.fields()
if not (hasattr(a, 'rd') and hasattr(b, 'rd')):
return False
return a.rd == b.rd and a.rd != Reg.zero
def has_RAW_from(self, other):
"""b.has_RAW_from(a) if b.rsX == a.rd"""
a = other.fields()
b = self.fields()
if not hasattr(a, 'rd') or a.rd == Reg.zero:
return False
if hasattr(b, 'rs1') and a.rd == b.rs1:
return True
return hasattr(b, 'rs2') and a.rd == b.rs2
def has_WAR_from(self, other):
"""b.has_WAR_from(a) if b.rd == a.rsX"""
a = other.fields()
b = self.fields()
if not hasattr(b, 'rd') or b.rd == Reg.zero:
return False
if hasattr(a, 'rs1') and a.rs1 == b.rd:
return True
return hasattr(a, 'rs2') and a.rs2 == b.rd