ALTERA Quartus NIOSとVerilogとのコラボシミュレーション

← ポチっていただけると嬉しいです

ALTERA FPGAです。Intelのっていうのが未だにしっくりこないALTERAです。

先日はツールであるQuartusのインストールというまぁ言ってみれば初心者向けの記事を書きました。

ALETRA FPGAの始め方 QuartusPrimeのインストール
FPGAのお話です 今までの部署はXilinxだったんですが、今の部署はAlteraってことで、久しぶりにAlteraに戻ってます。 確か12年くらい前のCycloneIIIの時代に某製品用のHD-SDI出力とかを作ってたのが最後だ...

初心者向けであれば、VerilogでLチカのようなシンプルな回路を作ってみましょう!ってなるんでしょうが、
そんなの私もやっててもつまんないし、以前XilinxですがZYBOで書きましたんでね。
なので、今回は中級者向けって感じになるのかな?ひょっとしたら上級者でもこんなことしてないかもっていう内容になるかと思いますが、組み込みCPUであるNIOSと自作の回路を組み合わせてNIOSのソフトウェア込みでシミュレーションをするって方法を紹介してみます。
っていうか自分が勉強がてらやってみたのでその記録ともいいます。

以前の部署はXilinxでZynqでしたのでさすがにこんな事はしていません。
まぁ設計者っていうよりその管理者だったので自分はあんまり触ってないし。
しかしPetaLinuxの立ち上げとかシミュレーションできるのかなぁ。やれても結構時間かかりそう。

Xilinxでは一般に組み込みプロセッサというとMicroBlazeでしょうが、同じことはできるんじゃないでしょうか?
私はMicroBlazeってほとんど使ったこと無いのでよくわかりませんが。

概要

さて、始めましょう。

ここで例にする回路は非常にシンプル。
NiosにPIOを2個つなげてNiosのシステム生成。自作の回路はPIOから出てくる信号を加算して出力だけ。
ってのを試してみます。

動作としてはNios上のソフトで2個のPIOに値を書き込み、ハードウェア(回路)でその2値を加算するといった立派なコラボです。

まっそんな無理やり分担分けしたアホな回路は普通は作りませんけど、シンプルな回路例としてはいいでしょう

使うIPは
NiosII/e
そのワーク用のRAMとしてOnChipMemory
PIO
だけで、後は加算回路をVerilog記述します。

Quartus起動

New Project Wizardにて適当にプロジェクトを作ります。

Project TypeはEmptyで

Add Filesはとりあえず無し

Deviceは実際に焼くわけじゃないので適当にCyclone Vくらいにしておきますかね。

EDA Tool Settingsは以前の設定が残っているのかも。
私はSimulationだけ指定。

でFinish

Platform Designer

まずNIOS周りを作ります。
Tools→Platform Designerを起動します。

Sysem Contentsにはclk_0というClock Sourceだけ存在します。
最初Clock Sourceって何なの?PLL?って思いましたが、
ただのスルーするだけのブロックのようです。便宜的なものですかね。

まずはNIOSを追加

高機能のNios II/fはライセンスがいるみたいなのでフリーのNios II/eを選択しました。

また後で設定するのでとりあえずFinishで終わります。

次にOn-Chip NemoryでNIOS用のワークRAMを追加します。

容量を128kに設定しただけで後はデフォルトのままFinish

最後にPIOを追加します。

デフォルトのまま8bitの出力のみでFinish

同じものを2個作るので今できたPIOを複製しましょう。

Xilinxだと1個のPIOで2出力って出来たはず。いちいち増やすの面倒ですね。

まだ接続していないのでエラーはたくさん出ているかと思いますが、
使いたいモジュールは揃いました。

接続していきます。

これらのモジュールの接続は簡単ですね。
Clock/Resetを接続して、PIOの出力を外ピンに出す。

NIOSからのアクセスはAvalonっちゅうプロトコルを使うようです。
XilinxだとAXIとかがデフォルトだったんじゃなかったっけなぁ。
逆にどうしたらAXIでI/Fできるのかはわかってません。なので今のところはAvalon一択。

