Forum Discussion

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

Off by 1 error with Altera SRAM controller IP?

I have a Nios II system running on a DE2-115 that is connected to both the DRAM and SRAM on board. I have written a VGA driver that uses the avalon bus to fetch pixel data from either DRAM or SRAM, depending on the address stored in a register. The system works flawlessly when reading from DRAM. However, when I change the address to point at the SRAM chip instead, the output on the monitor shifts right by 1 pixel. 1 Pixel is 16 bits, the word size of the SRAM chip.

Memory is written by the Nios II core and read by this simple module I wrote:

module vga_dram_master(
    //clk domain
        input clk,
        input resetn,
        input start,
        input read_from_addr,
        
        //avalon master for accessing SDRAM
        output master_address, 
        output master_read, 
        input master_read_data, 
        input master_wait_request,
        input master_read_data_valid,
    
    //VGA_CLK domain
        input VGA_CLK,
        input read_pixel,
        output pixel_out
);    
    //draw green on the screen if the fifo bottoms out
    wire data_out;
    assign pixel_out = rdempty ? 16'h03E0 : data_out;
    
    //fifo to buffer data and bridge clock domains
    wire rdempty, wrfull;
    wire wrusedw;
    vga_pixel F1(!resetn | start, master_read_data, VGA_CLK, read_pixel, clk, master_read_data_valid, data_out, rdempty, wrfull, wrusedw);
    
    //pipelined access 
    assign master_address = currAddress; 
    assign master_read = !wrusedw && (wordsRead < 19'd480000) && (currAddress != 0);
    
    reg currAddress;
    reg wordsRead;
    always @ (posedge clk or negedge resetn) begin
        if(!resetn) begin
            currAddress <= 32'd0;
            wordsRead <= 19'd0;
        end else begin
            if (start) begin
                currAddress <= read_from_addr;
                wordsRead <= 19'd0;
            end else if (!master_wait_request && master_read) begin
                wordsRead <= wordsRead + 1'd1;
                currAddress <= currAddress + 2'd2;
            end
        end
    end
endmodule

I have poured over this code looking for mistakes, but I haven't found any. And, everything works perfectly when accessing DRAM! Here is a simple program I wrote for the Nios Core to demonstrate the problem. I can flick the switch back and forth and see the image on monitor move by 1 pixel.


int main(void) {        
    uint16_t *SRAM = (uint16_t*)0x20000000;
    uint16_t *DRAM = (uint16_t*)0x08000000;
    VGA = 0; //disable the screen for a second
    //draw the same gradient background into both buffers
    gradient(SRAM);
    gradient(DRAM);
    flush_dcache();
    
    //this commented line fixes the issue. But why is it happening in the first place!!!?!?
    //SRAM++; 
    uint16_t* buff = {SRAM , DRAM }; 
    while(1) {
        //wait for screen refresh
        while(!VGA);
        //read from DRAM or SRAM based on 0th switch
        VGA = (uint32_t)buff & 1];
    }
    return 0;
}

Really unsure what is happening here.

3 Replies

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

    Have you tried memory testing the SRAM using Nios by itself? It almost sounds like the off-chip timings are incorrect and you are reading data too late.

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

    --- Quote Start ---

    Have you tried memory testing the SRAM using Nios by itself? It almost sounds like the off-chip timings are incorrect and you are reading data too late.

    --- Quote End ---

    Talking about timings made me check my timequest settings, they were wrong. After fixing them, I'm now failing timing with a Recovery path between read_from_addr on the clk domain and the output of the VGA pixel fifo (from the verilog in the first post)

    slack: -2.156 ns

    from_node: nios2:u0|VGA_Controller:vga_controller|fbAddr[7]

    to_node: nios2:u0|VGA_Controller:vga_controller|VGA_driver:VGA0|vga_dram_master:DM0|vga_pixel:F1|dcfifo_mixed_widths:dcfifo_mixed_widths_component|dcfifo_c1l1:auto_generated|altsyncram_mv61:fifo_ram|q_b[0]

    launch clock: P0|altpll_component|auto_generated|pll1|clk[0] (my 90 MHz system clock)

    latch clock: P0|altpll_component|auto_generated|pll1|clk[2] (the VGA pixel clock - 40 MHz)

    I don't really understand what is happening here - I have the register 'currAddress' in between the input 'read_from_addr' (which is driven by fbAddr). So why is timing failing on this path, when it shouldn't even exist? Shouldn't there be a node 'currAddress' in the middle of that path?

    The SRAM issue is probably a symptom of this problem - when I was running the FPGA again today, the offset was different.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    What does the full path show in Timequest? It should show you all the hops between the from_node and the to_node. A recovery timing failure means that an asynchronous signal is arriving too late to the latch destination. The full path might show why the launching address has an asynchronous path to the output of the FIFO RAM. I suspect the full path will look something like this: fbAddr[7] --> Qsys decode logic --> rdreq of DCFIFO --> q_b[0] but I'm not sure why DCFIFO would have an asynchronous load/set/reset in this case dependent on the read request coming at it though.

    Did you recompile the design after making the timing changes? If not I would close the Quartus project, delete the /db directories, then recompile because it could be that the fitter spent so much time fixing a setup issue that it introduced recovery failures and re-running the fitter might fix that.