Verilog。 RAMラッパーとそれが必要な理由

実際、FPGAの知識は2歳です。 私の学生時代、アルテラとザイリンクスのコード例を読んで、RAMとROMの説明はほとんど必要ありませんでした。 FIFOがあることは知っていましたが、なぜ必要なのかさえ知りませんでした。 そこで、プロジェクトを「額に」することで卒業証書を守りました。



FPGAの分野での「知識」のおかげで仕事を見つけました。 すぐに問題が判明しました。既存のプロジェクトが論理要素の90%を占めており、私の仕事はプロジェクトにデジタルフィルターを配置することでした。 この問題を解決するための「額」が不可能であることは明らかです。 既存のコードを書き換えますか? 助けにはなりましたが、大したことはありませんでした。 そしてそのとき初めて、タスクを解決するためにRAMをどのように使用できるかを理解しました。



注意! ここではLEDは点滅しません。 代わりに、M4Kのラッパーシフト実装が検討されます。 この出版物は、FPGAとVerilogの知識が最小限であることを前提としています。



そもそも、別のプロジェクトで必要なフィルターを「額に入れる」ことで、どこに薄いスポットがあるかが明らかになりました。 長さ444 * 16ビット(444はフィルター次数、16ビットはワード次元)のシフトレジスタであることが判明しました。 これでやるべきことがありました。 シフトが些細な操作であるという事実に思い付きました。 ステップNでレジスタの現在の値を取得し、ステップN-1で取得したレジスタの値をそこに書き込みます。 コード例を読んだ後、RAMを使用してこれを行う方法を見つけました。



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







これは例から直接取られており、パラメータのみが変更されています。 上記のように、このRAMを機能させる必要があります。 このために、RAMラッパーと呼ばれるものが作成されました。 シンプルなステートマシン:



 module upr #(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 = ram; assign we_ram = r_we; 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}}; reg [ (DATA_WIDTH-1) : 0 ] ram = {DATA_WIDTH{1'b0}}; reg r_we = 1'b0; always @(posedge clk) if(en) begin case(r_state) state0: r_state <= state1; state1: r_state <= state2; state2: r_state <= state1; endcase end always @(posedge clk) case(r_state) state0: begin r_we <= 1'b0; r_adr <= {ADDR_WIDTH{1'b0}}; ram <= data_in; end state1: begin r_we <= 1'b1; if(r_adr == {ADDR_WIDTH{1'b0}}) ram <= data_in; else ram <= ram_upr; end state2: begin r_adr <= r_adr + 1'b1; r_we <= 1'b0; end endcase endmodule
      
      







2段階+リセット中に使用できる1つの状態で動作します。 ステートマシンは非常に単純です。遷移条件はなく、クロック信号の先頭のみです。 最初のステップ(State1)は、データ、またはRAMからの以前の出力値をキャプチャすることです。 そのようなフィードバックが判明しました。 2番目のステップでは、書き込み信号が1に設定され、RAMが必要なものをキャプチャします。



このアプローチには大きなマイナスがあります-シフトには2クロックサイクルかかりますが、この問題も簡単に解決できます。 もう1つの重要なマイナス点は、1クロックサイクルでシフトレジスタから少なくとも2つの値を「引き出す」ことができないことです。 これは、これの畳み込みエンコーダーがしないことを意味します。 このアプローチの利点は、メモリによる論理要素(LEまたはスライス)の節約です。



2つの対策の問題を解決する方法、さらに興味深いことに、これに基づいて完全にパラメーター化されたデジタルFIRフィルターを作成する方法は、誰にとっても興味深いものかどうかを教えてくれます。



All Articles