litex/
liteeth.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! LiteX LiteEth peripheral
6//!
7//! The hardware source and any documentation can be found in the
8//! [LiteEth Git
9//! repository](https://github.com/enjoy-digital/liteeth).
10
11use crate::event_manager::LiteXEventManager;
12use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
13use core::cell::Cell;
14use core::slice;
15use kernel::debug;
16use kernel::utilities::cells::{OptionalCell, TakeCell};
17use kernel::utilities::StaticRef;
18use kernel::ErrorCode;
19
20// Both events have the same index since they are located on different
21// event manager instances
22const LITEETH_TX_EVENT: usize = 0;
23const LITEETH_RX_EVENT: usize = 0;
24
25type LiteEthRXEV<'a, R> = LiteXEventManager<
26    'a,
27    u8,
28    <R as LiteXSoCRegisterConfiguration>::ReadOnly8,
29    <R as LiteXSoCRegisterConfiguration>::ReadWrite8,
30    <R as LiteXSoCRegisterConfiguration>::ReadWrite8,
31>;
32type LiteEthTXEV<'a, R> = LiteEthRXEV<'a, R>;
33
34#[repr(C)]
35pub struct LiteEthMacRegisters<R: LiteXSoCRegisterConfiguration> {
36    /// ETHMAC_SRAM_WRITER_SLOT
37    rx_slot: R::ReadOnly8,
38    /// ETHMAC_SRAM_WRITER_LENGTH
39    rx_length: R::ReadOnly32,
40    /// ETHMAC_SRAM_WRITER_ERRORS
41    rx_errors: R::ReadOnly32,
42    /// ETHMAC_SRAM_WRITER_EV
43    rx_ev_status: R::ReadOnly8,
44    rx_ev_pending: R::ReadWrite8,
45    rx_ev_enable: R::ReadWrite8,
46
47    /// ETHMAC_SRAM_READER_START
48    tx_start: R::ReadWrite8,
49    tx_ready: R::ReadOnly8,
50    tx_level: R::ReadOnly8,
51    tx_slot: R::ReadWrite8,
52    /// ETHMAC_SRAM_READER_LENGTH
53    tx_length: R::ReadWrite16,
54    /// ETHMAC_SRAM_READER_EV
55    tx_ev_status: R::ReadOnly8,
56    tx_ev_pending: R::ReadWrite8,
57    tx_ev_enable: R::ReadWrite8,
58
59    /// ETHMAC_PREAMBLE_CRC
60    preamble_crc: R::ReadWrite8,
61    /// ETHMAC_PREAMBLE_ERRORS
62    preamble_errors: R::ReadOnly8,
63    /// ETHMAC_CRC_ERRORS
64    crc_errors: R::ReadOnly32,
65}
66
67impl<R: LiteXSoCRegisterConfiguration> LiteEthMacRegisters<R> {
68    fn rx_ev(&self) -> LiteEthRXEV<'_, R> {
69        LiteEthRXEV::<R>::new(&self.rx_ev_status, &self.rx_ev_pending, &self.rx_ev_enable)
70    }
71
72    fn tx_ev(&self) -> LiteEthTXEV<'_, R> {
73        LiteEthTXEV::<R>::new(&self.tx_ev_status, &self.tx_ev_pending, &self.tx_ev_enable)
74    }
75}
76
77pub trait LiteEthClient {
78    fn tx_done(&self, rc: Result<(), ErrorCode>, packet_buffer: &'static mut [u8]);
79    fn rx_packet(&self, packet: &'static mut [u8], len: usize);
80}
81
82pub struct LiteEth<'a, R: LiteXSoCRegisterConfiguration> {
83    mac_regs: StaticRef<LiteEthMacRegisters<R>>,
84    mac_memory_base: usize,
85    mac_memory_len: usize,
86    slot_size: usize,
87    rx_slots: usize,
88    tx_slots: usize,
89    client: OptionalCell<&'a dyn LiteEthClient>,
90    tx_packet: TakeCell<'static, [u8]>,
91    rx_buffer: TakeCell<'static, [u8]>,
92    initialized: Cell<bool>,
93}
94
95impl<'a, R: LiteXSoCRegisterConfiguration> LiteEth<'a, R> {
96    pub unsafe fn new(
97        mac_regs: StaticRef<LiteEthMacRegisters<R>>,
98        mac_memory_base: usize,
99        mac_memory_len: usize,
100        slot_size: usize,
101        rx_slots: usize,
102        tx_slots: usize,
103        rx_buffer: &'static mut [u8],
104    ) -> LiteEth<'a, R> {
105        LiteEth {
106            mac_regs,
107            mac_memory_base,
108            mac_memory_len,
109            slot_size,
110            rx_slots,
111            tx_slots,
112            client: OptionalCell::empty(),
113            tx_packet: TakeCell::empty(),
114            rx_buffer: TakeCell::new(rx_buffer),
115            initialized: Cell::new(false),
116        }
117    }
118
119    pub fn set_client(&self, client: &'a dyn LiteEthClient) {
120        self.client.set(client);
121    }
122
123    pub fn initialize(&self) {
124        // Sanity check the memory parameters
125        //
126        // Technically the constructor is unsafe as it will (over the
127        // lifetime of this struct) "cast" the raw mac_memory pointer
128        // (and slot offsets) into pointers and access them
129        // directly. However checking it at runtime once seems like a
130        // good idea.
131        assert!(
132            (self.rx_slots + self.tx_slots) * self.slot_size <= self.mac_memory_len,
133            "LiteEth: slots would exceed assigned MAC memory area"
134        );
135
136        assert!(self.rx_slots > 0, "LiteEth: no RX slot");
137        assert!(self.tx_slots > 0, "LiteEth: no TX slot");
138
139        // Clear any pending EV events
140        self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
141        self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
142
143        // Disable TX events (only enabled when a packet is sent)
144        self.mac_regs.tx_ev().disable_event(LITEETH_TX_EVENT);
145
146        // Enable RX events
147        self.mac_regs.rx_ev().enable_event(LITEETH_RX_EVENT);
148
149        self.initialized.set(true);
150    }
151
152    unsafe fn get_slot_buffer(&self, tx: bool, slot_id: usize) -> Option<&mut [u8]> {
153        if (tx && slot_id > self.tx_slots) || (!tx && slot_id > self.rx_slots) {
154            return None;
155        }
156
157        let slots_offset = if tx {
158            self.mac_memory_base + self.slot_size * self.rx_slots
159        } else {
160            self.mac_memory_base
161        };
162
163        let slot_addr = slots_offset + slot_id * self.slot_size;
164        Some(slice::from_raw_parts_mut(
165            slot_addr as *mut u8,
166            self.slot_size,
167        ))
168    }
169
170    pub fn return_rx_buffer(&self, rx_buffer: &'static mut [u8]) {
171        // Assert that we won't overwrite a buffer
172        assert!(
173            self.rx_buffer.is_none(),
174            "LiteEth: return RX buffer while one is registered"
175        );
176
177        // Put the buffer back
178        self.rx_buffer.replace(rx_buffer);
179
180        // In case we received a packet RX interrupt but couldn't
181        // handle it due to the missing buffer, reenable RX interrupts
182        self.mac_regs.rx_ev().enable_event(LITEETH_RX_EVENT);
183    }
184
185    fn rx_interrupt(&self) {
186        // Check whether we have a buffer to read the packet into. If
187        // not, we must disable, but not clear the event and enable it
188        // again as soon as we get the buffer back from the client
189        if self.rx_buffer.is_none() {
190            self.mac_regs.rx_ev().disable_event(LITEETH_RX_EVENT);
191        } else {
192            // Get the buffer first to be able to check the length
193            let rx_buffer = self.rx_buffer.take().unwrap();
194
195            // Get the frame length. If it exceeds the length of the
196            // rx_buffer, discard the packet, put the buffer back
197            let pkt_len = self.mac_regs.rx_length.get() as usize;
198            if pkt_len > rx_buffer.len() {
199                debug!("LiteEth: discarding ethernet packet with len {}", pkt_len);
200
201                // Acknowledge the interrupt so that the HW may use the slot again
202                self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
203
204                // Replace the buffer
205                self.rx_buffer.replace(rx_buffer);
206            } else {
207                // Obtain the packet slot id
208                let slot_id: usize = self.mac_regs.rx_slot.get().into();
209
210                // Get the slot buffer reference
211                let slot = unsafe {
212                    self.get_slot_buffer(false, slot_id).unwrap() // Unwrap fail = LiteEth: invalid RX slot id
213                };
214
215                // Copy the packet into the buffer
216                rx_buffer[..pkt_len].copy_from_slice(&slot[..pkt_len]);
217
218                // Since all data is copied, acknowledge the interrupt
219                // so that the slot is ready for use again
220                self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
221
222                self.client
223                    .map(move |client| client.rx_packet(rx_buffer, pkt_len));
224            }
225        }
226    }
227
228    /// Transmit an ethernet packet over the interface
229    ///
230    /// For now this will only use a single slot on the interface and
231    /// is therefore blocking. A client must wait until a callback to
232    /// `tx_done` prior to sending a new packet.
233    pub fn transmit(
234        &self,
235        packet: &'static mut [u8],
236        len: usize,
237    ) -> Result<(), (Result<(), ErrorCode>, &'static mut [u8])> {
238        if packet.len() < len || len > u16::MAX as usize {
239            return Err((Err(ErrorCode::INVAL), packet));
240        }
241
242        if self.tx_packet.is_some() {
243            return Err((Err(ErrorCode::BUSY), packet));
244        }
245
246        let slot = unsafe { self.get_slot_buffer(true, 0) }.unwrap(); // Unwrap fail = LiteEth: no TX slot
247        if slot.len() < len {
248            return Err((Err(ErrorCode::SIZE), packet));
249        }
250
251        // Copy the packet into the slot HW buffer
252        slot[..len].copy_from_slice(&packet[..len]);
253
254        // Put the currently transmitting packet into the designated
255        // TakeCell
256        self.tx_packet.replace(packet);
257
258        // Set the slot and packet length
259        self.mac_regs.tx_slot.set(0);
260        self.mac_regs.tx_length.set(len as u16);
261
262        // Wait for the device to be ready to transmit
263        while self.mac_regs.tx_ready.get() == 0 {}
264
265        // Enable TX interrupts
266        self.mac_regs.tx_ev().enable_event(LITEETH_TX_EVENT);
267
268        // Start the transmission
269        self.mac_regs.tx_start.set(1);
270
271        Ok(())
272    }
273
274    fn tx_interrupt(&self) {
275        // Deassert the interrupt, but can be left enabled
276        self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
277
278        if self.tx_packet.is_none() {
279            debug!("LiteEth: tx interrupt called without tx_packet set");
280        }
281
282        // We use only one slot, so this event is unambiguous
283        let packet = self.tx_packet.take().unwrap(); // Unwrap fail = LiteEth: TakeCell empty in tx callback
284        self.client
285            .map(move |client| client.tx_done(Ok(()), packet));
286    }
287
288    pub fn service_interrupt(&self) {
289        // The interrupt could've been generated by both a packet
290        // being received or finished transmitting. Check and handle
291        // both cases
292
293        if self.mac_regs.rx_ev().event_asserted(LITEETH_RX_EVENT) {
294            self.rx_interrupt();
295        }
296
297        if self.mac_regs.tx_ev().event_asserted(LITEETH_TX_EVENT) {
298            self.tx_interrupt();
299        }
300    }
301}