Skip to content

Commit 2a1dd98

Browse files
deadprogramaykevl
authored andcommitted
compiler: support output file using UF2 bootloader format
Signed-off-by: Ron Evans <[email protected]>
1 parent 2c03192 commit 2a1dd98

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,13 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
266266
if err != nil {
267267
return err
268268
}
269+
} else if outext == ".uf2" {
270+
// Get UF2 from the .elf file.
271+
tmppath = filepath.Join(dir, "main"+outext)
272+
err := ConvertELFFileToUF2File(executable, tmppath)
273+
if err != nil {
274+
return err
275+
}
269276
}
270277
return action(tmppath)
271278
}
@@ -321,6 +328,8 @@ func Flash(pkgName, target, port string, config *BuildConfig) error {
321328
fileExt = ".elf"
322329
case strings.Contains(spec.Flasher, "{bin}"):
323330
fileExt = ".bin"
331+
case strings.Contains(spec.Flasher, "{uf2}"):
332+
fileExt = ".uf2"
324333
default:
325334
return errors.New("invalid target file - did you forget the {hex} token in the 'flash' section?")
326335
}

uf2.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Converts firmware files from BIN to UF2 format before flashing.
2+
//
3+
// For more information about the UF2 firmware file format, please see:
4+
// https://github.com/Microsoft/uf2
5+
//
6+
//
7+
package main
8+
9+
import (
10+
"bytes"
11+
"encoding/binary"
12+
"io/ioutil"
13+
)
14+
15+
// ConvertELFFileToUF2File converts an ELF file to a UF2 file.
16+
func ConvertELFFileToUF2File(infile, outfile string) error {
17+
// Read the .text segment.
18+
_, data, err := ExtractTextSegment(infile)
19+
if err != nil {
20+
return err
21+
}
22+
23+
output, _ := ConvertBinToUF2(data)
24+
return ioutil.WriteFile(outfile, output, 0644)
25+
}
26+
27+
// ConvertBinToUF2 converts the binary bytes in input to UF2 formatted data.
28+
func ConvertBinToUF2(input []byte) ([]byte, int) {
29+
blocks := split(input, 256)
30+
output := make([]byte, 0)
31+
32+
bl := NewUF2Block()
33+
bl.SetNumBlocks(len(blocks))
34+
35+
for i := 0; i < len(blocks); i++ {
36+
bl.SetBlockNo(i)
37+
bl.SetData(blocks[i])
38+
39+
output = append(output, bl.Bytes()...)
40+
bl.IncrementAddress(bl.payloadSize)
41+
}
42+
43+
return output, len(blocks)
44+
}
45+
46+
const (
47+
uf2MagicStart0 = 0x0A324655 // "UF2\n"
48+
uf2MagicStart1 = 0x9E5D5157 // Randomly selected
49+
uf2MagicEnd = 0x0AB16F30 // Ditto
50+
uf2StartAddress = 0x2000
51+
)
52+
53+
// UF2Block is the structure used for each UF2 code block sent to device.
54+
type UF2Block struct {
55+
magicStart0 uint32
56+
magicStart1 uint32
57+
flags uint32
58+
targetAddr uint32
59+
payloadSize uint32
60+
blockNo uint32
61+
numBlocks uint32
62+
familyID uint32
63+
data []uint8
64+
magicEnd uint32
65+
}
66+
67+
// NewUF2Block returns a new UF2Block struct that has been correctly populated
68+
func NewUF2Block() *UF2Block {
69+
return &UF2Block{magicStart0: uf2MagicStart0,
70+
magicStart1: uf2MagicStart1,
71+
magicEnd: uf2MagicEnd,
72+
targetAddr: uf2StartAddress,
73+
flags: 0x0,
74+
familyID: 0x0,
75+
payloadSize: 256,
76+
data: make([]byte, 476),
77+
}
78+
}
79+
80+
// Bytes converts the UF2Block to a slice of bytes that can be written to file.
81+
func (b *UF2Block) Bytes() []byte {
82+
buf := bytes.NewBuffer(make([]byte, 0, 512))
83+
binary.Write(buf, binary.LittleEndian, b.magicStart0)
84+
binary.Write(buf, binary.LittleEndian, b.magicStart1)
85+
binary.Write(buf, binary.LittleEndian, b.flags)
86+
binary.Write(buf, binary.LittleEndian, b.targetAddr)
87+
binary.Write(buf, binary.LittleEndian, b.payloadSize)
88+
binary.Write(buf, binary.LittleEndian, b.blockNo)
89+
binary.Write(buf, binary.LittleEndian, b.numBlocks)
90+
binary.Write(buf, binary.LittleEndian, b.familyID)
91+
binary.Write(buf, binary.LittleEndian, b.data)
92+
binary.Write(buf, binary.LittleEndian, b.magicEnd)
93+
94+
return buf.Bytes()
95+
}
96+
97+
// IncrementAddress moves the target address pointer forward by count bytes.
98+
func (b *UF2Block) IncrementAddress(count uint32) {
99+
b.targetAddr += b.payloadSize
100+
}
101+
102+
// SetData sets the data to be used for the current block.
103+
func (b *UF2Block) SetData(d []byte) {
104+
b.data = make([]byte, 476)
105+
copy(b.data[:], d)
106+
}
107+
108+
// SetBlockNo sets the current block number to be used.
109+
func (b *UF2Block) SetBlockNo(bn int) {
110+
b.blockNo = uint32(bn)
111+
}
112+
113+
// SetNumBlocks sets the total number of blocks for this UF2 file.
114+
func (b *UF2Block) SetNumBlocks(total int) {
115+
b.numBlocks = uint32(total)
116+
}
117+
118+
// split splits a slice of bytes into a slice of byte slices of a specific size limit.
119+
func split(input []byte, limit int) [][]byte {
120+
var block []byte
121+
output := make([][]byte, 0, len(input)/limit+1)
122+
for len(input) >= limit {
123+
block, input = input[:limit], input[limit:]
124+
output = append(output, block)
125+
}
126+
if len(input) > 0 {
127+
output = append(output, input[:len(input)])
128+
}
129+
return output
130+
}

0 commit comments

Comments
 (0)