openmv/tools/alif/maintenance.py
2024-08-28 20:06:14 +02:00

780 lines
20 KiB
Python
Executable File

#!/usr/bin/env python3
#pylint: disable=invalid-name,superfluous-parens,anomalous-unicode-escape-in-string
#pylint: disable=import-error,wrong-import-position,wildcard-import,undefined-variable
"""
Maintenance mode tool
- Uses synchronous ISP commands
__author__ = "ronyett"
__copyright__ = "ALIF Seminconductor"
__version__ = "0.06.000"
__status__ = "Dev" "
"""
import os
import sys
import argparse
sys.path.append("./isp")
from serialport import serialPort # ISP Serial support
from serialport import COM_TIMEOUT_RX_DEFAULT
from isp_protocol import * # ISP protocol constants
from isp_core import *
from isp_util import *
from isp_print import *
#from isp_print import isp_print_color, isp_print_cursor_enable
#from isp_print import isp_print_cursor_disable,isp_print_terminal_reset
from toc_decode import * # ISP TOC support
from power_decode import * # ISP POWER support
from utils.config import *
from utils.user_validations import validateArgList
from recovery import recovery_action
MENU_DB = 'utils/maintDB.db'
MENU_CFG = 'utils/menuconfDB.db'
# Define Version constant for each separate tool
# 0.01.000 Concept+realisation
# 0.02.000 add get TOC
# 0.03.000 add get CPU status
# 0.04.000 add get MRAM contents
# 0.05.000 add get SERAM metrics and terminal mode
# 0.06.000 add ERASE app mram feature
# 0.07.000 add FAST ERASE app mram feature
# 0.07.001 Cursor reset after terminal mode
# 0.08.001 add groups for menu items
# 0.08.002 Cursor reset after Hard Maintenance mode
# 0.08.003 added Ctrl-C in showAndSelectOptions()
# 0.08.004 added python error exit code
# 0.09.000 added menu configurations
# 0.10.000 Integrated recoery mode
# 0.11.000 added get POWER
# 0.12.000 add command line option
# 0.13.000 get baudrate from DBs
# 0.14.000 add get clock data
# 0.15.000 add Fast Erase including NTOC
# 0.16.000 add Recovery MRAM_READ
TOOL_VERSION = "0.16.000"
EXIT_WITH_ERROR = 1
FAST_ERASE_SIZE = 16
def reset_action(isp):
"""
reset_action
reset the device, send ISP request to reset
"""
isp_reset(isp)
def get_revision_action(isp):
"""
get device revision_action
obtain the device revision info
"""
isp_start(isp)
isp_get_revision(isp)
isp_stop(isp)
def get_toc_action(isp):
"""
get toc action
obtain the TOC info from target
"""
isp_start(isp)
isp_get_toc_data(isp)
isp_stop(isp)
def get_power_action(isp):
"""
get power action
obtain the POWER info from target
"""
isp_start(isp)
isp_get_power_data(isp)
isp_stop(isp)
def get_clock_action(isp):
"""
get clock action
obtain the Clock, XTal, PLL info from target
"""
isp_start(isp)
isp_get_clock_data(isp)
isp_stop(isp)
def get_banner_action(isp):
"""
get_banner_action
obtain the SERAM banner
"""
isp_start(isp)
isp_get_banner(isp)
isp_stop(isp)
def get_cpu_status_action(isp):
"""
get cpu status_action
obtain the cpu boot info
"""
isp_start(isp)
isp_get_cpu_status(isp)
isp_stop(isp)
def get_enquiry_action(isp):
"""
get_enquiry_action
Enquire what is at the end of the cable
"""
isp_start(isp)
isp_enquiry(isp)
isp_stop(isp)
def get_mram_info_action(isp):
"""
get the real MRAM details
"""
isp_start(isp)
isp_get_mram_contents(isp)
isp_stop(isp)
def get_seram_info_action(isp):
"""
get_seram_info_action
get seram metrics
"""
isp_start(isp)
isp_get_seram_metrics(isp)
isp_stop(isp)
def get_mram_read_action(isp):
"""
get MRAMvalue
"""
offset = input('Enter hex word offset: ')
try:
offset_hex = int(offset,16)
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
except:
print("[ERROR] not a valid offset")
return
isp_start(isp)
isp_read_mram(isp,offset_hex)
isp_stop(isp)
def get_otp_read(isp):
"""
get OTP value
"""
offset = input('Enter word addr(hex): ')
try:
offset_hex = int(offset,16)
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
except:
print("[ERROR] not a valid hex offset")
return
isp_start(isp)
isp_read_otp(isp,offset_hex)
isp_stop(isp)
def get_address_action(isp):
"""
get address value
"""
address = input('Enter addr(hex): ')
try:
address_hex = int(address,16)
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
except:
print("[ERROR] not a valid address")
return
isp_start(isp)
isp_get_address(isp,address_hex)
isp_stop(isp)
def set_address_action(isp):
"""
set address value
"""
address = input('Enter addr(hex): ')
try:
address_hex = int(address,16)
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
except:
print("[ERROR] not a valid address")
return
data = input('Enter data(hex): ')
try:
data_hex = int(data,16)
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
except:
print("[ERROR] not a valid address")
return
isp_set_address(isp,address_hex,data_hex)
def get_trace_buffer(isp):
"""
get SEROM trace buffer
"""
isp_start(isp)
isp_get_trace_buffer(isp)
isp_stop(isp)
def get_seram_trace_buffer(isp):
"""
get SERAM trace buffer
"""
isp_start(isp)
try:
isp_get_seram_trace_buffer(isp)
except:
print('[ERROR] Unable to get SERAM trace when SEROM is in recovery mode')
isp_stop(isp)
def set_print_enable_action(isp):
"""
set print enable
"""
isp_set_print_enable(isp, True)
def set_print_disable_action(isp):
"""
set print disable
"""
isp_set_print_enable(isp, False)
def set_logger_enable_action(isp):
"""
set Logger enable
"""
isp_set_log_enable(isp, True)
def set_logger_disable_action(isp):
"""
set Logger disable
"""
isp_set_log_enable(isp, False)
def terminal_mode(isp):
"""
terminal_mode
Enter terminal mode, use [CTRL-C] to exit
"""
ctrlc = CtrlCHandler() # [CTRL-C] handling
print("[TERMINAL] Ctrl-C to exit")
going = True
while going:
output_line = isp.readSerial(ISP_MAXIMUM_PACKET_SIZE)
raw_print = bytearray()
for i in range(len(output_line)):
raw_print.append(output_line[i])
try:
ascii_char = raw_print.decode('ascii')
print(ascii_char,flush=True,end='')
sys.stdout.flush()
except UnicodeDecodeError:
pass
if ctrlc.Handler_exit():
print("[INFO] CTRL-C")
break
print("[TERMINAL] Ends")
isp_print_cursor_enable()
isp_print_terminal_reset()
def application_mram_erase(isp, address, erase_length, reset):
"""
erase_application_mram_action
erase the Application area of MRAM based on the device details
"""
global baud_rate
erase_length_fmt = "{:,}".format(erase_length)
pattern = 0x00000000
print("[INFO] erasing 0x%x %s bytes" %(address,erase_length_fmt))
put_target_in_maintenance_mode(isp, baud_rate,False)
isp_start(isp)
isp_mram_erase(isp,address,erase_length,pattern)
isp_stop(isp)
if reset:
isp_reset(isp)
def full_erase_application_mram_action(isp):
"""
full_erase_application_mram_action
Full erase the Application area of MRAM based on the device details
"""
global OEM_BASE_ADDRESS
global OEM_MRAM_SIZE
address = OEM_BASE_ADDRESS
erase_length = OEM_MRAM_SIZE
application_mram_erase(isp, address, erase_length, True)
print("[INFO] Full Erase done")
def fast_erase_app_include_start_mram_action(isp):
"""
fast_erase_app_include_start_mram_action
Fast erase the Application area of MRAM (16 bytes) based on the device details
Plus the beginning of MRAM (0x8000-0000)
"""
global ALIF_BASE_ADDRESS
global OEM_BASE_ADDRESS
# erase APP TOC
address = ALIF_BASE_ADDRESS - FAST_ERASE_SIZE
erase_length = FAST_ERASE_SIZE
# this function can't be called twice as target stops to respond
application_mram_erase(isp, address, erase_length, False)
# erase start of mram (remove residuals from applications that start at 0x8000-0000)
address = OEM_BASE_ADDRESS
erase_length = FAST_ERASE_SIZE
# this function can't be called twice as target stops to respond
application_mram_erase(isp, address, erase_length, True)
print("[INFO] Fast Erase done (incl. NTOC)")
def fast_erase_application_mram_action(isp):
"""
fast_erase_application_mram_action
Fast erase the Application area of MRAM (16 bytes) based on the device details
"""
global ALIF_BASE_ADDRESS
address = ALIF_BASE_ADDRESS - FAST_ERASE_SIZE
erase_length = FAST_ERASE_SIZE
application_mram_erase(isp, address, erase_length, True)
print("[INFO] Fast Erase done")
def process_log_data(log_data):
"""
process_log_data
"""
#print("log_data: ", log_data)
max_index = log_data[0]
head = log_data[1]
index = log_data[2] # start from the tail
data = log_data[3]
ordered_data = []
while True:
if index == max_index:
index = 0
if index == head:
break
ordered_data.append(chr(data[index]))
index += 1
#print("ordered data: ", ordered_data)
isp_print_color("blue", "........\n\n")
display_str = ""
for ch in ordered_data:
if ch == '\00':
if display_str != "":
severity = display_str[0]
if severity >= '0' and severity <= '9':
display_str = display_str[1:]
isp_print_color("blue", display_str)
display_str = ""
else:
display_str += ch
def get_log_data_action(isp):
"""
get_log_data_action
"""
isp_start(isp)
log_data = isp_get_log_data(isp)
if log_data != None:
process_log_data(log_data)
isp_stop(isp)
def maintenance_mode(isp):
"""
maintenance_mode
Enter Manitenance mode
"""
global handler
global baud_rate
Flicks="|/-\\|/-\\"
Flick = 0
isp.setTimeout(0.1)
isp.setBaudRate(baud_rate)
isp_print_cursor_disable()
print_count = 0
se_chars = [b'S', b'E', b'P', b'C', b'T', b'L']
se_index = 0
expected_char = b'\0'
while True:
send_data = bytearray(b'ALIF')
isp.writeSerial(send_data)
sys.stdout.flush()
recv_data = bytearray(isp.readSerial(1))
print("Waiting for Target..[RESET Platform] " + Flicks[Flick] +
'\r', flush=True, end="")
sys.stdout.flush()
# single character version
#if (recv_data == b'\xF0'):
# break
# looking for the sequence in se_chars
if se_index == 0:
if recv_data == se_chars[0]:
# start of sequence
se_index = 1
expected_char = se_chars[se_index]
elif recv_data != expected_char:
# unexpected character, reset the sequence
se_index = 0
expected_char = b'\0'
else:
# received the expected character
if se_index >= (len(se_chars) - 1):
# the entire sequence is received
break
se_index += 1
expected_char = se_chars[se_index]
if handler.Handler_exit():
print("\n[INFO] CTRL-C")
break
print_count = print_count + 1
if print_count > 5: # "Fake" delay, time.sleep() will halt
Flick = Flick + 1
if Flick == 8:
Flick -= 8
print_count = 0
send_data = bytearray(b'ALIF')
isp.writeSerial(send_data)
sys.stdout.flush()
# switch back to post pll baud rate
isp.setTimeout(COM_TIMEOUT_RX_DEFAULT)
isp.setBaudRate(baud_rate)
isp_print_cursor_enable()
def soft_maintenance_mode(isp):
"""
soft_maintenance_mode
enter Soft mode
"""
global baud_rate
put_target_in_maintenance_mode(isp, baud_rate,False)
def get_ecc_key(isp):
"""
get_ecc_key from device
"""
isp_start(isp)
isp_get_ecc_key(isp)
isp_stop(isp)
def get_firewall_config(isp):
"""
get_firewall_config
Pull back Firewall config from target
"""
isp_start(isp)
isp_get_firewall_config(isp)
isp_stop(isp)
def show_help(supported_commands):
"""
show_help
display all supported commands
"""
print("Supported commands:")
for item in sorted(supported_commands):
print('\t{}'.format(supported_commands[item][0]))
def maintenance_menu(supported_commands, isp):
"""
maintenance_mode
Show Menu options and get user input
"""
option = 'x'
while option == 'x':
optList = []
print('\nAvailable options:\n')
i = 1
for item in supported_commands:
print("%2s - %s" % (str(i), item))
i += 1
optList.append(item)
try:
option = input('\nSelect an option (Enter to return): ')
except EOFError:
print("[Ctrl-C EOF]")
sys.exit()
except KeyboardInterrupt:
print("[Ctrl-C]")
sys.exit()
if option == '':
return
try:
idx = int(option)
except ValueError:
print('Invalid option - Please try again')
option = 'x'
continue
if idx < 1 or idx > len(optList):
print('Invalid option - Please try again')
option = 'x'
continue
try:
func = globals()[supported_commands[optList[idx-1]]]
except ValueError:
print("oops")
func(isp)
option = 'x'
def showAndSelectOptions(list):
"""
showAndSelectOptions
"""
option = 'x'
while option == 'x':
print('\nAvailable options:\n')
i = 1
optList = []
for opt in list:
print("%2s - %s" % (str(i), opt))
i += 1
optList.append(opt)
try:
option = input('\nSelect an option (Enter to exit): ')
except EOFError:
print("[Ctrl-C EOF]")
sys.exit(0)
if option == '':
sys.exit(0)
try:
idx = int(option)
except ValueError:
print('[ERROR] Invalid option - Please try again')
option = 'x'
continue
if idx < 1 or idx > len(list):
print('[ERROR] Invalid option - Please try again')
option = 'x'
return optList[idx-1]
def read_json_file(file):
"""
read_json_file
"""
f = open(file, 'r')
try:
data = json.load(f)
except JSONDecodeError as e:
print("[ERROR] in JSON file.")
print(str(e))
sys.exit(EXIT_WITH_ERROR)
except ValueError as v:
print("[ERROR] in JSON file:")
print(str(v))
sys.exit(EXIT_WITH_ERROR)
except:
print("[ERROR] Unknown error loading JSON file")
sys.exit(EXIT_WITH_ERROR)
f.close()
return data
def checkRestrictions(menu_item, menu_config):
"""
checkRestrictions
"""
if menu_item in menu_config.keys():
for item in menu_config[menu_item]:
if globals()[item] in menu_config[menu_item][item]:
return True
return False
def command_line_mode(isp,cli_option):
"""
command_line_mode
- operate on commands passed in rather than the menus
"""
command_line_lut = {
"sesbanner" : get_banner_action
}
if cli_option in command_line_lut:
command_line_lut[cli_option](isp)
else:
print("[ERROR] Command %s not recognized" %cli_option)
sys.exit(EXIT_WITH_ERROR)
def main():
"""
Maintenance tool
"""
global ALIF_BASE_ADDRESS
global OEM_BASE_ADDRESS
global OEM_MRAM_SIZE
global DEVICE_FEATURE_SET
global DEVICE_FAMILY
global DEVICE_REVISION
global DEVICE_PART_NUMBER
global DEVICE_FEATURE_SET_REVISION
global handler
global baud_rate
if sys.version_info.major == 2:
print("[ERROR] You need Python 3 for this application!")
sys.exit(EXIT_WITH_ERROR)
# Deal with Command Line
parser = argparse.ArgumentParser(description=
'FUSION Maintenance Tool')
parser.add_argument("-b", "--baudrate",
help="serial port baud rate",
type=int)
parser.add_argument("-d" , "--discover", action='store_true',
default=False, help="COM port discovery")
parser.add_argument("-opt" ,"--option",
type=str,default="",
help="call option [sesbanner]")
parser.add_argument("-V" , "--version",
help="Display Version Number",
action="store_true")
parser.add_argument("-v" , "--verbose",
help="verbosity mode",
action="store_true")
args = parser.parse_args()
if args.version:
print(TOOL_VERSION)
sys.exit()
# memory defines for Alif/OEM MRAM Addresses and Sizes
load_global_config()
ALIF_BASE_ADDRESS = utils.config.ALIF_BASE_ADDRESS
OEM_BASE_ADDRESS = utils.config.APP_BASE_ADDRESS
OEM_MRAM_SIZE = utils.config.APP_MRAM_SIZE
DEVICE_FEATURE_SET = utils.config.DEVICE_FEATURE_SET
DEVICE_FAMILY = utils.config.DEVICE_FAMILY
DEVICE_REVISION = utils.config.DEVICE_REVISION
DEVICE_PART_NUMBER = utils.config.DEVICE_PART_NUMBER
DEVICE_REV_BAUD_RATE = utils.config.DEVICE_REV_BAUD_RATE
# create a compound variable to address use cases as Fusion Rev B0
DEVICE_FEATURE_SET_REVISION = DEVICE_FEATURE_SET + '_' + DEVICE_REVISION
baud_rate = DEVICE_REV_BAUD_RATE[DEVICE_REVISION]
if args.baudrate is not None:
baud_rate = args.baudrate
handler = CtrlCHandler() # [CTRL-C] handling
os.system('') # Help MS-DOS window with ESC sequences
# Serial dabbling open up port.
isp = serialPort(baud_rate) # Create ISP session
isp.setVerbose(args.verbose) # turn on or off verbose mode for the host
isp.CTRLCHandler = handler # CTRL C Handler for this ISP session
if args.discover: # discover the COM ports if requested
print('Discover')
isp.discoverSerialPorts()
errorCode = isp.openSerial()
if errorCode is False:
print("[ERROR] isp openSerial failed for %s" %isp.getPort())
sys.exit(EXIT_WITH_ERROR)
print("[INFO] %s open Serial port success" %isp.getPort())
# Set up the baud rate ((same calls as in recovery.py))
#if baud_rate != COM_BAUD_RATE_DEFAULT:
# isp.setBaudRate(baud_rate)
print("[INFO] baud rate", isp.getBaudRate())
# Process Maintenance Grouped Menus
menuDB = read_json_file(MENU_DB)
groupsDB = menuDB['groups']
# load menu configurations
menuCfgDB = read_json_file(MENU_CFG)
if args.option:
command_line_mode(isp,args.option)
else:
opt = '*'
while opt != '':
opt = showAndSelectOptions(groupsDB)
# filter available commands based on silicon, revision, etc.
supported_commands = menuDB[groupsDB[opt]]
menu_options = {}
for item in supported_commands:
if not checkRestrictions(supported_commands[item], menuCfgDB):
menu_options[item] = supported_commands[item]
maintenance_menu(menu_options, isp)
isp.closeSerial()
# Reset the Terminal of any ANSI Escape sequence debris
isp_print_cursor_enable()
isp_print_terminal_reset()
if __name__ == "__main__":
main()