NIOSのMasterからペリフェラルのスレーブへデータとインストラクションを接続すればいいみたいです。
pioの出力はexternal_connectionってところでExportをダブルクリックして名前を入れます。

わかりやすいようにClockとResetを色を変えてますが、こんな感じになったかと思います。

まだやることはあります。

NIOSに戻ります。

配置されているNIOS(nios2_gen2_0となっているとこ)をダブルクリックで設定が立ち上がりますので、
Vectorsタブで各メモリをOn-Chip Nemoryに設定します。

このウィンドウはFinishとか無いので✕で閉じます。

先にOn-Chip Memoryを置いてからNiosを追加した場合は効率よく設定できたかと思いますが、順番に特に意味は無いです。

アドレスを設定します。
System → Assign Base Addresses で 勝手に都合よく設定されるようです。

これでエラーはなくなったはずです。

ここでエラーがあったら何かが間違ってます。

Generateします

Create Simulation Model を Noneから変えてモデル生成をさせます。
Pathは適当に指定してからGenerate

Save?っ出てくるので適当な名前でSaveしておきます。

終わったってわけじゃなくて、Saveが終わったってことです。

Closeを押すと生成が始まります。

暫く待つと終了しますのでClose

FinishでPlatform Designerを終わります。

QuartusにPlatform登録

Quartusの方にこんなダイアログが出てきますのでOKを押します。

このダイアログに沿って
Assignments→SettingのFilesメニューから
先にできた.qipと.sipをAddします。

ただし、sipファイルはダイアログからでは見えないので、
フィルターを変更してやる必要がありました。
ってことは、Addする必要あるのかな? qsysは登録しなくていいの? よくわからんなぁ。
まぁとりあえずこれで進めてうまくいったのでいいのかなー

トップ回路作成とNIOS組み込み

さて、今度はオリジナル回路の作成です。
Verilogで作りました。別にVHDLでも回路図でもいいですが、回路図で書いた場合は言語に変換しておく必要があるようです。

Intel:回路図を含んだデザインを ModelSim でシミュレーションしたいのですが、うまくいきません。 - 半導体事業 - マクニカ

File → New でVerilog HDL Fileを選択します。

適当にトップ階層を作ります。

PlatformDesignerで作った回路のテンプレートは、指定した名前の下のディレクトリに *_inst.v/vhdとしてありましたのでそれから流用するのが楽でしょう。

こんな感じの中身でした。

nios u0 (
  .clk_clk (<connected-to-clk_clk>), // clk.clk
  .reset_reset_n (<connected-to-reset_reset_n>), // reset.reset_n
  .pio0_export (<connected-to-pio0_export>), // pio0.export
  .pio1_export (<connected-to-pio1_export>) // pio1.export
);

それを用いてこんな感じでトップ階層作りました。
要はNIOSが吐く2つのPIO出力の8ビットの値を足して9bitの値として出力するっていう、まぁ普通だと作らないような回路です。
Niosでそのまま計算してしまえよって思いますもんね。

ソースはこんな感じ。

module nios_pio(
  input wire clock,
  input wire reset_n,
  output reg [8:0] data_o
);
  wire [7:0] pio0,pio1;
  nios u0 (
    .clk_clk ( clock ), // clk.clk 
    .reset_reset_n ( reset_n ), // reset.reset_n
    .pio0_export ( pio0 ), // pio0.export
    .pio1_export ( pio1 ) // pio1.export
  ); 
  always@(posedge clock or negedge reset_n) begin
    if( !reset_n ) data_o <= 9'h000;
    else data_o <= pio0 + pio1;
  end
endmodule

で、今作ったファイルを
Assignments → Settings のFileメニューから追加します。

今作ったモジュールがトップだよって指定しておきます。

これでCompileします。

今回はSimulationするだけだし、制約も何もつけてないのでAnalysis&Synthesisだけでいいかと思います。

NIOSソフトウェア

