色んなパラメータでFPGAコンパイルと回路面積の見積もり自動化

##はじめに
FPGA設計においては、回路の面積と速度（クロック周波数）のトレードオフが常に問題となります。

速度性能はパイプラインレジスター（Flip Flop）を追加すれば向上しますが、回路面積は回路アーキテクチャ、演算精度を何ビットにするか、LUTのテーブル点数、アルゴリズムのパラメータ（フィルタ次数、FFT点数などなど）によって変わってきます。

VHDL/Verilogコードを書いていると、何パターンものコードを書き直して試すなんてことは面倒すぎてとてもじゃないけど出来ませんよね。しかし、MATLAB/SimulinkのモデルからHDLコードを生成する自動コード生成ツールHDL Coderを使えばパラメータ変更～HDLコード生成～論理合成などの一連の処理を自動化できるので、その方法について書きます。

##FPGA実装するサンプル
あまり時間がかからずに試せて、パラメータで回路面積が変わりそうなサンプルということで、三角関数SINをCORDICアルゴリズムを使った回路アーキテクチャで実装してみたいと思います。
CORDICはSIN/COS/TAN, EXP, Sqrtなど様々な算術演算をLUTと加減算の収束演算で行うアルゴリズムでハードウェア実装手法としてよく使われています。

Simulinkで簡単なモデルを作成し、それに対してMATALBコードでキックしてHDLコード生成～FPGAコンパイルを行います。

####テストベンチおよびHDL生成対象を含むトップ階層
Chirpのasinしたものを入力信号にしているので、出力信号はSin波になります。出力はdouble型の信号と差分を取って誤差を計算しています。
この図では入出力信号のビット幅は12bitになっていますが、これはパラメータで可変させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/fb3a1f6a-966d-61de-9856-ba719390e196.png)
固定小数点データのビット幅を設定するのはData Type Conversionブロックです。ビット幅のパラメータはMATLABから設定できるよう、変数（bitWidth）にしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/70ce131d-87fe-dd89-bf3a-cafcdd0127b7.png)

####HDLコード生成対象サブシステム
HDLコード生成対象はSinブロックのみ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/04cfff45-e3a0-1556-39f7-3144a6ea6a6e.png)

収束演算をさせるCORDICアルゴリズムの繰り返し回数のパラメータも変数（cordicIter）に設定しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/4d8164a5-dbe6-d16c-ac94-1d8e2806c300.png)

##MATLABからFPGAコンパイルを行うHDLワークフローアドバイザー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/ed5e8156-e0f7-0e6d-6889-53d5a1196d15.png)

これのファイルメニューから「スクリプトへのエクスポート」を選択して、MATLABコードを生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/8f049601-00f5-34b7-efb7-727336eda2c5.png)

##自動実行用のMATLABコード

以下のファイル（hdlworkflowCORDIC.m）はHDLワークフローアドバイザーで自動生成したものです。そのファイルを少し編集（ディレクトリ設定したり、不要なところ削除したり）しています。

<details><summary>自動生成されたMATLABコード（クリックすると展開）</summary><div>

```matlab:hdlworkflowCORDIC.m
function hdlworkflowCORDIC(modelName, subsystemName, bitWidth, cordicIter)
%% Load the Model
load_system(modelName);

PrjDir = ['hdl_' modelName '_b' num2str(bitWidth) '_c' num2str(cordicIter)];
HDLTargetDir = [PrjDir '\hdlsrc'];

%% Model HDL Parameters
hdlset_param(modelName, 'HDLSubsystem', [modelName '/' subsystemName]);

%% Workflow Configuration Settings
% Construct the Workflow Configuration Object with default settings
hWC = hdlcoder.WorkflowConfig('SynthesisTool','Altera QUARTUS II','TargetWorkflow','Generic ASIC/FPGA');

% Specify the top level project directory
hWC.ProjectFolder = PrjDir;

% Set Workflow tasks to run
hWC.RunTaskGenerateRTLCodeAndTestbench = true;
hWC.RunTaskVerifyWithHDLCosimulation = false;
hWC.RunTaskCreateProject = true;
hWC.RunTaskPerformLogicSynthesis = true;
hWC.RunTaskPerformMapping = true;
hWC.RunTaskPerformPlaceAndRoute = false;
hWC.RunTaskAnnotateModelWithSynthesisResult = false;

% Set properties related to 'RunTaskGenerateRTLCodeAndTestbench' Task
hWC.GenerateRTLCode = true;
hWC.GenerateTestbench = false;
hWC.GenerateValidationModel = false;

% Set properties related to 'RunTaskCreateProject' Task
hWC.Objective = hdlcoder.Objective.None;
hWC.AdditionalProjectCreationTclFiles = '';

% Set properties related to 'RunTaskPerformMapping' Task
hWC.SkipPreRouteTimingAnalysis = false;

% Set properties related to 'RunTaskPerformPlaceAndRoute' Task
hWC.IgnorePlaceAndRouteErrors = false;

% Set properties related to 'RunTaskAnnotateModelWithSynthesisResult' Task
hWC.CriticalPathSource = 'pre-route';
hWC.CriticalPathNumber =  1;
hWC.ShowAllPaths = false;
hWC.ShowDelayData = false;
hWC.ShowUniquePaths = false;
hWC.ShowEndsOnly = false;

% Validate the Workflow Configuration Object
hWC.validate;

%% Run the workflow
hdlcoder.runWorkflow([modelName '/' subsystemName], hWC);

```
</div></details>

