X-Git-Url: http://git.rot13.org/?p=BML_sump2;a=blobdiff_plain;f=sump2.py;fp=sump2.py;h=796f1135163d85ebc58bda6eeaf830c21b12cd29;hp=86478afab25177996c3a29a7b6a74a25c3259cce;hb=52cbfe785eb2d30bb74b92f6d38a01483799d7b0;hpb=b6ac52f611f53e96447a6bee178d418f14623985 diff --git a/sump2.py b/sump2.py index 86478af..796f113 100755 --- a/sump2.py +++ b/sump2.py @@ -2,7 +2,6 @@ # sump2 # Copyright (c) Kevin M. Hubbard 2016 BlackMesaLabs # -# # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later @@ -65,6 +64,13 @@ # https://pypi.python.org/packages/...../pyserial-3.1.1-py2.py3-none-any.whl # pip install pyserial-3.1.1-py2.py3-none-any.whl # +# [ spidev for RaspberryPi SPI Communications to icoboard ] +# sudo apt-get install python-dev +# sudo apt-get install git +# git clone git://github.com/doceme/py-spidev +# cd py-spidev +# sudo python setup.py install +# # TODO: Vertical resizing of window has issues. Signal scrolling isnt updated # # Revision History: @@ -104,6 +110,7 @@ # 10.20.16 khubbard Improve centering to trigger post acquisition. # 10.21.16 khubbard Acquisition_Length fixed. Also works with RLE now. # 10.24.16 khubbard Fixed RLE cropping not showing DWORDs.Speed Improvs +# 12.19.16 khubbard Fork for Pi and icoboard from Windows original. ############################################################################### import time from time import sleep; @@ -113,13 +120,13 @@ import sys; import os; import platform; import locale; - +import spidev; class main(object): def __init__(self): # import math # pow # import types # type - self.vers = "2016.10.24"; + self.vers = "2016.12.21"; print("Welcome to SUMP2 " + self.vers + " by BlackMesaLabs"); self.mode_cli = True; @@ -135,7 +142,7 @@ class main(object): self.math = math; list2file( self, "sump2_manual.txt", init_manual(self ) ); - locale.setlocale( locale.LC_NUMERIC, 'English' ); +# locale.setlocale( locale.LC_NUMERIC, 'English' ); init_globals( self );# Internal software variables self.file_log = open ( self.vars["file_log"] , 'w' ); @@ -226,6 +233,7 @@ class main(object): # Load in the wavefile if os.path.exists( file_name ): + draw_header( self, ( "load_format() "+ file_name ) );# Pi print( "load_format() ", file_name ); load_format( self, file_name ); trig_i = sump_dump_data(self); @@ -272,11 +280,12 @@ class main(object): recalc_max_samples( self ); - # Draw the 1st startup screen screen_refresh( self ); self.context = "gui"; + self.pygame.mouse.set_cursor(*pygame.cursors.arrow);# Pi + # GUI Main Loop self.clock = self.pygame.time.Clock(); self.time = self.pygame.time; @@ -326,8 +335,12 @@ class main(object): # for non-RLE this is trivial, for RLE it is more complicated trig_to_start = trig_i - 0; trig_to_end = self.max_samples - trig_i; - start = trig_i - min( trig_to_start, trig_to_end ); - stop = trig_i + min( trig_to_start, trig_to_end ); +# start = trig_i - min( trig_to_start, trig_to_end ); +# stop = trig_i + min( trig_to_start, trig_to_end ); +# HERE88 +# Pi is slower for rendering, so render less + start = trig_i - min( trig_to_start, trig_to_end ) // 2; + stop = trig_i + min( trig_to_start, trig_to_end ) // 2; proc_cmd( self, "zoom_to", [str(start), str(stop) ] ); # proc_cmd( self, "zoom_to", ["0", str( self.max_samples ) ] ); @@ -345,12 +358,12 @@ class main(object): for event in pygame.event.get(): # User did something # VIDEORESIZE - if event.type == pygame.VIDEORESIZE: - self.screen= pygame.display.set_mode(event.dict['size'], - pygame.RESIZABLE | - pygame.HWSURFACE | - pygame.DOUBLEBUF); - self.resize_on_mouse_motion = True;# Delay redraw until resize done +# if event.type == pygame.VIDEORESIZE: +# self.screen= pygame.display.set_mode(event.dict['size'], +# pygame.RESIZABLE | +# pygame.HWSURFACE | +# pygame.DOUBLEBUF); +# self.resize_on_mouse_motion = True;# Delay redraw until resize done # Detect when console box has gained focus and switch from GUI to BD_SHELL # and loop in a keyboard loop processing commands. Exit on a NULL Command. @@ -376,6 +389,8 @@ class main(object): proc_cmd( self, "font_larger" , [] ); elif ( event.key == pygame.K_END ): proc_cmd( self, "font_smaller" , [] ); + elif ( event.key == pygame.K_Q ): + proc_cmd( self, "quit" , [] ); elif ( event.key == pygame.K_RIGHT ): num_samples = self.sample_room // 16; proc_cmd( self, "scroll_right", [str(num_samples)] ); @@ -660,7 +675,7 @@ class main(object): # draw_screen( self ); # screen_flip( self ); - shutdown( self ); + shutdown( self, False, False ); return;# This is end of main program loop ############################################################################### @@ -690,12 +705,28 @@ def display_init( self ): log( self, ["display_init()"] ); import pygame # Import PyGame Module pygame.init() # Initialize the game engine - self.screen_width = int( self.vars["screen_width"], 10 ); - self.screen_height = int( self.vars["screen_height"], 10 ); +# self.screen_width = int( self.vars["screen_width"], 10 ); +# self.screen_height = int( self.vars["screen_height"], 10 ); # pygame.NOFRAME, pygame.FULLSCREEN + + ( self.screen_width, self.screen_height ) = \ + (pygame.display.Info().current_w, pygame.display.Info().current_h); + print( self.screen_width ); + print( self.screen_height ); + +# self.screen_width = 800; +# self.screen_height = 600; + self.screen=pygame.display.set_mode( [ self.screen_width, self.screen_height ], - pygame.RESIZABLE | pygame.HWSURFACE | pygame.DOUBLEBUF ); + pygame.FULLSCREEN ); +# pygame.FULLSCREEN | pygame.HWSURFACE | pygame.DOUBLEBUF ); + +# self.screen=pygame.display.set_mode( +# [ self.screen_width, self.screen_height ], +# pygame.RESIZABLE | pygame.HWSURFACE | pygame.DOUBLEBUF ); + + self.pygame = pygame; self.pygame.display.set_icon( create_icon( self ) ); draw_header( self, "" ); @@ -1322,7 +1353,8 @@ def init_vars( self, file_ini ): vars["bd_server_ip" ] = "localhost"; vars["bd_server_socket" ] = "21567"; vars["uut_name" ] = "UUT"; - vars["sump_addr" ] = "00000090" ;# Addr of sump2_ctrl_reg +# vars["sump_addr" ] = "00000090" ;# Addr of sump2_ctrl_reg + vars["sump_addr" ] = "00000010" ;# Addr of sump2_ctrl_reg vars["sump_script_inc_filter"] = "*.txt"; vars["sump_script_exc_filter"] = "sump2_*.txt"; vars["sump_trigger_type" ] = "or_rising"; @@ -1725,9 +1757,18 @@ def proc_cmd( self, cmd, parms ): elif ( cmd == "bd_shell" ): bd_shell(self, cmd_start ="" ); - elif ( cmd == "quit" or cmd == "exit" ): + elif ( cmd == "load_fpga" ): + load_fpga(self); + + elif ( cmd=="quit" or cmd=="exit" or cmd=="shutdown" or cmd=="reboot" ): self.done=True; - shutdown( self ); + if ( cmd == "quit" or cmd == "exit" ): + shutdown( self, False,False );# Normal App exit to command line + else: + if ( cmd == "reboot" ): + shutdown( self, True, True );# Pi System Shutdown with Reboot + else: + shutdown( self, True, False );# Pi System Shutdown elif ( cmd == "sump_connect" ): sump_connect(self); @@ -1985,6 +2026,7 @@ def proc_cmd( self, cmd, parms ): draw_header( self,"save_bmp() : Saved " + filename ); self.last_filesave = filename; elif ( cmd == "save_png" ): + # Warning - on Pi red and green are swapped on PNG saves screen_erase( self ); draw_screen( self ); screen_flip( self ); @@ -1993,6 +2035,31 @@ def proc_cmd( self, cmd, parms ): draw_header( self,"save_png() : Saved " + filename ); self.last_filesave = filename; +# # Workaround for Pi Pygame bug swapping Red and Green on PNG save +# # This requires PIL - not part of PyGame or Raspbian +# filename = make_unique_filename( self, "sump2_", ".bmp" ); +# self.pygame.image.save( self.screen, filename ); +# from PIL import Image; +# im = Image.open( filename ); +# filename = make_unique_filename( self, "sump2_", ".png" ); +# im.save( filename ) ; +# draw_header( self,"save_png() : Saved " + filename ); +# self.last_filesave = filename; + + # Workaround for Pi Pygame bug swapping Red and Green on PNG save + # This works, but takes a minute on just a 640x480 display +# img = self.pygame.surfarray.array3d( self.screen ) +# img_copy = self.pygame.surfarray.array3d( self.screen ) +# for y in range(0, self.screen_height ): +# for x in range(0, self.screen_width ): +# img_copy[x][y][0] = img[x][y][1]; +# img_copy[x][y][1] = img[x][y][0]; +# surf = self.pygame.surfarray.make_surface(img_copy); +# filename = make_unique_filename( self, "sump2_", ".png" ); +# self.pygame.image.save( surf , filename ); +# draw_header( self,"save_png() : Saved " + filename ); +# self.last_filesave = filename; + elif ( cmd == "font_larger" or cmd == "font_smaller" ): size = int( self.vars["font_size"] ); if ( cmd == "font_larger" ): @@ -2850,8 +2917,18 @@ def draw_header( self, txt ): if ( self.fatal_msg != None ): uut_name = "DEMO Mode :"; txt = self.fatal_msg; - self.pygame.display.set_caption( \ - "SUMP2 " + self.vers + " (c) 2016 BlackMesaLabs : "+uut_name+" "+txt); + msg ="SUMP2 " + self.vers + " (c) 2016 BlackMesaLabs : "+uut_name+" "+txt; + self.pygame.display.set_caption( msg ); + + # Pi display header at top of surface since full screen + msg += " ";# Erase old text that was wider + txt = self.font.render( msg , True, self.color_fg, self.color_bg ); + y1 = self.txt_height // 2; # Gap from top border + x1 = self.txt_width; # Gap from left border + self.screen.blit(txt, (x1,y1 )); + screen_flip( self ); + + #HERE99 if ( self.gui_active == True ): import pygame; pygame.event.get();# Avoid "( Not Responding )" @@ -3070,41 +3147,10 @@ def get_popup_sel( self ): # Find a monospaced font to use def get_font( self , font_name, font_height ): log( self, ["get_font() " + font_name ] ); -# print "get_font()"; - import fnmatch; -# font_name = "khmerossystem"; -# font_name = "dejavusansmono"; - font_height = int( font_height, 10 ); # Conv String to Int - font_list = self.pygame.font.get_fonts(); # List of all fonts on System - self.font_list = []; - for each in font_list: - log( self, ["get_font() : Located Font = " + each ] ); - # Make a list of fonts that might work based on their name - if ( ( "mono" in each.lower() ) or - ( "courier" in each.lower() ) or - ( "fixed" in each.lower() ) ): - self.font_list.append( each ); - - if ( font_name == None or font_name == "" ): - font_list = self.pygame.font.get_fonts(); # List of all fonts on System - for each in font_list: - log( self, ["get_font() : Located Font = " + each ] ); - ends_with_mono_list = fnmatch.filter(font_list,"*mono"); - if ends_with_mono_list : - font_name = ends_with_mono_list[0];# Take 1st one -# log( self, ["get_font() : Using Font = " + font_name ] ); - else: - font_name = self.font_list[0]; # Take 1st one -# log( self, ["get_font() : Using Font = " + font_name ] ); - try: - font = self.pygame.font.SysFont( font_name , font_height ); -# log( self, ["get_font() : Using Font = " + font_name ] ); - except: - font = self.pygame.font.Font( None , font_height );# Default Pygame Font -# log( self, ["get_font() : Using Default Font"] ); - - # Calculate Width and Height of font for future reference -# txt = font.render("X",True, ( 255,255,255 ) ); + # Pi Specific +# font_size = 18; + font_size = int(font_height); + font = self.pygame.font.Font('freesansbold.ttf', font_size ); txt = font.render("4",True, ( 255,255,255 ) ); self.txt_width = txt.get_width(); self.txt_height = txt.get_height(); @@ -3167,7 +3213,8 @@ def draw_screen( self ): # 1st Display the Net Names # y = self.txt_height / 2; # Gap from top border - y = self.txt_height // 2; # Gap from top border +# y = self.txt_height // 2; # Gap from top border + y = self.txt_height * 2; # Pi top header x = self.txt_width; # Gap from left border self.sig_name_start_x = x; self.sig_name_start_y = y; @@ -3361,6 +3408,8 @@ def draw_screen( self ): None; else: # print("Rendering samples."); + import time; + start_time = time.time(); surface.fill( self.color_bg ); if ( self.debug ): print( "value_surface_valid==False"); @@ -3557,6 +3606,9 @@ def draw_screen( self ): txt = "Fast Rendering Complete"; else: txt = "Full Rendering Complete"; + stop_time = time.time(); + total_time = stop_time - start_time; + print("Render Time = %d" % total_time ); draw_header( self, txt ); x = self.sig_value_start_x; @@ -3702,7 +3754,7 @@ def draw_screen( self ): # self.screen.blit(txt1, ( x, y1 ) ); # self.screen.blit(txt2, ( x, y2 ) ); # self.screen.blit(txt3, ( x, y2 ) ); -# print (str(self.max_samples));# HERE13 +# print (str(self.max_samples));# y = self.screen_height - int(self.txt_height * 1.5 ); x = self.sig_name_start_x; @@ -4288,14 +4340,19 @@ def list_remove( my_list, item ): # Establish connection to Sump2 hardware def sump_connect( self ): log( self, ["sump_connect()"] ); - self.bd=Backdoor( self.vars["bd_server_ip"], - int( self.vars["bd_server_socket"], 10 ) );# Note dec - if ( self.bd.sock == None ): - txt = "ERROR: Unable to locate BD_SERVER"; - self.fatal_msg = txt; - print( txt ); - log( self, [ txt ] ); - return False; +# self.bd=Backdoor( self.vars["bd_server_ip"], +# int( self.vars["bd_server_socket"], 10 ) );# Note dec +# if ( self.bd.sock == None ): +# txt = "ERROR: Unable to locate BD_SERVER"; +# self.fatal_msg = txt; +# print( txt ); +# log( self, [ txt ] ); +# return False; + self.spi_port = spidev.SpiDev(); + self.spi_port.open(0,1);# Note: icoboard uses CE0 for Mach, CE1 for Ice + self.spi_port.max_speed_hz = 32000000;# 1-32 MHz typical + self.mb = mesa_bus( self.spi_port);# Establish a MesaBus link + self.bd = local_bus( self.mb, False );# Establish a LocalBus link self.sump = Sump2( self.bd, int( self.vars["sump_addr"],16 ) ); self.sump.rd_cfg();# populate sump.cfg_dict[] with HW Configuration @@ -4540,7 +4597,7 @@ def sump_dump_data( self ): events = ram_bytes * 8;# Example, 32 events total for 4 ram_bytes self.dwords_start = 0; self.dwords_stop = ram_phys; - + start_time = time.time(); # Event Signals rd_page = 0; dump_data = sump_dump_var_ram(self,rd_page = rd_page ); @@ -4587,6 +4644,9 @@ def sump_dump_data( self ): my_signal.bit_bot = 0; for j in range( 0, ram_len, 1): my_signal.values.append( "%08x" % dump_data[j] ); + stop_time = time.time(); + total_time = stop_time - start_time; + print("Download Time = %d" % total_time ); sump_bundle_data( self ); recalc_max_samples( self ); @@ -4693,6 +4753,7 @@ def sump_dump_rle_data( self ): list2file( self, "sump2_rle_dump.txt", rle_hex_list ); print("expand_rle()"); + start_time = time.time(); (dump_data,trig_i) = expand_rle( self, start_t,stop_t,pre_trig,post_trig ); # print( len( dump_data ) ); @@ -4807,6 +4868,9 @@ def sump_dump_rle_data( self ): sump_bundle_data( self ); recalc_max_samples( self ); + stop_time = time.time(); + total_time = stop_time - start_time; + print("RLE Decompress Time = %d" % total_time ); return trig_i; def rle_undersample_signal( self, undersample_rate, my_signal ): @@ -5473,15 +5537,32 @@ def vcdfile2signal_list( self, file_name ): draw_header( self, "" ); return; -def shutdown( self ): + +def load_fpga( self ): + log( self, ["load_fpga()"]); + draw_header( self, "load_fpga()" ); + os.system("sudo ./icoprog -f < top_bitmap.bin");# Program IcoBoard Flash + os.system("sudo ./icoprog -b"); + draw_header( self, "Please reboot and restart SUMP2.py application" ); + + +def shutdown( self, system_shutdown = False, system_reboot = False ): log( self, ["shutdown()"]); var_dump( self, "sump2.ini" ); # Dump all variable to INI file proc_cmd( self, "save_format", [""] ); # Autosave the last format + if ( system_shutdown == True and system_reboot == False ): + draw_header( self, "Linux Shutdown initiated" ); + os.system("sudo shutdown -h now");# Pi Shutdown ( Halt ) + if ( system_shutdown == True and system_reboot == True ): + draw_header( self, "Linux Reboot initiated" ); + os.system("sudo shutdown -r now");# Pi Reboot + if ( self.mode_cli == False ): self.pygame.quit();# Be IDLE friendly print(""); print("Thank you for using SUMP2 " + self.vers + " by BlackMesaLabs"); print("Please encourage the development and use of open-source software"); + sys.exit(); return; @@ -5695,7 +5776,11 @@ def init_globals( self ): "Save_TXT","Save_VCD","Save_Rename"], # ["Fonts","Font_Larger","Font_Smaller"], ["Misc","Font_Larger","Font_Smaller", - "BD_SHELL","Manual"],"Quit"]; + "BD_SHELL","Manual"], +# "Quit"]; + ["System","Quit","Shutdown","Reboot","Load_FPGA"]];# Pi + + self.popup_list = self.popup_list_values; self.cmd_alias_hash_dict = {}; self.cmd_alias_hash_dict["zi"] = "zoom_in"; @@ -5774,6 +5859,8 @@ class signal(object): class Sump2: def __init__ ( self, backdoor, addr ): self.bd = backdoor; + + self.addr_ctrl = addr; self.addr_data = addr + 0x4; self.cmd_state_idle = 0x00; @@ -6087,6 +6174,144 @@ class Backdoor: payload += bin_data.decode("utf-8");# ByteArray to String return payload; + ############################################################################### -main = main(); +# Routines for Reading and Writing a remote 32bit LocalBus over MesaBus +# A local bus cycle is a pre-determined payload transported over the MesaBus +# LocalBus is mapped to SubSlot 0x0 +# 0x0 : Write DWORD or Burst starting at Address +# 0x1 : Read DWORD or Burst starting at Address +# 0x2 : Write Multiple DWORDs to same Address +# 0x3 : Read Multiple DWORDs from same Address +class local_bus: + def __init__ ( self, mesa_bus, dbg_flag ): + self.mesa_bus = mesa_bus; + self.dbg_flag = dbg_flag; + + def wr( self, addr, data, repeat = False ): + # LocalBus WR cycle is a Addr+Data 8byte payload + # Mesabus has maximum payload of 255 bytes, or 63 DWORDs. + # 1 DWORD is LB Addr, leaving 62 DWORDs available for data bursts + # if data is more than 62 dwords, parse it into multiple bursts + each_addr = addr; + data_list = data; + while ( len( data_list ) > 0 ): +# if ( len( data_list ) > 62 ): +# data_payload = data_list[0:62]; +# data_list = data_list[62:]; +# else: +# data_payload = data_list[0:]; +# data_list = []; + # Note: MesaBus on icoboard doesn't support bursts writes yet. + if ( len( data_list ) > 1 ): + data_payload = data_list[0:1]; + data_list = data_list[1:]; + else: + data_payload = data_list[0:]; + data_list = []; + + payload = ( "%08x" % each_addr ); + for each_data in data_payload: + payload += ( "%08x" % each_data ); + each_addr +=4; +# print(payload); + self.mesa_bus.wr( 0x00, 0x0, 0x0, payload ); + return; + + + def rd( self, addr, num_dwords = 1, repeat = False ): + dwords_remaining = num_dwords; + each_addr = addr; + rts = []; + rts_dword = "00000000"; + while ( dwords_remaining > 0 ): + # MesaBus has 255 byte limit on payload, so split up large reads +# if ( dwords_remaining > 62 ): +# n_dwords = 62; +# dwords_remaining -= 62; +# else: +# n_dwords = dwords_remaining; +# dwords_remaining = 0; + + # Note: MesaBus on icoboard doesn't support bursts or repeat reads yet. + if ( dwords_remaining > 1 ): + n_dwords = 1; + dwords_remaining -= 1; + else: + n_dwords = dwords_remaining; + dwords_remaining = 0; + + # LocalBus RD cycle is a Addr+Len 8byte payload to 0x00,0x0,0x1 + payload = ( "%08x" % each_addr ) + ( "%08x" % n_dwords ); + self.mesa_bus.wr( 0x00, 0x0, 0x1, payload ); + rts_mesa = self.mesa_bus.rd(); + # The Mesa Readback Ro packet resembles a Wi Write packet from slot 0xFE + # This is to support a synchronous bus that clocks 0xFFs for idle + # This only handles single DWORD reads and checks for: + # "F0FE0004"+"12345678" + "\n" + # "04" is num payload bytes and "12345678" is the read payload + if ( len( rts_mesa ) > 8 ): + rts_str = rts_mesa[8:];# Strip the FOFE0004 header + while ( len( rts_str ) >= 8 ): + rts_dword = rts_str[0:8]; + rts_str = rts_str[8:]; + try: + rts += [ int( rts_dword, 16 ) ]; + except: +# print("ERROR:Invalid LocalBus Read "+rts_mesa+" "+rts_dword); + if ( self.dbg_flag == "debug" ): + sys.exit(); + rts += [ 0x00000000 ]; + else: +# print("ERROR: Invalid LocalBus Read " + rts_mesa + " " + rts_dword); + if ( self.dbg_flag == "debug" ): + sys.exit(); + rts += [ 0x00000000 ]; + if ( repeat == False ): + each_addr += ( 4 * n_dwords );# Note dword to byte addr translation +# print( rts ); + return rts; + + +############################################################################### +# Routines for Reading and Writing Payloads over MesaBus +# A payload is a series of bytes in hexadecimal string format. A typical use +# for MesaBus is to transport a higher level Local Bus protocol for 32bit +# writes and reads. MesaBus is lower level and transports payloads to a +# specific device on a serial chained bus based on the Slot Number. +# More info at : https://blackmesalabs.wordpress.com/2016/03/04/mesa-bus/ +class mesa_bus: + def __init__ ( self, port ): + self.port = port; + + def wr( self, slot, subslot, cmd, payload ): + preamble = "FFF0"; + slot = "%02x" % slot; + subslot = "%01x" % subslot; + cmd = "%01x" % cmd; + num_bytes = "%02x" % ( len( payload ) / 2 ); + mesa_str = preamble + slot + subslot + cmd + num_bytes + payload; + if ( type( self.port ) == spidev.SpiDev ): + mesa_hex_list = []; + for i in range( 0, len(mesa_str)/2 ): + mesa_hex = int(mesa_str[i*2:i*2+2],16); + mesa_hex_list += [mesa_hex]; + rts = self.port.xfer2( mesa_hex_list ); + else: + self.port.wr( mesa_str ); + return; + def rd( self ): + if ( type( self.port ) == spidev.SpiDev ): + hex_str = ""; + rts = self.port.xfer2(8*[0xFF]); + for each in rts: + hex_str += ("%02x" % each ); + rts = hex_str; + else: + rts = self.port.rd(); + return rts; + + +############################################################################### +main = main();