blob: 0a109c5514b091a9f5fab8d4f7c1e1b5d93fcb89 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use byteorder::{LittleEndian, ReadBytesExt};
use num_enum::FromPrimitive;
use serde::Serialize;
use serde_annotate::Annotate;
use std::convert::TryFrom;
use std::time::Duration;
use thiserror::Error;
use crate::util::bitfield::BitField;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
TryFromIntError(#[from] std::num::TryFromIntError),
#[error("the range {0}..{1} is out of bounds")]
SliceRange(usize, usize),
#[error("SFDP header contains incorrect signature: {0:#010x}")]
WrongHeaderSignature(u32),
// This is only needed to meet the error conversion requirements for the most
// general case in the field! macro below.
#[error(transparent)]
Infallible(#[from] std::convert::Infallible),
}
/// The SFDP header identifies a valid SFDP, its version and the number of parameter headers.
#[derive(Debug, Serialize, Annotate)]
pub struct SfdpHeader {
#[annotate(format=hex, comment=comment_signature())]
pub signature: u32,
#[annotate(comment=comment_version())]
pub minor: u8,
pub major: u8,
#[annotate(comment = "Number of parameter headers minus one")]
pub nph: u8,
#[annotate(format=bin, comment="The reserved field should be all ones")]
pub reserved: u8,
}
impl SfdpHeader {
fn comment_signature(&self) -> Option<String> {
let signature = self.signature.to_le_bytes().map(|b| {
match b {
// The ASCII printable range is [0x20, 0x7E].
0x20..=0x7E => b as char,
_ => '.',
}
});
Some(format!(
"Signature value='{}{}{}{}' (should be 'SFDP')",
signature[0], signature[1], signature[2], signature[3],
))
}
fn comment_version(&self) -> Option<String> {
Some(format!("{}.{}", self.major, self.minor))
}
}
impl TryFrom<&[u8]> for SfdpHeader {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
const SFDP_HEADER_SIGNATURE: u32 = 0x50444653;
let mut reader = std::io::Cursor::new(buf);
let header = SfdpHeader {
signature: reader.read_u32::<LittleEndian>()?,
minor: reader.read_u8()?,
major: reader.read_u8()?,
nph: reader.read_u8()?,
reserved: reader.read_u8()?,
};
match header.signature {
SFDP_HEADER_SIGNATURE => Ok(header),
v => Err(Error::WrongHeaderSignature(v)),
}
}
}
/// The SFDP parameter header identifies additional parameter tables within the SFDP.
/// All devices are required to have a JEDEC parameter header and corresponding table.
#[derive(Debug, Serialize)]
pub struct SfdpPhdr {
pub id: u8,
pub minor: u8,
pub major: u8,
pub dwords: u8,
pub offset: u32,
}
impl TryFrom<&[u8]> for SfdpPhdr {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let mut reader = std::io::Cursor::new(buf);
Ok(SfdpPhdr {
id: reader.read_u8()?,
minor: reader.read_u8()?,
major: reader.read_u8()?,
dwords: reader.read_u8()?,
offset: reader.read_u32::<LittleEndian>()? & 0x00FFFFFFu32,
})
}
}
// The internal JEDEC parameter struct is used as an intermediate representation
// in creating the user-visible JEDEC parameter struct.
struct InternalJedecParams {
pub data: Vec<u32>,
}
impl TryFrom<&[u8]> for InternalJedecParams {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let mut reader = std::io::Cursor::new(buf);
let mut data = vec![0u32; buf.len() / 4];
reader.read_u32_into::<LittleEndian>(&mut data)?;
Ok(InternalJedecParams { data })
}
}
// The JEDEC parameter table is documented in JESD216 "Serial Flash Discoverable Parameters".
// The format of the parameter table is expressed in that document as a set of bitfields
// in the various "dwords" which make up the table.
//
// We use the `field` macro to express how to extract and convert the various bitfields
// into useful values. The macro generates a function named `ident` which extracts
// `bitsize` bits starting at `bitoffset` in the `field`th "dword" of the data.
//
// Since the documentation for dword offsets is 1-based, the marco parameter `field`
// is also 1-based.
macro_rules! field {
// Extract to bool.
($name:ident -> bool, $field:expr, $bitoffset:expr, $bitsize:expr) => {
pub fn $name(&self) -> Result<bool, Error> {
Ok(BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]) != 0)
}
};
// Extract an entire u32 word.
($name:ident -> u32, $field:expr, 0, 32) => {
pub fn $name(&self) -> Result<u32, Error> {
Ok(self.data[$field - 1])
}
};
// Extract a bitfield as u32.
($name:ident -> u32, $field:expr, $bitoffset:expr, $bitsize:expr) => {
pub fn $name(&self) -> Result<u32, Error> {
Ok(BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]))
}
};
// Extract a bitfield as some other type.
($name:ident -> $type:ty, $field:expr, $bitoffset:expr, $bitsize:expr) => {
pub fn $name(&self) -> Result<$type, Error> {
Ok(<$type>::try_from(
BitField::new($bitoffset, $bitsize).extract(self.data[$field - 1]),
)?)
}
};
}
impl InternalJedecParams {
field!(block_erase_size -> BlockEraseSize, 1, 0, 2);
field!(write_granularity -> WriteGranularity, 1, 2, 1);
field!(write_en_required -> bool, 1, 3, 1);
field!(write_en_opcode -> bool, 1, 4, 1);
field!(erase_opcode_4kib -> u8, 1, 8, 8);
field!(support_fast_read_112 -> bool, 1, 16, 1);
field!(address_modes -> SupportedAddressModes, 1, 17, 2);
field!(support_double_rate_clocking -> bool, 1, 19, 1);
field!(support_fast_read_122 -> bool, 1, 20, 1);
field!(support_fast_read_144 -> bool, 1, 21, 1);
field!(support_fast_read_114 -> bool, 1, 22, 1);
field!(density -> u32, 2, 0, 31);
field!(density_pow2 -> bool, 2, 31, 1);
field!(wait_states_144 -> u8, 3, 0, 5);
field!(mode_bits_144 -> u8, 3, 0, 3);
field!(read_opcode_144 -> u8, 3, 0, 8);
field!(wait_states_114 -> u8, 3, 16, 5);
field!(mode_bits_114 -> u8, 3, 21, 3);
field!(read_opcode_114 -> u8, 3, 24, 8);
field!(wait_states_122 -> u8, 4, 0, 5);
field!(mode_bits_122 -> u8, 4, 0, 3);
field!(read_opcode_122 -> u8, 4, 0, 8);
field!(wait_states_112 -> u8, 4, 16, 5);
field!(mode_bits_112 -> u8, 4, 21, 3);
field!(read_opcode_112 -> u8, 4, 22, 8);
field!(support_fast_read_222 -> bool, 5, 0, 1);
field!(support_fast_read_444 -> bool, 5, 4, 1);
field!(wait_states_222 -> u8, 6, 16, 5);
field!(mode_bits_222 -> u8, 6, 21, 3);
field!(read_opcode_222 -> u8, 6, 24, 8);
field!(wait_states_444 -> u8, 7, 16, 5);
field!(mode_bits_444 -> u8, 7, 21, 3);
field!(read_opcode_444 -> u8, 7, 24, 8);
field!(sector_type1_size -> u8, 8, 0, 8);
field!(sector_type1_opcode -> u8, 8, 8, 8);
field!(sector_type2_size -> u8, 8, 16, 8);
field!(sector_type2_opcode -> u8, 8, 24, 8);
field!(sector_type3_size -> u8, 9, 0, 8);
field!(sector_type3_opcode -> u8, 9, 8, 8);
field!(sector_type4_size -> u8, 9, 16, 8);
field!(sector_type4_opcode -> u8, 9, 24, 8);
// JESD216B pp. 20-21.
field!(erase_time_multiplier -> u32, 10, 0, 4);
field!(erase_type1_time -> u32, 10, 4, 5);
field!(erase_type1_unit -> usize, 10, 9, 2);
field!(erase_type2_time -> u32, 10, 11, 5);
field!(erase_type2_unit -> usize, 10, 16, 2);
field!(erase_type3_time -> u32, 10, 18, 5);
field!(erase_type3_unit -> usize, 10, 23, 2);
field!(erase_type4_time -> u32, 10, 25, 5);
field!(erase_type4_unit -> usize, 10, 30, 2);
// JESD216B pg. 22.
field!(pgm_time_multiplier -> u32, 11, 0, 4);
field!(page_size -> u32, 11, 4, 4);
field!(page_pgm_time -> u32, 11, 8, 5);
field!(page_pgm_unit -> usize, 11, 13, 1);
field!(byte_pgm_time -> u32, 11, 14, 4);
field!(byte_pgm_unit -> usize, 11, 18, 1);
field!(additional_byte_pgm_time -> u32, 11, 19, 4);
field!(additional_byte_pgm_unit -> usize, 11, 23, 1);
field!(chip_erase_time -> u32, 11, 24, 5);
field!(chip_erase_unit -> usize, 11, 29, 2);
// JESD216B pp. 23-24.
field!(prohibited_pgm_suspend -> u8, 12, 0, 4);
field!(prohibited_erase_suspend -> u8, 12, 4, 4);
field!(pgm_resume_to_suspend -> u32, 12, 9, 4);
field!(suspend_pgm_latency_time -> u32, 12, 13, 5);
field!(suspend_pgm_latency_unit -> usize, 12, 18, 2);
field!(erase_resume_to_suspend -> u32, 12, 20, 4);
field!(suspend_erase_latency_time -> u32, 12, 24, 5);
field!(suspend_erase_latency_unit -> usize, 12, 29, 2);
field!(suspend_resume_supported -> bool, 12, 31, 1);
// JESD216B pg. 25.
field!(pgm_resume_instruction -> u8, 13, 0, 8);
field!(pgm_suspend_instruction -> u8, 13, 8, 8);
field!(resume_instruction -> u8, 13, 16, 8);
field!(suspend_instruction -> u8, 13, 24, 8);
// JESD216B pg. 25.
field!(status_busy_polling -> u8, 14, 2, 6);
field!(exit_deep_powerdown_time -> u32, 14, 8, 5);
field!(exit_deep_powerdown_unit -> usize, 14, 13, 2);
field!(exit_deep_powerdown_instruction -> u8, 14, 15, 8);
field!(enter_deep_powerdown_instruction -> u8, 14, 23, 8);
field!(deep_powerdown_supported -> bool, 14, 31, 1);
// JESD216B pp. 26-27.
field!(mode_444_disable -> u8, 15, 0, 4);
field!(mode_444_enable -> u8, 15, 4, 5);
field!(mode_444_supported -> bool, 15, 9, 1);
field!(mode_444_exit -> u8, 15, 10, 6);
field!(mode_444_entry -> u8, 15, 16, 4);
field!(quad_enable_requirements -> u8, 15, 20, 3);
field!(hold_or_reset_disable -> bool, 15, 23, 1);
// JESD216B pp. 28-29.
field!(status_reg1_write_enable -> u8, 16, 0, 6);
field!(soft_reset_support -> u8, 16, 8, 6);
field!(exit_4b_addressing -> u16, 16, 14, 10);
field!(enter_4b_addressing -> u8, 16, 24, 8);
// JESD216D.01 pg. 44.
field!(wait_states_188 -> u8, 17, 0, 5);
field!(mode_bits_188 -> u8, 17, 5, 3);
field!(read_opcode_188 -> u8, 17, 8, 8);
field!(wait_states_118 -> u8, 17, 16, 5);
field!(mode_bits_118 -> u8, 17, 21, 3);
field!(read_opcode_118 -> u8, 17, 24, 8);
// JESD216D.01 pg. 45.
field!(output_driver_strength -> u8, 18, 18, 5);
field!(spi_protocol_reset -> bool, 18, 23, 1);
field!(data_strobe_str -> u8, 18, 24, 2);
field!(data_qpi_str -> bool, 18, 26, 1);
field!(data_qpi_dtr -> bool, 18, 27, 1);
field!(octal_dtr_command -> u8, 18, 29, 2);
field!(octal_byte_order -> u8, 18, 31, 1);
// JESD216D.01 pg. 48.
field!(mode_8s8s8s_disable-> u8, 19, 0, 4);
field!(mode_8s8s8s_enable-> u8, 19, 4, 5);
field!(mode_088_supported -> bool, 19, 9, 1);
field!(mode_088_exit -> u8, 19, 10, 6);
field!(mode_088_enter -> u8, 19, 16, 4);
field!(octal_enable_requirements -> u8, 19, 20, 3);
// JESD216D.01 pp. 49-51.
field!(max_speed_4s4s4s_no_strobe -> MaxSpeed, 20, 0, 4);
field!(max_speed_4s4s4s_strobe -> MaxSpeed, 20, 4, 4);
field!(max_speed_4d4d4d_no_strobe -> MaxSpeed, 20, 8, 4);
field!(max_speed_4d4d4d_strobe -> MaxSpeed, 20, 12, 4);
field!(max_speed_8s8s8s_no_strobe -> MaxSpeed, 20, 16, 4);
field!(max_speed_8s8s8s_strobe -> MaxSpeed, 20, 20, 4);
field!(max_speed_8d8d8d_no_strobe -> MaxSpeed, 20, 24, 4);
field!(max_speed_8d8d8d_strobe -> MaxSpeed, 20, 28, 4);
// JESD216F.01 pg. 52.
field!(support_fast_read_1s1d1d -> bool, 21, 0, 1);
field!(support_fast_read_1s2d2d -> bool, 21, 1, 1);
field!(support_fast_read_1s4d4d -> bool, 21, 2, 1);
field!(support_fast_read_4s4d4d -> bool, 21, 3, 1);
// JESD216F.01 pg. 53.
field!(wait_states_1s1d1d -> u8, 22, 0, 5);
field!(mode_bits_1s1d1d -> u8, 22, 5, 3);
field!(read_opcode_1s1d1d -> u8, 22, 8, 8);
field!(wait_states_1s2d2d -> u8, 22, 16, 5);
field!(mode_bits_1s2d2d -> u8, 22, 21, 3);
field!(read_opcode_1s2d2d -> u8, 22, 24, 8);
// JESD216F.01 pg. 54.
field!(wait_states_1s4d4d -> u8, 23, 0, 5);
field!(mode_bits_1s4d4d -> u8, 23, 5, 3);
field!(read_opcode_1s4d4d -> u8, 23, 8, 8);
field!(wait_states_4s4d4d -> u8, 23, 16, 5);
field!(mode_bits_4s4d4d -> u8, 23, 21, 3);
field!(read_opcode_4s4d4d -> u8, 23, 24, 8);
}
/// `BlockEraseSize` represents whether or not the device can perform
/// a 4KiB erase.
#[derive(Debug, Eq, PartialEq, FromPrimitive, Clone, Copy, Serialize)]
#[repr(u32)]
pub enum BlockEraseSize {
Reserved0 = 0,
Block4KiB = 1,
Reserved2 = 2,
BlockNot4KiB = 3,
#[num_enum(default)]
Invalid,
}
impl Default for BlockEraseSize {
fn default() -> Self {
BlockEraseSize::Invalid
}
}
/// `WriteGranularity` represents whether or not the device has an internal
/// buffer for program operations.
#[derive(Debug, Eq, PartialEq, FromPrimitive, Clone, Copy, Serialize)]
#[repr(u32)]
pub enum WriteGranularity {
Granularity1Byte = 0,
Granularity64Bytes = 1,
#[num_enum(default)]
Invalid,
}
impl Default for WriteGranularity {
fn default() -> Self {
WriteGranularity::Invalid
}
}
/// `SupportedAddressModes` represents which addressing modes are valid for
/// the device.
#[derive(Debug, Eq, PartialEq, FromPrimitive, Clone, Copy, Serialize)]
#[repr(u32)]
pub enum SupportedAddressModes {
Mode3b = 0,
Mode3b4b = 1,
Mode4b = 2,
Reserved = 3,
#[num_enum(default)]
Invalid,
}
impl Default for SupportedAddressModes {
fn default() -> Self {
SupportedAddressModes::Invalid
}
}
#[derive(Debug, Eq, PartialEq, FromPrimitive, Clone, Copy, Serialize)]
#[repr(u32)]
pub enum MaxSpeed {
Reserved0 = 0,
Speed33MHz,
Speed50MHz,
Speed66MHz,
Speed80MHz,
Speed100MHz,
Speed133MHz,
Speed166MHz,
Speed200MHz,
Speed250MHz,
Speed266MHz,
Speed333MHz,
Speed400MHz,
Reserved10,
NotCharacterized,
#[num_enum(default)]
NotSupported,
}
impl Default for MaxSpeed {
fn default() -> Self {
MaxSpeed::NotSupported
}
}
/// `FastReadParam` represents the parameters for the different styles of fast read.
#[derive(Default, Debug, Serialize, Annotate)]
pub struct FastReadParam {
pub wait_states: u8,
pub mode_bits: u8,
#[annotate(format=hex)]
pub opcode: u8,
}
/// `SectorErase` represents the supported erase sector sizes of the device.
#[derive(Default, Debug, Serialize, Annotate)]
pub struct SectorErase {
pub size: u32,
#[annotate(format=hex)]
pub opcode: u8,
pub time: Option<TimeBound>,
}
#[derive(Default, Debug, Serialize)]
pub struct TimeBound {
#[serde(with = "humantime_serde")]
pub typical: Duration,
#[serde(with = "humantime_serde")]
pub maximum: Duration,
}
/// The JEDEC parameter table is documented in JESD216 "Serial Flash Discoverable Parameters".
/// This structure represents the parameter table after decoding it from the packed bitfield
/// representation documented by JEDEC.
///
/// I have access to the following specs:
/// - JESD216 dated April 2011 (Flash parameters version 1.0).
/// - JESD216B dated May 2014 (Flash parameters version 1.6).
/// - JESD216D dated August 2019 (Flash parameters version 1.7, unchanged since rev C).
/// - JESD216F dated February 2022 (Flash parameters version 1.7, unchanged since rev D).
///
/// - Version 1.0 defines a table of 9 "dwords". I'm considering this to be the
/// minimum required parameter table.
/// - Rev B, version 1.6 extends the table to 16 "dwords", including information about
/// erase timing, powerdown and alternate mode requirements.
/// - Rev D, version 1.7 extends the table to 20 "dwords", including information about
/// eight-lane SPI modes.
/// - Rev F, version 1.7 extends the table to 23 "dwords", including information about
/// dual data rate operations.
#[derive(Default, Debug, Serialize, Annotate)]
pub struct JedecParams {
/// Erase granularity.
pub block_erase_size: BlockEraseSize,
/// Write granularity / buffer size.
pub write_granularity: WriteGranularity,
/// WREN instruction required for writing to volatile status register.
pub write_en_required: bool,
/// Write enable opocde (this is a single bit in the jedec table; expanded
/// to the actual opcode here).
#[annotate(format=hex)]
pub write_en_opcode: u8,
/// Erase opcode for 4KiB sector erase.
#[annotate(format=hex)]
pub erase_opcode_4kib: u8,
/// Support Fast Read 1-1-2.
pub support_fast_read_112: bool,
/// The address modes supported by the device.
pub address_modes: SupportedAddressModes,
/// Device supports double rate clocking.
pub support_double_rate_clocking: bool,
/// Other styles of Fast Read. The numerical designator refers to
/// the instruction transfer mode, the address transfer mode and data
/// transfer mode.
/// ie: 1-2-2 means single bit mode for the opcode, dual mode for the address
/// and dual mode for the data phase.
pub support_fast_read_122: bool,
pub support_fast_read_144: bool,
pub support_fast_read_114: bool,
pub support_fast_read_222: bool,
pub support_fast_read_444: bool,
/// The density of the part. In the jedec table, this is expressed in bits,
/// but it is converted to bytes here.
pub density: u32,
/// Parameters for the various supported FastRead modes.
pub param_112: FastReadParam,
pub param_122: FastReadParam,
pub param_114: FastReadParam,
pub param_144: FastReadParam,
pub param_222: FastReadParam,
pub param_444: FastReadParam,
/// Erase sector sizes. It is common for devices to support a 4KiB erase
/// size, a 32KiB erase size and a 64KiB erase size.
pub erase: [SectorErase; 4],
// TODO: Get ahold of Revs A, C & E and restructure the revision extensions.
pub rev_b: Option<JedecParamsRevB>,
pub rev_d: Option<JedecParamsRevD>,
pub rev_f: Option<JedecParamsRevF>,
}
/// The Rev B extensions to the JEDEC parameters table.
#[derive(Default, Debug, Serialize, Annotate)]
pub struct JedecParamsRevB {
pub page_size: u32,
pub page_program_time: TimeBound,
pub byte_program_time: TimeBound,
pub additional_byte_program_time: TimeBound,
pub chip_erase_time: TimeBound,
pub suspend_resume_supported: bool,
pub prohibited_ops_program_suspend: u8,
pub prohibited_ops_erase_suspend: u8,
#[serde(with = "humantime_serde")]
pub program_resume_to_suspend: Duration,
#[serde(with = "humantime_serde")]
pub suspend_in_progress_program_latency: Duration,
#[serde(with = "humantime_serde")]
pub erase_resume_to_suspend: Duration,
#[serde(with = "humantime_serde")]
pub suspend_in_progress_erase_latency: Duration,
#[annotate(format=hex)]
pub program_resume_instruction: u8,
#[annotate(format=hex)]
pub program_suspend_instruction: u8,
#[annotate(format=hex)]
pub resume_instruction: u8,
#[annotate(format=hex)]
pub suspend_instruction: u8,
pub deep_powerdown_supported: bool,
#[annotate(format=hex)]
pub enter_deep_powerdown_instruction: u8,
#[annotate(format=hex)]
pub exit_deep_powerdown_instruction: u8,
#[serde(with = "humantime_serde")]
pub exit_deep_powerdown_delay: Duration,
pub status_register_polling: u8,
pub hold_or_reset_disable: bool,
pub quad_enable_requirements: u8,
pub mode_444_entry: u8,
pub mode_444_exit: u8,
pub mode_444_supported: bool,
pub mode_444_disable: u8,
pub mode_444_enable: u8,
pub enter_4b_addressing: u8,
pub exit_4b_addressing: u16,
pub soft_reset_support: u8,
pub status_reg1_write_enable: u8,
}
/// The Rev D extensions to the JEDEC parameters table.
#[derive(Default, Debug, Serialize)]
pub struct JedecParamsRevD {
pub param_118: FastReadParam,
pub param_188: FastReadParam,
pub output_driver_strength: u8,
pub jedec_spi_protocol_reset: bool,
pub data_strobe_waveform_str: u8,
pub data_strobe_support_4s4s4s: bool,
pub data_strobe_support_4d4d4d: bool,
pub octal_dtr_command: u8,
pub octal_byte_order: u8,
pub mode_8s8s8s_disable: u8,
pub mode_8s8s8s_enable: u8,
pub mode_088_supported: bool,
pub mode_088_exit: u8,
pub mode_088_enter: u8,
pub octal_enable_requirements: u8,
pub max_speed_4s4s4s_no_strobe: MaxSpeed,
pub max_speed_4s4s4s_strobe: MaxSpeed,
pub max_speed_4d4d4d_no_strobe: MaxSpeed,
pub max_speed_4d4d4d_strobe: MaxSpeed,
pub max_speed_8s8s8s_no_strobe: MaxSpeed,
pub max_speed_8s8s8s_strobe: MaxSpeed,
pub max_speed_8d8d8d_no_strobe: MaxSpeed,
pub max_speed_8d8d8d_strobe: MaxSpeed,
}
/// The Rev F extensions to the JEDEC parameters table.
#[derive(Default, Debug, Serialize)]
pub struct JedecParamsRevF {
pub param_1s1d1d: Option<FastReadParam>,
pub param_1s2d2d: Option<FastReadParam>,
pub param_1s4d4d: Option<FastReadParam>,
pub param_4s4d4d: Option<FastReadParam>,
}
impl JedecParams {
// JESD216B section 6.4.13 (pg. 20)
const ERASE_TIME_UNITS: [Duration; 4] = [
Duration::from_millis(1),
Duration::from_millis(16),
Duration::from_millis(128),
Duration::from_secs(1),
];
// JESD216B section 6.4.14 (pg. 22)
const CHIP_ERASE_UNITS: [Duration; 4] = [
Duration::from_millis(16),
Duration::from_millis(256),
Duration::from_secs(4),
Duration::from_secs(64),
];
// JESD216B section 6.4.14 (pg. 22)
const PAGE_PGM_UNITS: [Duration; 2] = [Duration::from_micros(8), Duration::from_micros(64)];
// JESD216B section 6.4.14 (pg. 22)
const BYTE_PGM_UNITS: [Duration; 2] = [Duration::from_micros(1), Duration::from_micros(8)];
// JESD216B section 6.4.15 (pg. 23)
const SUSPEND_RESUME_UNITS: [Duration; 4] = [
Duration::from_nanos(128),
Duration::from_micros(1),
Duration::from_micros(8),
Duration::from_micros(64),
];
fn pow2_size(shift: u8) -> u32 {
if shift == 0 {
0
} else {
1u32 << shift
}
}
}
impl TryFrom<&[u8]> for JedecParams {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let p = InternalJedecParams::try_from(buf)?;
let size_bytes = if p.density_pow2()? {
1u32 << ((p.density()? & 0x7FFF_FFFF) - 8)
} else {
(p.density()? + 1) / 8
};
let mut jedec = JedecParams {
block_erase_size: p.block_erase_size()?,
write_granularity: p.write_granularity()?,
write_en_required: p.write_en_required()?,
write_en_opcode: if p.write_en_opcode()? { 0x50 } else { 0x06 },
erase_opcode_4kib: p.erase_opcode_4kib()?,
support_fast_read_112: p.support_fast_read_112()?,
address_modes: p.address_modes()?,
support_double_rate_clocking: p.support_double_rate_clocking()?,
support_fast_read_122: p.support_fast_read_122()?,
support_fast_read_144: p.support_fast_read_144()?,
support_fast_read_114: p.support_fast_read_114()?,
support_fast_read_222: p.support_fast_read_222()?,
support_fast_read_444: p.support_fast_read_444()?,
density: size_bytes,
erase: [
SectorErase {
size: Self::pow2_size(p.sector_type1_size()?),
opcode: p.sector_type1_opcode()?,
time: None,
},
SectorErase {
size: Self::pow2_size(p.sector_type2_size()?),
opcode: p.sector_type2_opcode()?,
time: None,
},
SectorErase {
size: Self::pow2_size(p.sector_type3_size()?),
opcode: p.sector_type3_opcode()?,
time: None,
},
SectorErase {
size: Self::pow2_size(p.sector_type4_size()?),
opcode: p.sector_type4_opcode()?,
time: None,
},
],
..Default::default()
};
if jedec.support_fast_read_112 {
jedec.param_112 = FastReadParam {
wait_states: p.wait_states_112()?,
mode_bits: p.mode_bits_112()?,
opcode: p.read_opcode_112()?,
}
}
if jedec.support_fast_read_122 {
jedec.param_122 = FastReadParam {
wait_states: p.wait_states_122()?,
mode_bits: p.mode_bits_122()?,
opcode: p.read_opcode_122()?,
}
}
if jedec.support_fast_read_144 {
jedec.param_144 = FastReadParam {
wait_states: p.wait_states_144()?,
mode_bits: p.mode_bits_144()?,
opcode: p.read_opcode_144()?,
}
}
if jedec.support_fast_read_114 {
jedec.param_114 = FastReadParam {
wait_states: p.wait_states_114()?,
mode_bits: p.mode_bits_114()?,
opcode: p.read_opcode_114()?,
}
}
if jedec.support_fast_read_222 {
jedec.param_222 = FastReadParam {
wait_states: p.wait_states_222()?,
mode_bits: p.mode_bits_222()?,
opcode: p.read_opcode_222()?,
}
}
if jedec.support_fast_read_444 {
jedec.param_444 = FastReadParam {
wait_states: p.wait_states_444()?,
mode_bits: p.mode_bits_444()?,
opcode: p.read_opcode_444()?,
}
}
if p.data.len() >= 16 {
let mult = 2 * (p.erase_time_multiplier()? + 1);
let type1 = Self::ERASE_TIME_UNITS[p.erase_type1_unit()?] * (p.erase_type1_time()? + 1);
let type2 = Self::ERASE_TIME_UNITS[p.erase_type2_unit()?] * (p.erase_type2_time()? + 1);
let type3 = Self::ERASE_TIME_UNITS[p.erase_type3_unit()?] * (p.erase_type3_time()? + 1);
let type4 = Self::ERASE_TIME_UNITS[p.erase_type4_unit()?] * (p.erase_type4_time()? + 1);
jedec.erase[0].time = Some(TimeBound {
typical: type1,
maximum: type1 * mult,
});
jedec.erase[1].time = Some(TimeBound {
typical: type2,
maximum: type2 * mult,
});
jedec.erase[2].time = Some(TimeBound {
typical: type3,
maximum: type3 * mult,
});
jedec.erase[3].time = Some(TimeBound {
typical: type4,
maximum: type4 * mult,
});
let mult = 2 * (p.pgm_time_multiplier()? + 1);
let chip_erase_unit = Self::CHIP_ERASE_UNITS[p.chip_erase_unit()?];
let page_pgm_unit = Self::PAGE_PGM_UNITS[p.page_pgm_unit()?];
let byte_pgm_unit = Self::BYTE_PGM_UNITS[p.byte_pgm_unit()?];
let additional_byte_pgm_unit = Self::BYTE_PGM_UNITS[p.additional_byte_pgm_unit()?];
jedec.rev_b = Some(JedecParamsRevB {
page_size: 1u32 << p.page_size()?,
page_program_time: TimeBound {
typical: page_pgm_unit * (1 + p.page_pgm_time()?),
maximum: page_pgm_unit * (1 + p.page_pgm_time()?) * mult,
},
byte_program_time: TimeBound {
typical: byte_pgm_unit * (1 + p.byte_pgm_time()?),
maximum: byte_pgm_unit * (1 + p.byte_pgm_time()?) * mult,
},
additional_byte_program_time: TimeBound {
typical: additional_byte_pgm_unit * (1 + p.additional_byte_pgm_time()?),
maximum: additional_byte_pgm_unit * (1 + p.additional_byte_pgm_time()?) * mult,
},
chip_erase_time: TimeBound {
typical: chip_erase_unit * (1 + p.chip_erase_time()?),
maximum: chip_erase_unit * (1 + p.chip_erase_time()?) * mult,
},
suspend_resume_supported: !p.suspend_resume_supported()?,
prohibited_ops_program_suspend: p.prohibited_pgm_suspend()?,
prohibited_ops_erase_suspend: p.prohibited_erase_suspend()?,
program_resume_to_suspend: Duration::from_millis(64)
* (1 + p.pgm_resume_to_suspend()?),
suspend_in_progress_program_latency: Self::SUSPEND_RESUME_UNITS
[p.suspend_pgm_latency_unit()?]
* (1 + p.suspend_pgm_latency_time()?),
erase_resume_to_suspend: Duration::from_millis(64)
* (1 + p.erase_resume_to_suspend()?),
suspend_in_progress_erase_latency: Self::SUSPEND_RESUME_UNITS
[p.suspend_erase_latency_unit()?]
* (1 + p.suspend_erase_latency_time()?),
program_resume_instruction: p.pgm_resume_instruction()?,
program_suspend_instruction: p.pgm_suspend_instruction()?,
resume_instruction: p.suspend_instruction()?,
suspend_instruction: p.resume_instruction()?,
deep_powerdown_supported: !p.deep_powerdown_supported()?,
enter_deep_powerdown_instruction: p.enter_deep_powerdown_instruction()?,
exit_deep_powerdown_instruction: p.exit_deep_powerdown_instruction()?,
exit_deep_powerdown_delay: Self::SUSPEND_RESUME_UNITS
[p.exit_deep_powerdown_unit()?]
* (1 + p.exit_deep_powerdown_time()?),
status_register_polling: p.status_busy_polling()?,
hold_or_reset_disable: p.hold_or_reset_disable()?,
quad_enable_requirements: p.quad_enable_requirements()?,
mode_444_entry: p.mode_444_entry()?,
mode_444_exit: p.mode_444_exit()?,
mode_444_supported: p.mode_444_supported()?,
mode_444_disable: p.mode_444_disable()?,
mode_444_enable: p.mode_444_enable()?,
enter_4b_addressing: p.enter_4b_addressing()?,
exit_4b_addressing: p.exit_4b_addressing()?,
soft_reset_support: p.soft_reset_support()?,
status_reg1_write_enable: p.status_reg1_write_enable()?,
});
}
if p.data.len() >= 20 {
jedec.rev_d = Some(JedecParamsRevD {
param_118: FastReadParam {
wait_states: p.wait_states_118()?,
mode_bits: p.mode_bits_118()?,
opcode: p.read_opcode_118()?,
},
param_188: FastReadParam {
wait_states: p.wait_states_188()?,
mode_bits: p.mode_bits_188()?,
opcode: p.read_opcode_188()?,
},
output_driver_strength: p.output_driver_strength()?,
jedec_spi_protocol_reset: p.spi_protocol_reset()?,
data_strobe_waveform_str: p.data_strobe_str()?,
data_strobe_support_4s4s4s: p.data_qpi_str()?,
data_strobe_support_4d4d4d: p.data_qpi_dtr()?,
octal_dtr_command: p.octal_dtr_command()?,
octal_byte_order: p.octal_byte_order()?,
mode_8s8s8s_disable: p.mode_8s8s8s_disable()?,
mode_8s8s8s_enable: p.mode_8s8s8s_enable()?,
mode_088_supported: p.mode_088_supported()?,
mode_088_exit: p.mode_088_exit()?,
mode_088_enter: p.mode_088_enter()?,
octal_enable_requirements: p.octal_enable_requirements()?,
max_speed_4s4s4s_no_strobe: p.max_speed_4s4s4s_no_strobe()?,
max_speed_4s4s4s_strobe: p.max_speed_4s4s4s_strobe()?,
max_speed_4d4d4d_no_strobe: p.max_speed_4d4d4d_no_strobe()?,
max_speed_4d4d4d_strobe: p.max_speed_4d4d4d_strobe()?,
max_speed_8s8s8s_no_strobe: p.max_speed_8s8s8s_no_strobe()?,
max_speed_8s8s8s_strobe: p.max_speed_8s8s8s_strobe()?,
max_speed_8d8d8d_no_strobe: p.max_speed_8d8d8d_no_strobe()?,
max_speed_8d8d8d_strobe: p.max_speed_8d8d8d_strobe()?,
});
}
if p.data.len() >= 23 {
let mut rev_f = JedecParamsRevF::default();
if p.support_fast_read_1s1d1d()? {
rev_f.param_1s1d1d = Some(FastReadParam {
wait_states: p.wait_states_1s1d1d()?,
mode_bits: p.mode_bits_1s1d1d()?,
opcode: p.read_opcode_1s1d1d()?,
});
}
if p.support_fast_read_1s2d2d()? {
rev_f.param_1s2d2d = Some(FastReadParam {
wait_states: p.wait_states_1s2d2d()?,
mode_bits: p.mode_bits_1s2d2d()?,
opcode: p.read_opcode_1s2d2d()?,
});
}
if p.support_fast_read_1s4d4d()? {
rev_f.param_1s4d4d = Some(FastReadParam {
wait_states: p.wait_states_1s4d4d()?,
mode_bits: p.mode_bits_1s4d4d()?,
opcode: p.read_opcode_1s4d4d()?,
});
}
if p.support_fast_read_4s4d4d()? {
rev_f.param_4s4d4d = Some(FastReadParam {
wait_states: p.wait_states_4s4d4d()?,
mode_bits: p.mode_bits_4s4d4d()?,
opcode: p.read_opcode_4s4d4d()?,
});
}
jedec.rev_f = Some(rev_f);
}
Ok(jedec)
}
}
/// An `UnknownParams` structure represents SFDP parameter tables for which
/// we don't have a specialized parser.
#[derive(Debug, Serialize)]
pub struct UnknownParams {
pub data: Vec<u32>,
}
impl TryFrom<&[u8]> for UnknownParams {
type Error = Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let mut reader = std::io::Cursor::new(buf);
let mut data = vec![0u32; buf.len() / 4];
reader.read_u32_into::<LittleEndian>(&mut data)?;
Ok(UnknownParams { data })
}
}
/// The `Sfdp` structure represents the decoded SFDP table.
#[derive(Debug, Serialize)]
pub struct Sfdp {
pub header: SfdpHeader,
pub phdr: Vec<SfdpPhdr>,
pub jedec: JedecParams,
pub params: Vec<UnknownParams>,
}
impl Sfdp {
/// Given an initial SFDP buffer calculate the number of bytes needed for
/// the entire SFDP.
pub fn length_required(buf: &[u8]) -> Result<usize, Error> {
if buf.len() < 256 {
// Technically, we could read out the first 8-bytes, then
// figure out how many phdrs there are, read out that number
// times 8-byte chunks, then find the max extent of the pointed-to
// headers and perform the calculation in the else-arm below.
//
// We punt and read out the first 256 bytes, which is often more
// than enough. In the event it isn't, we assume the first 256
// bytes will contain all of the phdrs (it will) and perform
// the max-extent calculation below.
Ok(256)
} else {
let header = SfdpHeader::try_from(&buf[0..8])?;
let mut len = 0;
for i in 0..=header.nph {
let start = (8 + i * 8) as usize;
let end = start + 8;
let phdr = SfdpPhdr::try_from(&buf[start..end])?;
len = std::cmp::max(len, phdr.offset + (phdr.dwords * 4) as u32);
log::debug!("computed sfdp len = {}", len);
}
Ok(len as usize)
}
}
}
/// Convert a byte buffer into an SFDP structure.
impl TryFrom<&[u8]> for Sfdp {
type Error = anyhow::Error;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
let header = SfdpHeader::try_from(buf.get(0..8).ok_or(Error::SliceRange(0, 8))?)?;
let mut phdr = Vec::new();
for i in 0..=header.nph {
let start = (8 + i * 8) as usize;
let end = start + 8;
phdr.push(SfdpPhdr::try_from(
buf.get(start..end).ok_or(Error::SliceRange(start, end))?,
)?);
}
let start = phdr[0].offset as usize;
let end = start + phdr[0].dwords as usize * 4;
let jedec =
JedecParams::try_from(buf.get(start..end).ok_or(Error::SliceRange(start, end))?)?;
let mut params = Vec::new();
for ph in phdr.iter().take((header.nph as usize) + 1).skip(1) {
let start = ph.offset as usize;
let end = start + ph.dwords as usize * 4;
params.push(UnknownParams::try_from(
buf.get(start..end).ok_or(Error::SliceRange(start, end))?,
)?);
}
Ok(Sfdp {
header,
phdr,
jedec,
params,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use anyhow::Result;
use humantime::parse_duration;
#[rustfmt::skip]
const SFDP_MX66L1G: &[u8; 512] = include_bytes!("SFDP_MX66L1G.bin");
#[allow(clippy::bool_assert_comparison)]
#[test]
fn test_decode_mx66l1g() -> Result<()> {
let sfdp = Sfdp::try_from(&SFDP_MX66L1G[..])?;
// A simple spot-check of values from the SFDP table.
assert_eq!(sfdp.header.signature, 0x50444653);
assert_eq!(sfdp.header.major, 1);
assert_eq!(sfdp.header.minor, 6);
assert_eq!(sfdp.header.nph, 2);
assert_eq!(sfdp.jedec.block_erase_size, BlockEraseSize::Block4KiB);
assert_eq!(sfdp.jedec.write_en_required, false);
assert_eq!(sfdp.jedec.write_en_opcode, 0x06);
assert_eq!(sfdp.jedec.erase_opcode_4kib, 0x20);
assert_eq!(sfdp.jedec.support_fast_read_112, true);
assert_eq!(sfdp.jedec.address_modes, SupportedAddressModes::Mode3b4b);
assert_eq!(sfdp.jedec.density, 128 * 1024 * 1024);
assert_eq!(sfdp.jedec.erase[0].size, 4096);
assert_eq!(sfdp.jedec.erase[0].opcode, 0x20);
// The datasheet says typ=30ms, max=400ms. The SFDP-reported
// erase timings are advisory, have a limited representations
// and won't match the data sheet exactly.
assert_eq!(
sfdp.jedec.erase[0].time.as_ref().unwrap().typical,
parse_duration("30ms")?
);
assert_eq!(
sfdp.jedec.erase[0].time.as_ref().unwrap().maximum,
parse_duration("420ms")?
);
assert_eq!(sfdp.jedec.erase[1].size, 32768);
assert_eq!(sfdp.jedec.erase[1].opcode, 0x52);
// The datasheet says typ=0.15s, max=1s.
assert_eq!(
sfdp.jedec.erase[1].time.as_ref().unwrap().typical,
parse_duration("160ms")?
);
assert_eq!(
sfdp.jedec.erase[1].time.as_ref().unwrap().maximum,
parse_duration("2s 240ms")?
);
assert_eq!(sfdp.jedec.erase[2].size, 65536);
assert_eq!(sfdp.jedec.erase[2].opcode, 0xd8);
// The datasheet says typ=0.28s, max=2s.
assert_eq!(
sfdp.jedec.erase[2].time.as_ref().unwrap().typical,
parse_duration("288ms")?
);
assert_eq!(
sfdp.jedec.erase[2].time.as_ref().unwrap().maximum,
parse_duration("4s 32ms")?
);
assert_eq!(sfdp.jedec.erase[3].size, 0);
assert_eq!(sfdp.jedec.erase[3].opcode, 0xff);
let rev_b = sfdp.jedec.rev_b.as_ref().expect("rev_b parameters");
assert_eq!(rev_b.page_size, 256);
// The datasheet says typ=0.25ms, max=3ms.
assert_eq!(rev_b.page_program_time.typical, parse_duration("256us")?);
assert_eq!(rev_b.page_program_time.maximum, parse_duration("3ms 72us")?);
// The datasheet says typ=25us, max=60us.
assert_eq!(rev_b.byte_program_time.typical, parse_duration("32us")?);
assert_eq!(rev_b.byte_program_time.maximum, parse_duration("384us")?);
// The datasheet doesn't mention the additional byte programming time.
assert_eq!(
rev_b.additional_byte_program_time.typical,
parse_duration("1us")?
);
assert_eq!(
rev_b.additional_byte_program_time.maximum,
parse_duration("12us")?
);
// The datasheet says typ=200s, max=600s. The typical time is the closest
// possible representation given the range and units available in the SFDP.
assert_eq!(rev_b.chip_erase_time.typical, parse_duration("4m 16s")?);
assert_eq!(rev_b.chip_erase_time.maximum, parse_duration("51m 12s")?);
// The particular MX66L1G sampled doesn't have a RevD or RevF table.
assert!(sfdp.jedec.rev_d.is_none());
assert!(sfdp.jedec.rev_f.is_none());
Ok(())
}
// Regression test for https://github.com/lowRISC/opentitan/issues/13477
#[test]
fn test_bad_header_signature() -> Result<()> {
let buf = &[255u8; 256];
let sfdp = Sfdp::try_from(&buf[..]);
assert!(sfdp.is_err());
let err = sfdp.unwrap_err();
assert_eq!(
err.to_string(),
"SFDP header contains incorrect signature: 0xffffffff"
);
Ok(())
}
}