Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
13 years ago

Circular Buffer Implementation

Good morning. Am trying to implement a circular buffer to write to external SRAM. I am using two always constructs. The first determines that something should be written (there are multiple potential writes in this function) and the second actually performs the write. However, I do not see the writes occurring.

First Q: Have changed between blocking v. non-blocking with no change in performance (The writes never occur) Second Q: As an old SW guy, if I create a 256 elemnt buffer with 8 bit wr and rd indexes, the indexes automatically wrap from 255 to 0 when I increment. Is this the same behavior in Verliog?

Example code (with unimportant stuff omitted):

reg [19:0] sram_buffer [7:0]

reg [7:0]sram_buffer_rd_index;

reg [7:0]sram_buffer_wr_index;

always @(posedge clk or negedge reset_n)

begin

if (sram_buffer_rd_index != sram_buffer_wr_index)

begin

decision[sram_buffer[sram_buffer_rd_index]] <= 1;

sram_buffer_rd_index = sram_buffer_rd_index + 1;

end

end

always @(posedge clk or negedge reset_n)

begin

/* Unimportant Stuff ommitted */

sram_buffer[sram_buffer_wr_index] = data[0];

// Increment the decision coverage write SM write counter

sram_buffer_wr_index = sram_buffer_wr_index + 1;

/* Unimportant Stuff ommitted */

sram_buffer[sram_buffer_wr_index] = data[1];

// Increment the decision coverage write SM write counter

sram_buffer_wr_index = sram_buffer_wr_index + 1;

/* Unimportant Stuff ommitted */

sram_buffer[sram_buffer_wr_index] = data[2];

// Increment the decision coverage write SM write counter

sram_buffer_wr_index = sram_buffer_wr_index + 1;

end

7 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I have some questions for you. Where is your external SRAM that you were talking about? sram_buffer in your code are going to be implemented as registers or internal ram depending on how you access it. Do you want a 256 byte memory? You just have a 20 byte memory (20x8 bits) in sram_buffer. Also doing three writes and additions within the same clock cycle (second always) is a bit much for a regular ram. One of the hardest things to do coming from a software background going to HDL/FPGA is to be able to envision what hardware your code turns into.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi, lethenstrom.. After I posted, I almost wrote a clarification note, but held off. the sram_buffer name and the external ram comment are misleading. External SRAM is used. But it is inapplicable to my little problem here. You are correct, I am not writing to SRAM. The intent was to have a 256x20bit circular buffer. I already see a mistake based on your comments. I believe it should look something like:

    reg [19:0] sram_buffer [255:0]

    My understanding (although it may be wrong) is that the [19:0] specifies the "width" of an element and the [255:0] specifies the number of elements.

    The intent was to let quartus define it as either set of registers that are used as addresses into the decision array (BTW decision is defined as a 1 bit by 0x80000 array and is defined as memory).

    Thanks for the input. I look forward to you further guidance....
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Yes you are correct that

    --- Quote Start ---

    reg [19:0] sram_buffer [255:0]

    --- Quote End ---

    will be 256 20-bit words. I'm still not quite with you on what you are trying to do. When it comes to memories, if you want Quartus to infer memories you should take a look at this document http://www.altera.com/literature/hb/qts/qts_qii51007.pdf page 11-13 and on describes on how to infer memories. You can also instantiate memories explicitly in your code. To explicitly add a memory use the Megawizard tool under the tools menu in Quartus.

    /Boris
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Sorry, still learning the lingo....

    What I have is multiple locations in a state machine that could write to multiple elemnts of the decision array. I struggled with the inferred RAM a few weeks ago and although I understand the basics, I don't think I have a full appreciation for the paradigm. Besides my custom code, the decision array is accessed by a NIOS CPU. I believe that the Altera MM Slave architecture used to access it from the NIOS infers it as RAM (Thats what the syntehesizer says).

    When I tried to simply write to the decision array from more than 1 location in the state machine, the compiler and assembler runs out of memory (using web edition). Therefore, I created the circular buffer which appears to compile and synthesize. It also worked sporadically.

    You comment about trying to do 3 writes and additions prompted me define a SM that divides these up. Any explanations you could provide regarding inferred ram would be greatly appreciated.

    ME
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    BTW, this little adventure in Verilog has really given me an appreciation for you pesky FPGA guys 8>). My background is many years (more than I care to admit) of embedded HW (board layout, microprocessors, etc.) and safety critical SW. But this is my first FPGA design. It has been very rewarding to learn something new. But it has also been a challenge to keep it constrained to a project schedule....

    Thanks again....
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    If you want to see a simple buffering sample, head over to the alterawiki and look for the "Modular SGDMA". In the dispatcher block I have a custom FIFO which by definition is a type of circular buffer. If it was me I would implement a controller with Avalon-MM masters on it, one to point at the head and the other points to the tail of the buffer. Then you can hook those masters up to your SRAM slave and not worry about all the interfacing stuff in between. Then your controller just has to ensure the head and tail locations never cross (might be easier with a single master interface).

    In case you are wondering why I bothered to write my own FIFO..... I needed shadow registering capabilities to write portions of the FIFO word at a time.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Quartus infers RAM when possible; that is, it uses the dedicated RAM blocks to store information, instead of using registers.

    The RAM blocks cannot be used as flexibly as registers, though. Therefore, RAM blocks can only be inferred when the code describes behavior that can actually be implemented using block RAMs.

    Below, the code for a basic dual-port, same clock, 16x1024 memory which will be inferred into RAM blocks.

    Although Quartus is smart enough to infer RAM blocks from more complicated code, when in doubt it's best to have a clear piece of code for the RAM and work from there.

    reg   myMemory  ;
    // Port A
    wire   addressA;
    wire   dataInA;
    wire  writeEnableA;
    reg    dataOutA;
    // Port B
    wire   addressB;
    wire   dataInB;
    wire writeEnableB;
    reg    dataOutB;
    always @ (posedge clk) begin
    if (writeEnableA) begin
    myMemory <= dataInA;
    dataOutA <= dataInA;
    end 
    else
    dataOutA <= myMemory;
    if (writeEnableB)  begin
    myMemory <= dataInB;
    dataOutB <= dataInB;
    end
    else
    dataOutB <= myMemory;
    end
    // Write logic needs to control address*, writeEnable* and dataIn*
    // Read logic needs to control address* and gets data from dataOut*