From b5ee9b510db2ca975ee61b2e96df4cc854227476 Mon Sep 17 00:00:00 2001 From: Juhan Oskar Hennoste Date: Sun, 20 Jul 2025 00:12:09 +0300 Subject: [PATCH 1/2] Implement support for 32-bit memory access scheme. Tested on STM32C071. --- examples/hal.rs | 4 +- src/endpoint_memory.rs | 247 ++++++++++++++++++++++++++++------------- src/lib.rs | 6 +- 3 files changed, 172 insertions(+), 85 deletions(-) diff --git a/examples/hal.rs b/examples/hal.rs index 345719c..86d28f8 100644 --- a/examples/hal.rs +++ b/examples/hal.rs @@ -1,6 +1,6 @@ //! USB peripheral -use stm32_usbd::UsbPeripheral; +use stm32_usbd::{MemoryAccess, UsbPeripheral}; use stm32f1xx_hal::pac::{RCC, USB}; pub use stm32_usbd::UsbBus; @@ -20,7 +20,7 @@ unsafe impl UsbPeripheral for Peripheral { const DP_PULL_UP_FEATURE: bool = false; const EP_MEMORY: *const () = 0x4000_6000 as _; const EP_MEMORY_SIZE: usize = 512; - const EP_MEMORY_ACCESS_2X16: bool = false; + const EP_MEMORY_ACCESS: MemoryAccess = MemoryAccess::Word16x1; fn enable() { let rcc = unsafe { &*RCC::ptr() }; diff --git a/src/endpoint_memory.rs b/src/endpoint_memory.rs index 104be90..4ae869c 100644 --- a/src/endpoint_memory.rs +++ b/src/endpoint_memory.rs @@ -1,140 +1,230 @@ use crate::endpoint::NUM_ENDPOINTS; use crate::UsbPeripheral; -use core::marker::PhantomData; use core::slice; +use core::{marker::PhantomData, mem}; use usb_device::{Result, UsbError}; use vcell::VolatileCell; +/// Different endpoint memory access schemes +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MemoryAccess { + /// 16x1 bits per word. Each 32-bit word is accessed as one 16-bit half-word. The second half-word of the word is ignored. + /// + /// This matches the behavior of `EP_MEMORY_ACCESS_2X16 = false` from previous versions of this library. + Word16x1, + /// 16x2 bits per word. Each 32-bit word is accessed as two 16-bit half-words. + /// + /// This matches the behavior of `EP_MEMORY_ACCESS_2X16 = true` from previous versions of this library. + Word16x2, + /// 32x1 bits per word. Each 32-bit word is accessed as one 32-bit word. + Word32x1, +} + +impl MemoryAccess { + /// Value to multiply offsets within the EP memory by when calculating address to read or write to. + #[inline(always)] + const fn offset_multiplier(self) -> usize { + match self { + MemoryAccess::Word16x1 => 2, + MemoryAccess::Word16x2 | MemoryAccess::Word32x1 => 1, + } + } + + /// Size of unit used when reading and writing EP memory, in bytes. + #[inline(always)] + const fn unit_size(self) -> usize { + match self { + MemoryAccess::Word16x1 | MemoryAccess::Word16x2 => 2, + MemoryAccess::Word32x1 => 4, + } + } +} + pub struct EndpointBuffer { - mem: &'static mut [VolatileCell], + mem_ptr: *mut (), + mem_len: usize, marker: PhantomData, } +unsafe impl Send for EndpointBuffer {} + impl EndpointBuffer { pub fn new(offset_bytes: usize, size_bytes: usize) -> Self { - let ep_mem_ptr = USB::EP_MEMORY as *mut VolatileCell; - - let offset_words = offset_bytes >> 1; - let count_words = size_bytes >> 1; - let offset_u16_words; - let count_u16_words; - if USB::EP_MEMORY_ACCESS_2X16 { - offset_u16_words = offset_words; - count_u16_words = count_words; - } else { - offset_u16_words = offset_words * 2; - count_u16_words = count_words * 2; - }; - - unsafe { - let mem = slice::from_raw_parts_mut(ep_mem_ptr.add(offset_u16_words), count_u16_words); - Self { - mem, - marker: PhantomData, - } - } - } + let mem_offset_bytes = offset_bytes * USB::EP_MEMORY_ACCESS.offset_multiplier(); + let mem_len = size_bytes * USB::EP_MEMORY_ACCESS.offset_multiplier() / USB::EP_MEMORY_ACCESS.unit_size(); - #[inline(always)] - fn read_word(&self, index: usize) -> u16 { - if USB::EP_MEMORY_ACCESS_2X16 { - self.mem[index].get() - } else { - self.mem[index * 2].get() + let mem_ptr = unsafe { USB::EP_MEMORY.byte_add(mem_offset_bytes).cast_mut() }; + Self { + mem_ptr, + mem_len, + marker: PhantomData, } } #[inline(always)] - fn write_word(&self, index: usize, value: u16) { - if USB::EP_MEMORY_ACCESS_2X16 { - self.mem[index].set(value); - } else { - self.mem[index * 2].set(value); - } + fn get_mem_slice(&self) -> &mut [VolatileCell] { + unsafe { slice::from_raw_parts_mut(self.mem_ptr.cast(), self.mem_len) } } pub fn read(&self, mut buf: &mut [u8]) { - let mut index = 0; + if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { + let mem = self.get_mem_slice::(); - while buf.len() >= 2 { - let word = self.read_word(index); + let mut index = 0; - buf[0] = (word & 0xff) as u8; - buf[1] = (word >> 8) as u8; + while buf.len() >= 4 { + let value = mem[index].get().to_ne_bytes(); + index += USB::EP_MEMORY_ACCESS.offset_multiplier(); - index += 1; + buf[0..4].copy_from_slice(&value); + buf = &mut buf[4..]; + } - buf = &mut buf[2..]; - } + if buf.len() > 0 { + let value = mem[index].get().to_ne_bytes(); + buf.copy_from_slice(&value[0..buf.len()]); + } + } else { + let mem = self.get_mem_slice::(); + + let mut index = 0; + + while buf.len() >= 2 { + let value = mem[index].get().to_ne_bytes(); + index += USB::EP_MEMORY_ACCESS.offset_multiplier(); - if buf.len() > 0 { - buf[0] = (self.read_word(index) & 0xff) as u8; + buf[0..2].copy_from_slice(&value); + buf = &mut buf[2..]; + } + + if buf.len() > 0 { + let value = mem[index].get().to_ne_bytes(); + buf.copy_from_slice(&value[0..buf.len()]); + } } } pub fn write(&self, mut buf: &[u8]) { - let mut index = 0; + if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { + let mem = self.get_mem_slice::(); + + let mut index = 0; + + while buf.len() >= 4 { + let mut value = [0; 4]; + value.copy_from_slice(&buf[0..4]); + buf = &buf[4..]; + + mem[index].set(u32::from_ne_bytes(value)); + index += USB::EP_MEMORY_ACCESS.offset_multiplier(); + } - while buf.len() >= 2 { - let value: u16 = buf[0] as u16 | ((buf[1] as u16) << 8); - self.write_word(index, value); - index += 1; + if buf.len() > 0 { + let mut value = [0; 4]; + value[0..buf.len()].copy_from_slice(buf); + mem[index].set(u32::from_ne_bytes(value)); + } + } else { + let mem = self.get_mem_slice::(); - buf = &buf[2..]; - } + let mut index = 0; - if buf.len() > 0 { - self.write_word(index, buf[0] as u16); + while buf.len() >= 2 { + let mut value = [0; 2]; + value.copy_from_slice(&buf[0..2]); + buf = &buf[2..]; + + mem[index].set(u16::from_ne_bytes(value)); + index += USB::EP_MEMORY_ACCESS.offset_multiplier(); + } + + if buf.len() > 0 { + let mut value = [0; 2]; + value[0..buf.len()].copy_from_slice(buf); + mem[index].set(u16::from_ne_bytes(value)); + } } } pub fn offset(&self) -> u16 { - let buffer_address = self.mem.as_ptr() as usize; - let word_size = if USB::EP_MEMORY_ACCESS_2X16 { 2 } else { 4 }; - let index = (buffer_address - USB::EP_MEMORY as usize) / word_size; - (index << 1) as u16 + let offset_bytes = self.mem_ptr as usize - USB::EP_MEMORY as usize; + (offset_bytes / USB::EP_MEMORY_ACCESS.offset_multiplier()) as u16 } pub fn capacity(&self) -> usize { - let len_words = if USB::EP_MEMORY_ACCESS_2X16 { - self.mem.len() + self.mem_len * USB::EP_MEMORY_ACCESS.unit_size() / USB::EP_MEMORY_ACCESS.offset_multiplier() + } +} + +pub struct Field { + ptr: *const (), + marker: PhantomData, +} + +impl Field { + #[inline(always)] + pub fn get(&self) -> u16 { + if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { + let unaligned_offset = self.ptr as usize & 0b11; + let cell: &VolatileCell = unsafe { &*self.ptr.byte_sub(unaligned_offset).cast() }; + let value: [u16; 2] = unsafe { mem::transmute(cell.get()) }; + value[unaligned_offset >> 1] } else { - self.mem.len() / 2 - }; - len_words << 1 + let cell: &VolatileCell = unsafe { &*self.ptr.cast() }; + cell.get() + } + } + + #[inline(always)] + pub fn set(&self, value: u16) { + if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { + let unaligned_offset = self.ptr as usize & 0b11; + let cell: &VolatileCell = unsafe { &*self.ptr.byte_sub(unaligned_offset).cast() }; + let mut previous_value: [u16; 2] = unsafe { mem::transmute(cell.get()) }; + previous_value[unaligned_offset >> 1] = value; + cell.set(unsafe { mem::transmute(previous_value) }); + } else { + let cell: &VolatileCell = unsafe { &*self.ptr.cast() }; + cell.set(value); + } } } #[repr(C)] pub struct BufferDescriptor { - ptr: *const VolatileCell, + ptr: *const (), marker: PhantomData, } impl BufferDescriptor { #[inline(always)] - fn field(&self, index: usize) -> &'static VolatileCell { - let mul = if USB::EP_MEMORY_ACCESS_2X16 { 1 } else { 2 }; - unsafe { &*(self.ptr.add(index * mul)) } + fn field(&self, index: usize) -> Field { + let mul = USB::EP_MEMORY_ACCESS.offset_multiplier(); + let ptr = unsafe { self.ptr.byte_add(index * 2 * mul) }; + Field { + ptr, + marker: PhantomData, + } } #[inline(always)] - pub fn addr_tx(&self) -> &'static VolatileCell { + pub fn addr_tx(&self) -> Field { self.field(0) } #[inline(always)] - pub fn count_tx(&self) -> &'static VolatileCell { + pub fn count_tx(&self) -> Field { self.field(1) } #[inline(always)] - pub fn addr_rx(&self) -> &'static VolatileCell { + pub fn addr_rx(&self) -> Field { self.field(2) } #[inline(always)] - pub fn count_rx(&self) -> &'static VolatileCell { + pub fn count_rx(&self) -> Field { self.field(3) } } @@ -167,14 +257,11 @@ impl EndpointMemoryAllocator { } pub fn buffer_descriptor(index: u8) -> BufferDescriptor { - let mul = if USB::EP_MEMORY_ACCESS_2X16 { 1 } else { 2 }; - - unsafe { - let ptr = (USB::EP_MEMORY as *const VolatileCell).add((index as usize) * 4 * mul); - BufferDescriptor { - ptr, - marker: Default::default(), - } + let mul = USB::EP_MEMORY_ACCESS.offset_multiplier(); + let ptr = unsafe { USB::EP_MEMORY.byte_add((index as usize) * 8 * mul).cast() }; + BufferDescriptor { + ptr, + marker: Default::default(), } } } diff --git a/src/lib.rs b/src/lib.rs index 3f92e32..65d5168 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod endpoint; mod endpoint_memory; mod registers; pub use crate::bus::UsbBus; +pub use crate::endpoint_memory::MemoryAccess; mod pac; @@ -31,9 +32,8 @@ pub unsafe trait UsbPeripheral: Send + Sync { /// Endpoint memory access scheme /// - /// Check Reference Manual for details. - /// Set to `true` if "2x16 bits/word" access scheme is used, otherwise set to `false`. - const EP_MEMORY_ACCESS_2X16: bool; + /// See `MemoryAccess` enum for more details. Check Reference Manual to determine the correct access scheme to use. + const EP_MEMORY_ACCESS: MemoryAccess; /// Enables USB device on its peripheral bus fn enable(); From 7893801516d372aaaec86b6dd6e40c450e1e46f2 Mon Sep 17 00:00:00 2001 From: Juhan Oskar Hennoste Date: Fri, 25 Jul 2025 22:40:59 +0300 Subject: [PATCH 2/2] Adjust get_mem_slice signature --- src/endpoint_memory.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/endpoint_memory.rs b/src/endpoint_memory.rs index 4ae869c..4b3c9ce 100644 --- a/src/endpoint_memory.rs +++ b/src/endpoint_memory.rs @@ -62,14 +62,17 @@ impl EndpointBuffer { } } + /// # Safety + /// + /// Caller must ensure that while the returned reference exists, no mutable references to the section of EP memory covered by this slice exist. #[inline(always)] - fn get_mem_slice(&self) -> &mut [VolatileCell] { - unsafe { slice::from_raw_parts_mut(self.mem_ptr.cast(), self.mem_len) } + unsafe fn get_mem_slice(&self) -> &[VolatileCell] { + unsafe { slice::from_raw_parts(self.mem_ptr.cast(), self.mem_len) } } pub fn read(&self, mut buf: &mut [u8]) { if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { - let mem = self.get_mem_slice::(); + let mem = unsafe { self.get_mem_slice::() }; let mut index = 0; @@ -86,7 +89,7 @@ impl EndpointBuffer { buf.copy_from_slice(&value[0..buf.len()]); } } else { - let mem = self.get_mem_slice::(); + let mem = unsafe { self.get_mem_slice::() }; let mut index = 0; @@ -107,7 +110,7 @@ impl EndpointBuffer { pub fn write(&self, mut buf: &[u8]) { if USB::EP_MEMORY_ACCESS == MemoryAccess::Word32x1 { - let mem = self.get_mem_slice::(); + let mem = unsafe { self.get_mem_slice::() }; let mut index = 0; @@ -115,7 +118,7 @@ impl EndpointBuffer { let mut value = [0; 4]; value.copy_from_slice(&buf[0..4]); buf = &buf[4..]; - + mem[index].set(u32::from_ne_bytes(value)); index += USB::EP_MEMORY_ACCESS.offset_multiplier(); } @@ -126,7 +129,7 @@ impl EndpointBuffer { mem[index].set(u32::from_ne_bytes(value)); } } else { - let mem = self.get_mem_slice::(); + let mem = unsafe { self.get_mem_slice::() }; let mut index = 0;