Forum Discussion

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

SRAM IP that work on some builds, and some it doesn't!

Hello All!

I'm having an issue implementing some VERY simple IP for an asynchronous SRAM chip (on digikey: http://www.digikey.com/product-detail/en/is61wv25616bll-10bli/706-1104-nd/1831380). It's really making me doubt myself.

It's just a dumb bit of code, that's I've used years ago on an old project (Quartus II 9.1 I think)

module sram(

// global clk/reset

clk,

reset_n,

// avalon slave

s_chipselect_n,

s_byteenable_n,

s_write_n,

s_read_n,

s_address,

s_writedata,

s_readdata,

// SRAM interface

SRAM_DQ,

SRAM_ADDR,

SRAM_UB_n,

SRAM_LB_n,

SRAM_WE_n,

SRAM_CE_n,

SRAM_OE_n

);

parameter DATA_BITS = 16;

parameter ADDR_BITS = 18;

input clk;

input reset_n;

input s_chipselect_n;

input [(DATA_BITS/8-1):0] s_byteenable_n;

input s_write_n;

input s_read_n;

input [(ADDR_BITS-1):0] s_address;

input [(DATA_BITS-1):0] s_writedata;

output [(DATA_BITS-1):0] s_readdata;

output SRAM_CE_n;

output SRAM_OE_n;

output SRAM_LB_n;

output SRAM_UB_n;

output SRAM_WE_n;

output [(ADDR_BITS-1):0] SRAM_ADDR;

inout [(DATA_BITS-1):0] SRAM_DQ;

assign SRAM_DQ = SRAM_WE_n ? 'hz : s_writedata;

assign s_readdata = SRAM_DQ;

assign SRAM_ADDR = s_address;

assign SRAM_WE_n = s_write_n;

assign SRAM_OE_n = s_read_n;

assign SRAM_CE_n = s_chipselect_n;

assign {SRAM_UB_n,SRAM_LB_n} = s_byteenable_n;

endmodule

[/INDENT]

That's it, just 54 lines with white space.

The issue I'm having is that:

  1. Using Quartus II 12.1 Build 177, I will build the project, and it works 100%.

  2. Then, I'll change something very very simple in the Verilog, recompile and find that my Nios2 will not run - and gives me a verify failed error:

  3. https://mail.google.com/mail/u/1/?ui=2&ik=13b831dd1d&view=att&th=13e8b81cdbc4b34e&attid=0.0.1&disp=emb&realattid=ii_13e8b7772d7b4a5d&zw&atsh=1

  4. The change can be as simple as moving a single output to a different pin on the device, and then nios2 just won't execute for SRAM verify failure. I'm losing my cool by this point.

  5. Now, if I go back to Quartus and just hit *compile* again, without making any changes at all, the new SOF works fine, Nios2 verifies and runs from SRAM... really lost my cool now.

If Quartus uses the same fitter seed each time where's the random'ness come from? It should compile exactly the same sof each time... but I'm getting different results.

The SRAM doesn't have any electrical faults - it's highly reliable when Quartus builds it right. It's fast enough to read single cycle (10ns cycle) on a Nios2/e running at 50MHz. I have two working tested boards that I'm testing on, both give me identical results. I've sprayed them with cold spray to test for bad soldering, damage to the devices from reflow, etc. All Quartus settings are default, and I have rebuilt my project from scratch many times - I use Quartus shell and a TCL script to load assigmnents for pins and IO voltage - nothing else is changed.

Is this my fault or is Quartus screwing me over here... ? I'm downloading 13.0 to see if it does the same. I'm not great with TimeQuest, but I can't see any warnings, all my slack is +ve as it's a simple design. There are NO critical warnings in the whole compilation.

Please critique my code and tell me if it's my fault - am I having asynchronous issues? Should I register the signals in and out? It's an asychronous RAM with 10ns performance, it shouldn't be necessary right? The avalon bus will make all synchronous when it samples?

Please help... (whimpering noise)...

