前回の記事で、プリエンファシスモジュールを使用すると最大動作周波数が140MHzから110MHzに低下してしまう事象が見られました。システムクロックは77.824MHzですから問題とはなりませんが、サンプリング周波数152kHzに対して速すぎるシステムクロックは、消費電力や動作周波数の面で無駄なので落としてみてはどうかとアドバイスを受けて試してみることにしました。
結果、プリエンファシスモジュールのクロック周波数をシステムクロックの8分の1に変更したところ最大動作周波数が低下しなくなりました。
クロック信号供給方法の見直し
受け売りですが・・・同期回路のクロックはIC内部のすべてフリップフロップに数10psオーダで同時に供給する必要があります。このため、FPGAにはクロックを供給するための特別な配線リソースが組み込まれています。このリソースをクロック信号に使わせるため、FPGA設計ツールにクロック信号であることを明確に伝える配慮が必要だそうです。
改善点
・ASRCのマスタクロックを codec_if.v モジュール内部のDCMで生成していたが、DCMをトップ階層に変更した。
・外部クロック信号をいったん clock_gen というモジュールに引き込んだうえでFPGA全体のクロック信号にアサインしていたが、DCMのCLK0ピンから出力される77.824MHzをクロックとして使用するよう変更した。ついでに clock_gen の名前は、reset_gen と変更した。
top.v
module top(
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,
clk_i, resetsw_i, n_reset_o, n_sw_i
);
output asrc_mclk_o;
output asrc_bck_o;
output asrc_lrck_o;
input asrc_data_i;
input spdif_nonaudio_i;
input spdif_error_i;
input codec_datar_i;
output codec_sck_o;
output codec_mck_o;
output codec_lrck_o;
output codec_datas_o;
input clk_i;
input resetsw_i;
output n_reset_o;
input [3:0] n_sw_i;
wire [23:0] if_l_o;
wire [23:0] if_r_o;
wire [23:0] l15k_l_o;
wire [23:0] l15k_r_o;
wire [23:0] pe_l_o;
wire [23:0] pe_r_o;
wire en;
wire clk;
wire clk_9M7;
wire reset;
wire if_sel;
// ASRC/CODEC Interface
codec_if codec_if(
.asrc_bck_o(asrc_bck_o),
.asrc_lrck_o(asrc_lrck_o),
.asrc_data_i(asrc_data_i),
.spdif_nonaudio_i(spdif_nonaudio_i),
.spdif_error_i(spdif_error_i),
.codec_datar_i(codec_datar_i),
.codec_sck_o(codec_sck_o),
.codec_mck_o(codec_mck_o),
.codec_lrck_o(codec_lrck_o),
.codec_datas_o(codec_datas_o),
.if_l_o(if_l_o),
.if_r_o(if_r_o),
.if_mon0_i(pe_l_o),
.if_mon1_i(pe_r_o),
.en(en),
.clk(clk),
.reset(reset),
.if_sel(n_sw_i[3])
);
// Reset Generator
reset_gen reset_gen(
.clk(clk),
.resetsw_i(resetsw_i),
.reset(reset),
.n_reset_o(n_reset_o)
);
// DCM_SP: 77.824MHz *3/8 => 29.184MHz
pll78to29 pll78to29 (
.CLKIN_IN(clk_i),
.CLKFX_OUT(asrc_mclk_o),
// .CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT),
.CLK0_OUT(clk)
);
// DCM_SP: 77.824MHz *1/8 => 9.728MHz
pll78to10 pll78to10 (
.CLKIN_IN(clk),
.CLKDV_OUT(clk_9M7)
// .CLK0_OUT(CLK0_OUT)
);
// 15kHz FIR LPF
lpf15k lpf15k(
.l15k_l_i(if_l_o),
.l15k_r_i(if_r_o),
.l15k_l_o(l15k_l_o),
.l15k_r_o(l15k_r_o),
.clk(clk),
.reset(reset),
.l15k_thru(~n_sw_i[2])
);
// Pre-emphasis
pre_emph pre_emph(
.pe_l_i(l15k_l_o),
.pe_r_i(l15k_r_o),
.pe_l_o(pe_l_o),
.pe_r_o(pe_r_o),
.clk(clk_9M7),
.reset(reset),
.pe_thru(~n_sw_i[1])
);
endmodule
DCM_SP (pll78to29) 設定画面1
DCM_SP (pll78to29) 設定画面2
DCM_SP (pll78to29) 設定画面3
DCM_SP (pll78to10) 設定画面1
DCM_SP (pll78to10) 設定画面2
codec_if.v
module codec_if(
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
);
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 [23:0] if_l_o;
output [23:0] if_r_o;
input [23:0] if_mon0_i;
input [23: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 [23:0] sreg0; // ASRC L
reg [23:0] sreg1; // ASRC R
reg [23:0] sreg2; // CODEC L
reg [23:0] sreg3; // CODEC R
// Send Buffer
reg [23:0] sreg4; // Ch0 /L
reg [23:0] sreg5; // Ch1 /R
reg [23:0] if_l_o;
reg [23:0] if_r_o;
reg codec_datas_o;
wire spdif_valid;
wire bck;
///////////////////////////////////////////////
// timing generator
///////////////////////////////////////////////
always @(posedge clk or posedge reset)
begin
if (reset)
div_count <= 0;
else if (div_count == 511)
div_count <= 0;
else
div_count <= div_count + 1;
end
assign en = (div_count == 511);
assign read_latch = (div_count[2:0] == 3);
assign write_latch = (div_count[2:0] == 0);
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;
///////////////////////////////////////////////
// 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 (24 > bit_count)
begin
sreg0[23 - bit_count] <= asrc_data_i;
sreg2[23 - bit_count] <= codec_datar_i;
end
// Right Channel
else if ((bit_count > 31) & (bit_count < (24 + 32)))
begin
sreg1[23 - bit_count + 32] <= asrc_data_i;
sreg3[23 - bit_count + 32] <= codec_datar_i;
end
end
end
// S/PDIF_valid
assign spdif_valid = ~(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 (24 > bit_count)
codec_datas_o <= sreg4[23 - bit_count];
// Right Channel
else if ((bit_count > 31) & (bit_count < (24 + 32)))
codec_datas_o <= sreg5[23 - bit_count +32];
else
codec_datas_o <= 0;
end
end
endmodule
reset_gen.v
module reset_gen(clk, resetsw_i, reset, n_reset_o);
input clk;
input resetsw_i;
output reset;
output n_reset_o;
// reset signal
reg [19:0] reset_count;
reg reset;
always @(posedge clk or negedge resetsw_i)
begin
if (~resetsw_i)
begin
reset_count <= 0;
reset <= 1;
end
else if (reset_count == 20'hFFFFF)
reset <= 0;
else
reset_count <= reset_count + 1;
end
assign n_reset_o = ~reset;
endmodule
0 件のコメント:
コメントを投稿