SatCat5
net_cfgbus.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/cfgbus_core.h>
7 #include <satcat5/ethernet.h>
8 #include <satcat5/eth_dispatch.h>
9 #include <satcat5/log.h>
10 #include <satcat5/net_cfgbus.h>
11 #include <satcat5/udp_core.h>
12 #include <satcat5/udp_dispatch.h>
13 
14 namespace log = satcat5::log;
17 using satcat5::net::Type;
18 
19 // Set verbosity level (0/1/2)
20 static const unsigned DEBUG_VERBOSE = 0;
21 
22 // Define command opcodes
23 static const u8 OPMASK_CMD = 0xF0; // MSBs = Command
24 static const u8 OPMASK_WREN = 0x0F; // LSBs = Write-mask
25 static const u8 OPCODE_NOOP = 0x00; // No-op
26 static const u8 OPCODE_WRITE0 = 0x20; // Write no-increment
27 static const u8 OPCODE_WRITE1 = 0x30; // Write auto-increment
28 static const u8 OPCODE_READ0 = 0x40; // Read no-increment
29 static const u8 OPCODE_READ1 = 0x50; // Read auto-increment
30 
31 // Define Type codes for each supported protocol.
32 static const Type TYPE_ETH_CMD =
33  Type(satcat5::eth::ETYPE_CFGBUS_CMD.value);
34 static const Type TYPE_ETH_ACK =
35  Type(satcat5::eth::ETYPE_CFGBUS_ACK.value);
36 static const Type TYPE_UDP_CMD =
37  Type(satcat5::udp::PORT_CFGBUS_CMD.value);
38 static const Type TYPE_UDP_ACK =
39  Type(satcat5::udp::PORT_CFGBUS_ACK.value);
40 
41 // Support non-word-atomic writes?
42 // (These are extremely rare in remotely-operated systems.)
43 #ifndef SATCAT5_PROTOCFG_SUPPORT_WRMASK
44 #define SATCAT5_PROTOCFG_SUPPORT_WRMASK 0
45 #endif
46 
47 // Helper function for byte-by-byte writes, if enabled.
48 inline void write_mask(volatile u32* reg, u32 val, u8 mask)
49 {
50 #if SATCAT5_PROTOCFG_SUPPORT_WRMASK
51  // Break the command into individual byte-writes.
52  u8* val8 = (u8*)&val;
53  volatile u8* reg8 = (volatile u8*)ptr;
54  if (satcat5::HOST_BYTE_ORDER() == satcat5::SATCAT5_BIG_ENDIAN) {
55  if (mask & 0x08) reg8[0] = val8[0];
56  if (mask & 0x04) reg8[1] = val8[1];
57  if (mask & 0x02) reg8[2] = val8[2];
58  if (mask & 0x01) reg8[3] = val8[3];
59  } else {
60  if (mask & 0x01) reg8[0] = val8[0];
61  if (mask & 0x02) reg8[1] = val8[1];
62  if (mask & 0x04) reg8[2] = val8[2];
63  if (mask & 0x08) reg8[3] = val8[3];
64  }
65 #endif
66 }
67 
68 satcat5::eth::ProtoConfig::ProtoConfig(
71  unsigned max_devices)
72  : satcat5::net::ProtoConfig(
73  cfg, iface, TYPE_ETH_CMD, TYPE_ETH_ACK, max_devices)
74 {
75  // Nothing else to initialize.
76 }
77 
78 satcat5::udp::ProtoConfig::ProtoConfig(
81  unsigned max_devices)
82  : satcat5::net::ProtoConfig(
83  cfg, iface, TYPE_UDP_CMD, TYPE_UDP_ACK, max_devices)
84 {
85  // Nothing else to initialize.
86 }
87 
88 ProtoConfig::ProtoConfig(
91  const Type& cmd,
92  const Type& ack,
93  unsigned max_devices)
94  : satcat5::net::Protocol(cmd)
95  , m_cfg(cfg)
96  , m_iface(iface)
97  , m_acktype(ack)
98  , m_max_devices(max_devices)
99 {
100  m_iface->add(this);
101 }
102 
103 #if SATCAT5_ALLOW_DELETION
105 {
106  m_iface->remove(this);
107 }
108 #endif
109 
111 {
112  // Sanity check for the main header.
113  if (src.get_read_ready() < 8) {
114  log::Log(log::ERROR, "ProtoConfig: Invalid command");
115  return;
116  }
117 
118  // Read header contents.
119  u8 opcode = src.read_u8();
120  u8 len8 = src.read_u8();
121  u8 seq = src.read_u8();
122  u8 rsvd = src.read_u8();
123  u32 addr = src.read_u32();
124 
125  u8 cmd = opcode & OPMASK_CMD;
126  u8 wren = opcode & OPMASK_WREN;
127  unsigned cmd_len = 1 + (unsigned)len8;
128 
129  // Diagnostics mimic the format used in "cfgbus_remote.cc".
130  if (DEBUG_VERBOSE > 0) {
131  log::Log(log::DEBUG, "ProtoConfig: Received command")
132  .write(opcode).write(addr).write((u16)cmd_len);
133  }
134 
135  // Predict reply-length.
136  unsigned reply_bytes = 8;
137  if (cmd == OPCODE_READ0 || cmd == OPCODE_READ1) {
138  reply_bytes += 4*cmd_len + 1;
139  }
140 
141  // Attempt to open reply packet.
142  satcat5::io::Writeable* dst = m_iface->open_reply(m_acktype, reply_bytes);
143  if (!dst) {
144  log::Log(log::WARNING, "ProtoConfig: Reply error");
145  return;
146  }
147 
148  // Start writing reply header.
149  dst->write_u8(opcode);
150  dst->write_u8(len8);
151  dst->write_u8(seq);
152  dst->write_u8(rsvd);
153  dst->write_u32(addr);
154 
155  // Get the read/write pointer.
156  volatile u32* regptr = m_cfg->get_register_mmap(addr);
157  u32 devaddr = (addr / REGS_PER_DEVICE);
158  u32 regaddr = (addr % REGS_PER_DEVICE);
159 
160  // Execute selected opcode:
161  const char* errmsg = 0;
162  if (cmd == OPCODE_NOOP) {
163  // No-op, send reply but take no other action.
164  } else if (devaddr >= m_max_devices
165  || regaddr >= REGS_PER_DEVICE
166  || regaddr + cmd_len > REGS_PER_DEVICE) {
167  errmsg = "Bad address";
168  } else if ((cmd == OPCODE_WRITE0 || cmd == OPCODE_WRITE1) && wren) {
169  // Sanity check on length, then execute writes.
170  if (src.get_read_ready() < 4*cmd_len) {
171  errmsg = "Bad length";
172  } else if (SATCAT5_PROTOCFG_SUPPORT_WRMASK && wren < OPMASK_WREN) {
173  for (unsigned a = 0 ; a < cmd_len ; ++a) { // Partial writes
174  write_mask(regptr, src.read_u32(), wren);
175  if (cmd == OPCODE_WRITE1) ++regptr;
176  }
177  } else {
178  for (unsigned a = 0 ; a < cmd_len ; ++a) { // Full-word writes
179  *regptr = src.read_u32();
180  if (cmd == OPCODE_WRITE1) ++regptr;
181  }
182  }
183  } else if ((cmd == OPCODE_READ0 || cmd == OPCODE_READ1) && !wren) {
184  // Execute reads and add each result to reply.
185  for (unsigned a = 0 ; a < cmd_len ; ++a) {
186  dst->write_u32(*regptr);
187  if (cmd == OPCODE_READ1) ++regptr;
188  }
189  // Read-error flag is always zero (not supported).
190  dst->write_u8(0);
191  } else {
192  errmsg = "Bad opcode";
193  }
194 
195  // Attempt to send reply?
196  if (errmsg) {
197  dst->write_abort();
198  } else {
199  bool ok = dst->write_finalize();
200  if (ok && DEBUG_VERBOSE > 1) {
201  log::Log(log::DEBUG, "ProtoConfig: Sent response")
202  .write(opcode).write(addr).write((u16)cmd_len);
203  }
204  if (!ok) errmsg = "Reply error";
205  }
206 
207  // Log any errors that occur.
208  if (errmsg) log::Log(log::ERROR, "ProtoConfig: ").write(errmsg);
209 }
ConfigBus core definitions.
const unsigned REGS_PER_DEVICE
Fixed ConfigBus gateware interface parameters.
Definition: cfgbus_core.h:45
Memory-mapped local ConfigBus.
Definition: cfgbus_core.h:199
volatile u32 * get_register_mmap(unsigned addr) const
Get a raw pointer to the designated combined-address.
Definition: cfgbus_core.h:214
Implemention of "net::Dispatch" for Ethernet frames.
Definition: eth_dispatch.h:21
Limited read of next N bytes.
Definition: io_readable.h:255
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
u8 read_u8()
One of many functions for reading integer/floating point values, see details.
Definition: io_readable.cc:44
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual void write_abort()
If possible, abort the current partially-written packet.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
The Log class creates and formats one log message.
Definition: log.h:195
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
The "Dispatch" is a generic interface that knows how to read a designated protocol layer and sort inc...
Definition: net_dispatch.h:36
void add(satcat5::net::Protocol *proto)
Register a Protocol object.
Definition: net_dispatch.h:55
virtual satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned len)=0
Open a reply to the sender of the most recent message by writing frame header(s) and returning a stre...
void remove(satcat5::net::Protocol *proto)
Unregister a Protocol object.
Definition: net_dispatch.h:59
Handler for ConfigBus network commands.
Definition: net_cfgbus.h:29
~ProtoConfig() SATCAT5_OPTIONAL_DTOR
Only children can access constructor/destructor.
Definition: net_cfgbus.cc:104
satcat5::cfg::ConfigBusMmap *const m_cfg
ConfigBus instance.
Definition: net_cfgbus.h:46
void frame_rcvd(satcat5::io::LimitedRead &src) override
Event handler to process incoming frames and respond.
Definition: net_cfgbus.cc:110
satcat5::net::Dispatch *const m_iface
Registered iface.
Definition: net_cfgbus.h:47
satcat5::net::Type const m_acktype
Type for replies.
Definition: net_cfgbus.h:48
const unsigned m_max_devices
Max # of devices.
Definition: net_cfgbus.h:49
A net::Protocol is the counterpart to net::Dispatch that handles a particular data stream,...
Definition: net_protocol.h:28
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
Diagnostic logging to UART and/or Ethernet ports.
Multipurpose filter for matching fields in network packets.
Definition: net_type.h:38