Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add applet for programming/reading TI/Chipcon 8051 based radios: CC111x, CC251x, CC253x, & CC254x #185

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a471e15
applet.program.chipcon: add support for reading & writing TI/chipcon …
samlittlewood Feb 18, 2020
7145721
Use globs for imports.
samlittlewood Feb 20, 2020
2b2b723
Reduce write block size to fit in smallest parts RAM.
samlittlewood Feb 20, 2020
a045a4e
Remove debug detritus - 'status' command.
samlittlewood Feb 20, 2020
e05858d
Remove speculative comments.
samlittlewood Feb 20, 2020
30a4ff0
Use IntEnum and IntFlag as appropriate.
samlittlewood Feb 20, 2020
038e7de
Get rid of debug LEDs (again).
samlittlewood Feb 20, 2020
4af9c71
Move device descriptions into glasgow.database.ti.chipcon.
samlittlewood Feb 20, 2020
9fb5efc
Combine hex records into one chuunk before writing.
samlittlewood Feb 20, 2020
b23acb4
Whitespace cleanup.
samlittlewood Feb 20, 2020
ee8f355
Tidy up variable names.
samlittlewood Feb 20, 2020
7b715d8
Use constant for write buffer address.
samlittlewood Feb 20, 2020
ee81fa0
Reworked reset and clocks/delays - all through fifo and no sleeps. So…
samlittlewood Feb 22, 2020
d5bbd1d
Cleanup remaining debris from reset and clock rework.
samlittlewood Feb 22, 2020
62a8b84
Handle writing lock bits with arguments, and cope with connecting to …
samlittlewood Feb 23, 2020
66aa35a
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Mar 2, 2020
74c227c
Extended to handle cc253x and cc254x.
samlittlewood Mar 9, 2020
a1ad15d
Heavy application of enums to get rid of inline opcode constants.
samlittlewood Mar 10, 2020
5554ed3
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Mar 10, 2020
1b98e83
Fix up typos.
samlittlewood Mar 10, 2020
7198ef2
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Mar 31, 2020
c7975e8
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Apr 6, 2020
4968b2f
Increase delay after programming page to cope with cc2430.
samlittlewood Apr 6, 2020
95227a7
Tidy up naming and docstrings.
samlittlewood Apr 6, 2020
b3df807
Tidy up whitespace and naming.
samlittlewood Apr 6, 2020
4a77938
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood May 27, 2020
ede5c32
Add dummy arguments to claim_interface in simulation.
samlittlewood May 27, 2020
79275f8
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Jul 16, 2020
175b0b7
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Jul 31, 2020
159e234
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Aug 21, 2020
a9d84c1
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Oct 1, 2020
534cbe7
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Oct 15, 2020
6649433
Merge remote-tracking branch 'upstream/master' into samlittlewood/app…
samlittlewood Nov 7, 2020
5d884ed
Merge remote-tracking branch 'upstream/main' into samlittlewood/apple…
samlittlewood Aug 4, 2021
a091b6f
Merge remote-tracking branch 'upstream/main' into samlittlewood/apple…
samlittlewood Sep 28, 2021
361b860
Merge remote-tracking branch 'upstream/main' into samlittlewood/apple…
samlittlewood Nov 16, 2021
0a20d6d
Merge remote-tracking branch 'upstream/main' into samlittlewood/apple…
samlittlewood Jun 10, 2022
bc4dea8
Update software/glasgow/applet/program/chipcon/ccdpi.py
samlittlewood Jun 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions software/glasgow/applet/all.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

samlittlewood marked this conversation as resolved.
Show resolved Hide resolved
from .internal.selftest import SelfTestApplet
from .internal.benchmark import BenchmarkApplet

Expand All @@ -24,6 +25,7 @@
from .debug.mips import DebugMIPSApplet

from .program.avr.spi import ProgramAVRSPIApplet
from .program.chipcon import ProgramChipconApplet
from .program.ice40_flash import ProgramICE40FlashApplet
from .program.ice40_sram import ProgramICE40SRAMApplet
from .program.m16c import ProgramM16CApplet
Expand Down
244 changes: 244 additions & 0 deletions software/glasgow/applet/program/chipcon/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# Chipcon, (now Texas Instruments) CC111x CC251x CC243x CC253x CC254x
#
import logging
import argparse
import asyncio
import math

from fx2.format import autodetect, input_data, flatten_data, output_data
from ....gateware.clockgen import *
from ... import *
from .ccdpi import *

class ProgramChipconApplet(GlasgowApplet, name="program-chipcon"):
logger = logging.getLogger(__name__)
help = "program TI/Chipcon CC111x CC251x CC243x CC253x CC254x"
description = """
Program and read back TI/Chipcon 8051 based SoC radios.
CC111x, CC251x, CC243x, CC253x and CC254x.
"""
__pins = ( "dclk", "ddat", "resetn")

