11#include <platform/concepts/ethernet.hh>
12#include <platform/sunburst/platform-gpio.hh>
13#include <platform/sunburst/v0.2/platform-spi.hh>
17 InterruptName::EthernetInterrupt,
29 static constexpr bool DebugEthernet =
false;
34 static constexpr bool DebugDroppedFrames =
true;
39 static constexpr uint16_t MaxFrameSize = 1500;
44 using Debug = ConditionalDebug<DebugEthernet,
"Ethernet driver">;
49 using DebugFrameDrops =
50 ConditionalDebug<DebugDroppedFrames,
"Ethernet driver">;
56 using Capability = CHERI::Capability<T>;
61 enum class GpioPin : uint8_t
63 EthernetChipSelect = 13,
70 enum class SpiCommand : uint8_t
85 enum class RegisterOffset : uint8_t
87 ChipConfiguration = 0x08,
89 MacAdressMiddle = 0x12,
90 MacAddressHigh = 0x14,
91 OnChipBusControl = 0x20,
93 MemoryBistInfo = 0x24,
98 TransmitControl = 0x70,
99 TransmitStatus = 0x72,
100 ReceiveControl1 = 0x74,
101 ReceiveControl2 = 0x76,
102 TransmitQueueMemoryInfo = 0x78,
103 ReceiveFrameHeaderStatus = 0x7C,
104 ReceiveFrameHeaderByteCount = 0x7E,
105 TransmitQueueCommand = 0x80,
106 ReceiveQueueCommand = 0x82,
107 TransmitFrameDataPointer = 0x84,
108 ReceiveFrameDataPointer = 0x86,
109 ReceiveDurationTimerThreshold = 0x8C,
110 ReceiveDataByteCountThreshold = 0x8E,
111 InterruptEnable = 0x90,
112 InterruptStatus = 0x92,
113 ReceiveFrameCountThreshold = 0x9c,
114 TransmitNextTotalFrameSize = 0x9E,
118 FlowControlLowWatermark = 0xB0,
119 FlowControlHighWatermark = 0xB2,
120 FlowControlOverrunWatermark = 0xB4,
122 ChipGlobalControl = 0xC6,
123 IndirectAccessControl = 0xC8,
124 IndirectAccessDataLow = 0xD0,
125 IndirectAccessDataHigh = 0xD2,
126 PowerManagementEventControl = 0xD4,
127 GoSleepWakeUp = 0xD4,
129 Phy1MiiBasicControl = 0xE4,
130 Phy1MiiBasicStatus = 0xE6,
133 Phy1AutoNegotiationAdvertisement = 0xEC,
134 Phy1AutoNegotiationLinkPartnerAbility = 0xEE,
135 Phy1SpecialControlStatus = 0xF4,
140 using MACAddress = std::array<uint8_t, 6>;
145 enum [[clang::flag_enum]] TransmitControl : uint16_t
147 TransmitEnable = 1 << 0,
148 TransmitCrcEnable = 1 << 1,
149 TransmitPaddingEnable = 1 << 2,
150 TransmitFlowControlEnable = 1 << 3,
151 FlushTransmitQueue = 1 << 4,
152 TransmitChecksumGenerationIp = 1 << 5,
153 TransmitChecksumGenerationTcp = 1 << 6,
154 TransmitChecksumGenerationIcmp = 1 << 8,
160 enum [[clang::flag_enum]] ReceiveControl1 : uint16_t
162 ReceiveEnable = 1 << 0,
163 ReceiveInverseFilter = 1 << 1,
164 ReceiveAllEnable = 1 << 4,
165 ReceiveUnicastEnable = 1 << 5,
166 ReceiveMulticastEnable = 1 << 6,
167 ReceiveBroadcastEnable = 1 << 7,
168 ReceiveMulticastAddressFilteringWithMacAddressEnable = 1 << 8,
169 ReceiveErrorFrameEnable = 1 << 9,
170 ReceiveFlowControlEnable = 1 << 10,
171 ReceivePhysicalAddressFilteringWithMacAddressEnable = 1 << 11,
172 ReceiveIpFrameChecksumCheckEnable = 1 << 12,
173 ReceiveTcpFrameChecksumCheckEnable = 1 << 13,
174 ReceiveUdpFrameChecksumCheckEnable = 1 << 14,
175 FlushReceiveQueue = 1 << 15,
181 enum [[clang::flag_enum]] ReceiveControl2 : uint16_t
183 ReceiveSourceAddressFiltering = 1 << 0,
184 ReceiveIcmpFrameChecksumEnable = 1 << 1,
185 UdpLiteFrameEnable = 1 << 2,
186 ReceiveIpv4Ipv6UdpFrameChecksumEqualZero = 1 << 3,
187 ReceiveIpv4Ipv6FragmentFramePass = 1 << 4,
188 DataBurst4Bytes = 0b000 << 5,
189 DataBurst8Bytes = 0b001 << 5,
190 DataBurst16Bytes = 0b010 << 5,
191 DataBurst32Bytes = 0b011 << 5,
192 DataBurstSingleFrame = 0b100 << 5,
198 enum [[clang::flag_enum]] ReceiveFrameHeaderStatus : uint16_t
200 ReceiveCrcError = 1 << 0,
201 ReceiveRuntFrame = 1 << 1,
202 ReceiveFrameTooLong = 1 << 2,
203 ReceiveFrameType = 1 << 3,
204 ReceiveMiiError = 1 << 4,
205 ReceiveUnicastFrame = 1 << 5,
206 ReceiveMulticastFrame = 1 << 6,
207 ReceiveBroadcastFrame = 1 << 7,
208 ReceiveUdpFrameChecksumStatus = 1 << 10,
209 ReceiveTcpFrameChecksumStatus = 1 << 11,
210 ReceiveIpFrameChecksumStatus = 1 << 12,
211 ReceiveIcmpFrameChecksumStatus = 1 << 13,
212 ReceiveFrameValid = 1 << 15,
218 enum [[clang::flag_enum]] ReceiveQueueCommand : uint16_t
220 ReleaseReceiveErrorFrame = 1 << 0,
221 StartDmaAccess = 1 << 3,
222 AutoDequeueReceiveQueueFrameEnable = 1 << 4,
223 ReceiveFrameCountThresholdEnable = 1 << 5,
224 ReceiveDataByteCountThresholdEnable = 1 << 6,
225 ReceiveDurationTimerThresholdEnable = 1 << 7,
226 ReceiveIpHeaderTwoByteOffsetEnable = 1 << 9,
227 ReceiveFrameCountThresholdStatus = 1 << 10,
228 ReceiveDataByteCountThresholdstatus = 1 << 11,
229 ReceiveDurationTimerThresholdStatus = 1 << 12,
235 enum [[clang::flag_enum]] TransmitQueueCommand : uint16_t
237 ManualEnqueueTransmitQueueFrameEnable = 1 << 0,
238 TransmitQueueMemoryAvailableMonitor = 1 << 1,
239 AutoEnqueueTransmitQueueFrameEnable = 1 << 2,
246 enum [[clang::flag_enum]] FrameDataPointer : uint16_t
252 FrameDataPointerAutoIncrement = 1 << 14,
258 enum [[clang::flag_enum]] Interrupt : uint16_t
260 EnergyDetectInterrupt = 1 << 2,
261 LinkupDetectInterrupt = 1 << 3,
262 ReceiveMagicPacketDetectInterrupt = 1 << 4,
263 ReceiveWakeupFrameDetectInterrupt = 1 << 5,
264 TransmitSpaceAvailableInterrupt = 1 << 6,
265 ReceiveProcessStoppedInterrupt = 1 << 7,
266 TransmitProcessStoppedInterrupt = 1 << 8,
267 ReceiveOverrunInterrupt = 1 << 11,
268 ReceiveInterrupt = 1 << 13,
269 TransmitInterrupt = 1 << 14,
270 LinkChangeInterruptStatus = 1 << 15,
276 enum [[clang::flag_enum]] Port1Control : uint16_t
278 Advertised10BTHalfDuplexCapability = 1 << 0,
279 Advertised10BTFullDuplexCapability = 1 << 1,
280 Advertised100BTHalfDuplexCapability = 1 << 2,
281 Advertised100BTFullDuplexCapability = 1 << 3,
282 AdvertisedFlowControlCapability = 1 << 4,
283 ForceDuplex = 1 << 5,
285 AutoNegotiationEnable = 1 << 7,
287 DisableAutoMDIMDIX = 1 << 10,
288 RestartAutoNegotiation = 1 << 13,
289 TransmitterDisable = 1 << 14,
296 enum [[clang::flag_enum]] Port1Status : uint16_t
298 Partner10BTHalfDuplexCapability = 1 << 0,
299 Partner10BTFullDuplexCapability = 1 << 1,
300 Partner100BTHalfDuplexCapability = 1 << 2,
301 Partner100BTFullDuplexCapability = 1 << 3,
302 PartnerFlowControlCapability = 1 << 4,
304 AutoNegotiationDone = 1 << 6,
306 OperationDuplex = 1 << 9,
307 OperationSpeed = 1 << 10,
308 PolarityReverse = 1 << 13,
316 const uint32_t *receiveInterruptFutex;
321 inline void set_gpio_output_bit(GpioPin pin,
bool value)
const
323 uint32_t shift =
static_cast<uint8_t
>(pin);
324 uint32_t output = gpio()->output;
325 output &= ~(1 << shift);
326 output |= value << shift;
327 gpio()->output = output;
333 [[nodiscard]] uint16_t register_read(RegisterOffset reg)
const
355 uint8_t addr =
static_cast<uint8_t
>(reg);
356 uint8_t byteEnable = (addr & 0x2) == 0 ? 0b0011 : 0b1100;
358 bytes[0] = (
static_cast<uint8_t
>(SpiCommand::ReadRegister) << 6) |
359 (byteEnable << 2) | (addr >> 6);
360 bytes[1] = (addr << 2) & 0b11110000;
362 set_gpio_output_bit(GpioPin::EthernetChipSelect,
false);
363 spi()->blocking_write(bytes,
sizeof(bytes));
365 spi()->blocking_read(
reinterpret_cast<uint8_t *
>(&val),
sizeof(val));
366 set_gpio_output_bit(GpioPin::EthernetChipSelect,
true);
373 void register_write(RegisterOffset reg, uint16_t val)
const
376 uint8_t addr =
static_cast<uint8_t
>(reg);
377 uint8_t byteEnable = (addr & 0x2) == 0 ? 0b0011 : 0b1100;
379 bytes[0] = (
static_cast<uint8_t
>(SpiCommand::WriteRegister) << 6) |
380 (byteEnable << 2) | (addr >> 6);
381 bytes[1] = (addr << 2) & 0b11110000;
383 set_gpio_output_bit(GpioPin::EthernetChipSelect,
false);
384 spi()->blocking_write(bytes,
sizeof(bytes));
385 spi()->blocking_write(
reinterpret_cast<uint8_t *
>(&val),
sizeof(val));
387 set_gpio_output_bit(GpioPin::EthernetChipSelect,
true);
393 void register_set(RegisterOffset reg, uint16_t mask)
const
395 uint16_t old = register_read(reg);
396 register_write(reg, old | mask);
402 void register_clear(RegisterOffset reg, uint16_t mask)
const
404 uint16_t old = register_read(reg);
405 register_write(reg, old & ~mask);
411 [[nodiscard, gnu::always_inline]] Capability<volatile SonataSpi> spi()
const
419 [[nodiscard, gnu::always_inline]] Capability<volatile SonataGPIO>
428 uint16_t framesToProcess = 0;
433 RecursiveMutex transmitBufferMutex;
438 std::unique_ptr<uint8_t[]> transmitBuffer;
444 RecursiveMutex receiveBufferMutex;
450 FlagLockPriorityInherited gpioLock;
455 std::unique_ptr<uint8_t[]> receiveBuffer;
463 transmitBuffer = std::make_unique<uint8_t[]>(MaxFrameSize);
464 receiveBuffer = std::make_unique<uint8_t[]>(MaxFrameSize);
467 set_gpio_output_bit(GpioPin::EthernetReset,
false);
469 set_gpio_output_bit(GpioPin::EthernetReset,
true);
471 uint16_t chipId = register_read(RegisterOffset::ChipIdEnable);
472 Debug::log(
"Chip ID is {}", chipId);
475 Debug::Assert((chipId & 0xFFF0) == 0x8870,
"Unexpected Chip ID");
479 register_write(RegisterOffset::TransmitFrameDataPointer,
480 FrameDataPointer::FrameDataPointerAutoIncrement);
481 register_write(RegisterOffset::TransmitControl,
482 TransmitControl::TransmitCrcEnable |
483 TransmitControl::TransmitPaddingEnable |
484 TransmitControl::TransmitFlowControlEnable |
485 TransmitControl::TransmitChecksumGenerationIp |
486 TransmitControl::TransmitChecksumGenerationTcp |
487 TransmitControl::TransmitChecksumGenerationIcmp);
488 register_write(RegisterOffset::ReceiveFrameDataPointer,
489 FrameDataPointer::FrameDataPointerAutoIncrement);
491 register_write(RegisterOffset::ReceiveFrameCountThreshold, 0x0001);
492 register_write(RegisterOffset::ReceiveControl1,
493 ReceiveControl1::ReceiveUnicastEnable |
494 ReceiveControl1::ReceiveMulticastEnable |
495 ReceiveControl1::ReceiveBroadcastEnable |
496 ReceiveControl1::ReceiveFlowControlEnable |
498 ReceivePhysicalAddressFilteringWithMacAddressEnable |
499 ReceiveControl1::ReceiveIpFrameChecksumCheckEnable |
500 ReceiveControl1::ReceiveTcpFrameChecksumCheckEnable |
501 ReceiveControl1::ReceiveUdpFrameChecksumCheckEnable);
508 RegisterOffset::ReceiveControl2,
509 ReceiveControl2::UdpLiteFrameEnable |
510 ReceiveControl2::ReceiveIpv4Ipv6UdpFrameChecksumEqualZero |
511 ReceiveControl2::ReceiveIpv4Ipv6FragmentFramePass |
512 ReceiveControl2::DataBurstSingleFrame);
514 RegisterOffset::ReceiveQueueCommand,
515 ReceiveQueueCommand::ReceiveFrameCountThresholdEnable |
516 ReceiveQueueCommand::AutoDequeueReceiveQueueFrameEnable);
522 register_set(RegisterOffset::Port1Control,
523 Port1Control::RestartAutoNegotiation);
527 register_write(RegisterOffset::FlowControlLowWatermark, 0x0600);
530 register_write(RegisterOffset::FlowControlHighWatermark, 0x0400);
533 register_write(RegisterOffset::InterruptStatus, 0xFFFF);
534 receiveInterruptFutex =
537 register_write(RegisterOffset::InterruptEnable, ReceiveInterrupt);
540 register_set(RegisterOffset::TransmitControl,
541 TransmitControl::TransmitEnable);
543 register_set(RegisterOffset::ReceiveControl1,
544 ReceiveControl1::ReceiveEnable);
560 static constexpr MACAddress mac_address_default()
562 return {0x3a, 0x30, 0x25, 0x24, 0xfe, 0x7a};
565 void mac_address_set(MACAddress address = mac_address_default())
567 register_write(RegisterOffset::MacAddressHigh,
568 (address[0] << 8) | address[1]);
569 register_write(RegisterOffset::MacAdressMiddle,
570 (address[2] << 8) | address[3]);
571 register_write(RegisterOffset::MacAddressLow,
572 (address[4] << 8) | address[5]);
575 uint32_t receive_interrupt_value()
577 return *receiveInterruptFutex;
580 int receive_interrupt_complete(
Timeout *timeout,
581 uint32_t lastInterruptValue)
596 if (*receiveInterruptFutex == lastInterruptValue)
598 Debug::log(
"Acknowledged interrupt, sleeping on futex {}",
599 receiveInterruptFutex);
601 timeout, receiveInterruptFutex, lastInterruptValue);
603 Debug::log(
"Scheduler announces interrupt has fired");
614 Capability<uint8_t> buffer;
617 friend class Ksz8851Ethernet;
618 LockGuard<RecursiveMutex> guard;
620 Frame(LockGuard<RecursiveMutex> &&guard,
621 Capability<uint8_t> buffer,
623 : guard(std::move(guard)), buffer(buffer), length(length)
633 uint16_t status = register_read(RegisterOffset::Port1Status);
634 return (status & Port1Status::LinkGood) != 0;
637 std::optional<Frame> receive_frame()
640 if (framesToProcess == 0)
642 uint16_t isr = register_read(RegisterOffset::InterruptStatus);
643 if (!(isr & ReceiveInterrupt))
649 register_write(RegisterOffset::InterruptStatus, ReceiveInterrupt);
654 register_read(RegisterOffset::ReceiveFrameCountThreshold) >> 8;
658 for (; framesToProcess; framesToProcess--)
661 register_read(RegisterOffset::ReceiveFrameHeaderStatus);
663 register_read(RegisterOffset::ReceiveFrameHeaderByteCount) &
666 (status & ReceiveFrameValid) &&
668 (ReceiveCrcError | ReceiveRuntFrame | ReceiveFrameTooLong |
669 ReceiveMiiError | ReceiveUdpFrameChecksumStatus |
670 ReceiveTcpFrameChecksumStatus | ReceiveIpFrameChecksumStatus |
671 ReceiveIcmpFrameChecksumStatus));
675 DebugFrameDrops::log(
"Dropping frame with status: {}", status);
683 DebugFrameDrops::log(
"Dropping frame with zero length");
691 uint16_t paddedLength = (length + 3) & ~0x3;
692 if (paddedLength > MaxFrameSize)
694 DebugFrameDrops::log(
"Dropping frame that is too large: {}",
701 Debug::log(
"Receiving frame of length {}", length);
703 LockGuard guard{receiveBufferMutex};
707 register_write(RegisterOffset::ReceiveFrameDataPointer,
708 FrameDataPointer::FrameDataPointerAutoIncrement);
709 register_set(RegisterOffset::ReceiveQueueCommand, StartDmaAccess);
712 uint8_t cmd =
static_cast<uint8_t
>(SpiCommand::ReadDma) << 6;
713 set_gpio_output_bit(GpioPin::EthernetChipSelect,
false);
714 spi()->blocking_write(&cmd, 1);
719 spi()->blocking_read(dummy,
sizeof(dummy));
721 spi()->blocking_read(receiveBuffer.get(), paddedLength);
723 set_gpio_output_bit(GpioPin::EthernetChipSelect,
true);
725 register_clear(RegisterOffset::ReceiveQueueCommand, StartDmaAccess);
726 framesToProcess -= 1;
728 Capability<uint8_t> boundedBuffer{receiveBuffer.get()};
729 boundedBuffer.bounds().set_inexact(length);
732 boundedBuffer.permissions() &=
733 CHERI::PermissionSet{CHERI::Permission::Load};
735 return Frame{std::move(guard), boundedBuffer, length};
749 bool send_frame(
const uint8_t *buffer, uint16_t length,
auto &&check)
752 uint16_t paddedLength = (length + 3) & ~0x3;
753 if (paddedLength > MaxFrameSize)
755 Debug::log(
"Frame size {} is larger than the maximum size", length);
773 memcpy(transmitBuffer.get(), buffer, length);
774 if (!check(transmitBuffer.get(), length))
783 while ((register_read(RegisterOffset::TransmitQueueMemoryInfo) &
788 Debug::log(
"Sending frame of length {}", length);
791 register_set(RegisterOffset::ReceiveQueueCommand, StartDmaAccess);
794 uint8_t cmd =
static_cast<uint8_t
>(SpiCommand::WriteDma) << 6;
795 set_gpio_output_bit(GpioPin::EthernetChipSelect,
false);
796 spi()->blocking_write(&cmd, 1);
798 uint32_t header =
static_cast<uint32_t
>(length) << 16;
799 spi()->blocking_write(
reinterpret_cast<uint8_t *
>(&header),
802 spi()->blocking_write(transmitBuffer.get(), paddedLength);
805 set_gpio_output_bit(GpioPin::EthernetChipSelect,
true);
808 register_clear(RegisterOffset::ReceiveQueueCommand, StartDmaAccess);
812 RegisterOffset::TransmitQueueCommand,
813 TransmitQueueCommand::ManualEnqueueTransmitQueueFrameEnable);
819 void drop_error_frame()
821 register_set(RegisterOffset::ReceiveQueueCommand,
822 ReleaseReceiveErrorFrame);
825 while (register_read(RegisterOffset::ReceiveQueueCommand) &
826 ReleaseReceiveErrorFrame)
CHERIoT RTOS heap allocator interface.
int __cheri_libcall heap_claim_ephemeral(TimeoutArgument timeout, const void *ptr, const void *ptr2)
Interface to the ephemeral claims mechanism.
C++ helpers for operating on capabilities.
bool check_pointer(auto &ptr, size_t space=sizeof(std::remove_pointer< decltype(ptr)>))
Checks that ptr is valid, unsealed, has at least Permissions, and has at least space bytes after the ...
@ Load
This capability can be used to load.
Class encapsulating a set of permissions.
Simple class representing a received Ethernet frame.
The driver for KSZ8851 SPI Ethernet MAC.
bool send_frame(const uint8_t *buffer, uint16_t length, auto &&check)
Send a packet.
Ksz8851Ethernet()
Initialise a reference to the Ethernet device.
static constexpr bool has_unique_mac_address()
This device does not have a unique MAC address and so users must provide a locally administered MAC a...
bool phy_link_status()
Check the link status of the PHY.
A simple RAII type that owns a lock.
#define STATIC_SEALED_VALUE(name)
Returns a sealed capability to the named object created with DECLARE_STATIC_SEALED_VALUE.
#define MMIO_CAPABILITY(type, name)
Provide a capability of the type volatile type * referring to the MMIO region exported in the linker ...
Concept for an Ethernet adaptor.
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
Futex (compare-and-wait) APIs.
int futex_timed_wait(TimeoutArgument timeout, const volatile uint32_t *address, uint32_t expected, uint32_t flags)
Compare the value at address to expected and, if they match, sleep the thread until a wake event is s...
This file describes the interfaces for compartments to wait for interrupts.
int interrupt_complete(InterruptCapability interruptcapability)
Acknowledge the end of handling an interrupt.
#define DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY( name, number, mayWait, mayComplete)
Helper macro to define an interrupt capability without a separate declaration.
const uint32_t * interrupt_futex_get(InterruptCapability interruptcapability)
Request the futex associated with an interrupt.
C++ wrappers for synchronisation primitives.
Structure representing a timeout.
Functions and types used for thread management.
static uint64_t thread_millisecond_wait(uint32_t milliseconds)
Wait for the specified number of milliseconds.
struct Timeout Timeout
Structure representing a timeout.