まずは、ASRC、コーデックのデータフォーマットを確認しておきます。
データフォーマットはともに同じで、オーディオデータは24ビット左詰め(Left Justified)です。
ASRC(AD1895)のデータフォーマット
CODEC(CS4270)のデータフォーマット
もう少し細かく見ておきます。
サンプルレート152kHzですからLRCKは同じ152kHz(周期約6.6us)、SCLKはLRCKの64倍の9.728MHz(周期約103ns)です。
ここでAD1895のタイミングダイアグラムを見ると、SCLKの立ち下がりよりtDOPD(20ns)未満でSDATAが確定することがわかります。SDATAは、次のSCLKの立ち下がりからtDOH(3ns)以上ホールドされます。このため、FPGAではSCLKの立ち上がりエッジ付近でSDATAを読めばデータを取り込むことができそうです。
ASRC(AD1895)のタイミングダイアグラム
次にCS4270のタイミングダイアグラムを見ると、AD1895よりもSDOUTが確定している時間が短いです。SCLKの立ち上がりを中心にして、前tstp(10ns以上)~後(5ns以上)の時間のみデータが有効でそれ以外では不定となります。
CODEC(CS4270)のタイミングダイアグラム
ところで、ASRCに与えるマスタクロック(MCLK)にはいくつか条件がついています。
・ASRC出力側サンプルレートの138倍以上
・30MHz以下
・Pulsewidth High 8ns以上
・Pulsewidth High 12ns以上
FPGAのクロック(512fs, 77.824MHz)を3分周しても良いですが、FPGA埋め込みのハードマクロD DCM_SP (Digital Clock Manager) を使ってクロックを3/8倍して192fsを作ります。コード中では、このハードマクロをpll78to29という名称でインスタンシエートしています。
DCM_SPのマニュアル類は次の通りです。
Spartan-3Eライブラリガイド(HDL用)
Spartan-3E FPGAファミリ: データシート(全モジュール)
DCM_SP 設定画面1
DCM_SP 設定画面2
シミュレーション
FPGAをさわり始めた頃は、『良くわからない』からシミュレーションを避けていました。それこそトライアンドエラー。今から考えるとよくやれていたものだと思います。
シミュレーションを使えるようになると何となく自信も出てきます。作成したコードを、実デバイスにインプリメントする前から一発動作してくれるじゃないかという期待です。(検証が十分ではないのでそんなに甘くはありません)
シミュレーションにはISE付属のISimを使っています。パラシリ変換器のパラレル入力(if_mon0_i/if_mon1_i)に定数をセットし、生成したシリアル信号(codec_datas_o)をシリパラ変換器の入力(asrc_data_i/codec_datar_i)にループバックしてパラレル信号(if_l_o/if_r_o)に戻して入出力を比較しました。
ISimでのシミュレーション結果
上の波形を拡大
Verilogコード
(注)このコードは実機テストしていません!
module codec_if(
asrc_mclk_o, asrc_bck_o, asrc_lrck_o, asrc_data_i,
spdif_nonaudio_i, spdif_error_i, codec_datar_i, codec_sck_o, codec_mck_o,
codec_lrck_o, codec_datas_o, if_l_o, if_r_o, if_mon0_i, if_mon1_i,
en, clk, reset, if_sel
);
parameter AUDIO_BIT_DEPTH = 24;
output asrc_mclk_o; // 192fs (DCM_SP)
output asrc_bck_o; // 64fs
output asrc_lrck_o; // fs
input asrc_data_i;
input spdif_nonaudio_i;
input spdif_error_i;
input codec_datar_i;
output codec_sck_o; // 64fs
output codec_mck_o; // 64fs
output codec_lrck_o; // fs
output codec_datas_o;
output [AUDIO_BIT_DEPTH-1:0] if_l_o;
output [AUDIO_BIT_DEPTH-1:0] if_r_o;
input [AUDIO_BIT_DEPTH-1:0] if_mon0_i;
input [AUDIO_BIT_DEPTH-1:0] if_mon1_i;
output en;
input clk; // 512fs
input reset;
input if_sel; // 0: ASRC, 1: CODEC
reg [8:0] div_count;
wire [5:0] bit_count;
wire read_latch;
wire write_latch;
wire en;
// Receive Buffer
reg [AUDIO_BIT_DEPTH-1:0] sreg0; // ASRC L
reg [AUDIO_BIT_DEPTH-1:0] sreg1; // ASRC R
reg [AUDIO_BIT_DEPTH-1:0] sreg2; // CODEC L
reg [AUDIO_BIT_DEPTH-1:0] sreg3; // CODEC R
// Send Buffer
reg [AUDIO_BIT_DEPTH-1:0] sreg4; // Ch0 /L
reg [AUDIO_BIT_DEPTH-1:0] sreg5; // Ch1 /R
reg [AUDIO_BIT_DEPTH-1:0] if_l_o;
reg [AUDIO_BIT_DEPTH-1:0] if_r_o;
reg codec_datas_o;
wire spdif_valid;
wire pll_locked;
wire bck;
///////////////////////////////////////////////
// timing generator
///////////////////////////////////////////////
always @(posedge clk or posedge reset)
begin
if (reset)
div_count <= 9'b0;
else if (div_count == 9'b1_1111_1111)
div_count <= 9'b0;
else
div_count <= div_count + 9'b1;
end
assign en = (div_count == 9'b1_1111_1111);
assign read_latch = (div_count[2:0] == 3'b011);
assign write_latch = (div_count[2:0] == 3'b000);
assign bit_count = div_count[8:3];
assign bck = div_count[2]; // 64fs
assign asrc_lrck_o = ~div_count[8]; // fs
assign codec_lrck_o = ~div_count[8]; // fs
assign asrc_bck_o = bck;
assign codec_sck_o = bck;
assign codec_mck_o = bck;
// DCM_SP: 77.824MHz *3/8 => 29.184MHz
pll78to29 pll78to29 (
.CLKIN_IN(clk), // Clock input (from IBUFG, BUFG or DCM)
.RST_IN(reset), // DCM asynchronous reset input
.CLKFX_OUT(asrc_mclk_o), // DCM CLK synthesis out (M/D)
.LOCKED_OUT(pll_locked) // DCM LOCK status output
);
///////////////////////////////////////////////
// ASRC, CODEC I/F (BITCLOCK_FREQUENCY = 64fs)
// Left-Justified, MSB-First
///////////////////////////////////////////////
// Serial to Parallel
always @(posedge clk or posedge reset)
begin
if (reset)
begin
sreg0 <= 0;
sreg1 <= 0;
sreg2 <= 0;
sreg3 <= 0;
end
else if (read_latch)
begin
// Left Channel
if (AUDIO_BIT_DEPTH > bit_count)
begin
sreg0[(AUDIO_BIT_DEPTH -1) - bit_count] <= asrc_data_i;
sreg2[(AUDIO_BIT_DEPTH -1) - bit_count] <= codec_datar_i;
end
// Right Channel
else if ((bit_count > 31) & (bit_count < (AUDIO_BIT_DEPTH +32)))
begin
sreg1[(AUDIO_BIT_DEPTH -1) - bit_count +32] <= asrc_data_i;
sreg3[(AUDIO_BIT_DEPTH -1) - bit_count +32] <= codec_datar_i;
end
end
end
// S/PDIF_valid
assign spdif_valid = pll_locked & ~(spdif_error_i | spdif_nonaudio_i);
// Mux
always @(posedge clk or posedge reset)
begin
if (reset)
begin
if_l_o <= 0;
if_r_o <= 0;
end
else if (en)
begin
// Input Select: CODEC
if (if_sel)
begin
if_l_o <= sreg2;
if_r_o <= sreg3;
end
// Input Select: ASRC
else
begin
if (spdif_valid)
begin
if_l_o <= sreg0;
if_r_o <= sreg1;
end
else
begin
if_l_o <= 0;
if_r_o <= 0;
end
end
end
end
// Parallel to Serial
always @(posedge clk or posedge reset)
begin
if (reset)
begin
sreg4 <= 0;
sreg5 <= 0;
end
// Send Buffer
else if (en)
begin
sreg4 <= if_mon0_i;
sreg5 <= if_mon1_i;
end
end
always @(posedge clk or posedge reset)
begin
if (reset)
codec_datas_o <= 0;
else if (write_latch)
begin
// Left Channel
if (AUDIO_BIT_DEPTH > bit_count)
codec_datas_o <= sreg4[(AUDIO_BIT_DEPTH -1) - bit_count];
// Right Channel
else if ((bit_count > 31) & (bit_count < (AUDIO_BIT_DEPTH +32)))
codec_datas_o <= sreg5[(AUDIO_BIT_DEPTH -1) - bit_count +32];
else
codec_datas_o <= 0;
end
end
endmodule
0 件のコメント:
コメントを投稿