github.com/aykevl/tinygo@v0.5.0/uf2.go (about) 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 := ExtractROM(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 }