diff --git a/Cargo.lock b/Cargo.lock index 6b6d361..27f394b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,11 +63,14 @@ name = "aspeed-ddk" version = "0.1.0" dependencies = [ "ast1060-pac", + "base64ct", "cortex-m", "cortex-m-rt", + "cortex-m-semihosting", "embedded-hal 1.0.0", "embedded-hal 1.0.0-alpha.1", "embedded-io", + "embedded-storage", "fugit", "heapless", "hex-literal", @@ -107,6 +110,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + [[package]] name = "bitfield" version = "0.13.2" @@ -198,6 +207,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -365,7 +383,7 @@ dependencies = [ [[package]] name = "proposed-traits" version = "0.1.0" -source = "git+https://github.com/rusty1968/proposed_traits.git?rev=85641310df5a5276c67f81621b104322cff0286c#85641310df5a5276c67f81621b104322cff0286c" +source = "git+https://github.com/stevenlee7189/proposed_traits.git?rev=496dba90a91aab8e4ae88bbaaa0e162df84ebe74#496dba90a91aab8e4ae88bbaaa0e162df84ebe74" dependencies = [ "async-trait", "embedded-hal 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index ffb99d7..e320b16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ resolver = "2" [workspace.package] edition = "2021" rust-version = "1.83.0" - [package] name = "aspeed-ddk" version = "0.1.0" @@ -32,7 +31,9 @@ embedded-hal = { version = "1.0.0" } embedded-hal-old = { git = "https://github.com/rust-embedded/embedded-hal.git", rev = "599d44fdc7e709cb9ae6580ec11c0b7f7f102", package = "embedded-hal" } embedded-io = "0.6.1" fugit = "0.3.7" -proposed-traits = { git = "https://github.com/rusty1968/proposed_traits.git", package = "proposed-traits", rev = "85641310df5a5276c67f81621b104322cff0286c" } +base64ct = "<1.8.0" +proposed-traits = { git = "https://github.com/stevenlee7189/proposed_traits.git", package = "proposed-traits", rev = "496dba90a91aab8e4ae88bbaaa0e162df84ebe74" } +embedded-storage = "0.3" hex-literal = "0.4" heapless = "0.8.0" nb = "1.1.0" @@ -40,5 +41,11 @@ paste = "1.0" cortex-m = { version = "0.7.5" } cortex-m-rt = { version = "0.6.5", features = ["device"] } +cortex-m-semihosting = "0.5" panic-halt = "1.0.0" +[profile.release] +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations + diff --git a/link.x b/link.x index 7f82b12..2832570 100644 --- a/link.x +++ b/link.x @@ -54,7 +54,7 @@ EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ /* # Pre-initialization function */ /* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, then the function this points to will be called before the RAM is initialized. */ -PROVIDE(__pre_init = DefaultPreInit); +/* PROVIDE(__pre_init = DefaultPreInit); */ /* # Sections */ SECTIONS diff --git a/src/i2c/ast1060_i2c.rs b/src/i2c/ast1060_i2c.rs index e007a8e..d5c2675 100644 --- a/src/i2c/ast1060_i2c.rs +++ b/src/i2c/ast1060_i2c.rs @@ -14,6 +14,8 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::delay::DelayNs; use embedded_hal::i2c::{NoAcknowledgeSource, Operation, SevenBitAddress}; use proposed_traits::i2c_target::I2CTarget; +#[cfg(feature = "i2c_target")] +use proposed_traits::i2c_target::TransactionDirection; static I2CGLOBAL_INIT: AtomicBool = AtomicBool::new(false); @@ -1348,7 +1350,14 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveRdReq { i2c_debug!(self.logger, "read_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + if let Ok(Some(val)) = + target.on_transaction_start(TransactionDirection::Read, false) + { + if let Some(slice) = self.sdma_buf.as_mut_slice(0, 1).get_mut(0) { + *slice = val; + i2c_debug!(self.logger, "read_requested val {:#x}", val); + } + } } } else if event == I2cSEvent::SlaveRdProc { i2c_debug!(self.logger, "read_processed"); @@ -1390,7 +1399,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> //if slave is ready to receive i2c_debug!(self.logger, "write_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + let _ = target.on_transaction_start(TransactionDirection::Write, false); } } else if event == I2cSEvent::SlaveWrRecvd { //Another I2C master has sent a byte to us which needs to be set in ‘val’ @@ -1440,7 +1449,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveWrReq { i2c_debug!(self.logger, "byte write_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + let _ = target.on_transaction_start(TransactionDirection::Write, false); } } else if event == I2cSEvent::SlaveWrRecvd { i2c_debug!(self.logger, "byte write_received"); @@ -1454,7 +1463,18 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveRdReq { i2c_debug!(self.logger, "byte read_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + match target.on_transaction_start(TransactionDirection::Read, false) { + Ok(Some(v)) => { + *val = v; + } + Ok(None) => { + *val = 0; + } + Err(e) => { + i2c_debug!(self.logger, "Failed on read_requested: {:?}", e); + *val = 0; + } + } } } else if event == I2cSEvent::SlaveRdProc { i2c_debug!(self.logger, "byte read_processed"); @@ -1660,6 +1680,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> cmd = SLAVE_TRIGGER_CMD; match self.xfer_mode { I2cXferMode::DmaMode => { + self.i2c.i2cs4c().write(|w| unsafe { w.bits(0) }); self.i2c.i2cs2c().modify(|_, w| unsafe { w.dmarx_buf_len_byte() .bits(u16::try_from(I2C_SLAVE_BUF_SIZE - 1).unwrap()) diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index eadc1f0..a5b9807 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -3,3 +3,5 @@ pub mod ast1060_i2c; pub mod common; pub mod i2c_controller; +pub mod pfr; +pub mod target; diff --git a/src/i2c/pfr/mod.rs b/src/i2c/pfr/mod.rs new file mode 100644 index 0000000..afd4aac --- /dev/null +++ b/src/i2c/pfr/mod.rs @@ -0,0 +1,4 @@ +// Licensed under the Apache-2.0 license + +#[cfg(feature = "i2c_target")] +pub mod swmbx; diff --git a/src/i2c/pfr/swmbx.rs b/src/i2c/pfr/swmbx.rs new file mode 100644 index 0000000..b2b3c3a --- /dev/null +++ b/src/i2c/pfr/swmbx.rs @@ -0,0 +1,468 @@ +// Licensed under the Apache-2.0 license + +use crate::uart::UartController; +use core::cell::Cell; +use core::mem::MaybeUninit; +use core::ptr::{read_volatile, write_volatile}; +use core::sync::atomic::{AtomicBool, Ordering}; +use embedded_io::Write; +use heapless::spsc::Queue; + +// Constants +pub const SWMBX_DEV_COUNT: usize = 2; +pub const SWMBX_NODE_COUNT: usize = 256; +pub const SWMBX_FIFO_COUNT: usize = 4; +pub const SWMBX_BUF_BASE: usize = 0x7e7b_0e00; +pub const SWMBX_BUF_SIZE: usize = 256; +pub const SWMBX_FIFO_DEPTH: usize = 256; +pub const SWMBX_INFO_BASE: usize = 0x7e7b_0f00; + +// Behavior flags +pub const SWMBX_PROTECT: u8 = 1 << 0; +pub const SWMBX_NOTIFY: u8 = 1 << 1; +pub const SWMBX_FIFO: u8 = 1 << 2; +pub const FLAG_MASK: u8 = SWMBX_PROTECT | SWMBX_NOTIFY | SWMBX_FIFO; + +// FIFO notify flags +pub const SWMBX_FIFO_NOTIFY_START: u8 = 1 << 0; +pub const SWMBX_FIFO_NOTIFY_STOP: u8 = 1 << 1; +pub const FIFO_NOTIFY_MASK: u8 = SWMBX_FIFO_NOTIFY_START | SWMBX_FIFO_NOTIFY_STOP; + +pub static mut SWMBX_CTRL: MaybeUninit = MaybeUninit::uninit(); +static INIT_DONE: AtomicBool = AtomicBool::new(false); + +extern "Rust" { + static mut UART_PTR: Option<&'static mut UartController<'static>>; +} + +#[macro_export] +macro_rules! swmbx_log { + ($($arg:tt)*) => {{ + use core::fmt::Write; + if let Some(uart) = unsafe { UART_PTR.as_mut() } { + let mut buf: heapless::String<64> = heapless::String::new(); + let _ = write!(buf, $($arg)*); + let _ = uart.write_all(b"[SWMBX] "); + let _ = uart.write_all(buf.as_bytes()); + let _ = uart.write_all(b"\r\n"); + } + }}; +} + +// --- FIFO Entry --- +#[derive(Copy, Clone, Debug)] +pub struct SwmbxFifoEntry { + pub value: u8, +} + +// --- Node entry per device+addr --- +#[derive(Copy, Clone, Debug, Default)] +pub struct SwmbxNode { + pub notify_flag: bool, + pub enabled_flags: u8, // uses SWMBX_PROTECT / SWMBX_NOTIFY bitmask +} + +// --- FIFO buffer per index --- +pub struct SwmbxFifo { + pub queue: Queue, + pub notify_flag: u8, // FIFO_NOTIFY_START/STOP + pub notify_start: bool, + pub fifo_write: bool, + pub fifo_offset: u8, + pub enabled: bool, + pub msg_index: usize, + pub max_msg_count: u8, +} + +impl SwmbxFifo { + pub const fn new() -> Self { + Self { + queue: Queue::new(), + notify_flag: 0, + notify_start: false, + fifo_write: false, + fifo_offset: 0, + enabled: false, + msg_index: 0, + max_msg_count: SWMBX_FIFO_DEPTH as u8, + } + } + + pub fn append_write(&mut self, value: u8) -> Result<(), ()> { + if self.queue.len() < self.max_msg_count as usize { + self.queue + .enqueue(SwmbxFifoEntry { value }) + .map_err(|_| ())?; + if self.msg_index == (self.max_msg_count - 1) as usize { + self.msg_index = 0; + } else { + self.msg_index = (self.msg_index + 1) % self.max_msg_count as usize; + } + swmbx_log!( + "append_write: value {:#x} added to FIFO, current index: {}", + value, + self.msg_index + ); + Ok(()) + } else { + swmbx_log!("append_write: FIFO full, cannot add value {:#x}", value); + Err(()) + } + } + + pub fn peek_read(&mut self) -> Result { + if let Some(entry) = self.queue.dequeue() { + swmbx_log!("peek_read: value {:#x} read from FIFO", entry.value,); + Ok(entry.value) + } else { + Err(()) + } + } + pub fn flush(&mut self) { + while self.queue.dequeue().is_some() {} + self.msg_index = 0; + } +} + +// --- Main SWMBX controller data --- +pub struct SwmbxCtrl { + pub mbx_en: Cell, + + pub node: [[SwmbxNode; SWMBX_NODE_COUNT]; SWMBX_DEV_COUNT], + pub fifo: [SwmbxFifo; SWMBX_FIFO_COUNT], + + pub mbx_fifo_execute: [bool; SWMBX_DEV_COUNT], + pub mbx_fifo_addr: [u8; SWMBX_DEV_COUNT], + pub mbx_fifo_idx: [u8; SWMBX_DEV_COUNT], + pub buffer_size: usize, +} + +impl SwmbxCtrl { + pub fn init(buffer_size: usize) -> &'static mut SwmbxCtrl { + if INIT_DONE.load(Ordering::SeqCst) { + return unsafe { SWMBX_CTRL.assume_init_mut() }; + } + + let ctrl = SwmbxCtrl { + mbx_en: Cell::new(0), + node: [[SwmbxNode::default(); SWMBX_NODE_COUNT]; SWMBX_DEV_COUNT], + fifo: [ + SwmbxFifo::new(), + SwmbxFifo::new(), + SwmbxFifo::new(), + SwmbxFifo::new(), + ], + mbx_fifo_execute: [false; SWMBX_DEV_COUNT], + mbx_fifo_addr: [0; SWMBX_DEV_COUNT], + mbx_fifo_idx: [0; SWMBX_DEV_COUNT], + buffer_size, + }; + + let ctrl_ref = unsafe { + SWMBX_CTRL.write(ctrl); + SWMBX_CTRL.assume_init_mut() + }; + + INIT_DONE.store(true, Ordering::SeqCst); + ctrl_ref + } + + pub fn store_ctrl_ptr(ctrl: &'static Self) { + unsafe { + let ptr = SWMBX_INFO_BASE as *mut *const SwmbxCtrl; + ptr.write_volatile(ctrl as *const _); + } + } + + pub fn load_ctrl_ptr() -> &'static Self { + unsafe { + let ptr = SWMBX_INFO_BASE as *const *const SwmbxCtrl; + &**ptr + } + } + + pub fn load_ctrl_ptr_mut() -> &'static mut Self { + unsafe { + let ptr = SWMBX_INFO_BASE as *const *mut SwmbxCtrl; + &mut **ptr + } + } + + pub fn update_notify(&mut self, port: usize, addr: u8, enable: bool) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT { + return Err(()); + } + + let node = &mut self.node[port][addr as usize]; + + if enable { + node.enabled_flags |= SWMBX_NOTIFY; + } else { + node.enabled_flags &= !SWMBX_NOTIFY; + } + + Ok(()) + } + pub fn update_fifo( + &mut self, + index: usize, + addr: u8, + depth: u8, + notify: u8, + enable: bool, + ) -> Result<(), ()> { + if index >= SWMBX_FIFO_COUNT { + return Err(()); + } + + let fifo = &mut self.fifo[index]; + fifo.enabled = enable; + + if enable { + fifo.fifo_offset = addr; + fifo.max_msg_count = depth; + fifo.notify_flag = notify; + fifo.msg_index = 0; + fifo.queue = Queue::new(); + } else { + fifo.queue = Queue::new(); + } + + Ok(()) + } + + pub fn flush_fifo(&mut self, index: usize) -> Result<(), ()> { + if index >= SWMBX_FIFO_COUNT { + return Err(()); + } + + self.fifo[index].flush(); + Ok(()) + } + pub fn enable_behavior(&self, flag: u8, enable: bool) -> Result<(), ()> { + if (flag & FLAG_MASK) == 0 { + return Err(()); + } + + let old = self.mbx_en.get(); + if enable { + self.mbx_en.set(old | flag); + } else { + self.mbx_en.set(old & !flag); + } + swmbx_log!("enable_behavior: {:#x} -> {:#x}", old, self.mbx_en.get()); + + Ok(()) + } + pub fn update_protect(&mut self, port: usize, addr: u8, enable: bool) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT { + return Err(()); + } + + let node = &mut self.node[port][addr as usize]; + if enable { + node.enabled_flags |= SWMBX_PROTECT; + } else { + node.enabled_flags &= !SWMBX_PROTECT; + } + + Ok(()) + } + + pub fn apply_protect( + &mut self, + port: usize, + bitmap: &[u32], + start_idx: usize, + ) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT || start_idx + bitmap.len() > 8 { + return Err(()); + } + + for (i, &val) in bitmap.iter().enumerate() { + let base = (start_idx + i) << 5; + for bit in 0..32 { + let addr = base + bit; + if addr >= SWMBX_NODE_COUNT { + break; + } + let node = &mut self.node[port][addr]; + if (val >> bit) & 1 != 0 { + node.enabled_flags |= SWMBX_PROTECT; + } else { + node.enabled_flags &= !SWMBX_PROTECT; + } + } + } + Ok(()) + } + + pub fn get_msg(&mut self, port: usize, addr: u8) -> u8 { + if (port as usize) >= SWMBX_DEV_COUNT { + swmbx_log!("get_msg invalid port {}", port); + return 0; + } + + if self.mbx_fifo_execute[port] && (self.mbx_en.get() & SWMBX_FIFO) != 0 { + let fifo_index = self.mbx_fifo_idx[port as usize] as usize; + + match self.fifo[fifo_index].peek_read() { + Ok(val) => { + return val; + } + Err(_) => { + // In C: give semaphore here — not implemented in Rust + swmbx_log!("FIFO empty at index {} (port {})", fifo_index, port); + return 0; + } + } + } + + let val = SwmbxBuffer::read(addr); + swmbx_log!("get_msg port: {}, addr: {:#x}, val: {:#x}", port, addr, val); + val + } + + pub fn send_msg(&mut self, port: usize, addr: u8, val: u8) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!( + "send_msg port: {}, addr: {:#x}, val: {:#x}", + port, + addr, + val + ); + + let mut write_to_buffer = false; + + if self.mbx_fifo_execute[port] && (self.mbx_en.get() & SWMBX_FIFO) != 0 { + let fifo_addr = self.mbx_fifo_addr[port]; + let fifo_index = self.mbx_fifo_idx[port] as usize; + + if self.fifo[fifo_index].append_write(val).is_err() { + self.node[port][addr as usize].notify_flag = true; + return; + } + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 + && (self.fifo[fifo_index].notify_flag & SWMBX_FIFO_NOTIFY_START) != 0 + && !self.fifo[fifo_index].notify_start + && (self.node[port][fifo_addr as usize].enabled_flags & SWMBX_NOTIFY) != 0 + { + self.node[port][fifo_addr as usize].notify_flag = true; + self.fifo[fifo_index].notify_start = true; + } + + if !self.fifo[fifo_index].fifo_write { + self.fifo[fifo_index].fifo_write = true; + } + } else { + let node = &mut self.node[port][addr as usize]; + + if (node.enabled_flags & SWMBX_PROTECT) == 0 || (self.mbx_en.get() & SWMBX_PROTECT) == 0 + { + write_to_buffer = true; + } + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 && (node.enabled_flags & SWMBX_NOTIFY) != 0 { + node.notify_flag = true; + } + } + + if write_to_buffer { + SwmbxBuffer::write(addr, val); + } + } + pub fn send_start(&mut self, port: usize, addr: u8) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!("send_start port: {}, addr: {:#x}", port, addr); + swmbx_log!("mbx_en: {:#x}", self.mbx_en.get()); + if let Some(fifo_index) = self.check_fifo(addr) { + self.mbx_fifo_execute[port] = true; + self.mbx_fifo_addr[port] = addr; + self.mbx_fifo_idx[port] = fifo_index as u8; + } else { + self.mbx_fifo_execute[port] = false; + } + } + + pub fn check_fifo(&self, addr: u8) -> Option { + self.fifo + .iter() + .position(|f| f.fifo_offset == addr && f.enabled) + } + + pub fn send_stop(&mut self, port: usize) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!("send_stop port: {}", port); + if self.mbx_fifo_execute[port] { + let fifo_addr = self.mbx_fifo_addr[port]; + let fifo_index = self.mbx_fifo_idx[port] as usize; + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 + && (self.fifo[fifo_index].notify_flag & SWMBX_FIFO_NOTIFY_STOP) != 0 + && self.fifo[fifo_index].fifo_write + { + if (self.node[port][fifo_addr as usize].enabled_flags & SWMBX_NOTIFY) != 0 { + self.node[port][fifo_addr as usize].notify_flag = true; + } + } + + self.fifo[fifo_index].notify_start = false; + self.fifo[fifo_index].fifo_write = false; + self.mbx_fifo_execute[port] = false; + self.mbx_fifo_addr[port] = 0; + self.mbx_fifo_idx[port] = 0; + } + } + pub fn swmbx_write(&mut self, fifo: bool, addr: u8, val: u8) -> Result<(), ()> { + if fifo { + if let Some(index) = self.check_fifo(addr) { + self.fifo[index].append_write(val)?; + Ok(()) + } else { + Err(()) + } + } else { + SwmbxBuffer::write(addr, val); + Ok(()) + } + } + + pub fn swmbx_read(&mut self, fifo: bool, addr: u8) -> Result { + if fifo { + if let Some(index) = self.check_fifo(addr) { + self.fifo[index].peek_read() + } else { + Err(()) + } + } else { + Ok(SwmbxBuffer::read(addr)) + } + } +} + +// --- SWMBX buffer abstraction --- +pub struct SwmbxBuffer; + +impl SwmbxBuffer { + #[inline] + pub fn read(addr: u8) -> u8 { + assert!((addr as usize) < SWMBX_BUF_SIZE); + unsafe { read_volatile((SWMBX_BUF_BASE + addr as usize) as *const u8) } + } + + #[inline] + pub fn write(addr: u8, val: u8) { + assert!((addr as usize) < SWMBX_BUF_SIZE); + unsafe { write_volatile((SWMBX_BUF_BASE + addr as usize) as *mut u8, val) }; + } +} diff --git a/src/i2c/target/mod.rs b/src/i2c/target/mod.rs new file mode 100644 index 0000000..4c71e08 --- /dev/null +++ b/src/i2c/target/mod.rs @@ -0,0 +1,4 @@ +// Licensed under the Apache-2.0 license + +#[cfg(feature = "i2c_target")] +pub mod swmbx_target; diff --git a/src/i2c/target/swmbx_target.rs b/src/i2c/target/swmbx_target.rs new file mode 100644 index 0000000..630c288 --- /dev/null +++ b/src/i2c/target/swmbx_target.rs @@ -0,0 +1,191 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{NoOpLogger, UartLogger}; +use crate::i2c::ast1060_i2c::Ast1060I2c; +use crate::i2c::common::{I2cConfigBuilder, I2cSpeed, I2cXferMode}; +use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; +use crate::i2c::pfr::swmbx::SwmbxCtrl; +use crate::pinctrl; +use crate::uart::UartController; +use embedded_hal::i2c::ErrorKind; +use embedded_io::Write; +use proposed_traits::i2c_target::{ + I2CCoreTarget, ReadTarget, RegisterAccess, TransactionDirection, WriteReadTarget, WriteTarget, +}; + +extern "Rust" { + static mut UART_PTR: Option<&'static mut UartController<'static>>; +} + +#[macro_export] +macro_rules! swmbx_target_log { + ($($arg:tt)*) => {{ + use core::fmt::Write; + if let Some(uart) = unsafe { UART_PTR.as_mut() } { + let mut buf: heapless::String<64> = heapless::String::new(); + let _ = write!(buf, $($arg)*); + let _ = uart.write_all(b"[SWMBX_TARGET] "); + let _ = uart.write_all(buf.as_bytes()); + let _ = uart.write_all(b"\r\n"); + } + }}; +} + +#[derive(Debug)] +pub enum SwmbxI2CError { + OtherError, +} + +impl embedded_hal::i2c::Error for SwmbxI2CError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} + +impl embedded_hal::i2c::ErrorType for SwmbxI2CTarget { + type Error = SwmbxI2CError; +} + +pub struct SwmbxI2CTarget { + address: u8, + read_idx: usize, + buffer_idx: u8, + first_write: bool, + port: usize, +} + +impl I2CCoreTarget for SwmbxI2CTarget { + fn init(&mut self, address: u8) -> Result<(), Self::Error> { + if address == 0 { + return Err(SwmbxI2CError::OtherError); + } + self.address = address; + Ok(()) + } + // read_requested or write_requested + fn on_transaction_start( + &mut self, + direction: TransactionDirection, + _repeated: bool, + ) -> Result, Self::Error> { + self.read_idx = 0; + + match direction { + TransactionDirection::Write => { + swmbx_target_log!("Write requested on port {}", self.port); + self.first_write = true; + Ok(None) + } + TransactionDirection::Read => { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + let val = ctrl.get_msg(self.port, self.buffer_idx); + swmbx_target_log!("Read requested on port {}: val: {}", self.port, val); + return Ok(Some(val)); + } + } + } + fn on_stop(&mut self) { + swmbx_target_log!("Stop on port {}", self.port); + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_stop(self.port); + self.first_write = true; + } + fn on_address_match(&mut self, address: u8) -> bool { + self.address == address + } +} + +impl ReadTarget for SwmbxI2CTarget { + // read_processed + fn on_read(&mut self, buffer: &mut [u8]) -> Result { + buffer[0] = 0; + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + let mut idx = self.buffer_idx as usize; + idx %= ctrl.buffer_size; // Ensure idx is within bounds + self.buffer_idx = idx as u8; + let _val = ctrl.get_msg(self.port, self.buffer_idx); + swmbx_target_log!("Read processed on port {}: val: {}", self.port, _val); + Ok(1) + } +} + +impl WriteTarget for SwmbxI2CTarget { + // write_received + fn on_write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + if data.len() == 0 { + // ignore empty writes + swmbx_target_log!("Empty write received on port {}", self.port); + return Ok(()); + } + + swmbx_target_log!("Write received on port {}: data: {:?}", self.port, data); + if self.first_write { + self.buffer_idx = data[0]; + self.first_write = false; + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_start(self.port, self.buffer_idx) + } else { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_msg(self.port, self.buffer_idx, data[0]); + let mut idx = self.buffer_idx as usize; + idx += 1; + idx %= ctrl.buffer_size; // Ensure idx is within bounds + self.buffer_idx = idx as u8; + } + + Ok(()) + } +} + +impl WriteReadTarget for SwmbxI2CTarget {} + +impl RegisterAccess for SwmbxI2CTarget { + fn write_register(&mut self, _address: u8, _data: u8) -> Result<(), Self::Error> { + swmbx_target_log!("Write register called on port {}", self.port); + Ok(()) + } + fn read_register(&mut self, _address: u8, _buffer: &mut [u8]) -> Result { + swmbx_target_log!("Read register called on port {}", self.port); + Ok(1) + } +} + +#[cfg(feature = "i2c_target")] +impl SwmbxI2CTarget { + pub fn new(port: usize, address: u8) -> Result { + if address == 0 { + return Err(SwmbxI2CError::OtherError); + } + Ok(Self { + address, + read_idx: 0, + buffer_idx: 0, + first_write: false, + port, + }) + } + pub fn attach( + &mut self, + i2c: &mut I2cController< + Ast1060I2c<'static, ast1060_pac::I2c, SwmbxI2CTarget, UartLogger<'static>>, + NoOpLogger, + >, + ) -> Result<(), SwmbxI2CError> { + let mut config = I2cConfigBuilder::new() + .xfer_mode(I2cXferMode::DmaMode) + .multi_master(true) + .smbus_timeout(true) + .smbus_alert(false) + .speed(I2cSpeed::Standard) + .build(); + + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); + i2c.hardware.init(&mut config); + + let static_self = unsafe { core::mem::transmute::<&mut Self, &'static mut Self>(self) }; + + i2c.hardware + .i2c_aspeed_slave_register(self.address, Some(static_self)) + .map_err(|_| SwmbxI2CError::OtherError) + } +} diff --git a/src/main.rs b/src/main.rs index 0587686..d49f7b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,13 @@ use core::sync::atomic::AtomicBool; use aspeed_ddk::uart::{Config, UartController}; use aspeed_ddk::watchdog::WdtController; use ast1060_pac::Peripherals; -use ast1060_pac::{Wdt, Wdt1}; +use ast1060_pac::{Spipf, Wdt, Wdt1}; use aspeed_ddk::ecdsa::AspeedEcdsa; use aspeed_ddk::hace_controller::HaceController; use aspeed_ddk::rsa::AspeedRsa; use aspeed_ddk::spi; +use aspeed_ddk::spimonitor::{RegionInfo, SpiMonitor, SpimExtMuxSel}; use aspeed_ddk::syscon::{ClockId, ResetId, SysCon}; use fugit::MillisDurationU32 as MilliSeconds; @@ -23,7 +24,11 @@ use aspeed_ddk::tests::functional::hash_test::run_hash_tests; use aspeed_ddk::tests::functional::hmac_test::run_hmac_tests; use aspeed_ddk::tests::functional::i2c_test; use aspeed_ddk::tests::functional::rsa_test::run_rsa_tests; + +#[cfg(feature = "i2c_target")] +use aspeed_ddk::tests::functional::swmbx_test; use aspeed_ddk::tests::functional::timer_test::run_timer_tests; + use panic_halt as _; use proposed_traits::system_control::ResetControl; @@ -97,6 +102,51 @@ fn test_wdt(uart: &mut UartController<'_>) { } } +fn release_bmc_spi(uart: &mut UartController<'_>) { + uart.write_all(b"\r\n####### SPIM0 setup #######\r\n") + .unwrap(); + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + + let read_blocked_regions = [RegionInfo { + /*pfm*/ + start: 0x0400_0000, + length: 0x0002_0000, + }]; + + let write_blocked_regions = [RegionInfo { + start: 0x0000_0000, + length: 0x0800_0000, + }]; + let mut spi_monitor0 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &read_blocked_regions, + u8::try_from(read_blocked_regions.len()).unwrap(), + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor0.spim_sw_rst(); + spi_monitor0.aspeed_spi_monitor_init(); + spi_monitor0.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); + // print spim pointer value +} + +fn setup_bmc_sequence(uart_controller: &mut UartController) { + // Enable BMC flash power + gpio_test::test_gpio_flash_power(uart_controller); + + // Release BMC SPI + release_bmc_spi(uart_controller); + + // Release BMC Reset + gpio_test::test_gpio_bmc_reset(uart_controller); +} + #[no_mangle] pub static HALT: AtomicBool = AtomicBool::new(true); @@ -164,10 +214,9 @@ fn main() -> ! { let mut rsa = AspeedRsa::new(&secure, delay); run_rsa_tests(&mut uart_controller, &mut rsa); - gpio_test::test_gpioa(&mut uart_controller); + // gpio_test::test_gpioa(&mut uart_controller); i2c_test::test_i2c_master(&mut uart_controller); - #[cfg(feature = "i2c_target")] - i2c_test::test_i2c_slave(&mut uart_controller); + test_wdt(&mut uart_controller); run_timer_tests(&mut uart_controller); @@ -180,6 +229,12 @@ fn main() -> ! { spi::spitest::test_spi2(&mut uart_controller); } // Initialize the peripherals here if needed + + setup_bmc_sequence(&mut uart_controller); + // Start SWMBX test + #[cfg(feature = "i2c_target")] + swmbx_test::test_swmbx(&mut uart_controller); + loop { cortex_m::asm::wfi(); } diff --git a/src/tests/functional/i2c_test.rs b/src/tests/functional/i2c_test.rs index 2f33a50..ad5af24 100644 --- a/src/tests/functional/i2c_test.rs +++ b/src/tests/functional/i2c_test.rs @@ -7,12 +7,10 @@ use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; use crate::pinctrl; use crate::uart::{self, Config, UartController}; use ast1060_pac::Peripherals; -#[cfg(feature = "i2c_target")] -use cortex_m::peripheral::NVIC; use embedded_hal::i2c::ErrorKind; use embedded_io::Write; use proposed_traits::i2c_target::{ - I2CCoreTarget, ReadTarget, RegisterAccess, WriteReadTarget, WriteTarget, + I2CCoreTarget, ReadTarget, RegisterAccess, TransactionDirection, WriteReadTarget, WriteTarget, }; #[derive(Debug)] @@ -44,7 +42,13 @@ impl I2CCoreTarget for DummyI2CTarget { self.address = address; Ok(()) } - fn on_transaction_start(&mut self, _repeated: bool) {} + fn on_transaction_start( + &mut self, + _direction: TransactionDirection, + _repeated: bool, + ) -> Result, Self::Error> { + Ok(None) + } fn on_stop(&mut self) {} fn on_address_match(&mut self, address: u8) -> bool { self.address == address @@ -211,93 +215,3 @@ pub fn test_i2c_master(uart: &mut UartController<'_>) { } } } - -#[cfg(feature = "i2c_target")] -static mut UART_PTR: Option<&'static mut UartController<'static>> = None; -#[cfg(feature = "i2c_target")] -static mut I2C0_INSTANCE: Option< - I2cController, NoOpLogger>, -> = None; - -#[cfg(feature = "i2c_target")] -#[no_mangle] -pub extern "C" fn i2c() { - unsafe { - if let Some(uart) = UART_PTR.as_mut() { - let _ = uart.write_all(b"[ISR] i2c0\r\n"); - } - if let Some(i2c0) = I2C0_INSTANCE.as_mut() { - let () = i2c0.hardware.handle_interrupt(); - } - } -} - -#[cfg(feature = "i2c_target")] -static mut TEST_TARGET: DummyI2CTarget = DummyI2CTarget { - address: 0x42, - buffer: [0; 16], - read_idx: 0, -}; -#[cfg(feature = "i2c_target")] -pub fn test_i2c_slave(uart: &mut UartController<'_>) { - writeln!(uart, "\r\n####### I2C slave test #######\r\n").unwrap(); - - let peripherals = unsafe { Peripherals::steal() }; - let mut delay = DummyDelay {}; - unsafe { - let mut dbg_uart = UartController::new( - peripherals.uart, - core::mem::transmute::<&mut DummyDelay, &'static mut DummyDelay>(&mut delay), - ); - - dbg_uart.init(&Config { - baud_rate: 115_200, - word_length: uart::WordLength::Eight as u8, - parity: uart::Parity::None, - stop_bits: uart::StopBits::One, - clock: 24_000_000, - }); - - let i2c_config = I2cConfigBuilder::new() - .xfer_mode(I2cXferMode::DmaMode) - .multi_master(true) - .smbus_timeout(true) - .smbus_alert(false) - .speed(I2cSpeed::Standard) - .build(); - //on DC-SCM board, i2c0 of ast1060 is connected to i2c4 of ast2600 - let mut i2c0: I2cController< - Ast1060I2c, - NoOpLogger, - > = I2cController { - hardware: Ast1060I2c::new(UartLogger::new(core::mem::transmute::< - &mut UartController<'_>, - &'static mut UartController<'static>, - >(&mut dbg_uart))), - config: i2c_config, - logger: NoOpLogger {}, - }; - - pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); - i2c0.hardware.init(&mut i2c0.config); - - match i2c0 - .hardware - .i2c_aspeed_slave_register(TEST_TARGET.address, None) - { - Ok(val) => { - writeln!(uart, "i2c slave register ok: {val:?}\r").unwrap(); - } - Err(e) => { - writeln!(uart, "i2c slave register err: {e:?}\r").unwrap(); - } - } - - UART_PTR = Some(core::mem::transmute::< - &mut UartController<'_>, - &'static mut UartController<'static>, - >(uart)); - I2C0_INSTANCE = Some(i2c0); - NVIC::unmask(ast1060_pac::Interrupt::i2c); - } -} diff --git a/src/tests/functional/mod.rs b/src/tests/functional/mod.rs index f37c0d5..d21125b 100644 --- a/src/tests/functional/mod.rs +++ b/src/tests/functional/mod.rs @@ -7,4 +7,6 @@ pub mod hmac_test; pub mod i2c_test; pub mod rsa_test; pub mod rsa_test_vec; +#[cfg(feature = "i2c_target")] +pub mod swmbx_test; pub mod timer_test; diff --git a/src/tests/functional/swmbx_test.rs b/src/tests/functional/swmbx_test.rs new file mode 100644 index 0000000..330cc69 --- /dev/null +++ b/src/tests/functional/swmbx_test.rs @@ -0,0 +1,200 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{DummyDelay, NoOpLogger, UartLogger}; +use crate::i2c::ast1060_i2c::Ast1060I2c; +use crate::i2c::common::I2cConfigBuilder; +use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; +use crate::pinctrl; +use crate::uart::{self, Config, UartController}; +use ast1060_pac::Peripherals; +use embedded_io::Write; + +use crate::i2c::pfr::swmbx; +use crate::i2c::pfr::swmbx::SwmbxCtrl; +use crate::i2c::target::swmbx_target::SwmbxI2CTarget; +use core::mem::MaybeUninit; +use cortex_m::peripheral::NVIC; + +static mut SWMBX_TARGET: MaybeUninit = MaybeUninit::uninit(); +static mut DBG_UART: MaybeUninit> = MaybeUninit::uninit(); +static mut DELAY: MaybeUninit = MaybeUninit::uninit(); + +pub const UFM_WRITE_FIFO: u8 = 0xd; +pub const UFM_READ_FIFO: u8 = 0xe; +pub const SWMBX_WRITE_FIFO_SIZE: u8 = 64; +pub const SWMBX_READ_FIFO_SIZE: u8 = 128; + +pub const BMC_UPDATE_INTENT: u8 = 0x13; + +#[no_mangle] +static mut UART_PTR: Option<&'static mut UartController<'static>> = None; + +#[cfg(feature = "i2c_target")] +static mut I2C_PTR: Option< + &'static mut I2cController< + Ast1060I2c, + NoOpLogger, + >, +> = None; +#[cfg(feature = "i2c_target")] +pub fn test_swmbx(uart: &mut UartController<'_>) { + unsafe { + UART_PTR = Some(core::mem::transmute::< + &mut UartController<'_>, + &'static mut UartController<'static>, + >(uart)); + } + writeln!(uart, "\r\n####### SWMBX test #######\r\n").unwrap(); + + let p = unsafe { Peripherals::steal() }; + + let delay: &'static mut DummyDelay = unsafe { + DELAY.write(DummyDelay {}); + &mut *DELAY.as_mut_ptr() + }; + + let dbg_uart: &'static mut UartController<'static> = unsafe { + DBG_UART.write(UartController::new(p.uart, delay)); + &mut *DBG_UART.as_mut_ptr() + }; + unsafe { + dbg_uart.init(&Config { + baud_rate: 115_200, + word_length: uart::WordLength::Eight as u8, + parity: uart::Parity::None, + stop_bits: uart::StopBits::One, + clock: 24_000_000, + }); + } + + writeln!(uart, "\r\n## SWMBX: Starting up...\r\n").ok(); + + let swmbx = SwmbxCtrl::init(swmbx::SWMBX_BUF_SIZE); + let _ = swmbx.enable_behavior( + swmbx::SWMBX_PROTECT | swmbx::SWMBX_NOTIFY | swmbx::SWMBX_FIFO, + true, + ); + let _ = swmbx.update_fifo( + 0, + UFM_WRITE_FIFO, + SWMBX_WRITE_FIFO_SIZE, + swmbx::SWMBX_FIFO_NOTIFY_STOP, + true, + ); + let _ = swmbx.update_fifo( + 1, + UFM_READ_FIFO, + SWMBX_READ_FIFO_SIZE, + swmbx::SWMBX_FIFO_NOTIFY_STOP, + true, + ); + let _ = swmbx.update_notify(0, BMC_UPDATE_INTENT, true); + let access_control: [[u32; 8]; 2] = [ + // BMC + [ + 0xfff704ff, 0xffffffff, 0xffffffff, 0xfffffff2, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, + ], + // PCH + [ + 0xfff884ff, 0xffffffff, 0xffffffff, 0xfffffff5, 0x00000000, 0x00000000, 0xffffffff, + 0xffffffff, + ], + ]; + let _ = swmbx.apply_protect(0, &access_control[0], 0); + let _ = swmbx.apply_protect(1, &access_control[1], 0); + SwmbxCtrl::store_ctrl_ptr(swmbx); + + let target = unsafe { + SWMBX_TARGET.write(SwmbxI2CTarget::new(0, 0x38).expect("invalid address")); + &mut *SWMBX_TARGET.as_mut_ptr() + }; + + let mut i2c0 = I2cController { + hardware: Ast1060I2c::new(UartLogger::new(dbg_uart)), + config: I2cConfigBuilder::new().build(), + logger: NoOpLogger {}, + }; + + target + .attach(&mut i2c0) + .expect("i2c target register failed"); + unsafe { + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); + I2C_PTR = Some(core::mem::transmute::< + &mut I2cController< + Ast1060I2c, + NoOpLogger, + >, + &'static mut I2cController< + Ast1060I2c, + NoOpLogger, + >, + >(&mut i2c0)); + NVIC::unmask(ast1060_pac::Interrupt::i2c); + } + loop { + // Execute the following command at the BMC prompt to verify SWMBX functionality: + // - Verify the SWMBX address: + // aspeed-pfr-tool -w 0x13 0x8 + // aspeed-pfr-tool -r 0x13 + // [Expected output: 0x8] + // + // - Verify the protected SWMBX address: + // aspeed-pfr-tool -w 0x12 0x8 + // aspeed-pfr-tool -r 0x12 + // [Expected output: 0x0] + // + // - Verify the notify functionality: + // aspeed-pfr-tool -w 0x13 0x8 + // On Ast1060 console, you should see [NOTIFY] SWMBX: notify triggered on port 0 addr 0x13 + // + // - Verify the FIFO functionality: + // aspeed-pfr-tool -w 0xd 0x1f + // aspeed-pfr-tool -w 0xd 0x2f + // aspeed-pfr-tool -w 0xd 0x3f + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // [Expected output: 0x1f, 0x2f, 0x3f, 0x0] + poll_notify(); + } +} + +fn poll_notify() { + unsafe { + if let Some(uart) = UART_PTR.as_mut() { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + + for port in 0..swmbx::SWMBX_DEV_COUNT { + for addr in 0..swmbx::SWMBX_NODE_COUNT { + let node = &mut ctrl.node[port][addr]; + if node.notify_flag { + if let Err(e) = writeln!( + uart, + "[NOTIFY] SWMBX: notify triggered on port {} addr {:#x}\r\n", + port, addr + ) { + let _ = writeln!(uart, "write error: {:?}\r\n", e); + } + + node.notify_flag = false; + } + } + } + } + } +} + +#[no_mangle] +pub extern "C" fn i2c() { + unsafe { + if let Some(uart) = UART_PTR.as_mut() { + let _ = uart.write_all(b"[ISR] I2C\r\n"); + } + if let Some(i2c) = I2C_PTR.as_mut() { + i2c.hardware.handle_interrupt(); + } + } +}