
import os

from migen import *

from litex import get_data_mod
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import *
from litex.soc.cores.cpu import CPU

dir = os.path.dirname(os.path.realpath(__file__))

# these select the top RTL file for each variant name
CPU_VARIANTS = {
    'AXI': 'A2P_AXI',
    'WB':  'A2P_WB',
    'standard': 'A2P_WB'   #wtf litex does this as default
}

GCC_FLAGS = {
   'WB' : '-fomit-frame-pointer -Wall -fno-builtin -nostdinc -fno-stack-protector -fexceptions -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes'
}

class A2P(CPU, AutoCSR):
    name                 = 'a2p'
    human_name           = 'a2p'
    family               = 'ppc32'
    variants             = CPU_VARIANTS
    data_width           = 32
    endianness           = 'big'
    gcc_triple           = 'powerpc-linux-gnu'
    linker_output_format = 'elf32-powerpc'
    nop                  = 'nop'
    io_regions           = {0x80000000: 0x80000000} # origin, length

    @property
    def mem_map(self):
        return {
            'rom':          0x00000000,
            'sram':         0x00004000,
            'main_ram':     0x40000000,
            'csr':          0xf0000000,
        }

    @property
    def gcc_flags(self):
        flags = GCC_FLAGS[self.variant]
        flags += " -D__a2p__"
        return flags

    def __init__(self, platform, variant='WB'):

         if variant == 'standard':
            variant = 'WB'

         self.platform         = platform
         self.variant          = variant
         self.human_name       = CPU_VARIANTS.get(variant, 'A2P')
         self.external_variant = None
         self.reset            = Signal()
         self.interrupt        = Signal(32)
         self.interruptS       = Signal()
         self.ibus             = ibus = wishbone.Interface()
         self.dbus             = dbus = wishbone.Interface()
         self.periph_buses     = [ibus, dbus]
         self.memory_buses     = []
         self.enableDebug      = False
         self.enableJTAG       = False
         self.externalResetVector = 0

        # # #

         self.cpu_params = dict(
                i_clk                    = ClockSignal(),
                i_reset                  = ResetSignal() | self.reset,

                i_externalInterrupt  = self.interrupt[0],
                i_externalInterruptS = self.interruptS,
                i_timerInterrupt    = 0,
                i_softwareInterrupt = 0,

                o_iBusWB_ADR      = ibus.adr,
                o_iBusWB_DAT_MOSI = ibus.dat_w,
                o_iBusWB_SEL      = ibus.sel,
                o_iBusWB_CYC      = ibus.cyc,
                o_iBusWB_STB      = ibus.stb,
                o_iBusWB_WE       = ibus.we,
                o_iBusWB_CTI      = ibus.cti,
                o_iBusWB_BTE      = ibus.bte,
                i_iBusWB_DAT_MISO = ibus.dat_r,
                i_iBusWB_ACK      = ibus.ack,
                i_iBusWB_ERR      = ibus.err,

                o_dBusWB_ADR      = dbus.adr,
                o_dBusWB_DAT_MOSI = dbus.dat_w,
                o_dBusWB_SEL      = dbus.sel,
                o_dBusWB_CYC      = dbus.cyc,
                o_dBusWB_STB      = dbus.stb,
                o_dBusWB_WE       = dbus.we,
                o_dBusWB_CTI      = dbus.cti,
                o_dBusWB_BTE      = dbus.bte,
                i_dBusWB_DAT_MISO = dbus.dat_r,
                i_dBusWB_ACK      = dbus.ack,
                i_dBusWB_ERR      = dbus.err
            )

         self.cpu_params['i_externalResetVector'] = self.externalResetVector

         # these need to connect to top nets
         if self.enableDebug:
            self.cpu_params['i_debugReset'] = 0
            self.cpu_params['o_debug_resetOut'] = 0
            self.cpu_params['i_debug_bus_cmd_valid'] = 0
            self.cpu_params['i_debug_bus_cmd_ready'] = 0
            self.cpu_params['i_debug_bus_cmd_payload_wr'] = 0
            self.cpu_params['i_debug_bus_cmd_payload_address'] = 0
            self.cpu_params['i_debug_bus_cmd_payload_data'] = 0
            self.cpu_params['o_debug_bus_rsp_data'] = 0

         if self.enableJTAG:
            self.cpu_params['i_jtag_tms'] = 0
            self.cpu_params['i_jtag_tck'] = 0
            self.cpu_params['i_jtag_tdi'] = 0
            self.cpu_params['o_jtag_tdo'] = 0

    def set_reset_address(self, reset_address):
        assert not hasattr(self, "reset_address")
        self.reset_address = reset_address
        self.cpu_params.update(i_externalResetVector=Signal(32, reset=reset_address))

    @staticmethod
    def add_sources(platform, variant="WB"):
        cpu_filename = CPU_VARIANTS[variant] + ".v"
        vdir = os.path.join(dir, 'verilog')
        platform.add_source(os.path.join(vdir, cpu_filename))

    def use_external_variant(self, variant_filename):
        self.external_variant = True
        self.platform.add_source(variant_filename)

    def do_finalize(self):
        assert hasattr(self, "reset_address")
        if not self.external_variant:
            self.add_sources(self.platform, self.variant)
        self.specials += Instance("A2P_WB", **self.cpu_params)
