Skip to content

Commit c70903a

Browse files
committed
Added support for reading/writing ethernet packets + finished init. Still WIP
1 parent 6038b5e commit c70903a

File tree

1 file changed

+274
-3
lines changed

1 file changed

+274
-3
lines changed

targets/core/nxp/lpc178x/emac.hpp

Lines changed: 274 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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>
@@ -14,6 +16,66 @@
1416
namespace 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

Comments
 (0)