SatCat5
port_mailbox.cc
1 // Copyright 2021-2024 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/port_mailbox.h>
7 
9 
10 // Maximum I/O segment-length.
11 // (Very long contiguous reads can exceed safe lock time.)
12 static const unsigned MAX_SEGMENT = 256;
13 
14 // Opcodes and bit-masks for the control register:
15 static const u32 ETHCMD_NOOP = (0x00u << 24);
16 static const u32 ETHCMD_WRNEXT = (0x02u << 24);
17 static const u32 ETHCMD_WRFINAL = (0x03u << 24);
18 static const u32 ETHCMD_RESET = (0xFFu << 24);
19 
20 static const u32 ETHREG_DVALID = (1u << 31);
21 static const u32 ETHREG_DFINAL = (1u << 30);
22 static const u32 ETHREG_ERROR = (1u << 29);
23 static const u32 ETHREG_DMASK = (0xFFu);
24 
25 Mailbox::Mailbox(satcat5::cfg::ConfigBus* cfg, unsigned devaddr, unsigned regaddr)
26  : satcat5::io::BufferedIO(
27  m_txbuff, SATCAT5_MAILBOX_BUFFSIZE, SATCAT5_MAILBOX_BUFFPKT,
28  m_rxbuff, SATCAT5_MAILBOX_BUFFSIZE, SATCAT5_MAILBOX_BUFFPKT)
29  , satcat5::cfg::Interrupt(cfg)
30  , m_hw_reg(cfg->get_register(devaddr, regaddr))
31 {
32  // Send reset command to hardware.
33  *m_hw_reg = ETHCMD_RESET;
34 }
35 
37 {
38  // Copy segments of data to the hardware buffer.
39  while (copy_tx_segment()) {}
40 }
41 
42 u32 Mailbox::copy_tx_segment()
43 {
44  satcat5::irq::AtomicLock lock("ETH-Tx");
45 
46  // How much data is available in the next segment?
47  // (Use peek/consume for better throughput.)
48  u32 pkt = m_tx.get_read_ready(); // Bytes to EOF
49  u32 seg = m_tx.get_peek_ready(); // Bytes to EOF or wraparound
50 
51  // Special case if empty.
52  if (!seg) return 0; // No data available
53 
54  // Cap segment length to avoid hogging lock time.
55  if (seg > MAX_SEGMENT) seg = MAX_SEGMENT;
56 
57  // Fetch pointer to start of next contiguous segment:
58  const u8* src = m_tx.peek(seg);
59 
60  // Is this a complete packet?
61  if (pkt == seg) {
62  // Complete packet, set FINAL flag.
63  for (unsigned a = 0 ; a < seg-1 ; ++a)
64  *m_hw_reg = ETHCMD_WRNEXT | src[a];
65  *m_hw_reg = ETHCMD_WRFINAL | src[seg-1];
67  } else {
68  // Partial packet.
69  for (unsigned a = 0 ; a < seg ; ++a)
70  *m_hw_reg = ETHCMD_WRNEXT | src[a];
71  m_tx.read_consume(seg);
72  }
73  return seg;
74 }
75 
76 u32 Mailbox::copy_rx_segment()
77 {
78  satcat5::irq::AtomicLock lock("ETH-Rx");
79 
80  // Get the next contiguous buffer segment.
81  u32 rem = m_rx.zcw_maxlen();
82  u8* dst = m_rx.zcw_start();
83 
84  // Abort if the FIFO is full.
85  if (!rem) return ETHREG_ERROR;
86 
87  // Cap segment length to avoid hogging lock time.
88  if (rem > MAX_SEGMENT) rem = MAX_SEGMENT;
89 
90  // Copy any received data to the software FIFO.
91  u32 reg = *m_hw_reg;
92  while ((rem) && (reg & ETHREG_DVALID)) {
93  // Copy the next byte...
94  *dst = (u8)(reg & ETHREG_DMASK);
95  ++dst; --rem;
96  // Was this the end of a frame?
97  if (reg & ETHREG_DFINAL) break;
98  // Poll hardware for the next byte...
99  if (rem) reg = *m_hw_reg;
100  }
101 
102  // Complete the write process.
103  u32 ncopy = dst - m_rx.zcw_start();
104  m_rx.zcw_write(ncopy);
105  if (reg & ETHREG_DFINAL)
106  m_rx.write_finalize(); // End of frame
107 
108  return reg;
109 }
110 
111 // Interrupt context copies received data ONLY.
112 // Data MUST be read immediately; any overflow is discarded.
114 {
115  // Copy one segment of received data.
116  // (Keep interrupts short to avoid priority inversion.)
117  u32 reg = copy_rx_segment();
118 
119  // If we encountered an error, flush any partial data.
120  if (reg & ETHREG_ERROR) {
121  while (reg & (ETHREG_DVALID | ETHREG_ERROR))
122  reg = *m_hw_reg; // Read and discard until empty
123  m_rx.write_abort(); // Discard any work in progress
124  }
125 }
Generic ConfigBus API.
Definition: cfgbus_core.h:124
satcat5::io::PacketBuffer m_tx
Transmit data (user writes, child reads)
Definition: io_buffer.h:57
satcat5::io::PacketBuffer m_rx
Receive data (user reads, child writes)
Definition: io_buffer.h:60
const u8 * peek(unsigned nbytes) const
Peek nbytes into the circular buffer.
Definition: pkt_buffer.cc:248
unsigned get_peek_ready() const
Find the longest available contiguous segment that can be requested by peek().
Definition: pkt_buffer.cc:242
bool write_finalize() override
Mark end of frame and release temporary working data.
Definition: pkt_buffer.cc:105
void zcw_write(unsigned nbytes)
Zero-copy write (zcw) mode, required for UART interface.
Definition: pkt_buffer.cc:164
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: pkt_buffer.cc:177
bool read_consume(unsigned nbytes) override
Read and discard 0 or more bytes.
Definition: pkt_buffer.cc:256
unsigned zcw_maxlen() const
Max contiguous write length (ZCW).
Definition: pkt_buffer.cc:142
void write_abort() override
If possible, abort the current partially-written packet.
Definition: pkt_buffer.cc:89
void read_finalize() override
Consume any remaining bytes in this frame, if applicable.
Definition: pkt_buffer.cc:266
u8 * zcw_start()
Pointer to a contiguous buffer (ZCW).
Definition: pkt_buffer.cc:154
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
Automatic lock or mutex.
Driver for the internal "MailBox" Ethernet port This class interfaces with "port_axi_mailbox" through...
Definition: port_mailbox.h:31
void data_rcvd(satcat5::io::Readable *src) override
The data_rcvd() callback is polled whenever data is available.
Definition: port_mailbox.cc:36
void irq_event() override
Interrupt service routine.