Add erase/write/read verify test.
fixed erase/write transit user error
seperated SPI and FMC segment range calculation.
diff --git a/target/ast10x0/peripherals/smc/controller.rs b/target/ast10x0/peripherals/smc/controller.rs
index 1ae7248..f588ba0 100644
--- a/target/ast10x0/peripherals/smc/controller.rs
+++ b/target/ast10x0/peripherals/smc/controller.rs
@@ -7,8 +7,9 @@
 use core::marker::PhantomData;
 
 use crate::smc::helpers::{
-    encode_segment, flash_capacity_bytes, get_mid_point_of_longest_one, spi_calibration_enable,
-    spi_freq_div, total_capacity_bytes, validate_dma_read, validate_mapped_range,
+    encode_fmc_segment, encode_spi_segment, flash_capacity_bytes, get_mid_point_of_longest_one,
+    spi_calibration_enable, spi_freq_div, total_capacity_bytes, validate_dma_read,
+    validate_mapped_range,
     SPI_CTRL_FREQ_MASK, SPI_DMA_CALC_CKSUM, SPI_DMA_CALIB_MODE, SPI_DMA_ENABLE,
     SPI_DMA_RAM_MAP_BASE,
 };
@@ -179,6 +180,13 @@
             _mode: PhantomData,
         })
     }
+    fn encode_segment(&self, start: usize, end: usize) -> Result<u32, SmcError> {
+        match self.config.controller_id {
+            SmcController::Fmc => encode_fmc_segment(start, end),
+            SmcController::Spi1 | SmcController::Spi2 => encode_spi_segment(start, end),
+        }
+    }
+
     fn setup_segments(&self) -> Result<(), SmcError> {
         // Decode-range sizing is topology-aware.
         //
@@ -198,12 +206,12 @@
         total_capacity_bytes(self.config.cs0, self.config.cs1)?;
 
         if cs0_size > 0 {
-            let seg = encode_segment(0, cs0_size)?;
+            let seg = self.encode_segment(0, cs0_size)?;
             self.regs.write_cs0_segment(seg);
         }
 
         if cs1_size > 0 {
-            let seg = encode_segment(cs0_size, cs0_size + cs1_size)?;
+            let seg = self.encode_segment(cs0_size, cs0_size + cs1_size)?;
             self.regs.write_cs1_segment(seg);
         }
 
@@ -506,7 +514,7 @@
         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] & SPI_CTRL_FREQ_MASK) | 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;
 
         // Assert CS: inactive first, then active (matches aspeed-rust activate_user).
diff --git a/target/ast10x0/peripherals/smc/device/flash.rs b/target/ast10x0/peripherals/smc/device/flash.rs
index 70992a5..062409e 100644
--- a/target/ast10x0/peripherals/smc/device/flash.rs
+++ b/target/ast10x0/peripherals/smc/device/flash.rs
@@ -82,6 +82,10 @@
     pub const READ_ID: u8 = 0x9F;
 }
 
+const STATUS_WIP: u8 = 0x01;
+const WRITE_COMPLETE_MAX_POLLS: u32 = 1_000_000;
+const STATUS_POLL_SPIN_DELAY: u32 = 64;
+
 /// Addressing policy for SPI NOR command transactions.
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum FlashAddressingPolicy {
@@ -166,6 +170,12 @@
     Ok(true)
 }
 
