use alloc::format;
use alloc::vec::Vec;
use onerom_metadata::OneromAlgDmaConfig;
use crate::image::{Chip, ChipSetType};
use crate::{Error, MAX_IMAGE_SIZE, PAD_NO_CHIP_BYTE, Result};
use super::addr_layout::AddrLayout;
use super::cs_data_layout::CsDataLayout;
use super::rom_slot::{bytes_per_word, table_entries};
fn mangle_word(raw: u32, data_bit_positions: &[u8]) -> u32 {
let mut mangled = 0u32;
for (d, &bit_pos) in data_bit_positions.iter().enumerate() {
mangled |= ((raw >> d) & 1) << bit_pos;
}
mangled
}
pub fn build_rom_image(
addr_layout: &AddrLayout,
cs_data_layout: &CsDataLayout,
set_type: ChipSetType,
chips: &[Chip],
alg_dma: &OneromAlgDmaConfig,
) -> Result<Vec<u8>> {
let entries = table_entries(addr_layout) as usize;
let word_bytes = bytes_per_word(alg_dma) as usize;
let image_size = entries * word_bytes;
if image_size > MAX_IMAGE_SIZE {
return Err(Error::RomTableTooLarge {
size: image_size,
max: MAX_IMAGE_SIZE,
});
}
let addr_bit_positions: Vec<u8> = addr_layout
.addr_pin_gpios
.iter()
.map(|&gpio| gpio - addr_layout.gpio_base)
.collect();
let bank_bit_positions: Vec<u8> = match set_type {
ChipSetType::Single => {
if chips.len() != 1 {
return Err(Error::InvalidConfig {
error: format!("Single set must have exactly 1 chip, got {}", chips.len()),
});
}
Vec::new()
}
ChipSetType::Banked => {
let x1_bit = addr_layout
.x1_gpio
.expect("Banked AddrLayout must have x1_gpio")
- addr_layout.gpio_base;
match chips.len() {
2 => alloc::vec![x1_bit],
3 | 4 => {
let x2_bit = addr_layout
.x2_gpio
.expect("Banked AddrLayout must have x2_gpio")
- addr_layout.gpio_base;
alloc::vec![x1_bit, x2_bit]
}
n => {
return Err(Error::InvalidConfig {
error: format!("Banked set must have 2, 3 or 4 chips, got {n}"),
});
}
}
}
ChipSetType::Multi => {
if chips.len() < 2 || chips.len() > 3 {
return Err(Error::InvalidConfig {
error: format!("Multi set must have 2 or 3 chips, got {}", chips.len()),
});
}
Vec::new()
}
};
let multi_select: Option<(u8, u8, Option<u8>)> = if set_type == ChipSetType::Multi {
let cs1_gpio = cs_data_layout
.select_lines
.first()
.expect("Multi cs_data_layout must have at least one select line")
.gpio;
let cs1_bit = cs1_gpio - addr_layout.gpio_base;
let x1_bit = addr_layout
.x1_gpio
.expect("Multi AddrLayout must have x1_gpio")
- addr_layout.gpio_base;
let x2_bit = if chips.len() >= 3 {
Some(
addr_layout
.x2_gpio
.expect("3-chip Multi AddrLayout must have x2_gpio")
- addr_layout.gpio_base,
)
} else {
None
};
Some((cs1_bit, x1_bit, x2_bit))
} else {
None
};
let data_base = cs_data_layout.gpio_base + cs_data_layout.base_data_pin;
let data_bit_positions: Vec<u8> = cs_data_layout
.data_pin_gpios
.iter()
.map(|&gpio| gpio - data_base)
.collect();
let pad_raw: u32 =
(0..word_bytes).fold(0u32, |acc, b| acc | (PAD_NO_CHIP_BYTE as u32) << (8 * b));
let mangled_pad = mangle_word(pad_raw, &data_bit_positions);
let chip_data: Vec<&[u8]> = chips
.iter()
.enumerate()
.map(|(index, chip)| {
chip.data().ok_or(Error::MissingImageData {
chip_type: *chip.chip_type(),
index,
})
})
.collect::<Result<Vec<_>>>()?;
let chip_word_counts: Vec<usize> = chips
.iter()
.map(|chip| chip.chip_type().size_bytes() / word_bytes)
.collect();
let mut image = Vec::with_capacity(entries * word_bytes);
for i in 0..entries as u32 {
let mut chip_addr: usize = 0;
for (n, &bit_pos) in addr_bit_positions.iter().enumerate() {
let bit = ((i >> bit_pos) & 1) as usize;
chip_addr |= bit << n;
}
let chip_index: Option<usize> = if let Some((cs1_bit, x1_bit, x2_bit)) = multi_select {
let cs1_active = ((i >> cs1_bit) & 1) == 1;
let x1_active = ((i >> x1_bit) & 1) == 1;
let x2_active = x2_bit.is_some_and(|b| ((i >> b) & 1) == 1);
match (cs1_active, x1_active, x2_active) {
(true, false, false) => Some(0),
(false, true, false) => Some(1),
(false, false, true) => {
if chips.len() >= 3 { Some(2) } else { None }
}
_ => None, }
} else {
let mut bank_index: usize = 0;
for (n, &bit_pos) in bank_bit_positions.iter().enumerate() {
let bit = ((i >> bit_pos) & 1) as usize;
bank_index |= bit << n;
}
if bank_index < chips.len() {
Some(bank_index)
} else {
None
}
};
let chip_index = match chip_index {
None => {
for b in 0..word_bytes {
image.push(((mangled_pad >> (8 * b)) & 0xFF) as u8);
}
continue;
}
Some(idx) => idx,
};
let chip_size = chip_word_counts[chip_index];
if chip_addr >= chip_size {
if chip_size.is_power_of_two() {
chip_addr &= chip_size - 1;
} else {
for b in 0..word_bytes {
image.push(((mangled_pad >> (8 * b)) & 0xFF) as u8);
}
continue;
}
}
let chip_image = chip_data[chip_index];
let raw: u32 = match word_bytes {
1 => chip_image[chip_addr] as u32,
2 => {
let byte_addr = chip_addr * 2;
chip_image[byte_addr] as u32 | (chip_image[byte_addr + 1] as u32) << 8
}
n => unreachable!("bytes_per_word only returns 1 or 2, got {n}"),
};
let mangled = mangle_word(raw, &data_bit_positions);
for b in 0..word_bytes {
image.push(((mangled >> (8 * b)) & 0xFF) as u8);
}
}
Ok(image)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
use onerom_config::chip::ChipType;
use onerom_metadata::BitModes;
use crate::image::{CsConfig, CsLogic, SizeHandling};
use super::super::cs_data_layout::{SelectLine, SelectRole};
fn chip_with_bytes(filename: &str, bytes: &[u8]) -> Chip {
let mut image = vec![0u8; ChipType::Chip2364.size_bytes()];
image[..bytes.len()].copy_from_slice(bytes);
chip_with_image(filename, image)
}
fn chip_with_image(filename: &str, image: Vec<u8>) -> Chip {
chip_with_typed_image(filename, &ChipType::Chip2364, image)
}
fn chip_with_typed_image(filename: &str, chip_type: &ChipType, image: Vec<u8>) -> Chip {
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
Chip::from_raw_rom_image(
0,
filename.to_string(),
None,
Some(image.as_slice()),
vec![0u8; chip_type.size_bytes()],
chip_type,
cs_config,
&SizeHandling::None,
None,
)
.expect("chip construction should succeed")
}
fn alg_dma_8bit() -> OneromAlgDmaConfig {
OneromAlgDmaConfig::AlgDma0 {
bit_mode: BitModes::BitMode8,
continuous: 1,
}
}
fn alg_dma_16bit() -> OneromAlgDmaConfig {
OneromAlgDmaConfig::AlgDma0 {
bit_mode: BitModes::BitMode16,
continuous: 1,
}
}
fn identity_cs_data_layout_8bit() -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7],
base_cs_pin: 0,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
}
}
fn identity_cs_data_layout_16bit() -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 16,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
base_cs_pin: 16,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
}
}
#[test]
fn single_8bit_fire24a_2364_address_mangling() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 16,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![7, 6, 5, 4, 3, 2, 1, 0, 10, 11, 14, 15, 12],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 13,
base_data_pin: 3,
num_data_pins: 8,
data_pin_gpios: alloc::vec![16, 17, 18, 19, 20, 21, 22, 23],
base_cs_pin: 0,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let image: Vec<u8> = (0..ChipType::Chip2364.size_bytes() as u32)
.map(|k| k as u8)
.collect();
let chips = [chip_with_image("test.bin", image.clone())];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 1 << 16);
assert_eq!(table[0], image[0]);
assert_eq!(table[1], image[128]);
assert_eq!(table[2], image[64]);
assert_eq!(table[256], table[0]);
assert_eq!(table[(1usize << 16) - 1], image[8191]);
}
#[test]
fn data_pin_mangling_bit_reversal() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 1,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 13,
base_data_pin: 3,
num_data_pins: 8,
data_pin_gpios: alloc::vec![23, 22, 21, 20, 19, 18, 17, 16],
base_cs_pin: 0,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let chips = [chip_with_bytes("test.bin", &[0b0000_0001, 0b1000_0000])];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 2);
assert_eq!(table[0], 0b1000_0000);
assert_eq!(table[1], 0b0000_0001);
}
#[test]
fn banked_2bank_selection() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(1),
x2_gpio: None,
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [
chip_with_bytes("bank0.bin", &[0xAA, 0xBB]),
chip_with_bytes("bank1.bin", &[0xCC, 0xDD]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table, alloc::vec![0xAA, 0xBB, 0xCC, 0xDD]);
}
#[test]
fn banked_4bank_selection_x1_is_lsb() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(0),
x2_gpio: Some(1),
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [
chip_with_bytes("bank0.bin", &[0x00]),
chip_with_bytes("bank1.bin", &[0x11]),
chip_with_bytes("bank2.bin", &[0x22]),
chip_with_bytes("bank3.bin", &[0x33]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table, alloc::vec![0x00, 0x11, 0x22, 0x33]);
}
#[test]
fn banked_3bank_pad_value() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(0),
x2_gpio: Some(1),
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [
chip_with_bytes("bank0.bin", &[0x00]),
chip_with_bytes("bank1.bin", &[0x11]),
chip_with_bytes("bank2.bin", &[0x22]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table, alloc::vec![0x00, 0x11, 0x22, PAD_NO_CHIP_BYTE]);
}
#[test]
fn image_too_large_is_rejected() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 20,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [chip_with_bytes("test.bin", &[0x00])];
let result = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_8bit(),
);
assert!(matches!(
result,
Err(Error::RomTableTooLarge { size, max }) if size == 1 << 20 && max == MAX_IMAGE_SIZE
));
}
fn multi_2chip_addr_layout() -> AddrLayout {
AddrLayout {
gpio_base: 0,
num_addr_pins: 4,
x1_gpio: Some(3),
x2_gpio: None,
addr_pin_gpios: alloc::vec![0, 1],
excess_addr_pin_gpios: alloc::vec![],
}
}
fn multi_cs_data_layout_with_cs1_at(cs1_gpio: u8) -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7],
base_cs_pin: cs1_gpio,
num_cs_pins: 2,
cs_ignore_index: None,
select_lines: alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: cs1_gpio
},
SelectLine {
role: SelectRole::X1,
gpio: 3
},
],
commoned_lines: alloc::vec![],
alg_cs2: None,
}
}
#[test]
fn multi_2chip_8bit_one_hot_selection() {
let addr_layout = multi_2chip_addr_layout();
let cs_data_layout = multi_cs_data_layout_with_cs1_at(2);
let chips = [
chip_with_bytes("chip0.bin", &[0xA0, 0xA1, 0xA2, 0xA3]),
chip_with_bytes("chip1.bin", &[0xB0, 0xB1, 0xB2, 0xB3]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Multi,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 16);
assert_eq!(&table[0..4], &[PAD_NO_CHIP_BYTE; 4]); assert_eq!(&table[4..8], &[0xA0, 0xA1, 0xA2, 0xA3]); assert_eq!(&table[8..12], &[0xB0, 0xB1, 0xB2, 0xB3]); assert_eq!(&table[12..16], &[PAD_NO_CHIP_BYTE; 4]); }
#[test]
fn multi_3chip_8bit_one_hot_selection() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 4,
x1_gpio: Some(2),
x2_gpio: Some(3),
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7],
base_cs_pin: 1,
num_cs_pins: 3,
cs_ignore_index: None,
select_lines: alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 1
},
SelectLine {
role: SelectRole::X1,
gpio: 2
},
SelectLine {
role: SelectRole::X2,
gpio: 3
},
],
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let chips = [
chip_with_bytes("chip0.bin", &[0xA0, 0xA1]),
chip_with_bytes("chip1.bin", &[0xB0, 0xB1]),
chip_with_bytes("chip2.bin", &[0xC0, 0xC1]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Multi,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 16);
assert_eq!(table[0], PAD_NO_CHIP_BYTE);
assert_eq!(table[1], PAD_NO_CHIP_BYTE);
assert_eq!(table[2], 0xA0);
assert_eq!(table[3], 0xA1);
assert_eq!(table[4], 0xB0);
assert_eq!(table[5], 0xB1);
assert_eq!(table[6], PAD_NO_CHIP_BYTE);
assert_eq!(table[8], 0xC0);
assert_eq!(table[9], 0xC1);
#[allow(clippy::needless_range_loop)]
for idx in 10..16usize {
assert_eq!(table[idx], PAD_NO_CHIP_BYTE, "entry {idx} should be PAD");
}
}
#[test]
fn multi_2chip_ce_role_primary_select() {
let addr_layout = multi_2chip_addr_layout();
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7],
base_cs_pin: 2,
num_cs_pins: 2,
cs_ignore_index: None,
select_lines: alloc::vec![
SelectLine {
role: SelectRole::Ce,
gpio: 2
},
SelectLine {
role: SelectRole::X1,
gpio: 3
},
],
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let chips = [
chip_with_bytes("chip0.bin", &[0xC0, 0xC1, 0xC2, 0xC3]),
chip_with_bytes("chip1.bin", &[0xD0, 0xD1, 0xD2, 0xD3]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Multi,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed for Ce-role primary select");
assert_eq!(&table[4..8], &[0xC0, 0xC1, 0xC2, 0xC3]); assert_eq!(&table[8..12], &[0xD0, 0xD1, 0xD2, 0xD3]); }
#[test]
fn multi_with_wrong_chip_count_errors() {
let addr_layout = multi_2chip_addr_layout();
let cs_data_layout = multi_cs_data_layout_with_cs1_at(2);
let chips = [chip_with_bytes("only.bin", &[0x00])];
let result = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Multi,
&chips,
&alg_dma_8bit(),
);
assert!(matches!(result, Err(Error::InvalidConfig { .. })));
}
#[test]
fn single_with_wrong_chip_count_errors() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 1,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [
chip_with_bytes("a.bin", &[0x00, 0x00]),
chip_with_bytes("b.bin", &[0x00, 0x00]),
];
let result = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_8bit(),
);
assert!(matches!(result, Err(Error::InvalidConfig { .. })));
}
#[test]
fn banked_with_wrong_chip_count_errors() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(0),
x2_gpio: Some(1),
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let chips = [
chip_with_bytes("a.bin", &[0x00]),
chip_with_bytes("b.bin", &[0x00]),
chip_with_bytes("c.bin", &[0x00]),
chip_with_bytes("d.bin", &[0x00]),
chip_with_bytes("e.bin", &[0x00]),
];
let result = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_8bit(),
);
assert!(matches!(result, Err(Error::InvalidConfig { .. })));
}
#[test]
fn single_16bit_identity_mapping() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 1,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_16bit();
let chips = [chip_with_bytes("test.bin", &[0x01, 0x02, 0x03, 0x04])];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_16bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 4); assert_eq!(table, alloc::vec![0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn bitmode16_data_pin_mangling_bit_reversal() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 1,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 16,
data_pin_gpios: alloc::vec![15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
base_cs_pin: 16,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let chips = [chip_with_bytes("test.bin", &[0x01, 0x00, 0x80, 0x00])];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_16bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table, alloc::vec![0x00, 0x80, 0x00, 0x01]);
}
#[test]
fn banked_3bank_pad_value_16bit() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(0),
x2_gpio: Some(1),
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_16bit();
let chips = [
chip_with_bytes("bank0.bin", &[0x00, 0x10]),
chip_with_bytes("bank1.bin", &[0x01, 0x11]),
chip_with_bytes("bank2.bin", &[0x02, 0x12]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_16bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(
table,
alloc::vec![
0x00,
0x10,
0x01,
0x11,
0x02,
0x12,
PAD_NO_CHIP_BYTE,
PAD_NO_CHIP_BYTE
]
);
}
#[test]
fn non_power_of_2_chip_pads_upper_addresses() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 16,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = identity_cs_data_layout_8bit();
let mut image = vec![0u8; ChipType::Chip23QL384.size_bytes()]; image[49150] = 0xAB; image[49151] = 0xCD; let chips = [chip_with_typed_image(
"test.bin",
&ChipType::Chip23QL384,
image,
)];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Single,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 1 << 16); assert_eq!(table[49150], 0xAB); assert_eq!(table[49151], 0xCD); assert_eq!(table[49152], PAD_NO_CHIP_BYTE); assert_eq!(table[65535], PAD_NO_CHIP_BYTE); }
#[test]
fn banked_3bank_pad_value_non_identity_mapping_is_mangled() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 2,
x1_gpio: Some(0),
x2_gpio: Some(1),
addr_pin_gpios: Vec::new(),
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![7, 6, 5, 4, 3, 2, 1, 0],
base_cs_pin: 0,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: Vec::new(),
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let chips = [
chip_with_bytes("bank0.bin", &[0x00]),
chip_with_bytes("bank1.bin", &[0x11]),
chip_with_bytes("bank2.bin", &[0x22]),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Banked,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table[0], 0x00);
assert_eq!(table[1], 0x88);
assert_eq!(table[2], 0x44);
assert_eq!(
table[3], 0x55,
"PAD must be mangled: mangle(0xAA) = 0x55 under bit-reversal; \
got raw 0xAA means the pad path skipped mangle_word"
);
}
#[test]
fn multi_power_of_2_secondary_mirrors_across_extra_address_bit() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 15,
x1_gpio: Some(14),
x2_gpio: None,
addr_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 0,
num_data_pins: 8,
data_pin_gpios: alloc::vec![0, 1, 2, 3, 4, 5, 6, 7],
base_cs_pin: 13,
num_cs_pins: 2,
cs_ignore_index: None,
select_lines: alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13
},
SelectLine {
role: SelectRole::X1,
gpio: 14
},
],
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let mut secondary_image = vec![0u8; 4096];
for (i, b) in secondary_image.iter_mut().enumerate() {
*b = (i & 0xFF) as u8;
}
let chips = [
chip_with_bytes("primary.bin", &[]),
chip_with_typed_image(
"secondary.bin",
&ChipType::Chip2332,
secondary_image.clone(),
),
];
let table = build_rom_image(
&addr_layout,
&cs_data_layout,
ChipSetType::Multi,
&chips,
&alg_dma_8bit(),
)
.expect("build_rom_image should succeed");
assert_eq!(table.len(), 1 << 15);
for addr12 in [0x000usize, 0x001, 0x7FF, 0xFFF] {
let i_lo = (1 << 14) | addr12; let i_hi = (1 << 14) | (1 << 12) | addr12; assert_eq!(
table[i_lo], table[i_hi],
"addr12={addr12:#05x}: A12=0 entry {i_lo:#06x} \
({:#04x}) != A12=1 entry {i_hi:#06x} ({:#04x})",
table[i_lo], table[i_hi],
);
let expected = secondary_image[addr12 & 0xFFF];
assert_eq!(
table[i_lo], expected,
"addr12={addr12:#05x}: got {:#04x}, expected {expected:#04x}",
table[i_lo],
);
}
}
}