verify that spi1 and sp2 read works properly and they can be erase and program. dma read is not reliable(might be a placement issue)
diff --git a/target/ast10x0/peripherals/scu/routing.rs b/target/ast10x0/peripherals/scu/routing.rs index 784c206..5429fca 100644 --- a/target/ast10x0/peripherals/scu/routing.rs +++ b/target/ast10x0/peripherals/scu/routing.rs
@@ -160,16 +160,10 @@ self.unlock_write_protection(); let scu0f0 = self.regs().scu0f0().read().bits(); - pw_log::debug!("SPIM pre-config: SCU0F0=0x{:08x}", scu0f0 as u32); let op_idx = match ast1060_spim_op_idx(scu0f0) { Some(idx) => idx, None => return None, }; - pw_log::debug!( - "SPIM pre-config: active SPIM index {}, op index {}", - ((scu0f0 & 0x7) - 1) as u32, - op_idx as u32 - ); let clk = AST1060_SPIM_CLK_GPIO[op_idx]; let cs = AST1060_SPIM_CS_GPIO[op_idx]; @@ -206,11 +200,6 @@ write_volatile(cs_scu_addr, reg_val); } - pw_log::debug!( - "SPIM pre-config: op index {}, saved CLK direction mask 0x{:08x}", - op_idx as u32, - clk_gpio_ori_val[op_idx] as u32 - ); Some(SpimGpioOriVal { clk_gpio_ori_val }) }
diff --git a/target/ast10x0/peripherals/smc/controller.rs b/target/ast10x0/peripherals/smc/controller.rs index 2867f9b..2e8bd5d 100644 --- a/target/ast10x0/peripherals/smc/controller.rs +++ b/target/ast10x0/peripherals/smc/controller.rs
@@ -514,13 +514,13 @@ if cs == ChipSelect::Cs1 && self.config.cs1.is_none() { return Err(SmcError::InvalidChipSelect); } - + let cs_idx = cs as usize; // Derive user-mode base from the stored normal-read value: preserve // frequency bits and replace mode type with ASPEED_SPI_USER. let user_base = (self.normal_read_ctrl[cs_idx] & !0x7) | ASPEED_SPI_USER; let window = self.flash_window_base[cs_idx] as *mut u32; - + pw_log::debug!("cs ctrl: {:08x}", self.regs.read_cs_ctrl(cs) as u32); // Assert CS: inactive first, then active (matches aspeed-rust activate_user). self.regs .write_cs_ctrl(cs, user_base | ASPEED_SPI_USER_INACTIVE); @@ -550,6 +550,7 @@ self.regs .write_cs_ctrl(cs, user_base | ASPEED_SPI_USER_INACTIVE); self.regs.write_cs_ctrl(cs, self.normal_read_ctrl[cs_idx]); + pw_log::debug!("cs ctrl: {:08x}", self.regs.read_cs_ctrl(cs) as u32); Ok(()) } @@ -564,7 +565,7 @@ let cs_capacity = self.cs_capacity_bytes(cs)?; let use_4b_addr = spi_nor_uses_4b_addr(cs_capacity); let read_opcode = spi_nor_qread_cmd_for_capacity(cs_capacity); - //pw_log::info!("=== spi_read_init()==="); + pw_log::info!("=== spi_read_init()==="); let read_cmd = mode.data_io_bits() | (read_opcode << 16) | (dummy << 6) | ASPEED_SPI_NORMAL_READ; @@ -604,8 +605,8 @@ let reg = self.regs.read_cs_ctrl(cs); self.regs .write_cs_ctrl(cs, (reg & !SPI_CTRL_FREQ_MASK) | encoded_div); - self.normal_read_ctrl[cs_idx] &= (!SPI_CTRL_FREQ_MASK) | encoded_div; - + self.normal_read_ctrl[cs_idx] = self.regs.read_cs_ctrl(cs); + pw_log::debug!("normal_read_ctrl: {:08x}", self.normal_read_ctrl[cs_idx] as u32); Ok(()) }
diff --git a/target/ast10x0/tests/smc/read/target_spi1.rs b/target/ast10x0/tests/smc/read/target_spi1.rs index 04d23f7..8062d1f 100644 --- a/target/ast10x0/tests/smc/read/target_spi1.rs +++ b/target/ast10x0/tests/smc/read/target_spi1.rs
@@ -6,6 +6,8 @@ #![no_std] #![no_main] +const TEST_OFFSET: u32 = 0x10_0000; + use ast10x0_peripherals::scu::{ pinctrl::{PINCTRL_SPI1_QUAD, PINCTRL_SPIM1_DEFAULT}, ScuExtMuxSelect, ScuRegisters, SpiMonitorInstance, SpiMonitorPassthrough, SpiMonitorSource, @@ -100,13 +102,13 @@ } dump_smc_read(&buf, buf.len() as u32); - pw_log::info!("=== SPI1 DMA read @ 0x00000000 ==="); + pw_log::info!("=== SPI1 DMA read @ {:08x} ===", TEST_OFFSET as u32); let dma_buf = unsafe { core::slice::from_raw_parts_mut(0x41500 as *mut u8, 256) }; let mut dma_txn = SpiTransaction::dma_read_with_spim( &mut spi, SpiMonitorInstance::Spim0, ChipSelect::Cs0, - 0x0, + TEST_OFFSET, 0x41500usize, dma_buf.len() as u32, )?;
diff --git a/target/ast10x0/tests/smc/read/target_spi2.rs b/target/ast10x0/tests/smc/read/target_spi2.rs index 1b67bb3..bcbf546 100644 --- a/target/ast10x0/tests/smc/read/target_spi2.rs +++ b/target/ast10x0/tests/smc/read/target_spi2.rs
@@ -26,6 +26,8 @@ mod target_debug; use target_debug::{dump_smc_read, dump_smc_register}; +const TEST_OFFSET: u32 = 0x10_0000; + const SPI_FLASH_CONFIG: FlashConfig = FlashConfig { capacity_mb: 32, page_size: 256, @@ -185,7 +187,7 @@ &mut spi, SpiMonitorInstance::Spim2, ChipSelect::Cs0, - 0x0, + TEST_OFFSET, &mut buf, )?; if n != buf.len() { @@ -193,13 +195,13 @@ } dump_smc_read(&buf, buf.len() as u32); - pw_log::info!("=== SPI2 DMA read @ 0x00000000 ==="); + pw_log::info!("=== SPI2 DMA read @ {:08x} ===", TEST_OFFSET as u32); let dma_buf = unsafe { core::slice::from_raw_parts_mut(0x41500 as *mut u8, 256) }; let mut dma_txn = SpiTransaction::dma_read_with_spim( &mut spi, SpiMonitorInstance::Spim2, ChipSelect::Cs0, - 0x0, + TEST_OFFSET, 0x41500usize, dma_buf.len() as u32, )?;
diff --git a/target/ast10x0/tests/smc/write/spi_write_common.rs b/target/ast10x0/tests/smc/write/spi_write_common.rs index 72a3d21..99d64d8 100644 --- a/target/ast10x0/tests/smc/write/spi_write_common.rs +++ b/target/ast10x0/tests/smc/write/spi_write_common.rs
@@ -3,6 +3,8 @@ //! Shared destructive SPI flash test flow. +use core::cell::UnsafeCell; + use ast10x0_peripherals::smc::{ ChipSelect, FlashConfig, SmcConfig, SmcController, SmcError, SmcTopology, SpiReady, SpiTransaction, SpiUninit, TransferMode, @@ -24,9 +26,24 @@ const TEST_OFFSET: u32 = 0x10_0000; const PAGE_LEN: usize = 256; const SECTOR_LEN: usize = 4096; -const BACKUP_ADDR: usize = 0x41000; const STATUS_WIP: u8 = 1; +const STATUS_WEL: u8 = 1 << 1; const STATUS_MAX_POLLS: u32 = 1_000_000; +const STATUS_CLEAR_SAMPLES: u8 = 3; +const STATUS_POLL_DELAY: u32 = 64; +const DMA_BUFFER: u32 = 0x41500; + +#[repr(align(256))] +struct BackupBuffer(UnsafeCell<[u8; SECTOR_LEN]>); + +#[repr(align(256))] +struct PageBuffer([u8; PAGE_LEN]); + +// The test runs synchronously on one core and borrows this buffer only for the +// duration of run_flash_test(). +unsafe impl Sync for BackupBuffer {} + +static BACKUP_BUFFER: BackupBuffer = BackupBuffer(UnsafeCell::new([0; SECTOR_LEN])); fn fill_test_pattern(out: &mut [u8; PAGE_LEN], salt: u8) { let mut i = 0usize; @@ -37,20 +54,31 @@ } fn expect_erased(buf: &[u8]) -> Result<(), SmcError> { - for &byte in buf { + for (index, &byte) in buf.iter().enumerate() { if byte != 0xff { + pw_log::info!( + "erase verify failed at 0x{:08x}: expected ff, got {:02x}", + (TEST_OFFSET as usize + index) as u32, + byte as u32 + ); return Err(SmcError::HardwareError); } } Ok(()) } +#[allow(dead_code)] +fn is_erased(buf: &[u8]) -> bool { + buf.iter().all(|&byte| byte == 0xff) +} + fn command_with_spim( spi: &mut SpiReady, spim: SpiMonitorInstance, cmd: &[u8], tx: &[u8], rx: &mut [u8], + mode: TransferMode, ) -> Result<(), SmcError> { SpiTransaction::transceive_user_with_spim( spi, @@ -59,40 +87,124 @@ cmd, tx, rx, - TransferMode::Mode111, + mode, ) } -fn read_with_spim( +fn dma_read_with_spim( spi: &mut SpiReady, spim: SpiMonitorInstance, offset: u32, buf: &mut [u8], ) -> Result<(), SmcError> { - let n = SpiTransaction::read_with_spim(spi, spim, ChipSelect::Cs0, offset, buf)?; - if n != buf.len() { + let mut dma_txn = SpiTransaction::dma_read_with_spim( + spi, + spim, + ChipSelect::Cs0, + offset, + buf.as_mut_ptr() as usize, + u32::try_from(buf.len()).map_err(|_| SmcError::InvalidCapacity)?, + )?; + + loop { + match dma_txn.poll_dma_completion() { + core::task::Poll::Pending => core::hint::spin_loop(), + core::task::Poll::Ready(result) => return result, + } + } +} + +fn read_status(spi: &mut SpiReady, spim: SpiMonitorInstance) -> Result<u8, SmcError> { + let mut status = [0u8; 1]; + command_with_spim( + spi, + spim, + &[0x05], + &[], + &mut status, + TransferMode::Mode111, + )?; + Ok(status[0]) +} + +fn write_enable(spi: &mut SpiReady, spim: SpiMonitorInstance) -> Result<(), SmcError> { + command_with_spim( + spi, + spim, + &[0x06], + &[], + &mut [], + TransferMode::Mode111, + )?; + + let status = read_status(spi, spim)?; + if status & STATUS_WEL == 0 { + pw_log::info!("write enable failed: SR1=0x{:02x}", status as u32); return Err(SmcError::HardwareError); } Ok(()) } -fn write_enable(spi: &mut SpiReady, spim: SpiMonitorInstance) -> Result<(), SmcError> { - command_with_spim(spi, spim, &[0x06], &[], &mut []) +fn status_poll_delay() { + let mut cycles = 0u32; + while cycles < STATUS_POLL_DELAY { + core::hint::spin_loop(); + cycles += 1; + } } -fn wait_write_complete(spi: &mut SpiReady, spim: SpiMonitorInstance) -> Result<(), SmcError> { +fn wait_write_complete( + spi: &mut SpiReady, + spim: SpiMonitorInstance, + initial_status: u8, +) -> Result<(), SmcError> { let mut polls = 0u32; + let mut clear_samples = 0u8; + let mut status = initial_status; + while polls < STATUS_MAX_POLLS { - let mut status = [0u8; 1]; - command_with_spim(spi, spim, &[0x05], &[], &mut status)?; - if status[0] & STATUS_WIP == 0 { - return Ok(()); + if status & STATUS_WIP == 0 { + clear_samples += 1; + if clear_samples == STATUS_CLEAR_SAMPLES { + pw_log::info!( + "write complete: SR1=0x{:02x}, polls={}", + status as u32, + polls as u32 + ); + return Ok(()); + } + } else { + clear_samples = 0; } + + status_poll_delay(); + status = read_status(spi, spim)?; polls += 1; } Err(SmcError::Timeout) } +fn read_fast_with_spim( + spi: &mut SpiReady, + spim: SpiMonitorInstance, + offset: u32, + buf: &mut [u8], +) -> Result<(), SmcError> { + let address = offset.to_be_bytes(); + // 0x0C: 4-byte-address Fast Read, followed by one dummy byte. + let cmd = [ + 0x0c, address[0], address[1], address[2], address[3], 0x00, + ]; + command_with_spim( + spi, + spim, + &cmd, + &[], + buf, + TransferMode::Mode114, + ) +} + fn erase_sector( spi: &mut SpiReady, spim: SpiMonitorInstance, @@ -101,8 +213,21 @@ write_enable(spi, spim)?; let address = offset.to_be_bytes(); let cmd = [0x21, address[0], address[1], address[2], address[3]]; - command_with_spim(spi, spim, &cmd, &[], &mut [])?; - wait_write_complete(spi, spim) + command_with_spim( + spi, + spim, + &cmd, + &[], + &mut [], + TransferMode::Mode111, + )?; + let status = read_status(spi, spim)?; + pw_log::info!("erase command SR1=0x{:02x}", status as u32); + if status & STATUS_WIP == 0 { + pw_log::info!("erase command did not enter busy state"); + return Err(SmcError::HardwareError); + } + wait_write_complete(spi, spim, status) } fn program_page( @@ -114,27 +239,49 @@ write_enable(spi, spim)?; let address = offset.to_be_bytes(); let cmd = [0x12, address[0], address[1], address[2], address[3]]; - command_with_spim(spi, spim, &cmd, data, &mut [])?; - wait_write_complete(spi, spim) + command_with_spim( + spi, + spim, + &cmd, + data, + &mut [], + TransferMode::Mode114, + )?; + let status = read_status(spi, spim)?; + pw_log::info!("program command SR1=0x{:02x}", status as u32); + wait_write_complete(spi, spim, status) } +#[allow(dead_code)] fn verify_data( spi: &mut SpiReady, spim: SpiMonitorInstance, offset: u32, expected: &[u8], ) -> Result<(), SmcError> { - let mut read_buf = [0u8; PAGE_LEN]; + let dma_buf = unsafe { core::slice::from_raw_parts_mut(DMA_BUFFER as *mut u8, 256) }; let mut cursor = 0usize; while cursor < expected.len() { let len = core::cmp::min(PAGE_LEN, expected.len() - cursor); - read_with_spim( + dma_read_with_spim( spi, spim, offset + cursor as u32, - &mut read_buf[..len], + &mut dma_buf[..len], )?; - if read_buf[..len] != expected[cursor..cursor + len] { + if dma_buf[..len] != expected[cursor..cursor + len] { + let mut mismatch = 0usize; + while mismatch < len + && dma_buf[mismatch] == expected[cursor + mismatch] + { + mismatch += 1; + } + pw_log::info!( + "data verify failed at 0x{:08x}: expected {:02x}, got {:02x}", + (offset as usize + cursor + mismatch) as u32, + expected[cursor + mismatch] as u32, + dma_buf[mismatch] as u32 + ); return Err(SmcError::HardwareError); } cursor += len; @@ -142,6 +289,7 @@ Ok(()) } +#[allow(dead_code)] fn restore_sector( spi: &mut SpiReady, spim: SpiMonitorInstance, @@ -152,12 +300,10 @@ let mut cursor = 0usize; while cursor < original.len() { let len = core::cmp::min(PAGE_LEN, original.len() - cursor); - program_page( - spi, - spim, - TEST_OFFSET + cursor as u32, - &original[cursor..cursor + len], - )?; + let page = &original[cursor..cursor + len]; + if !is_erased(page) { + program_page(spi, spim, TEST_OFFSET + cursor as u32, page)?; + } cursor += len; } @@ -171,21 +317,46 @@ ) -> Result<(), SmcError> { erase_sector(spi, spim, TEST_OFFSET)?; - let mut read_buf = [0u8; PAGE_LEN]; - read_with_spim(spi, spim, TEST_OFFSET, &mut read_buf)?; - expect_erased(&read_buf)?; - dump_smc_read(&read_buf, PAGE_LEN as u32); + let mut read_buf = PageBuffer([0u8; PAGE_LEN]); + + read_fast_with_spim(spi, spim, TEST_OFFSET, &mut read_buf.0)?; + if let Err(error) = expect_erased(&read_buf.0) { + pw_log::info!("user-mode erase verification failed"); + return Err(error); + } + pw_log::info!("user-mode erase verification passed"); + + let dma_buf = unsafe { core::slice::from_raw_parts_mut((DMA_BUFFER+0x100) as *mut u8, 256) }; + dma_read_with_spim(spi, spim, TEST_OFFSET, dma_buf)?; + expect_erased(dma_buf)?; + + //dump_smc_read(dma_buf, PAGE_LEN as u32); let mut pattern = [0u8; PAGE_LEN]; fill_test_pattern(&mut pattern, salt); program_page(spi, spim, TEST_OFFSET, &pattern)?; - read_buf.fill(0); - read_with_spim(spi, spim, TEST_OFFSET, &mut read_buf)?; - if read_buf != pattern { + dma_read_with_spim(spi, spim, TEST_OFFSET, dma_buf)?; + + + if dma_buf != pattern { + let mut mismatch = 0usize; + while mismatch < PAGE_LEN && dma_buf[mismatch] == pattern[mismatch] { + mismatch += 1; + } + pw_log::info!( + "program verify failed at 0x{:08x}: expected {:02x}, got {:02x}", + (TEST_OFFSET as usize + mismatch) as u32, + pattern[mismatch] as u32, + dma_buf[mismatch] as u32 + ); + pw_log::info!("pattern::"); + dump_smc_read(&pattern, PAGE_LEN as u32); + pw_log::info!("dma buffer::"); + dump_smc_read(dma_buf, PAGE_LEN as u32); return Err(SmcError::HardwareError); } - dump_smc_read(&read_buf, PAGE_LEN as u32); + Ok(()) } @@ -195,7 +366,14 @@ salt: u8, ) -> Result<(), SmcError> { let mut jedec = [0u8; 3]; - command_with_spim(spi, spim, &[0x9f], &[], &mut jedec)?; + command_with_spim( + spi, + spim, + &[0x9f], + &[], + &mut jedec, + TransferMode::Mode111, + )?; pw_log::info!( "JEDEC ID: {:02x} {:02x} {:02x}", jedec[0] as u32, @@ -207,17 +385,20 @@ } pw_log::info!("=== backup test sector ==="); - let original = unsafe { core::slice::from_raw_parts_mut(BACKUP_ADDR as *mut u8, SECTOR_LEN) }; - read_with_spim(spi, spim, TEST_OFFSET, original)?; + // SAFETY: This test has exclusive access to BACKUP_BUFFER until it returns. + let original = unsafe { &mut *BACKUP_BUFFER.0.get() }; + dma_read_with_spim(spi, spim, TEST_OFFSET, original)?; pw_log::info!("=== erase/write/read test sector ==="); let test_result = destructive_test(spi, spim, salt); - + /* pw_log::info!("=== restore test sector ==="); let restore_result = restore_sector(spi, spim, original); test_result?; restore_result + */ + test_result } pub fn new_spi( @@ -228,7 +409,7 @@ controller_id: controller, cs0: Some(FLASH_CONFIG), cs1: None, - dma_enabled: false, + dma_enabled: true, enable_interrupts: false, topology, };