CLIのすゝめ (Intel FPGA)

本稿ではIntel FPGAで開発する際に発生する作業のCLIベースの手法についてまとめています。

# はじめに
筆者は基本的にCLIでできる作業はCLIでしたい派です。理由としては、一連の作業をスクリプト化しやすい、リモートサーバーで作業することが多くGUIが使いづらい、といった感じです。
しかし、IntelのチュートリアルではGUIベースの手法で説明されていることが多く、CLIベースの手法を調べるのが大変だったりします。そのため、本稿では個人的に使っているCLIベースの手法についてまとめてみました。

# 環境
OS: CentOS7
Intel FPGAツール群: 20.1

# QuartusPrime周り
## PINアサイン等の作業
PINアサインの情報や、インポートファイル等の情報は、Quartusプロジェクトファイル(.qpf)と同じ位置にある`.qsf`ファイルに記述されています。そのため、直接`.qsf`ファイルを編集することでPINアサイン等の作業ができます。構文に関しては、既に出来上がっているプロジェクト(公式で配布しているgolden_topとか？)の`.qsf`ファイルを見ればわかるため説明を省きます。
## プロジェクトファイルの作成
下記のようなtclファイルを用意し、`quartus_sh `コマンドを実行することで空のプロジェクトを生成できます。
<details><summary>make_project.tcl</summary><div>

```tcl
load_package flow
project_new pro_name -revision rev_name -overwrite

set_global_assignment -name FAMILY "Arria 10"
set_global_assignment -name DEVICE 10AX115S2F45I1SG
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1

export_assignment

project_close

```
</div></details>

```sh
quartus_sh -t make_project.tcl
```
下記のように事前にアサインメント等の情報を記述した状態でプロジェクトを生成することもできます。

<details><summary>make_project.tcl</summary><div>

```tcl
load_package flow
project_new pro_name -revision rev_name -overwrite

set_global_assignment -name FAMILY "Arria 10"
set_global_assignment -name DEVICE 10AX115S2F45I1SG
set_global_assignment -name PRESERVE_UNUSED_XCVR_CHANNEL ON
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
set_global_assignment -name POWER_AUTO_COMPUTE_TJ ON
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"

set_global_assignment -name TOP_LEVEL_ENTITY led

set_global_assignment -name SYSTEMVERILOG_FILE ../rtl/led.sv
set_global_assignment -name SDC_FILE           ./led.sdc
set_global_assignment -name IP_FILE ip/system/system_clock_in.ip
set_global_assignment -name IP_FILE ip/system/system_reset_in.ip
set_global_assignment -name QSYS_FILE system.qsys
set_global_assignment -name IP_FILE ip/system/system_onchip_memory2_0.ip
set_global_assignment -name IP_FILE ip/system/system_nios2_gen2_0.ip
set_global_assignment -name IP_FILE ip/system/system_jtag_uart_0.ip

set_global_assignment -name ALLOW_DSP_RETIMING ON
set_global_assignment -name ALLOW_RAM_RETIMING ON

#******************************************************************************
# PIN Location
#******************************************************************************
set_location_assignment PIN_F34 -to clk
set_location_assignment PIN_BD27 -to rst
set_instance_assignment -name IO_STANDARD "1.8 V" -to rst -entity top

set_location_assignment PIN_L28 -to led[0]
set_location_assignment PIN_K26 -to led[1]
set_location_assignment PIN_K25 -to led[2]
set_location_assignment PIN_L25 -to led[3]

export_assignments

project_close



```
</div></details>

また、下記のように`execute_module ...`という記述を追加するとプロジェクト生成と同時に合成も行います。
<details><summary>make_syn_project.tcl</summary><div>

