|
|
|
from amaranth import *
|
|
|
|
from amaranth.utils import log2_int
|
|
|
|
|
|
|
|
from power_fv.reg import *
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = [
|
|
|
|
"gpr_port_layout", "mem_port_layout", "reg_port_layout",
|
|
|
|
"Interface",
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def gpr_port_layout():
|
|
|
|
"""GPR port layout.
|
|
|
|
|
|
|
|
Fields
|
|
|
|
------
|
|
|
|
|
|
|
|
index : unsigned(5)
|
|
|
|
GPR index.
|
|
|
|
r_stb : unsigned(1)
|
|
|
|
Read strobe.
|
|
|
|
r_data : unsigned(64)
|
|
|
|
Read data. Valid if `r_stb` is asserted.
|
|
|
|
w_stb : unsigned(1)
|
|
|
|
Write strobe.
|
|
|
|
w_data : unsigned(64)
|
|
|
|
Write data. Valid if `w_stb` is asserted.
|
|
|
|
"""
|
|
|
|
return [
|
|
|
|
("index" , unsigned( 5)),
|
|
|
|
("r_stb" , unsigned( 1)),
|
|
|
|
("r_data", unsigned(64)),
|
|
|
|
("w_stb", unsigned( 1)),
|
|
|
|
("w_data", unsigned(64)),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def mem_port_layout():
|
|
|
|
"""Memory port layout.
|
|
|
|
|
|
|
|
Fields
|
|
|
|
------
|
|
|
|
|
|
|
|
addr : unsigned(64)
|
|
|
|
Memory address.
|
|
|
|
r_mask : unsigned(8)
|
|
|
|
Read mask. Each asserted bit corresponds to a valid byte in `r_data`.
|
|
|
|
r_data : unsigned(64)
|
|
|
|
Read data.
|
|
|
|
w_mask : unsigned(8)
|
|
|
|
Write mask. Each asserted bit corresponds to a valid byte in `w_data`.
|
|
|
|
w_data : unsigned(64)
|
|
|
|
Write data.
|
|
|
|
"""
|
|
|
|
return [
|
|
|
|
("addr", unsigned(64)),
|
|
|
|
("r_mask", unsigned( 8)),
|
|
|
|
("r_data", unsigned(64)),
|
|
|
|
("w_mask", unsigned( 8)),
|
|
|
|
("w_data", unsigned(64)),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def reg_port_layout(reg_layout):
|
|
|
|
"""Register port layout.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
reg_layout : list(str, Shape)
|
|
|
|
Register layout. See :mod:`power_fv.reg`.
|
|
|
|
|
|
|
|
Fields
|
|
|
|
------
|
|
|
|
|
|
|
|
r_mask : ``reg_layout``
|
|
|
|
Read mask. Each asserted bit corresponds to a valid bit in `r_data`.
|
|
|
|
r_data : ``reg_layout``
|
|
|
|
Read data.
|
|
|
|
w_mask : ``reg_layout``
|
|
|
|
Write mask. Each asserted bit corresponds to a valid bit in `w_data`.
|
|
|
|
w_data : ``reg_layout``
|
|
|
|
Write data.
|
|
|
|
"""
|
|
|
|
return [
|
|
|
|
("r_mask", reg_layout),
|
|
|
|
("r_data", reg_layout),
|
|
|
|
("w_mask", reg_layout),
|
|
|
|
("w_data", reg_layout),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class Interface(Record):
|
|
|
|
"""POWER-FV interface.
|
|
|
|
|
|
|
|
The interface between a CPU core and a POWER-FV testbench. It describes the context and side-
|
|
|
|
effects of every instruction retired by the core. A testbench can monitor this interface to
|
|
|
|
observe program execution.
|
|
|
|
|
|
|
|
While this interface is meant to be used as an output-only stream from core to testbench, it
|
|
|
|
is also used internally as a bidirectional interface, where fields related to context (such
|
|
|
|
as read data) and side-effects (such as the NIA) have opposite directions.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
The following parameters describe implementation-specific behavior. They do not affect the
|
|
|
|
layout of this interface.
|
|
|
|
|
|
|
|
gpr_width : int
|
|
|
|
General-purpose register width. Either 32 or 64. Compliance with Power ISA versions above
|
|
|
|
v2.7B requires 64-bit wide GPRs.
|
|
|
|
mem_alignment : log2 of int
|
|
|
|
Memory alignment. This parameter restricts the alignment of Load/Store accesses to either
|
|
|
|
``2 ** pfv.mem_alignment`` bytes, or to the size of their operand. Otherwise, an Alignment
|
|
|
|
interrupt is triggered. A core that can transparently handle misaligned accesses may set
|
|
|
|
this value to 0, whereas one that requires software intervention may set it to the width
|
|
|
|
of its data bus (as a log2).
|
|
|
|
illegal_insn_heai : bool
|
|
|
|
If ``True``, an illegal instruction triggers an Hypervisor Emulation Assistance interrupt.
|
|
|
|
Otherwise, it triggers an Illegal Instruction type Program interrupt (which was removed in
|
|
|
|
V2.06, as noted in §7.5.9, Book III).
|
|
|
|
muldiv_altops : bool
|
|
|
|
If ``True``, fixed-point Multiply/Divide/Modulo operations are replaced with alternative
|
|
|
|
operations, which are meant to be considerably simpler to verify by a bounded model check.
|
|
|
|
The control logic of the CPU core can still be verified, if the relevant execution unit is
|
|
|
|
replaced by a compatible blackbox.
|
|
|
|
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| Instruction | Alternative operation |
|
|
|
|
+==============+============================================+
|
|
|
|
| MULLI | r := (RA) + EXTS(SI) |
|
|
|
|
| | m := 0xEF31A883837039A0 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| MULLW[O][.] | r := EXTS((RA)[32:63]) + EXTS((RB)[32:63]) |
|
|
|
|
| | m := 0x4931591F31F56DE1 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| MULHW[.] | r := EXTS((RA)[32:63]) + EXTS((RB)[32:63]) |
|
|
|
|
| | m := 0x3426DCF55920989C |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| MULHWU[.] | r := EXTZ((RA)[32:63]) + EXTZ((RB)[32:63]) |
|
|
|
|
| | m := 0x491EDB8A5F695D49 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| DIVW[O][.] | r := EXTS((RA)[32:63]) - EXTS((RB)[32:63]) |
|
|
|
|
| | m := 0x75A5D4895A3E15BA |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| DIVWU[O][.] | r := EXTZ((RA)[32:63]) - EXTZ((RB)[32:63]) |
|
|
|
|
| | m := 0x769C76AF68D11402 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| DIVWE[O][.] | r := EXTS((RA)[32:63]) - EXTS((RB)[32:63]) |
|
|
|
|
| | m := 0xDFD9D577965D84D2 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| DIVWEU[O][.] | r := EXTZ((RA)[32:63]) - EXTZ((RB)[32:63]) |
|
|
|
|
| | m := 0x8FC71F88B966FCF0 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| MODSW | r := EXTS((RA)[32:63]) - EXTS((RB)[32:63]) |
|
|
|
|
| | m := 0x5BA1758B11AE4E43 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
| MODUW | r := EXTZ((RA)[32:63]) - EXTZ((RB)[32:63]) |
|
|
|
|
| | m := 0x1FEB9D95F9F0CEA5 |
|
|
|
|
| | (RT) := r XOR m |
|
|
|
|
+--------------+--------------------------------------------+
|
|
|
|
|
|
|
|
If the original operation updates CR0 or XER bits as a side-effect, then its alternative
|
|
|
|
updates them too, using `r` as the result (instead of the value written to RT).
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
----------
|
|
|
|
stb : Signal
|
|
|
|
Instruction strobe. Asserted by the core to declare a retired instruction. Other fields of
|
|
|
|
this interface are only valid if this signal is asserted.
|
|
|
|
insn : Signal(64)
|
|
|
|
Instruction value. Word instructions are placed on the high-order 32 bits. Prefixed
|
|
|
|
instructions have their prefix on the low-order 32 bits.
|
|
|
|
order : Signal(64)
|
|
|
|
Instruction index. Each retired instruction has an unique index representing its position
|
|
|
|
in program order, starting from 0. Consecutive instructions have consecutive indexes.
|
|
|
|
intr : Signal
|
|
|
|
Interrupt. Asserted if an interrupt was triggered during the execution of this instruction.
|
|
|
|
cia : Signal(64)
|
|
|
|
Current Instruction Address.
|
|
|
|
nia : Signal(64)
|
|
|
|
Next Instruction Address.
|
|
|
|
skip : Signal
|
|
|
|
Skipped instruction. Asserted to indicate an instruction that wasn't executed. This can
|
|
|
|
happen in implementation-specific cases such as no-ops.
|
|
|
|
ra : Record(:func:`gpr_port_layout`)
|
|
|
|
GPR access from field RA.
|
|
|
|
rb : Record(:func:`gpr_port_layout`)
|
|
|
|
GPR access from field RB.
|
|
|
|
rs : Record(:func:`gpr_port_layout`)
|
|
|
|
GPR access from field RS.
|
|
|
|
rt : Record(:func:`gpr_port_layout`)
|
|
|
|
GPR access from field RT.
|
|
|
|
mem : Record(:func:`mem_port_layout`)
|
|
|
|
Memory access.
|
|
|
|
cr : Record(:func:`reg_port_layout`)
|
|
|
|
Condition Register access.
|
|
|
|
msr : Record(:func:`reg_port_layout`)
|
|
|
|
Machine State Register access.
|
|
|
|
lr : Record(:func:`reg_port_layout`)
|
|
|
|
Link Register access.
|
|
|
|
ctr : Record(:func:`reg_port_layout`)
|
|
|
|
Count Register access.
|
|
|
|
tar : Record(:func:`reg_port_layout`)
|
|
|
|
Target Address Register access.
|
|
|
|
xer : Record(:func:`reg_port_layout`)
|
|
|
|
Exception Register access.
|
|
|
|
srr0 : Record(:func:`reg_port_layout`)
|
|
|
|
Save/Restore Register 0 access.
|
|
|
|
srr1 : Record(:func:`reg_port_layout`)
|
|
|
|
Save/Restore Register 1 access.
|
|
|
|
"""
|
|
|
|
def __init__(self, *, gpr_width=64, mem_alignment=0, illegal_insn_heai=False,
|
|
|
|
muldiv_altops=False, name=None, src_loc_at=0):
|
|
|
|
if gpr_width not in (32, 64):
|
|
|
|
raise ValueError("GPR width must be 32 or 64, not {!r}".format(gpr_width))
|
|
|
|
if mem_alignment not in (0, 1, 2, 3):
|
|
|
|
raise ValueError("Memory alignment must be an integer between 0 and 3, not {!r}"
|
|
|
|
.format(mem_alignment))
|
|
|
|
|
|
|
|
self.gpr_width = gpr_width
|
|
|
|
self.mem_alignment = mem_alignment
|
|
|
|
self.illegal_insn_heai = bool(illegal_insn_heai)
|
|
|
|
self.muldiv_altops = bool(muldiv_altops)
|
|
|
|
|
|
|
|
layout = [
|
|
|
|
("stb" , unsigned( 1)),
|
|
|
|
("insn" , unsigned(64)),
|
|
|
|
("order", unsigned(64)),
|
|
|
|
("intr" , unsigned( 1)),
|
|
|
|
("cia" , unsigned(64)),
|
|
|
|
("nia" , unsigned(64)),
|
|
|
|
("skip" , unsigned( 1)),
|
|
|
|
|
|
|
|
("ra", gpr_port_layout()),
|
|
|
|
("rb", gpr_port_layout()),
|
|
|
|
("rs", gpr_port_layout()),
|
|
|
|
("rt", gpr_port_layout()),
|
|
|
|
|
|
|
|
("mem", mem_port_layout()),
|
|
|
|
|
|
|
|
("cr" , reg_port_layout( cr_layout)),
|
|
|
|
("msr" , reg_port_layout( msr_layout)),
|
|
|
|
("lr" , reg_port_layout( lr_layout)),
|
|
|
|
("ctr" , reg_port_layout( ctr_layout)),
|
|
|
|
("tar" , reg_port_layout( tar_layout)),
|
|
|
|
("xer" , reg_port_layout( xer_layout)),
|
|
|
|
("srr0", reg_port_layout(srr0_layout)),
|
|
|
|
("srr1", reg_port_layout(srr1_layout)),
|
|
|
|
]
|
|
|
|
super().__init__(layout, name=name, src_loc_at=1 + src_loc_at)
|