You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

365 lines
14 KiB
Python

import os
import pathlib
import textwrap
from amaranth import *
from amaranth.asserts import *
from amaranth_soc import wishbone
from power_fv import pfv
from power_fv.core import PowerFVCore
__all__ = ["MicrowattWrapper", "MicrowattCore"]
class MicrowattWrapper(Elaboratable):
@classmethod
def add_check_arguments(cls, parser):
group = parser.add_argument_group(title="microwatt options")
group.add_argument(
"--bus-fairness", action="store_true",
help="add bus fairness constraints")
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,
HAS_ALTOPS => 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, *, bus_fairness=False, **kwargs):
self.pfv = pfv.Interface(mem_alignment=0, illegal_insn_heai=False, muldiv_altops=True)
self.wb_insn = wishbone.Interface(addr_width=29, data_width=64, granularity=8,
features=("stall",))
self.wb_data = wishbone.Interface(addr_width=29, data_width=64, granularity=8,
features=("stall",))
def keep_wb_fanout(wb_bus):
for field_name in ("adr", "dat_w", "sel", "cyc", "stb", "we"):
wb_bus[field_name].attrs["keep"] = True
keep_wb_fanout(self.wb_insn)
keep_wb_fanout(self.wb_data)
self.bus_fairness = bus_fairness;
self._toplevel_src = self.MICROWATT_TOPLEVEL.format(**kwargs)
def elaborate(self, platform):
m = Module()
wb_snoop = wishbone.Interface(addr_width=29, data_width=64, granularity=8)
dmi = Record([
("addr", unsigned( 4)),
("din", unsigned(64)),
("dout", unsigned(64)),
("req", unsigned( 1)),
("wr", unsigned( 1)),
("ack", unsigned( 1)),
])
terminated = Signal(attrs={"keep": True})
dmi.dout.attrs["keep"] = True
dmi.ack .attrs["keep"] = True
m.d.comb += [
self.wb_insn.dat_r.eq(AnySeq(64)),
self.wb_insn.ack .eq(AnySeq( 1)),
self.wb_insn.stall.eq(AnySeq( 1)),
self.wb_data.dat_r.eq(AnySeq(64)),
self.wb_data.ack .eq(AnySeq( 1)),
self.wb_data.stall.eq(AnySeq( 1)),
wb_snoop.adr .eq(AnySeq(29)),
wb_snoop.dat_w.eq(AnySeq(64)),
wb_snoop.sel .eq(AnySeq( 8)),
wb_snoop.cyc .eq(AnySeq( 1)),
wb_snoop.stb .eq(AnySeq( 1)),
wb_snoop.we .eq(AnySeq( 1)),
dmi.addr.eq(AnySeq( 4)),
dmi.din .eq(AnySeq(64)),
dmi.req .eq(AnySeq( 1)),
dmi.wr .eq(AnySeq( 1)),
]
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" , self.wb_insn.dat_r),
("i", "wishbone_insn_in.ack" , self.wb_insn.ack ),
("i", "wishbone_insn_in.stall", self.wb_insn.stall),
("o", "wishbone_insn_out.adr" , self.wb_insn.adr ),
("o", "wishbone_insn_out.dat" , self.wb_insn.dat_w),
("o", "wishbone_insn_out.sel" , self.wb_insn.sel ),
("o", "wishbone_insn_out.cyc" , self.wb_insn.cyc ),
("o", "wishbone_insn_out.stb" , self.wb_insn.stb ),
("o", "wishbone_insn_out.we" , self.wb_insn.we ),
("i", "wishbone_data_in.dat" , self.wb_data.dat_r),
("i", "wishbone_data_in.ack" , self.wb_data.ack ),
("i", "wishbone_data_in.stall", self.wb_data.stall),
("o", "wishbone_data_out.adr" , self.wb_data.adr ),
("o", "wishbone_data_out.dat" , self.wb_data.dat_w),
("o", "wishbone_data_out.sel" , self.wb_data.sel ),
("o", "wishbone_data_out.cyc" , self.wb_data.cyc ),
("o", "wishbone_data_out.stb" , self.wb_data.stb ),
("o", "wishbone_data_out.we" , self.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.skip" , self.pfv.skip ),
("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.mem" , self.pfv.mem ),
("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),
]
with m.If(self.pfv.stb):
m.d.comb += [
# no decrementer interrupts
Assume(self.pfv.msr.w_mask.ee.implies(~self.pfv.msr.w_data.ee)),
# no trace interrupts
Assume(self.pfv.msr.w_mask.te[0].implies(~self.pfv.msr.w_data.te[0])),
Assume(self.pfv.msr.w_mask.te[1].implies(~self.pfv.msr.w_data.te[1])),
]
if self.bus_fairness:
ibus_wait = Signal(2)
dbus_wait = Signal(2)
with m.If(self.wb_insn.cyc & self.wb_insn.stb & ~self.wb_insn.ack):
m.d.comb += Assume(self.wb_insn.stall)
m.d.sync += ibus_wait.eq(ibus_wait + 1)
with m.Else():
m.d.sync += ibus_wait.eq(0)
with m.If(self.wb_data.cyc & self.wb_data.stb & ~self.wb_data.ack):
m.d.comb += Assume(self.wb_data.stall)
m.d.sync += dbus_wait.eq(dbus_wait + 1)
with m.Else():
m.d.sync += dbus_wait.eq(0)
m.d.comb += [
Assume((ibus_wait < 2) & (dbus_wait < 2)),
]
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("./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._toplevel_src
platform.add_file(top_filename, top_contents)