Help us understand the problem. What is going on with this article?

Linux の FPGA Manager で Xilinx のビットストリームファイルを扱う方法

More than 1 year has passed since last update.

問題点

Linux Kernel 4.10 になって FPGA Region が追加されました。これは FPGA のコンフィギュレーションをユーザーから出きるようにした FPGA Manager の Higher Level Interface です。ところが、この FPGA Region に Vivado が生成したビットストリームファイルを指定すると、フォーマットが合わない旨のログが出て、コンフィギュレーションに失敗します。

原因

Zynq 用の FPGA Manager の Low level Interface である devcfg が、Vivado のビットストリームファイルを扱えないのが原因です。

devcfg には FPGA にダウンロードする Raw Data を渡さなければなりませんが、ビットストリームファイルにはヘッダに各種情報が含まれています。まずは、このヘッダを除去しなければなりません。

また、(何故か)、devcfg は Raw Data の1ワード(32bit,4Byte)単位でエンディアン変換(バイトスワップ)しなければなりません。

解決方法

この手の問題は、多分誰かが解決しているだろうと思ったらありました。

Zynq: Loading bitfile into FPGA from Linux (xdevcfg)

に、 https://github.com/milosoftware/meta-zynq/raw/master/recipes-bsp/fpga/fpga-image/fpga-bit-to-bin.py というツールが紹介されています。残念ながらここはリンクが切れていましが、検索したところ https://github.com/topic-embedded-products/meta-topic/blob/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py がみつかりました。

このオプションに --flip というのがあって、これでエンディアンを変換することができます。

shell$ fpga-bit-to-bin.py --flip input.bit output.bin

fpga-bit-to-bin.py

もしかしたら https://github.com/topic-embedded-products/meta-topic/blob/master/recipes-bsp/fpga/fpga-bit-to-bin/fpga-bit-to-bin.py がリンク切れになるかもしれませんので、こちらにも貼っておきます(ライセンスが GPL2.0なので問題ないはず)。作者に感謝。

fpga-bit-to-bin.py
#!/usr/bin/python
import sys
import os
import struct

def flip32(data):
    sl = struct.Struct('<I')
    sb = struct.Struct('>I')
    try:
        b = buffer(data)
    except NameError:
        # Python 3 does not have 'buffer'
        b = data
    d = bytearray(len(data))
    for offset in range(0, len(data), 4):
         sb.pack_into(d, offset, sl.unpack_from(b, offset)[0])
    return d

import argparse
parser = argparse.ArgumentParser(description='Convert FPGA bit files to raw bin format suitable for flashing')
parser.add_argument('-f', '--flip', dest='flip', action='store_true', default=False, help='Flip 32-bit endianess (needed for Zynq)')
parser.add_argument("bitfile", help="Input bit file name")
parser.add_argument("binfile", help="Output bin file name")
args = parser.parse_args()

short = struct.Struct('>H')
ulong = struct.Struct('>I')

bitfile = open(args.bitfile, 'rb')

l = short.unpack(bitfile.read(2))[0]
if l != 9:
    raise Exception("Missing <0009> header (0x%x), not a bit file" % l)
bitfile.read(l)
l = short.unpack(bitfile.read(2))[0]
d = bitfile.read(l)
if d != b'a':
    raise Exception("Missing <a> header, not a bit file")

l = short.unpack(bitfile.read(2))[0]
d = bitfile.read(l)
print("Design name: %s" % d)

# If bitstream is a partial bitstream, get some information from filename and header
if b"PARTIAL=TRUE" in d:
    print("Partial bitstream")
    partial = True;

    # Get node_nr from filename (last (group of) digits)
    for i in range (len(args.bitfile) - 1, 0, -1):
        if args.bitfile[i].isdigit():
            pos_end = i + 1
            for j in range (i - 1, 0, -1):
                if not args.bitfile[j].isdigit():
                    pos_start = j + 1
                    break
            break
    if pos_end != 0 and pos_end != 0:
        node_nr = int(args.bitfile[pos_start:pos_end])
    else:
        node_nr = 0
    print("NodeID: %s" % node_nr)

    # Get 16 least significant bits of UserID in design name
    pos_start = d.find(b"UserID=")
    if pos_start != -1:
        pos_end = d.find(b";", pos_start)
        pos_start = pos_end - 4
        userid = int(d[pos_start:pos_end], 16)
        print("UserID: 0x%x" % userid)

else:
    print("Full bitstream")
    partial = False
    node_nr = 0

KEYNAMES = {b'b': "Partname", b'c': "Date", b'd': "Time"}

while 1:
    k = bitfile.read(1)
    if not k:
        bitfile.close()
        raise Exception("unexpected EOF")
    elif k == b'e':
        l = ulong.unpack(bitfile.read(4))[0]
        print("Found binary data: %s" % l)
        d = bitfile.read(l)
        if args.flip:
            print("Flipping data...")
            d = flip32(d)
        # Open bin file
        binfile = open(args.binfile, 'wb')
        # Write header if it is a partial
        if partial:
            binfile.write(struct.pack("B", 0))
            binfile.write(struct.pack("B", node_nr))
            binfile.write(struct.pack(">H", userid))
        # Write the converted bit-2-bin data
        print("Writing data...")
        binfile.write(d)
        binfile.close()
        break
    elif k in KEYNAMES:
        l = short.unpack(bitfile.read(2))[0]
        d = bitfile.read(l)
        print(KEYNAMES[k], d)
    else:
        print("Unexpected key: %s" % k)
        l = short.unpack(bitfile.read(2))[0]
        d = bitfile.read(l)

bitfile.close()
ikwzm
元へっぽこ電子回路エンジニア。現在隠居中。どちらかというとVHDL派。最近はFPGA+SoC でいろいろやってます。github でもいろいろと公開してます。 https://github.com/ikwzm
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした