From 52cbfe785eb2d30bb74b92f6d38a01483799d7b0 Mon Sep 17 00:00:00 2001 From: Kevin Hubbard Date: Mon, 26 Dec 2016 13:40:07 +0100 Subject: [PATCH] https://blackmesalabs.wordpress.com/2016/12/22/sump2-100-msps-32bit-logic-analyzer-for-icoboardraspberrypi/ --- ico_gpio.py | 312 +++++++++++++++++++++++++++++++++++++++++++ start_app.sh | 1 + sump2.py | 367 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 609 insertions(+), 71 deletions(-) create mode 100755 ico_gpio.py create mode 100644 start_app.sh diff --git a/ico_gpio.py b/ico_gpio.py new file mode 100755 index 0000000..c80fc58 --- /dev/null +++ b/ico_gpio.py @@ -0,0 +1,312 @@ +############################################################################## +# (C) Copyright 2016 Kevin M. Hubbard, Black Mesa Labs +# 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# 11.25.2016 : Kevin M. Hubbard created +# +# Installing spidev for RaspberryPi for SPI communications +# 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 +############################################################################## +#import os.path; +#from pygame.locals import * +#import array; +#import random; + +import time; +import sys; +import datetime; +from time import sleep; +import spidev; + +class App (): + def __init__(self): + return; + + def main(self): + self.main_init(); + self.main_loop(); + + def main_init( self ): + args = sys.argv + [None]*5;# args[0] is this scripts name + self.dbg_flag = args[1];# ie debug or display or pi etc + + # use SPI for MesaBus instead of UART + self.spi_port = spidev.SpiDev(); + self.spi_port.open(0,1);# Note: icoboard uses CE0 for Mach, CE1 for Ice + # Note: SPI rate works up to 32 MHz, but the CS_L time is software + # controlled and the gap between 2 byte cycles is always about 300us + # Low Level SPI Timing + # 1 MHz : Write = 200uS, Read = 127uS + # 32 MHz : Write = 20uS, Read = 12uS + # LocalBus Timing + # Write : 800uS ( two back to back ) + # Read : 400uS + self.spi_port.max_speed_hz = 32000000;# 1-32 MHz typical + self.mb = mesa_bus( self.spi_port);# Establish a MesaBus link + self.lb = local_bus( self.mb, self.dbg_flag );# Establish a LocalBus link + self.define_reg_space(); + + +# print("Sleeping 5sec to allow for SUMP2 Arm"); +# sleep(5.0); +# # On PMOD P2, drive a 8bit binary counter to LEDs +# for i in range( 0,256,1 ): +# self.lb.wr( self.reg_pmod_p2_ctrl, [ i ] ); +# data = self.lb.rd( self.reg_pmod_p2_ctrl, 1 )[0]; +# sleep(0.02); +# self.lb.wr( self.reg_pmod_p2_ctrl, [0x55] ); +# print("READ %08X" % data ); +# sys.exit(); + + # Read MesaBus ID information from this FPGA + ( id_mfr, id_dev, id_snum, id_timestamp, english_time ) = self.mesa_id(); + print( id_mfr ); + print( id_dev ); + print( id_snum ); + print( id_timestamp ); + print( english_time ); + + # Read some registers + data = self.lb.rd( 0x00000000, 1 )[0]; + print("READ %08X" % data ); + data = self.lb.rd( 0x00000004, 1 )[0]; + print("READ %08X" % data ); + data = self.lb.rd( 0x00000008, 1 )[0]; + print("READ %08X" % data ); + + # Configure GPIO Pins on the 4 PMOD connectors P1,P2,P3,P4 + # Note: see source/reg_space.txt for bitfield definitions + self.lb.wr( self.reg_pmod_p1_cfg , [ 0x77777777 ] );# Watch SPI Traffic +# self.lb.wr( self.reg_pmod_p2_cfg , [ 0x11111111 ] ); +# self.lb.wr( self.reg_pmod_p3_cfg , [ 0x11111111 ] ); +# self.lb.wr( self.reg_pmod_p4_cfg , [ 0x11111111 ] ); +# sys.exit(); + +# self.lb.wr( self.reg_pmod_p1_ctrl, [ 0x00000001 ] ); +# self.lb.wr( self.reg_pmod_p2_ctrl, [ 0x00000003 ] ); +# self.lb.wr( self.reg_pmod_p3_ctrl, [ 0x00000007 ] ); +# self.lb.wr( self.reg_pmod_p4_ctrl, [ 0x0000000f ] ); + +# self.lb.wr( self.reg_pmod_p1_cfg , [ 0x00000a98 ] );# PWM0,1 and 2 + self.lb.wr( self.reg_pmod_p2_cfg , [ 0xfedcba98 ] );# PWM0,1 and 2 + self.lb.wr( self.reg_pmod_p3_cfg , [ 0xfedcba98 ] );# PWM0,1 and 2 + self.lb.wr( self.reg_pmod_p4_cfg , [ 0xfedcba98 ] );# PWM0,1 and 2 + self.lb.wr( self.reg_pwm0_cfg, [ 0x01001013 ] ); + self.lb.wr( self.reg_pwm1_cfg, [ 0x01002013 ] ); + self.lb.wr( self.reg_pwm2_cfg, [ 0x01004013 ] ); + self.lb.wr( self.reg_pwm3_cfg, [ 0x01008013 ] ); + self.lb.wr( self.reg_pwm4_cfg, [ 0x01001023 ] ); + self.lb.wr( self.reg_pwm5_cfg, [ 0x02001023 ] ); + self.lb.wr( self.reg_pwm6_cfg, [ 0x04001023 ] ); + self.lb.wr( self.reg_pwm7_cfg, [ 0x08001023 ] ); + + # On PMOD P2, drive a 8bit binary counter to LEDs +# for i in range( 0,256,1 ): +# self.lb.wr( self.reg_pmod_p2_ctrl, [ i ] ); +# sleep(0.02); +# self.lb.wr( self.reg_pmod_p2_ctrl, [0x55] ); + sys.exit(); + +# if ( self.platform == "pi" ): +# import os; +# os.system("sudo shutdown -h now"); + + def main_loop( self ): + self.test = False; + while (True): + sleep(1); + print("Looping"); + + def mesa_id( self ): + # Issue a low level MesaBus ID Request and make sure FPGA responds + try: + self.mb.wr( slot = 0x00, subslot = 0xF, cmd = 0xA, payload = "" ); + rts = self.mb.port.xfer2(20*[0xFF]); + hex_str = ""; + for each in rts: + hex_str += ("%02x" % each ); + rts = hex_str; + if ( len( rts ) == 40 ): + mesa_header = rts[0:8]; + id_mfr = rts[8:16]; + id_dev = rts[16:24]; + id_snum = rts[24:32]; + id_time = rts[32:40]; + english_time = datetime.datetime.fromtimestamp(int( \ + id_time,16)).strftime('%Y-%m-%d %H:%M:%S'); + return ( id_mfr, id_dev, id_snum, id_time,english_time ); + else: + return False; + except: + return False; + + def define_reg_space(self): + self.reg_id = 0x00000000; + self.reg_version = 0x00000004; + self.reg_timestamp = 0x00000008; + self.reg_chip_ctrl = 0x0000000c; + self.reg_sump2_ctrl = 0x00000010; + self.reg_sump2_data = 0x00000014; + self.reg_pmod_p1_cfg = 0x00000020; + self.reg_pmod_p2_cfg = 0x00000024; + self.reg_pmod_p3_cfg = 0x00000028; + self.reg_pmod_p4_cfg = 0x0000002c; + self.reg_pmod_p1_ctrl = 0x00000030; + self.reg_pmod_p2_ctrl = 0x00000034; + self.reg_pmod_p3_ctrl = 0x00000038; + self.reg_pmod_p4_ctrl = 0x0000003c; + self.reg_pwm0_cfg = 0x00000080; + self.reg_pwm1_cfg = 0x00000084; + self.reg_pwm2_cfg = 0x00000088; + self.reg_pwm3_cfg = 0x0000008c; + self.reg_pwm4_cfg = 0x00000090; + self.reg_pwm5_cfg = 0x00000094; + self.reg_pwm6_cfg = 0x00000098; + self.reg_pwm7_cfg = 0x0000009c; + return; + + def quit(self): + pygame.display.quit(); + + +############################################################################### +# 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 ): + # 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 = []; + 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 ): + dwords_remaining = num_dwords; + each_addr = addr; + rts = []; + rts_dword = "00000000"; + while ( dwords_remaining > 0 ): + if ( dwords_remaining > 62 ): + n_dwords = 62; + dwords_remaining -= 62; + 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 += [ 0xdeadbeef ]; + rts += [ 0x00000000 ]; + else: +# print("ERROR: Invalid LocalBus Read " + rts_mesa + " " + rts_dword); + if ( self.dbg_flag == "debug" ): + sys.exit(); +# rts += [ 0xdeadbeef ]; + rts += [ 0x00000000 ]; + each_addr += ( 4 * n_dwords ); + 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; +# print( mesa_str ); + 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]; +# print("%02x" % mesa_hex ); + rts = self.port.xfer2( mesa_hex_list ); +# print( rts); + 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; +# print rts; + else: + rts = self.port.rd(); +# print( rts ); + return rts; + +############################################################################### +app = App(); +app.main(); diff --git a/start_app.sh b/start_app.sh new file mode 100644 index 0000000..53a9b7f --- /dev/null +++ b/start_app.sh @@ -0,0 +1 @@ +python sump2.py 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(); -- 2.20.1