ようやくその実装ができましたので備忘録です。
今回はこちらを参考にさせていただきました。
構成
実装した構成を下図に示します。前回シミュレーションしたものと回路は異なりますが、構成要素としてはほぼ同じとなります。
LVDSの+側にアナログの電圧を入力し、-側にはCR回路を接続します。
ΔΣ型ADCには1bitの量子化器(ADC)と積分器が必要です。
ここではLVDSの差動入力を1bitの量子化器として利用し、CR回路が積分回路となります。
DFFの出力はΔΣ変調された信号が出力されます。
そのままでは1ビット幅のデータしか得られないので、デシメーションフィルタを通して多ビット幅のデータにします。今回はデシメーションフィルタとしてCICフィルタを実装してみました。
CICフィルタの出力には高い周波数成分が含まれるため、さらにLPFを通して余計な高周波成分を取り除いてあげます。
実装にはFPGAはわれらがMAX10ボード(CQ出版社 FPGA電子工作スーパーキットのボード)を使用します。
MAX10の一部にはADCが内蔵されており、今回使用したボードにもADCが内蔵されていますが、ガン無視して一からΔΣ型ADCを実装します。
ΔΣ変調部
ΔΣ変調部のHDLは非常に簡単なもので、クロックの立ち上がりのタイミングでLVDSの入力をサンプルするだけです。//---------------------------------------------------------- // Delta Sigma Modulator //---------------------------------------------------------- // DFF reg ovs_ff; always @(posedge clk_div, negedge res_n) begin if(~res_n) ovs_ff <= 1'b0; else ovs_ff <= adc_in; end assign adc_fb = ovs_ff;
抵抗とコンデンサの値については計算すれば最適な値が求められそうですが、今回はカットアンドトライでそれなりに動作するような値を決めています。
デシメーションフィルタ
ΔΣ変調された信号は1ビットのデータですので、これを多ビットのデータにする必要があります。デシメーションフィルタを使用することで、高速でサンプリングされた1ビットのデータを、サンプリング周波数を下げてビット数を増やすサンプリング周波数変換を行います。
今回はデシメーションフィルタとしてCIC(Cascade Integrated Comb)フィルタを使用しました。 CICフィルタについては下記が参考になります。
https://dspguru.com/dsp/tutorials/cic-filter-introduction
CICフィルタは比較的簡単に実装できます。CICフィルタの構成を下図に示します。
左側に3つ並んでいるのが積分器、右側に3つ並んでいるのが微分器です。
真ん中にあるのがリサンプリングです。下矢印はサンプリング周波数を下げていることを示しています。
この図は積分器および微分器のタップ数を3とした例を示しています。
最終的な最終的な出力のビット数は下記のようになります。
\[
B_{out} = \lceil N \log_2{RM} + B_{in} \rceil
\]
ここでBoutは出力のビット数、Nは積分器及び微分器のタップ数、Rはリサンプリング比、Mは微分器の遅延(今回は1です)、Binは入力のビット数です。
積分器の実装
まずは積分器を実装します。実装の際にはこちらのページを参考にさせていただきました。
//----------------------------------- // Integrator unit for CIC filter //----------------------------------- module cic_integration( clk, // clock in res_n, // reset negative in_data, // input data out_data // output data ); //--- Parameter declarations ---- parameter TAP_NUM = 4; parameter BIT_W = 9; //---- IO declarations ---------- input clk; input res_n; input [BIT_W-1:0] in_data; output [BIT_W-1:0] out_data; //---- local declarations ------- reg [BIT_W-1:0] data[0:TAP_NUM-1]; integer i; //---- module body -------------- always @(posedge clk, negedge res_n) begin if(~res_n) begin for(i=0; i<TAP_NUM; i=i+1) data[i] <= {BIT_W{1'b0}}; end else begin data[0] <= in_data + data[0]; for(i=1; i<TAP_NUM; i=i+1) data[i] <= data[i-1] + data[i]; end end assign out_data = data[TAP_NUM-1]; endmodule
特に難しいことはないです。
微分器の実装
次に微分器を実装します。//----------------------------------- // Dffirentiation unit for CIC filter //----------------------------------- module cic_differentiation( clk, // clock in res_n, // reset negative in_data, // input data out_data // output data ); //--- Parameter declarations ---- parameter TAP_NUM = 4; parameter BIT_W = 9; //---- IO declarations ---------- input clk; input res_n; input [BIT_W-1:0] in_data; output [BIT_W-1:0] out_data; //---- local declarations ------- reg [BIT_W-1:0] data[0:TAP_NUM-1]; reg [BIT_W-1:0] delay_data[0:TAP_NUM-1]; integer i; //---- module body -------------- always @(posedge clk, negedge res_n) begin if(~res_n) begin for(i=0; i<TAP_NUM; i=i+1) begin data[i] <= {BIT_W{1'b0}}; delay_data[i] <= {BIT_W{1'b0}}; end end else begin delay_data[0] <= in_data; data[0] <= in_data + ~delay_data[0] + {{BIT_W-1{1'b0}},1'b1}; for(i=1; i<TAP_NUM; i=i+1) begin delay_data[i] <= data[i-1]; data[i] <= data[i-1] + ~delay_data[i] + {{BIT_W-1{1'b0}},1'b1}; end end end assign out_data = data[TAP_NUM-1]; endmodule
CICフィルタの実装
そして積分器と微分器を組み合わせてCICフィルタを実装します。微分器はリサンプリングした後のクロックで動かしています。
//-------------------------- // CIC Filter //-------------------------- module cic_filter( clk, // clock input res_n, // reset_n input sig_in, // signal input (1bit) sig_out, // signal output clk_out // clock out ); //---- Parameter declaration ----------------------- // Data bit + TAP_NUM * log2(RE_SAMP * DELAY_NUM) parameter TAP_NUM = 4; parameter BIT_W = 9; parameter RE_SAMP = 2; //---- module IO declaration ----------------------- input clk; input res_n; input sig_in; // only 1bit output [BIT_W-1:0] sig_out; output clk_out; //---- clocl divider ------------------------------- reg [RE_SAMP-1:0] counter; wire clk_4; assign clk_4 = (counter == {RE_SAMP{1'b0}}) ? 1'b1 : 1'b0; assign clk_out = clk_4; always @(posedge clk, negedge res_n) begin if(~res_n) counter <= {RE_SAMP{1'h0}}; else counter <= counter + {{RE_SAMP-1{1'b0}},1'h1}; end //---- Integrator ---------------------------------- wire [BIT_W-1:0] int_out; wire [BIT_W-1:0] in_data; assign in_data = {{BIT_W-1{1'b0}},sig_in}; cic_integration #(TAP_NUM,BIT_W) cint( .clk (clk), .res_n (res_n), .in_data (in_data), .out_data (int_out) ); //---- Differentiator------------------------------ cic_differentiation #(TAP_NUM,BIT_W) cdiff( .clk (clk_4), .res_n (res_n), .in_data (int_out), .out_data (sig_out) ); endmodule
LPF
LPFとして移動平均フィルタを実装しました。単純に平均する時間分だけシフトレジスタを用意しておき、その平均を算出します。
//-------------------------------------------------------- // moving avarage filter //-------------------------------------------------------- module mav_filter( clk, // clk res_n, // reset_n in_data, // input data out_data ); //---- parameter declaration ----------------------------- parameter TAP_NUM = 8; parameter BIT_W = 9; parameter SHIFT_NUM = 3; //---- IO declaration ----------------------------------- input clk; input res_n; input [BIT_W-1:0] in_data; output [BIT_W-1:0] out_data; //---- local register and wire --------------------------- integer i; reg [BIT_W-1:0] data[0:TAP_NUM-1]; reg [BIT_W+SHIFT_NUM-1:0] add_data; reg [BIT_W+SHIFT_NUM-1:0] add_result; //---- module declaration -------------------------------- always @(posedge clk, negedge res_n) begin if(~res_n) begin for(i=0; i<TAP_NUM; i=i+1) begin data[i] <= {BIT_W{1'b0}}; add_data <= {BIT_W+SHIFT_NUM{1'b0}}; end end else begin data[0] <= {{SHIFT_NUM{1'b0}},in_data}; add_data = in_data; for(i=1; i<TAP_NUM; i=i+1) begin data[i] <= data[i-1]; add_data = add_data + data[i]; end end end assign out_data = add_data[BIT_W+SHIFT_NUM-1:SHIFT_NUM]; endmodule
動作確認
正弦波信号を入力し、ADCの出力信号を直接signaltapで取り出して動作を確認しました。 DACを構築して出力してもいいですが、DACの特性も影響するため、今回は直接観測しています。実装したパラメータは下記のとおりです。
・ΔΣ変調オーバサンプリングクロック:6MHz (48MHz/8)
・CICフィルタ積分器・微分器タップ数:4
・リサンプリング比:2^2=4
・移動平均フィルタタップ数:32
入力が1ビットなので、出力ビットは
\[
B_{out} = \lceil 4 \log_2{(4 \times 1)} + 1 \rceil = 9
\]
です。
LVDS入力およびフィードバック用の出力は2.5Vとしています。
最終的なADCのサンプリング周波数としては6MHzをリサンプリング比4で割った1.25MHzとなります。
テストモジュールを下記のように実装しました。
//------------------------------------------------------------ // Delta-Sigma ADC //------------------------------------------------------------ // clock `define CYCLE_1SEC 48000000 //-------------------- // Top of the FPGA //-------------------- module FPGA ( input wire clk, // 48MHz Clock input wire res_n, // Reset Switch input wire adc_in, output wire adc_fb, output wire delta_sigma_out, output wire clk_div ); //---- parameter declaration --------------------------------- parameter CIC_TAP_NUM = 4; parameter BIT_W = 9; parameter MAF_TAP_NUM = 32; parameter MAF_SHIFT_NUM = 5; //---------------------- // Divider //---------------------- reg [2:0] cnt_div; assign clk_div = (cnt_div == 3'd0) ? 1'b1 : 1'b0; always @(posedge clk, negedge res_n) begin if(~res_n) cnt_div <= 3'd0; else cnt_div <= cnt_div + 3'd1; end //---------------------------------------------------------- // Delta Sigma Modulator //---------------------------------------------------------- // DFF reg ovs_ff; always @(posedge clk_div, negedge res_n) begin if(~res_n) ovs_ff <= 1'b0; else ovs_ff <= adc_in; end assign adc_fb = ovs_ff; //---- CIC filter ------------------------------------------ wire [BIT_W-1:0] filter1_out; wire clk4; cic_filter #(CIC_TAP_NUM,BIT_W) filter1( .clk (clk_div), // clock input .res_n (res_n), // reset_n input .sig_in (ovs_ff), // signal input (1bit) .sig_out (filter1_out), // signal output (32bit) .clk_out (clk4) // clock out ); //-------------------------------- // moving average filter //-------------------------------- wire [BIT_W-1:0] filter2_out; mav_filter #(MAF_TAP_NUM,BIT_W,MAF_SHIFT_NUM) filter2( .clk (clk4), .res_n (res_n), // reset_n .in_data (filter1_out), // input data .out_data (filter2_out) ); assign delta_sigma_out = filter2_out[3]; endmodule
実際にMAX10ボードに書き込み、動作させました。
入力信号としては振幅2.4V(オフセット1.2V)、2kHzの正弦波を使用しました。
SignalTapで取得したCICフィルタの出力とLPFの出力を下記に示します。
CICフィルタの出力を見ると、正弦波っぽい波形が得られているものの、ノイズのようなものが乗っていることがわかります。
これに対して、LPFを通した後の波形を見るときれいな正弦波が得られていることがわかります。
無事に入力信号をAD変換することができていそうです。
出力は9ビットですが、振幅が200程度になっているのは1ビットは符号ビットとなっているためと思われます。(微分器は2の補数で計算している)
負の電圧は入力できないので、実質8ビットの出力となります。
振幅2.4Vなので出力ビットの最大値は 2.4V/2.5V * 255 ≒ 244程度になるはずです。
実際の出力電圧の振幅を見ると2.24V程度でした。 2.24V/2.5 * 255 ≒ 228 が程度となります。LPFの出力はLPFを通していますので、そのために振幅が小さくなっていると思われます。
積分器であるCR回路の定数と各フィルタの特性をいじればもっときれいな波形が得られると思いますが、精度のいらない用途ではこのままでも十分使用できるかなと思います。
0 件のコメント:
コメントを投稿