9 Replies

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

    Yes, you're having timing issues. I'm actually amazed that design works at all...

    Asynchronous SRAMs have a number of timing relationships that you need to take care of. The timings of the write sequence are particularly tricky.

    Simply connecting the async SRAM signals to the Avalon bus won't work.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    It probably only works at all because there is a bus width adapter just before it that is likely to sanitise the write signals.

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

    Thanks Guys,

    To take care of the timing sequences, do i just need to constrain the device IO in an SDC file? Is that the best way to go about it?

    My understanding was that the aSRAM would latch the data out upon presentation of WE_n, CS_n and ADDR. The timing of this device is 4.5ns setup for a read, with 0ns hold requirement. The avalon bus would then sample the DATA on clock rising, job done...
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    The write enable signal (from the Avalon faric) is only valid on the relevant clock edge (and probably qualified by some other signals).

    It could quite possibly be asserted (for short times) at any other time - which will generate random writes to your SRAM.

    So while you can do completely async reads, you have to synchronise the writes to the avalon clock.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks DSL,

    I've tried that, with this code, which has the same behaviour:

    
    module sram
    (
    	//	global clk/reset
    	input wire 		clk,
    	input wire 		reset_n,
    	// avalon slave
    	input wire									s_chipselect_n,
    	input wire 		 	s_byteenable_n,
    	input wire									s_write_n,
    	input wire 									s_read_n,
    	input wire		 	s_address,
    	input wire		 	s_writedata,
    	output wire 	 	s_readdata,
    	
    	// SRAM interface
    	inout wire		 	SRAM_DQ,
    	output reg		 	SRAM_ADDR,
    	output reg 		SRAM_UB_n,
    	output reg 		SRAM_LB_n,
    	output reg 		SRAM_WE_n,
    	output reg 		SRAM_CE_n,
    	output reg 		SRAM_OE_n
    );
    	parameter DATA_BITS		= 16;
    	parameter ADDR_BITS		= 18;
    	// internal registers
    	reg writedata_reg;
    	// combinatorial outputs
    	assign	SRAM_DQ 			=	SRAM_WE_n ? 'hz : writedata_reg;
    	assign	s_readdata			=	SRAM_DQ;
    	
    	// synchronous registers
    	always @ ( posedge clk )
    	begin
    		if ( s_chipselect_n == 0 ) // if this component selected
    		begin
    			writedata_reg 				<= s_writedata; 	// register the write data	off the bus		
    			SRAM_ADDR				<=	s_address;		// clock through other outputs to sram
    			SRAM_WE_n				<=	s_write_n;
    			SRAM_OE_n					<=	s_read_n;
    			SRAM_CE_n					<=	s_chipselect_n;
    			{ SRAM_UB_n,SRAM_LB_n }		<=	s_byteenable_n;
    		end
    	end
    endmodule
    

    What SignalTap is tell me, is that there is no chip select, no write and no read signal. So the bus is trying to read from the SRAM address space, but the control signals are not being asserted... In Nios I'm looping a memory test over the SRAM space. Could this be a problem on the avalon bus side of things? Is my code being optimised out for some reason?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Also, note that these pins aren't shared. There's no flash or anything - the interface just belongs to the SRAM IC.

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

    Well... in your most recent paste of code, your "if ( s_chipselect_n == 0 )" doesn't have any "else" to it, so SRAM_CE_n in particular is going to be stuck low the first time the if() gets hit.

    Taking a step back:

    In your original code and statement of problem, ignoring the correctness of your approach, if it works in one compile and doesn't work in another compile, this is a somewhat typical symptom of bad / insufficient timing constraints. You should fix that first.

    As far as your direct attempt at simply mapping SRAM pins to Avalon pins, you can probably get something workable that way using the _hw.tcl parameters to control the strobes if necessary. But I think the "Generic Tri-State Controller" component in Qsys is possibly the off-the-shelf component you are seeking. It has presets for several NOR flash and SRAM chips, and I don't think your chip can be too different.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks Ted,

    I've fixed the ( CS == 0 ) issue. :-)

    I'm hoping to fix this in TimeQuest with constraints. I've added below to my sdc, to account for SRAM time to clock out (tCO) and board delay (BDa):

    set_output_delay -add_delay -clock "clk_50" -max [expr $local_osc_period - $SRAM_tCO_max - $SRAM_BDa_max] [get_ports {sram_*}]

    Where

    [LIST]

    local_osc_period = 20

    SRAM_tCO_max = 10.5

    SRAM_BDa = 0.15

    [/LIST]

    My understanding is that this would ensure the fitter places the design so the propagation delay from FPGA -> Pin is less than 20-10.5-.15 = 9.35ns. That way I can keep the memory single cycle access; I don't want latency, so don't want to over-register everything. It works right now, but I'm going to need to test and look closer to ensure it's a solution, not just luck of the fitter.

    Can you advise what other constraints I should add?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    No, you got that reverse.

    You're telling the fitter it has 9.35 of maximum external delay, so in this case it will try to keep the internal FPGA delays it under 10.65 ns.

    Note that with aSRAM, the write sequence generaly needs to ensure you setup valid address and data a bit before you activate the write enable and you hold those valid address and data for a bit after you've disabled the write enable.

    Otherwise, you risk writing random stuff into random addresses.

    You need a small state machine to ensure that...