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 [LiteEth Git
8//! repository](https://github.com/enjoy-digital/liteeth).
9
10use crate::event_manager::LiteXEventManager;
11use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
12use core::cell::Cell;
13use core::slice;
14use kernel::debug;
15use kernel::hil::ethernet::{EthernetAdapterDatapath, EthernetAdapterDatapathClient};
16use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
17use kernel::utilities::StaticRef;
18use kernel::ErrorCode;
19
20// Both events have the same index since they are located on different event
21// 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    /// ETHMAC_SRAM_READER_READY
50    tx_ready: R::ReadOnly8,
51    /// ETHMAC_SRAM_READER_LEVEL
52    tx_level: R::ReadOnly8,
53    /// ETHMAC_SRAM_READER_SLOT
54    tx_slot: R::ReadWrite8,
55    /// ETHMAC_SRAM_READER_LENGTH
56    tx_length: R::ReadWrite16,
57    /// ETHMAC_SRAM_READER_EV
58    tx_ev_status: R::ReadOnly8,
59    tx_ev_pending: R::ReadWrite8,
60    tx_ev_enable: R::ReadWrite8,
61
62    /// ETHMAC_PREAMBLE_CRC
63    preamble_crc: R::ReadWrite8,
64    /// ETHMAC_PREAMBLE_ERRORS
65    preamble_errors: R::ReadOnly8,
66    /// ETHMAC_CRC_ERRORS
67    crc_errors: R::ReadOnly32,
68}
69
70impl<R: LiteXSoCRegisterConfiguration> LiteEthMacRegisters<R> {
71    fn rx_ev(&self) -> LiteEthRXEV<'_, R> {
72        LiteEthRXEV::<R>::new(&self.rx_ev_status, &self.rx_ev_pending, &self.rx_ev_enable)
73    }
74
75    fn tx_ev(&self) -> LiteEthTXEV<'_, R> {
76        LiteEthTXEV::<R>::new(&self.tx_ev_status, &self.tx_ev_pending, &self.tx_ev_enable)
77    }
78}
79
80pub struct LiteEth<'a, const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> {
81    mac_regs: StaticRef<LiteEthMacRegisters<R>>,
82    mac_memory_base: usize,
83    mac_memory_len: usize,
84    slot_size: usize,
85    rx_slots: usize,
86    tx_slots: usize,
87    client: OptionalCell<&'a dyn EthernetAdapterDatapathClient>,
88    tx_frame: TakeCell<'static, [u8]>,
89    tx_frame_info: MapCell<[(usize, u16); MAX_TX_SLOTS]>,
90    initialized: Cell<bool>,
91}
92
93impl<const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> LiteEth<'_, MAX_TX_SLOTS, R> {
94    pub unsafe fn new(
95        mac_regs: StaticRef<LiteEthMacRegisters<R>>,
96        mac_memory_base: usize,
97        mac_memory_len: usize,
98        slot_size: usize,
99        rx_slots: usize,
100        tx_slots: usize,
101    ) -> Self {
102        LiteEth {
103            mac_regs,
104            mac_memory_base,
105            mac_memory_len,
106            slot_size,
107            rx_slots,
108            tx_slots,
109            client: OptionalCell::empty(),
110            tx_frame: TakeCell::empty(),
111            tx_frame_info: MapCell::new([(0, 0); MAX_TX_SLOTS]),
112            initialized: Cell::new(false),
113        }
114    }
115
116    pub fn initialize(&self) {
117        // Sanity check the memory parameters
118        //
119        // Technically the constructor is unsafe as it will (over the lifetime
120        // of this struct) "cast" the raw mac_memory pointer (and slot offsets)
121        // into pointers and access them directly. However checking it at
122        // runtime once seems like a good idea.
123        assert!(
124            (self.rx_slots + self.tx_slots) * self.slot_size <= self.mac_memory_len,
125            "LiteEth: slots would exceed assigned MAC memory area"
126        );
127
128        assert!(self.rx_slots > 0, "LiteEth: no RX slot");
129        assert!(self.tx_slots > 0, "LiteEth: no TX slot");
130
131        // Sanity check the length of the frame_info buffer, must be able to fit
132        // all `tx_slots` requested at runtime.
133        assert!(
134            MAX_TX_SLOTS >= self.tx_slots,
135            "LiteEth: MAX_TX_SLOTS ({}) must be larger or equal to tx_slots ({})",
136            MAX_TX_SLOTS,
137            self.tx_slots,
138        );
139
140        // Disable TX events (first enabled when a frame is sent)
141        self.mac_regs.tx_ev().disable_event(LITEETH_TX_EVENT);
142
143        // Clear all pending RX & TX events (there might be leftovers from the
144        // bootloader or a reboot, for which we don't want to generate an event)
145        //
146        // This is not sufficient to guarantee that all events will be cleared
147        // then. A frame could still be in reception or transmit.
148        while self.mac_regs.rx_ev().event_pending(LITEETH_RX_EVENT) {
149            self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
150        }
151        while self.mac_regs.tx_ev().event_pending(LITEETH_TX_EVENT) {
152            self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
153        }
154
155        self.initialized.set(true);
156    }
157
158    unsafe fn get_slot_buffer(&self, tx: bool, slot_id: usize) -> Option<&mut [u8]> {
159        if (tx && slot_id > self.tx_slots) || (!tx && slot_id > self.rx_slots) {
160            return None;
161        }
162
163        let slots_offset = if tx {
164            self.mac_memory_base + self.slot_size * self.rx_slots
165        } else {
166            self.mac_memory_base
167        };
168
169        let slot_addr = slots_offset + slot_id * self.slot_size;
170        Some(slice::from_raw_parts_mut(
171            slot_addr as *mut u8,
172            self.slot_size,
173        ))
174    }
175
176    fn rx_interrupt(&self) {
177        // Get the frame length
178        let pkt_len = self.mac_regs.rx_length.get();
179
180        // Obtain the frame slot id
181        let slot_id: usize = self.mac_regs.rx_slot.get().into();
182
183        // Obtain the frame reception timestamp. The `rx_timestamp` register is
184        // optional and disabled by default.
185        //
186        // let timestamp = self.mac_regs.rx_timestamp.get();
187
188        // Get the slot buffer reference
189        let slot = unsafe {
190            self.get_slot_buffer(false, slot_id)
191                .expect("LiteEth: invalid RX slot id")
192        };
193
194        // Give the client read-only access to the frame data
195        self.client
196            .map(|client| client.received_frame(&slot[..(pkt_len as usize)], None));
197
198        // Since all data is copied, acknowledge the interrupt so that the slot
199        // is ready for use again
200        self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
201    }
202
203    fn tx_interrupt(&self) {
204        // Store information about the frame that has been sent (from the return
205        // channel). Uncomment the below lines if hardware timestamping is
206        // enabled and frame TX timestamps are supposed to be recorded.
207        //
208        // let res_slot = self.mac_regs.tx_timestamp_slot.get();
209        // let res_timestamp = self.mac_regs.tx_timestamp.get();
210
211        // Acknowledge the event, removing the tx_res fields from the FIFO
212        self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
213
214        if self.tx_frame.is_none() {
215            debug!("LiteEth: tx interrupt called without tx_frame set");
216        }
217
218        // We use only one slot, so this event is unambiguous
219        let frame = self
220            .tx_frame
221            .take()
222            .expect("LiteEth: TakeCell empty in tx callback");
223
224        // Retrieve the previously stored frame information for this slot.
225        let slot_id = 0; // currently only use one TX slot
226        let (frame_identifier, len) = self
227            .tx_frame_info
228            .map(|pkt_info| pkt_info[slot_id])
229            .unwrap();
230
231        self.client.map(move |client| {
232            client.transmit_frame_done(Ok(()), frame, len, frame_identifier, None)
233        });
234    }
235
236    pub fn service_interrupt(&self) {
237        // The interrupt could've been generated by both a frame being received
238        // or finished transmitting. Check and handle both cases.
239
240        while self.mac_regs.tx_ev().event_asserted(LITEETH_TX_EVENT) {
241            self.tx_interrupt();
242        }
243
244        // `event_asserted` checks that the event is both pending _and_ enabled
245        // (raising a CPU interrupt). This means that reception is enabled, and
246        // we must handle it:
247        while self.mac_regs.rx_ev().event_asserted(LITEETH_RX_EVENT) {
248            self.rx_interrupt();
249        }
250    }
251}
252
253impl<'a, const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> EthernetAdapterDatapath<'a>
254    for LiteEth<'a, MAX_TX_SLOTS, R>
255{
256    fn set_client(&self, client: &'a dyn EthernetAdapterDatapathClient) {
257        self.client.set(client);
258    }
259
260    fn enable_receive(&self) {
261        // Enable RX event interrupts:
262        if !self.initialized.get() {
263            panic!("LiteEth: cannot enable_receive without prior initialization!");
264        }
265
266        self.mac_regs.rx_ev().enable_event(LITEETH_RX_EVENT);
267    }
268
269    fn disable_receive(&self) {
270        // Disable RX event interrupts:
271        self.mac_regs.rx_ev().disable_event(LITEETH_RX_EVENT);
272    }
273
274    /// Transmit an Ethernet frame over the interface
275    ///
276    /// For now this will only use a single slot on the interface and is
277    /// therefore blocking. A client must wait until a callback to `tx_done`
278    /// prior to sending a new frame.
279    fn transmit_frame(
280        &self,
281        frame: &'static mut [u8],
282        len: u16,
283        frame_identifier: usize,
284    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
285        if frame.len() < (len as usize) {
286            return Err((ErrorCode::INVAL, frame));
287        }
288
289        if self.tx_frame.is_some() {
290            return Err((ErrorCode::BUSY, frame));
291        }
292
293        // For now, we always use slot 0
294        let slot_id = 0;
295
296        let slot = unsafe { self.get_slot_buffer(true, slot_id) }.expect("LiteEth: no TX slot");
297        if slot.len() < (len as usize) {
298            return Err((ErrorCode::SIZE, frame));
299        }
300
301        // Set the slot's frame information
302        self.tx_frame_info
303            .map(|pkt_info| {
304                pkt_info[slot_id] = (frame_identifier, len);
305            })
306            .unwrap();
307
308        // Copy the frame into the slot HW buffer
309        slot[..(len as usize)].copy_from_slice(&frame[..(len as usize)]);
310
311        // Put the currently transmitting frame into the designated TakeCell
312        self.tx_frame.replace(frame);
313
314        // Set the slot and frame length
315        self.mac_regs.tx_slot.set(0);
316        self.mac_regs.tx_length.set(len);
317
318        // Wait for the device to be ready to transmit
319        while self.mac_regs.tx_ready.get() == 0 {}
320
321        // Enable TX events
322        self.mac_regs.tx_ev().enable_event(LITEETH_TX_EVENT);
323
324        // Start the transmission
325        self.mac_regs.tx_start.set(1);
326
327        Ok(())
328    }
329}