SatCat5
port_mailmap.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 <cstring>
7 #include <satcat5/log.h>
8 #include <satcat5/port_mailmap.h>
9 #include <satcat5/ptp_time.h>
10 
13 using satcat5::log::Log;
16 using satcat5::ptp::Time;
17 
18 // Enable additional diagnostics? (0/1/2)
19 // Note: Sending log messages may overwrite Tx timestamps before they are read.
20 static constexpr unsigned DEBUG_VERBOSE = 0;
21 
22 // Enable PTP support for the MailMap driver?
23 // Disabling this feature reduces required code size.
24 #ifndef SATCAT5_PTP_ENABLE
25 #define SATCAT5_PTP_ENABLE 1
26 #endif
27 
28 static const char* LBL_MAP = "MAP";
29 static const unsigned REGADDR_IRQ = 510; // Same as m_ctrl->rx_irq
30 static const unsigned REGADDR_CLK = 1012; // Same as m_ctrl->rt_clk_ctrl
31 
32 // Read standard 4-word timestamp, starting from designated register.
33 inline Time get_timestamp(volatile u32* addr) {
34  u32 secMSB = addr[0];
35  u32 secLSB = addr[1];
36  u32 nanoSec = addr[2];
37  u16 subNanoSec = addr[3];
38  u64 sec = ((u64)secMSB) << 32 | secLSB;
39  return Time(sec, nanoSec, subNanoSec);
40 }
41 
42 Mailmap::Mailmap(satcat5::cfg::ConfigBusMmap* cfg, unsigned devaddr)
43  : satcat5::cfg::Interrupt(cfg, devaddr, REGADDR_IRQ)
44  , m_ctrl((ctrl_reg*)cfg->get_device_mmap(devaddr))
45  , m_clock_reg(cfg->get_register(devaddr, REGADDR_CLK))
46  , m_wridx(0)
47  , m_wrovr(0)
48  , m_rdidx(0)
49  , m_rdlen(0)
50  , m_rdovr(0)
51 {
52  // No other initialization required.
53 }
54 
55 unsigned Mailmap::get_write_space() const {
56  AtomicLock lock(LBL_MAP);
57  if (m_ctrl->tx_ctrl) // Busy?
58  return 0;
59  else if (m_wrovr) // Overflow?
60  return 0;
61  else // Ready to write
62  return SATCAT5_MAILMAP_BYTES - m_wridx;
63 }
64 
65 void Mailmap::write_bytes(unsigned nbytes, const void* src) {
66  // For performance, use memcpy rather than repeated write_next().
67  AtomicLock lock(LBL_MAP);
68  if (nbytes <= get_write_space()) {
69  memcpy(m_ctrl->tx_buff + m_wridx, src, nbytes);
70  m_wridx += nbytes;
71  } else {write_overflow();}
72 }
73 
75  // Set overflow flag to prevent further writes.
76  m_wrovr = 1;
77 }
78 
80  // Discard partially-written packet and revert to idle.
81  m_wrovr = 0;
82  m_wridx = 0;
83 }
84 
86  AtomicLock lock(LBL_MAP);
87  if (m_wrovr) { // Buffer overflow
88  m_wrovr = 0; // Reset for next frame
89  m_wridx = 0;
90  return false;
91  } else if (m_wridx) { // Valid packet
92  m_ctrl->tx_ctrl = m_wridx; // Begin transmission
93  m_wridx = 0; // Reset for next frame
94  return true;
95  } else { // Empty packet
96  return false;
97  }
98 }
99 
101  m_rdovr = 1; // Set underflow flag
102 }
103 
104 unsigned Mailmap::get_read_ready() const {
105  AtomicLock lock(LBL_MAP);
106  if (m_rdovr)
107  return 0; // Read underflow
108  else if (m_rdlen)
109  return m_rdlen - m_rdidx; // Ready to read
110  else
111  return 0; // Waiting for new packet...
112 }
113 
114 bool Mailmap::read_bytes(unsigned nbytes, void* dst) {
115  // For performance, use memcpy rather than repeated read_next().
116  AtomicLock lock(LBL_MAP);
117  if (nbytes <= get_read_ready()) {
118  memcpy(dst, m_ctrl->rx_buff + m_rdidx, nbytes);
119  m_rdidx += nbytes;
120  return true;
121  } else {
122  read_underflow();
123  return false;
124  }
125 }
126 
128  AtomicLock lock(LBL_MAP);
129  if (m_rdlen) {
130  m_ctrl->rx_ctrl = 0; // Flush hardware buffer
131  m_rdidx = 0; // Reset for next frame
132  m_rdlen = 0;
133  m_rdovr = 0;
134  }
135 }
136 
137 void Mailmap::write_next(u8 data) {
138  m_ctrl->tx_buff[m_wridx++] = data; // Write next byte to hardware
139 }
140 
142  return m_ctrl->rx_buff[m_rdidx++]; // Read next byte from hardware
143 }
144 
146  // Interrupts indicate a new received packet. Update state.
147  m_rdlen = m_ctrl->rx_ctrl;
148  if (!m_rdlen) return; // Ignore empty packets
149  // PTP packet? Deferred notification to selected event-handler.
150  if (SATCAT5_PTP_ENABLE && ptp_dispatch(m_ctrl->rx_buff, m_rdlen)) {
151  ptp_notify_req();
152  } else {
153  request_poll();
154  }
155 }
156 
158  if (!SATCAT5_PTP_ENABLE) return satcat5::ptp::TIME_ZERO;
159  // Read the current system time.
160  m_ctrl->rt_clk_ctrl[4] = 0x01; // Request current time
161  Time tmp = get_timestamp(m_ctrl->rt_clk_ctrl); // Read the RTC
162  if (DEBUG_VERBOSE > 0) Log(DEBUG, "ptp_time_now").write_obj(tmp);
163  return tmp;
164 }
165 
167  if (!SATCAT5_PTP_ENABLE) return satcat5::ptp::TIME_ZERO;
168  // Initiate an outgoing PTP message
169  m_ctrl->ptp_status = 0x01; // Freeze the RTC
170  Time tmp = get_timestamp(m_ctrl->rt_clk_ctrl); // Read the RTC
171  if (DEBUG_VERBOSE > 0) Log(DEBUG, "ptp_tx_start").write_obj(tmp);
172  return tmp;
173 }
174 
176  if (!SATCAT5_PTP_ENABLE) return satcat5::ptp::TIME_ZERO;
177  // Returns the timestamp for the previous outgoing message as ptp::Time
178  Time tmp = get_timestamp(m_ctrl->tx_ptp_time); // Read packet timestamp
179  if (DEBUG_VERBOSE > 0) Log(DEBUG, "ptp_tx_timestamp").write_obj(tmp);
180  return tmp;
181 }
182 
184  if (!SATCAT5_PTP_ENABLE) return satcat5::ptp::TIME_ZERO;
185  // Returns the timestamp for the current received message as ptp::Time
186  Time tmp = get_timestamp(m_ctrl->rx_ptp_time); // Read packet timestamp
187  if (DEBUG_VERBOSE > 0) Log(DEBUG, "ptp_rx_timestamp").write_obj(tmp);
188  return tmp;
189 }
190 
192  return this; // PTP and normal data use the same interface.
193 }
194 
196  return this; // PTP and normal data use the same interface.
197 }
198 
200  : Mailmap(cfg, devaddr), m_wrtmp(0) {}
201 
202 void MailmapAligned::write_bytes(unsigned nbytes, const void* src) {
203  const u8* src8 = (const u8*)src;
204  AtomicLock lock(LBL_MAP);
205  if (nbytes <= get_write_space()) {
206  while (nbytes) {
207  if ((m_wridx % 4) || (nbytes < 4)) {
208  // Revert to byte-at-a-time copy.
209  write_next(*src8); ++src8; --nbytes;
210  } else {
211  // Word-at-a-time copy.
212  const u32* src32 = (const u32*)src8;
213  volatile u32* dst32 = (volatile u32*)(m_ctrl->tx_buff + m_wridx);
214  *dst32 = *src32;
215  src8 += 4; m_wridx += 4; nbytes -= 4;
216  }
217  }
218  } else {write_overflow();}
219 }
220 
221 bool MailmapAligned::read_bytes(unsigned nbytes, void* dst) {
222  u8* dst8 = (u8*)dst;
223  AtomicLock lock(LBL_MAP);
224  if (nbytes <= get_read_ready()) {
225  while (nbytes) {
226  if ((m_rdidx % 4) || (nbytes < 4)) {
227  // Revert to byte-at-a-time copy.
228  *dst8 = read_next(); ++dst8; --nbytes;
229  } else {
230  // Word-at-a-time copy.
231  volatile u32* src32 = (volatile u32*)(m_ctrl->rx_buff + m_rdidx);
232  u32* dst32 = (u32*)dst8;
233  *dst32 = *src32;
234  dst8 += 4; m_rdidx += 4; nbytes -= 4;
235  }
236  }
237  return true;
238  } else {
239  read_underflow();
240  return false;
241  }
242 }
243 
245  // Add the new byte to the accumulator.
246  unsigned offset = m_wridx % 4;
247  if (offset == 0) m_wrtmp = 0;
248  u8* tmp8 = (u8*)(&m_wrtmp);
249  tmp8[offset] = data;
250  // Write the entire accumulator word to the hardware buffer.
251  volatile u32* dst32 = (volatile u32*)(m_ctrl->tx_buff + m_wridx - offset);
252  *dst32 = m_wrtmp;
253  ++m_wridx;
254 }
255 
257  // Read the entire word from the hardware buffer.
258  unsigned offset = m_rdidx % 4;
259  volatile u32* src32 = (volatile u32*)(m_ctrl->rx_buff + m_rdidx - offset);
260  const u32 tmp32 = *src32;
261  ++m_rdidx;
262  // Extract the byte of interest.
263  const u8* tmp8 = (const u8*)(&tmp32);
264  return tmp8[offset];
265 }
Memory-mapped local ConfigBus.
Definition: cfgbus_core.h:199
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
Automatic lock or mutex.
The Log class creates and formats one log message.
Definition: log.h:195
Log & write_obj(const T &obj)
Templated wrapper for custom output formatting.
Definition: log.h:251
void request_poll()
Call this method to request polling at a later time.
Definition: polling.cc:208
Alternate MailMap driver using word-aligned access only.
Definition: port_mailmap.h:101
u8 read_next() override
Read the next byte from the underlying buffer or device.
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
void write_bytes(unsigned nbytes, const void *src) override
Write 0 or more bytes from a buffer.
MailmapAligned(satcat5::cfg::ConfigBusMmap *cfg, unsigned devaddr)
Constructor links this driver to a ConfigBus address.
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
Driver for the internal "MailMap" Ethernet port.
Definition: port_mailmap.h:36
void write_overflow() override
Optional error handling for write overflow.
Definition: port_mailmap.cc:74
void read_underflow() override
Optional error handling for read underflow.
satcat5::io::Readable * ptp_rx_read() override
Return an object suitable for reading the next PTP frame.
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
unsigned get_read_ready() const override
How many bytes can be read without blocking?
satcat5::ptp::Time ptp_tx_start() override
Begin sending a timestamped message.
u8 read_next() override
Read the next byte from the underlying buffer or device.
satcat5::ptp::Time ptp_tx_timestamp() override
Return timestamp of the most recent outgoing message.
void irq_event() override
Interrupt service routine.
void write_abort() override
If possible, abort the current partially-written packet.
Definition: port_mailmap.cc:79
bool write_finalize() override
Mark end of frame and release temporary working data.
Definition: port_mailmap.cc:85
void read_finalize() override
Consume any remaining bytes in this frame, if applicable.
satcat5::io::Writeable * ptp_tx_write() override
Return an object suitable for writing the next PTP frame.
unsigned get_write_space() const override
How many bytes can be written without blocking?
Definition: port_mailmap.cc:55
void write_bytes(unsigned nbytes, const void *src) override
Write 0 or more bytes from a buffer.
Definition: port_mailmap.cc:65
bool read_bytes(unsigned nbytes, void *dst) override
Read 0 or more bytes into a buffer.
satcat5::ptp::Time ptp_rx_timestamp() override
Return timestamp of the current incoming message.
satcat5::ptp::Time ptp_time_now() override
Return the best available estimate of the current time.
bool ptp_dispatch(const u8 *peek, unsigned length)
Determine if an incoming packet is a PTP message.
void ptp_notify_req()
Notify the PTP callback object in deferred mode.
Definition: ptp_interface.h:80
High-precision timestamp for use with PTP / IEEE1588.
Definition: ptp_time.h:41
Diagnostic logging to UART and/or Ethernet ports.
constexpr s8 DEBUG
Define basic priority codes for log messages.
Definition: log.h:109
High-precision "Time" object for use with PTP / IEEE1588.