Note: An OpenLCB Bootloader Protocol

it should be possible to load new programs into OpenLCB nodes connected via CAN bus. Even better would be a common protocol for that.

The rest of this note is where we develop that.

Environment of Proposal



Design Choices


This overlaps a little with questions of configuration: Both are changing the internal state of a node. (See the memory configuration protcol document) Do we want a single protocol that can do everything? The real difference about a bootloader is that you can't assume that the node is actually working. Cheap nodes don't have room for two copies of the program, so they have to retreat to a small corner of memory, erase the rest, and get the download. This is really different from modifying some configuration variables.

Does a OpenLCB node have to really be an OpenLCB node while downloading? E.g. if the little downloader kernel can't take part in various other interactions, what are the implications?

Legacy approach

An approach based on e.g. the Digitrax board load protocol, in OpenLCB terms:

Datagrams to set up the load process (request, get permission, negotiate stream)

Followed by a stream that contains the data. End of stream means “boot into new code”

That raises the question of the format of the data within the stream – Intel Hex format, with restrictions on e.g. data alignment and order.

What does the negotiation at the beginning doing?

It has to tell the code that a download is coming, but that could also be done i.e. by the stream content ID at the start of the stream.

Issue: How to handle timing for writing, and any requirement to erase first. Is this something that gets negotiated at the start? The download is prepared for specific hardware, so the programmer knows what the limitations are. Those could be specified and control how fast the data is being sent down the stream?

Datagram/stream approach

(From David Harris email)

So, I think you are suggesting that we use datagrams to set up a bidirectional stream between two nodes:

The originating node would do: Stream.setup(originating-NID, target-NID, originating-stream-id, originating-maxbuffer)

And expect a response from the target: Stream.setupAck(target-NID, originating-NID, target-stream-id, target-maxbuffer).

After this, the buffer-size has been negotiated as min(originating, target), zero means the stream has been rejected.

At this point each node can send stream messages to its opposite, and these stream-messages are identified by their NID:stream-id.

I suggest we let the stream-messages be one of two types: (1) 'command' messages, that inform the far-end of the intent and the content-format of subsequent data:
Stream.sendCommand(src-NID, src-stream-id, command, *data);

and (2) 'data' messages:
Stream.sendData(src-NID, src-stream-id, *data).

Examples of 'command' messages are:
(Error, type, data)
(Upload, mem-type, address)

An example of an stream might be:
--> Stream.setup(0001, 0055, 05, 200)
<-- Stream.setupAck(0055, 0001, 01, 100) // the stream is now set-up, with buffer of 100 bytes
--> Stream.sendCommand(0001, 05, upload, flash, 0x0000) // start an upload
--> Stream.sendData(0001, 05, 0x45, 0x64, 0x00, 0x55, 0x55, 0x55, ...)
--> Stream.sendData(0001, 05, 0xFF, 0xFE, 0x00, 0x8F, 0x55, 0x55, ...)
<-- Stream.sendCommand(0055, 01, dataProceed) // target can accept more data
--> Stream.sendCommand(0001, 05, upload, flash, 0x0100) // new upload address
--> Stream.sendData(0001, 05, 0x45, 0x64, 0x00, 0x55, 0x55, 0x55, ...)
--> Stream.sendData(0001, 05, 0xFF, 0xFE, 0x00, 0x8F, 0x55, 0x55, ...)
<-- Stream.sendCommand(0055, 01, dataProceed)
--> Stream.sendCommand(0001, 05, setVariable32, 0x0234, 0x12345678) // set a 32-bit variable
--> Stream.sendCommand(0001, 05, setVariable16, 0x0010, 0x1234) // set a 16-bit variable
--> Stream.sendCommand(0001, 05, reqVariable16, 0x0010) // request a variable
<-- Stream.sendCommand(0055, 01, repVariable16, 0x0010, 0x1234) // report a variable

I am not sure at all about the last stuff, may be duplicating what datagrams should be doing.

Site hosted by

This is SVN $Revision: 827 $