# ----------------------------------------------------------------------
# -- Generic Scons script for Sintesizing hardware with APIO or scons
# -- on an icestick or Icezum Alhambra
# ----------------------------------------------------------------------
import os
from os.path import join
import platform
import glob
from SCons.Script import (Builder, DefaultEnvironment, Default, AlwaysBuild,
                          GetOption, Environment, Exit, COMMAND_LINE_TARGETS,
                          ARGUMENTS, Variables, Help)

# --------------------------------------------------------------------------
# -- Board types: icestick, icezum, go-board
# -- Each board has its own paramaters for the FPGA: type, size and package
# ---------------------------------------------------------------------------
boards = {'ICESTICK': ['hx', '1k', 'tq144'],
          'ICEZUM': ['hx', '1k', 'tq144'],
          'GO-BOARD': ['hx', '1k', 'vq100']
          }

# -- Default board
BOARD_DFL = 'ICESTICK'

# -- Get the board type from the command line
vars = Variables()
vars.Add('board', 'Set the FPGA board', BOARD_DFL)

BOARD = ARGUMENTS.get('board', BOARD_DFL).upper()

# -- Set the fpga flags according to the board type
try:
    (FPGA_TYPE, FPGA_SIZE, FPGA_PACK) = boards[BOARD]
except KeyError:
    print("\n--> Unkown board: {}".format(BOARD))
    exit(1)


# -- This is obsolete. Will be removed in fugure
# -----------------------------------------------------------------------------
# -- FLAGs:
# --
# -- Default values compatible with the icestick and icezum alhambra boards
# --
# -----------------------------------------------------------------------------
# -------- Icestick / IceZUM Alhambra boards:
# -- FPGA_SIZE_DFL = 1k
# -- FPGA_TYPE_DFL = hx
# -- FPGA_PACK_DFL = tq144
#
# -------- Nandland go board
# -- FPGA_SIZE_DFL = 1k
# -- FPGA_TYPE_DFL = hx
# -- FPGA_PACK_DFL = vq100
# --
# -----------------------------------------------------------------------------
#
# -- Size. Possible values: 1k, 8k
FPGA_SIZE_DFL = '1k'

# -- Type. Possible values: hx, lp
FPGA_TYPE_DFL = 'hx'

# -- Package. Possible values: swg16tr, cm36, cm49, cm81, cm121, cm225, qn84,
# --   cb81, cb121, cb132, vq100, tq144, ct256
FPGA_PACK_DFL = 'tq144'
# -------------------------------------------------------------------

# -- Add the FPGA flags as variables to be shown with the -h scons option
vars.Add('fpga_pack', 'Set the ICE40 FPGA packages', FPGA_PACK_DFL)
vars.Add('fpga_type', 'Set the ICE40 FPGA type (hx/lp)', FPGA_TYPE_DFL)
vars.Add('fpga_size', 'Set the ICE40 FPGA size (1k/8k)', FPGA_SIZE_DFL)


# -- Get the value for the FPGA flags: either default value or passed through
# -- the scons parameters
# FPGA_SIZE = ARGUMENTS.get('fpga_size', FPGA_SIZE_DFL)
# FPGA_TYPE = ARGUMENTS.get('fpga_type', FPGA_TYPE_DFL)
# FPGA_PACK = ARGUMENTS.get('fpga_pack', FPGA_PACK_DFL)

# -- Just for debugging
print("BOARD: {}".format(BOARD))
print("FPGA_SIZE: {}".format(FPGA_SIZE))
print("FPGA_TYPE: {}".format(FPGA_TYPE))
print("FPGA_PACK: {}".format(FPGA_PACK))


# -- Executables extension
EXT = ''
if 'Windows' == platform.system():
    EXT = '.exe'

# -- Target name
TARGET = 'hardware'

# -- Get a list of all the verilog files in the src folfer, in ASCII, with
# -- the full path. All these files are used for the simulation
v_nodes = glob.glob('*.v')
src_sim = ["{}".format(f) for f in v_nodes]

# --------- Get the Testbench file (there should be only 1)
# -- Create a list with all the files finished in _tb.v. It should contain
# -- the test bench
list_tb = [f for f in src_sim if f[-5:].upper() == "_TB.V"]

