2017年5月30日火曜日

サンプルレートコンバータとのインターフェースをModelSimでシミュレーション

DE0nanoにSRC4382 サンプルレートコンバータを接続するため、必要なクロックの生成とPCM信号をやりとりするインターフェースをVerilogでRTL記述します。インターフェースと言ってもシリアル・パラレル変換とパラレル・シリアル変換なんですが、いろんな書き方があります。今回は、以前作成したコードを少しアレンジしただけでしたが、当時は参考書を片手にとても苦労した覚えがあります。
シミュレーションは、テストベンチを作る手間がかかりますが、バグ取りがラクなので結局は早道です。備忘録として無償の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 件のコメント:

コメントを投稿