use crate::fcb;
use crate::fields::*;
use crate::lookup::LookupTable;
pub struct Builder {
pub read_sample_clock_source: ReadSampleClockSource,
pub cs_hold_time: CSHoldTime,
pub cs_setup_time: CSSetupTime,
pub column_address_width: ColumnAddressWidth,
pub device_mode_configuration: DeviceModeConfiguration,
pub wait_time_cfg_commands: WaitTimeConfigurationCommands,
pub device_mode_seq: DeviceModeSequence,
pub device_type: DeviceType,
pub serial_flash_pad_type: FlashPadType,
pub serial_clk_freq: SerialClockFrequency,
pub flash_a1_size: SerialFlashSize<A1>,
pub flash_a2_size: SerialFlashSize<A2>,
pub flash_b1_size: SerialFlashSize<B1>,
pub flash_b2_size: SerialFlashSize<B2>,
pub lookup_table: LookupTable,
}
impl Builder {
pub fn build(self) -> Result<fcb::FCB, Box<dyn std::error::Error>> {
let mut fcb = fcb::FCB::new();
fcb.field_comment(
0x00C,
&(self.read_sample_clock_source as u8).to_le_bytes(),
"readSampleClkSrc",
);
fcb.field_comment(0x00D, &self.cs_hold_time, "csHoldTime");
fcb.field_comment(0x00E, &self.cs_setup_time, "csSetupTime");
fcb.field_comment(0x00F, &self.column_address_width, "columnAddressWidth");
fcb.field_comment(
0x010,
match self.device_mode_configuration {
DeviceModeConfiguration::Disabled => &[0],
DeviceModeConfiguration::Enabled(_) => &[1],
},
"deviceModeCfgEnable",
);
fcb.field_comment(0x013, &self.wait_time_cfg_commands, "waitTimeCfgCommands");
fcb.field_comment(0x014, &self.device_mode_seq, "deviceModeSeq");
if let DeviceModeConfiguration::Enabled(arg) = self.device_mode_configuration {
fcb.field_comment(0x018, &arg, "deviceModeArg");
}
fcb.field_comment(
0x044,
match self.device_type {
DeviceType::SerialNOR(_) => &[1],
},
"deviceType",
);
fcb.field_comment(
0x045,
&(self.serial_flash_pad_type as u8).to_le_bytes(),
"sflashPadType",
);
fcb.field_comment(
0x046,
&(self.serial_clk_freq as u8).to_le_bytes(),
"serialClkFreq",
);
fcb.field_comment(
0x050,
&self.flash_a1_size.size.to_le_bytes(),
"sflashA1Size",
);
fcb.field_comment(
0x054,
&self.flash_a2_size.size.to_le_bytes(),
"sflashA2Size",
);
fcb.field_comment(
0x058,
&self.flash_b1_size.size.to_le_bytes(),
"sflashB1Size",
);
fcb.field_comment(
0x05C,
&self.flash_b2_size.size.to_le_bytes(),
"sflashB2Size",
);
let lookup_table_offset = 0x080;
for (idx, byte) in self.lookup_table.iter().enumerate() {
fcb.field_comment(
lookup_table_offset + idx,
&[*byte],
format!("lookupTable[{}]", idx,),
);
}
match self.device_type {
DeviceType::SerialNOR(norcb) => {
fcb.field_comment(0x1C0, &norcb.page_size, "pageSize");
fcb.field_comment(0x1C4, &norcb.sector_size, "sectorSize");
fcb.field_comment(
0x1C8,
&(norcb.ip_cmd_serial_clk_freq as u8).to_le_bytes(),
"ipCmdSerialClkFreq",
);
}
}
Ok(fcb)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::nor;
#[test]
fn teensy4_fcb() {
let nor_cb = nor::ConfigurationBlock {
page_size: nor::PageSize::new(256),
sector_size: nor::SectorSize::new(4096),
ip_cmd_serial_clk_freq: nor::SerialClockFrequency::MHz30,
};
let lookup_table = {
let mut lookup = LookupTable::new();
lookup.insert_u32(0, 0x0A18_04EB);
lookup.insert_u32(1, 0x2604_3206);
lookup.insert_u32(4, 0x2404_0405);
lookup.insert_u32(12, 0x0000_0406);
lookup.insert_u32(20, 0x0818_0420);
lookup.insert_u32(32, 0x0818_04D8);
lookup.insert_u32(36, 0x0818_0402);
lookup.insert_u32(37, 0x0000_2004);
lookup.insert_u32(44, 0x0000_0460);
lookup
};
let builder = Builder {
read_sample_clock_source: ReadSampleClockSource::LoopbackFromDQSPad,
cs_hold_time: CSHoldTime::new(0x01),
cs_setup_time: CSSetupTime::new(0x02),
column_address_width: ColumnAddressWidth::other_devices(),
device_mode_configuration: DeviceModeConfiguration::Disabled,
wait_time_cfg_commands: WaitTimeConfigurationCommands::disable(),
device_mode_seq: DeviceModeSequence::new(0, 0),
flash_a1_size: SerialFlashSize::new(0x0020_0000),
flash_a2_size: SerialFlashSize::default(),
flash_b1_size: SerialFlashSize::default(),
flash_b2_size: SerialFlashSize::default(),
serial_clk_freq: SerialClockFrequency::MHz60,
serial_flash_pad_type: FlashPadType::Quad,
device_type: DeviceType::SerialNOR(nor_cb),
lookup_table,
};
let fcb = builder.build().unwrap();
let expected: [u32; 128] = [
0x4246_4346,
0x5601_0000,
0,
0x0002_0101,
0x0000_0000,
0,
0,
0x0000_0000,
0,
0,
0,
0,
0,
0,
0,
0,
0x0000_0000,
0x0003_0401,
0,
0,
0x0020_0000,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x0000_0000,
0x0A18_04EB,
0x2604_3206,
0,
0,
0x2404_0405,
0,
0,
0,
0,
0,
0,
0,
0x0000_0406,
0,
0,
0,
0,
0,
0,
0,
0x0818_0420,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x0818_04D8,
0,
0,
0,
0x0818_0402,
0x0000_2004,
0,
0,
0,
0,
0,
0,
0x0000_0460,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
256,
4096,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
];
let mut actual: [u32; 128] = [0; 128];
for (bytes, slot) in fcb.raw.chunks_exact(4).zip(actual.iter_mut()) {
use std::convert::TryInto;
*slot = u32::from_le_bytes(bytes.try_into().unwrap());
}
const CHUNK_TEST_SIZE: usize = 16;
for (idx, (actual_chunk, expected_chunk)) in actual
.chunks(CHUNK_TEST_SIZE)
.zip(expected.chunks(CHUNK_TEST_SIZE))
.enumerate()
{
assert_eq!(
actual_chunk,
expected_chunk,
"Start index {}",
idx * CHUNK_TEST_SIZE
);
}
}
}