Add board crate to ast10x0 target
diff --git a/target/ast10x0/board/BUILD.bazel b/target/ast10x0/board/BUILD.bazel
new file mode 100644
index 0000000..15a420c
--- /dev/null
+++ b/target/ast10x0/board/BUILD.bazel
@@ -0,0 +1,19 @@
+# Licensed under the Apache-2.0 license
+# SPDX-License-Identifier: Apache-2.0
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+load("//target/ast10x0:defs.bzl", "TARGET_COMPATIBLE_WITH")
+
+rust_library(
+    name = "ast10x0_board",
+    srcs = [
+        "src/lib.rs",
+    ],
+    crate_name = "ast10x0_board",
+    edition = "2024",
+    target_compatible_with = TARGET_COMPATIBLE_WITH,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//target/ast10x0/peripherals",
+    ],
+)
diff --git a/target/ast10x0/board/README.md b/target/ast10x0/board/README.md
new file mode 100644
index 0000000..a606897
--- /dev/null
+++ b/target/ast10x0/board/README.md
@@ -0,0 +1,14 @@
+# ast10x0_board
+
+Board-level integration crate for AST10x0 platforms.
+
+This is the empty scaffold. The crate exists so that board-specific
+descriptors, monitor wiring, and SPIM routing helpers have a stable
+home as they get added on top of the SCU + SPIMONITOR primitives in
+`peripherals`.
+
+## Build
+
+```
+bazelisk build --config=virt_ast10x0 //target/ast10x0/board:ast10x0_board
+```
diff --git a/target/ast10x0/board/src/lib.rs b/target/ast10x0/board/src/lib.rs
new file mode 100644
index 0000000..b921388
--- /dev/null
+++ b/target/ast10x0/board/src/lib.rs
@@ -0,0 +1,88 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+#![no_std]
+
+#![deny(
+    clippy::unwrap_used,
+    clippy::expect_used,
+    clippy::panic,
+    clippy::unreachable,
+    clippy::todo,
+    clippy::unimplemented
+)]
+
+use ast10x0_peripherals::scu::{PinctrlPin, ScuRegisters};
+use ast10x0_peripherals::scu::{ClockRegisterHalf, ScuRegisterHalf};
+
+/// Board descriptor metadata for AST10x0 board initialization.
+#[derive(Clone, Debug)]
+pub struct Ast10x0BoardDescriptor {
+    /// Pin control groups to apply during board init.
+    /// Applied in order via `ScuRegisters::apply_pinctrl_group()`.
+    pub pinctrl_groups: &'static [&'static [PinctrlPin]],
+}
+
+/// Runtime board object that executes hardware initialization steps.
+pub struct Ast10x0Board {
+    descriptor: Ast10x0BoardDescriptor,
+}
+
+impl Ast10x0Board {
+    /// Create a board runtime object from board metadata.
+    #[must_use]
+    pub const fn new(descriptor: Ast10x0BoardDescriptor) -> Self {
+        Self { descriptor }
+    }
+
+    /// Initialize board: apply pinctrl groups and initialize I2C subsystem.
+    ///
+    /// This performs the complete platform-level initialization:
+    /// 1. Apply pinctrl groups
+    /// 2. Enable I2C clock via SCU
+    /// 3. Assert I2C/SMBus controller reset
+    /// 4. Delay for reset to settle
+    /// 5. Deassert reset
+    /// 6. Delay for recovery
+    /// 7. Configure I2C global registers (clock dividers, etc.)
+    ///
+    /// # Safety
+    /// - Must be called only once during board initialization.
+    /// - Not thread-safe; caller must ensure no concurrent SCU or I2C accesses.
+    pub unsafe fn init(&self) {
+        // Unlock SCU once before the sequence of writes (aspeed-rust pattern)
+        let scu = unsafe { ScuRegisters::new_global_unlocked() };
+
+        // Apply pinctrl groups
+        for group in self.descriptor.pinctrl_groups {
+            scu.apply_pinctrl_group(group);
+        }
+
+        // Enable I2C clock (Group 0, bit 2)
+        scu.ungate_clock_mask(ClockRegisterHalf::Lower, 1 << 2);
+
+        // Assert I2C reset (Upper half, bit 2)
+        scu.assert_reset_mask(ScuRegisterHalf::Upper, 1 << 2);
+        delay_us(1000);
+
+        // Deassert I2C reset
+        scu.deassert_reset_mask(ScuRegisterHalf::Upper, 1 << 2);
+        delay_us(1000);
+
+        // Configure I2C global registers (clock dividers, etc.)
+        unsafe { ast10x0_peripherals::i2c::init_i2c_global() };
+    }
+}
+
+/// Simple busy-wait delay in microseconds.
+///
+/// This is a placeholder; production code should use a proper timer or delay provider.
+/// Spins for approximately `micros` microseconds.
+#[inline]
+fn delay_us(micros: u32) {
+    // Very rough approximation: ~16 cycles per microsecond on Cortex-M4 @ ~50MHz
+    // This is calibration-free but inaccurate; improve for production.
+    for _ in 0..(micros * 16) {
+        core::hint::spin_loop();
+    }
+}
\ No newline at end of file