Verilog。 RAM上のデジタルフィルター

FPGAに大きなデジタルフィルターを配置する必要がある場合はどうすればよいですか? そして、ボードがすでに離婚している場合はどうなりますか? 鉄は古いですか? プロジェクトに残っているスペースはほとんどありませんか? このトピックでは、アルテラCyclone II EP2C15 FPGAでのデジタルFIRフィルターの可能な実装の1つを検討します。 実際、これはサンドボックスからのこのテーマの続きです。

LEコストを削減しながらRAMにシフトレジスタを作成する方法と、これからデジタルフィルターを取得する方法について説明します。





フィルターはどのように機能しますか? 基本的な操作は累積乗算です。 フィルタ係数は、シフトレジスタの値と乗算され、合計されます。 すべて、詳細に入らない場合。 必要な材料が表明されたので、ビジネスに取り掛かりましょう。



累積乗算


フィルターの次数でフィルターの周波数応答の目的のタイプを既に決定し、その係数を取得したと考えています。入力データの速度はわかっています。 これらのパラメーターを何らかの方法でパラメーター化するとさらに良いでしょう。 やってみよう。 累積乗算の私の実装は次のとおりです。

module mult #(parameter COEF_WIDTH = 24, parameter DATA_WIDTH = 16, parameter ADDR_WIDTH = 9, parameter MULT_WIDTH = COEF_WIDTH + DATA_WIDTH) ( input wire clk, input wire en, input wire [ (ADDR_WIDTH-1) : 0 ] ad, input wire signed [ (COEF_WIDTH-1) : 0 ] coe, input wire signed [ (DATA_WIDTH-1) : 0 ] pip, output wire signed [ (DATA_WIDTH-1) : 0 ] dout ); wire signed [(MULT_WIDTH-1) : 0 ] mu = coe * pip; reg signed [ (MULT_WIDTH-1) : 0 ] rac = {(MULT_WIDTH){1'b0}}; reg signed [ (DATA_WIDTH-1) : 0 ] ro = {DATA_WIDTH{1'b0}}; assign dout = ro; always @(posedge clk) if(en) if(ad == {ADDR_WIDTH{1'b0}}) begin rac <= mu; ro <= rac[ (MULT_WIDTH-2) -: (DATA_WIDTH) ]; end else rac <= rac + mu; endmodule
      
      







ADDR_WIDTH = 9なのはなぜですか? フィルターの次数は2 ^ 9 = 512に等しく選択されるため、最初に、これは分周器またはPLLから周波数を取得しやすくするために行われます。 次に、サンプルレートが16 kHzであったため、周波数を512倍に増やす機会がありました。 しかし、それについては後で。 もちろん、パラメータ化のために非常に読みやすいわけではありませんが、理解することができます。



フィルター係数


上記のリンク上のサンドボックスからトピックを読みますか? RAMパターンはありましたか? このテンプレートは、もう私たちには合いません。 そのRAMを1クロックサイクルで読み書きすることができませんでした。 多分すべては知識ではないからですが、フィルター係数はこのモジュールに保存されています:



 module coef #(parameter DATA_WIDTH=24, parameter ADDR_WIDTH=9) ( input wire [(DATA_WIDTH-1):0] data, input wire [(ADDR_WIDTH-1):0] addr, input wire we, input wire clk, output wire [(DATA_WIDTH-1):0] coef_rom ); reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0]; reg [(DATA_WIDTH-1):0] data_out; assign coef_rom = data_out; initial begin rom[0 ] = 24'b000000000000000000000000; rom[1 ] = 24'b000000000000000000000001; //new year tree rom[510] = 24'b000000000000000000000001; rom[511] = 24'b000000000000000000000000; end always @ (posedge clk) begin data_out <= rom[addr]; if (we) rom[addr] <= data; end endmodule
      
      







落胆に追いつかないように、約508のオッズは省略されました。 なぜ16ビットではなく24ビットなのか? 私はスペクトルが好きです。 しかし、これは重要ではありません。 オッズを変えることは、長い仕事ではありません。 さらに、初期開始後に$ readmembまたは$ readmemhスクリプトを使用してメモリ初期化ファイルを添付できます。



シフトレジスタ


これが実際に私がこれを書く主な理由です。 たぶん、誰かが彼がすでに知っていると思うでしょう。 たぶん、他の何かが善の作者について、そこの車輪について何かを考えるでしょう。

ここでは、ラッパーを使用してRAMにシフトレジスタを作成する方法を説明します。 おそらく誰もが、RAMがシフトレジスタとして機能できることをFPGAのハンドブックで読んでいます。 どうやって? 私はそれをやった、それについて複雑なことは何もない。 しかし、なぜですか? Cycloneファミリは、メモリバイアスを備えたデバイスとして位置付けられています。「デバイスは、FPGAデザインのオンチップメモリ​​のニーズに対応するための組み込みメモリ構造を備えています。」 この問題は、RAMとラッパーの2つで解決されます。 RAMはフィルター係数の保存に似ています:



 module pip #(parameter DATA_WIDTH=16, parameter ADDR_WIDTH=9) ( input wire [(DATA_WIDTH-1):0] data, input wire [(ADDR_WIDTH-1):0] read_addr, write_addr, input wire we, input wire clk, output wire [(DATA_WIDTH-1):0] pip_ram ); reg [DATA_WIDTH-1:0] ram[2**ADDR_WIDTH-1:0]; reg [(DATA_WIDTH-1):0] data_out; assign pip_ram = data_out; always @ (posedge clk) begin data_out <= ram[read_addr]; if (we) ram[write_addr] <= data; end endmodule
      
      







唯一のことは、RAMを初期化せずに、自動的にゼロで埋められることです。 ちなみに、この手法は、フィルター係数が2 ^ N未満の場合、フィルター係数を記録するときに使用できます。

