SOF ファイルフォーマット リバエベース 解説

# 概要
Altera / Intel 製 FPGA のビットファイル（コンフィグレーションデータ）のフォーマットをリバエベースで解説する

本記事では確認できたことのみを述べるにとどまる
（不具合、故障等は責任を持ちません）

# 環境
Quartus Prime Lite 22.1
Ubuntu
FPGA : MAX10（10M25SAE144A7G）
実装回路：1 Hz Lチカ
# SOF と POF （多分）
現時点（23/11/09）では SOF と POF においてファイルフォーマットの違いは確認できなかった

### SOF : Sram Object File
FPGA の SRAM 部へ書き込む際に Quartus プログラマで使用する

#### POF : Programmer Object File
MAX 等の Flash へ書き込む際に Quartus プログラマで使用する

# フォーマット概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253609/f50b554e-f266-c264-3474-028543a16035.png)
<br>
12 Byte からなるファイルヘッダと複数のパケットで構成される。一番最後のパケットはターミネータパケットとなる

## File Header 部（12 Byte 固定？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253609/3bce3b88-ebb1-eb24-0b05-04c2ab28fef4.png)
<br>
File Header 部はフォーマットID、謎、パケット数で構成される

### ID（3 Byte）
3 Byte で構成される
SOF なら `S` `O` `F`
POF なら `P` `O` `F`
となる

### 謎（5 Byte）
詳細不明
SOF では `0` `0` `0` `0` `0`
POF では `0` `0` `0` `1` `0`
が確認できている

### パケット数（4 Byte）
以降に続くパケットの数を格納している
エンディアンはリトル

## Packet 部（6+可変長）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253609/9b5b2893-d97b-ec7f-b4ef-739d7244aaea.png)
Packet 部はパケットTag、パケット長、データで構成される
### Tag（16 bit）
パケットが何のデータなのか記載されている
エンディアンはリトル
Tag = 8 のときターミネータとなり2 Byte のデータ部に CRC が記載される

### Length（32 bit）
パケット長を格納している

### data（可変長）
データ

## Tag について
詳細は公開されていない
ref -> https://community.intel.com/t5/Programmable-Devices/I-want-knowing-POF-file-format-structures/td-p/74357?profile.language=ja

# Appendix
## 解析例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253609/d62501da-6787-ab43-ba77-7c61314b3323.png)
同じコンパイルで得られた Max10 の SOF, POF


## 解析app
``` main.go
package main

import (
	"os"
	"encoding/binary"
	"fmt"
)

type Header struct {
	Fmt []byte
	Ln uint32
	Z []byte
}

type Packet struct {
	Tag uint16
	Ln uint32
	D []byte
}

func main() {
	println(os.Args[1])
	
	fp, err := os.Open(os.Args[1])
	defer fp.Close() 
	if err != nil {
		println("File Open Error")
	}

	fi, _ := fp.Stat()
	fs := fi.Size()
	println("File Size =", fs, "\n")

	var h Header
	h, fs = rdHead(fp, fs)
	println("Left = ", fs)
	
	p := make([]Packet, h.Ln)
	for i := uint32(0); i < h.Ln; i++ {
		println("\nN =", i)
		p[i], fs = rdPacket(fp, fs)
		printPacketHead(p[i])
		if p[i].Tag == 8 {
			printPacketData(p[i])
		}
		println("Left = ", fs)
	}
}

func rdHead (fp *os.File, fs int64) (Header, int64) {
	var h Header
	h.Fmt = make([]byte, 3)
	h.Z = make([]byte, 5)

	binary.Read(fp, binary.LittleEndian, &h.Fmt)
	binary.Read(fp, binary.LittleEndian, &h.Z)
	binary.Read(fp, binary.LittleEndian, &h.Ln)

	println(string(h.Fmt))
	for i := 0; i < len(h.Z); i++ {
//		fmt.Printf("%d : %02x = %d\n", i, h.Z[i], h.Z[i])
	}
	println("Packet NUM =", h.Ln)

	return h, fs - 12
}

func rdPacket (fp *os.File, fs int64) (Packet, int64) {
	var p Packet

	binary.Read(fp, binary.LittleEndian, &p.Tag)
	binary.Read(fp, binary.LittleEndian, &p.Ln)
	p.D = make([]byte, p.Ln)
	binary.Read(fp, binary.LittleEndian, &p.D)

	return p, fs - int64(p.Ln + 6)
}

func printPacketHead(inp Packet) {
	fmt.Printf("id : %d \n",inp.Tag)
	fmt.Printf("length : %d \n", inp.Ln)
}
	
func printPacketData(inp Packet) {
	if inp.Id == 8 {
		fmt.Printf("%c %c : %02x %02x", inp.D[0],	inp.D[1],	inp.D[0],	inp.D[1])
		println()
	} else {
		for i := uint32(0); i < inp.Ln; i++ {
			fmt.Printf("%d : %02x = %d\n", i, inp.D[i], inp.D[i])
		}
	}
}

```