Skip to content

Commit

Permalink
applet.program.ecp5_sram: new applet. (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Oct 28, 2023
1 parent 46cb9db commit 6073637
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/archive
Submodule archive updated 29 files
+ G00059/ina233.pdf
+ G00060/APX811_812_Rev.2.pdf
+ G00061/CAT24M01-D_Rev.5.PDF
+ G00062/BL24C256A_1.91.pdf
+ G00063/DAC081C08x_SNAS449F_Revision_F.pdf
+ G00064/PCA6408A_Rev.1.pdf
+ G00065/sn74lvc1t45_SCES515L_Revision_L.pdf
+ G00066/tlv733p_SBVS320A_Revision_A.pdf
+ G00067/tpd3s0x4_SLVSCP4B_Revision_B.pdf
+ G00068/TPS731xx_SBVS034N_Revision_N.pdf
+ ...014A_CY7C68015A_CY7C68016A_EZ-USB_FX2LP_USB_MICROCONTROLLER_HIGH_SPEED_USB_PERIPHERAL_CONTROLLER_Rev.AC.pdf
+ G00070/iCE40-LP-HX-Family-Data-Sheet_Revision_3.6.pdf
+ G00071/ICETechnologyLibrary_Version_3.0.pdf
+ G00072/Lattice-PCB-Layout-Recommandations-BGA-Packages_Revision_4.4.pdf
+ G00073/iCE40-Programming-Configuration_Revision_3.2.pdf
+ G00074/iCE40-Hardware-Checklist_Revision_1.8.pdf
+ G00075/Lattice-Package-Diagrams_Revision_6.0.pdf
+ G00076/Memory-Usage-Guide-for-iCE40-Devices_Revision_1.7.pdf
+ G00077/iCE40-sysCLOCK-PLL-Design-Usage-Guide_Revision_1.2.pdf
+ G00078/Lattice-Solder-Reflow-Guide-for-Surface-Mount-Devices_Revision_4.2.pdf
+ G00079/Using_Differential_IOLVDS_Sub-LVDS_in_iCE40_Devices_Revision_1.5.pdf
+ G00080/EZ-USB_TECHNICAL_REFERENCE_MANUAL_Revision_G.pdf
+ G00081/ds055.pdf
+ G00082/ds056.pdf
+ G00083/Sensirion_Environmental_Sensor_Node_SEN5x_Datasheet.pdf
+ G00084/TNxxx - STUSB4500 NVM Description_V1.1.pdf
+ G00085/ieee-standard-for-higher-performance-protocol-for-the-standard-d.pdf
+ G00086/DS_SX1276-7-8-9_W_APP_V7.pdf
+ G00087/FPGA-TN-02039-1-7-ECP5-and-ECP5-5G-sysCONFIG.pdf
128 changes: 128 additions & 0 deletions software/glasgow/applet/program/ecp5_sram/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# XXX: where is JTAG programming described?

import sys
import logging
import argparse

from ....arch.jtag import *
from ....arch.lattice.ecp5 import *
from ....support.bits import *
from ....support.logging import *
from ....database.jedec import *
from ....database.lattice.ecp5 import *
from ... import *
from ...interface.jtag_probe import JTAGProbeApplet


class ECP5JTAGError(GlasgowAppletError):
pass


class ECP5JTAGInterface:
def __init__(self, interface, logger):
self.lower = interface
self._logger = logger
self._level = logging.DEBUG if self._logger.name == __name__ else logging.TRACE

def _log(self, message, *args):
self._logger.log(self._level, f"ECP5: " + message, *args)

async def identify(self):
await self.lower.test_reset()
await self.lower.write_ir(IR_IDCODE)
idcode = DR_IDCODE.from_bits(await self.lower.read_dr(32))
self._log("read id mfg-id=%03x part-id=%04x version=%01x",
idcode.mfg_id, idcode.part_id, idcode.version)
return idcode, devices_by_idcode[idcode.to_int()]

async def read_status(self):
await self.lower.write_ir(IR_LSC_READ_STATUS)
status = LSC_Status.from_bits(await self.lower.read_dr(32))
self._log("status %s", status.bits_repr())
return status

async def programming_enable(self):
self._log("programming enable")
await self.lower.write_ir(IR_ISC_ENABLE)
await self.lower.run_test_idle(10) # XXX verify timing

async def programming_disable(self):
self._log("programing disable")
await self.lower.write_ir(IR_ISC_DISABLE)
await self.lower.run_test_idle(10) # XXX verify timing

async def load_bitstream(self, bitstream,
callback=lambda done, total: None):
bitstream = bits(bitstream)
self._log("load bitstream bit-length=%d", len(bitstream) * 8)

# The FPGA expects bitstream bytes to be shifted in MSB-first.
bitstream = bitstream.byte_reversed()

# Enter bitstream burst load mode.
await self.lower.write_ir(IR_LSC_BITSTREAM_BURST)

# Send bitstream in medium sized chunks. This is faster because the JTAG probe currently
# doesn't optimize the case of sending very large `bits` values well.
await self.lower.enter_shift_dr()
chunk_size = 4096
for chunk_start in range(0, len(bitstream), chunk_size):
callback(chunk_start, len(bitstream))
await self.lower.shift_tdi(bitstream[chunk_start:chunk_start + chunk_size], last=False)
await self.lower.flush()
callback(len(bitstream), len(bitstream))
await self.lower.shift_tdi(bits("00000000"), last=True) # dummy write to exit Shift-DR
await self.lower.enter_update_dr()

async def program_bitstream(self, bitstream,
callback=lambda done, total: None):
await self.programming_enable()
await self.load_bitstream(bitstream, callback=callback)
await self.programming_disable()

status = await self.read_status()
if status.DONE:
self._logger.info("FPGA successfully configured")
else:
error_code = BSE_Error_Code(status.BSE_Error_Code).explanation
raise GlasgowAppletError(f"FPGA failed to configure: {error_code}")


class ProgramECP5SRAMApplet(JTAGProbeApplet):
logger = logging.getLogger(__name__)
help = "Program SRAM of ECP5 FPGAs via JTAG"
description = """
Program the volatile configuration memory of ECP5 FPGAs.
"""

@staticmethod
def _show_progress(done, total):
if sys.stdout.isatty():
sys.stdout.write("\r\033[0K")
if done < total:
sys.stdout.write(f"{done / total * 100:.0f}% complete")
sys.stdout.flush()

@classmethod
def add_interact_arguments(cls, parser):
parser.add_argument(
"bitstream", metavar="BITSTREAM", type=argparse.FileType("rb"),
help="bitstream file")

async def run(self, device, args):
jtag_iface = await self.run_lower(ProgramECP5SRAMApplet, device, args)
return ECP5JTAGInterface(jtag_iface, self.logger)

async def interact(self, device, args, ecp5_iface):
idcode, ecp5_device = await ecp5_iface.identify()
if ecp5_device is None:
raise ECP5JTAGError("cannot operate on unknown device with IDCODE={:#10x}"
.format(idcode.to_int()))
self.logger.info("found device %s", ecp5_device.name)

await ecp5_iface.program_bitstream(args.bitstream.read(), callback=self._show_progress)

@classmethod
def tests(cls):
from . import test
return test.ProgramECP5SRAMAppletTestCase
8 changes: 8 additions & 0 deletions software/glasgow/applet/program/ecp5_sram/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from ... import *
from . import ProgramECP5SRAMApplet


class ProgramECP5SRAMAppletTestCase(GlasgowAppletTestCase, applet=ProgramECP5SRAMApplet):
@synthesis_test
def test_build(self):
self.assertBuilds()
Empty file.
91 changes: 91 additions & 0 deletions software/glasgow/arch/lattice/ecp5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Ref: FPGA-TN-02039 ECP5 and ECP5-5G sysCONFIG Usage Guide
# Ref: https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/EH/FPGA-TN-02039-1-7-ECP5-and-ECP5-5G-sysCONFIG.pdf
# Accession: G00087

import enum

from ...support.bits import *
from ...support.bitstruct import *


__all__ = [
# IR values
"IR_IDCODE", "IR_LSC_READ_STATUS", "IR_ISC_ENABLE", "IR_ISC_DISABLE", "IR_ISC_ERASE",
"IR_LSC_BITSTREAM_BURST",
# DR structures
"Config_Target", "BSE_Error_Code", "LSC_Status",
]


# IR values (ascending numeric order)
# XXX: where did these values come from?
IR_ISC_ERASE = bits("00001110")
IR_ISC_DISABLE = bits("00100110")
IR_LSC_READ_STATUS = bits("00111100")
IR_LSC_BITSTREAM_BURST = bits("01111010")
IR_ISC_ENABLE = bits("11000110")
IR_IDCODE = bits("11100000")


# Lattice status register
class Config_Target(enum.IntEnum):
SRAM = 0b000
eFuse = 0b001


class BSE_Error_Code(enum.IntEnum):
No_error = 0b000
ID_error = 0b001
CMD_error = 0b010
CRC_error = 0b011
PRMB_error = 0b100
ABRT_error = 0b101
OVFL_error = 0b110
SDM_error = 0b111

@property
def explanation(self):
if self == self.No_error:
return "success"
if self == self.ID_error:
return "IDCODE mismatch"
if self == self.CMD_error:
return "illegal command"
if self == self.CRC_error:
return "checksum error"
if self == self.ABRT_error:
return "configuration aborted"
if self == self.OVFL_error:
return "data overflow error"
if self == self.SDM_error:
return "bitstream past the size of SRAM array"


LSC_Status = bitstruct("LSC_Status", 32, [
("Transparent_Mode", 1),
("Config_Target", 3),
("JTAG_Active", 1),
("PWD_Protection", 1),
(None, 1), # Not used
("Decrypt_Enable", 1),
("DONE", 1),
("ISC_Enable", 1),
("Write_Enable", 1),
("Read_Enable", 1),
("Busy_Flag", 1),
("Fail_Flag", 1),
("FEA_OTP", 1),
("Decrypt_Only", 1),
("PWD_Enable", 1),
(None, 3), # Not used
("Encrypt_Preamble", 1),
("Std_Preamble", 1),
("SPIm_Fail_1", 1),
("BSE_Error_Code", 3),
("Execution_Error", 1),
("ID_Error", 1),
("Invalid_Command", 1),
("SED_Error", 1),
("Bypass_Mode", 1),
("Flow_Through_Mode",1),
])
Empty file.
27 changes: 27 additions & 0 deletions software/glasgow/database/lattice/ecp5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from collections import defaultdict, namedtuple


__all__ = ["devices", "devices_by_idcode", "devices_by_name"]


ECP5Device = namedtuple("ECP5Device", ("name", "idcode"))


devices = [
ECP5Device("LFE5U-12", idcode=0x21111043),
ECP5Device("LFE5U-25", idcode=0x41111043),
ECP5Device("LFE5U-45", idcode=0x41112043),
ECP5Device("LFE5U-85", idcode=0x41113043),
ECP5Device("LFE5UM-25", idcode=0x01111043),
ECP5Device("LFE5UM-45", idcode=0x01112043),
ECP5Device("LFE5UM-85", idcode=0x01113043),
ECP5Device("LFE5UM5G-25", idcode=0x81111043),
ECP5Device("LFE5UM5G-45", idcode=0x81112043),
ECP5Device("LFE5UM5G-85", idcode=0x81113043),
]

devices_by_idcode = defaultdict(lambda: None,
((device.idcode, device) for device in devices))

devices_by_name = defaultdict(lambda: None,
((device.name, device) for device in devices))
1 change: 1 addition & 0 deletions software/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ debug-arm = "glasgow.applet.debug.arm.jtag:DebugARMJTAGApplet"
debug-mips = "glasgow.applet.debug.mips:DebugMIPSApplet"

program-avr-spi = "glasgow.applet.program.avr.spi:ProgramAVRSPIApplet"
program-ecp5-sram = "glasgow.applet.program.ecp5_sram:ProgramECP5SRAMApplet"
program-ice40-flash = "glasgow.applet.program.ice40_flash:ProgramICE40FlashApplet"
program-ice40-sram = "glasgow.applet.program.ice40_sram:ProgramICE40SRAMApplet"
program-m16c = "glasgow.applet.program.m16c:ProgramM16CApplet"
Expand Down

0 comments on commit 6073637

Please sign in to comment.