FC2カウンター FPGAの部屋 2013年11月09日
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

シミュレーション用 同期FIFO IP

AXI4 Master Interfaceモジュールの作製2(シミュレーション)”で”axi_slave_BFM.v を変更して、ネストされたアドレス転送を受け付けることにした。そのためには、ネストされたReadアドレス転送を溜めておくためにFIFOが必要となる。それで、シミュレーションに使用するために汎用の同期FIFO を作ることにした。

シミュレーション用に、Verilog HDLを使用して、汎用に使える同期FIFOを書いた。
更に、テストベンチも作ってISEでシミュレーションを行った。そのシミュレーション波形を下に示す。
sync_fifo_3_131109.png

ちなみに、シミュレーション用で簡単に作ってあるが、論理合成、インプリメントもできる。出力がFFを通っていないので、予想通りにSLICEMにマップされた。
下にProject Navigator の画面を示す。
sync_fifo_1_131109.png

下にFPGA Editor の画面を示す。
sync_fifo_2_131109.png

シミュレーション用 同期FIFO sync_fifo.v を下に貼っておく。

// Synchronous FIFO for Simulation
//
// 2013/11/08 by marsee
//
// ライセンスは二条項BSDライセンス (2-clause BSD license)とします。

`default_nettype none

module sync_fifo #(
    parameter integer C_MEMORY_SIZE =        512,    // Word (not byte), 2のn乗
    parameter integer DATA_BUS_WIDTH =        32        // RAM Data Width
)
(
    input    wire    clk,
    input    wire    rst,
    input    wire    wr_en,
    input    wire    [DATA_BUS_WIDTH-1:0]    din,
    output    wire    full,
    output    wire    almost_full,
    input    wire    rd_en,
    output    wire     [DATA_BUS_WIDTH-1:0]    dout,
    output    wire    empty,
    output    wire    almost_empty
);

    // Beyond Circuts, Constant Function in Verilog 2001を参照しました
    // http://www.beyond-circuits.com/wordpress/2008/11/constant-functions/
    function integer log2;
        input integer addr;
        begin
            addr = addr - 1;
            for (log2=0; addr>0; log2=log2+1)
                addr = addr >> 1;
        end
    endfunction
    
    reg        [DATA_BUS_WIDTH-1:0]    mem    [0:C_MEMORY_SIZE-1];
    reg        [log2(C_MEMORY_SIZE)-1:0]   mem_waddr = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]   mem_raddr = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]    rp = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]    wp = 0;

    wire    [log2(C_MEMORY_SIZE)-1:0]   plus_1 = 1;
    wire    [log2(C_MEMORY_SIZE)-1:0]   plus_2 = 2;

    wire    almost_full_node;
    wire    almost_empty_node;

    integer i;
    // initialize RAM Data
    initial begin
        for (i=0; i<C_MEMORY_SIZE; i=i+1)
            mem[i] = 0;
    end

    // Write
    always @(posedge clk) begin
        if (rst) begin
            mem_waddr <= 0;
            wp <= 0;
        end else begin
            if (wr_en) begin
                mem_waddr <= mem_waddr + 1;
                wp <= wp + 1;
            end
        end
    end

    always @(posedge clk) begin
         if (wr_en) begin
            mem[mem_waddr] <= din;
        end
    end

    assign full = (wp+plus_1 == rp) ? 1'b1 : 1'b0;
    assign almost_full_node = (wp+plus_2 == rp) ? 1'b1 : 1'b0;
    assign almost_full = full | almost_full_node;

    // Read
    always @(posedge clk) begin
        if (rst) begin
            mem_raddr <= 0;
            rp <= 0;
        end else begin
            if (rd_en) begin
                mem_raddr <= mem_raddr + 1;
                rp <= rp + 1;
            end
        end
    end

    assign dout = mem[mem_raddr];

    assign empty = (wp == rp) ? 1'b1 : 1'b0;
    assign almost_empty_node = (wp == rp+plus_1) ? 1'b1 : 1'b0;
    assign almost_empty = empty | almost_empty_node;
endmodule

`default_nettype wire


インスタンスする際にメモリ容量 C_MEMORY_SIZE はカウンタが回る都合上、2のn乗である必要がある。

次にテストベンチ (sync_fifo_tb.v) を下に貼っておく。

`default_nettype none
`timescale 100ps / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: marsee
//
// Create Date:   11:52:36 11/08/2013
// Design Name:   sync_fifo
// Module Name:   D:/HDL/FndISEWork/Zynq-7000/ZedBoard/test/sync_fifo/sync_fifo_tb.v
// Project Name:  sync_fifo
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: sync_fifo
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module sync_fifo_tb;

    parameter DELAY = 10;

    // Inputs
    wire clk;
    wire rst;
    reg wr_en;
    reg [31:0] din;
    reg rd_en;

    // Outputs
    wire full;
    wire almost_full;
    wire [31:0] dout;
    wire empty;
    wire almost_empty;

    // Instantiate the Unit Under Test (UUT)
    sync_fifo #(
        .C_MEMORY_SIZE(16),
        .DATA_BUS_WIDTH(32)
    ) uut (
        .clk(clk), 
        .rst(rst), 
        .wr_en(wr_en), 
        .din(din), 
        .full(full), 
        .almost_full(almost_full), 
        .rd_en(rd_en), 
        .dout(dout), 
        .empty(empty), 
        .almost_empty(almost_empty)
    );

    initial begin
        // Initialize Inputs
        wr_en = 0;
        din = 0;
        rd_en = 0;

        // Wait 100 ns for global reset to finish
        #1000;
        
        // Add stimulus here
        fifo_write(32'h11223340, 15);
        fifo_read(15);
        fifo_write(32'h11223340, 4);
        fifo_read(4);    
    end

    // Write Task
    task fifo_write;
        input     [31:0]    din_temp;
        input    [31:0]    wr_cnt;
        integer i;
        begin
            @(posedge clk);
            #DELAY;
            wr_en = 1'b1;
            for (i=0; i<wr_cnt; i=i+1) begin
                din = din_temp;
                @(posedge clk);
                #DELAY;
                din_temp = din_temp + 1;
            end
            wr_en = 1'b0;
        end
    endtask

    // Read Task
    task fifo_read;
        input [31:0] rd_cnt;
        integer i;
        begin
            @(posedge clk);
            #DELAY;
            rd_en = 1'b1;
            for (i=0; i<rd_cnt; i=i+1) begin
                @(posedge clk);
                #DELAY;
            end
            rd_en = 1'b0;
        end
    endtask


    // clk_gen のインスタンス(clk)
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) CLKi (
        .clk_out(clk)
    );

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(rst),
        .init_done()
    );
      
endmodule


module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out,
    output    reg        init_done
);
    begin
        initial begin
            reset_out = RESET_STATE;
            init_done = 1'b0;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
            init_done = 1'b1;
        end
    end
endmodule

`default_nettype wire

  1. 2013年11月09日 04:14 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0