Search code examples
for-loopverilogfpgahdlgenerate

Is it synthesizable, using integer variable for the for-loop within a generate block in a always block?


In the code below, the line: mem_reg[wr_cmd_addr[SEG_ADDR_WIDTH*n +: INT_ADDR_WIDTH]][i*8 +: 8] <= wr_cmd_data[SEG_DATA_WIDTH*n+i*8 +: 8];

The index "i" is an integer type. It is being synthesized right??

I was under the impression that integer variables are only used for simulations in the initial procedural block

Also, the BRAM reg [SEG_DATA_WIDTH-1:0] mem_reg[2**INT_ADDR_WIDTH-1:0]; is being synthesized the number of times the genvar variable "n" loops in the for loop? The multiple generated BRAMs mem_reg will have the same names? And they cannot be accessed separately by name with something like: mem_reg[n] right?

`resetall
`timescale 1ns / 1ps
`default_nettype none

/*
 * DMA parallel simple dual port RAM
 */
module dma_psdpram #
(
    // RAM size
    parameter SIZE = 4096,
    // RAM segment count
    parameter SEG_COUNT = 2,
    // RAM segment data width
    parameter SEG_DATA_WIDTH = 128,
    // RAM segment byte enable width
    parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8,
    // RAM segment address width
    parameter SEG_ADDR_WIDTH = $clog2(SIZE/(SEG_COUNT*SEG_BE_WIDTH)),
    // Read data output pipeline stages
    parameter PIPELINE = 2
)
(
    input  wire                                clk,
    input  wire                                rst,

    /*
     * Write port
     */
    input  wire [SEG_COUNT*SEG_BE_WIDTH-1:0]   wr_cmd_be,
    input  wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] wr_cmd_addr,
    input  wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] wr_cmd_data,
    input  wire [SEG_COUNT-1:0]                wr_cmd_valid,
    output wire [SEG_COUNT-1:0]                wr_cmd_ready,
    output wire [SEG_COUNT-1:0]                wr_done,

    /*
     * Read port
     */
    input  wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] rd_cmd_addr,
    input  wire [SEG_COUNT-1:0]                rd_cmd_valid,
    output wire [SEG_COUNT-1:0]                rd_cmd_ready,
    output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] rd_resp_data,
    output wire [SEG_COUNT-1:0]                rd_resp_valid,
    input  wire [SEG_COUNT-1:0]                rd_resp_ready
);

parameter INT_ADDR_WIDTH = $clog2(SIZE/(SEG_COUNT*SEG_BE_WIDTH));

// check configuration
initial begin
    if (SEG_ADDR_WIDTH < INT_ADDR_WIDTH) begin
        $error("Error: SEG_ADDR_WIDTH not sufficient for requested size (min %d for size %d) (instance %m)", INT_ADDR_WIDTH, SIZE);
        $finish;
    end
end

generate

genvar n;

for (n = 0; n < SEG_COUNT; n = n + 1) begin

    (* ramstyle = "no_rw_check" *)
    reg [SEG_DATA_WIDTH-1:0] mem_reg[2**INT_ADDR_WIDTH-1:0];

    reg wr_done_reg = 1'b0;

    reg [PIPELINE-1:0] rd_resp_valid_pipe_reg = 0;
    reg [SEG_DATA_WIDTH-1:0] rd_resp_data_pipe_reg[PIPELINE-1:0];

    integer i, j;

    initial begin
        // two nested loops for smaller number of iterations per loop
        // workaround for synthesizer complaints about large loop counts
        for (i = 0; i < 2**INT_ADDR_WIDTH; i = i + 2**(INT_ADDR_WIDTH/2)) begin
            for (j = i; j < i + 2**(INT_ADDR_WIDTH/2); j = j + 1) begin
                mem_reg[j] = 0;
            end
        end

        for (i = 0; i < PIPELINE; i = i + 1) begin
            rd_resp_data_pipe_reg[i] = 0;
        end
    end

    always @(posedge clk) begin
        wr_done_reg <= 1'b0;

        for (i = 0; i < SEG_BE_WIDTH; i = i + 1) begin
            if (wr_cmd_valid[n] && wr_cmd_be[n*SEG_BE_WIDTH+i]) begin
                mem_reg[wr_cmd_addr[SEG_ADDR_WIDTH*n +: INT_ADDR_WIDTH]][i*8 +: 8] <= wr_cmd_data[SEG_DATA_WIDTH*n+i*8 +: 8];
                wr_done_reg <= 1'b1;
            end
        end

        if (rst) begin
            wr_done_reg <= 1'b0;
        end
    end

    assign wr_cmd_ready[n] = 1'b1;
    assign wr_done[n] = wr_done_reg;

    always @(posedge clk) begin
        if (rd_resp_ready[n]) begin
            rd_resp_valid_pipe_reg[PIPELINE-1] <= 1'b0;
        end

        for (j = PIPELINE-1; j > 0; j = j - 1) begin
            if (rd_resp_ready[n] || ((~rd_resp_valid_pipe_reg) >> j)) begin
                rd_resp_valid_pipe_reg[j] <= rd_resp_valid_pipe_reg[j-1];
                rd_resp_data_pipe_reg[j] <= rd_resp_data_pipe_reg[j-1];
                rd_resp_valid_pipe_reg[j-1] <= 1'b0;
            end
        end

        if (rd_cmd_valid[n] && rd_cmd_ready[n]) begin
            rd_resp_valid_pipe_reg[0] <= 1'b1;
            rd_resp_data_pipe_reg[0] <= mem_reg[rd_cmd_addr[SEG_ADDR_WIDTH*n +: INT_ADDR_WIDTH]];
        end

        if (rst) begin
            rd_resp_valid_pipe_reg <= 0;
        end
    end

    assign rd_cmd_ready[n] = rd_resp_ready[n] || ~rd_resp_valid_pipe_reg;

    assign rd_resp_valid[n] = rd_resp_valid_pipe_reg[PIPELINE-1];
    assign rd_resp_data[SEG_DATA_WIDTH*n +: SEG_DATA_WIDTH] = rd_resp_data_pipe_reg[PIPELINE-1];
end

endgenerate

endmodule

`resetall

Solution

  • try to use named blocks:

    for (n = 0; n < SEG_COUNT; n = n + 1) begin : blkname
      reg [SEG_DATA_WIDTH-1:0] mem_reg [2**INT_ADDR_WIDTH-1:0];
    end
    

    And access as:

    assign x = blkname[i].mem_reg[j];