```tcl
load_package flow
project_new pro_name -revision rev_name -overwrite

set_global_assignment -name FAMILY "Arria 10"
set_global_assignment -name DEVICE 10AX115S2F45I1SG
set_global_assignment -name PRESERVE_UNUSED_XCVR_CHANNEL ON
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
set_global_assignment -name POWER_AUTO_COMPUTE_TJ ON
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"

set_global_assignment -name TOP_LEVEL_ENTITY led

set_global_assignment -name SYSTEMVERILOG_FILE ../rtl/led.sv
set_global_assignment -name SDC_FILE           ./led.sdc
set_global_assignment -name IP_FILE ip/system/system_clock_in.ip
set_global_assignment -name IP_FILE ip/system/system_reset_in.ip
set_global_assignment -name QSYS_FILE system.qsys
set_global_assignment -name IP_FILE ip/system/system_onchip_memory2_0.ip
set_global_assignment -name IP_FILE ip/system/system_nios2_gen2_0.ip
set_global_assignment -name IP_FILE ip/system/system_jtag_uart_0.ip

set_global_assignment -name ALLOW_DSP_RETIMING ON
set_global_assignment -name ALLOW_RAM_RETIMING ON

#******************************************************************************
# PIN Location
#******************************************************************************
set_location_assignment PIN_F34 -to clk
set_location_assignment PIN_BD27 -to rst
set_instance_assignment -name IO_STANDARD "1.8 V" -to rst -entity top

set_location_assignment PIN_L28 -to led[0]
set_location_assignment PIN_K26 -to led[1]
set_location_assignment PIN_K25 -to led[2]
set_location_assignment PIN_L25 -to led[3]

export_assignments

execute_module -tool map
execute_module -tool fit
execute_module -tool sta -args {--do_report_timing}
execute_module -tool asm

project_close

```
</div></details>

## プロジェクトの合成
プロジェクトファイルのあるディレクトリで下記のコマンドを実行することでプロジェクトの合成ができます。下記コマンドの`project_name`の部分には`.qpf`ファイルのベース名、`revision_name`の部分には`.qsf`ファイルのベース名が入ります。

```sh
quartus_ipgenerate project_name -c revision_name --run_default_mode_op
quartus_syn --read_settings_files=on --write_settings_files=off project_name -c revision_name
quartus_fit --read_settings_files=on --write_settings_files=off project_name -c revision_name
quartus_sta project_name -c revision_name --mode=finalize
quartus_asm --read_settings_files=on --write_settings_files=off project_name -c revision_name
```

## SOFファイルのコンフィギュレーション
下記のコマンドを実行することで、コンフィギュレーションできます。

```sh
quartus_pgm -c 1 -m jtag -o P\;./sof_path/sof_name.sof
```
FPGAボードが複数接続されている場合、`-c 1`の部分の数値を変えることでコンフィギュレーションするFPGAボードの選択ができます。

## System Consoleの起動
System ConsoleをGUIで使用する場合は次のようになりますが、
Quartus Primeメニューの Tools > System Debugging Tools > System Console
下記のコマンドを実行することで、ターミナル上で直接System Consoleを使うことができます。

```sh
/quartus_path/sopc_builder/bin/system-console --cli --disable_readline
```

## Platform DesignerによるHDLの生成
流石に、Platform Designerでのシステム設計等の作業はCLIベースではできないのですが、HDLの生成(Generate HDL)であれば下記のコマンドからできます。`.qsys`ファイルに紐付いているHDLファイルを変更したときとかに、いちいちGUIからGenerate HDLをしなくて済みます。

```sh
qsys-generate ./qsys_name.qsys --simulation=VERILOG --allow-mixed-language-simulation --output-directory=./output_dir --family="Arria 10" --part=10AX115N2F45E1SG
sim-script-gen --system-file=./qsys_name.qsys --output-directory=./output_dir/sim --use-relative-paths=true
qsys-generate ./qsys_name.qsys --synthesis=VERILOG --output-directory=./output_dir --family="Arria 10" --part=10AX115S2F45I1SG
```

# ModelSim周り
## 簡単なシミュレーション
下記のようなスクリプトファイルを用意し実行することでシミュレーションができます。個人的には、テキストベースで確認できる情報はターミナルに出力し、できるだけ波形を見る量を減らしたほうが良いと思っています。
<details><summary>sim.sh</summary><div>

```shell
if [ ! -e ./work ]; then
    vlib ./work
fi

vlog \
  +define+SIM \
  ../rtl/led.sv \
  tb.sv

vsim -c -keepstdout tb -do 'add wave -r *;run -all;quit'
```
</div></details>
波形情報は`vsim.wlf`に出力され、`vsim`コマンドでGUIベースで波形を確認することができます。

```sh
vsim vsim.wlf
```
また、`vlog`コマンドは下記のようにファイルをまとめたリストを引数に取ることもできます。

```filelist.f
../rtl/led/led.sv
../rtl/counter/counter.sv
```

