diff --git a/.vscode/.gitignore b/.vscode/.gitignore new file mode 100644 index 000000000..9fbb9ec95 --- /dev/null +++ b/.vscode/.gitignore @@ -0,0 +1,3 @@ +*.cortex-debug.*.json +launch.json +tasks.json \ No newline at end of file diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b9887f9b3..21adb5ddf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,6 +67,7 @@ seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} +bit_field = "0.10.2" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -1443,4 +1444,4 @@ stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 11820b7a0..b3dbe1e2f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -57,6 +57,8 @@ pub mod rtc; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(stm32wb)] +pub mod tl_mbox; #[cfg(usart)] pub mod usart; #[cfg(all(usb, feature = "time"))] diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs new file mode 100644 index 000000000..a2c0758d1 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -0,0 +1,26 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; +use crate::ipcc::Ipcc; + +pub struct Ble; + +impl Ble { + pub fn new(ipcc: &mut Ipcc) -> Self { + unsafe { + LST_init_head(EVT_QUEUE.as_mut_ptr()); + + TL_BLE_TABLE = MaybeUninit::new(BleTable { + pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), + pcs_buffer: CS_BUFFER.as_mut_ptr().cast(), + pevt_queue: EVT_QUEUE.as_ptr().cast(), + phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), + }); + } + + ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); + + Ble + } +} diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs new file mode 100644 index 000000000..1dde5d61c --- /dev/null +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -0,0 +1,104 @@ +//! CPU1 CPU2 +//! | (SYSTEM) | +//! |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------| +//! | | +//! | (ZIGBEE) | +//! |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>| +//! | | +//! |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------| +//! | | +//! |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------| +//! | | +//! | (THREAD) | +//! |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->| +//! | | +//! |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------| +//! | | +//! |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--| +//! | | +//! | (BLE) | +//! |----HW_IPCC_BLE_CMD_CHANNEL--------------------->| +//! | | +//! |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->| +//! | | +//! |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------| +//! | | +//! | (BLE LLD) | +//! |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->| +//! | | +//! |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------| +//! | | +//! |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------| +//! | | +//! | (MAC) | +//! |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->| +//! | | +//! |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL| +//! | | +//! | (BUFFER) | +//! |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>| +//! | | +//! | (TRACE) | +//! |<----HW_IPCC_TRACES_CHANNEL----------------------| +//! | | +//! + +pub mod cpu1 { + use crate::ipcc::IpccChannel; + + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; +} + +pub mod cpu2 { + use crate::ipcc::IpccChannel; + + pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; + pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5; +} diff --git a/embassy-stm32/src/tl_mbox/cmd.rs b/embassy-stm32/src/tl_mbox/cmd.rs new file mode 100644 index 000000000..3507c3231 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/cmd.rs @@ -0,0 +1,49 @@ +use super::PacketHeader; + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct Cmd { + pub cmd_code: u16, + pub payload_len: u8, + pub payload: [u8; 255], +} + +impl Default for Cmd { + fn default() -> Self { + Self { + cmd_code: 0, + payload_len: 0, + payload: [0u8; 255], + } + } +} + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct CmdSerial { + pub ty: u8, + pub cmd: Cmd, +} + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct CmdPacket { + pub header: PacketHeader, + pub cmd_serial: CmdSerial, +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct AclDataSerial { + pub ty: u8, + pub handle: u16, + pub length: u16, + pub acl_data: [u8; 1], +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct AclDataPacket { + pub header: PacketHeader, + pub acl_data_serial: AclDataSerial, +} diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs new file mode 100644 index 000000000..4244db810 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -0,0 +1,8 @@ +/// the payload of [`Evt`] for a command status event +#[derive(Copy, Clone)] +#[repr(C, packed)] +pub struct CsEvt { + pub status: u8, + pub num_cmd: u8, + pub cmd_code: u16, +} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs new file mode 100644 index 000000000..cf4797305 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -0,0 +1,30 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{ + MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, + TL_MEM_MANAGER_TABLE, +}; + +pub struct MemoryManager; + +impl MemoryManager { + pub fn new() -> Self { + unsafe { + LST_init_head(FREE_BUFF_QUEUE.as_mut_ptr()); + LST_init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); + + TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { + spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), + spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), + ble_pool: EVT_POOL.as_ptr().cast(), + ble_pool_size: POOL_SIZE as u32, + pevt_free_buffer_queue: FREE_BUFF_QUEUE.as_mut_ptr(), + traces_evt_pool: core::ptr::null(), + traces_pool_size: 0, + }); + } + + MemoryManager + } +} diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs new file mode 100644 index 000000000..73d2ca6d6 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -0,0 +1,318 @@ +use core::mem::MaybeUninit; + +use bit_field::BitField; + +use self::ble::Ble; +use self::cmd::{AclDataPacket, CmdPacket}; +use self::evt::CsEvt; +use self::mm::MemoryManager; +use self::sys::Sys; +use self::unsafe_linked_list::LinkedListNode; +use crate::ipcc::Ipcc; + +mod ble; +mod channels; +mod cmd; +mod evt; +mod mm; +mod sys; +mod unsafe_linked_list; + +pub type PacketHeader = LinkedListNode; + +const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); +const TL_EVT_HEADER_SIZE: usize = 3; +const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); + +const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5; +const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; +const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; + +const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); + +const fn divc(x: usize, y: usize) -> usize { + (x + y - 1) / y +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct SafeBootInfoTable { + version: u32, +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct RssInfoTable { + version: u32, + memory_size: u32, + rss_info: u32, +} + +/// # Version +/// - 0 -> 3 = Build - 0: Untracked - 15:Released - x: Tracked version +/// - 4 -> 7 = branch - 0: Mass Market - x: ... +/// - 8 -> 15 = Subversion +/// - 16 -> 23 = Version minor +/// - 24 -> 31 = Version major +/// # Memory Size +/// - 0 -> 7 = Flash ( Number of 4k sector) +/// - 8 -> 15 = Reserved ( Shall be set to 0 - may be used as flash extension ) +/// - 16 -> 23 = SRAM2b ( Number of 1k sector) +/// - 24 -> 31 = SRAM2a ( Number of 1k sector) +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct WirelessFwInfoTable { + version: u32, + memory_size: u32, + thread_info: u32, + ble_info: u32, +} + +impl WirelessFwInfoTable { + pub fn version_major(&self) -> u8 { + let version = self.version; + (version.get_bits(24..31) & 0xff) as u8 + } + + pub fn version_minor(&self) -> u8 { + let version = self.version; + (version.get_bits(16..23) & 0xff) as u8 + } + + pub fn subversion(&self) -> u8 { + let version = self.version; + (version.get_bits(8..15) & 0xff) as u8 + } + + /// size of FLASH, expressed in number of 4K sectors + pub fn flash_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(0..7) & 0xff) as u8 + } + + /// size for SRAM2a, expressed in number of 1K sectors + pub fn sram2a_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(24..31) & 0xff) as u8 + } + + /// size of SRAM2b, expressed in number of 1K sectors + pub fn sram2b_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(16..23) & 0xff) as u8 + } +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct DeviceInfoTable { + pub safe_boot_info_table: SafeBootInfoTable, + pub rss_info_table: RssInfoTable, + pub wireless_fw_info_table: WirelessFwInfoTable, +} + +#[repr(C, packed)] +struct BleTable { + pcmd_buffer: *mut CmdPacket, + pcs_buffer: *const u8, + pevt_queue: *const u8, + phci_acl_data_buffer: *mut AclDataPacket, +} + +#[repr(C, packed)] +struct ThreadTable { + no_stack_buffer: *const u8, + cli_cmd_rsp_buffer: *const u8, + ot_cmd_rsp_buffer: *const u8, +} + +#[repr(C, packed)] +struct SysTable { + pcmd_buffer: *mut CmdPacket, + sys_queue: *const LinkedListNode, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct LldTestTable { + cli_cmd_rsp_buffer: *const u8, + m0_cmd_buffer: *const u8, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct BleLldTable { + cmd_rsp_buffer: *const u8, + m0_cmd_buffer: *const u8, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct ZigbeeTable { + notif_m0_to_m4_buffer: *const u8, + appli_cmd_m4_to_m0_buffer: *const u8, + request_m0_to_m4_buffer: *const u8, +} + +#[repr(C, packed)] +struct MemManagerTable { + spare_ble_buffer: *const u8, + spare_sys_buffer: *const u8, + + ble_pool: *const u8, + ble_pool_size: u32, + + pevt_free_buffer_queue: *mut LinkedListNode, + + traces_evt_pool: *const u8, + traces_pool_size: u32, +} + +#[repr(C, packed)] +struct TracesTable { + traces_queue: *const u8, +} + +#[repr(C, packed)] +struct Mac802_15_4Table { + pcmd_rsp_buffer: *const u8, + pnotack_buffer: *const u8, + evt_queue: *const u8, +} + +/// reference table. Contains pointers to all other tables +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct RefTable { + pub device_info_table: *const DeviceInfoTable, + ble_table: *const BleTable, + thread_table: *const ThreadTable, + sys_table: *const SysTable, + mem_manager_table: *const MemManagerTable, + traces_table: *const TracesTable, + mac_802_15_4_table: *const Mac802_15_4Table, +} + +#[link_section = "TL_REF_TABLE"] +pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[allow(dead_code)] // Not used currently but reserved +#[link_section = "MB_MEM2"] +static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +// not in shared RAM +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[allow(dead_code)] // Not used currently but reserved +#[link_section = "MB_MEM2"] +static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +// "magic" numbers from ST ---v---v +static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); + +pub struct TlMbox { + _sys: Sys, + _ble: Ble, + _mm: MemoryManager, +} + +impl TlMbox { + /// initializes low-level transport between CPU1 and BLE stack on CPU2 + pub fn init(ipcc: &mut Ipcc) -> TlMbox { + unsafe { + TL_REF_TABLE = MaybeUninit::new(RefTable { + device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), + ble_table: TL_BLE_TABLE.as_ptr(), + thread_table: TL_THREAD_TABLE.as_ptr(), + sys_table: TL_SYS_TABLE.as_ptr(), + mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), + traces_table: TL_TRACES_TABLE.as_ptr(), + mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), + }); + + TL_SYS_TABLE = MaybeUninit::zeroed(); + TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); + TL_BLE_TABLE = MaybeUninit::zeroed(); + TL_THREAD_TABLE = MaybeUninit::zeroed(); + TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed(); + TL_TRACES_TABLE = MaybeUninit::zeroed(); + TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed(); + + EVT_POOL = MaybeUninit::zeroed(); + SYS_SPARE_EVT_BUF = MaybeUninit::zeroed(); + BLE_SPARE_EVT_BUF = MaybeUninit::zeroed(); + + CS_BUFFER = MaybeUninit::zeroed(); + BLE_CMD_BUFFER = MaybeUninit::zeroed(); + HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); + } + + ipcc.init(); + + let _sys = Sys::new(ipcc); + let _ble = Ble::new(ipcc); + let _mm = MemoryManager::new(); + + TlMbox { _sys, _ble, _mm } + } + + pub fn wireless_fw_info(&self) -> Option { + let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table }; + + // zero version indicates that CPU2 wasn't active and didn't fill the information table + if info.version != 0 { + Some(*info) + } else { + None + } + } +} diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs new file mode 100644 index 000000000..13ae7f9f9 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -0,0 +1,24 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use crate::ipcc::Ipcc; + +pub struct Sys; + +impl Sys { + pub fn new(ipcc: &mut Ipcc) -> Self { + unsafe { + LST_init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); + + TL_SYS_TABLE = MaybeUninit::new(SysTable { + pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), + sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), + }); + } + + ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); + + Sys + } +} diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs new file mode 100644 index 000000000..9caf01d1d --- /dev/null +++ b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs @@ -0,0 +1,123 @@ +//! Unsafe linked list. +//! Translated from ST's C by `c2rust` tool. + +#![allow( + dead_code, + mutable_transmutes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] + +use cortex_m::interrupt; + +#[derive(Copy, Clone)] +#[repr(C, packed(4))] +pub struct LinkedListNode { + pub next: *mut LinkedListNode, + pub prev: *mut LinkedListNode, +} + +impl Default for LinkedListNode { + fn default() -> Self { + LinkedListNode { + next: core::ptr::null_mut(), + prev: core::ptr::null_mut(), + } + } +} + +pub unsafe fn LST_init_head(mut listHead: *mut LinkedListNode) { + (*listHead).next = listHead; + (*listHead).prev = listHead; +} + +pub unsafe fn LST_is_empty(mut listHead: *mut LinkedListNode) -> bool { + interrupt::free(|_| ((*listHead).next) == listHead) +} + +pub unsafe fn LST_insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*listHead).next; + (*node).prev = listHead; + (*listHead).next = node; + (*(*node).next).prev = node; + }); +} + +pub unsafe fn LST_insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = listHead; + (*node).prev = (*listHead).prev; + (*listHead).prev = node; + (*(*node).prev).next = node; + }); +} + +pub unsafe fn LST_remove_node(mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*(*node).prev).next = (*node).next; + (*(*node).next).prev = (*node).prev; + }); +} + +pub unsafe fn LST_remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).next; + LST_remove_node((*listHead).next); + }); +} + +pub unsafe fn LST_remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).prev; + LST_remove_node((*listHead).prev); + }); +} + +pub unsafe fn LST_insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*ref_node).next; + (*node).prev = ref_node; + (*ref_node).next = node; + (*(*node).next).prev = node; + }); +} + +pub unsafe fn LST_insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = ref_node; + (*node).prev = (*ref_node).prev; + (*ref_node).prev = node; + (*(*node).prev).next = node; + }); +} + +pub unsafe fn LST_get_size(mut listHead: *mut LinkedListNode) -> usize { + interrupt::free(|_| { + let mut size = 0; + let mut temp: *mut LinkedListNode = core::ptr::null_mut::(); + + temp = (*listHead).next; + while temp != listHead { + size += 1; + temp = (*temp).next + } + + size + }) +} + +pub unsafe fn LST_get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).next; + }); +} + +pub unsafe fn LST_get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).prev; + }); +} diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 5d78d79e5..d23fdc513 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,7 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +# runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +runner = "teleprobe local run --chip STM32WB55RG --elf" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index db1816da3..3c7e3e874 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index 0e48c916d..5a07b7d19 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -10,6 +10,17 @@ MEMORY RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K } +/* + Memory size for STM32WB55xC with 512K FLASH + + MEMORY + { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + } +*/ + /* Place stack at the end of SRAM1 */ _stack_start = ORIGIN(RAM) + LENGTH(RAM); @@ -19,23 +30,6 @@ _stack_start = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED - TL_DEVICE_INFO_TABLE 0x2003001c (NOLOAD) : { *(TL_DEVICE_INFO_TABLE) } >RAM_SHARED - TL_BLE_TABLE 0x2003003c (NOLOAD) : { *(TL_BLE_TABLE) } >RAM_SHARED - TL_THREAD_TABLE 0x2003004c (NOLOAD) : { *(TL_THREAD_TABLE) } >RAM_SHARED - TL_SYS_TABLE 0x20030058 (NOLOAD) : { *(TL_SYS_TABLE) } >RAM_SHARED - TL_MEM_MANAGER_TABLE 0x20030060 (NOLOAD) : { *(TL_MEM_MANAGER_TABLE) } >RAM_SHARED - TL_TRACES_TABLE 0x2003007c (NOLOAD) : { *(TL_TRACES_TABLE) } >RAM_SHARED - TL_MAC_802_15_4_TABLE 0x20030080 (NOLOAD) : { *(TL_MAC_802_15_4_TABLE) } >RAM_SHARED - - HCI_ACL_DATA_BUFFER 0x20030a08 (NOLOAD) : { *(HCI_ACL_DATA_BUFFER) } >RAM_SHARED - BLE_CMD_BUFFER 0x200308fc (NOLOAD) : { *(BLE_CMD_BUFFER) } >RAM_SHARED - BLE_SPARE_EVT_BUF 0x200301a8 (NOLOAD) : { *(BLE_SPARE_EVT_BUF) } >RAM_SHARED - SYS_SPARE_EVT_BUF 0x200302b4 (NOLOAD) : { *(SYS_SPARE_EVT_BUF) } >RAM_SHARED - EVT_POOL 0x200303c0 (NOLOAD) : { *(EVT_POOL) } >RAM_SHARED - SYS_CMD_BUF 0x2003009c (NOLOAD) : { *(SYS_CMD_BUF) } >RAM_SHARED - SYSTEM_EVT_QUEUE 0x20030b28 (NOLOAD) : { *(SYSTEM_EVT_QUEUE) } >RAM_SHARED - EVT_QUEUE 0x20030b10 (NOLOAD) : { *(EVT_QUEUE) } >RAM_SHARED - CS_BUFFER 0x20030b18 (NOLOAD) : { *(CS_BUFFER) } >RAM_SHARED - TRACES_EVT_QUEUE 0x20030094 (NOLOAD) : { *(TRACES_EVT_QUEUE) } >RAM_SHARED - FREE_BUF_QUEUE 0x2003008c (NOLOAD) : { *(FREE_BUF_QUEUE) } >RAM_SHARED + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED } diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs new file mode 100644 index 000000000..6876526ae --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::tl_mbox::TlMbox; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let mbox = TlMbox::init(&mut ipcc); + + loop { + let wireless_fw_info = mbox.wireless_fw_info(); + match wireless_fw_info { + None => error!("not yet initialized"), + Some(fw_info) => { + let version_major = fw_info.version_major(); + let version_minor = fw_info.version_minor(); + let subversion = fw_info.subversion(); + + let sram2a_size = fw_info.sram2a_size(); + let sram2b_size = fw_info.sram2b_size(); + + info!( + "version {}.{}.{} - SRAM2a {} - SRAM2b {}", + version_major, version_minor, subversion, sram2a_size, sram2b_size + ); + + break; + } + } + + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 83bf1e9c9..bd8d90abe 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -18,6 +18,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] +ble = [] not-gpdma = [] [dependencies] @@ -43,6 +44,11 @@ chrono = { version = "^0.4", default-features = false, optional = true} # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. +[[bin]] +name = "ble" +path = "src/bin/ble.rs" +required-features = [ "ble",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs new file mode 100644 index 000000000..f4c864c65 --- /dev/null +++ b/tests/stm32/src/bin/ble.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: ble + +#[path = "../example_common.rs"] +mod example_common; +use embassy_executor::Spawner; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::tl_mbox::TlMbox; +use embassy_time::{Duration, Timer}; +use example_common::*; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + let config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let mbox = TlMbox::init(&mut ipcc); + + loop { + let wireless_fw_info = mbox.wireless_fw_info(); + match wireless_fw_info { + None => error!("not yet initialized"), + Some(fw_info) => { + let version_major = fw_info.version_major(); + let version_minor = fw_info.version_minor(); + let subversion = fw_info.subversion(); + + let sram2a_size = fw_info.sram2a_size(); + let sram2b_size = fw_info.sram2b_size(); + + info!( + "version {}.{}.{} - SRAM2a {} - SRAM2b {}", + version_major, version_minor, subversion, sram2a_size, sram2b_size + ); + + break; + } + } + + Timer::after(Duration::from_millis(500)).await; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +}