use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use onerom_config::hw::Board;
use onerom_metadata::{GpioOverride, OneromAlgDataConfig};
use crate::image::{ChipSetType, CsConfig, CsLogic};
use super::addr_layout::AddrLayout;
use super::cs_data_layout::{CsDataLayout, SelectRole};
fn cs_logic_for_role(role: SelectRole, cs_config: &CsConfig) -> CsLogic {
match role {
SelectRole::Cs1 | SelectRole::X1 | SelectRole::X2 | SelectRole::HalfSelect => {
cs_config.cs1_logic().unwrap()
}
SelectRole::Cs2 => cs_config.cs2_logic().unwrap_or(CsLogic::ActiveLow),
SelectRole::Cs3 => cs_config.cs3_logic().unwrap_or(CsLogic::ActiveLow),
SelectRole::Ce => CsLogic::ActiveLow,
SelectRole::Oe => CsLogic::ActiveLow,
}
}
fn required_cs_logic(set_type: ChipSetType) -> CsLogic {
match set_type {
ChipSetType::Single | ChipSetType::Banked => CsLogic::ActiveLow,
ChipSetType::Multi => CsLogic::ActiveHigh,
}
}
pub fn encode_override(gpio: u8, ov: GpioOverride) -> u8 {
((ov as u8) << 6) | (gpio & 0x3F)
}
pub fn build_cs_overrides(
layout: &CsDataLayout,
set_type: ChipSetType,
cs_config: &CsConfig,
secondary_cs_configs: &[CsConfig],
) -> Vec<u8> {
let required = required_cs_logic(set_type);
let per_chip_role = layout
.select_lines
.first()
.map(|l| l.role)
.unwrap_or(SelectRole::Cs1);
layout
.select_lines
.iter()
.filter_map(|line| {
let configured = match line.role {
SelectRole::X1 => secondary_cs_configs
.first()
.map(|c| cs_logic_for_role(per_chip_role, c))
.unwrap_or(CsLogic::ActiveLow),
SelectRole::X2 => secondary_cs_configs
.get(1)
.map(|c| cs_logic_for_role(per_chip_role, c))
.unwrap_or(CsLogic::ActiveLow),
_ => cs_logic_for_role(line.role, cs_config),
};
if configured != required {
Some(encode_override(line.gpio, GpioOverride::GpioOverInvert))
} else {
None
}
})
.chain(layout.commoned_lines.iter().filter_map(|line| {
if cs_logic_for_role(line.role, cs_config) != required {
Some(encode_override(line.gpio, GpioOverride::GpioOverInvert))
} else {
None
}
}))
.collect()
}
pub fn build_gpio_x_overrides(
layout: &AddrLayout,
set_type: ChipSetType,
num_chips: usize,
board: Board,
) -> Vec<u8> {
if !matches!(set_type, ChipSetType::Banked) {
return Vec::new();
}
if board.x_jumper_pull() != 0 {
return Vec::new();
}
let mut params = Vec::with_capacity(2);
if let Some(x1_gpio) = layout.x1_gpio {
params.push(encode_override(x1_gpio, GpioOverride::GpioOverInvert));
}
#[allow(clippy::collapsible_if)]
if num_chips >= 3 {
if let Some(x2_gpio) = layout.x2_gpio {
params.push(encode_override(x2_gpio, GpioOverride::GpioOverInvert));
}
}
params
}
pub fn build_unused_addr_overrides(
addr_layout: &AddrLayout,
cs_data_layout: &CsDataLayout,
alg_data: &OneromAlgDataConfig,
) -> Vec<u8> {
let mut used: BTreeSet<u8> = BTreeSet::new();
used.extend(addr_layout.addr_pin_gpios.iter().copied());
used.extend(addr_layout.x1_gpio);
used.extend(addr_layout.x2_gpio);
used.extend(addr_layout.excess_addr_pin_gpios.iter().copied());
used.extend(cs_data_layout.data_pin_gpios.iter().copied());
used.extend(cs_data_layout.select_lines.iter().map(|line| line.gpio));
let cs_base = cs_data_layout.gpio_base + cs_data_layout.base_cs_pin;
used.extend(cs_base..cs_base + cs_data_layout.num_cs_pins);
if let OneromAlgDataConfig::AlgData1 { byte_pin, .. } = alg_data {
used.insert(*byte_pin + cs_data_layout.gpio_base);
}
let window_start = addr_layout.gpio_base;
let window_end = addr_layout.gpio_base + addr_layout.num_addr_pins;
(window_start..window_end)
.filter(|gpio| !used.contains(gpio))
.map(|gpio| encode_override(gpio, GpioOverride::GpioOverLow))
.collect()
}
#[cfg(test)]
mod tests {
use super::super::cs_data_layout::SelectLine;
use super::*;
fn layout_with(select_lines: Vec<SelectLine>) -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 16,
num_data_pins: 8,
data_pin_gpios: alloc::vec![16, 17, 18, 19, 20, 21, 22, 23],
base_cs_pin: 13,
num_cs_pins: select_lines.len() as u8,
cs_ignore_index: None,
select_lines,
commoned_lines: alloc::vec![],
alg_cs2: None,
}
}
fn layout_with_commoned(
select_lines: Vec<SelectLine>,
commoned_lines: Vec<SelectLine>,
) -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 16,
num_data_pins: 8,
data_pin_gpios: alloc::vec![16, 17, 18, 19, 20, 21, 22, 23],
base_cs_pin: 13,
num_cs_pins: (select_lines.len() + commoned_lines.len()) as u8,
cs_ignore_index: None,
select_lines,
commoned_lines,
alg_cs2: None,
}
}
#[test]
fn single_active_low_no_override() {
let layout = layout_with(alloc::vec![SelectLine {
role: SelectRole::Cs1,
gpio: 13
}]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Single, &cs_config, &[]);
assert!(overrides.is_empty());
}
#[test]
fn single_active_high_inverted() {
let layout = layout_with(alloc::vec![SelectLine {
role: SelectRole::Cs1,
gpio: 13
}]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveHigh), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Single, &cs_config, &[]);
assert_eq!(
overrides,
alloc::vec![encode_override(13, GpioOverride::GpioOverInvert)]
);
}
#[test]
fn multi_active_low_all_inverted() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13
},
SelectLine {
role: SelectRole::X1,
gpio: 14
},
SelectLine {
role: SelectRole::X2,
gpio: 15
},
]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let overrides = build_cs_overrides(
&layout,
ChipSetType::Multi,
&cs_config,
&[
CsConfig::new(Some(CsLogic::ActiveLow), None, None),
CsConfig::new(Some(CsLogic::ActiveLow), None, None),
],
);
assert_eq!(
overrides,
alloc::vec![
encode_override(13, GpioOverride::GpioOverInvert),
encode_override(14, GpioOverride::GpioOverInvert),
encode_override(15, GpioOverride::GpioOverInvert),
]
);
}
#[test]
fn multi_active_high_no_override() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13
},
SelectLine {
role: SelectRole::X1,
gpio: 14
},
]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveHigh), None, None);
let overrides = build_cs_overrides(
&layout,
ChipSetType::Multi,
&cs_config,
&[CsConfig::new(Some(CsLogic::ActiveHigh), None, None)],
);
assert!(overrides.is_empty());
}
#[test]
fn multi_mixed_polarity_only_cs1_inverted() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13
},
SelectLine {
role: SelectRole::X1,
gpio: 14
},
]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let secondary = &[CsConfig::new(Some(CsLogic::ActiveHigh), None, None)];
let overrides = build_cs_overrides(&layout, ChipSetType::Multi, &cs_config, secondary);
assert_eq!(
overrides,
alloc::vec![encode_override(13, GpioOverride::GpioOverInvert)]
);
}
#[test]
fn half_select_active_high_inverted_for_single() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Ce,
gpio: 13
},
SelectLine {
role: SelectRole::HalfSelect,
gpio: 14
},
]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveHigh), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Single, &cs_config, &[]);
assert_eq!(
overrides,
alloc::vec![encode_override(14, GpioOverride::GpioOverInvert)]
);
}
#[test]
fn half_select_active_low_no_override_for_single() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Ce,
gpio: 13
},
SelectLine {
role: SelectRole::HalfSelect,
gpio: 14
},
]);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Single, &cs_config, &[]);
assert!(overrides.is_empty());
}
fn addr_layout_with_x(x1: u8, x2: u8) -> AddrLayout {
AddrLayout {
gpio_base: 0,
num_addr_pins: 16,
x1_gpio: Some(x1),
x2_gpio: Some(x2),
addr_pin_gpios: alloc::vec![7, 6, 5, 4, 3, 2, 1, 0, 10, 11, 14, 15, 12],
excess_addr_pin_gpios: alloc::vec![],
}
}
#[test]
fn x_override_single_never() {
let layout = addr_layout_with_x(14, 15);
let overrides = build_gpio_x_overrides(&layout, ChipSetType::Single, 1, Board::Fire24A);
assert!(overrides.is_empty());
}
#[test]
fn x_override_multi_never() {
let layout = addr_layout_with_x(14, 15);
let overrides = build_gpio_x_overrides(&layout, ChipSetType::Multi, 3, Board::Fire24A);
assert!(overrides.is_empty());
}
#[test]
fn x_override_banked_2chip_x1_only() {
let layout = addr_layout_with_x(14, 15);
let overrides = build_gpio_x_overrides(&layout, ChipSetType::Banked, 2, Board::Fire24A);
assert_eq!(
overrides,
alloc::vec![encode_override(14, GpioOverride::GpioOverInvert)]
);
}
#[test]
fn x_override_banked_3chip_x1_and_x2() {
let layout = addr_layout_with_x(14, 15);
let overrides = build_gpio_x_overrides(&layout, ChipSetType::Banked, 3, Board::Fire24A);
assert_eq!(
overrides,
alloc::vec![
encode_override(14, GpioOverride::GpioOverInvert),
encode_override(15, GpioOverride::GpioOverInvert),
]
);
}
#[test]
fn x_override_banked_4chip_x1_and_x2() {
let layout = addr_layout_with_x(14, 15);
let overrides = build_gpio_x_overrides(&layout, ChipSetType::Banked, 4, Board::Fire24A);
assert_eq!(
overrides,
alloc::vec![
encode_override(14, GpioOverride::GpioOverInvert),
encode_override(15, GpioOverride::GpioOverInvert),
]
);
}
#[test]
fn encode_override_invert_format() {
assert_eq!(
encode_override(14, GpioOverride::GpioOverInvert),
(1u8 << 6) | 14
);
assert_eq!(encode_override(14, GpioOverride::GpioOverNormal), 14);
}
fn alg_data0_high() -> OneromAlgDataConfig {
OneromAlgDataConfig::AlgData0 {
clkdiv_int: 1,
clkdiv_frac: 0,
gpio_base: 0,
base_data_pin: 16,
word_size: 8,
}
}
fn fire24a_2364_cs_data() -> CsDataLayout {
CsDataLayout {
gpio_base: 0,
base_data_pin: 16,
num_data_pins: 8,
data_pin_gpios: alloc::vec![16, 17, 18, 19, 20, 21, 22, 23],
base_cs_pin: 13,
num_cs_pins: 1,
cs_ignore_index: None,
select_lines: alloc::vec![SelectLine {
role: SelectRole::Cs1,
gpio: 13,
}],
commoned_lines: alloc::vec![],
alg_cs2: None,
}
}
#[test]
fn unused_single_forces_unused_x_pins() {
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 overrides =
build_unused_addr_overrides(&addr_layout, &fire24a_2364_cs_data(), &alg_data0_high());
assert_eq!(
overrides,
alloc::vec![
encode_override(8, GpioOverride::GpioOverLow),
encode_override(9, GpioOverride::GpioOverLow),
]
);
}
#[test]
fn unused_banked_excludes_used_x1() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 16,
x1_gpio: Some(9),
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 overrides =
build_unused_addr_overrides(&addr_layout, &fire24a_2364_cs_data(), &alg_data0_high());
assert_eq!(
overrides,
alloc::vec![encode_override(8, GpioOverride::GpioOverLow)]
);
}
#[test]
fn unused_full_window_emits_nothing() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 16,
x1_gpio: Some(9),
x2_gpio: Some(8),
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 overrides =
build_unused_addr_overrides(&addr_layout, &fire24a_2364_cs_data(), &alg_data0_high());
assert!(overrides.is_empty());
}
#[test]
fn unused_excludes_byte_pin_and_commoned_cs_range() {
let addr_layout = AddrLayout {
gpio_base: 0,
num_addr_pins: 8,
x1_gpio: None,
x2_gpio: None,
addr_pin_gpios: alloc::vec![0, 1, 2, 3],
excess_addr_pin_gpios: alloc::vec![],
};
let cs_data_layout = CsDataLayout {
gpio_base: 0,
base_data_pin: 24,
num_data_pins: 4,
data_pin_gpios: alloc::vec![24, 25, 26, 27],
base_cs_pin: 6,
num_cs_pins: 2, cs_ignore_index: None,
select_lines: alloc::vec![SelectLine {
role: SelectRole::Ce,
gpio: 7,
}],
commoned_lines: alloc::vec![],
alg_cs2: None,
};
let alg_data = OneromAlgDataConfig::AlgData1 {
clkdiv_int: 1,
clkdiv_frac: 0,
gpio_base: 0,
base_data_pin: 24,
word_size: 16,
byte_pin: 5,
a_minus_1_pin: 27,
};
let overrides = build_unused_addr_overrides(&addr_layout, &cs_data_layout, &alg_data);
assert_eq!(
overrides,
alloc::vec![encode_override(4, GpioOverride::GpioOverLow)]
);
}
#[test]
fn multi_active_low_commoned_inverted() {
let layout = layout_with_commoned(
alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13,
},
SelectLine {
role: SelectRole::X1,
gpio: 15,
},
],
alloc::vec![SelectLine {
role: SelectRole::Cs2,
gpio: 14,
}],
);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), Some(CsLogic::ActiveLow), None);
let secondary = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Multi, &cs_config, &[secondary]);
assert_eq!(
overrides,
alloc::vec![
encode_override(13, GpioOverride::GpioOverInvert),
encode_override(15, GpioOverride::GpioOverInvert),
encode_override(14, GpioOverride::GpioOverInvert),
]
);
}
#[test]
fn multi_active_high_commoned_not_inverted() {
let layout = layout_with_commoned(
alloc::vec![
SelectLine {
role: SelectRole::Cs1,
gpio: 13,
},
SelectLine {
role: SelectRole::X1,
gpio: 15,
},
],
alloc::vec![SelectLine {
role: SelectRole::Cs2,
gpio: 14,
}],
);
let cs_config = CsConfig::new(Some(CsLogic::ActiveLow), Some(CsLogic::ActiveHigh), None);
let secondary = CsConfig::new(Some(CsLogic::ActiveLow), None, None);
let overrides = build_cs_overrides(&layout, ChipSetType::Multi, &cs_config, &[secondary]);
assert_eq!(
overrides,
alloc::vec![
encode_override(13, GpioOverride::GpioOverInvert),
encode_override(15, GpioOverride::GpioOverInvert),
]
);
}
#[test]
fn multi_cs2_primary_active_high_x_pins_not_inverted() {
let layout = layout_with(alloc::vec![
SelectLine {
role: SelectRole::Cs2,
gpio: 14,
},
SelectLine {
role: SelectRole::X1,
gpio: 15,
},
SelectLine {
role: SelectRole::X2,
gpio: 16,
},
]);
let cs_config = CsConfig::new(
Some(CsLogic::Ignore),
Some(CsLogic::ActiveHigh),
Some(CsLogic::Ignore),
);
let overrides = build_cs_overrides(
&layout,
ChipSetType::Multi,
&cs_config,
&[
CsConfig::new(
Some(CsLogic::Ignore),
Some(CsLogic::ActiveHigh),
Some(CsLogic::Ignore),
),
CsConfig::new(
Some(CsLogic::Ignore),
Some(CsLogic::ActiveHigh),
Some(CsLogic::Ignore),
),
],
);
assert!(
overrides.is_empty(),
"CS2-primary active-high multi must not invert X pins, got {overrides:?}"
);
}
}