```shell
vlog \
  +define+SIM \
  -f filelist.f \
  tb.sv
```

## Platform Designerの出力ファイルをシミュレーションに使用する場合
下記の１行目のように、Platform Designerによって生成された`msim_setup.tcl`を用いてライブラリの生成を行います。`vlog`、`vsim`コマンドではこれらのライブラリを`-L`で紐付けします。紐付けるライブラリの名前は、１行目を実行した際に生成される`libraries`ディレクトリの中を見ることで分かります。
<details><summary>sim.sh</summary><div>

```shell
vsim -c -do 'set QSYS_SIMDIR "platform_designer_output_path/sim";do platform_designer_output_path/sim/mentor/msim_setup.tcl;com;quit'

vlog \
  -L work \
  -L work_lib \
  -L altera_mf_ver \
  -L altera_avalon_sc_fifo_1920 \
  -L altera_avalon_st_handshake_clock_crosser_1920 \
  -L altera_avalon_st_pipeline_stage_1920 \
  -L altera_irq_mapper_1920 \
  tb.sv

vsim -c -keepstdout tb \
  -L work \
  -L work_lib \
  -L altera_mf_ver \
  -L altera_avalon_sc_fifo_1920 \
  -L altera_avalon_st_handshake_clock_crosser_1920 \
  -L altera_avalon_st_pipeline_stage_1920 \
  -L altera_irq_mapper_1920 \
  -do 'add wave -r *;run -all;quit'

```
</div></details>

# Nios® II 周り
## サンプルデザインの生成
下記のコマンドを実行することで、サンプルデザインが生成され、同時に`.elf`ファイルも生成されます。

```sh 
nios2-swexample-create --sopc-file=sopcinfo_path/system.sopcinfo --type=hello_world_small --app-dir=app --bsp-dir=bsp
cd app
./create-this-app
```
`--sopc-file=`には、Niosデザインを含む`.qsys`ファイルによって生成された`.sopcinfo`ファイルを指定する必要があります。
`--type=`には、生成したいサンプルデザインを指定し、生成可能なサンプルデザインの種類は下記のコマンドで確認することができます。

```sh 
nios2-swexample-create --list
```
appディレクトリの中にサンプルデザインのソースファイルが生成されており、ソースコードに変更を加えた場合、appディレクトリで`make`コマンドを打つことで`.elf`ファイルが更新されます。

## `.elf`ファイルの転送
下記のコマンドでプログラムをFPGAのNiosモジュールに転送できます。

```sh 
nios2-download -c 1 hello_world_small.elf
```

また、下記のコマンドを実行することで、プログラム内のテキスト出力をターミナルに表示できます。

```sh 
nios2-terminal -c 1
```

## サンプルデザインを利用しない場合
まず、下記のコマンドより、bspを作成します。第一引数は、`bsp-type`で、`hal`か`ucosii`を選択できます。

```sh 
nios2-bsp hal ./bsp sopcinfo_path/system.sopcinfo
```
次に、下記のコマンドより、`.elf`ファイルを生成するために必要なMakefileを作成します。この際、ソースファイルのあるディレクトリを`--src-dir=`に指定することで自動でソースファイルを紐付けてくれます。

```sh
nios2-app-generate-makefile --bsp-dir=./bsp --app-dir=./app --src-dir=./src
```
appディレクトリにMakefileが生成され、`make`コマンドを打つことで`.elf`ファイルを生成できます。
ソースファイルが複数のディレクトリにある場合、下記のコマンドより、追加で紐付けることができます。

```sh
nios2-app-update-makefile --app-dir=./app --add-src-dir=./src2
```

## `.sopcinfo`ファイルを更新した場合
`.sopcinfo`ファイルを更新した場合、下記のコマンドでbspを更新する必要があります。
この際に、bsp内のファイルが一部初期化されてしまうため注意する必要があります。

```sh
nios2-bsp-generate-files --settings=./bsp/settings.bsp --bsp-dir=./bsp
```

## BSP Editorによる設定
BSP Editorによる設定はbspディレクトリの`settings.bsp`に記述されているため、直接`settings.bsp`を変更することでもbspの設定を変更できます(xml形式なため読みづらいですが)。変更後は、下記のコマンドを実行することで変更を反映できます。


```sh
nios2-bsp-update-settings --settings=./bsp/settings.bsp --bsp-dir=./bsp
```