Everyday is Sunday !

趣味満載のブログ #電子工作 #東方 #アニメ #音楽

VSCode でやる Efinix FPGA 開発環境構築 〜Ubuntu 論理合成 編

※この記事、意外と見られているので 2023-11-19 加筆

はじめに

VSCode を使った Efinix FPGA の開発環境構築について記載します。
下記の構成で作成予定です。

  • 論理合成編
  • info, warning, error の確認編
  • シミュレーション編

FPGA 開発に共通して言えることですが、IDE デフォルトのエディタはコード補完等弱いので使いづらいです。
そこで、ソースコードは VSCode や Vim などの外部エディタで書いて、
合成は IDE のボタンをポチポチして結果を確認するといった流れが多いと思います。
途中でアプリを切り替えたり、マウス操作を挟んだりするのは凄く面倒で余分な時間もかかるので、
どうにかできないかと思い、色々模索してかなり良い感じの環境が整ったので備忘録として残します。
※プロジェクトは作成済みとして記載します。

開発環境

  • Ubuntu 22.04 LTS
  • Efinity 2023.1
  • VSCode v1.80.0
  • GTKWave v3.3.104
  • iverilog v11.0

Efinity、VSCode の役割分担

最初に、基本的に Efinix FPGA の開発環境である Efinity (GUI) でしかできないことは下記です。

  • 新規プロジェクトの作成
  • 内部ロジアナの JTAG デバッグ
  • netlist や Routing のグラフィカル表示
  • IP生成


次に、VSCode でも実現可能だが、Efinity を使用したほうが楽なことは下記です。

  • Interface Designer による周辺機能の設定 (※場合による、後述)
  • SPI-Rom への Bitstream 書き込み (※場合による、単体の Bittstream であれば VSCode が楽)


最後に、VSCode で実現した方が圧倒的に楽なことは下記です。

  • ソースコードの記述 (今回)
  • synthesize や roulte などの論理合成 (今回)
  • FPGA への Bitstream 書き込み (今回)
  • Info, Warning, Error の確認 (次回)
  • Simlation (次々回)

この 5つの項目について記載していきます。

VSCode 論理合成のコマンド設定と実行

