GCC Code Coverage Report


Directory: lib/fprime-samd/fprime-samd/
File: Svc/Framer/Framer.cpp
Date: 2026-10-20 16:48:53
Exec Total Coverage
Lines: 66 72 91.7%
Functions: 7 7 100.0%
Branches: 63 118 53.4%

Line Branch Exec Source
1 // ======================================================================
2 // \title Framer.cpp
3 // \author tumbar
4 // \brief cpp file for Framer component implementation class
5 // ======================================================================
6
7 #include "fprime-samd/Svc/Framer/Framer.hpp"
8 #include "Drv/ByteStreamDriverModel/ByteStreamStatusEnumAc.hpp"
9 #include "Fw/Com/ComBuffer.hpp"
10 #include "Fw/Types/Assert.hpp"
11 #include "Svc/FprimeProtocol/FrameHeaderSerializableAc.hpp"
12 #include "Svc/FprimeProtocol/FrameTrailerSerializableAc.hpp"
13 #include "Utils/Hash/Hash.hpp"
14 #include "samd-config/FramerConfig.hpp"
15
16 namespace Samd21 {
17
18 // ----------------------------------------------------------------------
19 // Component construction and destruction
20 // ----------------------------------------------------------------------
21
22 6 Framer ::Framer(const char* const compName)
23 6 : FramerComponentBase(compName), m_driverConnected(false), m_activeBufferIdx(0), m_droppedPackets(0) {
24 // Initialize both buffers to IDLE state with space reserved for header
25
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (FwIndexType i = 0; i < 2; i++) {
26 12 m_buffers[i].size = 0;
27 12 m_buffers[i].state = IDLE;
28 }
29 6 }
30
31 12 Framer ::~Framer() {}
32
33 // ----------------------------------------------------------------------
34 // Handler implementations for typed input ports
35 // ----------------------------------------------------------------------
36
37 23 void Framer ::comPacketQueueIn_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
38 23 TxBuffer* activeBuf = &m_buffers[m_activeBufferIdx];
39
40 // Calculate space needed: frame header + ComBuffer data + trailer
41
1/2
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 FwSizeType spaceNeeded = Svc::FprimeProtocol::FrameHeader::SERIALIZED_SIZE + data.getSize() +
42 23 Svc::FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
43
44 // If both buffers are in flight (backpressure), drop this packet
45
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
23 if (activeBuf->state == TRANSMITTING) {
46 ✗ this->m_droppedPackets++;
47 ✗ return;
48 }
49
50 // If adding this ComBuffer would overflow, flush first
51
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 22 times.
23 if (activeBuf->size + spaceNeeded > FramerConfig::FRAMER_TX_BUFFER_SIZE) {
52
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 flushActiveBuffer();
53
54 // flushActiveBuffer() swapped the active buffer -- re-acquire it
55 1 activeBuf = &m_buffers[m_activeBufferIdx];
56
57 // If both buffers are in flight (backpressure), drop this packet
58
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (activeBuf->state == TRANSMITTING) {
59 ✗ this->m_droppedPackets++;
60 ✗ return;
61 }
62
63 // After flush, the swapped-in buffer should be idle and empty
64
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 FW_ASSERT(activeBuf->state == IDLE);
65
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 FW_ASSERT(activeBuf->size == 0);
66 }
67
68 // Mark buffer as active if it's idle
69
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 12 times.
23 if (activeBuf->state == IDLE) {
70 11 activeBuf->state = ACTIVE;
71 }
72
73 // Append ComBuffer data directly to the buffer (after existing data)
74
1/2
✓ Branch 8 taken 23 times.
✗ Branch 9 not taken.
23 Fw::Buffer bufWrapper(activeBuf->data + activeBuf->size, FramerConfig::FRAMER_TX_BUFFER_SIZE - activeBuf->size);
75
3/6
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
23 Fw::ExternalSerializeBuffer serializer(bufWrapper.getData(), bufWrapper.getSize());
76
77 // Write the packet header
78
2/6
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 23 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
23 FW_ASSERT(data.getSize() <= std::numeric_limits<Svc::FprimeProtocol::TokenType>::max(),
79 static_cast<FwAssertArgType>(data.getSize()));
80
81
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 Svc::FprimeProtocol::FrameHeader header;
82
2/4
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 23 times.
✗ Branch 10 not taken.
23 header.set_lengthField(static_cast<Svc::FprimeProtocol::TokenType>(data.getSize()));
83
84
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 Fw::SerializeStatus status = serializer.serializeFrom(header);
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
86
87 // Write the packet data
88
2/4
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 14 taken 23 times.
✗ Branch 15 not taken.
23 status = serializer.serializeFrom(data.getBuffAddr(), data.getSize(), Fw::Serialization::OMIT_LENGTH);
89
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
23 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status, data.getSize(), portNum);
90
91 // Compute the CRC of the packet (header + data)
92
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 Utils::HashBuffer hashBuffer;
93
1/2
✓ Branch 4 taken 23 times.
✗ Branch 5 not taken.
23 Utils::Hash::hash(activeBuf->data + activeBuf->size,
94
1/2
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
23 data.getSize() + Svc::FprimeProtocol::FrameHeader::SERIALIZED_SIZE, hashBuffer);
95
96 // Write trailer with CRC
97
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 Svc::FprimeProtocol::FrameTrailer trailer;
98
2/4
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
23 trailer.set_crcField(hashBuffer.asBigEndianU32());
99
100
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 status = serializer.serializeFrom(trailer);
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
102
103 // Update buffer size to include the packet frame
104
1/2
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
23 activeBuf->size += serializer.getSize();
105
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 FW_ASSERT(activeBuf->size <= FramerConfig::FRAMER_TX_BUFFER_SIZE);
106 23 }
107
108 6 void Framer ::drvConnected_handler(FwIndexType portNum) {
109 6 this->m_driverConnected = true;
110 6 }
111
112 5 void Framer ::drvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer, const Drv::ByteStreamStatus& status) {
113 // Find which buffer was returned
114
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 for (FwIndexType i = 0; i < 2; i++) {
115
6/8
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 2 times.
✓ Branch 15 taken 5 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 5 times.
✓ Branch 18 taken 2 times.
7 if (m_buffers[i].state == TRANSMITTING && fwBuffer.getData() == m_buffers[i].data) {
116 // Mark buffer as idle and ready for reuse, reset the size
117 5 m_buffers[i].state = IDLE;
118 5 m_buffers[i].size = 0;
119 5 return;
120 }
121 }
122 ✗ FW_ASSERT(false); // Buffer returned that we didn't send
123 }
124
125 10 void Framer ::flushActiveBuffer() {
126
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
10 FW_ASSERT(this->m_driverConnected);
127
128 10 TxBuffer& activeBuf = m_buffers[m_activeBufferIdx];
129
130 // Nothing to flush if buffer is idle or contains no data
131
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
10 if (activeBuf.state == IDLE || activeBuf.size == 0) {
132 ✗ return;
133 }
134
135 // Buffer must not already be in flight
136
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 FW_ASSERT(activeBuf.state == ACTIVE);
137
138 // Mark buffer as in-flight
139 10 activeBuf.state = TRANSMITTING;
140
141 // Switch to the next buffer
142 10 m_activeBufferIdx = (m_activeBufferIdx + 1) % 2;
143
144 // Send the complete frame to the driver
145
1/2
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
10 Fw::Buffer txBuffer(activeBuf.data, activeBuf.size);
146
1/2
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
10 drvSendOut_out(0, txBuffer);
147 10 }
148
149 10 void Framer ::schedIn_handler(FwIndexType portNum, U32 context) {
150
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
10 if (this->m_driverConnected) {
151 // Only flush if the other buffer is not transmitting
152 10 FwIndexType nextBufferIdx = (m_activeBufferIdx + 1) % 2;
153 10 TxBuffer& nextBuf = m_buffers[nextBufferIdx];
154
155
2/4
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
10 this->tlmWrite_DroppedPackets(this->m_droppedPackets);
156
157
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
10 if (nextBuf.state == TRANSMITTING) {
158 // Can't flush because both buffers would be in flight
159 // Wait for next schedule cycle
160 1 return;
161 }
162
163 9 flushActiveBuffer();
164 }
165 }
166
167 } // namespace Samd21
168