+fn poll_delay() {
+    for _ in 0..STATUS_POLL_SPIN_DELAY {
+        core::hint::spin_loop();
+    }
+}
+
 enum FlashBackend<'a> {
     Fmc(&'a FmcReady),
     Spi(&'a SpiReady),
@@ -472,13 +482,19 @@
         Ok(id)
     }
 
+    fn write_enable(&mut self) -> Result<(), SmcError> {
+        let profile = self.command_profile();
+        self.issue_command(&[profile.write_enable], &[])
+    }
+
     fn wait_write_complete(&self, max_polls: u32) -> Result<(), SmcError> {
         let mut polls = 0u32;
         while polls < max_polls {
             let sr = self.read_status_impl()?;
-            if (sr & 0x01) == 0 {
+            if (sr & STATUS_WIP) == 0 {
                 return Ok(());
             }
+            poll_delay();
             polls += 1;
         }
         Err(SmcError::Timeout)
@@ -510,10 +526,10 @@
 
         let profile = self.command_profile();
         let width = self.addr_width();
-        self.issue_command(&[profile.write_enable], &[])?;
+        self.write_enable()?;
         let (cmd, len) = encode_addr_cmd(profile.erase_sector_4k, offset, width);
         self.issue_command(&cmd[..len], &[])?;
-        self.wait_write_complete(10_000)
+        self.wait_write_complete(WRITE_COMPLETE_MAX_POLLS)
     }
 
     fn program_page(&mut self, offset: u32, data: &[u8]) -> Result<usize, SmcError> {
@@ -521,10 +537,10 @@
 
         let profile = self.command_profile();
         let width = self.addr_width();
-        self.issue_command(&[profile.write_enable], &[])?;
+        self.write_enable()?;
         let (cmd, len) = encode_addr_cmd(profile.page_program, offset, width);
         self.issue_command(&cmd[..len], data)?;
-        self.wait_write_complete(10_000)?;
+        self.wait_write_complete(WRITE_COMPLETE_MAX_POLLS)?;
         Ok(data.len())
     }
 
diff --git a/target/ast10x0/peripherals/smc/helpers.rs b/target/ast10x0/peripherals/smc/helpers.rs
index 78a889a..27c9d0d 100644
--- a/target/ast10x0/peripherals/smc/helpers.rs
+++ b/target/ast10x0/peripherals/smc/helpers.rs
@@ -164,18 +164,32 @@
     })
 }
 
-/// Encode a memory segment into hardware register format.
+/// Encode an FMC memory segment into hardware register format.
 ///
