-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Transmission pipeline and cosmetic adjustments to the API #34
Conversation
…k to 10k to speed it up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming we don't have the work in here to handle a low-priority, long-timeout item in a tx queue?
libudpard/udpard.c
Outdated
#define SUBJECT_MULTICAST_GROUP_ADDRESS_MASK 0xEF000000UL | ||
#define SERVICE_MULTICAST_GROUP_ADDRESS_MASK 0xEF010000UL | ||
|
||
UDPARD_PRIVATE uint32_t makeSubjectIPGroupAddress(const UdpardPortID subject_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think we should switch. I don't mind having a bunch of individual test executables. It's how I structured CETL even without the need to test cpp internals.
…rd.c>; not buildable" This reverts commit a35339c.
No, I considered this to not be part of what we defined as "significant completion". This will be taken care of after the core functionality is in place: #33
Okay, I just did that and discovered (too late) that it won't work. The problem is that it is difficult to compile a C file using a C++ compiler without suppressing a very large set of sensible compiler warnings. Clang-Tidy and Sonar can also be made to not emit diagnostics for the included C file but it it slightly less trivial than I would like it to be. As C and C++ are, after all, very different languages, I'm worried that by compiling C with a C++ compiler we may create maintenance issues for us in the future, particularly if we decide to switch to a newer version of either standard at one point. You can see what it looks like in the second-to-last commit (the next one reverts it). I suggest keeping the existing design as-is. The presence of |
import the .c into an
|
Could you please elaborate on the value of this approach compared to |
It avoids polluting the production code with a test macro or any knowledge of the tests. I can accept any crazy plan needed in unit tests but not in the product. |
…ard.c directly (#37)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, all my comments are just nits.
libudpard/udpard.h
Outdated
|
||
/// The port number is defined in the Cyphal/UDP Specification. The same port number is used for all transfer kinds. | ||
#define UDPARD_UDP_PORT 9382U | ||
#define UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME (UDPARD_MTU_MAX - 4U) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably just blind, but where is UDPARD_MTU_MAX
defined? Should this be UDPARD_MTU_DEFAULT - 4U
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, this went unnoticed because there was no test for this. There is now, it's called testMakeChainSingleFrameDefaultMTU
and it ensures that UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME
-sized payload fits into a UDPARD_MTU_DEFAULT
-sized frame, and a spillover occurs when the payload is enlarged by 1 byte.
/// See UDPARD_MTU_*. | ||
/// The value can be changed arbitrarily at any time between enqueue operations. | ||
size_t mtu_bytes; | ||
/// The value is constrained by the library to be positive. | ||
size_t mtu; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not mtu_bytes
? Would prefer to keep the units
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no strong preference either way but the constants do not mention bytes and MTU is in bytes (octets) by definition. We can change it back if needed.
libudpard/udpard.c
Outdated
break; | ||
} | ||
const bool last = (payload_size_with_crc - offset) <= mtu; | ||
byte_t* write_ptr = txSerializeHeader(&tqi->payload_buffer[0], meta, (uint32_t) out.count, last); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're using out.count
as the frame index, could you make the count
member of TxChain
a uint32_t
so you don't have to cast here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
count
is used to represent the number of items allocated in memory without relation to the design of the protocol header (that part belongs to a different scope), so I assumed it is reasonable to usesize_t
here to avoid scope leakage.- Usage of
uint32_t
requires a cast attx->queue_size += chain.count
in narrow-word platforms like AVR (caught by the CI).
UdpardPriority priority; ///< Do we need this exposed in the public structure? We already have DSCP there. | ||
// The MISRA violation here is hard to get rid of without having to allocate a separate memory block for the | ||
// payload, which is much more costly risk-wise. | ||
byte_t payload_buffer[]; // NOSONAR MISRA C 18.7 Flexible array member. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you choosing not to make the payload buffer the size of UDPARD_MTU_DEFAULT
to save on memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is not the main reason. The main reason is to support jumbo frames with MTU >> 1500 bytes.
libudpard/udpard.c
Outdated
const uint32_t frame_index, | ||
const bool end_of_transfer) | ||
{ | ||
byte_t* p = destination_buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a more descriptive variable name we could use instead of p
or this common practice to refer to a generic pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think p
is common, but on the other hand, some style guides prohibit single-word names except i,j,k, reserved for loop counters, so I changed it to ptr
.
libudpard/udpard.c
Outdated
user_transfer_reference); | ||
if (NULL == out.head) | ||
{ | ||
out.head = tqi; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does tqi
stand for? tx_queue_item
? We have a style guideline that I like about not abbreviating identifiers by removing letters since it helps with readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed as item
.
libudpard/udpard.c
Outdated
// ===================================================================================================================== | ||
|
||
/// This is a subclass of UdpardTxItem. A pointer to this type can be cast to UdpardTxItem safely. | ||
/// This is standard-compliant. The paragraph 6.7.2.1.15 says: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which standard? The C standard?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
return p; | ||
} | ||
|
||
static inline byte_t* txSerializeHeader(byte_t* const destination_buffer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function would be more readable if we had a struct for the frame header like we did before:
https://github.com/OpenCyphal-Garage/libudpard/blob/main/libudpard/udpard.h#L133
https://github.com/OpenCyphal-Garage/libudpard/blob/main/libudpard/udpard.c#L325
And then populated the fields of the struct rather than using pointer arithmetic.
Is there a reason this new implementation moved away from that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The old approach is endian-sensitive.
- I think that TransferMetadata offers a stronger and more useful abstraction because it encapsulates all transfer-specific metadata in a single entity that is independent of values that vary within the transfer (the frame index and the EOR flag), which allows passing it unmodified from the top-level function to the header serializer.
Kudos, SonarCloud Quality Gate passed! |
Nudging @thirtytwobits to proceed with the review because I'm looking into integrating libudpard - once ready - into 107-Arduino-Cyphal 😁 |
👀 |
Please focus on
udpard.c
andudpard.h
. Upon a closer inspection it appeared to be easier to produce a completely new implementation that exploits the design features of Cyphal/UDP instead of adapting the original CAN implementation.The code is fully covered by tests; Sonar reports a slightly lower value because of imperfections of llvm-cov.
Golden tests were produced with the help of PyCyphal; relevant Python snippets are provided in the comments.
The commit history is uninformative and should not be looked at.