@classmethod
def add_build_arguments(cls, parser, access):
super().add_build_arguments(parser, access)
for pin in cls.__pins:
access.add_pin_argument(parser, pin, default=True)
parser.add_argument(
"-f", "--frequency", metavar="FREQ", type=int, default=1000,
help="set bit rate to FREQ kHz (default: %(default)s)")
parser.add_argument(
"-s", "--flash-size", metavar="FLASH-SIZE", type=int,
help="set flash size FLASH-SIZE Kbytes")

def build(self, target, args):
self.mux_interface = iface = target.multiplexer.claim_interface(self, args)
iface.add_subtarget(CCDPISubtarget(
pads=iface.get_pads(args, pins=self.__pins),
out_fifo=iface.get_out_fifo(),
in_fifo=iface.get_in_fifo(auto_flush=False),
period_cyc=math.ceil(target.sys_clk_freq / (args.frequency * 1000)),
delay_cyc=self.derive_clock(input_hz=target.sys_clk_freq,
output_hz=4e6,
clock_name="delay")
))

async def run(self, device, args):
iface = await device.demultiplexer.claim_interface(self, self.mux_interface, args)
chipcon_iface = CCDPIInterface(iface, self.logger, args.flash_size)
return chipcon_iface

@classmethod
def add_interact_arguments(cls, parser):
def address(arg):
return int(arg, 0)
def length(arg):
return int(arg, 0)
p_operation = parser.add_subparsers(dest="operation", metavar="OPERATION")
p_identify = p_operation.add_parser(
"identify", help="read identity and revision from connected device")
p_erase = p_operation.add_parser(
"erase", help="erase whole device.")
p_erase_page = p_operation.add_parser(
"erase-page", help="erase whole device.")
p_erase_page.add_argument(
"address", metavar="ADDRESS", type=address,
help="erase memory from address ADDRESS")
p_read = p_operation.add_parser(
"read", help="read memory")
p_read.add_argument(
"address", metavar="ADDRESS", type=address,
help="read memory from address ADDRESS")
p_read.add_argument(
"length", metavar="LENGTH", type=length,
help="read LENGTH bytes from memory")
p_read.add_argument(
"--code", metavar="CODE", type=argparse.FileType("wb"),
help="read memory contents into CODE")
p_read.add_argument(
"--lock-bits", metavar="LOCK-BITS", type=argparse.FileType("wb"),
help="read flash information page into LOCK-BITS")
samlittlewood marked this conversation as resolved.
Show resolved Hide resolved
p_write = p_operation.add_parser(
"write", help="write and verify memory")
p_write.add_argument(
"--code", metavar="CODE", type=argparse.FileType("rb"),
help="program code memory contents from CODE")
p_write.add_argument(
"--lock-size", metavar="LOCK-SIZE", type=int, default=0,
help="Set flash lock size - size in KBytes of upper flash memory which is write protected.")
p_write.add_argument(
"--lock-debug", action="store_true",
help="Set debug lock bit - disable all operations except id, status and erase.")
p_write.add_argument(
"--lock-boot", action="store_true",
help="Set boot page lock bit, disable writing to page 0")
p_write.add_argument(
"--lock-bits", metavar="LOCK-BITS", type=argparse.FileType("rb"),
help="program flash information page from LOCK-BITS")
p_write.add_argument(
"--no-erase", action="store_true",
help="do not erase chip before writing")
p_write.add_argument(
"--no-verify", action="store_true",
help="do not verify code after writing")

@staticmethod
def _check_format(file, kind):
try:
autodetect(file)
except ValueError:
raise CCDPIError("cannot determine %s file format" % kind)

