How to load .hex bytes for one AVR into another as constant

Go To Last Post
3 posts / 0 new
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Suppose I have an assembled and linked .hex file that was built for a Tiny2313. Somehow, I need to get the raw bytes represented by that .hex file into a Mega162 as byte constants that can be read by the Mega162 so the Mega162 can send it to a bootloader running on Tiny2313 chips connected to the Mega162:

ldi XH, high(flashcode)
ldi XL, low(flashcode)
// ... add some whole multiple of 32 
// ... and read 32 bytes for broadcasting to eager, waiting bootloader:
ld r16, X+
rcall sendByteToWaitingBootloader
// etc

// the next line basically shows what I want to do, though obviously not the right way to do it...
.org 0x400 ; just an example
flashcode: .db #include "newcodeforslaves.hex"

How can I do it?

Remember, I don't want to literally load the ascii values for each character in the .hex file... I want to load the bytes represented by them. If, say, the .hex file represented 2000 bytes, and flashcode: points at flash memory location 0x400 in the Mega162, I'd want the first byte at 0x400, the second at 0x401, etc... all the way to the end. Assuming, of course, that there aren't any other hidden problems waiting to bite me.


Background Info on the bootloader scheme I came up with (not strictly necessary to answer the problem above):

A mega162 controller's TXD pin is connected to the RXD pins of 50 Tiny2313 slaves at roughly 1 foot intervals.

At reset, the Tiny2313s wait up to 1 second for something (presumably the controller) to pull and hold the bus low for at least 1/10 of a second. If nothing pulls the bus low, they go into "headless" mode and assume there's no controller. At that point, they look for a signature at the end of .cseg indicating that code to handle that scenario exists, and do an rjmp to 0x302 if it does (where, presumably, there's another rjmp leading to that code). Otherwise, they go into "zombie" mode (headless and stupid, they nevertheless try to output random colors to four RGB leds).

If the bus is pulled low for at least 1/10 second, but less than 3 seconds, the slaves do an rjmp to 0x300, where there's presumably another rjmp to the main event loop's entry point which enables their USARTs and waits for normal operating commands from the controller (this is the normal case).

If the bus stays low for more than 10 seconds, they assume any code outside the safe core is corrupt and go straight into zombie mode.

If the bus is held low for more than 3 seconds, but less than 10 seconds, the slaves enter bootloader mode and prepare to reflash themselves. The basic sequence is this:

* multi-processor mode is set for the USART.

* the master sends 0x69 flagged as an address. The slaves see it and clear MPCM.

* the master sends a byte representing the page to be reflashed twice. If the two bytes differ, the slave turns the LEDs red and aborts the whole thing.

* the master sends 32 bytes, representing data from a .hex file some whole multiple of 32 from the reset vector at 0x0000. They're buffered in the first 32 bytes of the slave's sram.

* the master sends another 32 bytes, presumably representing the same data. They're buffered in the second 32 bytes of the slave's sram.

* the master sends the crc32 twice, pauses, deactivates its USART, turns the pin into an input, pauses, then waits for the bus to go high.

* Meanwhile, the slaves deactivate their USART, turn their pin into an output, and pull it low to indicate that they're busy. They proceed to verify that both new pages are identical and match the crc32, then compare them to the existing page. If there's no difference, they turn the LEDs green, turn their pins into inputs, and wait for the bus to go high. Otherwise, they rewrite the page and verify it (retrying up to 3 times if they fail).

* When the bus goes high, the master and slaves all re-enable their USARTs. The master sends 0xc5 ("need another try?"), the page offset (twice), the crc32 (twice), then disables the usart and watches the bus for 2 seconds to see whether anyone pulls it low. The slaves that reflashed successfully (or didn't need to reflash) just set multiple CPU mode so they can ignore the next round of broadcasts, sit there and wait. Slaves that couldn't reflash due to a data error disable the usart, pull the bus low for 1 second, then re-enable their usarts, whereupon the pages and crcs are resent, and everything continues as before. I haven't really decided how many times I want to retry before either aborting the whole reflash or skipping ahead to the next page anyway.

There's no place like ~/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The SRecord utility (it comes with WinAVR, but it is available as a standalone utility) has the ability to convert between various binary data formats.

It includes a filter to import ".hex" files into its binary buffer.

It also includes a filter to output its buffer into a C array. That is, if you had the hex file:


SRecord could convert it to a C file containing the expression

char data[] = {0x0C, 0x94, 0xBD, 0x17, 0x0C, 0x94, 0x9B, 0x74, ...};

Then you could link the C file into the rest of your master's source code, and away you go!

Is this the sort of thing you're looking for?

[edit] I see that you're using assembly. Well, SRecord can also produce a series of generic "DB" expressions (with decimal arguments) from its binary filter.

Assuming you're using an assembler that recognizes the "DB" directive, then you can use that, and #include the generated file.

I believe the Atmel assembler expects ".DB", not "DB". That means you'd need an intervening step where you search-and-replace every instance of "DB" with ".DB". You're back in business. [/edit]

[edit 2] A final consideration is that SRecord seems to create DB records with a fixed upper limit on the number of characters per line (rather than the number of storage bytes encoded by those characters). So it possible to see a record with 17 bytes on the line... that's a bad thing because the Atmel assembler will always word-align the final entry at the end of each DB record. Fortunately, the assembler issues a warning whenever it performs a zero-padding like that, so you could probably do a case-by-case massage of the affected lines to make sure they all line up correctly. [/edit]

[edit 3] It would be much easier if you were programming in C and could make use of SRecord's C filter instead!! You wouldn't need to massage that at all, except for the directive to locate the array in Flash.[/edit]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I stumbled upon this thread this morning when I was looking for a way to combine hex files for a similar purpose. SRecord is exactly what I was looking for. At first I used the c-array output of SRecord to include the other hex file in my application, but then I decided it would be better to simply combine the two hex files together after everything is compiled. That way, I won't need to recompile my application if the other hex file changes.

To combine my application and the other hex file, I issue a command similar to this:

srec_cat.exe app.hex -intel boot.hex -intel -offset=-0x1E000 -offset=0x10000 -output out.hex -intel -address-length=2 -line-length=43

-intel specifies that the file should be intel-hex
-offset=-0x1E000 moves the addresses of boot.hex down to 0 (0x1E000 is where the other app's bootloader starts)
-offset=0x10000 moves them back to 0x10000 (where i want it to appear in my application)
-address-length=2 forces the output to use 16-bit addressing
-line-length=43 limits the lines to 16 bytes of real data on each line

address-length and line-length are probably completely unnecessary, but it creates output that is identical to what IAR outputs, so that it's easier for me to compare files by hand.

The two offset options could be combined into a single offset option, -offset=-0xE000, but its easier to see where i'm moving it FROM and where i'm moving it TO by using two offsets.

Hope this helps, and thanks for helping me find SRecord =)

/* John Butera */