0
\$\begingroup\$

I'm trying to implement a buffer for an image processing pipeline and need to load data into BRAM.

I've been following an online tutorial (https://www.youtube.com/watch?v=n35zS__YEFQ) for implementing different parts of the system, but after synthesis and implementation, I can see that my code is using LUTRAM instead of BRAM. I have tried looking into the language templates provided by vivado for inferring single port BRAM and to me the code looks very similar, but I'm new to FPGAs and don't know much, so I'm not entirely sure how I can force BRAM to be inferred instead of LUTRAM.

here is my verilog code:

module buffer #(parameter NUM_ELEMENTS=10, ADDRESS_BITS=4, WIDTH=8) (
    input clk,
    input rst,
    input [WIDTH-1:0] dataIn,
    input validInput,
    input readReady,
    output wire [WIDTH-1:0] dataOut
    );
    
    reg [WIDTH-1:0] mem [NUM_ELEMENTS-1:0];
    reg [ADDRESS_BITS-1:0] wrPtr;
    reg [ADDRESS_BITS-1:0] rdPtr;
    
    always @ (posedge clk) begin
        if (rst) begin
            wrPtr <= 0;
        end else if (validInput) begin
            mem[wrPtr] <= dataIn;
            wrPtr <= wrPtr + 1;
        end
    end
    
    assign dataOut = mem[rdPtr];
    
    always @ (negedge clk) begin
        if (rst) begin
            rdPtr <= 0;
        end else if (readReady) begin   
            rdPtr <= rdPtr + 1;
        end
    end
    
endmodule

simulating the module shows that it is working correctly as expected (I need to take care of some edge cases where the read pointer goes past the end of the buffer, more on that later).

my test bench code is:

`timescale 1ns / 1ps

module buffer_tb();
    reg clk;
    reg rst;
    reg [buffer.WIDTH-1:0] dataIn;
    reg validInput;
    reg readReady;
    wire [buffer.WIDTH-1:0] dataOut;
    
    integer i;
    
    buffer dut (clk,rst,dataIn,validInput,readReady,dataOut);
    
    initial clk = 1;
    always #5 clk = ~clk;
    
    initial begin
        rst = 1;
        dataIn = 0;
        validInput = 0;
        readReady = 0;
        #10;
        rst = 0;
        validInput = 1;
        for (i=0; i<10; i = i+1) begin
            dataIn = 2*i;
            #10;
        end
        validInput = 0;
        #40;
        readReady = 1;
        #100;
        readReady = 0;
        $stop;
    end
    
endmodule

and the output of the simulation is: enter image description here

but opening project summary or the elaborated design shows the incorrect use of LUTRAM:

enter image description here

I emphasize again that I'm a total beginner and just started using FPGAs; After searching a bit on xilinx's guides, I still can't find any way of specifying the type of RAM, only that it can be inferred when using a very specific syntax, which seems odd and I haven't managed to make it work.

My main questions are:

  1. how can you infer BRAM instead of LUT? do I have to copy the suggested example code line by line or are there changes permitted?
  2. if I have to copy the template codes directly, would it better to use the IP core generator to instantiate BRAM in the block design and write a controller for it (the buffer module acting as a wrapper), or whether to use the actual code template? to be honest I don't even know what the differences are. as I understand it, the block design gets converted to verilog anyways.
  3. I only need to write to buffers till they are full, then read from them until they are empty. Should I be using a FIFO instead of trying to implement my own buffer code? The way I understood the online tutorial for image processing, I can create more buffers than required and fill them while processing other portions of the data, then multiplex the buffers, but I'm not sure what the advantages and downsides of a FIFO block are, compared to a hand written buffer.
\$\endgroup\$
3
  • \$\begingroup\$ Before that did u calculate how much bram is needed for a frame buffer and how much is available. A typical 7 series low end xilinx FPGA doesn't have enough bram to incorporate say 640x480 VGA frame. \$\endgroup\$ – Mitu Raj May 30 at 15:40
  • \$\begingroup\$ Yes, I will be periodically filling the buffer with data from QSPI modules which give me enough capacity for all the data input. the actual image will be loaded via DMA from a ZYNQ processor but that's way in the future. I can't even get the buffer itself to work how I want it :( \$\endgroup\$ – OM222O May 30 at 15:49
  • \$\begingroup\$ DMA+DDR+Processor is how usually video engines are implemented on an FPGA. BRAMs can be used to implement line buffers or FIFOs on video/image processing pipeline. But they are not big enough in most FPGAs for frame buffers especially if at least double buffering is needed. And yes, you have to follow coding guidelines and attributes to properly infer the BRAM using RTL. \$\endgroup\$ – Mitu Raj May 30 at 16:03
1
\$\begingroup\$

If you only need a small buffer (<16 data entries), there's absolutely no reason to prefer BRAM over LUTRAM.

BRAMs are located in specific areas of the chip, which may mean that relatively long paths are required to get to them from your other logic. LUTRAM can always be located close to the logic that uses it.

On the other hand, larger buffers (>64 data entries) will almost always infer BRAM instead of LUTRAM. There are also synthesis directives that can force the choice one way or the other. See the documentation for the tools you're using.

And yes, FIFOs are much more commonly used in video processing. I have written generic modules for both single-clock (synchronous) and dual-clock (asynchronous) FIFOs that I use all of the time in my video pipeline designs. I allow the synthesis tools to infer the appropriate type of RAM for the specified FIFO size. If you need the absolute maximum performance, use the vendor's IP generator, which will take full advantage of any specialized support logic that is on the chip.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ (* ram_style = "block" *) is the directive in Verilog. \$\endgroup\$ – Mitu Raj May 30 at 16:11
  • \$\begingroup\$ unfortunately even using the directive and increasing the number of elements to 1024 still doesn't result in using BRAMs :( I will go back and start modifying the provided examples line by line to see where my mistake is, but having to run synthesis every time to double check is a bit time consuming. \$\endgroup\$ – OM222O May 30 at 16:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.