#!/usr/bin/python
#===============================================================================
# CERN (BE-CO-HT)
# MultiBoot example script
#===============================================================================
# author: Theodor Stana (t.stana@cern.ch)
#
# date of creation: 2013-08-13
#
# version: 1.0
#
# description:
#     This module exemplifies reading and writing to the CONV-TTL-BLO FPGA
#     registers for sending a bitstream to the CONV-TTL-BLO flash and then
#     issuing the IPROG command to the FPGA to load the new bitstream from the
#     flash.
#
#     To communicate to the CONV-TTL-BLO, an EI2C object is used to open a
#     coonection to the ELMA crate and send the writereg, writemregs and readreg
#     commands.
#
# dependencies:
#     ei2c.py
#     ei2cexcept.py
#
# references:
#   [1] M25P32 32Mb 3V NOR Serial Flash Embedded Memory
#       http://cern.ch/go/z7pw
#
#===============================================================================
# GNU LESSER GENERAL PUBLIC LICENSE
#===============================================================================
# This source file is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version. This source is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details. You should have
# received a copy of the GNU Lesser General Public License along with this
# source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
#===============================================================================
# last changes:
#    2013-08-13    Theodor Stana     File created
#===============================================================================
#  TODO: -
#===============================================================================

import os
import sys
sys.path.append("../ei2c")
import time
from ei2c import *
import ei2cdefine
from ei2cexcept import *
sys.path.append("lib-multiboot")
from xil_multiboot import *

# Return the list of gateware files at the requested path; each board should
# have a folder under "path" with its specific name. Files under this folder
# with the board's name should be bitstream files only.
def get_gw_list(path):
    path = path+'/'
    return [path+f for f in os.listdir(path) if not os.path.isdir(f)]

# MAIN "FUNCTION"
if __name__ == "__main__":

    # Get the IP, user and password for the ELMA crate from ei2cdefine.py
    ip   = ei2cdefine.HNAME
    user = ei2cdefine.USER
    pwd  = ei2cdefine.PWD
    elma = EI2C(ip, user, pwd)
    elma.open()

    # Ask for slot number
    while 1:
        try:
            slot = raw_input("Slot no.: ")
            slot = int(slot)
            break
        except TypeError as e:
            print("Please input a decimal slot number.")
        except SlotError as e:
            print(e.strerror)
        except ValueError as e :
            print ("%s is not a valid integer." % e.args[0].split(": ")[1])
        except KeyboardInterrupt:
            sys.exit();
        except:
            print("Unexpected error: ", sys.exc_info()[0])

    # For blocking boads:
    # The PULSETEST bitstream has the MultiBoot module starting at address 0x300
    # Gateware versions < 3.0, or golden <0.2 have it starting at address 0x040
    boardid = elma.read(slot,0x00)
    gwvers = elma.read(slot, 0x04) & 0xff
    baseaddr = 0x100
    if (boardid == 0x54424c4f):
        if (gwvers == 0xff):
            baseaddr = 0x300
        elif ((gwvers >= 0x10) and (gwvers <= 0x22)) or \
             (((gwvers & 0xf0) == 0x00) and (gwvers < 0x02)):
            baseaddr = 0x040

    mb = XilMultiboot(ELMA_I2C_MULTIBOOT, elma, slot, baseaddr, "")

    # Read config reg, if user wants to
    while 1:
        rdcfgreg = raw_input("Read config reg? (y/n) ")
        if (rdcfgreg == 'y'):
            mb.rdcfgreg()
            break
        elif (rdcfgreg == 'n'):
            break

    # Ask what else to do (read/write/iprog)
    while 1:
        rd = raw_input("Read from flash? (y/n) ")
        if (rd == 'y'):
            print("This might take a VERY long time if you read a lot of bytes...")
            while 1:
                try:
                    rdsa = raw_input("Start address: 0x")
                    rdsa = int(rdsa, 16)
                    rdea = raw_input("End address:   0x")
                    rdea = int(rdea, 16)
                    break
                except ValueError as e:
                    print "Please input a hexadecimal address!"
                except KeyboardInterrupt:
                    sys.exit()
                except:
                    print "Unexpected error: ", sys.exc_info()[0]
            break
        elif (rd == 'n'):
            break

    while 1:
        wr = raw_input("Write to flash?  (y/n) ")
        if (wr == 'y'):
            while 1:
                wr = raw_input("Are you sure?    (y/n) ")
                if (wr == 'y'):
                    # Get gateware list from current folder
                    print("Which gateware would you like to program?")
                    gw_list = sorted(get_gw_list("bin"))
                    for (i, gw) in enumerate(gw_list):
                        print("  %2d -> %s" % (i, os.path.basename(gw)))
                    if (len(gw_list) == 0):
                        print("No bitstreams file found in bin/ folder, cannot perform write...")
                        wr = 'n'
                        break
                    start = False
                    while (not start):
                        is_valid = False
                        while(not is_valid):
                            try :
                                print("")
                                choice = int(raw_input("Enter your choice : "))
                                if choice < len(gw_list):
                                    is_valid = 1
                                else:
                                    print("%d is not in range 0-%d"%(choice, len(gw_list)-1))
                            except ValueError as e :
                                print ("%s is not a valid integer." % e.args[0].split(": ")[1])

                        gw_path = gw_list[choice]
                        print("Selected gateware: %s" % os.path.basename(gw_path))
                        inp = raw_input("Is this okay?    (y/n) ");
                        if (inp.lower() == 'y'):
                            start = True
                            print("")

                    # Set bitstream filename to selected bitstream
                    mb.set_bitstream_file(gw_list[choice])
                    break
                elif (wr == 'n'):
                    break
            break
        elif (wr == 'n'):
            break

    while 1:
        iprog = raw_input("Issue IPROG?     (y/n) ")
        if (iprog == 'y') or (iprog == 'n'):
            break

    # Ask for multiboot address on write or IPROG
    if (wr == 'y') or (iprog == 'y'):
        while 1:
            try:
                multiboot_addr = raw_input("MultiBoot bitstream address: 0x")
                multiboot_addr = int(multiboot_addr, 16)
                break
            except ValueError as e:
                print "Please input a hexadecimal address!"
            except KeyboardInterrupt:
                sys.exit()
            except:
                print "Unexpected error: ", sys.exc_info()[0]

    # Now do something based on user selection
    if (rd == 'y'):
        mb.read(rdsa, rdea)
    if (wr == 'y'):
        mb.write(multiboot_addr)
    if (iprog == 'y'):
        mb.iprog(multiboot_addr)

    # Close I2C connection
    elma.close()

