SRAM Controller Timing

20 views
Skip to first unread message

Rob Holmes

unread,
Dec 19, 2025, 2:17:51 PM (3 days ago) Dec 19
to FPGAwars: explorando el lado libre
Hi all,

I have a very simple SRAM controller running in my project, which looks like the code at the end. Works fine, but i want to understand its limitations and wanted a sense check on my thoughts.

The SRAM it is connected to is the IS61WV25616BLL-10BLI which according to the datasheet has a 10ns access time.

So.... because i am holding the write pulse for half a clock cycle with the line
sram_we = (~wr_en)|sys_clk;

So half a clock cycle has to be > 10ns, which means the max clock period i can run this at is 20ns?

Which means that a 50Mhz clock would be right on the limit and anything below that is pretty safe to use?

Fully prepared to be way off the mark, but i think this makes sense.



reg [11:0] dataOutReg;

assign sram_addr = wr_en ?  write_addr : read_addr;

assign sram_io = wr_en ? data_in : 12'bzzzzzzzzzzzz;

assign sram_we = (~wr_en)|sys_clk;

always @(posedge sys_clk) begin
  if (wr_en == 1'b0)
     dataOutReg <= sram_io;
end
 
assign data_out = dataOutReg;


charli va

unread,
Dec 19, 2025, 3:03:01 PM (3 days ago) Dec 19
to fpga-wars-explora...@googlegroups.com
Hi Rob!! i think 


--
Has recibido este mensaje porque estás suscrito al grupo "FPGAwars: explorando el lado libre" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a fpga-wars-explorando-el...@googlegroups.com.
Para ver este debate, visita https://groups.google.com/d/msgid/fpga-wars-explorando-el-lado-libre/addd543d-718c-49f9-979c-252fe694a1c1n%40googlegroups.com.

charli va

unread,
Dec 19, 2025, 3:11:18 PM (3 days ago) Dec 19
to fpga-wars-explora...@googlegroups.com
Sorry, I sent the email while I was writing!

I think you have a minor conceptual error, but overall you're on the right track.

First, regarding the speed, you're right. 50 MHz is safe because the low 10 ns pulse meets the minimum 8 ns requirement for the write pulse (I skimmed the chip's technical specifications, but I think your margins are correct).
The real problem, I believe, is that your code updates the address on the exact same clock edge that ends the write pulse (posedge). This creates a hold-time violation (race condition). If the address changes a fraction of a nanosecond before the write signal is deactivated, you corrupt the memory. Keep in mind that this type of memory changes very quickly in real time, and you need to "capture" the data at the precise moment.

I think the solution might involve changing the write logic to the negative edge (negedge) . This ensures the write pulse ends safely before the direction changes.

Something like this (I haven't tested it, but I think it might help):

// 1. Make Write Enable Active (Low) when Clock is HIGH
// This centers the write pulse in the middle of the address window.
assign sram_we = ~(wr_en & sys_clk);

assign sram_addr = wr_en ? write_addr : read_addr;
assign sram_io = (wr_en && !sram_we) ? data_in : 12'bzzzzzzzzzzzz;

// 2. Update Address and Data on the NEGATIVE edge
// By the time this edge happens, 'sram_we' has already gone High (Inactive),
// guaranteeing zero hold-time violations.
always @(negedge sys_clk) begin
    if (wr_en) begin
       // Logic to advance write_addr or data_in goes here
    end
end

// 3. Read data on POSITIVE edge (Standard)
always @(posedge sys_clk) begin
    if (!wr_en) dataOutReg <= sram_io;
end


Good night and go ahead!!!

Rob Holmes

unread,
Dec 20, 2025, 4:20:28 AM (3 days ago) Dec 20
to fpga-wars-explora...@googlegroups.com
Thankyou for the reply, good to know i was at least right about the timings.

I have not experienced any issues with the current implementation yet, but that will likely be due to luck more than anything. By the time im done id really like to have the "perfect" sram controller that i can just rely on in future projects.

Took quite a few hours last night to simulate the situation you described but i managed to model it in a test. So now i have a specific test case to work against.

image.png

around t=149.5ns
sram_addr changes from 0x00200 to 0x00300  tooo early.
0.5ns later, clk goes high and sram_we_n goes high
BUT... 
But the sram_addr has already seen the new address 0x00300 during the write

Implementing your changes:

image.png

This is looking good to me.
I cant simulate data coming early because its now latched on the negative edge outside the controller, which i guess is the point.

I left out this change

assign sram_io = (wr_en && !sram_we) ? data_in : 12'bzzzzzzzzzzzz;
as it seemed to cause issues with the hold time, but i cant be certain.

This gives me a controller once adding in the rest of the signals and some sensible renaming as

module sram #(
    parameter ADDR_WIDTH = 18,  // 256K addresses
    parameter DATA_WIDTH = 16   // 16-bit data
)(
    input wire clk,
    input wire rst_n,
   
    // User interface
    input wire [ADDR_WIDTH-1:0] wr_addr,
    input wire [ADDR_WIDTH-1:0] rd_addr,
    input wire [DATA_WIDTH-1:0] wr_data,
    input wire wr_en,
    input wire rd_en,
    input wire [1:0] byte_en,    // [1]=upper byte, [0]=lower byte
    output reg [DATA_WIDTH-1:0] rd_data,
   
    // SRAM chip interface
    output wire [ADDR_WIDTH-1:0] sram_addr,
    inout wire [DATA_WIDTH-1:0] sram_data,
    output wire sram_ce_n,       // Chip Enable (active low)
    output wire sram_oe_n,       // Output Enable (active low)
    output wire sram_we_n,       // Write Enable (active low)
    output wire sram_ub_n,       // Upper Byte Enable (active low)
    output wire sram_lb_n        // Lower Byte Enable (active low)
);

    // Set the SRAM address to either the write or read address
    assign sram_addr = wr_en ? wr_addr : rd_addr;
   
    // Set the SRAM data to wr_data if writing, otherwise tristate
    assign sram_data = wr_en ? wr_data : {DATA_WIDTH{1'bz}};
   
    // Chip enable always active
    assign sram_ce_n = 1'b0;
   
    // Output enable always active
    // This could have been: assign sram_oe_n = ~rd_en; but the tristating should cover me?
    assign sram_oe_n = 1'b0;
   
    // Write enable: low during HIGH clock phase when writing
    // When using I need to update wr_addr, wr_data, and wr_en at negedge to ensure
    // signals are stable during the write pulse (HIGH phase)
    assign sram_we_n = ~(wr_en & clk);
   
    // Byte enables (active low) i dont need these but probably should allow them.
    assign sram_ub_n = ~byte_en[1];
    assign sram_lb_n = ~byte_en[0];
   
    // Latch read data on clock edge when not writing
    always @(posedge clk) begin
        if (!wr_en)
            rd_data <= sram_data;
    end

endmodule

Ill try this out on hardware later and see what happens, i have learnt loads during the night.

Just want to say thanks for your patience and help as always.




Reply all
Reply to author
Forward
0 new messages