Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c733acea0456
Choose a base ref
...
head repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: bdb299f9663f
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Apr 6, 2019

  1. applet.memory.onfi: increase wait states to 3.

    This is a band-aid at most; we really need to rewrite this part of
    the applet properly. However, without this band-aid, currently
    not even the ONFI parameter page can be read reliably.
    whitequark committed Apr 6, 2019
    Copy the full SHA
    431cada View commit details
  2. Copy the full SHA
    aaa0c07 View commit details
  3. Copy the full SHA
    bdb299f View commit details
Showing with 313 additions and 19 deletions.
  1. +1 −1 docs/archive
  2. +146 −18 software/glasgow/applet/memory/onfi/__init__.py
  3. +166 −0 software/glasgow/protocol/onfi.py
2 changes: 1 addition & 1 deletion docs/archive
164 changes: 146 additions & 18 deletions software/glasgow/applet/memory/onfi/__init__.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
from ....support.pyrepl import *
from ....support.logging import *
from ....database.jedec import *
from ....protocol.onfi import *
from ... import *


@@ -64,7 +65,7 @@ def __init__(self, pads, in_fifo, out_fifo):
control = Signal(3)
length = Signal(16)

wait_cyc = 2 # revB needs at least two wait states for reliable reads
wait_cyc = 3 # currently required for reliable reads
timer = Signal(max=wait_cyc + 2, reset=wait_cyc)

self.submodules.fsm = FSM(reset_state="RECV-COMMAND")
@@ -256,9 +257,9 @@ async def read_status(self):
async def is_write_protected(self):
return (await self.read_status() & BIT_STATUS_WRITE_PROT) == 0

async def read_parameter_page(self):
self._log("read parameter page")
return await self._do_read(command=0xEC, address=[0x00], wait=True, length=512)
async def read_parameter_page(self, copies=3):
self._log("read parameter page copies=%d", copies)
return await self._do_read(command=0xEC, address=[0x00], wait=True, length=copies * 256)

async def read_unique_id(self):
self._log("read unique ID")
@@ -381,6 +382,9 @@ def count(arg):
# TODO(py3.7): add required=True
p_operation = parser.add_subparsers(dest="operation", metavar="OPERATION")

p_identify = p_operation.add_parser(
"identify", help="identify device using ONFI parameter page")

