# 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
# 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:
# 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;
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;
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' );
# 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);
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;
# 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 ) ] );
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.
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)] );
# draw_screen( self );
# screen_flip( self );
- shutdown( self );
+ shutdown( self, False, False );
return;# This is end of main program loop
###############################################################################
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, "" );
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";
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);
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 );
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" ):
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 )"
# 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();
# 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;
None;
else:
# print("Rendering samples.");
+ import time;
+ start_time = time.time();
surface.fill( self.color_bg );
if ( self.debug ):
print( "value_surface_valid==False");
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;
# 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;
# 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
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 );
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 );
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 ) );
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 ):
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;
"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";
class Sump2:
def __init__ ( self, backdoor, addr ):
self.bd = backdoor;
+
+
self.addr_ctrl = addr;
self.addr_data = addr + 0x4;
self.cmd_state_idle = 0x00;
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();