さて、次はNIOSの方でソフトウェアを組みましょう。
Tools → Nios II Software Build Tools for Eclipse を起動します。

WorkspaceはQuartusでのディレクトリ下にでも作っておきます。

File → New → Nos II Application and BSP from Template を選択

SOPC Information Fileとして、Quartusのワーキングディレクトリ下にできていたファイルを選択します。
PlatformDesignerが作ったファイルみたいです。

するとCPU nameが勝手に設定されます。

Project nameには適当に名前を入れて
Project templateはどれでもいいんですが、一番シンプルにBlankにしてみます。

するとProject Explorerに表示されるようになりました。

さて、ソフトを書いてみましょう。
File → New → Otherを選択

C/C++下のSource Fileを選択してNext

Source folderにはBrowseで出てくる中のbspがついてない方を。
Source fileには適当なファイル名.cを記入してFinish。

TemplateはC++でいいのかなぁ?まぁ対して違わないんでしょう。

内部エディタでソースファイルが出てきたので、そこにソフトを記載します。

今回は適当に2個の値を作ってPIO0/PIO1に設定を繰り返すって行います。
ソースはこんな感じ

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "system.h"
#include "altera_avalon_pio_regs.h"

int main() {
    unsigned char p0, p1;
    while(1) {
        p0 = (unsigned char)(rand()%256);
        p1 = (unsigned char)(rand()%256);
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, p0);
        IOWR_ALTERA_AVALON_PIO_DATA(PIO_1_BASE, p1);
        usleep(100);
    }
    return 0;
}

Lチカじゃないんだからusleepなんてなくてもいいんですが、シミュレーション結果の波形を見やすいようにusleepを適当な値でいれてます。
それとunsigned charはuint8_tの方が今どきなのかな。

altera_avalon_pio_regs.h はbspの方のディレクトリのdrivers/incの下にありました。
system.hの方はbspディレクトの直下にありました。
今作ったNIOSシステムのアドレスや関数等が定義されています。

なので、その2つのインクルードファイルの定義を使って書いていくわけです。

書き終わったら
Project Explorerのbspがついている方を右クリックして → Nos II → BSP Editorを選択します

Mainタブで enable_sim_optimize ってのにチェックを入れておきます。

これでシミュレーションが短縮されるようですが、何が端折られるのかってよく知りません。

で、Generate → Exit
でbspを再生成しておきます。

ソフトウェアをビルドします。

Project Explorerのbspがついていない方を右クリックして → Build Project を選択。

エラーが無いと無事に終わるはずです。

で、ここから肝というか面倒なんですが、
Project Explorerのbspがついていない方を右クリックして → Make Targets → Build を選択。

mem_init_generate を選択してからBuildをクリック

これで、メモリファイルが作成されます。

このできたhexファイルをPlatformDesignerフォルダーの下の
simulation\submodulesの下にある同名のファイルに上書きします。

次、Simulation実行スクリプトを編集します。

PlatformDesignerフォルダーの下の simulation\mentor\に msim_setup.tclというスクリプトファイルがありますので、それをエディタで開きます。

echo vlog っていう行の最後に
vlog -reportprogress 300 -work work 自分の作った回路のVerilogファイル
vlog -reportprogress 300 -work work 自分の作ったシミュレーションファイル
と追記します。

ここで罠があります。
パスの区切りは\じゃなくて/です。
エクスプローラからコピってきたら\になりますが、これだとうまくいきませんので、/にしてください。
中途半端にUnix(WSL)とWindows環境が入り混じってるんで面倒なんですよね。

テストベンチ作成

あ、シミュレーションファイル(テストベンチ)作るの忘れてましたね。

とりあえずクロックとリセットだけは与えないといけないので、NIOSからちょっと脱線しますがここでシミュレーションファイルを作っておきます。

QuartusのProjectNavigatorからトップ階層のVerilog選んで右クリックのCreate Verilog Instantiation Template Files for Current Fileを選択

