tinygo/builder/uf2.go
Ayke van Laethem 8e6cb89ceb main: refactor compile/link part to a builder package
This is a large commit that moves all code directly related to
compiling/linking into a new builder package. This has a number of
advantages:

  * It cleanly separates the API between the command line and the full
    compilation (with a very small API surface).
  * When the compiler finally compiles one package at a time (instead of
    everything at once as it does now), something will have to invoke it
    once per package. This builder package will be the natural place to
    do that, and also be the place where the whole process can be
    parallelized.
  * It allows the TinyGo compiler to be used as a package. A client can
    simply import the builder package and compile code using it.

As part of this refactor, the following additional things changed:

  * Exported symbols have been made unexported when they weren't needed.
  * The compilation target has been moved into the compileopts.Options
    struct. This is done because the target really is just another
    compiler option, and the API is simplified by moving it in there.
  * The moveFile function has been duplicated. It does not really belong
    in the builder API but is used both by the builder and the command
    line. Moving it into a separate package didn't seem useful either
    for what is essentially an utility function.
  * Some doc strings have been improved.

Some future changes/refactors I'd like to make after this commit:

  * Clean up the API between the builder and the compiler package.
  * Perhaps move the test files (in testdata/) into the builder package.
  * Perhaps move the loader package into the builder package.
2019-11-11 20:53:50 +01:00

130 строки
3,5 КиБ
Go

package builder
// 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"
"io/ioutil"
)
// convertELFFileToUF2File converts an ELF file to a UF2 file.
func convertELFFileToUF2File(infile, outfile string) error {
// Read the .text segment.
targetAddress, data, err := extractROM(infile)
if err != nil {
return err
}
output, _ := convertBinToUF2(data, uint32(targetAddress))
return ioutil.WriteFile(outfile, output, 0644)
}
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
blocks := split(input, 256)
output := make([]byte, 0)
bl := newUF2Block(targetAddr)
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)
}
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) *uf2Block {
return &uf2Block{magicStart0: uf2MagicStart0,
magicStart1: uf2MagicStart1,
magicEnd: uf2MagicEnd,
targetAddr: targetAddr,
flags: 0x0,
familyID: 0x0,
payloadSize: 256,
data: make([]byte, 476),
}
}
// 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 {
block, input = input[:limit], input[limit:]
output = append(output, block)
}
if len(input) > 0 {
output = append(output, input[:len(input)])
}
return output
}