こちらが自分で書いた、上のコードをCallしてHDLコード生成とコンパイルを繰り返し実行するMATLABコードです。

```matlab:run_cordic_hdlworkflow.m
modelName = 'cordicSynth0';
open_system(modelName)
subsystemName = 'CORDIC_Sin';

%% パラメータ設定
bitWidthParam = 8:18;       % bit幅は8~18bit
cordicIterParam = 8:18;     % CORDICの繰り返し回数は8~18回
% ただし制約がある: cordicIter <= bitWidth

%% 繰り返しコンパイル実行
for n = 1:numel(bitWidthParam)
    for m = 1:numel(cordicIterParam)
        bitWidth = bitWidthParam(n)
        cordicIter = cordicIterParam(m)
        if cordicIter > bitWidth    % 制約に引っかかる場合は処理をスキップ
            disp('Skip HDL Generation')
        else
            out = sim(modelName);   % 誤差計算のためシミュレーション実行
            errorSignal = out.logsout.getElement('FxptError').Values.Data;
            stdError(n,m) = std(errorSignal)    % 標準偏差
            maxError(n,m) = max(errorSignal)    % 最大誤差
            % 自動生成されたコンパイル用スクリプトにパラメータを与えて実行
            hdlworkflowCORDIC(modelName, subsystemName, bitWidth, cordicIter)
        end
    end
end

%% Max Error Plot
bar3(maxError)
h = gca;
h.XTickLabel = num2str(bitWidthParam');
xlabel('Bit width')
h.YTickLabel = num2str(cordicIterParam');
ylabel('CORDIC iteration')
h.ZScale = 'log';
zlabel('Max error')

```
##自動実行結果
準備ができたら後はMATLABを実行して、遊びに行くか寝るかして待つだけです。
翌朝実行結果を確認しましょう。

CORDIC IterationとBit Widthを変化させたときの最大誤差をグラフ（z軸は対数）と表で示します。
※ ただし、$CORDIC Iteration \leqq  Bit Width $
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/d7de4207-f5de-bde7-855d-677dcc274681.png)
※これbar3チャートなんですが、縦棒がちゃんと表示されてないバグ（笑）


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/a84277f9-c1fc-56d2-e2be-a723bff22c09.png)

下表にIntel Quartusでコンパイルした時の回路面積を示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/d79dc466-9b00-c34e-714c-8f39b51168f7.png)

※ ターゲットデバイスはIntel Arria10 10AS066H1F34E1SG

##まとめ
回路設計パラメータを変化させて、HDLコード生成を行い、FPGAコンパイルを自動実行する方法について示しました。
MATLABを使って一連の作業をプログラム化して自動化することができました。

##反省点
Bit幅やCORDIC Iterationは増やせば誤差や回路面積が大きくなることは予めわかっているので、1bit, 1 Cordic Iteration刻みで結果を確認する必要は無く、ある程度荒い刻みで実行すれば、あとは補間できます。
極端に言うと、最小値と最大値だけコンパイルしておけば、おおよその間の値は補間して求められそうです。

##ソース
ファイル一式は[こちら](https://github.com/SacredTubes/Qiita_AutoFPGASynth)に保存してあります。