するとプロジェクトフォルダ直下に*_inst.vが出来上がりますのでそれを流用するのがいいかもしれません。
もちろん今回程度のレベルならわざわざ作らなくて自分で一から書くのでもいいかとは思いますが。

といわけで、こんなファイルを作りました。あえて説明するほどのことはないシンプルな内容です。

`timescale 1ns/1ns
module test();
    parameter CYCLE = 100;
    parameter DELAY = 3; 

    reg clock;
    reg reset_n;
    wire [8:0] data_o; 
    nios_pio nios_pio_inst(
        .clock( clock ) , // input clock_sig
        .reset_n( reset_n ) , // input reset_n_sig
        .data_o( data_o ) // output [8:0] data_o_sig
    );

    initial begin
        clock = 0;
        forever #(CYCLE/2) clock = !clock;
    end

    initial begin
        reset_n = #DELAY 0;
        repeat(100) @(posedge clock);
        reset_n = #DELAY 1;
    end
endmodule

作ったら登録しておきます。
Assignments → Settings → Simulation

TimeScaleを1nsに設定。シミュレーションファイルにtimescale書いたけどどっちが優先されるのかなぁ?

NativeLink settingsをCompile test benchにチェックしてからTestBenches → New → 作ったシミュレーションファイルを指定して Add
Test bench nameにシミュレーションモジュールのトップ名を指定してOK

さらにOK

そしてOK

さて、脱線から戻ります。

シミュレーション

msim_setup.tclの編集の続きです。
vlogの追記は終わったので今度はvsimです。

vsimは2個あるかと思いますが、両方やっておきましょう。
やたら長いvsimコマンドの最後にトップモジュールの指定($TOP_LEVEL_NAME)がありますので、そこを自分が作ったトップモジュール名に変更します。
テストベンチでのトップモジュールです。私の場合はtest

vsimの後にadd wave とか runとかつけておいてもいいです。

これで準備完了。シミュレーションしてみます。

メニューからQuestを立ち上げます。
File → Change Directory でmsim_setup.tclがあるディレクトリを指定します。

Tools → Tcl → Execute Macroを選択

Change Directoryが間違ってなければmsim_setup.tclが見えるダイアログがたちあがるはずなので、それを選択して 開く をクリック

するとTransciptウィンドウにちょっと表示されてプロンプト状態になります。

ここでld_debugと打ってリターン。

これでシミュレーションが立ち上がるはずです。

階層も自分の作ったシミュレーションモジュールが一番上になってます。

そのトップ階層の変数、信号名もちゃんとみれてます。

NIOS周りがちゃんと動くのかも見ることができます。

とりあえずPIOの中のdata_outだけ見てみることにします。
階層を降りていって、pioモジュールを選択するとその中の信号が見えるのでdata_outを選択してAdd Wave

他にも見たい信号があればどんどん入れてみましょう。

で、runしてみます。

とりあえず10msくらい動かしてみますかね。
run 10ms

うまく動きました。

2D+CF=FC
46+29=6F
うん。いいですね。

拡大すると動作がわかります

PIO0にセットしてからPIO1にセットするのに30クロックくらい遅れがあります。
PIOの値が変わってから計算結果出力は1クロック。これは回路で記載してますから当たり前ですね。

ってことで、自作回路とPlatformDesignerとのコラボシミュレーションが出来ました。

おわりに

まとめると
PlatformDesignerでGenerateする時にシミュレーションモデル作成としておく
PlatformDesignerで作った.qipと.sipをFile登録
回路作成(Verilog) PlatformDesignerで作ったブロックをインスタンシエート
テストベンチ作成&登録
NIOSソフトウェア作成、Build
メモリ初期ファイルもBuildし入れ替える
シミュレーションスクリプトに追記&修正
って感じですかね。
結構面倒です。

 

ってことで一通り行ってみました。

まぁ実際の回路だったらとんでもない時間食いそうなのでどこまで実用的なの?ってのはあるかもしれませんが、一応出来ますよってことがわかったかと思います。

コメント

タイトルとURLをコピーしました