From b9752da8ac84786158bfac848f4af23fd98cc5aa Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Fri, 2 Dec 2016 14:53:43 -0800 Subject: [PATCH] Initial --- .gitignore | 2 ++ README.md | 37 +++++++++++++++++++++++++++++++++++++ uf2.h | 34 ++++++++++++++++++++++++++++++++++ utils/uf2conv.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 uf2.h create mode 100644 utils/uf2conv.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c2098d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tmp +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..eccb012 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# USB Flashing Format (UF2) + +UF2 (USB Flashing Format) is a name of a file format, that is particularly +suitable for flashing devices over MSC (Mass Storage Class most commonly used +by various pendrives) The file consists of 512 byte blocks, each of which is +self-contained and independent of others. + +Each 512 byte block consist of (see `uf2.h` for details): +* magic numbers at the beginning and at the end +* address where the data should be flashed +* size of data +* data (up to 476 bytes; for SAMD it's 256 bytes so it's easy to flash in one go) + +Thus, it's really easy for the microcontroller to recognize a block of +a UF2 file is written and immediately write it to flash. + +In `uf2conv.c` you can find a small converter from `.bin` to `.uf2`. + +## Board identification + +There is also `BOARD_ID`, which is meant to be machine-readable and specific +to a given version of board hardware. The programming environment might use +this to suggest packages to be imported (i.e., a package for a particular +external flash chip, SD card etc.). + +These configuration values can be read from `INFO_UF2.TXT` file. +Presence of this file can be tested to see if the board supports `UF2` flashing, +while contest, particularly `Board-ID` field, can be used for feature detection. + +The current flash contents of the board is exposed as `CURRENT.UF2` file. +This file includes the bootloader address space. The last word of bootloader +space points to the string holding the `INFO_UF2.TXT` file, so it can be parsed +by a programming environment to determine which board does the `.UF2` file comes from. + +## License + +MIT diff --git a/uf2.h b/uf2.h new file mode 100644 index 0000000..753e04b --- /dev/null +++ b/uf2.h @@ -0,0 +1,34 @@ +#ifndef UF2FORMAT_H +#define UF2FORMAT_H 1 + +#include +#include + +// All entries are little endian. + +#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" +#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected +#define UF2_MAGIC_END 0x0AB16F30UL // Ditto + +// If set, the block is "comment" and should not be flashed to the device +#define UF2_FLAG_NOFLASH 0x00000001 + +typedef struct { + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t reserved; + + // raw data; + uint8_t data[476]; + + // store magic also at the end to limit damage from partial block reads + uint32_t magicEnd; +} UF2_Block; + +#endif diff --git a/utils/uf2conv.c b/utils/uf2conv.c new file mode 100644 index 0000000..0f5f16e --- /dev/null +++ b/utils/uf2conv.c @@ -0,0 +1,45 @@ +#include +#include +#include "uf2format.h" + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "USAGE: %s file.bin [file.uf2]\n", argv[0]); + return 1; + } + FILE *f = fopen(argv[1], "rb"); + if (!f) { + fprintf(stderr, "No such file: %s\n", argv[1]); + return 1; + } + + fseek(f, 0L, SEEK_END); + uint32_t sz = ftell(f); + fseek(f, 0L, SEEK_SET); + + const char *outname = argc > 2 ? argv[2] : "flash.uf2"; + + FILE *fout = fopen(outname, "wb"); + + UF2_Block bl; + memset(&bl, 0, sizeof(bl)); + + bl.magicStart0 = UF2_MAGIC_START0; + bl.magicStart1 = UF2_MAGIC_START1; + bl.magicEnd = UF2_MAGIC_END; + bl.targetAddr = APP_START_ADDRESS; + bl.numBlocks = (sz + 255) / 256; + bl.payloadSize = 256; + int numbl = 0; + while (fread(bl.data, 1, bl.payloadSize, f)) { + bl.blockNo = numbl++; + fwrite(&bl, 1, sizeof(bl), fout); + bl.targetAddr += bl.payloadSize; + // clear for next iteration, in case we get a short read + memset(bl.data, 0, sizeof(bl.data)); + } + fclose(fout); + fclose(f); + printf("Wrote %d blocks to %s\n", numbl, outname); + return 0; +} \ No newline at end of file