stream_ モジュール群についての解析を行っている。
まずは、基本的なストリーム・モジュールについて。
stream_register.sv
シンプルなready/validハンドシェイクを備えたレジスタモジュールである。
すべての制御信号の組み合わせパスを切断しないため、完全なパス切断が必要な場合はspill_registerを使用すべきである。
/// Register with a simple stream-like ready/valid handshake. /// This register does not cut combinatorial paths on all control signals; if you need a complete /// cut, use the `spill_register`. module stream_register #( parameter type T = logic // Vivado requires a default value for type parameters. ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous active-low reset input logic clr_i, // Synchronous clear input logic testmode_i, // Test mode to bypass clock gating // Input port input logic valid_i, output logic ready_o, input T data_i, // Output port output logic valid_o, input logic ready_i, output T data_o ); logic reg_ena; assign ready_o = ready_i | ~valid_o; assign reg_ena = valid_i & ready_o; // Load-enable FFs with synch clear `FFLARNC(valid_o, valid_i, ready_o, clr_i, 1'b0 , clk_i, rst_ni) `FFLARNC(data_o, data_i, reg_ena, clr_i, T'('0), clk_i, rst_ni) endmodule
ready_oの条件はassign ready_o = ready_i | ~valid_o;で定義されている。
これは、以下の2つの条件のいずれかが満たされた場合に、上流に対して準備できている(ready_o = 1)ことを示す。
下流が準備できている場合 (
ready_i = 1): 下流がデータを受け取る準備ができている場合、レジスタに保持されているデータが次のクロックサイクルで下流に転送されるため、レジスタは空き状態になり、新しいデータを受け入れられる。レジスタが空いている場合 (
valid_o = 0、つまり~valid_o = 1): レジスタにデータが保持されていない場合、常に新しいデータを受け入れられる。
逆に、レジスタにデータが保持されている(valid_o = 1)かつ下流が準備できていない(ready_i = 0)場合のみ、ready_o = 0となり、上流からのデータ受け入れを停止する。
(これは論理的には valid_o & ~ready_i でデータを受け入れ停止することを意味し、つまりこの否定 ~(valid_o & ~ready_i) = ~valid_o | ready_i が受け入れ条件として成立している)。
これは、1段のレジスタしかないため、保持されているデータが下流に転送されるまで、新しいデータを受け入れられないことを意味する。
レジスタイネーブル信号reg_enaはvalid_i & ready_oで生成され、上流がデータを有効にしている(valid_i = 1)かつ、レジスタが受け入れ可能な状態(ready_o = 1)の場合にのみ、データがレジスタにロードされる。
stream_fifo.sv
任意の深さ(0から232まで)をサポートするストリームFIFOモジュールである。フォールスルーモードをサポートし、fifo_v3を内部で使用している。
module stream_fifo #( /// FIFO is in fall-through mode parameter bit FALL_THROUGH = 1'b0, /// Default data width if the fifo is of type logic parameter int unsigned DATA_WIDTH = 32, /// Depth can be arbitrary from 0 to 2**32 parameter int unsigned DEPTH = 8, parameter type T = logic [DATA_WIDTH-1:0], // DO NOT OVERWRITE THIS PARAMETER parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic flush_i, // flush the fifo input logic testmode_i, // test_mode to bypass clock gating output logic [ADDR_DEPTH-1:0] usage_o, // fill pointer // input interface input T data_i, // data to push into the fifo input logic valid_i, // input data valid output logic ready_o, // fifo is not full // output interface output T data_o, // output data output logic valid_o, // fifo is not empty input logic ready_i // pop head from fifo ); logic push, pop; logic empty, full; assign push = valid_i & ~full; assign pop = ready_i & ~empty; assign ready_o = ~full; assign valid_o = ~empty; fifo_v3 #( .FALL_THROUGH (FALL_THROUGH), .DATA_WIDTH (DATA_WIDTH), .DEPTH (DEPTH), .dtype(T) ) fifo_i ( .clk_i, .rst_ni, .flush_i, .testmode_i, .full_o (full), .empty_o (empty), .usage_o, .data_i, .push_i (push), .data_o, .pop_i (pop) ); endmodule
ready_oの条件はassign ready_o = ~full;で定義されている。
これは、FIFOが満杯でない場合(fullが0の場合)にready_oが1になることを意味する。
つまり、FIFOに空きがある限り、上流からのデータを受け入れる準備ができていることを示す。
実際のデータプッシュ操作は、assign push = valid_i & ~full;で制御される。
上流がvalid_iをアサートし、かつFIFOが満杯でない場合にのみ、データがFIFOにプッシュされる。
この設計により、FIFOが満杯の場合は上流に対してready_o = 0を返し、データの受け入れを一時的に停止する。
これにより、FIFOのオーバーフローを防止し、ready/validハンドシェイクプロトコルに準拠した動作を実現している。
同様に、出力側のvalid_oはassign valid_o = ~empty;で定義されており、FIFOが空でない場合にデータが有効であることを示す。
stream_fifo_optimal_wrap.sv
深さに応じて最適な実装を選択するラッパーモジュールである。
深さが2の場合はspill_registerを使用し、深さが3以上の場合はstream_fifoを使用する。
深さ0と1の設定は無意味であるため、エラーを発生させる。
/// Optimal implementation of a stream FIFO based on the common cells modules. /// Selects the smaller and faster spill register if the depth is 2 and the FIFO if /// the depth is >2. Throws an error for the meaningless configurations depth 0 and 1. module stream_fifo_optimal_wrap #( /// Depth can be arbitrary from 2 to 2**32 parameter int unsigned Depth = 32'd8, /// Type of the FIFO parameter type type_t = logic, /// Print information when the simulation launches parameter bit PrintInfo = 1'b0, // DO NOT OVERWRITE THIS PARAMETER parameter int unsigned AddrDepth = (Depth > 32'd1) ? $clog2(Depth) : 32'd1 ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic flush_i, // flush the fifo input logic testmode_i, // test_mode to bypass clock gating output logic [AddrDepth-1:0] usage_o, // fill pointer // input interface input type_t data_i, // data to push into the fifo input logic valid_i, // input data valid output logic ready_o, // fifo is not full // output interface output type_t data_o, // output data output logic valid_o, // fifo is not empty input logic ready_i // pop head from fifo ); //-------------------------------------- // Prevent Depth 0 and 1 //-------------------------------------- // Throw an error if depth is 0 or 1 `ifndef SYNTHESIS if (Depth < 32'd2) begin : gen_fatal initial begin $fatal(1, "FIFO of depth %d does not make any sense!", Depth); end end `endif //-------------------------------------- // Spill register (depth 2) //-------------------------------------- // Instantiate a spill register for depth 2 if (Depth == 32'd2) begin : gen_spill // print info `ifndef SYNTHESIS if (PrintInfo) begin : gen_info initial begin $display("[%m] Instantiate spill register (of depth %d)", Depth); end end `endif // spill register spill_register_flushable #( .T ( type_t ), .Bypass ( 1'b0 ) ) i_spill_register_flushable ( .clk_i, .rst_ni, .flush_i, .valid_i, .ready_o, .data_i, .valid_o, .ready_i, .data_o ); // usage is not supported assign usage_o = 'x; end //-------------------------------------- // FIFO register (depth 3+) //-------------------------------------- // default to stream fifo if (Depth > 32'd2) begin : gen_fifo // print info `ifndef SYNTHESIS if (PrintInfo) begin : gen_info initial begin $info("[%m] Instantiate stream FIFO of depth %d", Depth); end end `endif // stream fifo stream_fifo #( .DEPTH ( Depth ), .T ( type_t ) ) i_stream_fifo ( .clk_i, .rst_ni, .flush_i, .testmode_i, .usage_o, .data_i, .valid_i, .ready_o, .data_o, .valid_o, .ready_i ); end endmodule : stream_fifo_optimal_wrap