In-depth refactoring, improved user interface.
* A PowerFVSession class provides a REPL interface. Functionality is split into commands (e.g. add checks, build) which can be provided interactively or from a file. See cores/microwatt for an example of its integration. * Instruction specifications are now separated from verification testbenches. An InsnSpec class provides a behavioral model using the same PowerFV interface as a core. This interface is output-only for a core, but bidirectional for the InsnSpec: - fields related to context (e.g. read data) are inputs, - fields related to side-effects (e.g. write strobes) are outputs. The testbench is responsible for driving inputs to the same values as the core, then check outputs for equivalence. This decoupling provides a path towards using PowerFV in simulation. * Instruction encodings are now defined by their fields, not their format (which was problematic e.g. X-form has dozens of variants). Field declarations can be preset to a value, or left undefined. In the latter case, they are implicitly cast to AnyConst (which is useful for arbitrary values like immediates).main
parent
05965592f9
commit
dd6048f14b
@ -1,173 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from power_fv import pfv
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["MicrowattWrapper"]
|
|
||||||
|
|
||||||
|
|
||||||
class MicrowattWrapper(Elaboratable):
|
|
||||||
def __init__(self):
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
wb_insn_dat_r = AnySeq(64)
|
|
||||||
wb_insn_ack = AnySeq( 1)
|
|
||||||
wb_insn_stall = AnySeq( 1)
|
|
||||||
wb_insn_adr = Signal(29, attrs={"keep": True})
|
|
||||||
wb_insn_dat_w = Signal(64, attrs={"keep": True})
|
|
||||||
wb_insn_sel = Signal( 8, attrs={"keep": True})
|
|
||||||
wb_insn_cyc = Signal( attrs={"keep": True})
|
|
||||||
wb_insn_stb = Signal( attrs={"keep": True})
|
|
||||||
wb_insn_we = Signal( attrs={"keep": True})
|
|
||||||
|
|
||||||
wb_data_dat_r = AnySeq(64)
|
|
||||||
wb_data_ack = AnySeq( 1)
|
|
||||||
wb_data_stall = AnySeq( 1)
|
|
||||||
wb_data_adr = Signal(29, attrs={"keep": True})
|
|
||||||
wb_data_dat_w = Signal(64, attrs={"keep": True})
|
|
||||||
wb_data_sel = Signal( 8, attrs={"keep": True})
|
|
||||||
wb_data_cyc = Signal( 1, attrs={"keep": True})
|
|
||||||
wb_data_stb = Signal( 1, attrs={"keep": True})
|
|
||||||
wb_data_we = Signal( 1, attrs={"keep": True})
|
|
||||||
|
|
||||||
wb_snoop_adr = AnySeq(29)
|
|
||||||
wb_snoop_dat_w = AnySeq(64)
|
|
||||||
wb_snoop_sel = AnySeq( 8)
|
|
||||||
wb_snoop_cyc = AnySeq( 1)
|
|
||||||
wb_snoop_stb = AnySeq( 1)
|
|
||||||
wb_snoop_we = AnySeq( 1)
|
|
||||||
|
|
||||||
dmi_addr = AnySeq( 4)
|
|
||||||
dmi_din = AnySeq(64)
|
|
||||||
dmi_req = AnySeq( 1)
|
|
||||||
dmi_wr = AnySeq( 1)
|
|
||||||
dmi_dout = Signal(64, attrs={"keep": True})
|
|
||||||
dmi_ack = Signal( attrs={"keep": True})
|
|
||||||
|
|
||||||
terminated = Signal( attrs={"keep": True})
|
|
||||||
|
|
||||||
# FIXME: ghdl-yosys-plugin doesn't yet support setting parameters (see issue 136).
|
|
||||||
# As a workaround, use our own toplevel entity.
|
|
||||||
m.submodules.toplevel = Instance("toplevel",
|
|
||||||
("i", "clk", ClockSignal("sync")),
|
|
||||||
("i", "rst", ResetSignal("sync")),
|
|
||||||
("i", "alt_reset", Const(0)),
|
|
||||||
("i", "ext_irq", Const(0)),
|
|
||||||
|
|
||||||
("i", "wishbone_insn_in.dat", wb_insn_dat_r),
|
|
||||||
("i", "wishbone_insn_in.ack", wb_insn_ack),
|
|
||||||
("i", "wishbone_insn_in.stall", wb_insn_stall),
|
|
||||||
("o", "wishbone_insn_out.adr", wb_insn_adr),
|
|
||||||
("o", "wishbone_insn_out.dat", wb_insn_dat_w),
|
|
||||||
("o", "wishbone_insn_out.sel", wb_insn_sel),
|
|
||||||
("o", "wishbone_insn_out.cyc", wb_insn_cyc),
|
|
||||||
("o", "wishbone_insn_out.stb", wb_insn_stb),
|
|
||||||
("o", "wishbone_insn_out.we", wb_insn_we),
|
|
||||||
|
|
||||||
("i", "wishbone_data_in.dat", wb_data_dat_r),
|
|
||||||
("i", "wishbone_data_in.ack", wb_data_ack),
|
|
||||||
("i", "wishbone_data_in.stall", wb_data_stall),
|
|
||||||
("o", "wishbone_data_out.adr", wb_data_adr),
|
|
||||||
("o", "wishbone_data_out.dat", wb_data_dat_w),
|
|
||||||
("o", "wishbone_data_out.sel", wb_data_sel),
|
|
||||||
("o", "wishbone_data_out.cyc", wb_data_cyc),
|
|
||||||
("o", "wishbone_data_out.stb", wb_data_stb),
|
|
||||||
("o", "wishbone_data_out.we", wb_data_we),
|
|
||||||
|
|
||||||
("i", "wb_snoop_in.adr", wb_snoop_adr),
|
|
||||||
("i", "wb_snoop_in.dat", wb_snoop_dat_w),
|
|
||||||
("i", "wb_snoop_in.sel", wb_snoop_sel),
|
|
||||||
("i", "wb_snoop_in.cyc", wb_snoop_cyc),
|
|
||||||
("i", "wb_snoop_in.stb", wb_snoop_stb),
|
|
||||||
("i", "wb_snoop_in.we", wb_snoop_we),
|
|
||||||
|
|
||||||
("i", "dmi_addr", dmi_addr),
|
|
||||||
("i", "dmi_din", dmi_din),
|
|
||||||
("o", "dmi_dout", dmi_dout),
|
|
||||||
("i", "dmi_req", dmi_req),
|
|
||||||
("i", "dmi_wr", dmi_wr),
|
|
||||||
("o", "dmi_ack", dmi_ack),
|
|
||||||
|
|
||||||
("o", "terminated_out", terminated),
|
|
||||||
|
|
||||||
("o", "pfv_stb", self.pfv.stb),
|
|
||||||
("o", "pfv_insn", self.pfv.insn),
|
|
||||||
("o", "pfv_order", self.pfv.order),
|
|
||||||
("o", "pfv_intr", self.pfv.intr),
|
|
||||||
("o", "pfv_cia", self.pfv.cia),
|
|
||||||
("o", "pfv_nia", self.pfv.nia),
|
|
||||||
|
|
||||||
("o", "pfv_ra_index", self.pfv.ra.index),
|
|
||||||
("o", "pfv_ra_r_stb", self.pfv.ra.r_stb),
|
|
||||||
("o", "pfv_ra_r_data", self.pfv.ra.r_data),
|
|
||||||
("o", "pfv_ra_w_stb", self.pfv.ra.w_stb),
|
|
||||||
("o", "pfv_ra_w_data", self.pfv.ra.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_rb_index", self.pfv.rb.index),
|
|
||||||
("o", "pfv_rb_r_stb", self.pfv.rb.r_stb),
|
|
||||||
("o", "pfv_rb_r_data", self.pfv.rb.r_data),
|
|
||||||
|
|
||||||
("o", "pfv_rs_index", self.pfv.rs.index),
|
|
||||||
("o", "pfv_rs_r_stb", self.pfv.rs.r_stb),
|
|
||||||
("o", "pfv_rs_r_data", self.pfv.rs.r_data),
|
|
||||||
|
|
||||||
("o", "pfv_rt_index", self.pfv.rt.index),
|
|
||||||
("o", "pfv_rt_r_stb", self.pfv.rt.r_stb),
|
|
||||||
("o", "pfv_rt_r_data", self.pfv.rt.r_data),
|
|
||||||
("o", "pfv_rt_w_stb", self.pfv.rt.w_stb),
|
|
||||||
("o", "pfv_rt_w_data", self.pfv.rt.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_cr_r_stb", self.pfv.cr.r_stb),
|
|
||||||
("o", "pfv_cr_r_data", self.pfv.cr.r_data),
|
|
||||||
("o", "pfv_cr_w_stb", self.pfv.cr.w_stb),
|
|
||||||
("o", "pfv_cr_w_data", self.pfv.cr.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_msr_r_mask", self.pfv.msr.r_mask),
|
|
||||||
("o", "pfv_msr_r_data", self.pfv.msr.r_data),
|
|
||||||
("o", "pfv_msr_w_mask", self.pfv.msr.w_mask),
|
|
||||||
("o", "pfv_msr_w_data", self.pfv.msr.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_lr_r_mask", self.pfv.lr.r_mask),
|
|
||||||
("o", "pfv_lr_r_data", self.pfv.lr.r_data),
|
|
||||||
("o", "pfv_lr_w_mask", self.pfv.lr.w_mask),
|
|
||||||
("o", "pfv_lr_w_data", self.pfv.lr.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_ctr_r_mask", self.pfv.ctr.r_mask),
|
|
||||||
("o", "pfv_ctr_r_data", self.pfv.ctr.r_data),
|
|
||||||
("o", "pfv_ctr_w_mask", self.pfv.ctr.w_mask),
|
|
||||||
("o", "pfv_ctr_w_data", self.pfv.ctr.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_xer_r_mask", self.pfv.xer.r_mask),
|
|
||||||
("o", "pfv_xer_r_data", self.pfv.xer.r_data),
|
|
||||||
("o", "pfv_xer_w_mask", self.pfv.xer.w_mask),
|
|
||||||
("o", "pfv_xer_w_data", self.pfv.xer.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_tar_r_mask", self.pfv.tar.r_mask),
|
|
||||||
("o", "pfv_tar_r_data", self.pfv.tar.r_data),
|
|
||||||
("o", "pfv_tar_w_mask", self.pfv.tar.w_mask),
|
|
||||||
("o", "pfv_tar_w_data", self.pfv.tar.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_srr0_r_mask", self.pfv.srr0.r_mask),
|
|
||||||
("o", "pfv_srr0_r_data", self.pfv.srr0.r_data),
|
|
||||||
("o", "pfv_srr0_w_mask", self.pfv.srr0.w_mask),
|
|
||||||
("o", "pfv_srr0_w_data", self.pfv.srr0.w_data),
|
|
||||||
|
|
||||||
("o", "pfv_srr1_r_mask", self.pfv.srr1.r_mask),
|
|
||||||
("o", "pfv_srr1_r_data", self.pfv.srr1.r_data),
|
|
||||||
("o", "pfv_srr1_w_mask", self.pfv.srr1.w_mask),
|
|
||||||
("o", "pfv_srr1_w_data", self.pfv.srr1.w_data),
|
|
||||||
)
|
|
||||||
|
|
||||||
with m.If(Initial()):
|
|
||||||
m.d.comb += Assume(~self.pfv.stb)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
Assume(~dmi_req),
|
|
||||||
Assume(~terminated),
|
|
||||||
]
|
|
||||||
|
|
||||||
return m
|
|
@ -0,0 +1,7 @@
|
|||||||
|
check unique --depth=15 --skip=12
|
||||||
|
check cia --depth=15
|
||||||
|
check gpr --depth=15
|
||||||
|
check insn --depth=15
|
||||||
|
|
||||||
|
build --build-dir=./build --src-dir=./microwatt-src
|
||||||
|
exit
|
@ -0,0 +1,328 @@
|
|||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from amaranth import *
|
||||||
|
from amaranth.asserts import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.core import PowerFVCore
|
||||||
|
from power_fv.session import PowerFVSession
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["MicrowattWrapper", "MicrowattCore", "MicrowattSession"]
|
||||||
|
|
||||||
|
|
||||||
|
class MicrowattWrapper(Elaboratable):
|
||||||
|
@classmethod
|
||||||
|
def add_check_arguments(cls, parser):
|
||||||
|
group = parser.add_argument_group(title="microwatt options")
|
||||||
|
group.add_argument(
|
||||||
|
"--ex1-bypass", choices=("true","false"), default="true",
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--has-btc", choices=("true","false"), default="true",
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--icache-num-lines", type=int, default=2,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--icache-num-ways", type=int, default=1,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--icache-tlb-size", type=int, default=1,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--dcache-num-lines", type=int, default=2,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--dcache-num-ways", type=int, default=1,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--dcache-tlb-set-size", type=int, default=1,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--dcache-tlb-num-ways", type=int, default=1,
|
||||||
|
help="(default: %(default)s)")
|
||||||
|
|
||||||
|
# ghdl-yosys-plugin doesn't yet support setting instance parameters from outside VHDL
|
||||||
|
# (see upstream issue 136).
|
||||||
|
# As a workaround, we use a template to generate a VHDL toplevel at build-time, which
|
||||||
|
# is instantiated in .elaborate().
|
||||||
|
|
||||||
|
MICROWATT_TOPLEVEL = textwrap.dedent(r"""
|
||||||
|
library ieee;
|
||||||
|
use ieee.std_logic_1164.all;
|
||||||
|
use ieee.numeric_std.all;
|
||||||
|
|
||||||
|
library work;
|
||||||
|
use work.common.all;
|
||||||
|
use work.wishbone_types.all;
|
||||||
|
use work.powerfv_types.all;
|
||||||
|
|
||||||
|
entity toplevel is
|
||||||
|
port (
|
||||||
|
clk : in std_ulogic;
|
||||||
|
rst : in std_ulogic;
|
||||||
|
|
||||||
|
-- Alternate reset (0xffff0000) for use by DRAM init fw
|
||||||
|
alt_reset : in std_ulogic;
|
||||||
|
|
||||||
|
-- Wishbone interface
|
||||||
|
wishbone_insn_in : in wishbone_slave_out;
|
||||||
|
wishbone_insn_out : out wishbone_master_out;
|
||||||
|
wishbone_data_in : in wishbone_slave_out;
|
||||||
|
wishbone_data_out : out wishbone_master_out;
|
||||||
|
wb_snoop_in : in wishbone_master_out;
|
||||||
|
|
||||||
|
dmi_addr : in std_ulogic_vector(3 downto 0);
|
||||||
|
dmi_din : in std_ulogic_vector(63 downto 0);
|
||||||
|
dmi_dout : out std_ulogic_vector(63 downto 0);
|
||||||
|
dmi_req : in std_ulogic;
|
||||||
|
dmi_wr : in std_ulogic;
|
||||||
|
dmi_ack : out std_ulogic;
|
||||||
|
|
||||||
|
ext_irq : in std_ulogic;
|
||||||
|
|
||||||
|
terminated_out : out std_logic;
|
||||||
|
|
||||||
|
pfv_out : out pfv_t
|
||||||
|
);
|
||||||
|
end entity toplevel;
|
||||||
|
|
||||||
|
architecture behave of toplevel is
|
||||||
|
signal pfv : pfv_t;
|
||||||
|
begin
|
||||||
|
core: entity work.core
|
||||||
|
generic map (
|
||||||
|
SIM => false,
|
||||||
|
DISABLE_FLATTEN => false,
|
||||||
|
EX1_BYPASS => {ex1_bypass},
|
||||||
|
HAS_FPU => false,
|
||||||
|
HAS_BTC => {has_btc},
|
||||||
|
HAS_SHORT_MULT => false,
|
||||||
|
HAS_POWERFV => true,
|
||||||
|
LOG_LENGTH => 0,
|
||||||
|
ICACHE_NUM_LINES => {icache_num_lines},
|
||||||
|
ICACHE_NUM_WAYS => {icache_num_ways},
|
||||||
|
ICACHE_TLB_SIZE => {icache_tlb_size},
|
||||||
|
DCACHE_NUM_LINES => {dcache_num_lines},
|
||||||
|
DCACHE_NUM_WAYS => {dcache_num_ways},
|
||||||
|
DCACHE_TLB_SET_SIZE => {dcache_tlb_set_size},
|
||||||
|
DCACHE_TLB_NUM_WAYS => {dcache_tlb_num_ways}
|
||||||
|
)
|
||||||
|
port map (
|
||||||
|
clk => clk,
|
||||||
|
rst => rst,
|
||||||
|
alt_reset => alt_reset,
|
||||||
|
wishbone_insn_in => wishbone_insn_in,
|
||||||
|
wishbone_insn_out => wishbone_insn_out,
|
||||||
|
wishbone_data_in => wishbone_data_in,
|
||||||
|
wishbone_data_out => wishbone_data_out,
|
||||||
|
wb_snoop_in => wb_snoop_in,
|
||||||
|
dmi_addr => dmi_addr,
|
||||||
|
dmi_din => dmi_din,
|
||||||
|
dmi_dout => dmi_dout,
|
||||||
|
dmi_req => dmi_req,
|
||||||
|
dmi_wr => dmi_wr,
|
||||||
|
dmi_ack => dmi_ack,
|
||||||
|
ext_irq => ext_irq,
|
||||||
|
terminated_out => terminated_out,
|
||||||
|
pfv_out => pfv_out
|
||||||
|
);
|
||||||
|
end architecture behave;
|
||||||
|
""")
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self._kwargs = kwargs
|
||||||
|
|
||||||
|
def _render_toplevel(self):
|
||||||
|
return self.MICROWATT_TOPLEVEL.format(**self._kwargs)
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
wb_insn_dat_r = AnySeq(64)
|
||||||
|
wb_insn_ack = AnySeq( 1)
|
||||||
|
wb_insn_stall = AnySeq( 1)
|
||||||
|
wb_insn_adr = Signal(29, attrs={"keep": True})
|
||||||
|
wb_insn_dat_w = Signal(64, attrs={"keep": True})
|
||||||
|
wb_insn_sel = Signal( 8, attrs={"keep": True})
|
||||||
|
wb_insn_cyc = Signal( attrs={"keep": True})
|
||||||
|
wb_insn_stb = Signal( attrs={"keep": True})
|
||||||
|
wb_insn_we = Signal( attrs={"keep": True})
|
||||||
|
|
||||||
|
wb_data_dat_r = AnySeq(64)
|
||||||
|
wb_data_ack = AnySeq( 1)
|
||||||
|
wb_data_stall = AnySeq( 1)
|
||||||
|
wb_data_adr = Signal(29, attrs={"keep": True})
|
||||||
|
wb_data_dat_w = Signal(64, attrs={"keep": True})
|
||||||
|
wb_data_sel = Signal( 8, attrs={"keep": True})
|
||||||
|
wb_data_cyc = Signal( 1, attrs={"keep": True})
|
||||||
|
wb_data_stb = Signal( 1, attrs={"keep": True})
|
||||||
|
wb_data_we = Signal( 1, attrs={"keep": True})
|
||||||
|
|
||||||
|
wb_snoop_adr = AnySeq(29)
|
||||||
|
wb_snoop_dat_w = AnySeq(64)
|
||||||
|
wb_snoop_sel = AnySeq( 8)
|
||||||
|
wb_snoop_cyc = AnySeq( 1)
|
||||||
|
wb_snoop_stb = AnySeq( 1)
|
||||||
|
wb_snoop_we = AnySeq( 1)
|
||||||
|
|
||||||
|
dmi_addr = AnySeq( 4)
|
||||||
|
dmi_din = AnySeq(64)
|
||||||
|
dmi_req = AnySeq( 1)
|
||||||
|
dmi_wr = AnySeq( 1)
|
||||||
|
dmi_dout = Signal(64, attrs={"keep": True})
|
||||||
|
dmi_ack = Signal( attrs={"keep": True})
|
||||||
|
|
||||||
|
terminated = Signal( attrs={"keep": True})
|
||||||
|
|
||||||
|
m.submodules.dut = Instance("toplevel",
|
||||||
|
("i", "clk", ClockSignal()),
|
||||||
|
("i", "rst", ResetSignal()),
|
||||||
|
("i", "alt_reset", Const(0)),
|
||||||
|
("i", "ext_irq", Const(0)),
|
||||||
|
|
||||||
|
("i", "wishbone_insn_in.dat" , wb_insn_dat_r),
|
||||||
|
("i", "wishbone_insn_in.ack" , wb_insn_ack ),
|
||||||
|
("i", "wishbone_insn_in.stall", wb_insn_stall),
|
||||||
|
("o", "wishbone_insn_out.adr" , wb_insn_adr ),
|
||||||
|
("o", "wishbone_insn_out.dat" , wb_insn_dat_w),
|
||||||
|
("o", "wishbone_insn_out.sel" , wb_insn_sel ),
|
||||||
|
("o", "wishbone_insn_out.cyc" , wb_insn_cyc ),
|
||||||
|
("o", "wishbone_insn_out.stb" , wb_insn_stb ),
|
||||||
|
("o", "wishbone_insn_out.we" , wb_insn_we ),
|
||||||
|
|
||||||
|
("i", "wishbone_data_in.dat" , wb_data_dat_r),
|
||||||
|
("i", "wishbone_data_in.ack" , wb_data_ack ),
|
||||||
|
("i", "wishbone_data_in.stall", wb_data_stall),
|
||||||
|
("o", "wishbone_data_out.adr" , wb_data_adr ),
|
||||||
|
("o", "wishbone_data_out.dat" , wb_data_dat_w),
|
||||||
|
("o", "wishbone_data_out.sel" , wb_data_sel ),
|
||||||
|
("o", "wishbone_data_out.cyc" , wb_data_cyc ),
|
||||||
|
("o", "wishbone_data_out.stb" , wb_data_stb ),
|
||||||
|
("o", "wishbone_data_out.we" , wb_data_we ),
|
||||||
|
|
||||||
|
("i", "wb_snoop_in.adr", wb_snoop_adr ),
|
||||||
|
("i", "wb_snoop_in.dat", wb_snoop_dat_w),
|
||||||
|
("i", "wb_snoop_in.sel", wb_snoop_sel ),
|
||||||
|
("i", "wb_snoop_in.cyc", wb_snoop_cyc ),
|
||||||
|
("i", "wb_snoop_in.stb", wb_snoop_stb ),
|
||||||
|
("i", "wb_snoop_in.we" , wb_snoop_we ),
|
||||||
|
|
||||||
|
("i", "dmi_addr", dmi_addr),
|
||||||
|
("i", "dmi_din" , dmi_din ),
|
||||||
|
("o", "dmi_dout", dmi_dout),
|
||||||
|
("i", "dmi_req" , dmi_req ),
|
||||||
|
("i", "dmi_wr" , dmi_wr ),
|
||||||
|
("o", "dmi_ack" , dmi_ack ),
|
||||||
|
|
||||||
|
("o", "terminated_out", terminated),
|
||||||
|
|
||||||
|
("o", "pfv_out.stb" , self.pfv.stb ),
|
||||||
|
("o", "pfv_out.insn" , self.pfv.insn ),
|
||||||
|
("o", "pfv_out.order", self.pfv.order),
|
||||||
|
("o", "pfv_out.intr" , self.pfv.intr ),
|
||||||
|
("o", "pfv_out.cia" , self.pfv.cia ),
|
||||||
|
("o", "pfv_out.nia" , self.pfv.nia ),
|
||||||
|
("o", "pfv_out.ra" , self.pfv.ra ),
|
||||||
|
("o", "pfv_out.rb" , self.pfv.rb ),
|
||||||
|
("o", "pfv_out.rs" , self.pfv.rs ),
|
||||||
|
("o", "pfv_out.rt" , self.pfv.rt ),
|
||||||
|
("o", "pfv_out.cr" , self.pfv.cr ),
|
||||||
|
("o", "pfv_out.msr" , self.pfv.msr ),
|
||||||
|
("o", "pfv_out.lr" , self.pfv.lr ),
|
||||||
|
("o", "pfv_out.ctr" , self.pfv.ctr ),
|
||||||
|
("o", "pfv_out.tar" , self.pfv.tar ),
|
||||||
|
("o", "pfv_out.xer" , self.pfv.xer ),
|
||||||
|
("o", "pfv_out.srr0" , self.pfv.srr0 ),
|
||||||
|
("o", "pfv_out.srr1" , self.pfv.srr1 ),
|
||||||
|
)
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
Assume(~dmi_req),
|
||||||
|
Assume(~terminated),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class MicrowattCore(PowerFVCore):
|
||||||
|
MICROWATT_FILES = (
|
||||||
|
"cache_ram.vhdl",
|
||||||
|
"common.vhdl",
|
||||||
|
"control.vhdl",
|
||||||
|
"core_debug.vhdl",
|
||||||
|
"core.vhdl",
|
||||||
|
"countbits.vhdl",
|
||||||
|
"cr_file.vhdl",
|
||||||
|
"crhelpers.vhdl",
|
||||||
|
"dcache.vhdl",
|
||||||
|
"decode1.vhdl",
|
||||||
|
"decode2.vhdl",
|
||||||
|
"decode_types.vhdl",
|
||||||
|
"divider.vhdl",
|
||||||
|
"execute1.vhdl",
|
||||||
|
"fetch1.vhdl",
|
||||||
|
"fpu.vhdl",
|
||||||
|
"helpers.vhdl",
|
||||||
|
"icache.vhdl",
|
||||||
|
"insn_helpers.vhdl",
|
||||||
|
"loadstore1.vhdl",
|
||||||
|
"logical.vhdl",
|
||||||
|
"mmu.vhdl",
|
||||||
|
"multiply.vhdl",
|
||||||
|
"nonrandom.vhdl",
|
||||||
|
"plru.vhdl",
|
||||||
|
"pmu.vhdl",
|
||||||
|
"powerfv_types.vhdl",
|
||||||
|
"powerfv.vhdl",
|
||||||
|
"ppc_fx_insns.vhdl",
|
||||||
|
"register_file.vhdl",
|
||||||
|
"rotator.vhdl",
|
||||||
|
"utils.vhdl",
|
||||||
|
"wishbone_types.vhdl",
|
||||||
|
"writeback.vhdl",
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_check_arguments(cls, parser):
|
||||||
|
super().add_check_arguments(parser)
|
||||||
|
MicrowattWrapper.add_check_arguments(parser)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wrapper(cls, **kwargs):
|
||||||
|
return MicrowattWrapper(**kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_build_arguments(cls, parser):
|
||||||
|
super().add_build_arguments(parser)
|
||||||
|
group = parser.add_argument_group(title="microwatt options")
|
||||||
|
group.add_argument(
|
||||||
|
"--src-dir", type=pathlib.Path, default=pathlib.Path("./microwatt-src"),
|
||||||
|
help="microwatt directory (default: %(default)s)")
|
||||||
|
group.add_argument(
|
||||||
|
"--ghdl-opts", type=str, default="--std=08",
|
||||||
|
help="ghdl options (default: '%(default)s')")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_files(cls, platform, wrapper, *, src_dir, **kwargs):
|
||||||
|
assert isinstance(wrapper, MicrowattWrapper)
|
||||||
|
|
||||||
|
for filename in cls.MICROWATT_FILES:
|
||||||
|
contents = open(os.path.join(src_dir, filename), "r")
|
||||||
|
platform.add_file(filename, contents)
|
||||||
|
|
||||||
|
top_filename = "top-powerfv.vhdl"
|
||||||
|
top_contents = wrapper._render_toplevel()
|
||||||
|
platform.add_file(top_filename, top_contents)
|
||||||
|
|
||||||
|
|
||||||
|
class MicrowattSession(PowerFVSession, core_cls=MicrowattCore):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
MicrowattSession().main()
|
@ -1,209 +0,0 @@
|
|||||||
library ieee;
|
|
||||||
use ieee.std_logic_1164.all;
|
|
||||||
use ieee.numeric_std.all;
|
|
||||||
|
|
||||||
library work;
|
|
||||||
use work.common.all;
|
|
||||||
use work.wishbone_types.all;
|
|
||||||
use work.powerfv_types.all;
|
|
||||||
|
|
||||||
entity toplevel is
|
|
||||||
port (
|
|
||||||
clk : in std_ulogic;
|
|
||||||
rst : in std_ulogic;
|
|
||||||
|
|
||||||
-- Alternate reset (0xffff0000) for use by DRAM init fw
|
|
||||||
alt_reset : in std_ulogic;
|
|
||||||
|
|
||||||
-- Wishbone interface
|
|
||||||
wishbone_insn_in : in wishbone_slave_out;
|
|
||||||
wishbone_insn_out : out wishbone_master_out;
|
|
||||||
|
|
||||||
wishbone_data_in : in wishbone_slave_out;
|
|
||||||
wishbone_data_out : out wishbone_master_out;
|
|
||||||
|
|
||||||
wb_snoop_in : in wishbone_master_out;
|
|
||||||
|
|
||||||
dmi_addr : in std_ulogic_vector(3 downto 0);
|
|
||||||
dmi_din : in std_ulogic_vector(63 downto 0);
|
|
||||||
dmi_dout : out std_ulogic_vector(63 downto 0);
|
|
||||||
dmi_req : in std_ulogic;
|
|
||||||
dmi_wr : in std_ulogic;
|
|
||||||
dmi_ack : out std_ulogic;
|
|
||||||
|
|
||||||
ext_irq : in std_ulogic;
|
|
||||||
|
|
||||||
terminated_out : out std_logic;
|
|
||||||
|
|
||||||
pfv_stb : out std_ulogic;
|
|
||||||
pfv_insn : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_order : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_intr : out std_ulogic;
|
|
||||||
pfv_cia : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_nia : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_ra_index : out std_ulogic_vector( 4 downto 0);
|
|
||||||
pfv_ra_r_stb : out std_ulogic;
|
|
||||||
pfv_ra_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_ra_w_stb : out std_ulogic;
|
|
||||||
pfv_ra_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_rb_index : out std_ulogic_vector( 4 downto 0);
|
|
||||||
pfv_rb_r_stb : out std_ulogic;
|
|
||||||
pfv_rb_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_rs_index : out std_ulogic_vector( 4 downto 0);
|
|
||||||
pfv_rs_r_stb : out std_ulogic;
|
|
||||||
pfv_rs_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_rt_index : out std_ulogic_vector( 4 downto 0);
|
|
||||||
pfv_rt_r_stb : out std_ulogic;
|
|
||||||
pfv_rt_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_rt_w_stb : out std_ulogic;
|
|
||||||
pfv_rt_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_cr_r_stb : out std_ulogic_vector( 7 downto 0);
|
|
||||||
pfv_cr_r_data : out std_ulogic_vector(31 downto 0);
|
|
||||||
pfv_cr_w_stb : out std_ulogic_vector( 7 downto 0);
|
|
||||||
pfv_cr_w_data : out std_ulogic_vector(31 downto 0);
|
|
||||||
|
|
||||||
pfv_msr_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_msr_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_msr_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_msr_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_lr_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_lr_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_lr_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_lr_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_ctr_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_ctr_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_ctr_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_ctr_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_xer_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_xer_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_xer_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_xer_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_tar_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_tar_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_tar_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_tar_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_srr0_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr0_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr0_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr0_w_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
|
|
||||||
pfv_srr1_r_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr1_r_data : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr1_w_mask : out std_ulogic_vector(63 downto 0);
|
|
||||||
pfv_srr1_w_data : out std_ulogic_vector(63 downto 0)
|
|
||||||
);
|
|
||||||
end entity toplevel;
|
|
||||||
|
|
||||||
architecture behave of toplevel is
|
|
||||||
signal pfv : pfv_t;
|
|
||||||
begin
|
|
||||||
core: entity work.core
|
|
||||||
generic map (
|
|
||||||
SIM => false,
|
|
||||||
DISABLE_FLATTEN => false,
|
|
||||||
EX1_BYPASS => true,
|
|
||||||
HAS_FPU => false,
|
|
||||||
HAS_BTC => true,
|
|
||||||
HAS_SHORT_MULT => false,
|
|
||||||
HAS_POWERFV => true,
|
|
||||||
LOG_LENGTH => 0,
|
|
||||||
ICACHE_NUM_LINES => 2,
|
|
||||||
ICACHE_NUM_WAYS => 1,
|
|
||||||
ICACHE_TLB_SIZE => 1,
|
|
||||||
DCACHE_NUM_LINES => 2,
|
|
||||||
DCACHE_NUM_WAYS => 1,
|
|
||||||
DCACHE_TLB_SET_SIZE => 1,
|
|
||||||
DCACHE_TLB_NUM_WAYS => 1
|
|
||||||
)
|
|
||||||
port map (
|
|
||||||
clk => clk,
|
|
||||||
rst => rst,
|
|
||||||
alt_reset => alt_reset,
|
|
||||||
wishbone_insn_in => wishbone_insn_in,
|
|
||||||
wishbone_insn_out => wishbone_insn_out,
|
|
||||||
wishbone_data_in => wishbone_data_in,
|
|
||||||
wishbone_data_out => wishbone_data_out,
|
|
||||||
wb_snoop_in => wb_snoop_in,
|
|
||||||
dmi_addr => dmi_addr,
|
|
||||||
dmi_din => dmi_din,
|
|
||||||
dmi_dout => dmi_dout,
|
|
||||||
dmi_req => dmi_req,
|
|
||||||
dmi_wr => dmi_wr,
|
|
||||||
dmi_ack => dmi_ack,
|
|
||||||
ext_irq => ext_irq,
|
|
||||||
terminated_out => terminated_out,
|
|
||||||
pfv_out => pfv
|
|
||||||
);
|
|
||||||
|
|
||||||
pfv_stb <= pfv.stb;
|
|
||||||
pfv_insn <= pfv.insn;
|
|
||||||
pfv_order <= pfv.order;
|
|
||||||
pfv_intr <= pfv.intr;
|
|
||||||
pfv_cia <= pfv.cia;
|
|
||||||
pfv_nia <= pfv.nia;
|
|
||||||
|
|
||||||
pfv_ra_index <= pfv.ra.index;
|
|
||||||
pfv_ra_r_stb <= pfv.ra.r_stb;
|
|
||||||
pfv_ra_r_data <= pfv.ra.r_data;
|
|
||||||
pfv_ra_w_stb <= pfv.ra.w_stb;
|
|
||||||
pfv_ra_w_data <= pfv.ra.w_data;
|
|
||||||
pfv_rb_index <= pfv.rb.index;
|
|
||||||
pfv_rb_r_stb <= pfv.rb.r_stb;
|
|
||||||
pfv_rb_r_data <= pfv.rb.r_data;
|
|
||||||
pfv_rs_index <= pfv.rs.index;
|
|
||||||
pfv_rs_r_stb <= pfv.rs.r_stb;
|
|
||||||
pfv_rs_r_data <= pfv.rs.r_data;
|
|
||||||
pfv_rt_index <= pfv.rt.index;
|
|
||||||
pfv_rt_r_stb <= pfv.rt.r_stb;
|
|
||||||
pfv_rt_r_data <= pfv.rt.r_data;
|
|
||||||
pfv_rt_w_stb <= pfv.rt.w_stb;
|
|
||||||
pfv_rt_w_data <= pfv.rt.w_data;
|
|
||||||
|
|
||||||
pfv_cr_r_stb <= pfv.cr.r_stb;
|
|
||||||
pfv_cr_r_data <= pfv.cr.r_data;
|
|
||||||
pfv_cr_w_stb <= pfv.cr.w_stb;
|
|
||||||
pfv_cr_w_data <= pfv.cr.w_data;
|
|
||||||
|
|
||||||
pfv_msr_r_mask <= pfv.msr.r_mask;
|
|
||||||
pfv_msr_r_data <= pfv.msr.r_data;
|
|
||||||
pfv_msr_w_mask <= pfv.msr.w_mask;
|
|
||||||
pfv_msr_w_data <= pfv.msr.w_data;
|
|
||||||
|
|
||||||
pfv_lr_r_mask <= pfv.lr.r_mask;
|
|
||||||
pfv_lr_r_data <= pfv.lr.r_data;
|
|
||||||
pfv_lr_w_mask <= pfv.lr.w_mask;
|
|
||||||
pfv_lr_w_data <= pfv.lr.w_data;
|
|
||||||
|
|
||||||
pfv_ctr_r_mask <= pfv.ctr.r_mask;
|
|
||||||
pfv_ctr_r_data <= pfv.ctr.r_data;
|
|
||||||
pfv_ctr_w_mask <= pfv.ctr.w_mask;
|
|
||||||
pfv_ctr_w_data <= pfv.ctr.w_data;
|
|
||||||
|
|
||||||
pfv_xer_r_mask <= pfv.xer.r_mask;
|
|
||||||
pfv_xer_r_data <= pfv.xer.r_data;
|
|
||||||
pfv_xer_w_mask <= pfv.xer.w_mask;
|
|
||||||
pfv_xer_w_data <= pfv.xer.w_data;
|
|
||||||
|
|
||||||
pfv_tar_r_mask <= pfv.tar.r_mask;
|
|
||||||
pfv_tar_r_data <= pfv.tar.r_data;
|
|
||||||
pfv_tar_w_mask <= pfv.tar.w_mask;
|
|
||||||
pfv_tar_w_data <= pfv.tar.w_data;
|
|
||||||
|
|
||||||
pfv_srr0_r_mask <= pfv.srr0.r_mask;
|
|
||||||
pfv_srr0_r_data <= pfv.srr0.r_data;
|
|
||||||
pfv_srr0_w_mask <= pfv.srr0.w_mask;
|
|
||||||
pfv_srr0_w_data <= pfv.srr0.w_data;
|
|
||||||
|
|
||||||
pfv_srr1_r_mask <= pfv.srr1.r_mask;
|
|
||||||
pfv_srr1_r_data <= pfv.srr1.r_data;
|
|
||||||
pfv_srr1_w_mask <= pfv.srr1.w_mask;
|
|
||||||
pfv_srr1_w_data <= pfv.srr1.w_data;
|
|
||||||
|
|
||||||
end architecture behave;
|
|
@ -1,194 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import os
|
|
||||||
import multiprocessing
|
|
||||||
|
|
||||||
from amaranth import *
|
|
||||||
|
|
||||||
from power_fv.checks import *
|
|
||||||
from power_fv.checks.all import *
|
|
||||||
from power_fv.build import SymbiYosysPlatform
|
|
||||||
|
|
||||||
from _wrapper import MicrowattWrapper
|
|
||||||
|
|
||||||
|
|
||||||
def microwatt_files():
|
|
||||||
_filenames = (
|
|
||||||
"cache_ram.vhdl",
|
|
||||||
"common.vhdl",
|
|
||||||
"control.vhdl",
|
|
||||||
"core_debug.vhdl",
|
|
||||||
"core.vhdl",
|
|
||||||
"countbits.vhdl",
|
|
||||||
"cr_file.vhdl",
|
|
||||||
"crhelpers.vhdl",
|
|
||||||
"dcache.vhdl",
|
|
||||||
"decode1.vhdl",
|
|
||||||
"decode2.vhdl",
|
|
||||||
"decode_types.vhdl",
|
|
||||||
"divider.vhdl",
|
|
||||||
"execute1.vhdl",
|
|
||||||
"fetch1.vhdl",
|
|
||||||
"fpu.vhdl",
|
|
||||||
"helpers.vhdl",
|
|
||||||
"icache.vhdl",
|
|
||||||
"insn_helpers.vhdl",
|
|
||||||
"loadstore1.vhdl",
|
|
||||||
"logical.vhdl",
|
|
||||||
"mmu.vhdl",
|
|
||||||
"multiply.vhdl",
|
|
||||||
"nonrandom.vhdl",
|
|
||||||
"plru.vhdl",
|
|
||||||
"pmu.vhdl",
|
|
||||||
"powerfv_types.vhdl",
|
|
||||||
"powerfv.vhdl",
|
|
||||||
"ppc_fx_insns.vhdl",
|
|
||||||
"register_file.vhdl",
|
|
||||||
"rotator.vhdl",
|
|
||||||
"utils.vhdl",
|
|
||||||
"wishbone_types.vhdl",
|
|
||||||
"writeback.vhdl",
|
|
||||||
)
|
|
||||||
|
|
||||||
for filename in _filenames:
|
|
||||||
contents = open(os.path.join(os.curdir, "microwatt", filename), "r").read()
|
|
||||||
yield filename, contents
|
|
||||||
|
|
||||||
top_filename = "microwatt_top.vhdl"
|
|
||||||
top_contents = open(os.path.join(os.curdir, top_filename), "r").read()
|
|
||||||
yield top_filename, top_contents
|
|
||||||
|
|
||||||
|
|
||||||
def run_check(*args):
|
|
||||||
check_name, tb_args = args
|
|
||||||
|
|
||||||
check = PowerFVCheck.registry[check_name]()
|
|
||||||
dut = MicrowattWrapper()
|
|
||||||
tb = check.get_testbench(dut, **tb_args)
|
|
||||||
|
|
||||||
platform = SymbiYosysPlatform()
|
|
||||||
for filename, contents in microwatt_files():
|
|
||||||
platform.add_file(filename, contents)
|
|
||||||
|
|
||||||
tb_name = "{}_tb".format(check_name)
|
|
||||||
build_dir = "build/{}".format(tb_name)
|
|
||||||
|
|
||||||
platform.build(
|
|
||||||
top = tb,
|
|
||||||
name = tb_name,
|
|
||||||
build_dir = build_dir,
|
|
||||||
mode = "bmc",
|
|
||||||
ghdl_opts = "--std=08",
|
|
||||||
prep_opts = "-nordff",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-j", "--jobs",
|
|
||||||
help="Number of worker processes (default: os.cpu_count())",
|
|
||||||
type=int,
|
|
||||||
default=os.cpu_count(),
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
checks = [
|
|
||||||
("cons_unique", {"post": 15, "pre": 12}),
|
|
||||||
("cons_ia_fwd", {"post": 15}),
|
|
||||||
("cons_gpr", {"post": 15}),
|
|
||||||
("cons_cr", {"post": 15}),
|
|
||||||
("cons_lr", {"post": 15}),
|
|
||||||
("cons_ctr", {"post": 15}),
|
|
||||||
("cons_xer", {"post": 15}),
|
|
||||||
("cons_tar", {"post": 15}),
|
|
||||||
("cons_srr0", {"post": 15}),
|
|
||||||
("cons_srr1", {"post": 15}),
|
|
||||||
|
|
||||||
("insn_b", {"post": 15}),
|
|
||||||
("insn_ba", {"post": 15}),
|
|
||||||
("insn_bl", {"post": 15}),
|
|
||||||
("insn_bla", {"post": 15}),
|
|
||||||
("insn_bc", {"post": 15}),
|
|
||||||
("insn_bca", {"post": 15}),
|
|
||||||
("insn_bcl", {"post": 15}),
|
|
||||||
("insn_bcla", {"post": 15}),
|
|
||||||
("insn_bclr", {"post": 15}),
|
|
||||||
("insn_bclrl", {"post": 15}),
|
|
||||||
("insn_bcctr", {"post": 15}),
|
|
||||||
("insn_bcctrl", {"post": 15}),
|
|
||||||
("insn_bctar", {"post": 15}),
|
|
||||||
("insn_bctarl", {"post": 15}),
|
|
||||||
|
|
||||||
("insn_crand", {"post": 15}),
|
|
||||||
("insn_cror", {"post": 15}),
|
|
||||||
("insn_crnand", {"post": 15}),
|
|
||||||
("insn_crxor", {"post": 15}),
|
|
||||||
("insn_crnor", {"post": 15}),
|
|
||||||
("insn_crandc", {"post": 15}),
|
|
||||||
("insn_creqv", {"post": 15}),
|
|
||||||
("insn_crorc", {"post": 15}),
|
|
||||||
("insn_mcrf", {"post": 15}),
|
|
||||||
|
|
||||||
("insn_addi", {"post": 15}),
|
|
||||||
("insn_addis", {"post": 15}),
|
|
||||||
("insn_addpcis", {"post": 15}),
|
|
||||||
("insn_add", {"post": 15}),
|
|
||||||
("insn_add.", {"post": 15}),
|
|
||||||
("insn_addo", {"post": 15}),
|
|
||||||
("insn_addo.", {"post": 15}),
|
|
||||||
("insn_addic", {"post": 15}),
|
|
||||||
("insn_addic.", {"post": 15}),
|
|
||||||
("insn_subf", {"post": 15}),
|
|
||||||
("insn_subf.", {"post": 15}),
|
|
||||||
("insn_subfo", {"post": 15}),
|
|
||||||
("insn_subfo.", {"post": 15}),
|
|
||||||
("insn_subfic", {"post": 15}),
|
|
||||||
("insn_addc", {"post": 15}),
|
|
||||||
("insn_addc.", {"post": 15}),
|
|
||||||
("insn_addco", {"post": 15}),
|
|
||||||
("insn_addco.", {"post": 15}),
|
|
||||||
("insn_adde", {"post": 15}),
|
|
||||||
("insn_adde.", {"post": 15}),
|
|
||||||
("insn_addeo", {"post": 15}),
|
|
||||||
("insn_addeo.", {"post": 15}),
|
|
||||||
("insn_subfc", {"post": 15}),
|
|
||||||
("insn_subfc.", {"post": 15}),
|
|
||||||
("insn_subfco", {"post": 15}),
|
|
||||||
("insn_subfco.", {"post": 15}),
|
|
||||||
("insn_subfe", {"post": 15}),
|
|
||||||
("insn_subfe.", {"post": 15}),
|
|
||||||
("insn_subfeo", {"post": 15}),
|
|
||||||
("insn_subfeo.", {"post": 15}),
|
|
||||||
("insn_addme", {"post": 15}),
|
|
||||||
("insn_addme.", {"post": 15}),
|
|
||||||
("insn_addmeo", {"post": 15}),
|
|
||||||
("insn_addmeo.", {"post": 15}),
|
|
||||||
("insn_addze", {"post": 15}),
|
|
||||||
("insn_addze.", {"post": 15}),
|
|
||||||
("insn_addzeo", {"post": 15}),
|
|
||||||
("insn_addzeo.", {"post": 15}),
|
|
||||||
("insn_subfme", {"post": 15}),
|
|
||||||
("insn_subfme.", {"post": 15}),
|
|
||||||
("insn_subfmeo", {"post": 15}),
|
|
||||||
("insn_subfmeo.", {"post": 15}),
|
|
||||||
("insn_subfze", {"post": 15}),
|
|
||||||
("insn_subfze.", {"post": 15}),
|
|
||||||
("insn_subfzeo", {"post": 15}),
|
|
||||||
("insn_subfzeo.", {"post": 15}),
|
|
||||||
("insn_addex", {"post": 15}),
|
|
||||||
("insn_neg", {"post": 15}),
|
|
||||||
("insn_neg.", {"post": 15}),
|
|
||||||
("insn_nego", {"post": 15}),
|
|
||||||
("insn_nego.", {"post": 15}),
|
|
||||||
|
|
||||||
("insn_cmpi", {"post": 15}),
|
|
||||||
("insn_cmpli", {"post": 15}),
|
|
||||||
("insn_cmp", {"post": 15}),
|
|
||||||
("insn_cmpl", {"post": 15}),
|
|
||||||
|
|
||||||
("insn_mtspr", {"post": 15}),
|
|
||||||
("insn_mfspr", {"post": 15}),
|
|
||||||
]
|
|
||||||
|
|
||||||
with multiprocessing.Pool(processes=args.jobs) as pool:
|
|
||||||
pool.starmap(run_check, checks)
|
|
@ -1 +0,0 @@
|
|||||||
from .plat import *
|
|
@ -0,0 +1,73 @@
|
|||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from power_fv.build import sby
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PowerFVCheck"]
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFVCheckMeta(ABCMeta):
|
||||||
|
all_checks = {}
|
||||||
|
|
||||||
|
def __new__(metacls, clsname, bases, namespace, name=None, **kwargs):
|
||||||
|
if name is not None:
|
||||||
|
if name in metacls.all_checks:
|
||||||
|
raise NameError("Check {!r} already exists".format(name))
|
||||||
|
namespace["name"] = name
|
||||||
|
|
||||||
|
cls = ABCMeta.__new__(metacls, clsname, bases, namespace, **kwargs)
|
||||||
|
if name is not None:
|
||||||
|
metacls.all_checks[name] = cls
|
||||||
|
cls.name = name
|
||||||
|
return cls
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find(cls, *name):
|
||||||
|
for check_name, check_cls in cls.all_checks.items():
|
||||||
|
assert isinstance(check_name, tuple)
|
||||||
|
if len(name) > len(check_name):
|
||||||
|
continue
|
||||||
|
if name == check_name[:len(name)]:
|
||||||
|
yield check_name, check_cls
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFVCheck(metaclass=PowerFVCheckMeta):
|
||||||
|
@classmethod
|
||||||
|
def add_check_arguments(cls, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"name", metavar="NAME", type=str, help="name of the check")
|
||||||
|
parser.add_argument(
|
||||||
|
"--depth", type=int, default=15,
|
||||||
|
help="depth of the BMC, in clock cycles (default: %(default)s)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip", type=int, default=None,
|
||||||
|
help="skip the specified number of clock cycles (default: DEPTH-1))")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_build_arguments(cls, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"--build-dir", type=Path, default=Path("./build"),
|
||||||
|
help="output directory (default: %(default)s)")
|
||||||
|
|
||||||
|
def __init__(self, *, depth, skip, core, **kwargs):
|
||||||
|
self.depth = depth
|
||||||
|
self.skip = skip if skip is not None else depth - 1
|
||||||
|
self.core = core
|
||||||
|
self.dut = core.wrapper(**kwargs)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def testbench(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def build(self, *, build_dir, **kwargs):
|
||||||
|
platform = sby.SymbiYosysPlatform()
|
||||||
|
self.core.add_files(platform, self.dut, **kwargs)
|
||||||
|
|
||||||
|
top = self.testbench()
|
||||||
|
build_dir = build_dir / top.name
|
||||||
|
overrides = {key: str(value) for key, value in kwargs.items()}
|
||||||
|
overrides["depth"] = str(self.depth)
|
||||||
|
overrides["skip"] = str(self.skip)
|
||||||
|
|
||||||
|
return platform.build(top, name=top.name, build_dir=build_dir, **overrides)
|
@ -0,0 +1,24 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Timer"]
|
||||||
|
|
||||||
|
|
||||||
|
class Timer(Elaboratable):
|
||||||
|
def __init__(self, start):
|
||||||
|
if not isinstance(start, int) or start < 0:
|
||||||
|
raise TypeError("Start value must be a non-negative integer, not {!r}"
|
||||||
|
.format(start))
|
||||||
|
|
||||||
|
self.ctr = Signal(range(start + 1), reset=start, reset_less=True)
|
||||||
|
self.zero = Signal()
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
with m.If(self.ctr != 0):
|
||||||
|
m.d.sync += self.ctr.eq(self.ctr - 1)
|
||||||
|
|
||||||
|
m.d.comb += self.zero.eq(self.ctr == 0)
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,4 @@
|
|||||||
|
from .unique import *
|
||||||
|
from .cia import *
|
||||||
|
from .gpr import *
|
||||||
|
from .insn.all import *
|
@ -0,0 +1,56 @@
|
|||||||
|
from amaranth import *
|
||||||
|
from amaranth.asserts import *
|
||||||
|
|
||||||
|
from power_fv.check import PowerFVCheck
|
||||||
|
from power_fv.check._timer import Timer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CIACheck"]
|
||||||
|
|
||||||
|
|
||||||
|
class CIACheck(PowerFVCheck, name=("cia",)):
|
||||||
|
"""CIA check.
|
||||||
|
|
||||||
|
Given a pair of instructions retiring in order, the CIA of the second must match the NIA
|
||||||
|
of the first.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def testbench(self):
|
||||||
|
return CIATestbench(self)
|
||||||
|
|
||||||
|
|
||||||
|
class CIATestbench(Elaboratable):
|
||||||
|
def __init__(self, check):
|
||||||
|
if not isinstance(check, CIACheck):
|
||||||
|
raise TypeError("Check must be an instance of CIACheck, not {!r}"
|
||||||
|
.format(check))
|
||||||
|
self.check = check
|
||||||
|
self.name = "cia_tb"
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.submodules.t_post = t_post = Timer(self.check.depth - 1)
|
||||||
|
m.submodules.dut = dut = self.check.dut
|
||||||
|
|
||||||
|
pred_stb = Signal()
|
||||||
|
pred_order = Signal(unsigned(64))
|
||||||
|
pred_nia = Signal(unsigned(64))
|
||||||
|
|
||||||
|
m.d.comb += pred_order.eq(AnyConst(64))
|
||||||
|
|
||||||
|
with m.If(dut.pfv.stb & (dut.pfv.order == pred_order)):
|
||||||
|
m.d.sync += [
|
||||||
|
pred_stb.eq(1),
|
||||||
|
pred_nia.eq(dut.pfv.nia)
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.If(t_post.zero):
|
||||||
|
m.d.comb += [
|
||||||
|
Assume(dut.pfv.stb),
|
||||||
|
Assume(dut.pfv.order == pred_order + 1),
|
||||||
|
Assume(dut.pfv.order > 0),
|
||||||
|
Assert(pred_stb.implies(dut.pfv.cia == pred_nia)),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,100 @@
|
|||||||
|
from amaranth import *
|
||||||
|
from amaranth.lib.coding import Encoder
|
||||||
|
from amaranth.asserts import *
|
||||||
|
|
||||||
|
from power_fv.check import PowerFVCheck
|
||||||
|
from power_fv.check._timer import Timer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["GPRCheck"]
|
||||||
|
|
||||||
|
|
||||||
|
class GPRCheck(PowerFVCheck, name=("gpr",)):
|
||||||
|
"""GPR consistency check.
|
||||||
|
|
||||||
|
Checks that:
|
||||||
|
* reads from a GPR return the last value that was written to it;
|
||||||
|
* writes to multiple GPRs by a single instruction (e.g. load with update) do not conflict
|
||||||
|
with each other.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def testbench(self):
|
||||||
|
return GPRTestbench(self)
|
||||||
|
|
||||||
|
|
||||||
|
class GPRTestbench(Elaboratable):
|
||||||
|
def __init__(self, check):
|
||||||
|
if not isinstance(check, GPRCheck):
|
||||||
|
raise TypeError("Check must be an instance of GPRCheck, not {!r}"
|
||||||
|
.format(check))
|
||||||
|
self.check = check
|
||||||
|
self.name = "gpr_tb"
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.submodules.t_post = t_post = Timer(self.check.depth - 1)
|
||||||
|
m.submodules.dut = dut = self.check.dut
|
||||||
|
|
||||||
|
spec_order = AnyConst(dut.pfv.order.shape())
|
||||||
|
spec_index = AnyConst(unsigned(5))
|
||||||
|
|
||||||
|
gpr_write = Record([("ra", 1), ("rb", 1), ("rs", 1), ("rt", 1)])
|
||||||
|
gpr_read = Record.like(gpr_write)
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
gpr_write.ra.eq(dut.pfv.ra.w_stb & (dut.pfv.ra.index == spec_index)),
|
||||||
|
gpr_write.rb.eq(dut.pfv.rb.w_stb & (dut.pfv.rb.index == spec_index)),
|
||||||
|
gpr_write.rs.eq(dut.pfv.rs.w_stb & (dut.pfv.rs.index == spec_index)),
|
||||||
|
gpr_write.rt.eq(dut.pfv.rt.w_stb & (dut.pfv.rt.index == spec_index)),
|
||||||
|
|
||||||
|
gpr_read .ra.eq(dut.pfv.ra.r_stb & (dut.pfv.ra.index == spec_index)),
|
||||||
|
gpr_read .rb.eq(dut.pfv.rb.r_stb & (dut.pfv.rb.index == spec_index)),
|
||||||
|
gpr_read .rs.eq(dut.pfv.rs.r_stb & (dut.pfv.rs.index == spec_index)),
|
||||||
|
gpr_read .rt.eq(dut.pfv.rt.r_stb & (dut.pfv.rt.index == spec_index)),
|
||||||
|
]
|
||||||
|
|
||||||
|
gpr_conflict = Record([("prev", 1), ("curr", 1)])
|
||||||
|
gpr_written = Record([("prev", 1)])
|
||||||
|
gpr_shadow = Record([("prev", 64)])
|
||||||
|
|
||||||
|
m.submodules.write_enc = write_enc = Encoder(len(gpr_write))
|
||||||
|
m.d.comb += write_enc.i.eq(gpr_write)
|
||||||
|
|
||||||
|
with m.If(dut.pfv.stb & write_enc.i.any() & write_enc.n):
|
||||||
|
# Write conflict: more then one bit of `gpr_write` is asserted.
|
||||||
|
m.d.comb += gpr_conflict.curr.eq(1)
|
||||||
|
m.d.sync += gpr_conflict.prev.eq(dut.pfv.order < spec_order)
|
||||||
|
|
||||||
|
with m.If(dut.pfv.stb & (dut.pfv.order < spec_order)):
|
||||||
|
with m.If(gpr_write.any()):
|
||||||
|
m.d.sync += gpr_written.prev.eq(1)
|
||||||
|
with m.If(gpr_write.ra):
|
||||||
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.ra.w_data)
|
||||||
|
with m.If(gpr_write.rb):
|
||||||
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rb.w_data)
|
||||||
|
with m.If(gpr_write.rs):
|
||||||
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rs.w_data)
|
||||||
|
with m.If(gpr_write.rt):
|
||||||
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rt.w_data)
|
||||||
|
|
||||||
|
with m.If(t_post.zero):
|
||||||
|
m.d.comb += [
|
||||||
|
Assume(dut.pfv.stb),
|
||||||
|
Assume(dut.pfv.order == spec_order),
|
||||||
|
Assert(~gpr_conflict.curr),
|
||||||
|
Assert(~gpr_conflict.prev),
|
||||||
|
]
|
||||||
|
with m.If(gpr_written.prev):
|
||||||
|
with m.If(gpr_read.ra):
|
||||||
|
m.d.comb += Assert(dut.pfv.ra.r_data == gpr_shadow.prev)
|
||||||
|
with m.If(gpr_read.rb):
|
||||||
|
m.d.comb += Assert(dut.pfv.rb.r_data == gpr_shadow.prev)
|
||||||
|
with m.If(gpr_read.rs):
|
||||||
|
m.d.comb += Assert(dut.pfv.rs.r_data == gpr_shadow.prev)
|
||||||
|
with m.If(gpr_read.rt):
|
||||||
|
m.d.comb += Assert(dut.pfv.rt.r_data == gpr_shadow.prev)
|
||||||
|
|
||||||
|
m.d.comb += Assert(~Past(t_post.zero))
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,204 @@
|
|||||||
|
from amaranth import *
|
||||||
|
from amaranth.asserts import *
|
||||||
|
|
||||||
|
from power_fv.check import PowerFVCheck, PowerFVCheckMeta
|
||||||
|
from power_fv.check._timer import Timer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["InsnCheckMeta", "InsnCheck", "InsnTestbench"]
|
||||||
|
|
||||||
|
|
||||||
|
class InsnCheckMeta(PowerFVCheckMeta):
|
||||||
|
def __new__(metacls, clsname, bases, namespace, spec_cls=None, insn_cls=None, **kwargs):
|
||||||
|
if insn_cls is not None:
|
||||||
|
name = ("insn", insn_cls.__name__.lower())
|
||||||
|
else:
|
||||||
|
name = None
|
||||||
|
|
||||||
|
cls = PowerFVCheckMeta.__new__(metacls, clsname, bases, namespace, name=name, **kwargs)
|
||||||
|
if spec_cls is not None:
|
||||||
|
cls.spec_cls = spec_cls
|
||||||
|
if insn_cls is not None:
|
||||||
|
cls.insn_cls = insn_cls
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
class InsnCheck(PowerFVCheck, metaclass=InsnCheckMeta):
|
||||||
|
def __init__(self, *, depth, skip, core, **kwargs):
|
||||||
|
super().__init__(depth=depth, skip=skip, core=core, **kwargs)
|
||||||
|
self.insn = self.insn_cls()
|
||||||
|
self.spec = self.spec_cls(self.insn)
|
||||||
|
|
||||||
|
def testbench(self):
|
||||||
|
return InsnTestbench(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InsnTestbench(Elaboratable):
|
||||||
|
def __init__(self, check):
|
||||||
|
if not isinstance(check, InsnCheck):
|
||||||
|
raise TypeError("Check must be an instance of InsnCheck, not {!r}"
|
||||||
|
.format(check))
|
||||||
|
self.check = check
|
||||||
|
self.name = "{}_tb".format("_".join(check.name))
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.submodules.t_post = t_post = Timer(self.check.depth - 1)
|
||||||
|
m.submodules.dut = dut = self.check.dut
|
||||||
|
m.submodules.spec = spec = self.check.spec
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
spec.pfv.order.eq(dut.pfv.order),
|
||||||
|
spec.pfv.cia .eq(dut.pfv.cia),
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.If(t_post.zero):
|
||||||
|
m.d.comb += [
|
||||||
|
Assume(dut.pfv.stb),
|
||||||
|
Assume(dut.pfv.insn == spec.pfv.insn),
|
||||||
|
Assert(dut.pfv.intr == spec.pfv.intr),
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.If(t_post.zero & ~spec.pfv.intr):
|
||||||
|
m.d.comb += [
|
||||||
|
Assert(dut.pfv.nia == spec.pfv.nia),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.submodules.ra = ra = _GPRFileTest(self.check, port="ra")
|
||||||
|
m.submodules.rb = rb = _GPRFileTest(self.check, port="rb")
|
||||||
|
m.submodules.rs = rs = _GPRFileTest(self.check, port="rs")
|
||||||
|
m.submodules.rt = rt = _GPRFileTest(self.check, port="rt")
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
spec.pfv.ra.r_data.eq(dut.pfv.ra.r_data),
|
||||||
|
spec.pfv.rb.r_data.eq(dut.pfv.rb.r_data),
|
||||||
|
spec.pfv.rs.r_data.eq(dut.pfv.rs.r_data),
|
||||||
|
spec.pfv.rt.r_data.eq(dut.pfv.rt.r_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.If(t_post.zero & ~spec.pfv.intr):
|
||||||
|
m.d.comb += [
|
||||||
|
Assert(ra.valid.all()),
|
||||||
|
Assert(rb.valid.all()),
|
||||||
|
Assert(rs.valid.all()),
|
||||||
|
Assert(rt.valid.all()),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.submodules.cr = cr = _SysRegTest(self.check, reg="cr" )
|
||||||
|
m.submodules.msr = msr = _SysRegTest(self.check, reg="msr" )
|
||||||
|
m.submodules.lr = lr = _SysRegTest(self.check, reg="lr" )
|
||||||
|
m.submodules.ctr = ctr = _SysRegTest(self.check, reg="ctr" )
|
||||||
|
m.submodules.tar = tar = _SysRegTest(self.check, reg="tar" )
|
||||||
|
m.submodules.xer = xer = _SysRegTest(self.check, reg="xer" )
|
||||||
|
m.submodules.srr0 = srr0 = _SysRegTest(self.check, reg="srr0")
|
||||||
|
m.submodules.srr1 = srr1 = _SysRegTest(self.check, reg="srr1")
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
spec.pfv.cr .r_data.eq(dut.pfv.cr .r_data),
|
||||||
|
spec.pfv.msr .r_data.eq(dut.pfv.msr .r_data),
|
||||||
|
spec.pfv.lr .r_data.eq(dut.pfv.lr .r_data),
|
||||||
|
spec.pfv.ctr .r_data.eq(dut.pfv.ctr .r_data),
|
||||||
|
spec.pfv.tar .r_data.eq(dut.pfv.tar .r_data),
|
||||||
|
spec.pfv.xer .r_data.eq(dut.pfv.xer .r_data),
|
||||||
|
spec.pfv.srr0.r_data.eq(dut.pfv.srr0.r_data),
|
||||||
|
spec.pfv.srr1.r_data.eq(dut.pfv.srr1.r_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.If(t_post.zero & ~spec.pfv.intr):
|
||||||
|
m.d.comb += [
|
||||||
|
Assert(cr .valid.all()),
|
||||||
|
Assert(msr .valid.all()),
|
||||||
|
Assert(lr .valid.all()),
|
||||||
|
Assert(ctr .valid.all()),
|
||||||
|
Assert(tar .valid.all()),
|
||||||
|
Assert(xer .valid.all()),
|
||||||
|
Assert(srr0.valid.all()),
|
||||||
|
Assert(srr1.valid.all()),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.d.comb += Assert(~Past(t_post.zero))
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class _GPRFileTest(Elaboratable):
|
||||||
|
def __init__(self, check, *, port):
|
||||||
|
self._dut = getattr(check.dut .pfv, port)
|
||||||
|
self._spec = getattr(check.spec.pfv, port)
|
||||||
|
|
||||||
|
self.valid = Record([
|
||||||
|
("read" , [("index", 1), ("r_stb", 1)]),
|
||||||
|
("write", [("index", 1), ("w_stb", 1), ("w_data", 1)]),
|
||||||
|
])
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
dut = Record.like(self._dut )
|
||||||
|
spec = Record.like(self._spec)
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
dut .eq(self._dut ),
|
||||||
|
spec.eq(self._spec),
|
||||||
|
|
||||||
|
# If the spec reads from a GPR, the DUT must read it too.
|
||||||
|
self.valid.read.index.eq(spec.r_stb.implies(dut.index == spec.index)),
|
||||||
|
self.valid.read.r_stb.eq(spec.r_stb.implies(dut.r_stb)),
|
||||||
|
|
||||||
|
# The DUT and the spec must write the same value to the same GPR.
|
||||||
|
self.valid.write.index .eq(spec.w_stb.implies(dut.index == spec.index)),
|
||||||
|
self.valid.write.w_stb .eq(spec.w_stb == dut.w_stb),
|
||||||
|
self.valid.write.w_data.eq(spec.w_stb.implies(dut.w_data == spec.w_data)),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class _SysRegTest(Elaboratable):
|
||||||
|
def __init__(self, check, *, reg):
|
||||||
|
self._dut = getattr(check.dut .pfv, reg)
|
||||||
|
self._spec = getattr(check.spec.pfv, reg)
|
||||||
|
|
||||||
|
self.valid = Record([
|
||||||
|
("read" , [("r_mask", 1)]),
|
||||||
|
("write", [("w_mask", 1), ("w_data", 1), ("r_mask", 1), ("r_data", 1)]),
|
||||||
|
])
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
dut = Record([
|
||||||
|
("r_mask", len(self._dut.r_mask)),
|
||||||
|
("r_data", len(self._dut.r_data)),
|
||||||
|
("w_mask", len(self._dut.w_mask)),
|
||||||
|
("w_data", len(self._dut.w_data)),
|
||||||
|
])
|
||||||
|
spec = Record.like(dut)
|
||||||
|
keep = Record([
|
||||||
|
("w_mask", len(self._dut.w_mask)),
|
||||||
|
])
|
||||||
|
|
||||||
|
def contains(a, mask, b=None):
|
||||||
|
if b is None:
|
||||||
|
b = mask
|
||||||
|
return a & mask == b & mask
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
dut .eq(self._dut ),
|
||||||
|
spec.eq(self._spec),
|
||||||
|
|
||||||
|
# The DUT and the spec must read from the same bits.
|
||||||
|
self.valid.read.r_mask.eq(contains(dut.r_mask, spec.r_mask)),
|
||||||
|
|
||||||
|
# The DUT and the spec must write the same values to the same bits.
|
||||||
|
self.valid.write.w_mask.eq(contains(dut.w_mask, spec.w_mask)),
|
||||||
|
self.valid.write.w_data.eq(contains(dut.w_data, spec.w_mask, spec.w_data)),
|
||||||
|
|
||||||
|
# The DUT may write to more bits than the spec iff their values are preserved.
|
||||||
|
keep.w_mask.eq(dut.w_mask & ~spec.w_mask),
|
||||||
|
self.valid.write.r_mask.eq(contains(dut.r_mask, keep.w_mask)),
|
||||||
|
self.valid.write.r_data.eq(contains(dut.r_data, keep.w_mask, dut.w_data)),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,73 @@
|
|||||||
|
from power_fv.insn import const
|
||||||
|
from power_fv.insn.spec.addsub import AddSubSpec
|
||||||
|
from power_fv.check.insn import InsnCheck
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ADDI" , "ADDIS" , "ADDPCIS",
|
||||||
|
"ADD" , "ADD_" , "ADDO" , "ADDO_" , "ADDIC" , "ADDIC_",
|
||||||
|
"SUBF" , "SUBF_" , "SUBFO" , "SUBFO_" , "SUBFIC",
|
||||||
|
"ADDC" , "ADDC_" , "ADDCO" , "ADDCO_" ,
|
||||||
|
"ADDE" , "ADDE_" , "ADDEO" , "ADDEO_" ,
|
||||||
|
"SUBFC" , "SUBFC_" , "SUBFCO" , "SUBFCO_" ,
|
||||||
|
"SUBFE" , "SUBFE_" , "SUBFEO" , "SUBFEO_" ,
|
||||||
|
"ADDME" , "ADDME_" , "ADDMEO" , "ADDMEO_" ,
|
||||||
|
"ADDZE" , "ADDZE_" , "ADDZEO" , "ADDZEO_" ,
|
||||||
|
"SUBFME", "SUBFME_", "SUBFMEO", "SUBFMEO_",
|
||||||
|
"SUBFZE", "SUBFZE_", "SUBFZEO", "SUBFZEO_",
|
||||||
|
"ADDEX" ,
|
||||||
|
"NEG" , "NEG_" , "NEGO" , "NEGO_" ,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ADDI (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDI ): pass
|
||||||
|
class ADDIS (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDIS ): pass
|
||||||
|
class ADDPCIS (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDPCIS ): pass
|
||||||
|
class ADD (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADD ): pass
|
||||||
|
class ADD_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADD_ ): pass
|
||||||
|
class ADDO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDO ): pass
|
||||||
|
class ADDO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDO_ ): pass
|
||||||
|
class ADDIC (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDIC ): pass
|
||||||
|
class ADDIC_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDIC_ ): pass
|
||||||
|
class SUBF (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBF ): pass
|
||||||
|
class SUBF_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBF_ ): pass
|
||||||
|
class SUBFO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFO ): pass
|
||||||
|
class SUBFO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFO_ ): pass
|
||||||
|
class SUBFIC (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFIC ): pass
|
||||||
|
class ADDC (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDC ): pass
|
||||||
|
class ADDC_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDC_ ): pass
|
||||||
|
class ADDCO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDCO ): pass
|
||||||
|
class ADDCO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDCO_ ): pass
|
||||||
|
class ADDE (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDE ): pass
|
||||||
|
class ADDE_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDE_ ): pass
|
||||||
|
class ADDEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDEO ): pass
|
||||||
|
class ADDEO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDEO_ ): pass
|
||||||
|
class SUBFC (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFC ): pass
|
||||||
|
class SUBFC_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFC_ ): pass
|
||||||
|
class SUBFCO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFCO ): pass
|
||||||
|
class SUBFCO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFCO_ ): pass
|
||||||
|
class SUBFE (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFE ): pass
|
||||||
|
class SUBFE_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFE_ ): pass
|
||||||
|
class SUBFEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFEO ): pass
|
||||||
|
class SUBFEO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFEO_ ): pass
|
||||||
|
class ADDME (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDME ): pass
|
||||||
|
class ADDME_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDME_ ): pass
|
||||||
|
class ADDMEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDMEO ): pass
|
||||||
|
class ADDMEO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDMEO_ ): pass
|
||||||
|
class ADDZE (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDZE ): pass
|
||||||
|
class ADDZE_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDZE_ ): pass
|
||||||
|
class ADDZEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDZEO ): pass
|
||||||
|
class ADDZEO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDZEO_ ): pass
|
||||||
|
class SUBFME (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFME ): pass
|
||||||
|
class SUBFME_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFME_ ): pass
|
||||||
|
class SUBFMEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFMEO ): pass
|
||||||
|
class SUBFMEO_(InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFMEO_): pass
|
||||||
|
class SUBFZE (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFZE ): pass
|
||||||
|
class SUBFZE_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFZE_ ): pass
|
||||||
|
class SUBFZEO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFZEO ): pass
|
||||||
|
class SUBFZEO_(InsnCheck, spec_cls=AddSubSpec, insn_cls=const.SUBFZEO_): pass
|
||||||
|
class ADDEX (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.ADDEX ): pass
|
||||||
|
class NEG (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.NEG ): pass
|
||||||
|
class NEG_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.NEG_ ): pass
|
||||||
|
class NEGO (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.NEGO ): pass
|
||||||
|
class NEGO_ (InsnCheck, spec_cls=AddSubSpec, insn_cls=const.NEGO_ ): pass
|
@ -0,0 +1,5 @@
|
|||||||
|
from .addsub import *
|
||||||
|
from .branch import *
|
||||||
|
from .cr import *
|
||||||
|
from .compare import *
|
||||||
|
from .spr import *
|
@ -0,0 +1,30 @@
|
|||||||
|
from power_fv.insn import const
|
||||||
|
from power_fv.insn.spec.branch import BranchSpec
|
||||||
|
from power_fv.check.insn import InsnCheck
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"B" , "BA" , "BL" , "BLA" ,
|
||||||
|
"BC", "BCA", "BCL", "BCLA",
|
||||||
|
"BCLR" , "BCLRL" ,
|
||||||
|
"BCCTR", "BCCTRL",
|
||||||
|
"BCTAR", "BCTARL",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class B (InsnCheck, spec_cls=BranchSpec, insn_cls=const.B ): pass
|
||||||
|
class BA (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BA ): pass
|
||||||
|
class BL (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BL ): pass
|
||||||
|
class BLA (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BLA ): pass
|
||||||
|
|
||||||
|
class BC (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BC ): pass
|
||||||
|
class BCA (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCA ): pass
|
||||||
|
class BCL (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCL ): pass
|
||||||
|
class BCLA (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCLA): pass
|
||||||
|
|
||||||
|
class BCLR (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCLR ): pass
|
||||||
|
class BCLRL (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCLRL ): pass
|
||||||
|
class BCCTR (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCCTR ): pass
|
||||||
|
class BCCTRL(InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCCTRL): pass
|
||||||
|
class BCTAR (InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCTAR ): pass
|
||||||
|
class BCTARL(InsnCheck, spec_cls=BranchSpec, insn_cls=const.BCTARL): pass
|
@ -0,0 +1,14 @@
|
|||||||
|
from power_fv.insn import const
|
||||||
|
from power_fv.insn.spec.compare import CompareSpec
|
||||||
|
from power_fv.check.insn import InsnCheck
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"CMPI", "CMPLI", "CMP", "CMPL",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CMPI (InsnCheck, spec_cls=CompareSpec, insn_cls=const.CMPI ): pass
|
||||||
|
class CMPLI (InsnCheck, spec_cls=CompareSpec, insn_cls=const.CMPLI): pass
|
||||||
|
class CMP (InsnCheck, spec_cls=CompareSpec, insn_cls=const.CMP ): pass
|
||||||
|
class CMPL (InsnCheck, spec_cls=CompareSpec, insn_cls=const.CMPL ): pass
|
@ -0,0 +1,20 @@
|
|||||||
|
from power_fv.insn import const
|
||||||
|
from power_fv.insn.spec.cr import CRSpec
|
||||||
|
from power_fv.check.insn import InsnCheck
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"CRAND", "CROR", "CRNAND", "CRXOR", "CRNOR", "CRANDC", "CREQV", "CRORC",
|
||||||
|
"MCRF" ,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CRAND (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRAND ): pass
|
||||||
|
class CROR (InsnCheck, spec_cls=CRSpec, insn_cls=const.CROR ): pass
|
||||||
|
class CRNAND (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRNAND): pass
|
||||||
|
class CRXOR (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRXOR ): pass
|
||||||
|
class CRNOR (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRNOR ): pass
|
||||||
|
class CRANDC (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRANDC): pass
|
||||||
|
class CREQV (InsnCheck, spec_cls=CRSpec, insn_cls=const.CREQV ): pass
|
||||||
|
class CRORC (InsnCheck, spec_cls=CRSpec, insn_cls=const.CRORC ): pass
|
||||||
|
class MCRF (InsnCheck, spec_cls=CRSpec, insn_cls=const.MCRF ): pass
|
@ -0,0 +1,27 @@
|
|||||||
|
from power_fv.insn import const
|
||||||
|
from power_fv.insn.spec.spr import SPRMoveSpec
|
||||||
|
from power_fv.check.insn import InsnCheck
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"MTXER" , "MFXER" ,
|
||||||
|
"MTLR" , "MFLR" ,
|
||||||
|
"MTCTR" , "MFCTR" ,
|
||||||
|
"MTSRR0", "MFSRR0",
|
||||||
|
"MTSRR1", "MFSRR1",
|
||||||
|
"MTTAR" , "MFTAR" ,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MTXER (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTXER ): pass
|
||||||
|
class MFXER (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFXER ): pass
|
||||||
|
class MTLR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTLR ): pass
|
||||||
|
class MFLR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFLR ): pass
|
||||||
|
class MTCTR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTCTR ): pass
|
||||||
|
class MFCTR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFCTR ): pass
|
||||||
|
class MTSRR0(InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTSRR0): pass
|
||||||
|
class MFSRR0(InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFSRR0): pass
|
||||||
|
class MTSRR1(InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTSRR1): pass
|
||||||
|
class MFSRR1(InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFSRR1): pass
|
||||||
|
class MTTAR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MTTAR ): pass
|
||||||
|
class MFTAR (InsnCheck, spec_cls=SPRMoveSpec, insn_cls=const.MFTAR ): pass
|
@ -0,0 +1,60 @@
|
|||||||
|
from amaranth import *
|
||||||
|
from amaranth.asserts import *
|
||||||
|
|
||||||
|
from power_fv.check import PowerFVCheck
|
||||||
|
from power_fv.check._timer import Timer
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["UniquenessCheck"]
|
||||||
|
|
||||||
|
|
||||||
|
class UniquenessCheck(PowerFVCheck, name=("unique",)):
|
||||||
|
"""Uniqueness check.
|
||||||
|
|
||||||
|
Checks that every retired instruction is assigned an unique `pfv.order` value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def testbench(self):
|
||||||
|
return UniquenessTestbench(self)
|
||||||
|
|
||||||
|
|
||||||
|
class UniquenessTestbench(Elaboratable):
|
||||||
|
def __init__(self, check):
|
||||||
|
if not isinstance(check, UniquenessCheck):
|
||||||
|
raise TypeError("Check must be an instance of UniquenessCheck, not {!r}"
|
||||||
|
.format(check))
|
||||||
|
self.check = check
|
||||||
|
self.name = "unique_tb"
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.submodules.t_pre = t_pre = Timer(self.check.skip - 1)
|
||||||
|
m.submodules.t_post = t_post = Timer(self.check.depth - 1)
|
||||||
|
m.submodules.dut = dut = self.check.dut
|
||||||
|
|
||||||
|
spec_order = AnyConst(dut.pfv.order.shape())
|
||||||
|
duplicate = Record([("prev", 1), ("curr", 1)])
|
||||||
|
|
||||||
|
with m.If(Rose(t_pre.zero)):
|
||||||
|
m.d.comb += [
|
||||||
|
Assume(dut.pfv.stb),
|
||||||
|
Assume(dut.pfv.order == spec_order),
|
||||||
|
Assert(~duplicate.prev),
|
||||||
|
Assert(~duplicate.curr),
|
||||||
|
]
|
||||||
|
|
||||||
|
with m.Elif(t_pre.zero):
|
||||||
|
with m.If(dut.pfv.stb & (dut.pfv.order == spec_order)):
|
||||||
|
m.d.sync += duplicate.prev.eq(1)
|
||||||
|
m.d.comb += duplicate.curr.eq(1)
|
||||||
|
|
||||||
|
with m.If(t_post.zero):
|
||||||
|
m.d.comb += [
|
||||||
|
Assert(~duplicate.prev),
|
||||||
|
Assert(~duplicate.curr),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.d.comb += Assert(~Past(t_post.zero))
|
||||||
|
|
||||||
|
return m
|
@ -1,12 +0,0 @@
|
|||||||
class PowerFVCheck:
|
|
||||||
registry = {}
|
|
||||||
name = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, name):
|
|
||||||
if name in cls.registry:
|
|
||||||
raise ValueError("Check name {!r} is already registered".format(name))
|
|
||||||
cls.registry[name] = cls
|
|
||||||
cls.name = name
|
|
||||||
|
|
||||||
def get_testbench(self, dut, *args, **kwargs):
|
|
||||||
raise NotImplementedError
|
|
@ -1,2 +0,0 @@
|
|||||||
from .cons.all import *
|
|
||||||
from .insn.all import *
|
|
@ -1,5 +0,0 @@
|
|||||||
from .unique import *
|
|
||||||
from .ia_fwd import *
|
|
||||||
from .gpr import *
|
|
||||||
from .cr import *
|
|
||||||
from .spr.all import *
|
|
@ -1,78 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["CRSpec", "CRCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class CRSpec(Elaboratable):
|
|
||||||
"""Condition Register consistency specification.
|
|
||||||
|
|
||||||
Checks that reads from CR fields return the last value that was written to them.
|
|
||||||
"""
|
|
||||||
def __init__(self, post):
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_order = AnyConst(self.pfv.order.width)
|
|
||||||
|
|
||||||
cr_map = Array(
|
|
||||||
Record([
|
|
||||||
("pfv", [
|
|
||||||
("r_stb", 1),
|
|
||||||
("r_data", 4),
|
|
||||||
("w_stb", 1),
|
|
||||||
("w_data", 4),
|
|
||||||
]),
|
|
||||||
("written", 1),
|
|
||||||
("shadow", 4),
|
|
||||||
], name=f"cr_{i}") for i in range(8)
|
|
||||||
)
|
|
||||||
|
|
||||||
cr_written_any = 0
|
|
||||||
|
|
||||||
for i, cr_field in enumerate(cr_map):
|
|
||||||
m.d.comb += [
|
|
||||||
cr_field.pfv.r_stb .eq(self.pfv.cr.r_stb[i]),
|
|
||||||
cr_field.pfv.r_data.eq(self.pfv.cr.r_data.word_select(i, 4)),
|
|
||||||
cr_field.pfv.w_stb .eq(self.pfv.cr.w_stb[i]),
|
|
||||||
cr_field.pfv.w_data.eq(self.pfv.cr.w_data.word_select(i, 4)),
|
|
||||||
]
|
|
||||||
cr_written_any |= cr_field.written
|
|
||||||
|
|
||||||
with m.If(self.pfv.stb & ~self.pfv.intr & (self.pfv.order <= spec_order)):
|
|
||||||
for cr_field in cr_map:
|
|
||||||
with m.If(cr_field.pfv.w_stb):
|
|
||||||
m.d.sync += [
|
|
||||||
cr_field.written.eq(1),
|
|
||||||
cr_field.shadow .eq(cr_field.pfv.w_data),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(Past(self.pfv.stb)),
|
|
||||||
Assume(Past(self.pfv.order) == spec_order),
|
|
||||||
Assume(cr_written_any),
|
|
||||||
]
|
|
||||||
with m.If(~Past(self.pfv.intr)):
|
|
||||||
for cr_field in cr_map:
|
|
||||||
with m.If(Past(cr_field.pfv.r_stb)):
|
|
||||||
m.d.sync += Assert(Past(cr_field.pfv.r_data) == Past(cr_field.shadow))
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class CRCheck(PowerFVCheck, name="cons_cr"):
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = CRSpec(post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,85 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["GPRSpec", "GPRCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class GPRSpec(Elaboratable):
|
|
||||||
"""GPR consistency specification.
|
|
||||||
|
|
||||||
Checks that:
|
|
||||||
* reads from a GPR return the last value that was written to it;
|
|
||||||
* writes to multiple GPRs by a single instruction (e.g. load with update) do not target the
|
|
||||||
same register.
|
|
||||||
"""
|
|
||||||
def __init__(self, post):
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_order = AnyConst(self.pfv.order.width)
|
|
||||||
spec_gpr_index = AnyConst(5)
|
|
||||||
|
|
||||||
gpr_ra_read = Signal()
|
|
||||||
gpr_ra_write = Signal()
|
|
||||||
gpr_rb_read = Signal()
|
|
||||||
gpr_rs_read = Signal()
|
|
||||||
gpr_rt_read = Signal()
|
|
||||||
gpr_rt_write = Signal()
|
|
||||||
|
|
||||||
gpr_conflict = Signal()
|
|
||||||
gpr_written = Signal()
|
|
||||||
gpr_shadow = Signal(64)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
gpr_ra_read .eq(self.pfv.ra.r_stb & (self.pfv.ra.index == spec_gpr_index)),
|
|
||||||
gpr_ra_write.eq(self.pfv.ra.w_stb & (self.pfv.ra.index == spec_gpr_index)),
|
|
||||||
gpr_rb_read .eq(self.pfv.rb.r_stb & (self.pfv.rb.index == spec_gpr_index)),
|
|
||||||
gpr_rs_read .eq(self.pfv.rs.r_stb & (self.pfv.rs.index == spec_gpr_index)),
|
|
||||||
gpr_rt_read .eq(self.pfv.rt.r_stb & (self.pfv.rt.index == spec_gpr_index)),
|
|
||||||
gpr_rt_write.eq(self.pfv.rt.w_stb & (self.pfv.rt.index == spec_gpr_index)),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.pfv.stb & ~self.pfv.intr & (self.pfv.order <= spec_order)):
|
|
||||||
with m.If(gpr_ra_write & gpr_rt_write):
|
|
||||||
m.d.sync += gpr_conflict.eq(1)
|
|
||||||
with m.If(gpr_ra_write | gpr_rt_write):
|
|
||||||
m.d.sync += gpr_written.eq(1)
|
|
||||||
with m.If(gpr_ra_write):
|
|
||||||
m.d.sync += gpr_shadow.eq(self.pfv.ra.w_data)
|
|
||||||
with m.Else():
|
|
||||||
m.d.sync += gpr_shadow.eq(self.pfv.rt.w_data)
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(Past(self.pfv.stb)),
|
|
||||||
Assume(Past(self.pfv.order) == spec_order),
|
|
||||||
]
|
|
||||||
with m.If(~Past(self.pfv.intr)):
|
|
||||||
m.d.sync += Assert(gpr_written.implies(~gpr_conflict))
|
|
||||||
with m.If(gpr_written & Past(gpr_ra_read)):
|
|
||||||
m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.ra.r_data))
|
|
||||||
with m.If(gpr_written & Past(gpr_rb_read)):
|
|
||||||
m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.rb.r_data))
|
|
||||||
with m.If(gpr_written & Past(gpr_rs_read)):
|
|
||||||
m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.rs.r_data))
|
|
||||||
with m.If(gpr_written & Past(gpr_rt_read)):
|
|
||||||
m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.rt.r_data))
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class GPRCheck(PowerFVCheck, name="cons_gpr"):
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = GPRSpec(post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,52 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["IAForwardSpec", "IAForwardCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class IAForwardSpec(Elaboratable):
|
|
||||||
"""IA forward specification.
|
|
||||||
|
|
||||||
Given two instructions retiring in order, the NIA of the first must match the CIA
|
|
||||||
of the second.
|
|
||||||
"""
|
|
||||||
def __init__(self, post):
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
pred_order = AnyConst(self.pfv.order.width)
|
|
||||||
pred_stb = Signal()
|
|
||||||
pred_nia = Signal.like(self.pfv.nia)
|
|
||||||
|
|
||||||
with m.If(self.pfv.stb & (self.pfv.order == pred_order)):
|
|
||||||
m.d.sync += [
|
|
||||||
pred_stb.eq(1),
|
|
||||||
pred_nia.eq(self.pfv.nia)
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.order == pred_order + 1),
|
|
||||||
Assume(self.pfv.order > 0),
|
|
||||||
Assert(pred_stb.implies(self.pfv.cia == pred_nia)),
|
|
||||||
]
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class IAForwardCheck(PowerFVCheck, name="cons_ia_fwd"):
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = IAForwardSpec(post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,77 +0,0 @@
|
|||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from ... import PowerFVCheck
|
|
||||||
from .... import pfv, tb
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["SPRSpec", "SPRCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class StorageSPRSpec(Elaboratable):
|
|
||||||
"""Storage SPR specification.
|
|
||||||
|
|
||||||
Checks that reads from supported SPRs return the last value that was written to them.
|
|
||||||
"""
|
|
||||||
def __init__(self, spr_name, spr_reset, post):
|
|
||||||
self.spr_name = spr_name
|
|
||||||
self.spr_reset = spr_reset
|
|
||||||
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pfv_spr(self):
|
|
||||||
return getattr(self.pfv, self.spr_name)
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spr = Record([
|
|
||||||
("written", 1),
|
|
||||||
("shadow", self.pfv_spr.r_data.width),
|
|
||||||
], name=self.spr_name)
|
|
||||||
|
|
||||||
spec_order = AnyConst(self.pfv.order.width)
|
|
||||||
|
|
||||||
with m.If(Initial()):
|
|
||||||
m.d.sync += Assume(spr.shadow == self.spr_reset)
|
|
||||||
|
|
||||||
with m.If(self.pfv.stb & (self.pfv.order <= spec_order)):
|
|
||||||
with m.If(self.pfv_spr.w_mask.any()):
|
|
||||||
m.d.sync += [
|
|
||||||
spr.written.eq(1),
|
|
||||||
spr.shadow .eq((spr.shadow & ~self.pfv_spr.w_mask) | (self.pfv_spr.w_data & self.pfv_spr.w_mask)),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(Past(self.pfv.stb)),
|
|
||||||
Assume(Past(self.pfv.order) == spec_order),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(spr.written):
|
|
||||||
p_spr_r_mask = Past(self.pfv_spr.r_mask)
|
|
||||||
p_spr_r_data = Past(self.pfv_spr.r_data)
|
|
||||||
p_spr_shadow = Past(spr.shadow)
|
|
||||||
m.d.sync += Assert((p_spr_r_data & p_spr_r_mask) == (p_spr_shadow & p_spr_r_mask))
|
|
||||||
|
|
||||||
m.d.sync += Assume(spr.written & Past(self.pfv_spr.r_mask).any()) # FIXME rm
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class StorageSPRCheck(PowerFVCheck, name="_cons_spr_storage"):
|
|
||||||
def __init_subclass__(cls, name, spr_name):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.spr_name = spr_name
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post, spr_reset=0):
|
|
||||||
tb_spec = StorageSPRSpec(self.spr_name, spr_reset, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,9 +0,0 @@
|
|||||||
from ._storage import StorageSPRCheck
|
|
||||||
|
|
||||||
|
|
||||||
class LR (StorageSPRCheck, name="cons_lr", spr_name="lr" ): pass
|
|
||||||
class CTR (StorageSPRCheck, name="cons_ctr", spr_name="ctr" ): pass
|
|
||||||
class XER (StorageSPRCheck, name="cons_xer", spr_name="xer" ): pass
|
|
||||||
class TAR (StorageSPRCheck, name="cons_tar", spr_name="tar" ): pass
|
|
||||||
class SRR0 (StorageSPRCheck, name="cons_srr0", spr_name="srr0"): pass
|
|
||||||
class SRR1 (StorageSPRCheck, name="cons_srr1", spr_name="srr1"): pass
|
|
@ -1,49 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["UniquenessSpec", "UniquenessCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class UniquenessSpec(Elaboratable):
|
|
||||||
"""Uniqueness specification.
|
|
||||||
|
|
||||||
A core cannot retire two instructions that share the same `pfv.order` identifier.
|
|
||||||
"""
|
|
||||||
def __init__(self, pre, post):
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.pre = tb.Trigger(cycle=pre)
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield from (self.pre, self.post)
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_order = AnyConst(self.pfv.order.shape())
|
|
||||||
duplicate = Signal()
|
|
||||||
|
|
||||||
with m.If(self.pre.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(spec_order == self.pfv.order),
|
|
||||||
Assume(~duplicate),
|
|
||||||
]
|
|
||||||
with m.Elif(self.pfv.stb & (self.pfv.order == spec_order)):
|
|
||||||
m.d.sync += duplicate.eq(1)
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += Assert(~duplicate)
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class UniquenessCheck(PowerFVCheck, name="cons_unique"):
|
|
||||||
def get_testbench(self, dut, pre, post):
|
|
||||||
tb_spec = UniquenessSpec(pre, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,370 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
from ._fmt import *
|
|
||||||
from ._insn import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AddSubtractSpec", "AddSubtractCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class AddSubtractSpec(Elaboratable):
|
|
||||||
def __init__(self, insn_cls, post):
|
|
||||||
self.insn_cls = insn_cls
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_insn = self.insn_cls()
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.insn[32:] == spec_insn),
|
|
||||||
]
|
|
||||||
|
|
||||||
spec_ra_r_stb = Signal()
|
|
||||||
spec_rb_r_stb = Signal()
|
|
||||||
spec_rt_w_stb = Signal()
|
|
||||||
spec_rt_w_data = Signal(64)
|
|
||||||
spec_cr_w_stb = Signal( 8)
|
|
||||||
spec_cr_w_data = Signal(32)
|
|
||||||
spec_msr_r_mask = Signal(64)
|
|
||||||
spec_xer_r_mask = Signal(64)
|
|
||||||
spec_xer_w_mask = Signal(64)
|
|
||||||
spec_xer_w_data = Signal(64)
|
|
||||||
|
|
||||||
src_a = Signal(signed(64))
|
|
||||||
src_b = Signal(signed(64))
|
|
||||||
src_c = Signal()
|
|
||||||
result = Signal(unsigned(65))
|
|
||||||
|
|
||||||
ca_64 = Signal()
|
|
||||||
ca_32 = Signal()
|
|
||||||
ov_64 = Signal()
|
|
||||||
ov_32 = Signal()
|
|
||||||
|
|
||||||
# Operand A : (RA) or 0 or NIA or ~(RA)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (ADDI, ADDIS)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_ra_r_stb.eq(spec_insn.ra != 0),
|
|
||||||
src_a.eq(Mux(spec_insn.ra != 0, self.pfv.ra.r_data, 0)),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, ADDPCIS):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_ra_r_stb.eq(0),
|
|
||||||
src_a.eq(self.pfv.nia),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
ADD , ADD_ , ADDO , ADDO_ ,
|
|
||||||
ADDIC, ADDIC_,
|
|
||||||
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
|
||||||
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
|
||||||
ADDME, ADDME_, ADDMEO, ADDMEO_,
|
|
||||||
ADDZE, ADDZE_, ADDZEO, ADDZEO_,
|
|
||||||
ADDEX,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_ra_r_stb.eq(1),
|
|
||||||
src_a.eq(self.pfv.ra.r_data),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
|
||||||
SUBFIC,
|
|
||||||
SUBFC , SUBFC_ , SUBFCO , SUBFCO_ ,
|
|
||||||
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
|
||||||
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
|
||||||
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
|
||||||
NEG , NEG_ , NEGO , NEGO_ ,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_ra_r_stb.eq(1),
|
|
||||||
src_a.eq(~self.pfv.ra.r_data),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
# Operand B : SI or SI<<16 or D<<16 or (RB) or -1 or 0
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (ADDI, ADDIC, ADDIC_, SUBFIC)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(0),
|
|
||||||
src_b.eq(spec_insn.si),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, ADDIS):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(0),
|
|
||||||
src_b.eq(Cat(Const(0, 16), spec_insn.si).as_signed()),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, ADDPCIS):
|
|
||||||
imm_d = Signal(signed(16))
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(0),
|
|
||||||
imm_d.eq(Cat(spec_insn.d2, spec_insn.d1, spec_insn.d0)),
|
|
||||||
src_b.eq(Cat(Const(0, 16), imm_d).as_signed()),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
ADD , ADD_ , ADDO , ADDO_ ,
|
|
||||||
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
|
||||||
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
|
||||||
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
|
||||||
SUBFC, SUBFC_, SUBFCO, SUBFCO_,
|
|
||||||
SUBFE, SUBFE_, SUBFEO, SUBFEO_,
|
|
||||||
ADDEX,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(1),
|
|
||||||
src_b.eq(self.pfv.rb.r_data),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
|
||||||
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(0),
|
|
||||||
src_b.eq(-1),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
|
||||||
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
|
||||||
NEG , NEG_ , NEGO , NEGO_ ,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rb_r_stb.eq(0),
|
|
||||||
src_b.eq(0),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
# Operand C : 0 or 1 or XER.CA or XER.OV
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (
|
|
||||||
ADDI , ADDIS , ADDPCIS,
|
|
||||||
ADD , ADD_ , ADDO , ADDO_ ,
|
|
||||||
ADDIC, ADDIC_,
|
|
||||||
ADDC , ADDC_ , ADDCO , ADDCO_,
|
|
||||||
)):
|
|
||||||
m.d.comb += src_c.eq(0)
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
|
||||||
SUBFIC,
|
|
||||||
SUBFC , SUBFC_, SUBFCO, SUBFCO_,
|
|
||||||
NEG , NEG_ , NEGO , NEGO_ ,
|
|
||||||
)):
|
|
||||||
m.d.comb += src_c.eq(1)
|
|
||||||
elif isinstance(spec_insn, (
|
|
||||||
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
|
||||||
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
|
||||||
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
|
||||||
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
|
||||||
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
|
||||||
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
|
||||||
)):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_xer_r_mask[63 - 34].eq(1), # XER.CA
|
|
||||||
src_c.eq(self.pfv.xer.r_data[63 - 34]),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, ADDEX):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_xer_r_mask[63 - 33].eq(1), # XER.OV
|
|
||||||
src_c.eq(self.pfv.xer.r_data[63 - 33]),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
# Result : Operand A + Operand B + Operand C
|
|
||||||
|
|
||||||
tmp_a = Signal(unsigned(65))
|
|
||||||
tmp_b = Signal(unsigned(65))
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
tmp_a .eq(src_a.as_unsigned()),
|
|
||||||
tmp_b .eq(src_b.as_unsigned()),
|
|
||||||
result.eq(tmp_a + tmp_b + src_c),
|
|
||||||
|
|
||||||
ca_64.eq(result[64]),
|
|
||||||
ca_32.eq(result[32] ^ src_a[32] ^ src_b[32]),
|
|
||||||
ov_64.eq((ca_64 ^ result[63]) & ~(src_a[63] ^ src_b[63])),
|
|
||||||
ov_32.eq((ca_32 ^ result[31]) & ~(src_a[31] ^ src_b[31])),
|
|
||||||
]
|
|
||||||
|
|
||||||
# GPRs
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rt_w_stb .eq(1),
|
|
||||||
spec_rt_w_data.eq(result[:64]),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(spec_ra_r_stb.implies(self.pfv.ra.r_stb)),
|
|
||||||
Assert(spec_rb_r_stb.implies(self.pfv.rb.r_stb)),
|
|
||||||
Assert(self.pfv.rt.w_stb == spec_rt_w_stb),
|
|
||||||
Assert(spec_rt_w_stb.implies(self.pfv.rt.w_data == spec_rt_w_data)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# MSR
|
|
||||||
|
|
||||||
msr_r_sf = Signal()
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_msr_r_mask[63 - 0].eq(1),
|
|
||||||
msr_r_sf.eq(self.pfv.msr.r_data[63 - 0]),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += Assert((self.pfv.msr.r_mask & spec_msr_r_mask) == spec_msr_r_mask)
|
|
||||||
|
|
||||||
# XER
|
|
||||||
|
|
||||||
xer_r_so = Signal()
|
|
||||||
xer_w_so = Signal()
|
|
||||||
xer_w_ov = Signal()
|
|
||||||
xer_w_ca = Signal()
|
|
||||||
xer_w_ov32 = Signal()
|
|
||||||
xer_w_ca32 = Signal()
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (
|
|
||||||
ADD_ , ADDO_ , ADDIC_ ,
|
|
||||||
SUBF_ , SUBFO_ ,
|
|
||||||
ADDC_ , ADDCO_ , ADDE_ , ADDEO_ ,
|
|
||||||
SUBFC_ , SUBFCO_ , SUBFE_ , SUBFEO_ ,
|
|
||||||
ADDME_ , ADDMEO_ , ADDZE_ , ADDZEO_ ,
|
|
||||||
SUBFME_, SUBFMEO_, SUBFZE_, SUBFZEO_,
|
|
||||||
NEG_ , NEGO_ ,
|
|
||||||
)):
|
|
||||||
# Read XER.SO (to update CR0)
|
|
||||||
m.d.comb += [
|
|
||||||
xer_r_so.eq(self.pfv.xer.r_data[63 - 32]),
|
|
||||||
spec_xer_r_mask[63 - 32].eq(1),
|
|
||||||
]
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (
|
|
||||||
ADDO , ADDO_ , SUBFO , SUBFO_ ,
|
|
||||||
ADDCO , ADDCO_ , SUBFCO , SUBFCO_ ,
|
|
||||||
ADDEO , ADDEO_ , SUBFEO , SUBFEO_ ,
|
|
||||||
ADDMEO, ADDMEO_, SUBFMEO, SUBFMEO_,
|
|
||||||
ADDZEO, ADDZEO_, SUBFZEO, SUBFZEO_,
|
|
||||||
NEGO , NEGO_ ,
|
|
||||||
)):
|
|
||||||
# Set XER.SO, write XER.OV and XER.OV32
|
|
||||||
m.d.comb += [
|
|
||||||
xer_w_so .eq(xer_w_ov),
|
|
||||||
xer_w_ov .eq(Mux(msr_r_sf, ov_64, ov_32)),
|
|
||||||
xer_w_ov32.eq(ov_32),
|
|
||||||
spec_xer_w_mask[63 - 32].eq(xer_w_so),
|
|
||||||
spec_xer_w_data[63 - 32].eq(xer_w_so),
|
|
||||||
spec_xer_w_mask[63 - 33].eq(1),
|
|
||||||
spec_xer_w_data[63 - 33].eq(xer_w_ov),
|
|
||||||
spec_xer_w_mask[63 - 44].eq(1),
|
|
||||||
spec_xer_w_data[63 - 44].eq(xer_w_ov32),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, ADDEX):
|
|
||||||
# Write XER.OV and XER.OV32
|
|
||||||
m.d.comb += [
|
|
||||||
xer_w_ov .eq(Mux(msr_r_sf, ca_64, ca_32)),
|
|
||||||
xer_w_ov32.eq(ca_32),
|
|
||||||
spec_xer_w_mask[63 - 33].eq(1),
|
|
||||||
spec_xer_w_data[63 - 33].eq(xer_w_ov),
|
|
||||||
spec_xer_w_mask[63 - 44].eq(1),
|
|
||||||
spec_xer_w_data[63 - 44].eq(xer_w_ov32),
|
|
||||||
]
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (
|
|
||||||
ADDIC , ADDIC_ , SUBFIC ,
|
|
||||||
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
|
||||||
SUBFC , SUBFC_ , SUBFCO , SUBFCO_ ,
|
|
||||||
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
|
||||||
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
|
||||||
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
|
||||||
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
|
||||||
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
|
||||||
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
|
||||||
)):
|
|
||||||
# Write XER.CA and XER.CA32
|
|
||||||
m.d.comb += [
|
|
||||||
xer_w_ca .eq(Mux(msr_r_sf, ca_64, ca_32)),
|
|
||||||
xer_w_ca32.eq(ca_32),
|
|
||||||
spec_xer_w_mask[63 - 34].eq(1),
|
|
||||||
spec_xer_w_data[63 - 34].eq(xer_w_ca),
|
|
||||||
spec_xer_w_mask[63 - 45].eq(1),
|
|
||||||
spec_xer_w_data[63 - 45].eq(xer_w_ca32),
|
|
||||||
]
|
|
||||||
|
|
||||||
keep_xer_w_mask = Signal(64)
|
|
||||||
keep_xer_w_data = Signal(64)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
keep_xer_w_mask.eq(self.pfv.xer.w_mask & ~spec_xer_w_mask),
|
|
||||||
keep_xer_w_data.eq(self.pfv.xer.r_data & keep_xer_w_mask),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert((self.pfv.xer.r_mask & spec_xer_r_mask) == spec_xer_r_mask),
|
|
||||||
Assert((self.pfv.xer.w_mask & spec_xer_w_mask) == spec_xer_w_mask),
|
|
||||||
Assert((self.pfv.xer.w_data & spec_xer_w_mask) == spec_xer_w_data),
|
|
||||||
Assert((self.pfv.xer.r_mask & keep_xer_w_mask) == keep_xer_w_mask),
|
|
||||||
Assert((self.pfv.xer.w_data & keep_xer_w_mask) == keep_xer_w_data),
|
|
||||||
]
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
cr0_w_lt = Signal()
|
|
||||||
cr0_w_gt = Signal()
|
|
||||||
cr0_w_eq = Signal()
|
|
||||||
cr0_w_so = Signal()
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (
|
|
||||||
ADD_ , ADDO_ , ADDIC_ ,
|
|
||||||
SUBF_ , SUBFO_ ,
|
|
||||||
ADDC_ , ADDCO_ , ADDE_ , ADDEO_ ,
|
|
||||||
SUBFC_ , SUBFCO_ , SUBFE_ , SUBFEO_ ,
|
|
||||||
ADDME_ , ADDMEO_ , ADDZE_ , ADDZEO_ ,
|
|
||||||
SUBFME_, SUBFMEO_, SUBFZE_, SUBFZEO_,
|
|
||||||
NEG_ , NEGO_ ,
|
|
||||||
)):
|
|
||||||
# Write CR0
|
|
||||||
m.d.comb += [
|
|
||||||
cr0_w_lt.eq(Mux(msr_r_sf, result[63], result[31])),
|
|
||||||
cr0_w_gt.eq(~(cr0_w_lt | cr0_w_eq)),
|
|
||||||
cr0_w_eq.eq(~Mux(msr_r_sf, result[:64].any(), result[:32].any())),
|
|
||||||
cr0_w_so.eq(Mux(xer_w_so, xer_w_so, xer_r_so)),
|
|
||||||
|
|
||||||
spec_cr_w_stb [ 7 - 0].eq(1),
|
|
||||||
spec_cr_w_data[31 - 0].eq(cr0_w_lt),
|
|
||||||
spec_cr_w_data[31 - 1].eq(cr0_w_gt),
|
|
||||||
spec_cr_w_data[31 - 2].eq(cr0_w_eq),
|
|
||||||
spec_cr_w_data[31 - 3].eq(cr0_w_so),
|
|
||||||
]
|
|
||||||
|
|
||||||
spec_cr_w_mask = Signal(32)
|
|
||||||
m.d.comb += spec_cr_w_mask.eq(Cat(Repl(s, 4) for s in spec_cr_w_stb))
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(self.pfv.cr.w_stb == spec_cr_w_stb),
|
|
||||||
Assert((self.pfv.cr.w_data & spec_cr_w_mask) == spec_cr_w_data),
|
|
||||||
]
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class AddSubtractCheck(PowerFVCheck, name="_insn_addsub"):
|
|
||||||
def __init_subclass__(cls, name, insn_cls):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.insn_cls = insn_cls
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = AddSubtractSpec(self.insn_cls, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,251 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
from ...utils import iea_mask
|
|
||||||
|
|
||||||
from ._fmt import *
|
|
||||||
from ._insn import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["BranchSpec", "BranchCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class BranchSpec(Elaboratable):
|
|
||||||
def __init__(self, insn_cls, post):
|
|
||||||
self.insn_cls = insn_cls
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_insn = self.insn_cls()
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.insn[32:] == spec_insn),
|
|
||||||
Assert(self.pfv.msr.w_mask[63 - 0]), # MSR.SF
|
|
||||||
]
|
|
||||||
|
|
||||||
msr_w_sf = Signal()
|
|
||||||
m.d.comb += msr_w_sf.eq(self.pfv.msr.w_data[63 - 0])
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
|
|
||||||
bo_valid_patterns = [
|
|
||||||
"001--",
|
|
||||||
"011--",
|
|
||||||
"1-1--",
|
|
||||||
]
|
|
||||||
if not isinstance(spec_insn, (BCCTR, BCCTRL)):
|
|
||||||
# bcctr/bcctrl forms with BO(2)=0 ("decrement and test CTR") are illegal.
|
|
||||||
bo_valid_patterns += [
|
|
||||||
"0000-",
|
|
||||||
"0001-",
|
|
||||||
"0100-",
|
|
||||||
"0101-",
|
|
||||||
"1-00-",
|
|
||||||
"1-01-",
|
|
||||||
]
|
|
||||||
|
|
||||||
bo_invalid = Signal()
|
|
||||||
m.d.comb += bo_invalid.eq(~spec_insn.bo.matches(*bo_valid_patterns))
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += Assert(bo_invalid.implies(self.pfv.intr))
|
|
||||||
|
|
||||||
# NIA
|
|
||||||
|
|
||||||
spec_nia = Signal(unsigned(64))
|
|
||||||
taken = Signal()
|
|
||||||
offset = Signal(signed(62))
|
|
||||||
target = Signal(signed(64))
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_I):
|
|
||||||
m.d.comb += [
|
|
||||||
taken .eq(1),
|
|
||||||
offset.eq(spec_insn.li)
|
|
||||||
]
|
|
||||||
|
|
||||||
elif isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
|
|
||||||
cond_bit = Signal()
|
|
||||||
ctr_any = Signal()
|
|
||||||
cond_ok = Signal()
|
|
||||||
ctr_ok = Signal()
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
cond_bit.eq(self.pfv.cr.r_data[::-1].bit_select(spec_insn.bi, width=1)),
|
|
||||||
ctr_any .eq( self.pfv.ctr.w_data[:32].any() |
|
|
||||||
(self.pfv.ctr.w_data[32:].any() & msr_w_sf)),
|
|
||||||
cond_ok .eq(spec_insn.bo[4-0] | (spec_insn.bo[4-1] == cond_bit)),
|
|
||||||
ctr_ok .eq(spec_insn.bo[4-2] | (ctr_any ^ spec_insn.bo[4-3])),
|
|
||||||
]
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (BCCTR, BCCTRL)):
|
|
||||||
m.d.comb += taken.eq(cond_ok)
|
|
||||||
else:
|
|
||||||
m.d.comb += taken.eq(cond_ok & ctr_ok)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_B):
|
|
||||||
m.d.comb += offset.eq(spec_insn.bd)
|
|
||||||
elif isinstance(spec_insn, (BCLR, BCLRL)):
|
|
||||||
m.d.comb += offset.eq(self.pfv.lr .r_data[2:])
|
|
||||||
elif isinstance(spec_insn, (BCCTR, BCCTRL)):
|
|
||||||
m.d.comb += offset.eq(self.pfv.ctr.r_data[2:])
|
|
||||||
elif isinstance(spec_insn, (BCTAR, BCTARL)):
|
|
||||||
m.d.comb += offset.eq(self.pfv.tar.r_data[2:])
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(taken):
|
|
||||||
if isinstance(spec_insn, (Instruction_I, Instruction_B)) and spec_insn.aa.value == 0:
|
|
||||||
m.d.comb += target.eq(self.pfv.cia + (offset << 2))
|
|
||||||
else:
|
|
||||||
m.d.comb += target.eq(offset << 2)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += target.eq(self.pfv.cia + 4)
|
|
||||||
|
|
||||||
m.d.comb += spec_nia.eq(iea_mask(target, msr_w_sf))
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += Assert(self.pfv.nia == spec_nia)
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
spec_cr_r_stb = Signal(8)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_I):
|
|
||||||
m.d.comb += spec_cr_r_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
|
|
||||||
m.d.comb += spec_cr_r_stb[::-1].bit_select(spec_insn.bi[2:], width=1).eq(1)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
for i, spec_cr_r_stb_bit in enumerate(spec_cr_r_stb):
|
|
||||||
pfv_cr_r_stb_bit = self.pfv.cr.r_stb[i]
|
|
||||||
m.d.sync += Assert(spec_cr_r_stb_bit.implies(pfv_cr_r_stb_bit))
|
|
||||||
|
|
||||||
# LR
|
|
||||||
|
|
||||||
spec_lr_r_stb = Signal()
|
|
||||||
spec_lr_w_stb = Signal()
|
|
||||||
|
|
||||||
spec_lr_r_mask = Signal(64)
|
|
||||||
spec_lr_w_mask = Signal(64)
|
|
||||||
spec_lr_w_data = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (Instruction_I, Instruction_B)):
|
|
||||||
m.d.comb += spec_lr_r_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, (Instruction_XL_bc)):
|
|
||||||
if isinstance(spec_insn, (BCLR, BCLRL)):
|
|
||||||
m.d.comb += spec_lr_r_stb.eq(1)
|
|
||||||
else:
|
|
||||||
m.d.comb += spec_lr_r_stb.eq(0)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
m.d.comb += spec_lr_w_stb.eq(spec_insn.lk)
|
|
||||||
|
|
||||||
cia_4 = Signal(unsigned(64))
|
|
||||||
m.d.comb += cia_4.eq(self.pfv.cia + 4)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_lr_r_mask.eq(Repl(spec_lr_r_stb, 64)),
|
|
||||||
spec_lr_w_mask.eq(Repl(spec_lr_w_stb, 64)),
|
|
||||||
spec_lr_w_data.eq(iea_mask(cia_4, msr_w_sf)),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert((self.pfv.lr.r_mask & spec_lr_r_mask) == spec_lr_r_mask),
|
|
||||||
Assert(self.pfv.lr.w_mask == spec_lr_w_mask),
|
|
||||||
Assert((self.pfv.lr.w_data & spec_lr_w_mask) == (spec_lr_w_data & spec_lr_w_mask)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# CTR
|
|
||||||
|
|
||||||
spec_ctr_r_stb = Signal()
|
|
||||||
spec_ctr_w_stb = Signal()
|
|
||||||
|
|
||||||
spec_ctr_r_mask = Signal(64)
|
|
||||||
spec_ctr_w_mask = Signal(64)
|
|
||||||
spec_ctr_w_data = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_I):
|
|
||||||
m.d.comb += spec_ctr_r_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, Instruction_B):
|
|
||||||
m.d.comb += spec_ctr_r_stb.eq(~spec_insn.bo[4-2])
|
|
||||||
elif isinstance(spec_insn, Instruction_XL_bc):
|
|
||||||
if isinstance(spec_insn, (BCCTR, BCCTRL)):
|
|
||||||
m.d.comb += spec_ctr_r_stb.eq(1)
|
|
||||||
else:
|
|
||||||
m.d.comb += spec_ctr_r_stb.eq(~spec_insn.bo[4-2])
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_I):
|
|
||||||
m.d.comb += spec_ctr_w_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, Instruction_B):
|
|
||||||
m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[4-2])
|
|
||||||
elif isinstance(spec_insn, Instruction_XL_bc):
|
|
||||||
if isinstance(spec_insn, (BCCTR, BCCTRL)):
|
|
||||||
m.d.comb += spec_ctr_w_stb.eq(0)
|
|
||||||
else:
|
|
||||||
m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[4-2])
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_ctr_r_mask.eq(Repl(spec_ctr_r_stb, 64)),
|
|
||||||
spec_ctr_w_mask.eq(Repl(spec_ctr_w_stb, 64)),
|
|
||||||
spec_ctr_w_data.eq(self.pfv.ctr.r_data - 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert((self.pfv.ctr.r_mask & spec_ctr_r_mask) == spec_ctr_r_mask),
|
|
||||||
Assert(self.pfv.ctr.w_mask == spec_ctr_w_mask),
|
|
||||||
Assert((self.pfv.ctr.w_data & spec_ctr_w_mask) == (spec_ctr_w_data & spec_ctr_w_mask)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# TAR
|
|
||||||
|
|
||||||
spec_tar_r_stb = Signal()
|
|
||||||
spec_tar_r_mask = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (Instruction_I, Instruction_B)):
|
|
||||||
m.d.comb += spec_tar_r_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, (Instruction_XL_bc)):
|
|
||||||
if isinstance(spec_insn, (BCTAR, BCTARL)):
|
|
||||||
m.d.comb += spec_tar_r_stb.eq(1)
|
|
||||||
else:
|
|
||||||
m.d.comb += spec_tar_r_stb.eq(0)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
m.d.comb += spec_tar_r_mask.eq(Repl(spec_tar_r_stb, 64))
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += Assert((self.pfv.tar.r_mask & spec_tar_r_mask) == spec_tar_r_mask)
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class BranchCheck(PowerFVCheck, name="_insn_branch"):
|
|
||||||
def __init_subclass__(cls, name, insn_cls):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.insn_cls = insn_cls
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = BranchSpec(self.insn_cls, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,152 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
from ._fmt import *
|
|
||||||
from ._insn import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["CompareSpec", "CompareCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class CompareSpec(Elaboratable):
|
|
||||||
def __init__(self, insn_cls, post):
|
|
||||||
self.insn_cls = insn_cls
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_insn = self.insn_cls()
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.insn[32:] == spec_insn),
|
|
||||||
]
|
|
||||||
|
|
||||||
# GPRs
|
|
||||||
|
|
||||||
spec_ra_r_stb = Signal()
|
|
||||||
spec_rb_r_stb = Signal()
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (CMPI, CMPLI, CMP, CMPL)):
|
|
||||||
m.d.comb += spec_ra_r_stb.eq(1)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (CMPI, CMPLI)):
|
|
||||||
m.d.comb += spec_rb_r_stb.eq(0)
|
|
||||||
elif isinstance(spec_insn, (CMP, CMPL)):
|
|
||||||
m.d.comb += spec_rb_r_stb.eq(1)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(self.pfv.ra.r_stb == spec_ra_r_stb),
|
|
||||||
Assert(self.pfv.rb.r_stb == spec_rb_r_stb),
|
|
||||||
]
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
spec_cr_w_stb = Signal(8)
|
|
||||||
spec_cr_w_data = Signal(32)
|
|
||||||
|
|
||||||
a = Signal(signed(64))
|
|
||||||
b = Signal(signed(64))
|
|
||||||
c = Record([("lt", 1), ("gt", 1), ("eq_", 1), ("so", 1)])
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (CMPI, CMP)):
|
|
||||||
with m.If(spec_insn.l):
|
|
||||||
m.d.comb += a.eq(self.pfv.ra.r_data)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += a.eq(self.pfv.ra.r_data[:32].as_signed())
|
|
||||||
elif isinstance(spec_insn, (CMPL, CMPLI)):
|
|
||||||
with m.If(spec_insn.l):
|
|
||||||
m.d.comb += a.eq(self.pfv.ra.r_data)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += a.eq(self.pfv.ra.r_data[:32].as_unsigned())
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if isinstance(spec_insn, CMPI):
|
|
||||||
m.d.comb += b.eq(spec_insn.si)
|
|
||||||
elif isinstance(spec_insn, CMPLI):
|
|
||||||
m.d.comb += b.eq(spec_insn.ui)
|
|
||||||
elif isinstance(spec_insn, CMP):
|
|
||||||
with m.If(spec_insn.l):
|
|
||||||
m.d.comb += b.eq(self.pfv.rb.r_data)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += b.eq(self.pfv.rb.r_data[:32].as_signed())
|
|
||||||
elif isinstance(spec_insn, CMPL):
|
|
||||||
with m.If(spec_insn.l):
|
|
||||||
m.d.comb += b.eq(self.pfv.rb.r_data)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += b.eq(self.pfv.rb.r_data[:32].as_unsigned())
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (CMPI, CMP)):
|
|
||||||
m.d.comb += [
|
|
||||||
c.lt.eq(a.as_signed() < b.as_signed()),
|
|
||||||
c.gt.eq(a.as_signed() > b.as_signed()),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, (CMPLI, CMPL)):
|
|
||||||
m.d.comb += [
|
|
||||||
c.lt.eq(a.as_unsigned() < b.as_unsigned()),
|
|
||||||
c.gt.eq(a.as_unsigned() > b.as_unsigned()),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
c.eq_.eq(a == b),
|
|
||||||
c.so .eq(self.pfv.xer.r_data[63 - 32]), # XER.SO
|
|
||||||
]
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_cr_w_stb[::-1].bit_select(spec_insn.bf, width=1).eq(1),
|
|
||||||
spec_cr_w_data[::-1].eq(Repl(c, 8)),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
for i in range(8):
|
|
||||||
spec_cr_w_field = spec_cr_w_data .word_select(i, width=4)
|
|
||||||
pfv_cr_w_field = self.pfv.cr.w_data.word_select(i, width=4)
|
|
||||||
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(self.pfv.cr.w_stb[i] == spec_cr_w_stb[i]),
|
|
||||||
Assert(spec_cr_w_stb[i].implies(pfv_cr_w_field == spec_cr_w_field)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# XER
|
|
||||||
|
|
||||||
spec_xer_r_mask = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (CMPI, CMPLI, CMP, CMPL)):
|
|
||||||
m.d.comb += spec_xer_r_mask[63 - 32].eq(1) # XER.SO
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += Assert((self.pfv.xer.r_mask & spec_xer_r_mask) == spec_xer_r_mask)
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class CompareCheck(PowerFVCheck, name="_insn_compare"):
|
|
||||||
def __init_subclass__(cls, name, insn_cls):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.insn_cls = insn_cls
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = CompareSpec(self.insn_cls, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,128 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
from ._fmt import *
|
|
||||||
from ._insn import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["CRSpec", "CRCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class CRSpec(Elaboratable):
|
|
||||||
def __init__(self, insn_cls, post):
|
|
||||||
self.insn_cls = insn_cls
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_insn = self.insn_cls()
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.insn[32:] == spec_insn),
|
|
||||||
]
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
spec_cr_r_stb = Signal(8)
|
|
||||||
spec_cr_w_stb = Signal(8)
|
|
||||||
spec_cr_w_data = Signal(32)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, Instruction_XL_crl):
|
|
||||||
ba_r_field = Signal(4)
|
|
||||||
bb_r_field = Signal(4)
|
|
||||||
bt_r_field = Signal(4)
|
|
||||||
ba_r_bit = Signal()
|
|
||||||
bb_r_bit = Signal()
|
|
||||||
bt_w_bit = Signal()
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
ba_r_field.eq(self.pfv.cr.r_data[::-1].word_select(spec_insn.ba[2:], width=4)),
|
|
||||||
bb_r_field.eq(self.pfv.cr.r_data[::-1].word_select(spec_insn.bb[2:], width=4)),
|
|
||||||
bt_r_field.eq(self.pfv.cr.r_data[::-1].word_select(spec_insn.bt[2:], width=4)),
|
|
||||||
|
|
||||||
ba_r_bit.eq(ba_r_field.bit_select(spec_insn.ba[:2], width=1)),
|
|
||||||
bb_r_bit.eq(bb_r_field.bit_select(spec_insn.bb[:2], width=1)),
|
|
||||||
]
|
|
||||||
|
|
||||||
if isinstance(spec_insn, CRAND):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit & bb_r_bit)
|
|
||||||
elif isinstance(spec_insn, CROR):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit | bb_r_bit)
|
|
||||||
elif isinstance(spec_insn, CRNAND):
|
|
||||||
m.d.comb += bt_w_bit.eq(~(ba_r_bit & bb_r_bit))
|
|
||||||
elif isinstance(spec_insn, CRXOR):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit ^ bb_r_bit)
|
|
||||||
elif isinstance(spec_insn, CRNOR):
|
|
||||||
m.d.comb += bt_w_bit.eq(~(ba_r_bit | bb_r_bit))
|
|
||||||
elif isinstance(spec_insn, CRANDC):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit & ~bb_r_bit)
|
|
||||||
elif isinstance(spec_insn, CREQV):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit == bb_r_bit)
|
|
||||||
elif isinstance(spec_insn, CRORC):
|
|
||||||
m.d.comb += bt_w_bit.eq(ba_r_bit | ~bb_r_bit)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
spec_bt_w_field = Signal(4)
|
|
||||||
|
|
||||||
for i, spec_bt_w_bit in enumerate(spec_bt_w_field):
|
|
||||||
bt_r_bit = bt_r_field[i]
|
|
||||||
with m.If(spec_insn.bt[:2] == i):
|
|
||||||
m.d.comb += spec_bt_w_bit.eq(bt_w_bit)
|
|
||||||
with m.Else():
|
|
||||||
m.d.comb += spec_bt_w_bit.eq(bt_r_bit)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
spec_cr_r_stb[::-1].bit_select(spec_insn.ba[2:], width=1).eq(1),
|
|
||||||
spec_cr_r_stb[::-1].bit_select(spec_insn.bb[2:], width=1).eq(1),
|
|
||||||
spec_cr_w_stb[::-1].bit_select(spec_insn.bt[2:], width=1).eq(1),
|
|
||||||
spec_cr_w_data[::-1].eq(Repl(spec_bt_w_field, 8))
|
|
||||||
]
|
|
||||||
|
|
||||||
elif isinstance(spec_insn, MCRF):
|
|
||||||
bfa_r_field = Signal(4)
|
|
||||||
|
|
||||||
m.d.comb += [
|
|
||||||
bfa_r_field.eq(self.pfv.cr.r_data[::-1].word_select(spec_insn.bfa, width=4)),
|
|
||||||
|
|
||||||
spec_cr_r_stb[::-1].bit_select(spec_insn.bfa, width=1).eq(1),
|
|
||||||
spec_cr_w_stb[::-1].bit_select(spec_insn.bf, width=1).eq(1),
|
|
||||||
spec_cr_w_data[::-1].eq(Repl(bfa_r_field, 8)),
|
|
||||||
]
|
|
||||||
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
for i in range(8):
|
|
||||||
spec_cr_w_field = spec_cr_w_data .word_select(i, width=4)
|
|
||||||
pfv_cr_w_field = self.pfv.cr.w_data.word_select(i, width=4)
|
|
||||||
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(spec_cr_r_stb[i].implies(self.pfv.cr.r_stb[i])),
|
|
||||||
Assert(self.pfv.cr.w_stb[i] == spec_cr_w_stb[i]),
|
|
||||||
Assert(spec_cr_w_stb[i].implies(pfv_cr_w_field == spec_cr_w_field)),
|
|
||||||
]
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class CRCheck(PowerFVCheck, name="_insn_cr"):
|
|
||||||
def __init_subclass__(cls, name, insn_cls):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.insn_cls = insn_cls
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = CRSpec(self.insn_cls, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,324 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import AnyConst
|
|
||||||
from amaranth.hdl.ast import ValueCastable
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"Instruction_I",
|
|
||||||
"Instruction_B",
|
|
||||||
"Instruction_D_add", "Instruction_D_cmp",
|
|
||||||
"Instruction_DX",
|
|
||||||
"Instruction_X_cmp",
|
|
||||||
"Instruction_XL_bc", "Instruction_XL_crl", "Instruction_XL_crf",
|
|
||||||
"Instruction_XFX_spr",
|
|
||||||
"Instruction_XO",
|
|
||||||
"Instruction_Z23_add",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_I(ValueCastable):
|
|
||||||
po = None
|
|
||||||
li = None
|
|
||||||
aa = None
|
|
||||||
lk = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, aa, lk):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
cls.aa = Const(aa, 1)
|
|
||||||
cls.lk = Const(lk, 1)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.li = AnyConst(signed(24))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self.lk, self.aa, self.li, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_B(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bo = None
|
|
||||||
bi = None
|
|
||||||
bd = None
|
|
||||||
aa = None
|
|
||||||
lk = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, aa, lk):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
cls.aa = Const(aa, 1)
|
|
||||||
cls.lk = Const(lk, 1)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bo = AnyConst(unsigned( 5))
|
|
||||||
self.bi = AnyConst(unsigned( 5))
|
|
||||||
self.bd = AnyConst( signed(14))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self.lk, self.aa, self.bd, self.bi, self.bo, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_XL_bc(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bo = None
|
|
||||||
bi = None
|
|
||||||
_0 = None
|
|
||||||
bh = None
|
|
||||||
xo = None
|
|
||||||
lk = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo, lk):
|
|
||||||
cls.po = Const(po, unsigned( 6))
|
|
||||||
cls.xo = Const(xo, unsigned(10))
|
|
||||||
cls.lk = Const(1)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bo = AnyConst(unsigned(5))
|
|
||||||
self.bi = AnyConst(unsigned(5))
|
|
||||||
self._0 = AnyConst(unsigned(3))
|
|
||||||
self.bh = AnyConst(unsigned(2))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self.lk, self.xo, self.bh, self._0, self.bi, self.bo, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_XL_crl(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bt = None
|
|
||||||
ba = None
|
|
||||||
bb = None
|
|
||||||
xo = None
|
|
||||||
_0 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo):
|
|
||||||
cls.po = Const(po, unsigned( 6))
|
|
||||||
cls.xo = Const(xo, unsigned(10))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bt = AnyConst(unsigned(5))
|
|
||||||
self.ba = AnyConst(unsigned(5))
|
|
||||||
self.bb = AnyConst(unsigned(5))
|
|
||||||
self._0 = AnyConst(unsigned(1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._0, self.xo, self.bb, self.ba, self.bt, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_XL_crf(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bf = None
|
|
||||||
_0 = None
|
|
||||||
bfa = None
|
|
||||||
_1 = None
|
|
||||||
_2 = None
|
|
||||||
xo = None
|
|
||||||
_3 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo):
|
|
||||||
cls.po = Const(po, unsigned( 6))
|
|
||||||
cls.xo = Const(xo, unsigned(10))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bf = AnyConst(unsigned(3))
|
|
||||||
self._0 = AnyConst(unsigned(2))
|
|
||||||
self.bfa = AnyConst(unsigned(3))
|
|
||||||
self._1 = AnyConst(unsigned(2))
|
|
||||||
self._2 = AnyConst(unsigned(5))
|
|
||||||
self._3 = AnyConst(unsigned(1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._3, self.xo, self._2, self._1, self.bfa, self._0, self.bf, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_D_add(ValueCastable):
|
|
||||||
po = None
|
|
||||||
rt = None
|
|
||||||
ra = None
|
|
||||||
_i = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.rt = AnyConst(unsigned(5))
|
|
||||||
self.ra = AnyConst(unsigned(5))
|
|
||||||
self._i = AnyConst(16)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def si(self):
|
|
||||||
return self._i.as_signed()
|
|
||||||
|
|
||||||
def ui(self):
|
|
||||||
return self._i.as_unsigned()
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._i, self.ra, self.rt, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_D_cmp(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bf = None
|
|
||||||
_0 = None
|
|
||||||
l = None
|
|
||||||
ra = None
|
|
||||||
_i = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bf = AnyConst(unsigned(3))
|
|
||||||
self._0 = AnyConst(unsigned(1))
|
|
||||||
self.l = AnyConst(unsigned(1))
|
|
||||||
self.ra = AnyConst(unsigned(5))
|
|
||||||
self._i = AnyConst(16)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def si(self):
|
|
||||||
return self._i.as_signed()
|
|
||||||
|
|
||||||
def ui(self):
|
|
||||||
return self._i.as_unsigned()
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._i, self.ra, self.l, self._0, self.bf, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_DX(ValueCastable):
|
|
||||||
po = None
|
|
||||||
rt = None
|
|
||||||
d1 = None
|
|
||||||
d0 = None
|
|
||||||
xo = None
|
|
||||||
d2 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
cls.xo = Const(xo, unsigned(5))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.rt = AnyConst(unsigned( 5))
|
|
||||||
self.d1 = AnyConst(unsigned( 5))
|
|
||||||
self.d0 = AnyConst(unsigned(10))
|
|
||||||
self.d2 = AnyConst(unsigned( 1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self.d2, self.xo, self.d0, self.d1, self.rt, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_X_cmp(ValueCastable):
|
|
||||||
po = None
|
|
||||||
bf = None
|
|
||||||
_0 = None
|
|
||||||
l = None
|
|
||||||
ra = None
|
|
||||||
rb = None
|
|
||||||
xo = None
|
|
||||||
_1 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo):
|
|
||||||
cls.po = Const(po, unsigned( 6))
|
|
||||||
cls.xo = Const(xo, unsigned(10))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.bf = AnyConst(unsigned(3))
|
|
||||||
self._0 = AnyConst(unsigned(1))
|
|
||||||
self.l = AnyConst(unsigned(1))
|
|
||||||
self.ra = AnyConst(unsigned(5))
|
|
||||||
self.rb = AnyConst(unsigned(5))
|
|
||||||
self._1 = AnyConst(unsigned(1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._1, self.xo, self.rb, self.ra, self.l, self._0, self.bf, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_XFX_spr(ValueCastable):
|
|
||||||
po = None
|
|
||||||
_gpr = None
|
|
||||||
spr = None
|
|
||||||
xo = None
|
|
||||||
_0 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo):
|
|
||||||
cls.po = Const(po, unsigned( 6))
|
|
||||||
cls.xo = Const(xo, unsigned(10))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._gpr = AnyConst(unsigned( 5))
|
|
||||||
self.spr = AnyConst(unsigned(10))
|
|
||||||
self._0 = AnyConst(unsigned( 1))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rs(self):
|
|
||||||
return self._gpr
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rt(self):
|
|
||||||
return self._gpr
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._0, self.xo, self.spr, self._gpr, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_XO(ValueCastable):
|
|
||||||
po = None
|
|
||||||
rt = None
|
|
||||||
ra = None
|
|
||||||
rb = None
|
|
||||||
oe = None
|
|
||||||
xo = None
|
|
||||||
rc = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo, oe=None, rc=None):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
cls.xo = Const(xo, unsigned(9))
|
|
||||||
if oe is not None:
|
|
||||||
cls.oe = Const(oe, unsigned(1))
|
|
||||||
if rc is not None:
|
|
||||||
cls.rc = Const(rc, unsigned(1))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.rt = AnyConst(unsigned(5))
|
|
||||||
self.ra = AnyConst(unsigned(5))
|
|
||||||
self.rb = AnyConst(unsigned(5))
|
|
||||||
if self.oe is None:
|
|
||||||
self.oe = AnyConst(unsigned(1))
|
|
||||||
if self.rc is None:
|
|
||||||
self.rc = AnyConst(unsigned(1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self.rc, self.xo, self.oe, self.rb, self.ra, self.rt, self.po)
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction_Z23_add(ValueCastable):
|
|
||||||
po = None
|
|
||||||
rt = None
|
|
||||||
ra = None
|
|
||||||
rb = None
|
|
||||||
cy = None
|
|
||||||
xo = None
|
|
||||||
_0 = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls, *, po, xo, cy):
|
|
||||||
cls.po = Const(po, unsigned(6))
|
|
||||||
cls.cy = Const(cy, unsigned(2))
|
|
||||||
cls.xo = Const(xo, unsigned(8))
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.rt = AnyConst(unsigned(5))
|
|
||||||
self.ra = AnyConst(unsigned(5))
|
|
||||||
self.rb = AnyConst(unsigned(5))
|
|
||||||
self._0 = AnyConst(unsigned(1))
|
|
||||||
|
|
||||||
@ValueCastable.lowermethod
|
|
||||||
def as_value(self):
|
|
||||||
return Cat(self._0, self.xo, self.cy, self.rb, self.ra, self.rt, self.po)
|
|
@ -1,99 +0,0 @@
|
|||||||
from . import _fmt
|
|
||||||
|
|
||||||
# Branches
|
|
||||||
|
|
||||||
class B (_fmt.Instruction_I, po=18, aa=0, lk=0): pass
|
|
||||||
class BA (_fmt.Instruction_I, po=18, aa=1, lk=0): pass
|
|
||||||
class BL (_fmt.Instruction_I, po=18, aa=0, lk=1): pass
|
|
||||||
class BLA (_fmt.Instruction_I, po=18, aa=1, lk=1): pass
|
|
||||||
|
|
||||||
class BC (_fmt.Instruction_B, po=16, aa=0, lk=0): pass
|
|
||||||
class BCA (_fmt.Instruction_B, po=16, aa=1, lk=0): pass
|
|
||||||
class BCL (_fmt.Instruction_B, po=16, aa=0, lk=1): pass
|
|
||||||
class BCLA (_fmt.Instruction_B, po=16, aa=1, lk=1): pass
|
|
||||||
|
|
||||||
class BCLR (_fmt.Instruction_XL_bc, po=19, xo= 16, lk=0): pass
|
|
||||||
class BCLRL (_fmt.Instruction_XL_bc, po=19, xo= 16, lk=1): pass
|
|
||||||
class BCCTR (_fmt.Instruction_XL_bc, po=19, xo=528, lk=0): pass
|
|
||||||
class BCCTRL (_fmt.Instruction_XL_bc, po=19, xo=528, lk=1): pass
|
|
||||||
class BCTAR (_fmt.Instruction_XL_bc, po=19, xo=560, lk=0): pass
|
|
||||||
class BCTARL (_fmt.Instruction_XL_bc, po=19, xo=560, lk=1): pass
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
class CRAND (_fmt.Instruction_XL_crl, po=19, xo=257): pass
|
|
||||||
class CROR (_fmt.Instruction_XL_crl, po=19, xo=449): pass
|
|
||||||
class CRNAND (_fmt.Instruction_XL_crl, po=19, xo=225): pass
|
|
||||||
class CRXOR (_fmt.Instruction_XL_crl, po=19, xo=193): pass
|
|
||||||
class CRNOR (_fmt.Instruction_XL_crl, po=19, xo= 33): pass
|
|
||||||
class CRANDC (_fmt.Instruction_XL_crl, po=19, xo=129): pass
|
|
||||||
class CREQV (_fmt.Instruction_XL_crl, po=19, xo=289): pass
|
|
||||||
class CRORC (_fmt.Instruction_XL_crl, po=19, xo=417): pass
|
|
||||||
|
|
||||||
class MCRF (_fmt.Instruction_XL_crf, po=19, xo=0): pass
|
|
||||||
|
|
||||||
# Add / Subtract from
|
|
||||||
|
|
||||||
class ADDI (_fmt.Instruction_D_add, po=14): pass
|
|
||||||
class ADDIS (_fmt.Instruction_D_add, po=15): pass
|
|
||||||
class ADDPCIS (_fmt.Instruction_DX, po=19, xo= 2): pass
|
|
||||||
class ADD (_fmt.Instruction_XO, po=31, xo=266, oe=0, rc=0): pass
|
|
||||||
class ADD_ (_fmt.Instruction_XO, po=31, xo=266, oe=0, rc=1): pass
|
|
||||||
class ADDO (_fmt.Instruction_XO, po=31, xo=266, oe=1, rc=0): pass
|
|
||||||
class ADDO_ (_fmt.Instruction_XO, po=31, xo=266, oe=1, rc=1): pass
|
|
||||||
class ADDIC (_fmt.Instruction_D_add, po=12): pass
|
|
||||||
class ADDIC_ (_fmt.Instruction_D_add, po=13): pass
|
|
||||||
class SUBF (_fmt.Instruction_XO, po=31, xo= 40, oe=0, rc=0): pass
|
|
||||||
class SUBF_ (_fmt.Instruction_XO, po=31, xo= 40, oe=0, rc=1): pass
|
|
||||||
class SUBFO (_fmt.Instruction_XO, po=31, xo= 40, oe=1, rc=0): pass
|
|
||||||
class SUBFO_ (_fmt.Instruction_XO, po=31, xo= 40, oe=1, rc=1): pass
|
|
||||||
class SUBFIC (_fmt.Instruction_D_add, po= 8): pass
|
|
||||||
class ADDC (_fmt.Instruction_XO, po=31, xo= 10, oe=0, rc=0): pass
|
|
||||||
class ADDC_ (_fmt.Instruction_XO, po=31, xo= 10, oe=0, rc=1): pass
|
|
||||||
class ADDCO (_fmt.Instruction_XO, po=31, xo= 10, oe=1, rc=0): pass
|
|
||||||
class ADDCO_ (_fmt.Instruction_XO, po=31, xo= 10, oe=1, rc=1): pass
|
|
||||||
class ADDE (_fmt.Instruction_XO, po=31, xo=138, oe=0, rc=0): pass
|
|
||||||
class ADDE_ (_fmt.Instruction_XO, po=31, xo=138, oe=0, rc=1): pass
|
|
||||||
class ADDEO (_fmt.Instruction_XO, po=31, xo=138, oe=1, rc=0): pass
|
|
||||||
class ADDEO_ (_fmt.Instruction_XO, po=31, xo=138, oe=1, rc=1): pass
|
|
||||||
class SUBFC (_fmt.Instruction_XO, po=31, xo= 8, oe=0, rc=0): pass
|
|
||||||
class SUBFC_ (_fmt.Instruction_XO, po=31, xo= 8, oe=0, rc=1): pass
|
|
||||||
class SUBFCO (_fmt.Instruction_XO, po=31, xo= 8, oe=1, rc=0): pass
|
|
||||||
class SUBFCO_ (_fmt.Instruction_XO, po=31, xo= 8, oe=1, rc=1): pass
|
|
||||||
class SUBFE (_fmt.Instruction_XO, po=31, xo=136, oe=0, rc=0): pass
|
|
||||||
class SUBFE_ (_fmt.Instruction_XO, po=31, xo=136, oe=0, rc=1): pass
|
|
||||||
class SUBFEO (_fmt.Instruction_XO, po=31, xo=136, oe=1, rc=0): pass
|
|
||||||
class SUBFEO_ (_fmt.Instruction_XO, po=31, xo=136, oe=1, rc=1): pass
|
|
||||||
class ADDME (_fmt.Instruction_XO, po=31, xo=234, oe=0, rc=0): pass
|
|
||||||
class ADDME_ (_fmt.Instruction_XO, po=31, xo=234, oe=0, rc=1): pass
|
|
||||||
class ADDMEO (_fmt.Instruction_XO, po=31, xo=234, oe=1, rc=0): pass
|
|
||||||
class ADDMEO_ (_fmt.Instruction_XO, po=31, xo=234, oe=1, rc=1): pass
|
|
||||||
class ADDZE (_fmt.Instruction_XO, po=31, xo=202, oe=0, rc=0): pass
|
|
||||||
class ADDZE_ (_fmt.Instruction_XO, po=31, xo=202, oe=0, rc=1): pass
|
|
||||||
class ADDZEO (_fmt.Instruction_XO, po=31, xo=202, oe=1, rc=0): pass
|
|
||||||
class ADDZEO_ (_fmt.Instruction_XO, po=31, xo=202, oe=1, rc=1): pass
|
|
||||||
class SUBFME (_fmt.Instruction_XO, po=31, xo=232, oe=0, rc=0): pass
|
|
||||||
class SUBFME_ (_fmt.Instruction_XO, po=31, xo=232, oe=0, rc=1): pass
|
|
||||||
class SUBFMEO (_fmt.Instruction_XO, po=31, xo=232, oe=1, rc=0): pass
|
|
||||||
class SUBFMEO_ (_fmt.Instruction_XO, po=31, xo=232, oe=1, rc=1): pass
|
|
||||||
class SUBFZE (_fmt.Instruction_XO, po=31, xo=200, oe=0, rc=0): pass
|
|
||||||
class SUBFZE_ (_fmt.Instruction_XO, po=31, xo=200, oe=0, rc=1): pass
|
|
||||||
class SUBFZEO (_fmt.Instruction_XO, po=31, xo=200, oe=1, rc=0): pass
|
|
||||||
class SUBFZEO_ (_fmt.Instruction_XO, po=31, xo=200, oe=1, rc=1): pass
|
|
||||||
class ADDEX (_fmt.Instruction_Z23_add, po=31, xo=170, cy=0): pass
|
|
||||||
class NEG (_fmt.Instruction_XO, po=31, xo=104, oe=0, rc=0): pass
|
|
||||||
class NEG_ (_fmt.Instruction_XO, po=31, xo=104, oe=0, rc=1): pass
|
|
||||||
class NEGO (_fmt.Instruction_XO, po=31, xo=104, oe=1, rc=0): pass
|
|
||||||
class NEGO_ (_fmt.Instruction_XO, po=31, xo=104, oe=1, rc=1): pass
|
|
||||||
|
|
||||||
# Compare
|
|
||||||
|
|
||||||
class CMPI (_fmt.Instruction_D_cmp, po=11): pass
|
|
||||||
class CMPLI (_fmt.Instruction_D_cmp, po=10): pass
|
|
||||||
class CMP (_fmt.Instruction_X_cmp, po=31, xo= 0): pass
|
|
||||||
class CMPL (_fmt.Instruction_X_cmp, po=31, xo= 32): pass
|
|
||||||
|
|
||||||
# Move To/From SPR
|
|
||||||
|
|
||||||
class MTSPR (_fmt.Instruction_XFX_spr, po=31, xo=467): pass
|
|
||||||
class MFSPR (_fmt.Instruction_XFX_spr, po=31, xo=339): pass
|
|
@ -1,165 +0,0 @@
|
|||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
|
|
||||||
from .. import PowerFVCheck
|
|
||||||
from ... import pfv, tb
|
|
||||||
|
|
||||||
from ._fmt import *
|
|
||||||
from ._insn import *
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["SPRMoveSpec", "SPRMoveCheck"]
|
|
||||||
|
|
||||||
|
|
||||||
class SPRMoveSpec(Elaboratable):
|
|
||||||
def __init__(self, insn_cls, post):
|
|
||||||
self.insn_cls = insn_cls
|
|
||||||
self.pfv = pfv.Interface()
|
|
||||||
self.post = tb.Trigger(cycle=post)
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield self.post
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# - don't hardcode SPRs, let cores declare what they support.
|
|
||||||
# - access restrictions (i.e. R/W, privileged)
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
spec_insn = self.insn_cls()
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
m.d.sync += [
|
|
||||||
Assume(self.pfv.stb),
|
|
||||||
Assume(self.pfv.insn[32:] == spec_insn),
|
|
||||||
]
|
|
||||||
|
|
||||||
spr = Record([
|
|
||||||
("num", 10),
|
|
||||||
("reserved", 1),
|
|
||||||
("undefined", 1),
|
|
||||||
("pfv", [
|
|
||||||
("r_mask", 64),
|
|
||||||
("r_data", 64),
|
|
||||||
("w_mask", 64),
|
|
||||||
("w_data", 64),
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
|
|
||||||
if isinstance(spec_insn, (MTSPR, MFSPR)):
|
|
||||||
m.d.comb += spr.num.eq(Cat(spec_insn.spr[5:10], spec_insn.spr[0:5]))
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.Switch(spr.num):
|
|
||||||
for num in range(808, 812):
|
|
||||||
with m.Case(num):
|
|
||||||
m.d.comb += spr.reserved.eq(1)
|
|
||||||
|
|
||||||
pfv_sprs = [
|
|
||||||
( 1, self.pfv.xer ),
|
|
||||||
( 8, self.pfv.lr ),
|
|
||||||
( 9, self.pfv.ctr ),
|
|
||||||
( 26, self.pfv.srr0),
|
|
||||||
( 27, self.pfv.srr1),
|
|
||||||
(815, self.pfv.tar ),
|
|
||||||
]
|
|
||||||
|
|
||||||
for num, pfv_spr in pfv_sprs:
|
|
||||||
with m.Case(num):
|
|
||||||
m.d.comb += [
|
|
||||||
spr.pfv.r_mask.eq(pfv_spr.r_mask),
|
|
||||||
spr.pfv.r_data.eq(pfv_spr.r_data),
|
|
||||||
spr.pfv.w_mask.eq(pfv_spr.w_mask),
|
|
||||||
spr.pfv.w_data.eq(pfv_spr.w_data),
|
|
||||||
]
|
|
||||||
|
|
||||||
with m.Default():
|
|
||||||
m.d.comb += spr.undefined.eq(1)
|
|
||||||
|
|
||||||
with m.If(self.post.stb):
|
|
||||||
# TODO: turn into assert
|
|
||||||
m.d.sync += Assume(spr.undefined.implies(self.pfv.intr))
|
|
||||||
|
|
||||||
# # MSR
|
|
||||||
# pfv_msr_pr = Signal()
|
|
||||||
# m.d.comb += pfv_msr_pr.eq(self.pfv.msr.r_data[63 - 49])
|
|
||||||
|
|
||||||
# with m.If(self.post.stb):
|
|
||||||
# m.d.sync += Assert(self.pfv.msr.r_mask[63 - 49])
|
|
||||||
|
|
||||||
# GPR
|
|
||||||
|
|
||||||
spec_rs_r_stb = Signal()
|
|
||||||
spec_rt_w_stb = Signal()
|
|
||||||
# spec_rt_w_mask = Signal(64)
|
|
||||||
spec_rt_w_data = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, MTSPR):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rs_r_stb.eq(~spr.reserved),
|
|
||||||
spec_rt_w_stb.eq(0),
|
|
||||||
]
|
|
||||||
|
|
||||||
elif isinstance(spec_insn, MFSPR):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_rs_r_stb .eq(0),
|
|
||||||
spec_rt_w_stb .eq(~spr.reserved),
|
|
||||||
spec_rt_w_data.eq(spr.pfv.r_data),
|
|
||||||
]
|
|
||||||
|
|
||||||
# # In problem state, reserved fields must return 0, so we include them in the mask.
|
|
||||||
# # In privileged state, reserved fields may return anything.
|
|
||||||
# with m.If(pfv_msr_pr):
|
|
||||||
# m.d.comb += spec_rt_w_mask.eq(Repl(1, 64))
|
|
||||||
# with m.Else():
|
|
||||||
# m.d.comb += spec_rt_w_mask.eq(spr.pfv.r_mask)
|
|
||||||
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(spec_rs_r_stb.implies(self.pfv.rs.r_stb)),
|
|
||||||
Assert(self.pfv.rt.w_stb == spec_rt_w_stb),
|
|
||||||
Assert(spec_rt_w_stb.implies(self.pfv.rt.w_data == (spr.pfv.r_data & spr.pfv.r_mask))),
|
|
||||||
]
|
|
||||||
# with m.If(spec_rt_w_stb):
|
|
||||||
# for i in range(64):
|
|
||||||
# with m.If(spr.pfv.w_mask[i]):
|
|
||||||
# m.d.sync += Assert(self.pfv.rt.w_data[i] == spec_rt_w_data[i])
|
|
||||||
|
|
||||||
# SPR
|
|
||||||
|
|
||||||
spec_spr_w_stb = Signal()
|
|
||||||
spec_spr_w_data = Signal(64)
|
|
||||||
|
|
||||||
if isinstance(spec_insn, MTSPR):
|
|
||||||
m.d.comb += [
|
|
||||||
spec_spr_w_stb .eq(1),
|
|
||||||
spec_spr_w_data.eq(self.pfv.rs.r_data),
|
|
||||||
]
|
|
||||||
elif isinstance(spec_insn, MFSPR):
|
|
||||||
m.d.comb += spec_spr_w_stb.eq(0)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
with m.If(self.post.stb & ~self.pfv.intr):
|
|
||||||
m.d.sync += [
|
|
||||||
Assert(spec_spr_w_stb == spr.pfv.w_mask.any()),
|
|
||||||
Assert((spr.pfv.w_data & spr.pfv.w_mask) == (spec_spr_w_data & spr.pfv.w_mask)),
|
|
||||||
]
|
|
||||||
|
|
||||||
return m
|
|
||||||
|
|
||||||
|
|
||||||
class SPRMoveCheck(PowerFVCheck, name="_insn_spr"):
|
|
||||||
def __init_subclass__(cls, name, insn_cls):
|
|
||||||
super().__init_subclass__(name)
|
|
||||||
cls.insn_cls = insn_cls
|
|
||||||
|
|
||||||
def get_testbench(self, dut, post):
|
|
||||||
tb_spec = SPRMoveSpec(self.insn_cls, post)
|
|
||||||
tb_top = tb.Testbench(tb_spec, dut)
|
|
||||||
return tb_top
|
|
@ -1,105 +0,0 @@
|
|||||||
from . import _insn
|
|
||||||
from ._branch import BranchCheck
|
|
||||||
from ._cr import CRCheck
|
|
||||||
from ._addsub import AddSubtractCheck
|
|
||||||
from ._compare import CompareCheck
|
|
||||||
from ._spr import SPRMoveCheck
|
|
||||||
|
|
||||||
|
|
||||||
# Branches
|
|
||||||
|
|
||||||
class B (BranchCheck, name="insn_b", insn_cls=_insn.B, ): pass
|
|
||||||
class BA (BranchCheck, name="insn_ba", insn_cls=_insn.BA ): pass
|
|
||||||
class BL (BranchCheck, name="insn_bl", insn_cls=_insn.BL ): pass
|
|
||||||
class BLA (BranchCheck, name="insn_bla", insn_cls=_insn.BLA ): pass
|
|
||||||
|
|
||||||
class BC (BranchCheck, name="insn_bc", insn_cls=_insn.BC ): pass
|
|
||||||
class BCA (BranchCheck, name="insn_bca", insn_cls=_insn.BCA ): pass
|
|
||||||
class BCL (BranchCheck, name="insn_bcl", insn_cls=_insn.BCL ): pass
|
|
||||||
class BCLA (BranchCheck, name="insn_bcla", insn_cls=_insn.BCLA ): pass
|
|
||||||
|
|
||||||
class BCLR (BranchCheck, name="insn_bclr", insn_cls=_insn.BCLR ): pass
|
|
||||||
class BCLRL (BranchCheck, name="insn_bclrl", insn_cls=_insn.BCLRL ): pass
|
|
||||||
class BCCTR (BranchCheck, name="insn_bcctr", insn_cls=_insn.BCCTR ): pass
|
|
||||||
class BCCTRL (BranchCheck, name="insn_bcctrl", insn_cls=_insn.BCCTRL): pass
|
|
||||||
class BCTAR (BranchCheck, name="insn_bctar", insn_cls=_insn.BCTAR ): pass
|
|
||||||
class BCTARL (BranchCheck, name="insn_bctarl", insn_cls=_insn.BCTARL): pass
|
|
||||||
|
|
||||||
# CR
|
|
||||||
|
|
||||||
class CRAND (CRCheck, name="insn_crand", insn_cls=_insn.CRAND ): pass
|
|
||||||
class CROR (CRCheck, name="insn_cror", insn_cls=_insn.CROR ): pass
|
|
||||||
class CRNAND (CRCheck, name="insn_crnand", insn_cls=_insn.CRNAND): pass
|
|
||||||
class CRXOR (CRCheck, name="insn_crxor", insn_cls=_insn.CRXOR ): pass
|
|
||||||
class CRNOR (CRCheck, name="insn_crnor", insn_cls=_insn.CRNOR ): pass
|
|
||||||
class CRANDC (CRCheck, name="insn_crandc", insn_cls=_insn.CRANDC): pass
|
|
||||||
class CREQV (CRCheck, name="insn_creqv", insn_cls=_insn.CREQV ): pass
|
|
||||||
class CRORC (CRCheck, name="insn_crorc", insn_cls=_insn.CRORC ): pass
|
|
||||||
|
|
||||||
class MCRF (CRCheck, name="insn_mcrf", insn_cls=_insn.MCRF ): pass
|
|
||||||
|
|
||||||
# Add / Subtract from
|
|
||||||
|
|
||||||
class ADDI (AddSubtractCheck, name="insn_addi", insn_cls=_insn.ADDI ): pass
|
|
||||||
class ADDIS (AddSubtractCheck, name="insn_addis", insn_cls=_insn.ADDIS ): pass
|
|
||||||
class ADDPCIS (AddSubtractCheck, name="insn_addpcis", insn_cls=_insn.ADDPCIS ): pass
|
|
||||||
class ADD (AddSubtractCheck, name="insn_add", insn_cls=_insn.ADD ): pass
|
|
||||||
class ADD_ (AddSubtractCheck, name="insn_add.", insn_cls=_insn.ADD_ ): pass
|
|
||||||
class ADDO (AddSubtractCheck, name="insn_addo", insn_cls=_insn.ADDO ): pass
|
|
||||||
class ADDO_ (AddSubtractCheck, name="insn_addo.", insn_cls=_insn.ADDO_ ): pass
|
|
||||||
class ADDIC (AddSubtractCheck, name="insn_addic", insn_cls=_insn.ADDIC ): pass
|
|
||||||
class ADDIC_ (AddSubtractCheck, name="insn_addic.", insn_cls=_insn.ADDIC_ ): pass
|
|
||||||
class SUBF (AddSubtractCheck, name="insn_subf", insn_cls=_insn.SUBF ): pass
|
|
||||||
class SUBF_ (AddSubtractCheck, name="insn_subf.", insn_cls=_insn.SUBF_ ): pass
|
|
||||||
class SUBFO (AddSubtractCheck, name="insn_subfo", insn_cls=_insn.SUBFO ): pass
|
|
||||||
class SUBFO_ (AddSubtractCheck, name="insn_subfo.", insn_cls=_insn.SUBFO_ ): pass
|
|
||||||
class SUBFIC (AddSubtractCheck, name="insn_subfic", insn_cls=_insn.SUBFIC ): pass
|
|
||||||
class ADDC (AddSubtractCheck, name="insn_addc", insn_cls=_insn.ADDC ): pass
|
|
||||||
class ADDC_ (AddSubtractCheck, name="insn_addc.", insn_cls=_insn.ADDC_ ): pass
|
|
||||||
class ADDCO (AddSubtractCheck, name="insn_addco", insn_cls=_insn.ADDCO ): pass
|
|
||||||
class ADDCO_ (AddSubtractCheck, name="insn_addco.", insn_cls=_insn.ADDCO_ ): pass
|
|
||||||
class ADDE (AddSubtractCheck, name="insn_adde", insn_cls=_insn.ADDE ): pass
|
|
||||||
class ADDE_ (AddSubtractCheck, name="insn_adde.", insn_cls=_insn.ADDE_ ): pass
|
|
||||||
class ADDEO (AddSubtractCheck, name="insn_addeo", insn_cls=_insn.ADDEO ): pass
|
|
||||||
class ADDEO_ (AddSubtractCheck, name="insn_addeo.", insn_cls=_insn.ADDEO_ ): pass
|
|
||||||
class SUBFC (AddSubtractCheck, name="insn_subfc", insn_cls=_insn.SUBFC ): pass
|
|
||||||
class SUBFC_ (AddSubtractCheck, name="insn_subfc.", insn_cls=_insn.SUBFC_ ): pass
|
|
||||||
class SUBFCO (AddSubtractCheck, name="insn_subfco", insn_cls=_insn.SUBFCO ): pass
|
|
||||||
class SUBFCO_ (AddSubtractCheck, name="insn_subfco.", insn_cls=_insn.SUBFCO_ ): pass
|
|
||||||
class SUBFE (AddSubtractCheck, name="insn_subfe", insn_cls=_insn.SUBFE ): pass
|
|
||||||
class SUBFE_ (AddSubtractCheck, name="insn_subfe.", insn_cls=_insn.SUBFE_ ): pass
|
|
||||||
class SUBFEO (AddSubtractCheck, name="insn_subfeo", insn_cls=_insn.SUBFEO ): pass
|
|
||||||
class SUBFEO_ (AddSubtractCheck, name="insn_subfeo.", insn_cls=_insn.SUBFEO_ ): pass
|
|
||||||
class ADDME (AddSubtractCheck, name="insn_addme", insn_cls=_insn.ADDME ): pass
|
|
||||||
class ADDME_ (AddSubtractCheck, name="insn_addme.", insn_cls=_insn.ADDME_ ): pass
|
|
||||||
class ADDMEO (AddSubtractCheck, name="insn_addmeo", insn_cls=_insn.ADDMEO ): pass
|
|
||||||
class ADDMEO_ (AddSubtractCheck, name="insn_addmeo.", insn_cls=_insn.ADDMEO_ ): pass
|
|
||||||
class ADDZE (AddSubtractCheck, name="insn_addze", insn_cls=_insn.ADDZE ): pass
|
|
||||||
class ADDZE_ (AddSubtractCheck, name="insn_addze.", insn_cls=_insn.ADDZE_ ): pass
|
|
||||||
class ADDZEO (AddSubtractCheck, name="insn_addzeo", insn_cls=_insn.ADDZEO ): pass
|
|
||||||
class ADDZEO_ (AddSubtractCheck, name="insn_addzeo.", insn_cls=_insn.ADDZEO_ ): pass
|
|
||||||
class SUBFME (AddSubtractCheck, name="insn_subfme", insn_cls=_insn.SUBFME ): pass
|
|
||||||
class SUBFME_ (AddSubtractCheck, name="insn_subfme.", insn_cls=_insn.SUBFME_ ): pass
|
|
||||||
class SUBFMEO (AddSubtractCheck, name="insn_subfmeo", insn_cls=_insn.SUBFMEO ): pass
|
|
||||||
class SUBFMEO_ (AddSubtractCheck, name="insn_subfmeo.", insn_cls=_insn.SUBFMEO_): pass
|
|
||||||
class SUBFZE (AddSubtractCheck, name="insn_subfze", insn_cls=_insn.SUBFZE ): pass
|
|
||||||
class SUBFZE_ (AddSubtractCheck, name="insn_subfze.", insn_cls=_insn.SUBFZE_ ): pass
|
|
||||||
class SUBFZEO (AddSubtractCheck, name="insn_subfzeo", insn_cls=_insn.SUBFZEO ): pass
|
|
||||||
class SUBFZEO_ (AddSubtractCheck, name="insn_subfzeo.", insn_cls=_insn.SUBFZEO_): pass
|
|
||||||
class ADDEX (AddSubtractCheck, name="insn_addex", insn_cls=_insn.ADDEX ): pass
|
|
||||||
class NEG (AddSubtractCheck, name="insn_neg", insn_cls=_insn.NEG ): pass
|
|
||||||
class NEG_ (AddSubtractCheck, name="insn_neg.", insn_cls=_insn.NEG_ ): pass
|
|
||||||
class NEGO (AddSubtractCheck, name="insn_nego", insn_cls=_insn.NEGO ): pass
|
|
||||||
class NEGO_ (AddSubtractCheck, name="insn_nego.", insn_cls=_insn.NEGO_ ): pass
|
|
||||||
|
|
||||||
# Compare
|
|
||||||
|
|
||||||
class CMPI (CompareCheck, name="insn_cmpi", insn_cls=_insn.CMPI ): pass
|
|
||||||
class CMPLI (CompareCheck, name="insn_cmpli", insn_cls=_insn.CMPLI ): pass
|
|
||||||
class CMP (CompareCheck, name="insn_cmp", insn_cls=_insn.CMP ): pass
|
|
||||||
class CMPL (CompareCheck, name="insn_cmpl", insn_cls=_insn.CMPL ): pass
|
|
||||||
|
|
||||||
# Move To/From SPR
|
|
||||||
|
|
||||||
class MTSPR (SPRMoveCheck, name="insn_mtspr", insn_cls=_insn.MTSPR): pass
|
|
||||||
class MFSPR (SPRMoveCheck, name="insn_mfspr", insn_cls=_insn.MFSPR): pass
|
|
@ -0,0 +1,19 @@
|
|||||||
|
__all__ = ["PowerFVCore"]
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFVCore:
|
||||||
|
@classmethod
|
||||||
|
def add_check_arguments(cls, parser):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wrapper(cls, **kwargs):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_build_arguments(cls, parser):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_files(cls, platform, wrapper, *, src_dir, **kwargs):
|
||||||
|
pass
|
@ -0,0 +1,111 @@
|
|||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
|
from amaranth import *
|
||||||
|
from amaranth.asserts import AnyConst
|
||||||
|
from amaranth.hdl.ast import ValueCastable, Value
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["InsnField", "WordInsn"]
|
||||||
|
|
||||||
|
|
||||||
|
class InsnField:
|
||||||
|
def __init__(self, value=None):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def shape(self):
|
||||||
|
return self._shape
|
||||||
|
|
||||||
|
@property
|
||||||
|
def offset(self):
|
||||||
|
return self._offset
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
if value is not None and not isinstance(value, int):
|
||||||
|
raise TypeError("Field value must be an integer, not {!r}"
|
||||||
|
.format(value))
|
||||||
|
self._value = value
|
||||||
|
|
||||||
|
def as_const(self):
|
||||||
|
if self.value is not None:
|
||||||
|
return Const(self.value, self.shape)
|
||||||
|
else:
|
||||||
|
return AnyConst(self.shape)
|
||||||
|
|
||||||
|
|
||||||
|
class WordInsn(ValueCastable):
|
||||||
|
SIZE = 32
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
value_map = {}
|
||||||
|
field_map = {}
|
||||||
|
curr_offset = 0
|
||||||
|
|
||||||
|
for field in sorted(self.fields, key=attrgetter('offset')):
|
||||||
|
if not isinstance(field, InsnField):
|
||||||
|
raise TypeError("Field must be an instance of InsnField, not {!r}"
|
||||||
|
.format(field))
|
||||||
|
if field.name in field_map:
|
||||||
|
raise ValueError("Duplicate field name '{}'".format(field.name))
|
||||||
|
if curr_offset > field.offset:
|
||||||
|
raise ValueError("Field '{}' at offset {} overlaps with its predecessor"
|
||||||
|
.format(field.name, field.offset))
|
||||||
|
|
||||||
|
# Add undefined bits located between this field and its predecessor.
|
||||||
|
if curr_offset < field.offset:
|
||||||
|
undef_bits = AnyConst(unsigned(field.offset - curr_offset))
|
||||||
|
value_map[curr_offset] = undef_bits
|
||||||
|
|
||||||
|
value_map[field.offset] = field.as_const()
|
||||||
|
field_map[field.name ] = field
|
||||||
|
|
||||||
|
curr_offset = field.offset + field.shape.width
|
||||||
|
|
||||||
|
if curr_offset > self.SIZE:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
# Add undefined bits located after the last field.
|
||||||
|
if curr_offset < self.SIZE:
|
||||||
|
undef_bits = AnyConst(unsigned(self.SIZE - curr_offset))
|
||||||
|
value_map[curr_offset] = undef_bits
|
||||||
|
|
||||||
|
self.field_map = field_map
|
||||||
|
self.value_map = value_map
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fields(self):
|
||||||
|
return self._fields
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self[name]
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
if isinstance(item, str):
|
||||||
|
try:
|
||||||
|
field = self.field_map[item]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError("WordInsn {!r} does not have a field '{}'"
|
||||||
|
.format(self, item))
|
||||||
|
value = self.value_map[field.offset]
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return Value.__getitem__(self, item)
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError("WordInsn {!r} does not have a field '{}'"
|
||||||
|
.format(self, item))
|
||||||
|
|
||||||
|
@ValueCastable.lowermethod
|
||||||
|
def as_value(self):
|
||||||
|
value = Cat(v for o, v in sorted(self.value_map.items(), reverse=True))
|
||||||
|
assert len(value) == self.SIZE
|
||||||
|
return value
|
@ -0,0 +1,115 @@
|
|||||||
|
from . import WordInsn
|
||||||
|
import power_fv.insn.field as f
|
||||||
|
|
||||||
|
|
||||||
|
# Branches
|
||||||
|
|
||||||
|
class B (WordInsn): _fields = (f.PO(18), f.LI(), f.AA(0), f.LK(0))
|
||||||
|
class BA (WordInsn): _fields = (f.PO(18), f.LI(), f.AA(1), f.LK(0))
|
||||||
|
class BL (WordInsn): _fields = (f.PO(18), f.LI(), f.AA(0), f.LK(1))
|
||||||
|
class BLA (WordInsn): _fields = (f.PO(18), f.LI(), f.AA(1), f.LK(1))
|
||||||
|
|
||||||
|
class BC (WordInsn): _fields = (f.PO(16), f.BO(), f.BI(), f.BD(), f.AA(0), f.LK(0))
|
||||||
|
class BCA (WordInsn): _fields = (f.PO(16), f.BO(), f.BI(), f.BD(), f.AA(1), f.LK(0))
|
||||||
|
class BCL (WordInsn): _fields = (f.PO(16), f.BO(), f.BI(), f.BD(), f.AA(0), f.LK(1))
|
||||||
|
class BCLA (WordInsn): _fields = (f.PO(16), f.BO(), f.BI(), f.BD(), f.AA(1), f.LK(1))
|
||||||
|
|
||||||
|
class BCLR (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL( 16), f.LK(0))
|
||||||
|
class BCLRL (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL( 16), f.LK(1))
|
||||||
|
class BCCTR (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL(528), f.LK(0))
|
||||||
|
class BCCTRL (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL(528), f.LK(1))
|
||||||
|
class BCTAR (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL(560), f.LK(0))
|
||||||
|
class BCTARL (WordInsn): _fields = (f.PO(19), f.BO(), f.BI(), f.BH(), f.XO_XL(560), f.LK(1))
|
||||||
|
|
||||||
|
# Condition Register
|
||||||
|
|
||||||
|
class CRAND (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(257))
|
||||||
|
class CROR (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(449))
|
||||||
|
class CRNAND (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(225))
|
||||||
|
class CRXOR (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(193))
|
||||||
|
class CRNOR (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL( 33))
|
||||||
|
class CRANDC (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(129))
|
||||||
|
class CREQV (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(289))
|
||||||
|
class CRORC (WordInsn): _fields = (f.PO(19), f.BT(), f.BA(), f.BB(), f.XO_XL(417))
|
||||||
|
class MCRF (WordInsn): _fields = (f.PO(19), f.BF(), f.BFA(), f.XO_XL( 0))
|
||||||
|
|
||||||
|
# Add / Subtract From
|
||||||
|
|
||||||
|
class ADDI (WordInsn): _fields = (f.PO(14), f.RT(), f.RA(), f.SI())
|
||||||
|
class ADDIS (WordInsn): _fields = (f.PO(15), f.RT(), f.RA(), f.SI())
|
||||||
|
class ADDPCIS (WordInsn): _fields = (f.PO(19), f.RT(), f.d1(), f.d0(), f.XO_DX(2), f.d2())
|
||||||
|
class ADD (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(266), f.Rc(0))
|
||||||
|
class ADD_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(266), f.Rc(1))
|
||||||
|
class ADDO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(266), f.Rc(0))
|
||||||
|
class ADDO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(266), f.Rc(1))
|
||||||
|
class ADDIC (WordInsn): _fields = (f.PO(12), f.RT(), f.RA(), f.SI())
|
||||||
|
class ADDIC_ (WordInsn): _fields = (f.PO(13), f.RT(), f.RA(), f.SI())
|
||||||
|
class SUBF (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 40), f.Rc(0))
|
||||||
|
class SUBF_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 40), f.Rc(1))
|
||||||
|
class SUBFO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 40), f.Rc(0))
|
||||||
|
class SUBFO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 40), f.Rc(1))
|
||||||
|
class SUBFIC (WordInsn): _fields = (f.PO( 8), f.RT(), f.RA(), f.SI())
|
||||||
|
class ADDC (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 10), f.Rc(0))
|
||||||
|
class ADDC_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 10), f.Rc(1))
|
||||||
|
class ADDCO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 10), f.Rc(0))
|
||||||
|
class ADDCO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 10), f.Rc(1))
|
||||||
|
class ADDE (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(138), f.Rc(0))
|
||||||
|
class ADDE_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(138), f.Rc(1))
|
||||||
|
class ADDEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(138), f.Rc(0))
|
||||||
|
class ADDEO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(138), f.Rc(1))
|
||||||
|
class SUBFC (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 8), f.Rc(0))
|
||||||
|
class SUBFC_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO( 8), f.Rc(1))
|
||||||
|
class SUBFCO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 8), f.Rc(0))
|
||||||
|
class SUBFCO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO( 8), f.Rc(1))
|
||||||
|
class SUBFE (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(136), f.Rc(0))
|
||||||
|
class SUBFE_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(136), f.Rc(1))
|
||||||
|
class SUBFEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(136), f.Rc(0))
|
||||||
|
class SUBFEO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(136), f.Rc(1))
|
||||||
|
class ADDME (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(234), f.Rc(0))
|
||||||
|
class ADDME_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(234), f.Rc(1))
|
||||||
|
class ADDMEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(234), f.Rc(0))
|
||||||
|
class ADDMEO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(234), f.Rc(1))
|
||||||
|
class ADDZE (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(202), f.Rc(0))
|
||||||
|
class ADDZE_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(202), f.Rc(1))
|
||||||
|
class ADDZEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(202), f.Rc(0))
|
||||||
|
class ADDZEO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(202), f.Rc(1))
|
||||||
|
class SUBFME (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(232), f.Rc(0))
|
||||||
|
class SUBFME_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(232), f.Rc(1))
|
||||||
|
class SUBFMEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(232), f.Rc(0))
|
||||||
|
class SUBFMEO_(WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(232), f.Rc(1))
|
||||||
|
class SUBFZE (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(200), f.Rc(0))
|
||||||
|
class SUBFZE_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(200), f.Rc(1))
|
||||||
|
class SUBFZEO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(200), f.Rc(0))
|
||||||
|
class SUBFZEO_(WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(200), f.Rc(1))
|
||||||
|
class ADDEX (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.CY(0), f.XO_Z23(170))
|
||||||
|
class NEG (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(104), f.Rc(0))
|
||||||
|
class NEG_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(0), f.XO(104), f.Rc(1))
|
||||||
|
class NEGO (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(104), f.Rc(0))
|
||||||
|
class NEGO_ (WordInsn): _fields = (f.PO(31), f.RT(), f.RA(), f.RB(), f.OE(1), f.XO(104), f.Rc(1))
|
||||||
|
|
||||||
|
# Compare
|
||||||
|
|
||||||
|
class CMPI (WordInsn): _fields = (f.PO(11), f.BF(), f.L_D10(), f.RA(), f.SI())
|
||||||
|
class CMPLI (WordInsn): _fields = (f.PO(10), f.BF(), f.L_D10(), f.RA(), f.UI())
|
||||||
|
class CMP (WordInsn): _fields = (f.PO(31), f.BF(), f.L_X10(), f.RA(), f.RB(), f.XO_X( 0))
|
||||||
|
class CMPL (WordInsn): _fields = (f.PO(31), f.BF(), f.L_X10(), f.RA(), f.RB(), f.XO_X( 32))
|
||||||
|
class CMPRB (WordInsn): _fields = (f.PO(31), f.BF(), f.L_X10(), f.RA(), f.RB(), f.XO_X(192))
|
||||||
|
class CMPEQB (WordInsn): _fields = (f.PO(31), f.BF(), f.L_X10(), f.RA(), f.RB(), f.XO_X(224))
|
||||||
|
|
||||||
|
# Move To/From System Register
|
||||||
|
|
||||||
|
class MTMSR (WordInsn): _fields = (f.PO(31), f.RS(), f.L_X15(), f.XO_X(146))
|
||||||
|
class MFMSR (WordInsn): _fields = (f.PO(31), f.RT(), f.XO_X( 83))
|
||||||
|
|
||||||
|
class MTXER (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR( 1), f.XO_XFX(467))
|
||||||
|
class MFXER (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR( 1), f.XO_XFX(339))
|
||||||
|
class MTLR (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR( 8), f.XO_XFX(467))
|
||||||
|
class MFLR (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR( 8), f.XO_XFX(339))
|
||||||
|
class MTCTR (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR( 9), f.XO_XFX(467))
|
||||||
|
class MFCTR (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR( 9), f.XO_XFX(339))
|
||||||
|
class MTSRR0 (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR( 26), f.XO_XFX(467))
|
||||||
|
class MFSRR0 (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR( 26), f.XO_XFX(339))
|
||||||
|
class MTSRR1 (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR( 27), f.XO_XFX(467))
|
||||||
|
class MFSRR1 (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR( 27), f.XO_XFX(339))
|
||||||
|
class MTTAR (WordInsn): _fields = (f.PO(31), f.RS(), f.SPR(815), f.XO_XFX(467))
|
||||||
|
class MFTAR (WordInsn): _fields = (f.PO(31), f.RT(), f.SPR(815), f.XO_XFX(339))
|
@ -0,0 +1,50 @@
|
|||||||
|
from amaranth import signed, unsigned
|
||||||
|
|
||||||
|
from . import InsnField
|
||||||
|
|
||||||
|
|
||||||
|
class AA (InsnField): _shape = unsigned( 1); _offset = 30; _name = "AA"
|
||||||
|
class BA (InsnField): _shape = unsigned( 5); _offset = 11; _name = "BA"
|
||||||
|
class BB (InsnField): _shape = unsigned( 5); _offset = 16; _name = "BB"
|
||||||
|
class BD (InsnField): _shape = signed(14); _offset = 16; _name = "BD"
|
||||||
|
class BF (InsnField): _shape = unsigned( 3); _offset = 6; _name = "BF"
|
||||||
|
class BFA (InsnField): _shape = unsigned( 3); _offset = 11; _name = "BFA"
|
||||||
|
class BH (InsnField): _shape = unsigned( 2); _offset = 19; _name = "BH"
|
||||||
|
class BI (InsnField): _shape = unsigned( 5); _offset = 11; _name = "BI"
|
||||||
|
class BO (InsnField): _shape = unsigned( 5); _offset = 6; _name = "BO"
|
||||||
|
class BT (InsnField): _shape = unsigned( 5); _offset = 6; _name = "BT"
|
||||||
|
class CY (InsnField): _shape = unsigned( 2); _offset = 21; _name = "CY"
|
||||||
|
class d0 (InsnField): _shape = signed(10); _offset = 16; _name = "d0"
|
||||||
|
class d1 (InsnField): _shape = signed( 5); _offset = 11; _name = "d1"
|
||||||
|
class d2 (InsnField): _shape = signed( 1); _offset = 31; _name = "d2"
|
||||||
|
class L_D10 (InsnField): _shape = unsigned( 1); _offset = 10; _name = "L"
|
||||||
|
class L_X10 (InsnField): _shape = unsigned( 1); _offset = 10; _name = "L"
|
||||||
|
class L_X15 (InsnField): _shape = unsigned( 1); _offset = 15; _name = "L"
|
||||||
|
class LI (InsnField): _shape = signed(24); _offset = 6; _name = "LI"
|
||||||
|
class LK (InsnField): _shape = unsigned( 1); _offset = 31; _name = "LK"
|
||||||
|
class OE (InsnField): _shape = unsigned( 1); _offset = 21; _name = "OE"
|
||||||
|
class PO (InsnField): _shape = unsigned( 6); _offset = 0; _name = "PO"
|
||||||
|
class RA (InsnField): _shape = unsigned( 5); _offset = 11; _name = "RA"
|
||||||
|
class RB (InsnField): _shape = unsigned( 5); _offset = 16; _name = "RB"
|
||||||
|
class Rc (InsnField): _shape = unsigned( 1); _offset = 31; _name = "Rc"
|
||||||
|
class RS (InsnField): _shape = unsigned( 5); _offset = 6; _name = "RS"
|
||||||
|
class RT (InsnField): _shape = unsigned( 5); _offset = 6; _name = "RT"
|
||||||
|
class SI (InsnField): _shape = signed(16); _offset = 16; _name = "SI"
|
||||||
|
class UI (InsnField): _shape = unsigned(16); _offset = 16; _name = "UI"
|
||||||
|
class XO (InsnField): _shape = unsigned( 9); _offset = 22; _name = "XO"
|
||||||
|
class XO_DX (InsnField): _shape = unsigned( 5); _offset = 26; _name = "XO"
|
||||||
|
class XO_X (InsnField): _shape = unsigned(10); _offset = 21; _name = "XO"
|
||||||
|
class XO_XFX(InsnField): _shape = unsigned(10); _offset = 21; _name = "XO"
|
||||||
|
class XO_XL (InsnField): _shape = unsigned(10); _offset = 21; _name = "XO"
|
||||||
|
class XO_Z23(InsnField): _shape = unsigned( 8); _offset = 23; _name = "XO"
|
||||||
|
|
||||||
|
|
||||||
|
class SPR(InsnField):
|
||||||
|
_shape = unsigned(10)
|
||||||
|
_offset = 11
|
||||||
|
_name = "SPR"
|
||||||
|
|
||||||
|
def __init__(self, value=None):
|
||||||
|
super().__init__(value)
|
||||||
|
if self.value is not None:
|
||||||
|
self.value = (self.value & 0x1f) << 5 | (self.value >> 5)
|
@ -0,0 +1,27 @@
|
|||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
from power_fv.insn import WordInsn
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["InsnSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class InsnSpec(metaclass=ABCMeta):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
@property
|
||||||
|
def insn(self):
|
||||||
|
return self._insn
|
||||||
|
|
||||||
|
@insn.setter
|
||||||
|
def insn(self, insn):
|
||||||
|
if not isinstance(insn, WordInsn):
|
||||||
|
raise TypeError("Instruction must be an instance of WordInsn, not {!r}"
|
||||||
|
.format(insn))
|
||||||
|
self._insn = insn
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def elaborate(self, platform):
|
||||||
|
raise NotImplementedError
|
@ -0,0 +1,279 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.insn.const import *
|
||||||
|
|
||||||
|
from . import InsnSpec
|
||||||
|
from .utils import iea
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["AddSubSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class AddSubSpec(InsnSpec, Elaboratable):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.stb .eq(1),
|
||||||
|
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
|
||||||
|
self.pfv.intr.eq(0),
|
||||||
|
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
|
||||||
|
self.pfv.msr.r_mask.sf.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
src_a = Signal(signed(64))
|
||||||
|
src_b = Signal(signed(64))
|
||||||
|
src_c = Signal()
|
||||||
|
result = Signal(unsigned(65))
|
||||||
|
|
||||||
|
ca_64 = Signal()
|
||||||
|
ca_32 = Signal()
|
||||||
|
ov_64 = Signal()
|
||||||
|
ov_32 = Signal()
|
||||||
|
|
||||||
|
# Operand A : (RA) or 0 or NIA or ~(RA)
|
||||||
|
|
||||||
|
if isinstance(self.insn, (ADDI, ADDIS)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ra.index.eq(self.insn.RA),
|
||||||
|
self.pfv.ra.r_stb.eq(self.insn.RA != 0),
|
||||||
|
src_a.eq(Mux(self.insn.RA != 0, self.pfv.ra.r_data, 0)),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, ADDPCIS):
|
||||||
|
m.d.comb += src_a.eq(self.pfv.nia)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
ADD , ADD_ , ADDO , ADDO_ ,
|
||||||
|
ADDIC, ADDIC_,
|
||||||
|
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
||||||
|
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
||||||
|
ADDME, ADDME_, ADDMEO, ADDMEO_,
|
||||||
|
ADDZE, ADDZE_, ADDZEO, ADDZEO_,
|
||||||
|
ADDEX,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ra.index.eq(self.insn.RA),
|
||||||
|
self.pfv.ra.r_stb.eq(1),
|
||||||
|
src_a.eq(self.pfv.ra.r_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
||||||
|
SUBFIC,
|
||||||
|
SUBFC , SUBFC_ , SUBFCO , SUBFCO_ ,
|
||||||
|
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
||||||
|
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
||||||
|
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
||||||
|
NEG , NEG_ , NEGO , NEGO_ ,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ra.index.eq(self.insn.RA),
|
||||||
|
self.pfv.ra.r_stb.eq(1),
|
||||||
|
src_a.eq(~self.pfv.ra.r_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Operand B : SI or SI<<16 or D<<16 or (RB) or -1 or 0
|
||||||
|
|
||||||
|
if isinstance(self.insn, (ADDI, ADDIC, ADDIC_, SUBFIC)):
|
||||||
|
m.d.comb += src_b.eq(self.insn.SI)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, ADDIS):
|
||||||
|
m.d.comb += src_b.eq(Cat(Const(0, 16), self.insn.SI).as_signed())
|
||||||
|
|
||||||
|
elif isinstance(self.insn, ADDPCIS):
|
||||||
|
imm_d = Signal(signed(16))
|
||||||
|
m.d.comb += [
|
||||||
|
imm_d.eq(Cat(self.insn.d2, self.insn.d1, self.insn.d0)),
|
||||||
|
src_b.eq(Cat(Const(0, 16), imm_d).as_signed()),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
ADD , ADD_ , ADDO , ADDO_ ,
|
||||||
|
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
||||||
|
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
||||||
|
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
||||||
|
SUBFC, SUBFC_, SUBFCO, SUBFCO_,
|
||||||
|
SUBFE, SUBFE_, SUBFEO, SUBFEO_,
|
||||||
|
ADDEX,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.rb.index.eq(self.insn.RB),
|
||||||
|
self.pfv.rb.r_stb.eq(1),
|
||||||
|
src_b.eq(self.pfv.rb.r_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
||||||
|
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
||||||
|
)):
|
||||||
|
m.d.comb += src_b.eq(-1)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
||||||
|
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
||||||
|
NEG , NEG_ , NEGO , NEGO_ ,
|
||||||
|
)):
|
||||||
|
m.d.comb += src_b.eq(0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Operand C : 0 or 1 or XER.CA or XER.OV
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
ADDI , ADDIS , ADDPCIS,
|
||||||
|
ADD , ADD_ , ADDO , ADDO_ ,
|
||||||
|
ADDIC, ADDIC_,
|
||||||
|
ADDC , ADDC_ , ADDCO , ADDCO_,
|
||||||
|
)):
|
||||||
|
m.d.comb += src_c.eq(0)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
SUBF , SUBF_ , SUBFO , SUBFO_ ,
|
||||||
|
SUBFIC,
|
||||||
|
SUBFC , SUBFC_, SUBFCO, SUBFCO_,
|
||||||
|
NEG , NEG_ , NEGO , NEGO_ ,
|
||||||
|
)):
|
||||||
|
m.d.comb += src_c.eq(1)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
||||||
|
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
||||||
|
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
||||||
|
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
||||||
|
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
||||||
|
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.r_mask.ca.eq(1),
|
||||||
|
src_c.eq(self.pfv.xer.r_data.ca),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, ADDEX):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.r_mask.ov.eq(1),
|
||||||
|
src_c.eq(self.pfv.xer.r_data.ov),
|
||||||
|
]
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Result : Operand A + Operand B + Operand C
|
||||||
|
|
||||||
|
src_a_zext = Signal(unsigned(65))
|
||||||
|
src_b_zext = Signal(unsigned(65))
|
||||||
|
result = Signal(unsigned(65))
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
src_a_zext.eq(src_a.as_unsigned()),
|
||||||
|
src_b_zext.eq(src_b.as_unsigned()),
|
||||||
|
result.eq(src_a_zext + src_b_zext + src_c),
|
||||||
|
|
||||||
|
ca_64.eq(result[64]),
|
||||||
|
ca_32.eq(result[32] ^ src_a[32] ^ src_b[32]),
|
||||||
|
ov_64.eq((ca_64 ^ result[63]) & ~(src_a[63] ^ src_b[63])),
|
||||||
|
ov_32.eq((ca_32 ^ result[31]) & ~(src_a[31] ^ src_b[31])),
|
||||||
|
|
||||||
|
self.pfv.rt.index .eq(self.insn.RT),
|
||||||
|
self.pfv.rt.w_stb .eq(1),
|
||||||
|
self.pfv.rt.w_data.eq(result[:64]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Read XER.SO (to update CR0)
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
ADD_ , ADDO_ , ADDIC_ ,
|
||||||
|
SUBF_ , SUBFO_ ,
|
||||||
|
ADDC_ , ADDCO_ , ADDE_ , ADDEO_ ,
|
||||||
|
SUBFC_ , SUBFCO_ , SUBFE_ , SUBFEO_ ,
|
||||||
|
ADDME_ , ADDMEO_ , ADDZE_ , ADDZEO_ ,
|
||||||
|
SUBFME_, SUBFMEO_, SUBFZE_, SUBFZEO_,
|
||||||
|
NEG_ , NEGO_ ,
|
||||||
|
)):
|
||||||
|
m.d.comb += self.pfv.xer.r_mask.so.eq(1)
|
||||||
|
|
||||||
|
# Write XER.SO, XER.OV and XER.OV32
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
ADDO , ADDO_ , SUBFO , SUBFO_ ,
|
||||||
|
ADDCO , ADDCO_ , SUBFCO , SUBFCO_ ,
|
||||||
|
ADDEO , ADDEO_ , SUBFEO , SUBFEO_ ,
|
||||||
|
ADDMEO, ADDMEO_, SUBFMEO, SUBFMEO_,
|
||||||
|
ADDZEO, ADDZEO_, SUBFZEO, SUBFZEO_,
|
||||||
|
NEGO , NEGO_ ,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.w_mask.ov .eq(1),
|
||||||
|
self.pfv.xer.w_data.ov .eq(Mux(self.pfv.msr.r_data.sf, ov_64, ov_32)),
|
||||||
|
self.pfv.xer.w_mask.ov32.eq(1),
|
||||||
|
self.pfv.xer.w_data.ov32.eq(ov_32),
|
||||||
|
]
|
||||||
|
with m.If(self.pfv.xer.w_data.ov):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.w_mask.so.eq(1),
|
||||||
|
self.pfv.xer.w_data.so.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, ADDEX):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.w_mask.ov .eq(1),
|
||||||
|
self.pfv.xer.w_data.ov .eq(Mux(self.pfv.msr.r_data.sf, ca_64, ca_32)),
|
||||||
|
self.pfv.xer.w_mask.ov32.eq(1),
|
||||||
|
self.pfv.xer.w_data.ov32.eq(ca_32),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Write XER.CA and XER.CA32
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
ADDIC , ADDIC_ , SUBFIC ,
|
||||||
|
ADDC , ADDC_ , ADDCO , ADDCO_ ,
|
||||||
|
SUBFC , SUBFC_ , SUBFCO , SUBFCO_ ,
|
||||||
|
ADDE , ADDE_ , ADDEO , ADDEO_ ,
|
||||||
|
SUBFE , SUBFE_ , SUBFEO , SUBFEO_ ,
|
||||||
|
ADDME , ADDME_ , ADDMEO , ADDMEO_ ,
|
||||||
|
SUBFME, SUBFME_, SUBFMEO, SUBFMEO_,
|
||||||
|
ADDZE , ADDZE_ , ADDZEO , ADDZEO_ ,
|
||||||
|
SUBFZE, SUBFZE_, SUBFZEO, SUBFZEO_,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.xer.w_mask.ca .eq(1),
|
||||||
|
self.pfv.xer.w_data.ca .eq(Mux(self.pfv.msr.r_data.sf, ca_64, ca_32)),
|
||||||
|
self.pfv.xer.w_mask.ca32.eq(1),
|
||||||
|
self.pfv.xer.w_data.ca32.eq(ca_32),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Write CR0
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
ADD_ , ADDO_ , ADDIC_ ,
|
||||||
|
SUBF_ , SUBFO_ ,
|
||||||
|
ADDC_ , ADDCO_ , ADDE_ , ADDEO_ ,
|
||||||
|
SUBFC_ , SUBFCO_ , SUBFE_ , SUBFEO_ ,
|
||||||
|
ADDME_ , ADDMEO_ , ADDZE_ , ADDZEO_ ,
|
||||||
|
SUBFME_, SUBFMEO_, SUBFZE_, SUBFZEO_,
|
||||||
|
NEG_ , NEGO_ ,
|
||||||
|
)):
|
||||||
|
cr0_w_mask = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])
|
||||||
|
cr0_w_data = Record([("so", 1), ("eq_", 1), ("gt", 1), ("lt", 1)])
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
cr0_w_mask .eq(0b1111),
|
||||||
|
cr0_w_data.so .eq(Mux(self.pfv.xer.w_mask.so, self.pfv.xer.w_data.so, self.pfv.xer.r_data.so)),
|
||||||
|
cr0_w_data.eq_.eq(~Mux(self.pfv.msr.r_data.sf, result[:64].any(), result[:32].any())),
|
||||||
|
cr0_w_data.gt .eq(~(cr0_w_data.lt | cr0_w_data.eq_)),
|
||||||
|
cr0_w_data.lt .eq(Mux(self.pfv.msr.r_data.sf, result[63], result[31])),
|
||||||
|
|
||||||
|
self.pfv.cr.w_mask.cr0.eq(cr0_w_mask),
|
||||||
|
self.pfv.cr.w_data.cr0.eq(cr0_w_data),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,171 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.insn.const import *
|
||||||
|
|
||||||
|
from . import InsnSpec
|
||||||
|
from .utils import iea
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["BranchSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class BranchSpec(InsnSpec, Elaboratable):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.stb .eq(1),
|
||||||
|
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
|
||||||
|
self.pfv.msr.r_mask.sf.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Raise an interrupt if the BO field is invalid.
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
BC , BCA , BCL , BCLA ,
|
||||||
|
BCLR , BCLRL , BCTAR, BCTARL,
|
||||||
|
BCCTR, BCCTRL,
|
||||||
|
)):
|
||||||
|
bo_valid_patterns = [
|
||||||
|
"001--",
|
||||||
|
"011--",
|
||||||
|
"1-1--",
|
||||||
|
]
|
||||||
|
# BO(2)=0 ("decrement and test CTR") is illegal for bcctr/bcctrl.
|
||||||
|
if not isinstance(self.insn, (BCCTR, BCCTRL)):
|
||||||
|
bo_valid_patterns += [
|
||||||
|
"0000-",
|
||||||
|
"0001-",
|
||||||
|
"0100-",
|
||||||
|
"0101-",
|
||||||
|
"1-00-",
|
||||||
|
"1-01-",
|
||||||
|
]
|
||||||
|
m.d.comb += self.pfv.intr.eq(~self.insn.BO.matches(*bo_valid_patterns))
|
||||||
|
|
||||||
|
else:
|
||||||
|
m.d.comb += self.pfv.intr.eq(0)
|
||||||
|
|
||||||
|
# Read MSR.SF
|
||||||
|
|
||||||
|
m.d.comb += self.pfv.msr.r_mask.sf.eq(1)
|
||||||
|
|
||||||
|
# Is this branch taken ?
|
||||||
|
|
||||||
|
taken = Signal()
|
||||||
|
|
||||||
|
cond_bit = Signal()
|
||||||
|
cond_ok = Signal()
|
||||||
|
ctr_any = Signal()
|
||||||
|
ctr_ok = Signal()
|
||||||
|
|
||||||
|
if isinstance(self.insn, (B, BA, BL, BLA)):
|
||||||
|
m.d.comb += taken.eq(1)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
BC , BCA , BCL , BCLA ,
|
||||||
|
BCLR , BCLRL , BCTAR, BCTARL,
|
||||||
|
BCCTR, BCCTRL,
|
||||||
|
)):
|
||||||
|
|
||||||
|
# If BO(0) = 0, test CR(BI)
|
||||||
|
with m.If(self.insn.BO[4 - 0]):
|
||||||
|
m.d.comb += cond_ok.eq(1)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.cr.r_mask[::-1].bit_select(self.insn.BI, width=1).eq(1),
|
||||||
|
|
||||||
|
cond_bit.eq(self.pfv.cr.r_data[::-1].bit_select(self.insn.BI, width=1)),
|
||||||
|
cond_ok .eq(cond_bit == self.insn.BO[4 - 1]),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, (BCCTR, BCCTRL)):
|
||||||
|
m.d.comb += taken.eq(cond_ok)
|
||||||
|
else:
|
||||||
|
# If BO(2) = 0, decrement CTR then test its value.
|
||||||
|
with m.If(self.insn.BO[4 - 2]):
|
||||||
|
m.d.comb += ctr_ok.eq(1)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ctr.r_mask.eq(Repl(1, 64)),
|
||||||
|
self.pfv.ctr.w_mask.eq(Repl(1, 64)),
|
||||||
|
self.pfv.ctr.w_data.eq(self.pfv.ctr.r_data - 1),
|
||||||
|
|
||||||
|
ctr_any.eq(iea(self.pfv.ctr.w_data, self.pfv.msr.r_data.sf).any()),
|
||||||
|
ctr_ok .eq(ctr_any ^ self.insn.BO[4 - 3]),
|
||||||
|
]
|
||||||
|
m.d.comb += taken.eq(cond_ok & ctr_ok)
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Compute the target address
|
||||||
|
|
||||||
|
target = Signal(unsigned(64))
|
||||||
|
base = Signal(unsigned(64))
|
||||||
|
offset = Signal( signed(62))
|
||||||
|
|
||||||
|
# base : CIA if AA=0, 0 otherwise
|
||||||
|
|
||||||
|
if isinstance(self.insn, (B, BL, BC, BCL)):
|
||||||
|
m.d.comb += base.eq(self.pfv.cia)
|
||||||
|
elif isinstance(self.insn, (
|
||||||
|
BA , BLA , BCA , BCLA ,
|
||||||
|
BCLR, BCLRL, BCCTR, BCCTRL, BCTAR, BCTARL,
|
||||||
|
)):
|
||||||
|
m.d.comb += base.eq(0)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# offset : LI or BD or LR>>2 or CTR>>2 or TAR>>2
|
||||||
|
|
||||||
|
if isinstance(self.insn, (B, BA, BL, BLA)):
|
||||||
|
m.d.comb += offset.eq(self.insn.LI)
|
||||||
|
elif isinstance(self.insn, (BC, BCA, BCL, BCLA)):
|
||||||
|
m.d.comb += offset.eq(self.insn.BD)
|
||||||
|
elif isinstance(self.insn, (BCLR, BCLRL)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.lr.r_mask[2:].eq(Repl(1, 62)),
|
||||||
|
offset.eq(self.pfv.lr.r_data[2:]),
|
||||||
|
]
|
||||||
|
elif isinstance(self.insn, (BCCTR, BCCTRL)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ctr.r_mask[2:].eq(Repl(1, 62)),
|
||||||
|
offset.eq(self.pfv.ctr.r_data[2:]),
|
||||||
|
]
|
||||||
|
elif isinstance(self.insn, (BCTAR, BCTARL)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.tar.r_mask[2:].eq(Repl(1, 62)),
|
||||||
|
offset.eq(self.pfv.tar.r_data[2:]),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# target : base + offset<<2
|
||||||
|
|
||||||
|
m.d.comb += target.eq(base + Cat(Const(0, 2), offset))
|
||||||
|
|
||||||
|
# Update NIA
|
||||||
|
|
||||||
|
with m.If(taken):
|
||||||
|
m.d.comb += self.pfv.nia.eq(iea(target, self.pfv.msr.r_data.sf))
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += self.pfv.nia.eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf))
|
||||||
|
|
||||||
|
# Write the return address to LR if LK=1
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
BL , BLA , BCL , BCLA,
|
||||||
|
BCLRL, BCCTRL, BCTARL,
|
||||||
|
)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.lr.w_mask.eq(Repl(1, 64)),
|
||||||
|
self.pfv.lr.w_data.eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,104 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.insn.const import *
|
||||||
|
|
||||||
|
from . import InsnSpec
|
||||||
|
from .utils import iea
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CompareSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class CompareSpec(InsnSpec, Elaboratable):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.stb .eq(1),
|
||||||
|
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
|
||||||
|
self.pfv.intr.eq(0),
|
||||||
|
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
|
||||||
|
self.pfv.msr.r_mask.sf.eq(1),
|
||||||
|
self.pfv.xer.r_mask.so.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
src_a = Signal(64)
|
||||||
|
src_b = Signal(64)
|
||||||
|
result = Record([
|
||||||
|
("lt", 1),
|
||||||
|
("gt", 1),
|
||||||
|
("eq_", 1),
|
||||||
|
("so", 1),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Operand A : (RA) or [(RA)(32:63) sign-extended or zero-extended]
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.ra.index.eq(self.insn.RA),
|
||||||
|
self.pfv.ra.r_stb.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, (CMPI, CMP)):
|
||||||
|
with m.If(self.insn.L):
|
||||||
|
m.d.comb += src_a.eq(self.pfv.ra.r_data)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += src_a.eq(self.pfv.ra.r_data[:32].as_signed())
|
||||||
|
elif isinstance(self.insn, (CMPL, CMPLI)):
|
||||||
|
with m.If(self.insn.L):
|
||||||
|
m.d.comb += src_a.eq(self.pfv.ra.r_data)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += src_a.eq(self.pfv.ra.r_data[:32].as_unsigned())
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Operand B : SI or UI or (RB) or [(RB)(32:63) sign-extended or zero-extended]
|
||||||
|
|
||||||
|
if isinstance(self.insn, (CMP, CMPL)):
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.rb.index.eq(self.insn.RB),
|
||||||
|
self.pfv.rb.r_stb.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, CMPI):
|
||||||
|
m.d.comb += src_b.eq(self.insn.SI)
|
||||||
|
elif isinstance(self.insn, CMPLI):
|
||||||
|
m.d.comb += src_b.eq(self.insn.UI)
|
||||||
|
elif isinstance(self.insn, CMP):
|
||||||
|
with m.If(self.insn.L):
|
||||||
|
m.d.comb += src_b.eq(self.pfv.rb.r_data)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += src_b.eq(self.pfv.rb.r_data[:32].as_signed())
|
||||||
|
elif isinstance(self.insn, CMPL):
|
||||||
|
with m.If(self.insn.L):
|
||||||
|
m.d.comb += src_b.eq(self.pfv.rb.r_data)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += src_b.eq(self.pfv.rb.r_data[:32].as_unsigned())
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
# Result
|
||||||
|
|
||||||
|
if isinstance(self.insn, (CMPI, CMP)):
|
||||||
|
m.d.comb += result.lt.eq(src_a.as_signed() < src_b.as_signed())
|
||||||
|
elif isinstance(self.insn, (CMPLI, CMPL)):
|
||||||
|
m.d.comb += result.lt.eq(src_a.as_unsigned() < src_b.as_unsigned())
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
result.gt .eq(~(result.lt | result.eq_)),
|
||||||
|
result.eq_.eq(src_a == src_b),
|
||||||
|
result.so .eq(self.pfv.xer.r_data.so),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.cr.w_mask[::-1].word_select(self.insn.BF, width=4).eq(0b1111),
|
||||||
|
self.pfv.cr.w_data[::-1].word_select(self.insn.BF, width=4).eq(result),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,81 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.insn.const import *
|
||||||
|
|
||||||
|
from . import InsnSpec
|
||||||
|
from .utils import iea
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CRSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class CRSpec(InsnSpec, Elaboratable):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.stb .eq(1),
|
||||||
|
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
|
||||||
|
self.pfv.intr.eq(0),
|
||||||
|
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
|
||||||
|
self.pfv.msr.r_mask.sf.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, (
|
||||||
|
CRAND, CROR , CRNAND, CRXOR,
|
||||||
|
CRNOR, CRANDC, CREQV , CRORC,
|
||||||
|
)):
|
||||||
|
src_a = Signal()
|
||||||
|
src_b = Signal()
|
||||||
|
result = Signal()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.cr.r_mask[::-1].bit_select(self.insn.BA, width=1).eq(1),
|
||||||
|
self.pfv.cr.r_mask[::-1].bit_select(self.insn.BB, width=1).eq(1),
|
||||||
|
|
||||||
|
src_a.eq(self.pfv.cr.r_data[::-1].bit_select(self.insn.BA, width=1)),
|
||||||
|
src_b.eq(self.pfv.cr.r_data[::-1].bit_select(self.insn.BB, width=1)),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, CRAND):
|
||||||
|
m.d.comb += result.eq(src_a & src_b)
|
||||||
|
if isinstance(self.insn, CROR):
|
||||||
|
m.d.comb += result.eq(src_a | src_b)
|
||||||
|
if isinstance(self.insn, CRNAND):
|
||||||
|
m.d.comb += result.eq(~(src_a & src_b))
|
||||||
|
if isinstance(self.insn, CRXOR):
|
||||||
|
m.d.comb += result.eq(src_a ^ src_b)
|
||||||
|
if isinstance(self.insn, CRNOR):
|
||||||
|
m.d.comb += result.eq(~(src_a | src_b))
|
||||||
|
if isinstance(self.insn, CRANDC):
|
||||||
|
m.d.comb += result.eq(src_a & ~src_b)
|
||||||
|
if isinstance(self.insn, CREQV):
|
||||||
|
m.d.comb += result.eq(src_a == src_b)
|
||||||
|
if isinstance(self.insn, CRORC):
|
||||||
|
m.d.comb += result.eq(src_a | ~src_b)
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.cr.w_mask[::-1].bit_select(self.insn.BT, width=1).eq(1),
|
||||||
|
self.pfv.cr.w_data[::-1].bit_select(self.insn.BT, width=1).eq(result),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif isinstance(self.insn, MCRF):
|
||||||
|
field = Signal(4)
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
field.eq(self.pfv.cr.r_data[::-1].word_select(self.insn.BFA, width=4)),
|
||||||
|
|
||||||
|
self.pfv.cr.r_mask[::-1].word_select(self.insn.BFA, width=4).eq(0b1111),
|
||||||
|
self.pfv.cr.w_mask[::-1].word_select(self.insn.BF, width=4).eq(0b1111),
|
||||||
|
self.pfv.cr.w_data[::-1].word_select(self.insn.BF, width=4).eq(field),
|
||||||
|
]
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,96 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
from power_fv import pfv
|
||||||
|
from power_fv.insn.const import *
|
||||||
|
from power_fv.reg import xer_layout
|
||||||
|
|
||||||
|
from . import InsnSpec
|
||||||
|
from .utils import iea
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["SPRMoveSpec"]
|
||||||
|
|
||||||
|
|
||||||
|
class SPRMoveSpec(InsnSpec, Elaboratable):
|
||||||
|
def __init__(self, insn):
|
||||||
|
self.pfv = pfv.Interface()
|
||||||
|
self.insn = insn
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.stb .eq(1),
|
||||||
|
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
|
||||||
|
self.pfv.nia .eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)),
|
||||||
|
self.pfv.msr.r_mask.sf.eq(1),
|
||||||
|
self.pfv.msr.r_mask.pr.eq(1),
|
||||||
|
]
|
||||||
|
|
||||||
|
# If SPR(0) = 1, this instruction is privileged.
|
||||||
|
|
||||||
|
spr_privileged = Signal()
|
||||||
|
spr_access_err = Signal()
|
||||||
|
|
||||||
|
m.d.comb += [
|
||||||
|
spr_privileged.eq(self.insn.SPR[9 - 0]),
|
||||||
|
spr_access_err.eq(spr_privileged & self.pfv.msr.r_data.pr),
|
||||||
|
self.pfv.intr.eq(spr_access_err),
|
||||||
|
]
|
||||||
|
|
||||||
|
def mXspr_spec(pfv_spr, mtspr_cls, mfspr_cls, reserved_mask):
|
||||||
|
if isinstance(self.insn, mtspr_cls):
|
||||||
|
# Copy (RS) to SPR.
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.rs.index.eq(self.insn.RS),
|
||||||
|
self.pfv.rs.r_stb.eq(1),
|
||||||
|
pfv_spr.w_mask.eq(~reserved_mask),
|
||||||
|
pfv_spr.w_data.eq(self.pfv.rs.r_data & ~reserved_mask),
|
||||||
|
]
|
||||||
|
|
||||||
|
if isinstance(self.insn, mfspr_cls):
|
||||||
|
# Copy SPR to (RT).
|
||||||
|
m.d.comb += [
|
||||||
|
self.pfv.rt.index.eq(self.insn.RT),
|
||||||
|
self.pfv.rt.w_stb.eq(1),
|
||||||
|
pfv_spr.r_mask.eq(~reserved_mask),
|
||||||
|
]
|
||||||
|
# In problem state, reading reserved bits returns 0.
|
||||||
|
with m.If(self.pfv.msr.r_data.pr):
|
||||||
|
m.d.comb += self.pfv.rt.w_data.eq(pfv_spr.r_data & ~reserved_mask)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += self.pfv.rt.w_data.eq(pfv_spr.r_data)
|
||||||
|
|
||||||
|
if isinstance(self.insn, (MTXER, MFXER)):
|
||||||
|
xer_reserved_mask = Record(xer_layout)
|
||||||
|
m.d.comb += [
|
||||||
|
xer_reserved_mask._56.eq(Repl(1, 1)),
|
||||||
|
xer_reserved_mask._46.eq(Repl(1, 2)),
|
||||||
|
xer_reserved_mask._35.eq(Repl(1, 9)),
|
||||||
|
xer_reserved_mask._0 .eq(Repl(1, 32)),
|
||||||
|
]
|
||||||
|
mXspr_spec(self.pfv.xer, MTXER, MFXER, xer_reserved_mask)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (MTLR, MFLR)):
|
||||||
|
mXspr_spec(self.pfv.lr, MTLR, MFLR, Const(0, 64))
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (MTCTR, MFCTR)):
|
||||||
|
mXspr_spec(self.pfv.ctr, MTCTR, MFCTR, Const(0, 64))
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (MTSRR0, MFSRR0)):
|
||||||
|
mXspr_spec(self.pfv.srr0, MTSRR0, MFSRR0, Const(0, 64))
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (MTSRR1, MFSRR1)):
|
||||||
|
# SRR1 bits should be treated as reserved if their corresponding MSR bits are also
|
||||||
|
# reserved; which is implementation-specific.
|
||||||
|
# We treat all bits as defined for now, but this may cause false positives.
|
||||||
|
srr1_reserved_mask = Const(0, 64)
|
||||||
|
mXspr_spec(self.pfv.srr1, MTSRR1, MFSRR1, srr1_reserved_mask)
|
||||||
|
|
||||||
|
elif isinstance(self.insn, (MTTAR, MFTAR)):
|
||||||
|
mXspr_spec(self.pfv.tar, MTTAR, MFTAR, Const(0, 64))
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
return m
|
@ -0,0 +1,76 @@
|
|||||||
|
from amaranth import *
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"_ea_layout",
|
||||||
|
"cr_layout", "msr_layout",
|
||||||
|
"lr_layout", "ctr_layout", "tar_layout",
|
||||||
|
"xer_layout",
|
||||||
|
"srr0_layout", "srr1_layout",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# used for registers that hold an effective address
|
||||||
|
_ea_layout = [
|
||||||
|
("_62", unsigned( 2)),
|
||||||
|
("ea" , unsigned(62)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cr_layout = [
|
||||||
|
(f"cr{i}", unsigned(4)) for i in reversed(range(8))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
msr_layout = [
|
||||||
|
("le" , unsigned( 1)),
|
||||||
|
("ri" , unsigned( 1)),
|
||||||
|
("pmm", unsigned( 1)),
|
||||||
|
("_60", unsigned( 1)),
|
||||||
|
("dr" , unsigned( 1)),
|
||||||
|
("ir" , unsigned( 1)),
|
||||||
|
("_56", unsigned( 2)),
|
||||||
|
("fe1", unsigned( 1)),
|
||||||
|
("te" , unsigned( 2)),
|
||||||
|
("fe0", unsigned( 1)),
|
||||||
|
("me" , unsigned( 1)),
|
||||||
|
("fp" , unsigned( 1)),
|
||||||
|
("pr" , unsigned( 1)),
|
||||||
|
("ee" , unsigned( 1)),
|
||||||
|
("_42", unsigned( 6)),
|
||||||
|
("s" , unsigned( 1)),
|
||||||
|
("vsx", unsigned( 1)),
|
||||||
|
("_39", unsigned( 1)),
|
||||||
|
("vec", unsigned( 1)),
|
||||||
|
("_32", unsigned( 6)),
|
||||||
|
("_6" , unsigned(26)),
|
||||||
|
("_5" , unsigned( 1)),
|
||||||
|
("_4" , unsigned( 1)),
|
||||||
|
("hv" , unsigned( 1)),
|
||||||
|
("_1" , unsigned( 2)),
|
||||||
|
("sf" , unsigned( 1)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
lr_layout = [("lr" , unsigned(64))]
|
||||||
|
ctr_layout = [("ctr", unsigned(64))]
|
||||||
|
tar_layout = _ea_layout
|
||||||
|
|
||||||
|
|
||||||
|
xer_layout = [
|
||||||
|
("_57" , unsigned( 7)), # size of Load/Store String Indexed transfers
|
||||||
|
("_56" , unsigned( 1)), # reserved
|
||||||
|
("_48" , unsigned( 8)), # reserved, but software can r/w it
|
||||||
|
("_46" , unsigned( 2)), # reserved
|
||||||
|
("ca32", unsigned( 1)),
|
||||||
|
("ov32", unsigned( 1)),
|
||||||
|
("_35" , unsigned( 9)), # reserved
|
||||||
|
("ca" , unsigned( 1)),
|
||||||
|
("ov" , unsigned( 1)),
|
||||||
|
("so" , unsigned( 1)),
|
||||||
|
("_0" , unsigned(32)), # reserved
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
srr0_layout = _ea_layout
|
||||||
|
srr1_layout = msr_layout
|
@ -0,0 +1,169 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import multiprocessing
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from power_fv.build import sby
|
||||||
|
from power_fv.core import PowerFVCore
|
||||||
|
from power_fv.check import PowerFVCheck
|
||||||
|
from power_fv.check.all import *
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PowerFVSession"]
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFVCommandExit(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PowerFVCommandError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _ArgumentParser(argparse.ArgumentParser):
|
||||||
|
def exit(self, status=0, message=None):
|
||||||
|
raise PowerFVCommandExit()
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
raise PowerFVCommandError()
|
||||||
|
|
||||||
|
|
||||||
|
class PowerFVSession:
|
||||||
|
def __init_subclass__(cls, *, core_cls, **kwargs):
|
||||||
|
super().__init_subclass__(**kwargs)
|
||||||
|
if not issubclass(core_cls, PowerFVCore):
|
||||||
|
raise TypeError("Core class must be a subclass of PowerFVCore, not {!r}"
|
||||||
|
.format(core_cls))
|
||||||
|
cls.core_cls = core_cls
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.parser = _ArgumentParser(add_help=False)
|
||||||
|
self.subparsers = self.parser.add_subparsers(help="commands")
|
||||||
|
self.namespace = dict()
|
||||||
|
|
||||||
|
self.add_help_subparser()
|
||||||
|
self.add_check_subparser()
|
||||||
|
self.add_dump_subparser()
|
||||||
|
self.add_expand_subparser()
|
||||||
|
self.add_build_subparser()
|
||||||
|
self.add_exit_subparser()
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
group = parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument(
|
||||||
|
"-i", dest="interact", action="store_true",
|
||||||
|
help="run commands interactively")
|
||||||
|
group.add_argument(
|
||||||
|
"-c", dest="cmdfile", type=argparse.FileType("r"), default=None,
|
||||||
|
help="run commands from CMDFILE")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
self._loop(args)
|
||||||
|
|
||||||
|
def _loop(self, args):
|
||||||
|
if args.cmdfile is None:
|
||||||
|
_readline = lambda: input("powerfv> ")
|
||||||
|
else:
|
||||||
|
_readline = args.cmdfile.readline
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = _readline().rstrip("\n")
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
self._eval(line.split())
|
||||||
|
|
||||||
|
def _eval(self, args=None):
|
||||||
|
try:
|
||||||
|
args = self.parser.parse_args(args)
|
||||||
|
assert hasattr(args, "_cmd")
|
||||||
|
cmd = args._cmd
|
||||||
|
del args._cmd
|
||||||
|
cmd(**vars(args))
|
||||||
|
except PowerFVCommandExit:
|
||||||
|
pass
|
||||||
|
except PowerFVCommandError:
|
||||||
|
self.help()
|
||||||
|
|
||||||
|
# Subparsers
|
||||||
|
|
||||||
|
def add_help_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("help", help="show this help message")
|
||||||
|
parser.set_defaults(_cmd=self.help)
|
||||||
|
|
||||||
|
def add_check_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("check", help="add checks to this session")
|
||||||
|
parser.set_defaults(_cmd=self.check)
|
||||||
|
|
||||||
|
PowerFVCheck .add_check_arguments(parser)
|
||||||
|
self.core_cls.add_check_arguments(parser)
|
||||||
|
|
||||||
|
def add_dump_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("dump", help="inspect check parameters")
|
||||||
|
parser.set_defaults(_cmd=self.dump)
|
||||||
|
|
||||||
|
def add_expand_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("expand", help="expand check parameters")
|
||||||
|
parser.set_defaults(_cmd=self.expand)
|
||||||
|
|
||||||
|
def add_build_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("build", help="execute the build plan")
|
||||||
|
parser.set_defaults(_cmd=self.build)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-j", "--jobs", type=int, default=os.cpu_count(),
|
||||||
|
help="number of worker processes (default: %(default)s)")
|
||||||
|
|
||||||
|
PowerFVCheck .add_build_arguments(parser)
|
||||||
|
self.core_cls.add_build_arguments(parser)
|
||||||
|
|
||||||
|
def add_exit_subparser(self):
|
||||||
|
parser = self.subparsers.add_parser("exit", help="exit")
|
||||||
|
parser.set_defaults(_cmd=self.exit)
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
|
||||||
|
def help(self, **kwargs):
|
||||||
|
self.parser.print_help()
|
||||||
|
|
||||||
|
def check(self, *, name, **kwargs):
|
||||||
|
self.namespace[name] = dict(**kwargs)
|
||||||
|
|
||||||
|
def dump(self, **kwargs):
|
||||||
|
pprint(self.namespace, sort_dicts=False)
|
||||||
|
|
||||||
|
def expand(self, **kwargs):
|
||||||
|
new_namespace = dict()
|
||||||
|
|
||||||
|
for check_name, check_args in self.namespace.items():
|
||||||
|
matches = list(PowerFVCheck.find(*check_name.split(":")))
|
||||||
|
if not matches:
|
||||||
|
raise NameError("Unknown check {!r}".format(check_name))
|
||||||
|
for match_name, match_cls in matches:
|
||||||
|
new_namespace[":".join(match_name)] = check_args
|
||||||
|
|
||||||
|
self.namespace = new_namespace
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_check(core_cls, check_name, check_args, build_args):
|
||||||
|
check_cls = PowerFVCheck.all_checks[tuple(check_name.split(":"))]
|
||||||
|
core = core_cls()
|
||||||
|
check = check_cls(core=core, **check_args)
|
||||||
|
check.build(**build_args)
|
||||||
|
|
||||||
|
def build(self, *, jobs, **kwargs):
|
||||||
|
self.expand()
|
||||||
|
|
||||||
|
map_func = PowerFVSession._build_check
|
||||||
|
map_args = []
|
||||||
|
|
||||||
|
for check_name, check_args in self.namespace.items():
|
||||||
|
map_args.append((self.core_cls, check_name, check_args, kwargs))
|
||||||
|
|
||||||
|
with multiprocessing.Pool(jobs) as pool:
|
||||||
|
pool.starmap(map_func, map_args)
|
||||||
|
|
||||||
|
def exit(self, **kwargs):
|
||||||
|
print("exiting")
|
||||||
|
sys.exit()
|
@ -1,72 +0,0 @@
|
|||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from amaranth import *
|
|
||||||
from amaranth.asserts import *
|
|
||||||
from amaranth.utils import bits_for
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Trigger", "Testbench"]
|
|
||||||
|
|
||||||
|
|
||||||
class Trigger:
|
|
||||||
def __init__(self, cycle, name=None, src_loc_at=0):
|
|
||||||
if not isinstance(cycle, int) or cycle <= 0:
|
|
||||||
raise ValueError("Clock cycle must be a positive integer, not {!r}"
|
|
||||||
.format(cycle))
|
|
||||||
|
|
||||||
self.stb = Signal(name=name, src_loc_at=1 + src_loc_at)
|
|
||||||
self.cycle = cycle
|
|
||||||
|
|
||||||
|
|
||||||
class Testbench(Elaboratable):
|
|
||||||
def __init__(self, spec, dut):
|
|
||||||
self.spec = spec
|
|
||||||
self.dut = dut
|
|
||||||
|
|
||||||
self._triggers = OrderedDict()
|
|
||||||
self._bmc_depth = None
|
|
||||||
self._frozen = False
|
|
||||||
|
|
||||||
for trigger in spec.triggers():
|
|
||||||
self.add_trigger(trigger)
|
|
||||||
|
|
||||||
def freeze(self):
|
|
||||||
if not self._frozen:
|
|
||||||
self._bmc_depth = max(t.cycle for t in self.triggers()) + 1
|
|
||||||
self._frozen = True
|
|
||||||
|
|
||||||
def add_trigger(self, trigger):
|
|
||||||
if self._frozen:
|
|
||||||
raise ValueError("Testbench is frozen.")
|
|
||||||
if not isinstance(trigger, Trigger):
|
|
||||||
raise TypeError("Trigger must be an instance of Trigger, not {!r}"
|
|
||||||
.format(trig))
|
|
||||||
self._triggers[id(trigger)] = trigger
|
|
||||||
|
|
||||||
def triggers(self):
|
|
||||||
yield from self._triggers.values()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bmc_depth(self):
|
|
||||||
self.freeze()
|
|
||||||
return self._bmc_depth
|
|
||||||
|
|
||||||
def elaborate(self, platform):
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
cycle = Signal(range(self.bmc_depth), reset=1)
|
|
||||||
|
|
||||||
with m.If(cycle < self.bmc_depth - 1):
|
|
||||||
m.d.sync += cycle.eq(cycle + 1)
|
|
||||||
|
|
||||||
for trigger in self.triggers():
|
|
||||||
m.d.comb += trigger.stb.eq(cycle == trigger.cycle)
|
|
||||||
|
|
||||||
m.submodules.spec = self.spec
|
|
||||||
m.submodules.dut = self.dut
|
|
||||||
|
|
||||||
m.d.comb += self.spec.pfv.eq(self.dut.pfv)
|
|
||||||
|
|
||||||
m.d.comb += Assume(ResetSignal() == Initial())
|
|
||||||
|
|
||||||
return m
|
|
Loading…
Reference in New Issue