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
365 lines
14 KiB
Python
2 years ago
|
import os
|
||
|
import pathlib
|
||
|
import textwrap
|
||
|
|
||
|
from amaranth import *
|
||
|
from amaranth.asserts import *
|
||
2 years ago
|
from amaranth_soc import wishbone
|
||
2 years ago
|
|
||
|
from power_fv import pfv
|
||
|
from power_fv.core import PowerFVCore
|
||
|
|
||
|
|
||
2 years ago
|
__all__ = ["MicrowattWrapper", "MicrowattCore"]
|
||
2 years ago
|
|
||
|
|
||
|
class MicrowattWrapper(Elaboratable):
|
||
|
@classmethod
|
||
|
def add_check_arguments(cls, parser):
|
||
|
group = parser.add_argument_group(title="microwatt options")
|
||
2 years ago
|
group.add_argument(
|
||
|
"--bus-fairness", action="store_true",
|
||
|
help="add bus fairness constraints")
|
||
2 years ago
|
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,
|
||
2 years ago
|
HAS_ALTOPS => true,
|
||
2 years ago
|
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;
|
||
|
""")
|
||
|
|
||
2 years ago
|
def __init__(self, *, bus_fairness=False, **kwargs):
|
||
|
self.pfv = pfv.Interface(mem_aligned=False, illegal_insn_heai=False, muldiv_altops=True)
|
||
2 years ago
|
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",))
|
||
2 years ago
|
|
||
2 years ago
|
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)
|
||
|
|
||
2 years ago
|
self.bus_fairness = bus_fairness;
|
||
2 years ago
|
self._toplevel_src = self.MICROWATT_TOPLEVEL.format(**kwargs)
|
||
2 years ago
|
|
||
|
def elaborate(self, platform):
|
||
|
m = Module()
|
||
|
|
||
2 years ago
|
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)),
|
||
|
]
|
||
2 years ago
|
|
||
|
m.submodules.dut = Instance("toplevel",
|
||
|
("i", "clk", ClockSignal()),
|
||
|
("i", "rst", ResetSignal()),
|
||
|
("i", "alt_reset", Const(0)),
|
||
|
("i", "ext_irq", Const(0)),
|
||
|
|
||
2 years ago
|
("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 ),
|
||
2 years ago
|
|
||
|
("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 ),
|
||
2 years ago
|
("o", "pfv_out.skip" , self.pfv.skip ),
|
||
2 years ago
|
("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 ),
|
||
2 years ago
|
("o", "pfv_out.mem" , self.pfv.mem ),
|
||
2 years ago
|
("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 += [
|
||
2 years ago
|
Assume(~dmi.req),
|
||
2 years ago
|
Assume(~terminated),
|
||
|
]
|
||
2 years ago
|
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])),
|
||
|
]
|
||
2 years ago
|
|
||
2 years ago
|
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)),
|
||
|
]
|
||
|
|
||
2 years ago
|
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(
|
||
2 years ago
|
"--src-dir", type=pathlib.Path, default=pathlib.Path("./src"),
|
||
2 years ago
|
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"
|
||
2 years ago
|
top_contents = wrapper._toplevel_src
|
||
2 years ago
|
platform.add_file(top_filename, top_contents)
|