11#ifndef KLIB_NXP_LPC178X_EMAC_HPP
22#define KLIB_NXP_LPC178X_EMAC_HPP
33
4- #include < klib/delay.hpp>
4+ #include < span>
5+ #include < algorithm>
56
7+ #include < klib/delay.hpp>
68#include < klib/multispan.hpp>
79#include < klib/io/core_clock.hpp>
810#include < klib/io/bus/ethernet.hpp>
1416namespace klib ::core::lpc178x::io {
1517 template <typename Emac, klib::io::ethernet::mii Mii, uint32_t MaxFrameLength = 1536 >
1618 class emac {
19+ public:
20+ /* *
21+ * @brief Packet descriptor
22+ *
23+ */
24+ struct descriptor {
25+ // pointer to the data buffer
26+ uint8_t * packet;
27+
28+ // control of the descriptor
29+ uint32_t control;
30+ };
31+
32+ static_assert (sizeof (descriptor) == (2 * sizeof (uint32_t )), " Invalid RX descriptor size" );
33+
34+ /* *
35+ * @brief Rx hash word
36+ *
37+ */
38+ struct receive_hash {
39+ // source address hash crc
40+ uint32_t source_address_crc: 9 ;
41+
42+ // unused bits
43+ uint32_t padding0: 7 ;
44+
45+ // source address hash crc
46+ uint32_t destination_address_crc: 9 ;
47+
48+ // unused bits
49+ uint32_t padding1: 7 ;
50+ };
51+
52+ static_assert (sizeof (receive_hash) == sizeof (uint32_t ), " Invalid RX hash size" );
53+
54+ /* *
55+ * @brief Rx status
56+ *
57+ */
58+ struct receive_status {
59+ // the status info of the rx
60+ uint32_t info;
61+
62+ // 2 concatenated 9 bit hashes
63+ receive_hash hash_crc;
64+ };
65+
66+ static_assert (sizeof (receive_status) == (2 * sizeof (uint32_t )), " Invalid RX status size" );
67+
68+ /* *
69+ * @brief Tx status
70+ *
71+ */
72+ struct transmit_status {
73+ // the status info of the rx
74+ uint32_t info;
75+ };
76+
77+ static_assert (sizeof (transmit_status) == sizeof (uint32_t ), " Invalid TX status size" );
78+
1779 protected:
1880 // make sure we have a valid media independent interface we support
1981 static_assert (
@@ -25,6 +87,12 @@ namespace klib::core::lpc178x::io {
2587 MaxFrameLength <= 0xffff , " Invalid max frame length"
2688 );
2789
90+ /* *
91+ * @brief Get the clk divider for the MIIM
92+ *
93+ * @param clock
94+ * @return constexpr uint32_t
95+ */
2896 constexpr static uint32_t get_clk_divider (uint32_t clock) {
2997 // all the available dividers and frequencies
3098 constexpr std::array<std::array<uint8_t , 2 >, 15 > dividers = {{
@@ -82,7 +150,64 @@ namespace klib::core::lpc178x::io {
82150 }
83151
84152 public:
85- static void init (const klib::io::ethernet::mac_address& mac) {
153+ /* *
154+ * @brief Init the lpc178x emac peripheral.
155+ *
156+ * @note Emac only has access to the peripheral ram. Providing a span to the
157+ * main sram will not work
158+ *
159+ * @param mac
160+ * @param tx_descriptor
161+ * @param tx_status
162+ * @param tx_data
163+ * @param rx_descriptor
164+ * @param rx_status
165+ * @param rx_data
166+ * @return result
167+ */
168+ static bool init (
169+ const klib::io::ethernet::mac_address& mac,
170+ const std::span<descriptor>& tx_descriptor, const std::span<transmit_status>& tx_status, const std::span<uint8_t >& tx_data,
171+ const std::span<descriptor>& rx_descriptor, const std::span<receive_status>& rx_status, const std::span<uint8_t >& rx_data
172+ )
173+ {
174+ // make sure all the buffers are not empty
175+ if ((!rx_descriptor.size ()) || (!rx_status.size ()) ||
176+ (!tx_descriptor.size ()) || (!tx_status.size ()))
177+ {
178+ return false ;
179+ }
180+
181+ // make sure the buffers are not empty
182+ if (!tx_data.size () || !rx_data.size ()) {
183+ return false ;
184+ }
185+
186+ // make sure all the tx status and descriptor size match
187+ if (tx_descriptor.size () != tx_status.size ()) {
188+ return false ;
189+ }
190+
191+ // make sure all the rx status and descriptor size match
192+ if (rx_descriptor.size () != rx_status.size ()) {
193+ return false ;
194+ }
195+
196+ // the rx status needs to be 8 byte alligned
197+ if (reinterpret_cast <uint32_t >(rx_status.data ()) & 0x7 ) {
198+ return false ;
199+ }
200+
201+ // check for the other allignments
202+ if ((reinterpret_cast <uint32_t >(tx_descriptor.data ()) & 0x3 ) ||
203+ (reinterpret_cast <uint32_t >(tx_status.data ()) & 0x3 ) ||
204+ (reinterpret_cast <uint32_t >(tx_data.data ()) & 0x3 ) ||
205+ (reinterpret_cast <uint32_t >(rx_descriptor.data ()) & 0x3 ) ||
206+ (reinterpret_cast <uint32_t >(rx_data.data ()) & 0x3 ))
207+ {
208+ return false ;
209+ }
210+
86211 // enable power to the peripheral
87212 target::io::power_control::enable<Emac>();
88213
@@ -175,7 +300,65 @@ namespace klib::core::lpc178x::io {
175300 Emac::port->SA1 = mac[2 ] << 8 | mac[3 ];
176301 Emac::port->SA2 = mac[4 ] << 8 | mac[5 ];
177302
178- // TODO: add descriptor initalisation
303+ // get the rx info
304+ const uint32_t rx_count = rx_descriptor.size ();
305+ const uint32_t rx_offset = rx_data.size () / rx_count;
306+
307+ // init the receive descriptor
308+ for (uint32_t i = 0 ; i < rx_count; i++) {
309+ // set the packet pointer
310+ rx_descriptor[i].packet = rx_data.data () + (rx_offset * i);
311+
312+ // set the control register (set the size + enable the rx done interrupt)
313+ rx_descriptor[i].control = ((0x1 << 31 ) | ((rx_offset - 1 ) & 0x7ff ));
314+
315+ // clear the status
316+ rx_status[i].info = 0 ;
317+ rx_status[i].hash_crc = {};
318+ }
319+
320+ // store the rx descriptors in the emac registers
321+ Emac::port->RXDESCRIPTOR = reinterpret_cast <uint32_t >(rx_descriptor.data ());
322+ Emac::port->RXSTATUS = reinterpret_cast <uint32_t >(rx_status.data ());
323+ Emac::port->RXDESCRIPTORNUMBER = rx_count - 1 ;
324+ Emac::port->RXCONSUMEINDEX = 0 ;
325+
326+ // get the tx info
327+ const uint32_t tx_count = tx_descriptor.size ();
328+ const uint32_t tx_offset = tx_data.size () / tx_count;
329+
330+ // init the transmit descriptor
331+ for (uint32_t i = 0 ; i < tx_count; i++) {
332+ // set the packet pointer
333+ tx_descriptor[i].packet = tx_data.data () + (tx_offset * i);
334+
335+ // clear the control register
336+ tx_descriptor[i].control = 0 ;
337+
338+ // clear the status
339+ tx_status[i].info = 0 ;
340+ }
341+
342+ // store the rx descriptors in the emac registers
343+ Emac::port->TXDESCRIPTOR = reinterpret_cast <uint32_t >(tx_descriptor.data ());
344+ Emac::port->TXSTATUS = reinterpret_cast <uint32_t >(tx_status.data ());
345+ Emac::port->TXDESCRIPTORNUMBER = tx_count - 1 ;
346+ Emac::port->TXPRODUCEINDEX = 0 ;
347+
348+ // receive broadcast and perfect match packets
349+ Emac::port->RXFILTERCTRL = (0x1 << 1 ) | (0x1 << 5 );
350+
351+ // Enable interrupts MAC Module Control Interrupt Enable Register
352+ ETHERNET->INTENABLE = (0x1 << 3 ) | (0x1 << 7 );
353+
354+ // Reset all ethernet interrupts in MAC module
355+ Emac::port->INTCLEAR = 0xffffffff ;
356+
357+ // Finally enable receive and transmit mode in ethernet core
358+ ETHERNET->COMMAND |= (0x1 | 0x2 );
359+ ETHERNET->MAC1 |= 0x1 ;
360+
361+ return true ;
179362 }
180363
181364 /* *
@@ -213,6 +396,94 @@ namespace klib::core::lpc178x::io {
213396 );
214397 }
215398
399+ /* *
400+ * @brief Returns if data is available in a rx buffer
401+ *
402+ * @return true
403+ * @return false
404+ */
405+ static bool has_data () {
406+ // return if there is a difference between the rx produce and consume index
407+ return Emac::port->RXPRODUCEINDEX != Emac::port->RXCONSUMEINDEX ;
408+ }
409+
410+ /* *
411+ * @brief Reads data into the provided buffer
412+ *
413+ * @param rx
414+ * @return uint32_t
415+ */
416+ static uint32_t read (const std::span<uint16_t > rx, const uint32_t offset) {
417+ // get the consume index
418+ const uint32_t index = Emac::port->RXCONSUMEINDEX ;
419+
420+ // get the amount of bytes we received in this buffer
421+ const uint32_t length = (klib::min (
422+ static_cast <uint32_t >(
423+ (reinterpret_cast <receive_status*>(Emac::port->RXSTATUS )[index].info ) & 0x7ff
424+ ),
425+ rx.size_bytes ()
426+ ));
427+
428+ // get the pointer to the buffer
429+ const uint8_t * buffer = (
430+ reinterpret_cast <descriptor*>(Emac::port->RXDESCRIPTOR )[index].packet
431+ );
432+
433+ // copy all the data from the buffer into the user provided buffer
434+ std::copy_n (buffer + offset, length, reinterpret_cast <uint8_t *>(rx.data ()));
435+
436+ // return the full length of the packet
437+ return length;
438+ }
439+
440+ /* *
441+ * @brief Release a received frame
442+ *
443+ */
444+ static void release () {
445+ // get the consume index
446+ const uint32_t index = Emac::port->RXCONSUMEINDEX ;
447+
448+ // move to the next index after reading
449+ const auto max = (Emac::port->RXDESCRIPTORNUMBER + 1 );
450+ const auto next = index + 1 ;
451+
452+ // limit the index to the max indexes we have
453+ Emac::port->RXCONSUMEINDEX = next % max;
454+ }
455+
456+ /* *
457+ * @brief Write into a tx buffer
458+ *
459+ * @param tx
460+ * @return result
461+ */
462+ static bool write (const std::span<uint16_t > tx) {
463+ // get the produce index
464+ const uint32_t index = Emac::port->TXPRODUCEINDEX ;
465+
466+ // get a reference to the descriptor
467+ descriptor& desc = (
468+ reinterpret_cast <descriptor*>(Emac::port->TXDESCRIPTOR )[index]
469+ );
470+
471+ // write the data to the buffer
472+ std::copy_n (tx.data (), tx.size (), reinterpret_cast <uint16_t *>(desc.packet ));
473+
474+ // write the size to the control
475+ desc.control = (0x1 << 30 ) | tx.size_bytes ();
476+
477+ // move to the next index after writing
478+ const auto max = (Emac::port->TXDESCRIPTORNUMBER + 1 );
479+ const auto next = index + 1 ;
480+
481+ // limit the index to the max indexes we have
482+ Emac::port->TXPRODUCEINDEX = next % max;
483+
484+ return true ;
485+ }
486+
216487 public:
217488 // miim interface to write to the phy
218489 class miim {
0 commit comments