if len(list_tb) > 1:
    print("---> WARNING: More than one testbenches used")

# -- Error checking
try:
    testbench = list_tb[0]

# -- there is no testbench
except IndexError:
    testbench = None

if 'sim' in COMMAND_LINE_TARGETS:
    if testbench is None:
        print("ERROR!!! NO testbench found for simulation")
        Exit(1)

    # -- Simulation name
    SIMULNAME, ext = os.path.splitext(testbench)
else:
    SIMULNAME = ''

# -------- Get the synthesis files.  They are ALL the files except the
# -------- testbench
src_synth = [f for f in src_sim if f not in list_tb]

if len(src_synth) == 0:
    print("------->   WARNING: no verilog files found (.v)")
    Exit(1)

# -- For debugging
print("----> Testbench: {}".format(testbench))
# print("SIM NAME: {}".format(SIMULNAME))

# -- Get the PCF file
PCF_list = glob.glob('*.pcf')

try:
    PCF = PCF_list[0]
except IndexError:
    print("\n--------> WARNING: no .pcf file found <----------\n")
    PCF = 'ERROR.pcf'

# -- Debug
print("----> PCF Found: {}".format(PCF))


# -- Define the Sintesizing Builder
synth = Builder(
    action='yosys{0} -p \"synth_ice40 -blif $TARGET\" $SOURCE'.format(EXT),
    suffix='.blif',
    src_suffix='.v')

pnr = Builder(
    action='arachne-pnr{0} -d {1} -P {2} -p {3} -o $TARGET $SOURCE'.format(
        EXT, FPGA_SIZE, FPGA_PACK, PCF),
    suffix='.asc',
    src_suffix='.blif')

bitstream = Builder(action='icepack{0} $SOURCE $TARGET'.format(EXT),
                    suffix='.bin',
                    src_suffix='.asc')

# -- Icetime builder
time_rpt = Builder(
    action='icetime{0} -d {1}{2} -P {3} -mtr $TARGET $SOURCE'.format(
        EXT, FPGA_TYPE, FPGA_SIZE, FPGA_PACK),
    suffix='.rpt',
    src_suffix='.asc')

# icetime -d $(FPGA_TYPE)$(FPGA_SIZE) -P $(FPGA_PACK) -mtr $@ $^

# -- Build the environment
env = DefaultEnvironment(BUILDERS={'Synth': synth, 'PnR': pnr,
                                   'Bin': bitstream, 'Time': time_rpt},
                         ENV=os.environ,
                         tools=[],
                         variables=vars)

# -- Show all the flags defined, when scons is invoked with -h
Help(vars.GenerateHelpText(env))

# -- Generate the bitstream
blif = env.Synth(TARGET, [src_synth])
asc = env.PnR(TARGET, [blif, PCF])
bitstream = env.Bin(TARGET, asc)

# -- Upload the bitstream into FPGA
upload = env.Alias('upload', bitstream, 'iceprog{0} $SOURCE'.format(EXT))
AlwaysBuild(upload)

# -- Target time: calculate the time
rpt = env.Time(asc)
t = env.Alias('time', rpt)
AlwaysBuild(t)

# -------------------- Simulation ------------------
# -- Constructor para generar simulacion: icarus Verilog
iverilog = Builder(action='iverilog -o $TARGET $SOURCES',
                   suffix='.out',
                   src_suffix='.v')

vcd = Builder(action=join(glob.os.getcwd(), "$SOURCE"),
              suffix='.vcd', src_suffix='.out')

simenv = Environment(BUILDERS={'IVerilog': iverilog, 'VCD': vcd},
                     ENV=os.environ)

out = simenv.IVerilog(SIMULNAME, src_sim)
vcd_file = simenv.VCD(SIMULNAME, out)

waves = simenv.Alias('sim', SIMULNAME+'.vcd', 'gtkwave ' +
                     "{} ".format(vcd_file[0]) + " " + SIMULNAME + ".gtkw")
AlwaysBuild(waves)

Default(bitstream)

# -- These is for cleaning the files generated using the alias targets
if GetOption('clean'):
    env.Default([out, t, vcd_file])
