use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::panic;
use onerom_config::chip::{ChipFunction, ChipType};
use onerom_config::fw::{FirmwareVersion, ServeAlg};
use onerom_config::hw::Board;
use onerom_config::mcu::Family as McuFamily;
use crate::meta::{
CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN, CHIP_SET_METADATA_LEN,
CHIP_SET_METADATA_LEN_EXTRA_INFO,
};
use crate::{Error, Result, builder::FirmwareConfig};
use crate::{MIN_FIRMWARE_OVERRIDES_VERSION, PAD_METADATA_BYTE};
pub const PAD_BLANK_BYTE: u8 = 0xAA;
pub const PAD_NO_CHIP_BYTE: u8 = 0xAA;
pub const PAD_RAM_BYTE: u8 = 0x55;
const CHIP_METADATA_LEN_NO_FILENAME: usize = 4;
const CHIP_METADATA_LEN_WITH_FILENAME: usize = 8;
const MIN_FW_VER_FIRE_28_18_ADDR_PINS: FirmwareVersion = FirmwareVersion::new(0, 6, 3, 0);
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum SizeHandling {
#[default]
None,
#[serde(alias = "dup")]
Duplicate,
#[serde(alias = "trunc")]
Truncate,
Pad,
}
impl SizeHandling {
pub fn supported_values() -> &'static [Self; 4] {
&[
SizeHandling::None,
SizeHandling::Duplicate,
SizeHandling::Truncate,
SizeHandling::Pad,
]
}
pub fn is_none(&self) -> bool {
matches!(self, SizeHandling::None)
}
}
impl core::fmt::Display for SizeHandling {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
SizeHandling::None => write!(f, "none"),
SizeHandling::Duplicate => write!(f, "duplicate"),
SizeHandling::Truncate => write!(f, "truncate"),
SizeHandling::Pad => write!(f, "pad"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum CsLogic {
ActiveLow,
ActiveHigh,
Ignore,
}
impl core::fmt::Display for CsLogic {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CsLogic::ActiveLow => write!(f, "active low"),
CsLogic::ActiveHigh => write!(f, "active high"),
CsLogic::Ignore => write!(f, "ignore"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub struct Location {
pub start: usize,
pub length: usize,
}
impl CsLogic {
pub fn try_from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"0" => Some(CsLogic::ActiveLow),
"1" => Some(CsLogic::ActiveHigh),
"ignore" => Some(CsLogic::Ignore),
_ => None,
}
}
pub fn c_value(&self) -> &str {
match self {
CsLogic::ActiveLow => "CS_ACTIVE_LOW",
CsLogic::ActiveHigh => "CS_ACTIVE_HIGH",
CsLogic::Ignore => "CS_NOT_USED",
}
}
pub fn c_enum_val(&self) -> u8 {
match self {
CsLogic::ActiveLow => 0,
CsLogic::ActiveHigh => 1,
CsLogic::Ignore => 2,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum CsConfig {
ChipSelect {
cs1: CsLogic,
cs2: Option<CsLogic>,
cs3: Option<CsLogic>,
},
CeOe,
}
impl CsConfig {
pub fn new(cs1: Option<CsLogic>, cs2: Option<CsLogic>, cs3: Option<CsLogic>) -> Self {
if cs1.is_none() && cs2.is_none() && cs3.is_none() {
Self::CeOe
} else {
let cs1 = cs1.expect("CS1 must be specified if any CS lines are used");
Self::ChipSelect { cs1, cs2, cs3 }
}
}
pub fn cs1_logic(&self) -> CsLogic {
match self {
CsConfig::ChipSelect { cs1, .. } => *cs1,
CsConfig::CeOe => CsLogic::ActiveLow,
}
}
pub fn cs2_logic(&self) -> Option<CsLogic> {
match self {
CsConfig::ChipSelect { cs2, .. } => *cs2,
CsConfig::CeOe => Some(CsLogic::ActiveLow),
}
}
pub fn cs3_logic(&self) -> Option<CsLogic> {
match self {
CsConfig::ChipSelect { cs3, .. } => *cs3,
CsConfig::CeOe => None,
}
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Chip {
index: usize,
filename: String,
label: Option<String>,
chip_type: ChipType,
cs_config: CsConfig,
data: Option<Vec<u8>>,
location: Option<Location>,
}
impl Chip {
fn new(
index: usize,
filename: String,
label: Option<String>,
chip_type: &ChipType,
cs_config: CsConfig,
data: Option<Vec<u8>>,
location: Option<Location>,
) -> Self {
Self {
index,
filename,
label,
chip_type: *chip_type,
cs_config,
data,
location,
}
}
pub fn index(&self) -> usize {
self.index
}
pub fn cs_config(&self) -> &CsConfig {
&self.cs_config
}
pub fn filename(&self) -> &str {
self.label.as_deref().unwrap_or(&self.filename)
}
pub fn chip_type(&self) -> &ChipType {
&self.chip_type
}
pub fn has_data(&self) -> bool {
self.data.is_some()
}
#[allow(clippy::too_many_arguments)]
pub fn from_raw_rom_image(
index: usize,
filename: String,
label: Option<String>,
source: Option<&[u8]>,
mut dest: Vec<u8>,
chip_type: &ChipType,
cs_config: CsConfig,
size_handling: &SizeHandling,
location: Option<Location>,
) -> Result<Self> {
if source.is_none() {
if chip_type.chip_function() == ChipFunction::Ram {
return Ok(Self::new(
index, filename, label, chip_type, cs_config, None, location,
));
} else {
return Err(Error::MissingFile { id: index });
}
}
let source = source.unwrap();
let source = if let Some(loc) = location {
let end = loc
.start
.checked_add(loc.length)
.ok_or(Error::BadLocation {
id: index,
reason: format!(
"Location overflow: start={:#X} length={:#X}",
loc.start, loc.length
),
})?;
if end > source.len() {
return Err(Error::ImageTooSmall {
chip_type: *chip_type,
index,
expected: end,
actual: source.len(),
});
}
&source[loc.start..end]
} else {
source
};
let expected_size = chip_type.size_bytes();
if dest.len() < expected_size {
return Err(Error::BufferTooSmall {
location: "Chip::from_raw_rom_image",
expected: expected_size,
actual: dest.len(),
});
}
let expected_size = if *chip_type == ChipType::Chip27C080 {
expected_size / 2
} else {
expected_size
};
match source.len().cmp(&expected_size) {
Ordering::Equal => {
match size_handling {
SizeHandling::None => {
dest[..expected_size].copy_from_slice(&source[..expected_size]);
}
_ => {
return Err(Error::RightSize {
chip_type: *chip_type,
size: expected_size,
size_handling: size_handling.clone(),
});
}
}
}
Ordering::Less => {
match size_handling {
SizeHandling::None => {
if chip_type.is_plugin() {
dest[..source.len()].copy_from_slice(source);
for byte in &mut dest[source.len()..expected_size] {
*byte = PAD_BLANK_BYTE;
}
} else {
return Err(Error::ImageTooSmall {
chip_type: *chip_type,
index,
expected: expected_size,
actual: source.len(),
});
}
}
SizeHandling::Duplicate => {
if !expected_size.is_multiple_of(source.len()) {
return Err(Error::DuplicationNotExactDivisor {
chip_type: *chip_type,
image_size: source.len(),
expected_size,
});
}
let multiples = expected_size / source.len();
for i in 0..multiples {
let start = i * source.len();
let end = start + source.len();
dest[start..end].copy_from_slice(source);
}
}
SizeHandling::Pad => {
dest[..source.len()].copy_from_slice(source);
for byte in &mut dest[source.len()..expected_size] {
*byte = PAD_BLANK_BYTE;
}
}
SizeHandling::Truncate => {
return Err(Error::ImageTooLarge {
chip_type: *chip_type,
image_size: source.len(),
expected_size,
});
}
}
}
Ordering::Greater => {
match size_handling {
SizeHandling::Truncate => {
dest[..expected_size].copy_from_slice(&source[..expected_size]);
}
_ => {
return Err(Error::ImageTooLarge {
chip_type: *chip_type,
image_size: source.len(),
expected_size,
});
}
}
}
}
Ok(Self::new(
index,
filename,
label,
chip_type,
cs_config,
Some(dest),
location,
))
}
fn address_to_logical(
phys_pin_to_addr_map: &[Option<usize>],
address: usize,
_board: &Board,
num_addr_lines: usize,
) -> usize {
let mut result = 0;
for (pin, item) in phys_pin_to_addr_map.iter().enumerate() {
#[allow(clippy::collapsible_if)]
if let Some(addr_bit) = item {
if *addr_bit < num_addr_lines {
if (address & (1 << pin)) != 0 {
result |= 1 << addr_bit;
}
}
}
}
result
}
fn byte_mangled(byte: u8, board: &Board) -> u8 {
let mut result = 0;
let data_pins = board.data_pins();
#[allow(clippy::needless_range_loop)]
for bit_pos in 0..8 {
if (byte & (1 << bit_pos)) != 0 {
let mut new_pos = data_pins[bit_pos];
if new_pos > 15 {
assert!(new_pos < 24);
new_pos -= 16;
} else {
assert!(new_pos < 8);
}
result |= 1 << new_pos;
}
}
result
}
fn get_byte_raw(&self, address: usize) -> u8 {
let data = self
.data
.as_ref()
.expect("Shouldn't be called get_byte_raw on empty image");
data.get(address).copied().unwrap_or_else(|| {
panic!(
"Address {} out of bounds for Chip image of size {}",
address,
data.len()
)
})
}
fn get_byte(
&self,
phys_pin_to_addr_map: &[Option<usize>],
address: usize,
board: &Board,
) -> u8 {
let data = self
.data
.as_ref()
.expect("Shouldn't be called get_byte on empty image");
let num_addr_lines = self.chip_type.num_addr_lines();
let transformed_address =
Self::address_to_logical(phys_pin_to_addr_map, address, board, num_addr_lines);
if transformed_address >= data.len() {
panic!(
"Transformed address {} out of bounds for Chip image of size {}",
transformed_address,
data.len()
);
}
let byte = data.get(transformed_address).copied().unwrap_or_else(|| {
panic!(
"Address {} out of bounds for Chip image of size {}",
transformed_address,
data.len()
)
});
Self::byte_mangled(byte, board)
}
fn chip_type_c_enum_val(&self) -> u8 {
match self.chip_type {
ChipType::Chip2316 => 0,
ChipType::Chip2332 => 1,
ChipType::Chip2364 => 2,
ChipType::Chip23128 => 3,
ChipType::Chip23256 => 4,
ChipType::Chip23512 => 5,
ChipType::Chip2704 => 6,
ChipType::Chip2708 => 7,
ChipType::Chip2716 => 8,
ChipType::Chip2732 => 9,
ChipType::Chip2764 => 10,
ChipType::Chip27128 => 11,
ChipType::Chip27256 => 12,
ChipType::Chip27512 => 13,
ChipType::Chip231024 => 14,
ChipType::Chip23C1010 | ChipType::Chip27C010 => 15,
ChipType::Chip27C020 => 16,
ChipType::Chip27C040 => 17,
ChipType::Chip27C080 => 18,
ChipType::Chip27C400 => 19,
ChipType::Chip6116 => 20,
ChipType::Chip27C301 => 21,
ChipType::SystemPlugin => 22,
ChipType::UserPlugin => 23,
ChipType::PioPlugin => 24,
ChipType::ChipSST39SF040 => 25,
ChipType::Chip28C16 => 26,
ChipType::Chip28C64 => 27,
ChipType::Chip28C256 => 28,
ChipType::Chip28C512 => 29,
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum ChipSetType {
#[default]
Single,
Banked,
Multi,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ChipSet {
pub id: usize,
pub set_type: ChipSetType,
pub serve_alg: ServeAlg,
pub chips: Vec<Chip>,
pub firmware_overrides: Option<FirmwareConfig>,
}
impl ChipSet {
pub fn new(
id: usize,
set_type: ChipSetType,
serve_alg: ServeAlg,
chips: Vec<Chip>,
firmware_overrides: Option<crate::builder::FirmwareConfig>,
) -> Result<Self> {
if chips.is_empty() {
return Err(Error::NoChips { id });
}
if chips.len() > 1 && set_type == ChipSetType::Single {
return Err(Error::TooManyChips {
id,
expected: 1,
actual: chips.len(),
});
}
if chips.len() == 1 && set_type != ChipSetType::Single {
return Err(Error::TooFewChips {
id,
expected: 2,
actual: chips.len(),
});
}
let serve_alg = match set_type {
ChipSetType::Single | ChipSetType::Banked => {
if !matches!(
serve_alg,
ServeAlg::Default | ServeAlg::AddrOnCs | ServeAlg::TwoCsOneAddr
) {
return Err(Error::InvalidServeAlg { serve_alg });
} else {
serve_alg
}
}
ChipSetType::Multi => ServeAlg::AddrOnAnyCs,
};
#[allow(clippy::collapsible_if)]
if let Some(ref overrides) = firmware_overrides {
if overrides.ice.is_none()
&& overrides.fire.is_none()
&& overrides.led.is_none()
&& overrides.swd.is_none()
&& overrides.serve_alg_params.is_none()
{
return Err(Error::InvalidConfig {
error: "firmware_overrides specified but all fields are None".to_string(),
});
}
if let Some(ref params) = overrides.serve_alg_params {
if params.params.is_empty() {
return Err(Error::InvalidConfig {
error: "serve_alg_params specified but params vec is empty".to_string(),
});
}
}
}
Ok(Self {
id,
set_type,
serve_alg,
chips,
firmware_overrides,
})
}
pub fn has_data(&self) -> bool {
self.chips[0].has_data()
}
pub fn multi_cs_logic(&self) -> Result<CsLogic> {
let first_cs1 = self.chips[0].cs_config.cs1_logic();
if self.chips.len() == 1 {
Ok(CsLogic::Ignore)
} else {
for chip in &self.chips {
if chip.cs_config.cs1_logic() != first_cs1 {
return Err(Error::InconsistentCsLogic {
first: first_cs1,
other: chip.cs_config.cs1_logic(),
});
}
}
#[allow(clippy::collapsible_if)]
if self.set_type == ChipSetType::Multi {
for chip in &self.chips {
if let Some(cs2) = chip.cs_config.cs2_logic() {
if cs2 != CsLogic::Ignore {
return Err(Error::InconsistentCsLogic {
first: CsLogic::Ignore,
other: cs2,
});
}
}
if let Some(cs3) = chip.cs_config.cs3_logic() {
if cs3 != CsLogic::Ignore {
return Err(Error::InconsistentCsLogic {
first: CsLogic::Ignore,
other: cs3,
});
}
}
}
}
Ok(self.chips[0].cs_config.cs1_logic())
}
}
pub fn chip_function(&self) -> ChipFunction {
self.chips[0].chip_type.chip_function()
}
pub fn image_size(&self, board: &Board, fw_version: &FirmwareVersion) -> usize {
let family = board.mcu_family();
let num_addr_pins = board.addr_pins().len();
let board_pins = board.chip_pins();
let num_chips = self.chips().len();
let set_type = &self.set_type;
assert!(num_chips >= 1);
if (*set_type == ChipSetType::Multi) || (*set_type == ChipSetType::Banked) {
assert!(board_pins == 24);
return 2_usize.pow(16); }
assert!(self.chips.len() == 1);
let chip = &self.chips[0];
if chip.chip_type().is_plugin() {
return 65536;
}
match (board_pins, family) {
(24, McuFamily::Stm32f4) => {
assert!(num_addr_pins == 13);
2_usize.pow(14) }
(28, McuFamily::Stm32f4) => {
assert!(num_addr_pins == 14);
2_usize.pow(16) }
(24, McuFamily::Rp2350) => {
assert!(num_addr_pins == 13);
2_usize.pow(16) }
(28, McuFamily::Rp2350) => {
if *fw_version < MIN_FW_VER_FIRE_28_18_ADDR_PINS {
2_usize.pow(16) } else {
assert!(num_addr_pins == 18);
match chip.chip_type() {
ChipType::Chip2364 => 2_usize.pow(18), ChipType::Chip231024 => 2_usize.pow(18), _ => 2_usize.pow(16), }
}
}
(32, McuFamily::Rp2350) => {
assert!(num_addr_pins == 19);
2_usize.pow(19) }
(40, McuFamily::Rp2350) => {
assert!(num_addr_pins == 19);
2_usize.pow(19) }
(_, _) => {
panic!(
"Unsupported board configuration: {} pins, family {:?}",
board_pins, family
);
}
}
}
fn truncate_phys_pin_to_addr_map(
phys_pin_to_addr_map: &mut [Option<usize>],
num_addr_lines: usize,
) {
for item in phys_pin_to_addr_map.iter_mut() {
#[allow(clippy::collapsible_if)]
if let Some(addr_bit) = item {
if *addr_bit >= num_addr_lines {
*item = None;
}
}
}
}
pub fn get_byte(
&self,
address: usize,
board: &Board,
fw_version: &FirmwareVersion,
invert_cs1_x: bool,
) -> u8 {
if (!self.has_data()) && (self.chip_function() == ChipFunction::Ram) {
return Chip::byte_mangled(PAD_RAM_BYTE, board);
}
if self.chip_function().is_plugin() {
return self.chips[0].get_byte_raw(address);
}
assert!(address < self.image_size(board, fw_version));
if (self.chips.len() == 1) || (self.set_type == ChipSetType::Banked) {
let (chip_index, masked_address) = if self.set_type != ChipSetType::Banked {
(0, address)
} else {
assert!(address < 65536, "Address out of bounds for banked Chip set");
let x1_pin = board.bit_x1();
let x2_pin = board.bit_x2();
let bank = if board.x_jumper_pull() == 1 {
((address >> x1_pin) & 1) | (((address >> x2_pin) & 1) << 1)
} else {
(!(address >> x1_pin) & 1) | ((!((address >> x2_pin) & 1)) << 1)
};
let mask = !(1 << x1_pin) & !(1 << x2_pin);
let masked_address = address & mask;
let chip_index = bank % self.chips.len(); (chip_index, masked_address)
};
let num_addr_lines = self.chips[chip_index].chip_type.num_addr_lines();
let mut phys_pin_to_addr_map = handle_snowflake_chip_types(
board,
board.phys_pin_to_addr_map(),
&self.chips[chip_index].chip_type,
);
Self::truncate_phys_pin_to_addr_map(&mut phys_pin_to_addr_map, num_addr_lines);
return self.chips[chip_index].get_byte(&phys_pin_to_addr_map, masked_address, board);
}
assert!(address < 65536, "Address out of bounds for multi-Chip set");
for (index, chip_in_set) in self.chips.iter().enumerate() {
let num_addr_lines = chip_in_set.chip_type.num_addr_lines();
let mut phys_pin_to_addr_map = handle_snowflake_chip_types(
board,
board.phys_pin_to_addr_map(),
&chip_in_set.chip_type,
);
Self::truncate_phys_pin_to_addr_map(&mut phys_pin_to_addr_map, num_addr_lines);
let pins_active_high = chip_in_set.cs_config.cs1_logic() == CsLogic::ActiveHigh;
let cs_pin = board.cs_bit_for_chip_in_set(chip_in_set.chip_type, index);
assert!(cs_pin <= 15, "Internal error: CS pin is > 15");
fn is_pin_active(
active_high: bool,
invert_cs1_x: bool,
address: usize,
pin: u8,
) -> bool {
if !invert_cs1_x {
if active_high {
(address & (1 << pin)) != 0
} else {
(address & (1 << pin)) == 0
}
} else {
if active_high {
(address & (1 << pin)) == 0
} else {
(address & (1 << pin)) != 0
}
}
}
let cs_active = is_pin_active(pins_active_high, invert_cs1_x, address, cs_pin);
if cs_active {
let cs1_pin = board.bit_cs1(chip_in_set.chip_type);
let x1_pin = board.bit_x1();
let x2_pin = board.bit_x2();
let cs1_is_active = is_pin_active(pins_active_high, invert_cs1_x, address, cs1_pin);
let x1_is_active = is_pin_active(pins_active_high, invert_cs1_x, address, x1_pin);
let x2_is_active = is_pin_active(pins_active_high, invert_cs1_x, address, x2_pin);
let active_count = [cs1_is_active, x1_is_active, x2_is_active]
.iter()
.filter(|&&x| x)
.count();
if active_count == 1 && self.check_chip_cs_requirements(chip_in_set, address, board)
{
return chip_in_set.get_byte(&phys_pin_to_addr_map, address, board);
}
}
}
Chip::byte_mangled(PAD_NO_CHIP_BYTE, board)
}
fn check_chip_cs_requirements(
&self,
chip_in_set: &Chip,
address: usize,
board: &Board,
) -> bool {
let cs_config = &chip_in_set.cs_config;
let chip_type = chip_in_set.chip_type;
if let Some(cs2_logic) = cs_config.cs2_logic() {
match cs2_logic {
CsLogic::Ignore => {
}
CsLogic::ActiveLow => {
let cs2_pin = board.bit_cs2(chip_type);
let cs2_active = (address & (1 << cs2_pin)) == 0;
if !cs2_active {
return false;
}
}
CsLogic::ActiveHigh => {
let cs2_pin = board.bit_cs2(chip_type);
let cs2_active = (address & (1 << cs2_pin)) != 0;
if cs2_active {
return false;
}
}
}
}
if let Some(cs3_logic) = cs_config.cs3_logic() {
match cs3_logic {
CsLogic::Ignore => {
}
CsLogic::ActiveLow => {
let cs3_pin = board.bit_cs3(chip_type);
let cs3_active = (address & (1 << cs3_pin)) == 0;
if !cs3_active {
return false;
}
}
CsLogic::ActiveHigh => {
let cs3_pin = board.bit_cs3(chip_type);
let cs3_active = (address & (1 << cs3_pin)) != 0;
if cs3_active {
return false;
}
}
}
}
true
}
#[allow(dead_code)]
fn mask_cs_selection_bits(&self, address: usize, chip_type: ChipType, board: &Board) -> usize {
let mut masked_address = address;
masked_address &= !(1 << board.bit_cs1(chip_type));
if board.supports_multi_chip_sets() {
let x1 = board.bit_x1();
let x2 = board.bit_x2();
assert!(x1 < 15 && x2 < 15, "X1/X2 pins must be less than 15");
masked_address &= !(1 << x1);
masked_address &= !(1 << x2);
}
match chip_type {
ChipType::Chip2332 => {
masked_address &= !(1 << board.bit_cs2(chip_type));
}
ChipType::Chip2316 => {
masked_address &= !(1 << board.bit_cs2(chip_type));
masked_address &= !(1 << board.bit_cs3(chip_type));
}
ChipType::Chip2364 => {
}
ChipType::Chip23128 => {
}
_ => {
panic!(
"Internal error: unsupported chip type {} in mask_cs_selection_bits",
chip_type.name()
);
}
}
masked_address & ((1 << 13) - 1) }
pub fn chips(&self) -> &[Chip] {
&self.chips
}
pub fn chips_metadata_len(&self, include_filenames: bool) -> usize {
let num_chips = self.chips.len();
let chip_metadata_len = if include_filenames {
CHIP_METADATA_LEN_WITH_FILENAME
} else {
CHIP_METADATA_LEN_NO_FILENAME
} * num_chips;
#[allow(clippy::let_and_return)]
chip_metadata_len
}
pub fn write_chip_metadata(
&self,
buf: &mut [u8],
chip_filename_ptrs: &[u32],
chip_metadata_ptrs: &mut [u32],
include_filenames: bool,
) -> Result<usize> {
let num_chips = self.chips.len();
let expected_len = self.chips_metadata_len(include_filenames);
if buf.len() < expected_len {
return Err(Error::BufferTooSmall {
location: "write_chip_metadata1",
expected: expected_len,
actual: buf.len(),
});
}
if chip_metadata_ptrs.len() < num_chips {
return Err(Error::BufferTooSmall {
location: "write_chip_metadata2",
expected: num_chips,
actual: chip_metadata_ptrs.len(),
});
}
let mut offset = 0;
for (ii, chip) in self.chips.iter().enumerate() {
chip_metadata_ptrs[ii] = offset as u32;
buf[offset] = chip.chip_type_c_enum_val();
offset += 1;
let is_plugin = chip.chip_type.chip_function().is_plugin();
buf[offset] = if is_plugin {
CsLogic::Ignore.c_enum_val()
} else {
chip.cs_config.cs1_logic().c_enum_val()
};
offset += 1;
buf[offset] = if is_plugin {
CsLogic::Ignore.c_enum_val()
} else {
chip.cs_config.cs2_logic().map_or(2, |cs| cs.c_enum_val())
};
offset += 1;
buf[offset] = if is_plugin {
CsLogic::Ignore.c_enum_val()
} else {
chip.cs_config.cs3_logic().map_or(2, |cs| cs.c_enum_val())
};
offset += 1;
if include_filenames {
let chip_filename_ptr = chip_filename_ptrs
.get(chip.index())
.copied()
.ok_or_else(|| Error::MissingPointer { id: chip.index() })?;
buf[offset..offset + 4].copy_from_slice(&chip_filename_ptr.to_le_bytes());
offset += 4;
}
}
Ok(offset)
}
pub fn write_chip_pointer_array(
&self,
buf: &mut [u8],
chip_metadata_ptrs: &[u32],
) -> Result<usize> {
let num_chips = self.chips.len();
let expected_len = 4 * num_chips;
if buf.len() < expected_len {
return Err(Error::BufferTooSmall {
location: "write_chip_pointer_array",
expected: expected_len,
actual: buf.len(),
});
}
if chip_metadata_ptrs.len() < num_chips {
return Err(Error::MissingPointer {
id: chip_metadata_ptrs.len(),
});
}
let mut offset = 0;
for ii in chip_metadata_ptrs.iter() {
buf[offset..offset + 4].copy_from_slice(&ii.to_le_bytes());
offset += 4;
}
Ok(offset)
}
#[allow(clippy::too_many_arguments)]
pub fn write_set_metadata(
&self,
buf: &mut [u8],
data_ptr: u32,
chip_array_ptr: u32,
board: &Board,
version: &FirmwareVersion,
serve_config_ptr: Option<u32>,
firmware_overrides_ptr: Option<u32>,
) -> Result<usize> {
let expected_len = Self::chip_set_metadata_len(version);
if buf.len() < expected_len {
return Err(Error::BufferTooSmall {
location: "write_set_metadata",
expected: expected_len,
actual: buf.len(),
});
}
let mut offset = 0;
buf[offset..offset + 4].copy_from_slice(&data_ptr.to_le_bytes());
offset += 4;
let data_size = self.image_size(board, version) as u32;
buf[offset..offset + 4].copy_from_slice(&data_size.to_le_bytes());
offset += 4;
buf[offset..offset + 4].copy_from_slice(&chip_array_ptr.to_le_bytes());
offset += 4;
let num_chips = self.chips.len() as u8;
buf[offset] = num_chips;
offset += 1;
let algorithm = self.serve_alg().c_enum_value();
buf[offset] = algorithm;
offset += 1;
let multi_cs_state = self.multi_cs_logic()?.c_enum_val();
buf[offset] = multi_cs_state;
offset += 1;
if version >= &MIN_FIRMWARE_OVERRIDES_VERSION {
buf[offset] = 1; } else {
buf[offset] = PAD_METADATA_BYTE; }
offset += 1;
assert_eq!(offset, 16, "First 16 bytes should be written");
if version >= &MIN_FIRMWARE_OVERRIDES_VERSION {
let serve_ptr = serve_config_ptr.unwrap_or(0xFFFFFFFF);
buf[offset..offset + 4].copy_from_slice(&serve_ptr.to_le_bytes());
offset += 4;
let fw_ptr = firmware_overrides_ptr.unwrap_or(0xFFFFFFFF);
buf[offset..offset + 4].copy_from_slice(&fw_ptr.to_le_bytes());
offset += 4;
buf[offset..offset + 40].copy_from_slice(&[0u8; 40]);
offset += 40;
assert_eq!(
offset, CHIP_SET_FIRMWARE_OVERRIDES_METADATA_LEN,
"Total should be 64 bytes for 0.6.0+"
);
}
assert_eq!(
offset, expected_len,
"Internal error: offset does not match expected length"
);
Ok(offset)
}
pub fn chip_set_metadata_len(version: &FirmwareVersion) -> usize {
if *version >= MIN_FIRMWARE_OVERRIDES_VERSION {
CHIP_SET_METADATA_LEN_EXTRA_INFO
} else {
CHIP_SET_METADATA_LEN
}
}
pub fn serve_alg(&self) -> ServeAlg {
self.serve_alg
}
}
fn handle_snowflake_chip_types(
board: &Board,
phys_pin_to_addr_map: &[Option<usize>],
chip_type: &ChipType,
) -> Vec<Option<usize>> {
let mut modified_map = phys_pin_to_addr_map.to_vec();
if board.chip_pins() == 24 && *chip_type == ChipType::Chip2732 {
let a11_index = modified_map.iter().position(|&x| x == Some(11));
let a12_index = modified_map.iter().position(|&x| x == Some(12));
if let (Some(i11), Some(i12)) = (a11_index, a12_index) {
modified_map[i11] = Some(12);
modified_map[i12] = Some(11);
} else {
panic!(
"Address lines A11 and/or A12 not found in phys_pin_to_addr_map for 2732 handling"
);
}
} else if board.chip_pins() == 28 && *chip_type == ChipType::Chip2364 {
let ce_pin = board.bit_ce(ChipType::Chip2764) as usize;
if let Some(i16) = modified_map.iter().position(|&x| x == Some(16)) {
modified_map.swap(i16, ce_pin);
} else {
panic!(
"Address line A16 not found in phys_pin_to_addr_map for 2364-in-28-pin handling"
);
}
let cs1_pin = board.bit_cs1(ChipType::Chip231024) as usize;
let i11 = modified_map
.iter()
.position(|&x| x == Some(11))
.expect("A11 not found for 2364-in-28-pin handling");
let i12 = modified_map
.iter()
.position(|&x| x == Some(12))
.expect("A12 not found for 2364-in-28-pin handling");
let old_cs1 = modified_map[cs1_pin];
modified_map[cs1_pin] = Some(11);
modified_map[i11] = Some(12);
modified_map[i12] = old_cs1;
} else if board.chip_pins() == 28 && *chip_type != ChipType::Chip231024 && *chip_type != ChipType::Chip2364 {
modified_map.remove(0);
modified_map.remove(0);
modified_map.push(None);
modified_map.push(None);
if *chip_type == ChipType::Chip28C256 {
let a14_index = modified_map.iter().position(|&x| x == Some(14));
let a15_index = modified_map.iter().position(|&x| x == Some(15));
if let (Some(i14), Some(i15)) = (a14_index, a15_index) {
modified_map[i14] = Some(15);
modified_map[i15] = Some(14);
} else {
panic!(
"Address lines A14 and/or A15 not found in phys_pin_to_addr_map for 28C256 handling"
);
}
}
} else if *chip_type == ChipType::Chip27C301 {
if let Some(a16_index) = modified_map.iter().position(|&x| x == Some(16)) {
if a16_index == 0 {
modified_map.remove(0);
modified_map.push(Some(16));
} else {
panic!(
"Address line A16 found at unexpected position {} in phys_pin_to_addr_map for 27C301 handling",
a16_index
);
}
} else {
panic!("Address line A16 not found in phys_pin_to_addr_map for 27C301 handling");
}
}
modified_map
}