Efinitx FPGA 開発には、CUIで実行できるコマンドが色々と提供されています。
VSCode の機能の一つである tasks.json に、提供されているコマンドを記載しショートカットキーで実行できるようにします。
tasks.json 作成後、"command" の部分に実行したいスクリプトなどを記載します。
.vscode フォルダはプロジェクト直下に作成しました。
tasks.json の作成方法などは略します。

   "version": "2.0.0",
    "tasks": [
        {
            "label": "Compile",
            "type": "shell",
      // source ... で efx_run.py の環境変数を登録、Efinity をインストールしたパスを入力
      // efx_run.py "project.xml" --flow compile で synthesis ~ bitstream 生成まで一括で実行
      // makefile に efx_run.py ... の部分を記載して、make コマンドも実行可能
            "command": "source /tools/efinity/2023.1/bin/setup.sh; efx_run.py ./Efinity-Project.xml --flow compile",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
 ]

Efinity でプロジェクトを作成すると プロジェクト名.xml ファイルが直下に作成されますので、指定します。
compile の他にも、単体で実行するコマンドがあります。
--flow compile の部分を変更するだけで良いです。(多分一番使うのは synthesize)

--flow map  // synthesize
--flow dbg_auto  // debugger , FPGA 内蔵ロジアナを使う場合の確認
--flow interface  // interface compile 周辺機器の設定が間違っていないかの確認
--flow pnr  // place roulette
--flow pgm // bitstream generate
--flow program --pgm_opts mode=jtag // JTAG program

efx_run.py ./Efinity-Project.xml xxx  // xxx の部分を変更する。

VSCode 上で Ctrl + shift + B を押すとtasks.json に記載されているコマンドが表示されます。 試しに Compile を実行してみます。

端末に実行結果が表示されます。プロジェクトで使用されているソースファイルを網羅し、syntesis などが開始されます。
エラーがない場合は "PASS" 、エラーがある場合は "FAIL " が表示されます。
次に生成された bitstream ファイルを FPGA に書き込んでみます。
次の JTAG 書き込みコマンドを実行します。

"command": "source /tools/efinity/2023.1/bin/setup.sh; efx_run.py ./Efinity-Project.xml --flow program --pgm_opts mode=jtag",

エラーが発生しました。ボード電源の入れ忘れ、またはJTAG の配線を疑います。
今回は JTAG ケーブルの接続を忘れていたので、接続して再トライします。

成功しました。FT2232H IC を使用しているため関連の名称や VID,PID が表示されます。
JTAG が PC に認識されていれば勝手に読み込んでくれる情報になります。
書き込みが完了すれば、回路が動作を開始するので、盛り込んだ機能の確認を行います。

lsusb コマンドを実行した様子。もし認識されない場合は USB ポートを変えてたり、ドライバ関連を疑います。

プロジェクトへのソースファイル追加方法

プロジェクト作成時に生成される "project.xml" を直接編集することで、ソースファイルの追加が可能です。
ファイルの1行目から数行進んだ辺りに、プロジェクトで使用する HDL ファイルが記載されている部分があります。
name="xxx.v" のところに新しく追加した HDL ファイルを相対パスで追記すれば、合成時に含まれます。

    <efx:design_info def_veri_version="sv_09" def_vhdl_version="vhdl_2008">
        <efx:top_module name="K5Stack10MidiTop" />
        <efx:design_file name="src/K5Stack10MidiTop.v" version="default" library="default" />
        <efx:design_file name="src/GpioBlock/GpioBlock.v" version="default" library="default" />
        <efx:design_file name="src/SPIBlock/SPISignal.v" version="default" library="default" />

おまけ1 Interface Designer の設定

Efinix FPGA のピン設定は、Vivado でいう xdc ファイルに記載する方法ではありません。
Interface Designer という専用の GUI 機能を活用して、ポチポチと設定していくことになります。
project.peri.xml を開くと、Interface Designer で設定した内容が記載されています。
このファイルの記載を直接いじると、ピンやPLL の設定を変更することが可能です。

IO の名称変更程度なら、一括変換で行えるので直接のほうが楽ですが、
IO の機能を変更する場合、乱雑に生成されているのと、変更箇所が多いため手動での修正は間違いやすいです。
(筆者も何度か失敗しています)

おまけ2 makefile を使用した FlashROM への書き込み実行

Flash Loader 用のプロジェクト直下に makefile を作成し下記を記載します。
プロジェクト名は "JTAG_Flash_Loader にしています。

PROJECT = ./JTAG_Flash_Loader.xml

jtag:
  efx_run.py $(PROJECT) --flow program --pgm_opts mode=jtag

次に、現在作成中のプロジェクト直下に makefile を作成し下記を記載します。
make -C $(JTAG...) の部分は、作成した JTAG_Flash_Loader のディレクトリに移動し、
移動先の makefile を実行することができます。jtag コマンドを実行することで、
Flash Loader 用の bitstream ファイルが FPGA に書き込まれます。

Flash Loader FPGA には、JTAG経由で Flash ROM に bitstream ファイルを書き込む機能が実装されています。
(Efinix から IP やプロジェクトの作成方法が記載されているデータシートが提供されています)
次の、mode=jtag_bridge の行を実行し、Flash ROM への書き込みが開始されます。

PROJECT = ./K5Stack10_MIDI.xml
JTAG_PROJECT = ../JTAG_Flash_Loader

flash:
  make -C $(JTAG_PROJECT) jtag
  efx_run.py $(PROJECT) --flow program --pgm_opts mode=jtag_bridge

最後に makefile, tasks.json の全貌を載せておきます。
上手く行かない方はコメントで気軽にご質問ください。

makefile

FILE = efx_run.py
PROJECT = ./K5Stack10_MIDI.xml
JTAG_PROJECT = ../JTAG_Flash_Loader
# SIM_PROJECT = ./src/common/fifo/sim
# SIM_PROJECT = ./src/UltraFastInterfaceBus/sim
# SIM_PROJECT = ./src/SynthesizerBlock/sim
# SIM_PROJECT = ./src/VideoTxBlock/VideoPixelGenUnit/sim
# SIM_PROJECT = ./src/AudioTxBlock/sim
# SIM_PROJECT = ./src/VideoTxBlock/sim
SIM_PROJECT = ./src/RAMBlock/sim

all: synthesis interface place pgm jtag

compile:
    $(FILE) $(PROJECT) --flow compile

synthesis:
    $(FILE) $(PROJECT) --flow map

debugger:
    $(FILE) $(PROJECT) --flow dbg_auto

interface:
    $(FILE) $(PROJECT) --flow interface

place:
    $(FILE) $(PROJECT) --flow pnr

pgm:
    $(FILE) $(PROJECT) --flow pgm

jtag:
    $(FILE) $(PROJECT) --flow program --pgm_opts mode=jtag

flash:
    make -C $(JTAG_PROJECT) jtag
    $(FILE) $(PROJECT) --flow program --pgm_opts mode=jtag_bridge

Update:
    make -C $(JTAG_PROJECT) jtag

sim:
    make -C $(SIM_PROJECT) sim

py:
    python3 python/InfoLog.py


json

{
    "version": "2.0.0",
    "tasks": [
        {
            //-----------------------------------------------------------------------------
            // Simlation 2023-07-22 Add
            //-----------------------------------------------------------------------------
            "label": "Sim",
            "type": "shell",
            "command": "make sim",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        {
            //-----------------------------------------------------------------------------
            // bitstream 出力まで一括実行
            //-----------------------------------------------------------------------------
            "label": "All",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make compile;make py;make flash",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        {
            //-----------------------------------------------------------------------------
            // bitstream 出力まで一括実行
            //-----------------------------------------------------------------------------
            "label": "Compile",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make compile;make py",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // map
        //-----------------------------------------------------------------------------
        {
            "label": "Synthesis",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make synthesis",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        
        //-----------------------------------------------------------------------------
        // debugger 使用時は実行しないと Place 実行時に FAIL 発生
        //-----------------------------------------------------------------------------
        {
            "label": "Debugger auto instantion",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make synthesis",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        
        //-----------------------------------------------------------------------------
        // コマンド実行時は、使用しない Interface Block は消さないと FAIL 発生
        //-----------------------------------------------------------------------------
        {
            "label": "Interface",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make interface",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // Place routing
        //-----------------------------------------------------------------------------
        {
            "label": "Place + Routing",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make place",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // Bitstream hex Generate
        //-----------------------------------------------------------------------------
        {
            "label": "Pgm",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make pgm",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // Jtag FPGA 書き込み
        //-----------------------------------------------------------------------------
        {
            "label": "Jtag",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make jtag",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        
        //-----------------------------------------------------------------------------
        // Flash rom 書き込み
        //-----------------------------------------------------------------------------
        {
            "label": "Flash",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make flash",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // Update Mode 起動
        //-----------------------------------------------------------------------------
        {
            "label": "Update",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make Update",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },

        //-----------------------------------------------------------------------------
        // Compile + JTAG
        //-----------------------------------------------------------------------------
        {
            "label": "Compile + JTAG",
            "type": "shell",
            "command": "source /tools/efinity/2023.1/bin/setup.sh;make compile;make py;make jtag",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
 ]
}