-/// Hardware uses 512 KB units for addressing.
-pub(crate) fn encode_segment(start: usize, end: usize) -> Result<u32, SmcError> {
-    let start_512k = (start >> 19) as u32;
-    let end_512k = ((end >> 19) - 1) as u32;
-
-    if start_512k > 0xFFFF || end_512k > 0xFFFF {
+/// FMC decode fields use 512 KiB alignment. `end` is exclusive.
+pub(crate) fn encode_fmc_segment(start: usize, end: usize) -> Result<u32, SmcError> {
+    if end == 0 || end <= start {
         return Err(SmcError::InvalidCapacity);
     }
 
-    Ok((end_512k << 16) | start_512k)
+    let start = start as u32;
+    let inclusive_end = (end - 1) as u32;
+    Ok(((((start >> 19) << 19) >> 16) & 0x0ff8)
+        | (((inclusive_end >> 19) << 19) & 0x0ff8_0000))
+}
+
+/// Encode an SPI1/SPI2 memory segment into hardware register format.
+///
+/// SPI decode fields use 1 MiB alignment. `end` is exclusive.
+pub(crate) fn encode_spi_segment(start: usize, end: usize) -> Result<u32, SmcError> {
+    if end == 0 || end <= start {
+        return Err(SmcError::InvalidCapacity);
+    }
+
+    let start = start as u32;
+    let inclusive_end = (end - 1) as u32;
+    Ok(((((start >> 20) << 20) >> 16) & 0xffff)
+        | (((inclusive_end >> 20) << 20) & 0xffff_0000))
 }
 
 /// Calculate AST-compatible SPI clock divider field for CS control registers.
@@ -267,18 +281,18 @@
 
     #[test]
     fn test_encode_segment() {
-        let seg = encode_segment(0, 16 * 1024 * 1024).unwrap();
-        let start_512k = seg & 0xFFFF;
-        let end_512k = (seg >> 16) & 0xFFFF;
+        let seg = encode_fmc_segment(0, 16 * 1024 * 1024).unwrap();
+        let start_512k = ((seg & 0x0ff8) << 16) >> 19;
+        let end_512k = (((seg & 0x0ff8_0000) | 0x0007_ffff) >> 19);
         assert_eq!(start_512k, 0);
         assert_eq!(end_512k, 31);
     }
 
     #[test]
     fn test_encode_segment_8mb_cs0() {
-        let seg = encode_segment(0, 8 * 1024 * 1024).unwrap();
-        let start_512k = seg & 0xFFFF;
-        let end_512k = (seg >> 16) & 0xFFFF;
+        let seg = encode_fmc_segment(0, 8 * 1024 * 1024).unwrap();
+        let start_512k = ((seg & 0x0ff8) << 16) >> 19;
+        let end_512k = (((seg & 0x0ff8_0000) | 0x0007_ffff) >> 19);
         assert_eq!(start_512k, 0);
         assert_eq!(end_512k, 15);
     }
@@ -287,9 +301,9 @@
     fn test_encode_segment_64mb_cs1_after_8mb_cs0() {
         let start = 8 * 1024 * 1024;
         let end = start + 64 * 1024 * 1024;
-        let seg = encode_segment(start, end).unwrap();
-        let start_512k = seg & 0xFFFF;
-        let end_512k = (seg >> 16) & 0xFFFF;
+        let seg = encode_fmc_segment(start, end).unwrap();
+        let start_512k = ((seg & 0x0ff8) << 16) >> 19;
+        let end_512k = (((seg & 0x0ff8_0000) | 0x0007_ffff) >> 19);
         assert_eq!(start_512k, 16);
         assert_eq!(end_512k, 143);
     }
@@ -328,7 +342,7 @@
 
     #[test]
     fn test_segment_overflow() {
-        let result = encode_segment(0, 512 * 1024 * 1024);
+        let result = encode_fmc_segment(0, 512 * 1024 * 1024);
         assert!(result.is_err());
     }
 
diff --git a/target/ast10x0/tests/smc/BUILD.bazel b/target/ast10x0/tests/smc/BUILD.bazel
index 0e61d54..9d9937a 100644
--- a/target/ast10x0/tests/smc/BUILD.bazel
+++ b/target/ast10x0/tests/smc/BUILD.bazel
@@ -6,6 +6,7 @@
     visibility = [
         "//target/ast10x0/tests/smc/dma_irq:__pkg__",
         "//target/ast10x0/tests/smc/read:__pkg__",
+        "//target/ast10x0/tests/smc/write:__pkg__",
     ],
 )
 
@@ -28,3 +29,13 @@
     name = "smc_irq_evb_test",
     actual = "//target/ast10x0/tests/smc/dma_irq:smc_irq_evb_test",
 )
