Skip to content

Instantly share code, notes, and snippets.

@cutaway
Last active April 27, 2022 13:14
Show Gist options
  • Save cutaway/7b661686be95117767a9 to your computer and use it in GitHub Desktop.
Save cutaway/7b661686be95117767a9 to your computer and use it in GitHub Desktop.
Script to interact with SPI EEPROM memory components using the BusPirate via pyBusPirateLite
#!/usr/bin/env python
# encoding: utf-8
"""
Adapted from spi_test.py by Sean Nelson on 2009-10-14.
Modified by Don C. Weber (cutaway) and InGuardians, Inc. 20141015
This file is part of pyBusPirate.
pyBusPirate 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.
pyBusPirate 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 pyBusPirate. If not, see <http://www.gnu.org/licenses/>.
"""
from serial.serialutil import SerialException
import sys, optparse, struct, time
from pyBusPirateLite.SPI import *
speed = [\
SPISpeed._30KHZ,\
SPISpeed._125KHZ,\
SPISpeed._250KHZ,\
SPISpeed._1MHZ,\
SPISpeed._2MHZ,\
SPISpeed._2_6MHZ,\
SPISpeed._4MHZ,\
SPISpeed._8MHZ\
]
speed_names = [\
"30 KHZ",\
"125 KHZ",\
"250 KHZ",\
"1 MHZ",\
"2 MHZ",\
"26 MHZ",\
"4 MHZ",\
"8 MHZ"\
]
def read_list_data(size):
data = []
for i in range(size+1):
data.append(0)
return data
# NOTE: bulk_trans is suppose to allow the writing of 16 bytes
# NOTE: but it only takes 4 bytes before it errors out and locks up the BusPirate.
#def blocks(data,size=16):
def blocks(data,size=4):
tmp = []
for e in range(0,len(data),size):
tmp.append([ord(d) for d in data[e:e+size]])
return tmp
def parse_prog_args():
parser = optparse.OptionParser(usage="%prog [options]",
version="%prog 1.1")
# Setup Parameters
parser.add_option("-s", "--size",
action="store", type=int, dest="flash_size", default=8192,
help="Size of Flashchip in bytes")
parser.add_option("-d", "--dev",
action="store", dest="dev_name", default="/dev/ttyUSB0",
help="The comm device to connect to. Example: -d /dev/ttyUSB0", type="string")
parser.add_option("-b", "--bulk_size",
action="store", dest="bsize", type=int, default=4,
help="Number of bytes to write during one bulk_trans. Default: 4. NOTE: Not EEPROM page or block size.")
parser.add_option("-S", "--spi_speed",
action="store", type=int, dest="spi_speed", default=5,
help="Set speed of SPI communications.\n0=30KHZ, 1=125KHZ, 2=250KHZ, 3=1MHZ, 4=2MHZ, 5=2_6MHZ, 6=4MHZ, 7=8MHZ")
# Actions to perform
parser.set_defaults(command="test")
parser.add_option("-t", "--test",
action="store_const", dest="command", const="test",
help="Test reads data from memory address 0.")
parser.add_option("-r", "--read",
action="store_const", dest="command", const="read",
help="read from SPI to standard out. Reads one word at a time.")
parser.add_option("-R", "--fread",
action="store_const", dest="command", const="fread",
help="read from SPI to the file destination provided.")
parser.add_option("-w", "--write",
action="store_const", dest="command", const="write",
help="write data to SPI. Example: -w \"Python and BusPirate\"")
parser.add_option("-W", "--fwrite",
action="store_const", dest="command", const="fwrite",
help="write from file to SPI. Not implemented, yet.")
parser.add_option("-e", "--erase",
action="store_const", dest="command", const="erase",
help="erase SPI. Not implemented, yet.")
parser.add_option("-i", "--id",
action="store_const", dest="command", const="id",
help="print Chip ID. Not implemented, yet.")
# Data for actions
parser.add_option("-a", "--address",
action="store", dest="address", type=int, default=0,
help="address from which to read or write.")
parser.add_option("-n", "--nbytes",
action="store", dest="nbytes", type=int, default=2,
help="number of bytes to read.")
parser.add_option("-f", "--file",
dest="dfile", action="store_true", default="/tmp/bp_file.dat",
help="File to process Default is /tmp/bp_file.dat. Example: -f /tmp/bp_file.dat")
parser.add_option("-X", "--string",
dest="dstring", action="store_true", default=False,
help="user input for writing are string values. Default is string. Example: -w \"Bus Pirate\" -H")
parser.add_option("-D", "--debug",
dest="DEBUG", action="store_true", default=False,
help="Debug mode to print debug information about SPI transations.")
(options, args) = parser.parse_args()
if options.DEBUG: print "Options:",options
if options.DEBUG: print "Args:",args
return (options, args)
'''
if options.command == "id" or options.command == "test" or options.command == "status":
return (options, args)
elif len(args) != 1:
parser.print_help()
print options
sys.exit(1)
else:
return (options, args)
'''
""" enter binary mode """
if __name__ == '__main__':
data = ""
(opt, args) = parse_prog_args()
if opt.command == "fread":
f=open(opt.dfile, 'wb')
elif opt.command == "fwrite":
f=open(opt.dfile, 'rb')
try:
#NOTE: Leave USB speed at max because it never really changes when using the BusPirate.
spi = SPI(opt.dev_name, 115200)
except SerialException as ex:
print ex
sys.exit();
print "Entering binmode: ",
if spi.BBmode():
print "OK."
else:
print "failed."
sys.exit()
print "Entering raw SPI mode: ",
if spi.enter_SPI():
print "OK."
else:
print "failed."
sys.exit()
print "Configuring SPI peripherals: ",
if spi.cfg_pins(PinCfg.POWER | PinCfg.CS | PinCfg.AUX):
print "OK."
else:
print "failed."
sys.exit()
print "Configuring SPI speed: ",
if spi.set_speed(speed[opt.spi_speed]):
print "OK."
if opt.DEBUG: print "SPI Speed set to:",speed_names[opt.spi_speed]
else:
print "failed."
sys.exit()
print "Configuring SPI mode configuration: ",
if spi.cfg_spi(SPICfg.CLK_EDGE | SPICfg.OUT_TYPE):
print "OK."
else:
print "failed."
sys.exit()
spi.timeout(0.2)
if opt.command == "test":
print "Testing EEPROM."
# Read Status Register
spi.CS_Low()
spi.bulk_trans(1, [0x5])
data = spi.bulk_trans(2, read_list_data(16))
print "Status Register:",data.encode('hex')
spi.CS_High()
# Read first eight bytes
print "Reading First 8 Bytes."
spi.CS_Low()
spi.bulk_trans(3, [0x3, 0, 0])
for e in range(8):
data = spi.bulk_trans(1, read_list_data(8))
print "Byte Data", str(e)+":",data.encode('hex')
spi.CS_High()
# Read first eight words
print "Reading First 8 Words."
spi.CS_Low()
spi.bulk_trans(3, [0x3, 0, 0])
for e in range(8):
data = spi.bulk_trans(2, read_list_data(16))
print "Word Data", str(e)+":",data.encode('hex')
spi.CS_High()
elif opt.command == "read":
print "Reading EEPROM."
spi.CS_Low()
# NOTE: The address is sent in two parts. Some chips have four (4) address bytes. So this may require adjustments.
high_addr = opt.address >> 8
low_addr = opt.address & 0xff
spi.bulk_trans(3, [0x3, high_addr, low_addr])
for i in range(opt.address,opt.nbytes + opt.address,2):
data = spi.bulk_trans(2, read_list_data(2))
print "0x{0:0>5}:".format(i),data.encode('hex')
spi.CS_High()
elif opt.command == "fread":
print "Reading EEPROM."
# Using opt.bsize because transfers can be unstable at larger sizes
spi.CS_Low()
spi.bulk_trans(3, [0x3, 0, 0])
for i in range(0,opt.flash_size,opt.bsize):
#spi.CS_Low()
data = spi.bulk_trans(opt.bsize, read_list_data(opt.bsize))
if opt.DEBUG: print "0x{0:0>5}:".format(opt.address+i),data.encode('hex')
f.write(data)
# Leave CS HIGH while reading
spi.CS_High()
elif opt.command == "write":
# NOTES: AUX pin needs to be toggled to disable/enable Write Protect but this SPI module does not control AUX.
# NOTES: Therefore AUX needs to be toggled using spi.cfg_pins which requires we manage all pins set HIGH.
# NOTES: The write commands need to be managed closely. Therefore cycling CS high/low after every command is necessary.
print "Writing user data to EEPROM at:",hex(opt.address)
#spi.AUX_Low() # This function should be defined but we need to handle it ourselves
# We have to unset the AUX to bring it low. Remember, CS has already been brought low so leave it unset.
if spi.cfg_pins(PinCfg.POWER):
if opt.DEBUG: print "AUX Low OK."
else:
if opt.DEBUG: print "AUX Low failed."
sys.exit()
# Write incoming data
addr = opt.address
if opt.dstring:
indata = blocks(args[0],opt.bsize)
else:
indata = blocks(args[0].decode('hex'),opt.bsize)
for e in indata:
high_addr = addr >> 8
low_addr = addr & 0xff
whead = [0x2, high_addr, low_addr]
dlen = len(e)
# WREN must be sent before every write. CS must be cycled to get it set.
spi.CS_Low()
spi.bulk_trans(1, [0x6])
spi.CS_High()
spi.CS_Low()
if opt.DEBUG: print "Sending:",(3 + len(e), whead + e)
spi.bulk_trans(3 + len(e), whead + e)
spi.CS_High()
addr += opt.bsize
# Disable writing
spi.CS_Low()
spi.bulk_trans(1, [0x4])
spi.CS_High()
#spi.AUX_High() # This function should be defined but we need to handle it ourselves
# We have to set the AUX to bring it high. Remember, CS has already been brought HIGH so we have to preserve it.
if spi.cfg_pins(PinCfg.POWER | PinCfg.CS | PinCfg.AUX):
if opt.DEBUG: print "AUX High OK."
else:
if opt.DEBUG: print "AUX High failed."
elif opt.command == "fwrite":
print "File writing is not implemented, yet."
pass
'''
print "Writing EEPROM."
spi.CS_Low()
# Enable writing
spi.bulk_trans(2, [0x1, 2])
spi.bulk_trans(1, [0x6])
spi.bulk_trans(1, [0x5])
data = spi.bulk_trans(2, read_list_data(16))
print "Status Register:",data.encode('hex')
spi.CS_High()
spi.CS_Low()
spi.bulk_trans(4, [0xA, 0, 0, 0])
for i in range((int(opt.flash_size)/16)):
spi.bulk_trans(16, None)
spi.CS_High()
'''
elif opt.command == "id":
# NOTE: This will be chip dependent and not implemented on all EEPROM
print "Chip ID is not implemented, yet."
pass
'''
print "Reading Chip ID: ",
spi.CS_Low()
d = spi.bulk_trans(4, [0x9F, 0, 0, 0])
spi.CS_High()
print "d:",d
for each in d[1:]:
print "%02X " % ord(each),
print
'''
elif opt.command == "erase":
print "Erase is not implemented, yet."
pass
print "Reset Bus Pirate to user terminal: ",
if opt.command == "fread" or opt.command == "fwrite":
f.close()
if spi.resetBP():
print "OK."
else:
print "failed."
sys.exit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment