シミュレーションは、テストベンチを作る手間がかかりますが、バグ取りがラクなので結局は早道です。備忘録として無償のModelSim - Intel FPGA Starter Editionを使った例を紹介します。
SRC4382とVerilogモジュールの接続イメージ
サンプルレートコンバータをスレーブモードで動作させるため、各種のクロック信号はDE0-nanoが供給します。さらにデジタルオーディオのPCMデータをシリアル・パラレル変換、パラレル・シリアル変換します。これをasrc_ifというモジュールに記述します。Quartusの事前設定
- Assignments ⇒ Settings ⇒ EDA Tool Settings(Simulation) ⇒ Tool name に "ModelSim-Altera"と指定。※"ModelSim"ではありません
- Tools ⇒ Options ⇒ EDA Tool Options の ModelSim-Alteraに C:\intelFPGA_lite\16.1\modelsim_ase\win32aloem と指定。
- ModelSimを起動するには、QuartusからTools ⇒ Run Simulation Tool ⇒ RTL Simulationとします。
新規プロジェクトの作成
- ModelSimで、File ⇒ New ⇒ Project とし、プロジェクト名を適当に指定します。Add items to the Projectウインドウが現れるが、何もせず閉じます。
- Project ⇒ Add to Project ⇒ Existing File として、シミュレーション対象のVerilogファイルを指定します。
- Project ⇒ Add to Project ⇒ New File として、テストベンチを新規作成・追加します。Add file as typeでVerilogを指定しておきます。保存場所はシミュレーション対象と同じフォルダが良いようです
- Compile ⇒ Compile All してテストベンチをコンパイルします。コンパイル結果は、プロジェクト ペインのStatusで確認できます。
シミュレーションの実行
- もしメガファンクションが含まれる場合は、メガファンクションのシミュレーションライブラリを登録します。シミュレーションライブラリ名は、MegaWizardの途中で表示されます。
- シミュレーションライブラリを登録は、Simulate ⇒ Start Simulation ⇒ Librariesタブ ⇒ Search Libraries( -L )欄のAdd ⇒ altera_mf_verを指定。verilog用なので _verのサフィックスが付きます
- 次にDesignタブでワーキングライブラリ(デフォルト名work)内でテストベンチを指定します。メガファンクション ALTPLLを使うときは、Resolutionを1psにしなくてはダメという場合もあるようです。
- Simulation Configurationに保存
ところでStart Simulation の設定は保存されないので、保存したいときはSimulation Configurationを使います。Project ⇒ Add to Project ⇒ Simulation Configurationで適当な名前を付けます。すると、ProjectペインにConfiguration名の項目が現れるので、次回からはこれをクリックします。
- シミュレーション結果を波形表示させるため、テストベンチを右クリックしてWaveウインドウに項目を追加します。
- シミュレーション時間を指定して Run をクリックしてシミュレーションを実行します。
- 無事波形が出ました。テストベンチでパラレル・シリアル変換とシリアル・パラレル変換をループバックさせたところミスを発見。1カ所修正して、シミュレーションでは動作できることがわかりました。
参考資料
使用したVerilogファイルとテストベンチ
module asrc_if
(
CLK,
RST,
LCH_IN,
RCH_IN,
LCH_OUT,
RCH_OUT,
SRC_MCLK,
SRC_RST,
SRC_BCK,
SRC_LRCK,
SRC_DOUT,
SRC_DIN,
SRC_LOCK
);
input CLK; // 512fs; 24.576MHz
input RST;
input [23:0] LCH_IN;
input [23:0] RCH_IN;
output [23:0] LCH_OUT;
output [23:0] RCH_OUT;
output SRC_MCLK; // 512fs
output SRC_RST;
output SRC_BCK; // 64fs
output SRC_LRCK; // fs; 48kHz
input SRC_DOUT;
output SRC_DIN;
input SRC_LOCK;
// clock generation 512fs -> asrc
reg [8:0] count;
wire en;
wire read_strb;
wire shift;
always @(posedge CLK)
begin
if (RST)
count <= 0;
else if (count == 9'h1FF)
count <= 0;
else
count <= count + 1'b1;
end
assign SRC_MCLK = CLK;
assign SRC_RST = ~RST;
assign SRC_BCK = count[2];
assign SRC_LRCK = ~count[8];
assign en = (count == 9'h1FF);
assign read_strb = (count[2:0] == 3'h3);
assign shift = (count[2:0] == 3'h7);
// Serial to Parallel
reg [63:0] rx_buf;
reg [23:0] LCH_OUT;
reg [23:0] RCH_OUT;
always @(posedge CLK)
begin
if (RST)
begin
rx_buf <= 0;
LCH_OUT <= 0;
RCH_OUT <= 0;
end
else if (read_strb)
rx_buf <= {rx_buf[62:0], SRC_DOUT};
else if (en && SRC_LOCK)
begin
LCH_OUT <= 0;
RCH_OUT <= 0;
end
else if (en)
begin
LCH_OUT <= rx_buf[63:40];
RCH_OUT <= rx_buf[31:8];
end
end
// Parallel to Serial
reg [63:0] tx_buf;
wire [23:0] LCH_IN;
wire [23:0] RCH_IN;
always @(posedge CLK)
begin
if (RST)
tx_buf <= 0;
else if (en)
tx_buf <= {LCH_IN, 8'b0, RCH_IN, 8'b0};
else if (shift)
tx_buf <= {tx_buf[62:0], 1'b0};
end
assign SRC_DIN = tx_buf[63];
endmodule
(テストベンチ)
`timescale 1ns/1ps
module asrc_if_tb ();
reg c0;
reg RST;
reg [23:0] LCH_IN;
reg [23:0] RCH_IN;
wire [23:0] LCH_OUT;
wire [23:0] RCH_OUT;
wire SRC_MCLK;
wire SRC_RST;
wire SRC_BCK;
wire SRC_LRCK;
wire LOOPBACK;
reg SRC_LOCK;
// ASRC Interface
asrc_if asrc_if (
.CLK(c0),
.RST(RST),
.LCH_IN(LCH_IN),
.RCH_IN(RCH_IN),
.LCH_OUT(LCH_OUT),
.RCH_OUT(RCH_OUT),
.SRC_MCLK(SRC_MCLK),
.SRC_RST(SRC_RST),
.SRC_BCK(SRC_BCK),
.SRC_LRCK(SRC_LRCK),
.SRC_DOUT(LOOPBACK),
.SRC_DIN(LOOPBACK),
.SRC_LOCK(SRC_LOCK)
);
initial begin
RST <= 1;
c0 <= 1;
SRC_LOCK <= 1;
#2
RST <= 0;
SRC_LOCK <= 0;
LCH_IN <= 24'hF0F0F0;
RCH_IN <= 24'hB0F0F0;
#1000 $finish;
end
always #1
c0 <= ~c0;
endmodule
0 件のコメント:
コメントを投稿