ラッパー自体:



 module upr #(parameter COEF_WIDTH = 24, parameter DATA_WIDTH = 16, parameter ADDR_WIDTH = 9) ( input wire clk, input wire en, input wire [ (DATA_WIDTH-1) : 0 ] ram_upr, input wire [ (DATA_WIDTH-1) : 0 ] data_in, output wire [ (DATA_WIDTH-1) : 0 ] upr_ram, output wire we_ram, output wire [ (ADDR_WIDTH-1) : 0 ] adr_out ); assign upr_ram = (r_adr == {ADDR_WIDTH{1'b0}}) ? data_in : ram_upr; assign we_ram = (r_state == state1) ? 1'b1 : 1'b0; assign adr_out = r_adr; reg [ 2 : 0 ] r_state = state0; localparam state0 = 3'b001, state1 = 3'b010, state2 = 3'b100; reg [ (ADDR_WIDTH-1) : 0 ] r_adr = {ADDR_WIDTH{1'b0}}; always @(posedge clk) if(en) begin case(r_state) state0: r_state <= state1; state1: r_state <= state1; state2: begin end endcase end always @(posedge clk) case(r_state) state0: r_adr <= {ADDR_WIDTH{1'b0}}; state1: r_adr <= r_adr + 1'b1; state2: begin end endcase endmodule
      
      





同じアドレスが、係数とシフトレジスタとともにRAMに供給されます。 シフトレジスタからRAMを介してフィードバックすると、前の値がモジュールに送られ、現在のアドレスに記録されます。 したがって、シフトは1サイクルではなく、1つの値ごとに実行されます。 入力ワードは、ゼロアドレスごとに書き込まれます。

一部の州が関与していないのに、なぜ私は頑固に状態マシンを使用しているのですか? 最初に参照によって書かれたものを思い出します。 現在、このモジュールは2倍の速度で動作します。つまり、他のすべての条件が同じであれば、半分の時間アイドル状態になります。 理論的には、この半分は何かで占有することができます。 これは、適応フィルタリング用のフィルター係数の再計算、または2番目のフィルター(タイムスロットのようなもの)の働きです。 ここには何もありませんし、FSMはここでは必要ありませんが、私はまだこのアタビズムを残しました。 FSMを削除することは、記述するよりも常に簡単です。



合計


shimantikから出たトップエンドファイルは次のとおりです。



 module filtr_ram( CLK, D_IN, MULT ); input CLK; input [15:0] D_IN; output [15:0] MULT; wire SYNTHESIZED_WIRE_13; wire [15:0] SYNTHESIZED_WIRE_1; wire [8:0] SYNTHESIZED_WIRE_14; wire SYNTHESIZED_WIRE_4; wire [15:0] SYNTHESIZED_WIRE_15; wire SYNTHESIZED_WIRE_6; wire [0:23] SYNTHESIZED_WIRE_8; wire [23:0] SYNTHESIZED_WIRE_11; assign SYNTHESIZED_WIRE_4 = 1; assign SYNTHESIZED_WIRE_6 = 0; assign SYNTHESIZED_WIRE_8 = 0; pip b2v_inst( .we(SYNTHESIZED_WIRE_13), .clk(CLK), .data(SYNTHESIZED_WIRE_1), .read_addr(SYNTHESIZED_WIRE_14), .write_addr(SYNTHESIZED_WIRE_14), .pip_ram(SYNTHESIZED_WIRE_15)); defparam b2v_inst.ADDR_WIDTH = 9; defparam b2v_inst.DATA_WIDTH = 16; upr b2v_inst1( .clk(CLK), .en(SYNTHESIZED_WIRE_4), .data_in(D_IN), .ram_upr(SYNTHESIZED_WIRE_15), .we_ram(SYNTHESIZED_WIRE_13), .adr_out(SYNTHESIZED_WIRE_14), .upr_ram(SYNTHESIZED_WIRE_1)); defparam b2v_inst1.ADDR_WIDTH = 9; defparam b2v_inst1.COEF_WIDTH = 24; defparam b2v_inst1.DATA_WIDTH = 16; coef b2v_inst3( .we(SYNTHESIZED_WIRE_6), .clk(CLK), .addr(SYNTHESIZED_WIRE_14), .data(SYNTHESIZED_WIRE_8), .coef_rom(SYNTHESIZED_WIRE_11)); defparam b2v_inst3.ADDR_WIDTH = 9; defparam b2v_inst3.DATA_WIDTH = 24; mult b2v_inst5( .clk(CLK), .en(SYNTHESIZED_WIRE_13), .ad(SYNTHESIZED_WIRE_14), .coe(SYNTHESIZED_WIRE_11), .pip(SYNTHESIZED_WIRE_15), .dout(MULT)); defparam b2v_inst5.ADDR_WIDTH = 9; defparam b2v_inst5.COEF_WIDTH = 24; defparam b2v_inst5.DATA_WIDTH = 16; endmodule
      
      







あなたはすぐに修正して、より美しくなることができます。

再び何が起こったのかについて。 主なマイナスは、この完全なシリアルフィルターです。 つまり、フィルターの周波数は、入力データの速度に対して2 ^(ADDR_WIDTH)回上げる必要があります。 この問題は、フィルターのインパルス応答が対称であれば解決できますが、シフトレジスタRAMは2つのアドレスに送信される2つのモジュールに分割する必要があり、RAMからの値はマルチモジュールで追加および乗算され、別の入力を追加する必要があります。 次に、周波数を2 ^(ADDR_WIDTH-1)回上げる必要があります。



Quartus 9.0のソースとプロジェクト

ifolder.ru/27556340



All Articles