binary-format supports uf2, nrf-dfu
This commit is contained in:
155
internal/firmware/uf2.go
Normal file
155
internal/firmware/uf2.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// From tinygo/builder/uf2.go
|
||||
|
||||
package firmware
|
||||
|
||||
// This file converts firmware files from BIN to UF2 format before flashing.
|
||||
//
|
||||
// For more information about the UF2 firmware file format, please see:
|
||||
// https://github.com/Microsoft/uf2
|
||||
//
|
||||
//
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// convertELFFileToUF2File converts an ELF file to a UF2 file.
|
||||
func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
|
||||
// Read the .text segment.
|
||||
targetAddress, data, err := extractROM(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(outfile, output, 0644)
|
||||
}
|
||||
|
||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
||||
func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) {
|
||||
blocks := split(input, 256)
|
||||
output := make([]byte, 0)
|
||||
|
||||
bl, err := newUF2Block(targetAddr, uf2FamilyID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
bl.SetNumBlocks(len(blocks))
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
bl.SetBlockNo(i)
|
||||
bl.SetData(blocks[i])
|
||||
|
||||
output = append(output, bl.Bytes()...)
|
||||
bl.IncrementAddress(bl.payloadSize)
|
||||
}
|
||||
|
||||
return output, len(blocks), nil
|
||||
}
|
||||
|
||||
const (
|
||||
uf2MagicStart0 = 0x0A324655 // "UF2\n"
|
||||
uf2MagicStart1 = 0x9E5D5157 // Randomly selected
|
||||
uf2MagicEnd = 0x0AB16F30 // Ditto
|
||||
)
|
||||
|
||||
// uf2Block is the structure used for each UF2 code block sent to device.
|
||||
type uf2Block struct {
|
||||
magicStart0 uint32
|
||||
magicStart1 uint32
|
||||
flags uint32
|
||||
targetAddr uint32
|
||||
payloadSize uint32
|
||||
blockNo uint32
|
||||
numBlocks uint32
|
||||
familyID uint32
|
||||
data []uint8
|
||||
magicEnd uint32
|
||||
}
|
||||
|
||||
// newUF2Block returns a new uf2Block struct that has been correctly populated
|
||||
func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) {
|
||||
var flags uint32
|
||||
var familyID uint32
|
||||
if uf2FamilyID != "" {
|
||||
flags |= flagFamilyIDPresent
|
||||
v, err := strconv.ParseUint(uf2FamilyID, 0, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
familyID = uint32(v)
|
||||
}
|
||||
return &uf2Block{magicStart0: uf2MagicStart0,
|
||||
magicStart1: uf2MagicStart1,
|
||||
magicEnd: uf2MagicEnd,
|
||||
targetAddr: targetAddr,
|
||||
flags: flags,
|
||||
familyID: familyID,
|
||||
payloadSize: 256,
|
||||
data: make([]byte, 476),
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
flagFamilyIDPresent = 0x00002000
|
||||
)
|
||||
|
||||
// Bytes converts the uf2Block to a slice of bytes that can be written to file.
|
||||
func (b *uf2Block) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 512))
|
||||
binary.Write(buf, binary.LittleEndian, b.magicStart0)
|
||||
binary.Write(buf, binary.LittleEndian, b.magicStart1)
|
||||
binary.Write(buf, binary.LittleEndian, b.flags)
|
||||
binary.Write(buf, binary.LittleEndian, b.targetAddr)
|
||||
binary.Write(buf, binary.LittleEndian, b.payloadSize)
|
||||
binary.Write(buf, binary.LittleEndian, b.blockNo)
|
||||
binary.Write(buf, binary.LittleEndian, b.numBlocks)
|
||||
binary.Write(buf, binary.LittleEndian, b.familyID)
|
||||
binary.Write(buf, binary.LittleEndian, b.data)
|
||||
binary.Write(buf, binary.LittleEndian, b.magicEnd)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// IncrementAddress moves the target address pointer forward by count bytes.
|
||||
func (b *uf2Block) IncrementAddress(count uint32) {
|
||||
b.targetAddr += b.payloadSize
|
||||
}
|
||||
|
||||
// SetData sets the data to be used for the current block.
|
||||
func (b *uf2Block) SetData(d []byte) {
|
||||
b.data = make([]byte, 476)
|
||||
copy(b.data[:], d)
|
||||
}
|
||||
|
||||
// SetBlockNo sets the current block number to be used.
|
||||
func (b *uf2Block) SetBlockNo(bn int) {
|
||||
b.blockNo = uint32(bn)
|
||||
}
|
||||
|
||||
// SetNumBlocks sets the total number of blocks for this UF2 file.
|
||||
func (b *uf2Block) SetNumBlocks(total int) {
|
||||
b.numBlocks = uint32(total)
|
||||
}
|
||||
|
||||
// split splits a slice of bytes into a slice of byte slices of a specific size limit.
|
||||
func split(input []byte, limit int) [][]byte {
|
||||
var block []byte
|
||||
output := make([][]byte, 0, len(input)/limit+1)
|
||||
for len(input) >= limit {
|
||||
// add all blocks
|
||||
block, input = input[:limit], input[limit:]
|
||||
output = append(output, block)
|
||||
}
|
||||
if len(input) > 0 {
|
||||
// add remaining block (that isn't full sized)
|
||||
output = append(output, input)
|
||||
}
|
||||
return output
|
||||
}
|
||||
Reference in New Issue
Block a user