Texas Instruments 16-bit processor help

28 views
Skip to first unread message

Adrian Ellis

unread,
Mar 11, 2024, 9:51:24 AMMar 11
to nanopb
Hi,

I'm using a low power Texas Instruments processor (TMS320F28379d) which is a 16-bit processor. I'm trying to send a high data rate (100Hz) message across Ethernet (via SPI).

I can encode my messages using nanopb, but creating a checksum and packing the data makes it too slow. If I double my CPU clock (80MHz to 160MHz) I can get to 70 packets a second, but this is still a long way from 100 packets a second.

There is a compiler intrinsic (__byte) in the C28x compiler that provides access to the MSB/LSB at assembly level, that can speed things up.

I was wondering how difficult it would be to update nanopb to use this intrinsic to pack data into the uint16_t array that I create for the stream?

Thanks in advance

Adrian

Petteri Aimonen

unread,
Mar 11, 2024, 9:59:03 AMMar 11
to nan...@googlegroups.com
Hi,

> I was wondering how difficult it would be to update nanopb to use this
> intrinsic to pack data into the uint16_t array that I create for the stream?

Easiest way would be to make a custom output stream function.
The stream write function has to copy the data to output array anyway, so
it could pack it in progress.

https://jpa.kapsi.fi/nanopb/docs/concepts.html#streams

Something like this (untested):

pb_ostream_t create_ostream16(uint16_t *buf, size_t bufsize)
{
pb_ostream_t result;
result.callback = write_ostream16;
result.state = buf;
result.max_size = bufsize;
result.bytes_written = 0;
result.errmsg = NULL;
return result;
}

bool write_ostream16(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
{
uint16_t *dest = stream.state & ~1;
bool odd = stream.state & 1;

/* TODO: Copy "count" bytes from buf into dest, doing whatever is
* needed to handle any odd bytes at beginning and end.
*/

stream->state += count;
return true;
}

--
Petteri

Adrian Ellis

unread,
Mar 11, 2024, 1:25:49 PMMar 11
to nanopb
Hi Petteri,

Thanks for the rapid reply and the sample code. I've had a quick attempt at getting things to work but have run into an issue.
I guess that the "& ~1" and "& 1" are trying to work out the byte address from the "stream.state", but I don't think this is possible as "stream.state" is a pointer to uint16_t.
Can I use "stream.bytes_written" to be able to store the actual byte index?
It might be easier to leave "stream.state" pointing to the start of the array, and then use "stream.bytes_written" plus an offset as the index.
This would work nicely with the intrinsic where __byte(stream.state, 0) writes to the LSB of the first uint16_t and __byte(stream.state, 1) writes to the MSB of the first uint16_t.
No other calculations are then needed.

Does this sound feasible?

Thanks again

Adrian

Petteri Aimonen

unread,
Mar 11, 2024, 1:27:34 PMMar 11
to nanopb
Hi,

No, bytes_written cannot be directly used this way:

> Your callback may be used with substreams. In this case bytes_left, bytes_written and max_size have smaller values than the original stream. Don’t use these values to calculate pointers.

But you can use "state" as a pointer to a larger struct that can contain any fields you need.

--
Petteri

Adrian Ellis

unread,
Mar 13, 2024, 7:47:22 AMMar 13
to nanopb
Hi again,

Thanks for your help. I can now write the data straight into the output buffer using a custom stream definition.

Unfortunately it still isn't quite quick enough to meet the data rate I need. I think I've optimised things in my code as much as I can but the protobuf encoding is taking most of the time.

I know it's a long shot but do you think there are any parts of protobuf that could be optimised (or straight removed) to decrease the encoding time?

I'm already using static memory structures and I don't feel I've got a huge amount of fields to encode. (e.g. two oneofs, nine repeated fields, 5 numeric fields, three submessages, one of which is repeated).

Thanks again

Adrian

Petteri Aimonen

unread,
Mar 13, 2024, 7:50:53 AMMar 13
to nanopb
Hi,

If the message structure is simple enough, you could opt for manually encoding the fields.
This avoids the overhead of first filling a struct and then traversing it.

E.g.

pb_ostream_t stream = ...;
pb_encode_tag(&stream, PB_WT_VARINT, MyMessage_MyField_tag);
pb_encode_varint(&stream, 1234);
...

It will require some studying of the protobuf encoding spec.

--
Petteri
Reply all
Reply to author
Forward
0 new messages