p_read = p_operation.add_parser(
"read", help="read data and spare contents for a page range")
p_read.add_argument(
@@ -433,27 +437,151 @@ async def interact(self, device, args, onfi_iface):
self.logger.info("JEDEC manufacturer %#04x (%s) device %#04x",
manufacturer_id, manufacturer_name, device_id)

page_size = args.page_size
spare_size = args.spare_size
block_size = args.block_size
onfi_param = None
if await onfi_iface.read_signature() == b"ONFI":
parameter_page = await onfi_iface.read_parameter_page()
if parameter_page[0:4] == b"ONFI":
# I don't actually have any *actually valid* ONFI flashes yet,
# so this isn't implemented or tested. Sigh. Cursed.
pass
else:
self.logger.warning("ONFI signature present, but parameter page missing")
try:
onfi_param = ONFIParameters(parameter_page[512:])
except ONFIParameterError as e:
self.logger.warning("invalid ONFI parameter page: %s", str(e))
else:
self.logger.warning("ONFI signature not present")

if None in (args.page_size, args.block_size, args.spare_size):
self.logger.error("your cursed device doesn't support ONFI properly")
self.logger.error("in the future, avoid angering witches")
self.logger.error("meanwhile, configure the Flash array parameters explicitly via "
"--page-size, --spare-size and --block-size")
if args.operation == "identify" and onfi_param is None:
self.logger.error("cannot identify non-ONFI device")
return
elif args.operation == "identify":
self.logger.info("ONFI revision %d.%d%s",
*onfi_param.revision,
"+" if onfi_param.revisions.unknown else "")

blocks = {}

onfi_jedec_manufacturer_name = \
jedec_mfg_name_from_bytes([onfi_param.jedec_manufacturer_id]) or "unknown"
blocks["ONFI manufacturer information"] = {
"JEDEC ID": "{:#04x} ({})"
.format(onfi_param.jedec_manufacturer_id, onfi_jedec_manufacturer_name),
"manufacturer": onfi_param.manufacturer,
"model": onfi_param.model,
"date code":
"(not specified)" if onfi_param.date_code is None else
"year %02d, week %02d".format(onfi_param.date_code.year,
onfi_param.date_code.week)
}

blocks["Features"] = {
"data bus width":
"16-bit" if onfi_param.features._16_bit_data_bus else "8-bit",
"multi-LUN operations":
"yes" if onfi_param.features.multiple_lun_ops else "no",
"block programming order":
"random" if onfi_param.features.non_seq_page_program else "sequential",
"interleaved operations":
"yes" if onfi_param.features.interleaved_ops else "no",
"odd-to-even copyback":
"yes" if onfi_param.features.odd_to_even_copyback else "no",
}

blocks["Optional commands"] = {
"Page Cache Program":
"yes" if onfi_param.opt_commands.page_cache_program else "no",
"Read Cache (Enhanced/End)":
"yes" if onfi_param.opt_commands.read_cache else "no",
"Get/Set Features":
"yes" if onfi_param.opt_commands.get_set_features else "no",
"Read Status Enhanced":
"yes" if onfi_param.opt_commands.read_status_enhanced else "no",
"Copyback Program/Read":
"yes" if onfi_param.opt_commands.copyback else "no",
"Read Unique ID":
"yes" if onfi_param.opt_commands.read_unique_id else "no",
}

blocks["Memory organization"] = {
"page size": "{} + {} bytes"
.format(onfi_param.bytes_per_page, onfi_param.bytes_per_spare),
"partial page size": "{} + {} bytes"
.format(onfi_param.bytes_per_partial_page, onfi_param.bytes_per_partial_spare),
"block size": "{} pages"
.format(onfi_param.pages_per_block),
"LUN size": "{} blocks; {} pages"
.format(onfi_param.blocks_per_lun,
onfi_param.blocks_per_lun * onfi_param.pages_per_block),
"target size": "{} LUNs; {} blocks; {} pages"
.format(onfi_param.luns_per_target,
onfi_param.luns_per_target * onfi_param.blocks_per_lun,
onfi_param.luns_per_target * onfi_param.blocks_per_lun
* onfi_param.pages_per_block),
"address cycles": "{} row, {} column"
.format(onfi_param.address_cycles.row, onfi_param.address_cycles.column),
"bits per cell": "{}"
.format(onfi_param.bits_per_cell),
"bad blocks per LUN": "{} (maximum)"
.format(onfi_param.max_bad_blocks_per_lun),
"block endurance": "{} cycles (maximum)"
.format(onfi_param.block_endurance),
"guaranteed blocks": "{} (at target beginning)"
.format(onfi_param.guaranteed_valid_blocks),
"guaranteed block endurance": "{} cycles"
.format(onfi_param.guaranteed_valid_block_endurance),
"programs per page": "{} (maximum)"
.format(onfi_param.programs_per_page),
# Partial programming constraints not displayed.
"ECC correctability": "{} bits (maximum, per 512 bytes)"
.format(onfi_param.ecc_correctability_bits),
# Interleaved operations not displayed.
}

blocks["Electrical parameters"] = {
"I/O pin capacitance": "{} pF"
.format(onfi_param.io_pin_capacitance),
"timing modes":
", ".join(str(mode) for mode in onfi_param.timing_modes),
"program cache timing modes":
", ".join(str(mode) for mode in onfi_param.program_cache_timing_modes) or
"(not supported)",
"page program time": "{} us (maximum)"
.format(onfi_param.max_page_program_time),
"block erase time": "{} us (maximum)"
.format(onfi_param.max_block_erase_time),
"page read time": "{} us (maximum)"
.format(onfi_param.max_page_read_time),
"change column setup time": "{} us (minimum)"
.format(onfi_param.min_change_column_setup_time),
}

for block, params in blocks.items():
self.logger.info("%s:", block)
for name, value in params.items():
self.logger.info("%27s: %s", name, value)

return

if onfi_param is not None:
if (args.page_size is not None or
args.block_size is not None or
args.spare_size is not None):
self.logger.warning("explicitly specified geometry is ignored in favor of "
"ONFI parameters")

page_size = onfi_param.bytes_per_page
spare_size = onfi_param.bytes_per_spare
block_size = onfi_param.pages_per_block
else:
if (args.page_size is None or
args.block_size is None or
args.spare_size is None):
self.logger.error("your cursed device doesn't support ONFI properly")
self.logger.error("in the future, avoid angering witches")
self.logger.error("meanwhile, configure the Flash array parameters explicitly via "
"--page-size, --spare-size and --block-size")
return

page_size = args.page_size
spare_size = args.spare_size
block_size = args.block_size

if args.operation in ("program", "erase"):
if await onfi_iface.is_write_protected():
self.logger.error("device is write-protected")
166 changes: 166 additions & 0 deletions software/glasgow/protocol/onfi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Ref: ONFI Rev 1.0
# Accession: G00030

import struct
import crcmod

from ..support.bits import *


__all__ = ["ONFIParameters", "ONFIParameterError"]


class ONFIParameterError(Exception):
pass


_crc_onfi = crcmod.mkCrcFun(0x18005, initCrc=0x4f4e, rev=False)


_ONFI_Revision = Bitfield("ONFI_Revision", 16, [
(None, 1),
("rev_1_0", 1),
("unknown", 14)
])


_ONFI_Features = Bitfield("ONFI_Features", 16, [
("_16_bit_data_bus", 1),
("multiple_lun_ops", 1),
("non_seq_page_program", 1),
("interleaved_ops", 1),
("odd_to_even_copyback", 1),
(None, 11)
])


_ONFI_Optional_Commands = Bitfield("ONFI_Optional_Commands", 16, [
("page_cache_program", 1),
("read_cache", 1),
("get_set_features", 1),
("read_status_enhanced", 1),
("copyback", 1),
("read_unique_id", 1),
(None, 10)
])


_ONFI_Date_Code = Bitfield("ONFI_Date_Code", 16, [
("year", 8),
("week", 8),
])


_ONFI_Address_Cycles = Bitfield("ONFI_Address_Cycles", 8, [
("row", 4),
("column", 4),
])


_ONFI_Block_Endurance = Bitfield("ONFI_Block_Endurance", 16, [
("value", 8),
("multiplier", 8),
])


_ONFI_Partial_Programming_Attributes = Bitfield("ONFI_Partial_Programming_Attributes", 8, [
("has_constraints", 1),
(None, 3),
("layout_is_data_spare", 1),
(None, 3)
])


_ONFI_Interleaved_Address_Bits = Bitfield("ONFI_Interleaved_Address_Bits", 8, [
("count", 4),
(None, 4)
])


_ONFI_Interleaved_Operation_Attributes = Bitfield("ONFI_Interleaved_Operation_Attributes", 8, [
("overlapped_supported", 1),
("no_address_restrictions", 1),
("program_cache_supported", 1),
("program_cache_address_restrictions", 1),
(None, 4)
])


class ONFIParameters:
def __init__(self, data):
assert len(data) >= 256 and len(data) % 256 == 0

if data[:4] != b"ONFI":
raise ONFIParameterError("invalid signature")

while len(data) > 0:
crc_expected, = struct.unpack_from("<H", data, offset=254)
crc_actual = _crc_onfi(data[:254])
if crc_expected == crc_actual:
break
# Switch to the next redundant parameters page.
data = data[256:]
else:
raise ONFIParameterError("integrity checks failed on all redundant pages")

# Revision information and features block
#
_, revisions, features, opt_commands, _ = \
struct.unpack_from("<4sHHH22s", data, offset=0)

self.revisions = _ONFI_Revision.from_int(revisions)
self.features = _ONFI_Features.from_int(features)
self.opt_commands = _ONFI_Optional_Commands.from_int(opt_commands)

# Highest supported ONFI revision that we know of.
self.revision = None
if self.revisions.rev_1_0:
self.revision = (1, 0)

# Manufacturer information block
#
manufacturer, model, self.jedec_manufacturer_id, date_code, _ = \
struct.unpack_from("<12s20sBH13s", data, offset=32)

self.manufacturer = manufacturer.decode("ascii").rstrip()
self.model = model.decode("ascii").rstrip()
if date_code == 0x0000:
self.date_code = None
else:
self.date_code = _ONFI_Date_Code.from_int(date_code)

# Memory organization block
#
self.bytes_per_page, self.bytes_per_spare, \
self.bytes_per_partial_page, self.bytes_per_partial_spare, \
self.pages_per_block, self.blocks_per_lun, self.luns_per_target, \
address_cycles, self.bits_per_cell, \
self.max_bad_blocks_per_lun, block_endurance, \
self.guaranteed_valid_blocks, self.guaranteed_valid_block_endurance, \
self.programs_per_page, partial_programming_attrs,\
self.ecc_correctability_bits, \
interleaved_address_bits, interleaved_op_attrs, _, = \
struct.unpack_from("<LHLHLLBBBHHBHBBBBB13s", data, offset=80)

self.address_cycles = _ONFI_Address_Cycles.from_int(address_cycles)
block_endurance = _ONFI_Block_Endurance.from_int(block_endurance)
self.block_endurance = block_endurance.value * (10 ** block_endurance.multiplier)
self.partial_programming_attrs = \
_ONFI_Partial_Programming_Attributes.from_int(partial_programming_attrs)
self.interleaved_address_bits = \
_ONFI_Interleaved_Address_Bits.from_int(interleaved_address_bits)
self.interleaved_op_attrs = \
_ONFI_Interleaved_Operation_Attributes.from_int(interleaved_op_attrs)

# Electrical parameters block
#
self.io_pin_capacitance, \
timing_mode_support, program_cache_timing_mode_support, \
self.max_page_program_time, self.max_block_erase_time, self.max_page_read_time, \
self.min_change_column_setup_time, _ = \
struct.unpack_from("<BHHHHHH23s", data, offset=128)

self.timing_modes = \
[mode for mode in range(6) if timing_mode_support & (1 << mode)]
self.program_cache_timing_modes = \
[mode for mode in range(6) if program_cache_timing_mode_support & (1 << mode)]