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  }