+
+alias(
+    name = "smc_write_test",
+    actual = "//target/ast10x0/tests/smc/write:smc_write_test",
+)
+
+alias(
+    name = "smc_write_evb_test",
+    actual = "//target/ast10x0/tests/smc/write:smc_write_evb_test",
+)
diff --git a/target/ast10x0/tests/smc/write/BUILD.bazel b/target/ast10x0/tests/smc/write/BUILD.bazel
new file mode 100644
index 0000000..9a2bb1a
--- /dev/null
+++ b/target/ast10x0/tests/smc/write/BUILD.bazel
@@ -0,0 +1,79 @@
+# Licensed under the Apache-2.0 license
+# SPDX-License-Identifier: Apache-2.0
+
+load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image", "system_image_test")
+load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen")
+load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script")
+load("@pigweed//pw_kernel/tooling/panic_detector:rust_binary_no_panics_test.bzl", "rust_binary_no_panics_test")
+load("@rules_rust//rust:defs.bzl", "rust_binary")
+load("//target/ast10x0:defs.bzl", "TARGET_COMPATIBLE_WITH")
+
+filegroup(
+    name = "system_config",
+    srcs = ["system.json5"],
+)
+
+target_codegen(
+    name = "codegen",
+    arch = "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m",
+    system_config = ":system_config",
+    target_compatible_with = TARGET_COMPATIBLE_WITH,
+)
+
+target_linker_script(
+    name = "linker_script",
+    system_config = ":system_config",
+    tags = ["kernel"],
+    target_compatible_with = TARGET_COMPATIBLE_WITH,
+    template = "//target/ast10x0:linker_script_template",
+)
+
+rust_binary(
+    name = "target",
+    srcs = [
+        "target.rs",
+        "//target/ast10x0/tests/smc:target_debug.rs",
+    ],
+    edition = "2024",
+    tags = ["kernel"],
+    target_compatible_with = TARGET_COMPATIBLE_WITH,
+    deps = [
+        ":codegen",
+        ":linker_script",
+        "//target/ast10x0:entry",
+        "//target/ast10x0/peripherals",
+        "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m",
+        "@pigweed//pw_kernel/kernel",
+        "@pigweed//pw_kernel/subsys/console:console_backend",
+        "@pigweed//pw_kernel/target:target_common",
+        "@pigweed//pw_log/rust:pw_log",
+    ],
+)
+
+system_image(
+    name = "smc_write_test",
+    kernel = ":target",
+    platform = "//target/ast10x0",
+    system_config = ":system_config",
+    tags = ["kernel"],
+    target_compatible_with = TARGET_COMPATIBLE_WITH,
+    userspace = False,
+    visibility = ["//visibility:public"],
+)
+
+system_image_test(
+    name = "smc_write_evb_test",
+    image = ":smc_write_test",
+    tags = ["hardware"],
+    target_compatible_with = select({
+        "//target/ast10x0:qemu_enabled": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    }),
+    visibility = ["//visibility:public"],
+)
+
+rust_binary_no_panics_test(
+    name = "no_panics_test",
+    binary = ":smc_write_test",
+    tags = ["kernel"],
+)
diff --git a/target/ast10x0/tests/smc/write/system.json5 b/target/ast10x0/tests/smc/write/system.json5
new file mode 100644
index 0000000..6c3a1a6
--- /dev/null
+++ b/target/ast10x0/tests/smc/write/system.json5
@@ -0,0 +1,17 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+// AST10x0 kernel-only SMC FMC CS1 erase/write/read verify test configuration.
+{
+    arch: {
+        type: "armv7m",
+        vector_table_start_address: 0x00000000,
+        vector_table_size_bytes: 1280,
+    },
+    kernel: {
+        flash_start_address: 0x00000500,
+        flash_size_bytes: 262144,
+        ram_start_address: 0x00040500,
+        ram_size_bytes: 393216,
+    },
+}
diff --git a/target/ast10x0/tests/smc/write/target.rs b/target/ast10x0/tests/smc/write/target.rs
new file mode 100644
index 0000000..ef5eb1d
--- /dev/null
+++ b/target/ast10x0/tests/smc/write/target.rs
@@ -0,0 +1,145 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+//! AST10x0 SMC FMC CS1 erase/write/read verify test target.
+
+#![no_std]
+#![no_main]
+
+#[allow(unused_imports)]
+use ast10x0_peripherals::scu::pinctrl::PINCTRL_FMC_QUAD;
+use ast10x0_peripherals::scu::ScuRegisters;
+use ast10x0_peripherals::smc::{
+    ChipSelect, FlashConfig, FmcUninit, SmcConfig, SmcController, SmcError, SmcTopology,
+    SpiNorFlash, SpiNorFlashDevice,
+};
+use console_backend::console_backend_write_all;
+use target_common::{declare_target, TargetInterface};
+use {console_backend as _, entry as _};
+
+#[path = "../target_debug.rs"]
+mod target_debug;
+use target_debug::{dump_smc_read, dump_smc_register};
+
+const CS0_CONFIG: FlashConfig = FlashConfig {
+    capacity_mb: 8,
+    page_size: 256,
+    sector_size: 4096,
+    block_size: 65536,
+    spi_clock_mhz: 50,
+};
+
+const CS1_CONFIG: FlashConfig = FlashConfig {
+    capacity_mb: 64,
+    page_size: 256,
+    sector_size: 4096,
+    block_size: 65536,
+    spi_clock_mhz: 50,
+};
+
+const TEST_OFFSET: u32 = 0x10_0000;
+const TEST_LEN: usize = 256;
+
+pub struct Target {}
+
+fn fill_test_pattern(out: &mut [u8; TEST_LEN]) {
+    let mut i = 0usize;
+    while i < out.len() {
+        out[i] = (i as u8).wrapping_mul(17).wrapping_add(0x5a);
+        i += 1;
+    }
+}
+
+fn expect_erased(buf: &[u8]) -> Result<(), SmcError> {
+    for &byte in buf {
+        if byte != 0xff {
+            return Err(SmcError::HardwareError);
+        }
+    }
+    Ok(())
+}
+
+fn run_smc_fmc_cs1_write_test() -> Result<(), SmcError> {
+    let scu = unsafe { ScuRegisters::new_global_unlocked() };
+    scu.apply_pinctrl_group(PINCTRL_FMC_QUAD);
+
+    let config = SmcConfig {
+        controller_id: SmcController::Fmc,
+        cs0: Some(CS0_CONFIG),
+        cs1: Some(CS1_CONFIG),
+        dma_enabled: true,
+        enable_interrupts: false,
+        topology: SmcTopology::BootSpi { master_idx: 0 },
+    };
+
+    pw_log::info!("=== AST10x0 SMC FMC CS1 write test ===");
+    let fmc = unsafe { FmcUninit::new(config)? };
+    let mut fmc = fmc.init()?;
+    fmc.spi_nor_read_init(ChipSelect::Cs1)?;
+
+    if !fmc.is_ready() {
+        return Err(SmcError::HardwareError);
+    }
+
+    let mut flash = SpiNorFlash::from_fmc_cs(&mut fmc, CS1_CONFIG, ChipSelect::Cs1)?;
+    let jedec = flash.jedec_id()?;
+    pw_log::info!(
+        "CS1 JEDEC ID: {:02x} {:02x} {:02x}",
+        jedec[0] as u32,
+        jedec[1] as u32,
+        jedec[2] as u32
+    );
+
+    pw_log::info!("=== erase CS1 sector ===");
+    flash.erase_sector(TEST_OFFSET)?;
+
+    let mut read_buf = [0u8; TEST_LEN];
+    let n = flash.read(TEST_OFFSET, &mut read_buf)?;
+    if n != TEST_LEN {
+        return Err(SmcError::HardwareError);
+    }
+    expect_erased(&read_buf)?;
+    dump_smc_read(&read_buf, TEST_LEN as u32);
+
+    pw_log::info!("=== program CS1 page ===");
+    let mut pattern = [0u8; TEST_LEN];
+    fill_test_pattern(&mut pattern);
+    let written = flash.program_page(TEST_OFFSET, &pattern)?;
+    if written != TEST_LEN {
+        return Err(SmcError::HardwareError);
+    }
+
+    pw_log::info!("=== read CS1 page ===");
+    read_buf.fill(0);
+    let n = flash.read(TEST_OFFSET, &mut read_buf)?;
+    if n != TEST_LEN || read_buf != pattern {
+        return Err(SmcError::HardwareError);
+    }
+    dump_smc_read(&read_buf, TEST_LEN as u32);
+
+    pw_log::info!("=== verify CS1 page ===");
+    if !flash.verify(TEST_OFFSET, &pattern)? {
+        return Err(SmcError::HardwareError);
+    }
+
+    dump_smc_register(0x7E62_0000, 16);
+    Ok(())
+}
+
+impl TargetInterface for Target {
+    const NAME: &'static str = "AST10x0 SMC FMC CS1 write Test";
+
+    fn main() -> ! {
+        let sentinel = if run_smc_fmc_cs1_write_test().is_ok() {
+            b"TEST_RESULT:PASS\n"
+        } else {
+            b"TEST_RESULT:FAIL\n"
+        };
+        let _ = console_backend_write_all(sentinel);
+
+        #[expect(clippy::empty_loop)]
+        loop {}
+    }
+}
+
+declare_target!(Target);