continue
Some checks failed
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled

This commit is contained in:
2025-07-04 17:39:51 +02:00
parent 3f432fbdc3
commit 667db13306
4 changed files with 215 additions and 46 deletions

View File

@@ -35,7 +35,7 @@ critical-section = "1"
libm = "0.2"
log = "0.4"
embassy-sync = "0.6"
embassy-net = { path = "../../../Rust/embassy/embassy-net", version = "0.7" }
embassy-net-driver = { path = "../../../Rust/embassy/embassy-net-driver", version = "0.2" }
raw-slicee = "0.1"
embedded-io-async = "0.6"

View File

@@ -1,3 +1,4 @@
use arbitrary_int::u14;
use embassy_sync::waitqueue::AtomicWaker;
pub use super::rx_descr;
@@ -11,10 +12,12 @@ pub struct EthernetEmbassyNet {
pub burst_size: usize,
pub mac_addr: [u8; 6],
pub rx_descr: &'static [rx_descr::Descriptor],
pub rx_index: usize,
pub tx_descr: &'static [tx_descr::Descriptor],
pub tx_index: usize,
pub link_state: embassy_net::driver::LinkState,
//pub rx_index: usize,
//pub tx_descr: &'static [tx_descr::Descriptor],
//pub tx_index: usize,
pub tx_descr: tx_descr::DescriptorList<'static>,
pub tx_bufs: &'static mut [super::AlignedBuffer],
pub link_state: embassy_net_driver::LinkState,
}
pub struct EmbassyNetRxToken {
@@ -22,45 +25,54 @@ pub struct EmbassyNetRxToken {
rx_index: usize,
}
impl embassy_net::driver::RxToken for EmbassyNetRxToken {
impl embassy_net_driver::RxToken for EmbassyNetRxToken {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(&mut [])
}
}
pub struct EmbassyNetTxToken {
tx_descr_ref: tx_descr::DescriptorListRef<'static>,
tx_index: usize,
pub struct EmbassyNetTxToken<'a> {
descr_list: &'a mut tx_descr::DescriptorList<'static>,
tx_bufs: &'a mut [super::AlignedBuffer],
}
impl embassy_net::driver::TxToken for EmbassyNetRxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
impl embassy_net_driver::TxToken for EmbassyNetTxToken<'_> {
fn consume<R, F>(mut self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
assert!(len <= super::MTU, "packet length exceeds MTU");
let tx_idx = self.descr_list.current_tx_idx();
let buffer = self.tx_bufs.get_mut(tx_idx).unwrap();
let result = f(&mut buffer.0);
// TODO: Clean and invalidate cache.
let addr = buffer.0.as_ptr() as u32;
self.descr_list.prepare_transfer_unchecked(addr, u14::new(len as u16), true, false);
result
}
}
impl EthernetEmbassyNet {
#[inline]
pub fn update_link_state(&mut self, link_state: embassy_net::driver::LinkState) {
pub fn update_link_state(&mut self, link_state: embassy_net_driver::LinkState) {
self.link_state = link_state;
}
#[inline]
pub fn set_link_state_up(&mut self) {
self.update_link_state(embassy_net::driver::LinkState::Up);
self.update_link_state(embassy_net_driver::LinkState::Up);
}
#[inline]
pub fn set_link_state_down(&mut self) {
self.update_link_state(embassy_net::driver::LinkState::Down);
self.update_link_state(embassy_net_driver::LinkState::Down);
}
}
impl embassy_net::Driver for EthernetEmbassyNet {
impl embassy_net_driver::Driver for EthernetEmbassyNet {
type RxToken<'a>
where
Self: 'a,
@@ -69,38 +81,44 @@ impl embassy_net::Driver for EthernetEmbassyNet {
type TxToken<'a>
where
Self: 'a,
= EmbassyNetTxToken;
= EmbassyNetTxToken<'a>;
fn receive(
&mut self,
cx: &mut core::task::Context,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
RX_WAKER.register(cx.waker());
None
}
fn transmit(&mut self, cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
TX_WAKER.register(cx.waker());
if self.tx_descr.full() {
return None;
}
Some(EmbassyNetTxToken {
descr_list: &mut self.tx_descr,
tx_bufs: &mut self.tx_bufs,
})
}
fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net::driver::LinkState {
fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net_driver::LinkState {
self.link_state
}
fn capabilities(&self) -> embassy_net::driver::Capabilities {
embassy_net::driver::Capabilities {
max_transmission_unit: super::MTU,
max_burst_size: Some(self.burst_size),
checksum: embassy_net::driver::ChecksumCapabilities {
ipv4: true,
udp: true,
tcp: true,
icmpv4: true,
icmpv6: true,
},
}
fn capabilities(&self) -> embassy_net_driver::Capabilities {
let mut capabilities = embassy_net_driver::Capabilities::default();
capabilities.max_transmission_unit = super::MTU;
capabilities.max_burst_size = Some(self.burst_size);
capabilities.checksum.ipv4 = embassy_net_driver::Checksum::Both;
capabilities.checksum.udp = embassy_net_driver::Checksum::Both;
capabilities.checksum.tcp = embassy_net_driver::Checksum::Both;
capabilities.checksum.icmpv4 = embassy_net_driver::Checksum::None;
capabilities.checksum.icmpv6 = embassy_net_driver::Checksum::None;
capabilities
}
fn hardware_address(&self) -> embassy_net::driver::HardwareAddress {
embassy_net::driver::HardwareAddress::Ethernet(self.mac_addr)
fn hardware_address(&self) -> embassy_net_driver::HardwareAddress {
embassy_net_driver::HardwareAddress::Ethernet(self.mac_addr)
}
}

View File

@@ -15,6 +15,10 @@ pub mod embassy_net;
pub const MTU: usize = 1536;
pub const MAX_MDC_SPEED: Hertz = Hertz::from_raw(2_500_000);
#[repr(align(32))]
#[derive(Debug, Clone, Copy)]
pub struct AlignedBuffer(pub [u8; MTU]);
#[cfg(not(feature = "7z010-7z007s-clg225"))]
use crate::gpio::mio::{
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,

View File

@@ -9,6 +9,7 @@ pub use super::shared::Ownership;
/// These descriptors are shared between software and hardware and contain information
/// related to frame reception.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Descriptor {
/// The first word of the descriptor which is the byte address of the buffer.
pub word0: u32,
@@ -32,6 +33,9 @@ pub enum TransmitChecksumGenerationStatus {
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug, PartialEq, Eq)]
pub struct Word1 {
/// The ownership bit must be set to [Ownership::Hardware] if a frame should be transmitted.
///
/// The controller will set this to [Ownership::Software] once the frame has been transmitted.
#[bit(31, rw)]
ownership: Ownership,
#[bit(30, rw)]
@@ -66,22 +70,39 @@ impl Descriptor {
self.word1.set_ownership(ownership);
}
#[inline]
pub fn ownership(&self) -> Ownership {
self.word1.ownership()
}
/// Set the wrap bit, which should be done for the last descriptor in the descriptor list.
#[inline]
pub fn set_wrap_bit(&mut self) {
self.word1.set_wrap(true);
}
#[inline]
pub fn set_tx_transfer_addr_unchecked(&mut self, addr: u32) {
self.word0 = addr;
}
/// Set the information for a transfer.
pub fn set_tx_transfer_info(
pub fn setup_tx_transfer_unchecked(
&mut self,
addr: u32,
tx_len: u14,
last_buffer: bool,
no_crc_generation: bool,
) {
self.word1.set_tx_len(tx_len);
self.word1.set_last_buffer(last_buffer);
self.word1.set_no_crc_generation(no_crc_generation);
self.set_tx_transfer_addr_unchecked(addr);
// Perform the read-modify-write sequence manually to ensure a minimum of reads/writes
// for the uncached memory.
let mut word1 = Word1::new_with_raw_value(self.word1.raw_value());
word1.set_tx_len(tx_len);
word1.set_last_buffer(last_buffer);
word1.set_no_crc_generation(no_crc_generation);
word1.set_ownership(Ownership::Hardware);
self.word1 = word1;
}
}
@@ -92,19 +113,72 @@ impl Default for Descriptor {
}
}
pub struct DescriptorListRef<'a>(&'a mut [Descriptor]);
#[derive(Debug)]
pub struct DescriptorList<'a> {
list: &'a mut [Descriptor],
/// The head index is used to handle the transmission of new frames.
tx_idx: usize,
/// The tail index is used to track the progress of active transmissions.
busy_idx: usize,
}
impl<'a> DescriptorListRef<'a> {
#[inline]
pub fn new(descriptor: &'a mut [Descriptor]) -> Self {
Self(descriptor)
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("tx error: {self:?}")]
pub struct TxError {
retry_limit_exceeded: bool,
late_collisions: bool,
ahb_error: bool,
checksum_generation: Option<TransmitChecksumGenerationStatus>,
}
impl TxError {
pub fn from_word1(word1: &Word1) -> Self {
Self {
retry_limit_exceeded: word1.retry_limit_exceeded(),
late_collisions: word1.late_collision(),
ahb_error: word1.transmit_frame_corruption_ahb_error(),
checksum_generation: if word1.checksum_status()
== TransmitChecksumGenerationStatus::NoError
{
None
} else {
Some(word1.checksum_status())
},
}
}
}
impl DescriptorListRef<'_> {
pub enum IncrementResult {
Busy,
Ok,
}
impl<'a> DescriptorList<'a> {
#[inline]
pub fn new(descr_list: &'a mut [Descriptor]) -> Self {
Self {
list: descr_list,
tx_idx: 0,
busy_idx: 0,
}
}
}
pub enum BusyHandlingResult {
/// Handled a descriptor slot where a TX error has occured.
TxError(TxError),
/// Handled one busy descriptor slot. More calls to this function are required to handle
/// all busy slots.
SlotHandled,
/// The busy index has caught up to the transmission index (all frames have been sent), or
/// the busy index is at an active transmission index.
Complete,
}
impl DescriptorList<'_> {
#[inline]
pub fn base_ptr(&self) -> *const Descriptor {
self.0.as_ptr()
self.list.as_ptr()
}
#[inline]
@@ -112,10 +186,83 @@ impl DescriptorListRef<'_> {
self.base_ptr() as u32
}
pub fn init(&mut self) {
for desc in self.0.iter_mut() {
desc.set_ownership(Ownership::Hardware);
pub fn init_or_reset(&mut self) {
self.tx_idx = 0;
self.busy_idx = 0;
for desc in self.list.iter_mut() {
desc.set_ownership(Ownership::Software);
}
self.0.last_mut().unwrap().set_wrap_bit();
self.list.last_mut().unwrap().set_wrap_bit();
}
#[inline]
pub fn increment_tx_idx(&mut self) {
self.tx_idx = (self.tx_idx + 1) % self.list.len();
}
/// Increment the busy index without checking whether it has become
/// larger than the transmission index.
#[inline]
pub fn increment_busy_idx_unchecked(&mut self) {
self.busy_idx = (self.busy_idx + 1) % self.list.len();
}
#[inline]
pub fn full(&self) -> bool {
self.list[self.tx_idx].ownership() == Ownership::Hardware
}
/// Check whether a tranfer has completed and handles the descriptor accordingly.
///
/// This should be called continuosly when a TX error or a TX completion interrupt has
/// occured until it returns [BusyHandlingResult::Complete].
pub fn check_and_handle_completed_transfer(&mut self) -> BusyHandlingResult {
let word1 = self.list[self.busy_idx].word1;
if word1.ownership() == Ownership::Hardware || self.busy_idx == self.tx_idx {
return BusyHandlingResult::Complete;
}
if word1.transmit_frame_corruption_ahb_error()
|| word1.retry_limit_exceeded()
|| word1.checksum_status() != TransmitChecksumGenerationStatus::NoError
|| word1.late_collision()
{
return BusyHandlingResult::TxError(TxError::from_word1(&word1));
}
self.list[self.busy_idx].word1 = Word1::builder()
.with_ownership(Ownership::Software)
.with_wrap(word1.wrap())
.with_retry_limit_exceeded(false)
.with_transmit_frame_corruption_ahb_error(false)
.with_late_collision(false)
.with_checksum_status(TransmitChecksumGenerationStatus::NoError)
.with_no_crc_generation(false)
.with_last_buffer(false)
.with_tx_len(u14::new(0))
.build();
self.increment_busy_idx_unchecked();
return BusyHandlingResult::SlotHandled;
}
#[inline]
pub fn current_tx_idx(&self) -> usize {
self.tx_idx
}
/// Prepare a transfer without checking whether that particular descriptor slot is used by
/// the hardware.
pub fn prepare_transfer_unchecked(
&mut self,
addr: u32,
tx_len: u14,
last_buffer: bool,
no_crc_generation: bool,
) {
self.list[self.tx_idx].setup_tx_transfer_unchecked(
addr,
tx_len,
last_buffer,
no_crc_generation,
);
self.increment_tx_idx();
}
}