async def interact(self, device, args, chipcon_iface):
await chipcon_iface.connect()
debug_locked = await chipcon_iface.get_status() & Status.DEBUG_LOCKED
if not debug_locked:
await chipcon_iface.clock_init()
self.logger.info("connected to {} Rev:{}{}".format(
chipcon_iface.device.name,
chipcon_iface.chip_rev,
" Debug Locked" if debug_locked else ""))
if debug_locked and args.operation not in ("identify, erase"):
raise CCDPIError("Cannot %s when debug is locked" % args.operation)
self.logger.info(args.operation)
if args.operation == "identify":
self.logger.info("Id:{:X} [{}] Rev:{:d}".format(
chipcon_iface.chip_id,
chipcon_iface.device.name,
chipcon_iface.chip_rev))
elif args.operation == "erase":
await chipcon_iface.chip_erase()
elif args.operation == "erase-page":
await chipcon_iface.erase_flash_page(args.address)
elif args.operation == "read":
if args.code:
self._check_format(args.code, "code")
self.logger.info("reading code (%d bytes)", args.length)
await chipcon_iface.set_config(0)
output_data(args.code,
await self.read_flash(chipcon_iface, args.address, args.length))
if args.lock_bits:
self._check_format(args.lock_bits, "lock-bits")
self.logger.info("reading flash information (%d bytes)", args.length)
await chipcon_iface.set_config(Config.SEL_FLASH_INFO_PAGE)
output_data(args.lock_bits,
await self.read_flash(args.address, args.length))
await chipcon_iface.set_config(0)
elif args.operation == "write":
if not args.no_erase:
self.logger.info("erasing chip")
await chipcon_iface.chip_erase()
await chipcon_iface.connect()
if args.code:
self._check_format(args.code, "code")
data = input_data(args.code)
self.logger.info("writing code (%d bytes)",
sum([len(chunk) for address, chunk in data]))
await chipcon_iface.set_config(0)
await self.write_flash(chipcon_iface, data,
chipcon_iface.device.write_block_size,
not args.no_verify)
if args.lock_bits or args.lock_boot or args.lock_size or args.lock_debug:
data = []
if args.lock_bits:
self._check_format(args.lock_bits, "lock-bits")
data += input_data(args.lock_bits)
if args.lock_boot or args.lock_size or args.lock_debug:
data += self._make_lock_bits(chipcon_iface,
args.lock_boot, args.lock_size, args.lock_debug)
self.logger.info("writing flash information (%d bytes)",
sum([len(chunk) for address, chunk in data]))
await chipcon_iface.set_config(Config.SEL_FLASH_INFO_PAGE)
# Cannot verify if debug is locked
await self.write_flash(chipcon_iface, data,
chipcon_iface.device.write_block_size, not args.lock_debug)
await chipcon_iface.set_config(0)
await chipcon_iface.disconnect()

async def read_flash(self, chipcon_iface, address, count):
"""Read data from flash."""
bytes = bytearray()
for block_address,block_count in self._aligned_range(address, count, 0x8000):
bytes += await chipcon_iface.read_flash(block_address, block_count)
self.logger.info("read %d bytes from %#07x", block_count, block_address)
return bytes

async def write_flash(self, chipcon_iface, data, block_size, verify=True):
"""Write data (and optionally verify) flash."""
if len(data) == 0:
return
elif len(data) == 1:
address,chunk = data[0]
elif len(data) > 1:
address,chunk = self._combine_chunks(data)
for block_address,block_size in self._aligned_range(address, len(chunk),
chipcon_iface.device.write_block_size):
block_offset = block_address-address
block = bytes(chunk[block_offset:block_offset+block_size])

await chipcon_iface.write_flash(block_address, block)
readback = await chipcon_iface.read_flash(block_address, len(block))
if verify and block != readback:
raise CCDPIError("verification failed at address %#07x" % (block_address,))
self.logger.info("written %d bytes to %#07x", len(block), address+block_offset)

def _combine_chunks(self, data):
"""Reduce a list of (adress,chunk) to a single entry combining all chunks.
Any gaps are filled with 0xff.
"""
start = min([addr for (addr, _) in data])
end = max([addr + len(chunk) for (addr, chunk) in data])
combined_data = bytearray([0xff] * (end - start))
for (addr, chunk) in data:
addr -= start
combined_data[addr:addr+len(chunk)] = chunk
return (start, combined_data)

def _aligned_range(self, addr, length, align):
"""Generate addr,length pairs that splits range addr,length on align boundaries."""
limit = addr+length
while addr < limit:
next_boundary = (addr//align+1) * align
count = min(limit-addr, next_boundary-addr)
yield (addr, count)
addr += count

def _make_lock_bits(self, chipcon_iface, boot, size, debug):
"""Construct data to write to flash information page encoding the given lock settings."""
if size not in chipcon_iface.device.write_protect_sizes:
raise CCDPIError("lock size %d is not valid for device - valid sizes are %s" %
(size, ",".join(str(s) for s in chipcon_iface.device.write_protect_sizes)))
lock_byte = chipcon_iface.device.write_protect_sizes[size] << 1
if not boot:
lock_byte |= 0x10
if not debug:
lock_byte |= 0x01
self.logger.debug("Lock byte: %#02x", lock_byte)
# Contrary to the data sheet, the lock byte appears to be byte 1 in information page.
return [(0,bytes([0xff, lock_byte]))]

# -------------------------------------------------------------------------------------------------
class ProgramChipconAppletTestCase(GlasgowAppletTestCase, applet=ProgramChipconApplet):
@synthesis_test
def test_build(self):
self.assertBuilds()