Forum Discussion
ak6dn
Regular Contributor
3 years agoHere is my setup on a Terasic DE1 board, which uses a CycloneII FPGA and an attached 10ns async SRAM device.
There is a 50MHz (20ns) clock input that is transformed thru a PLL to an 80MHz (12.5ns) clock for the logic.
I can perform memory tests and read and write this SRAM continuously (for days on end...) with no errors occurring.
FYI the SRAM is used as the main memory for an FPGA PDP-8 implementation
# Input 50MHz reference clock create_clock -period 20.0 -name CLOCK_50 [get_ports {CLOCK_50}] # Created clocks based on PLLs (CPUCLK = 80MHz) create_generated_clock -source {pll|altpll_component|pll|inclk[0]} -divide_by 5 -multiply_by 8 -duty_cycle 50 -name CPUCLK {pll|altpll_component|pll|clk[0]} ### external async SRAM timing ### # address/control outputs set_output_delay -clock CPUCLK -clock_fall -max 4.0 [get_ports {SRAM_*_L SRAM_A[*]}] set_output_delay -clock CPUCLK -clock_fall -min 0.5 [get_ports {SRAM_*_L SRAM_A[*]}] # write data outputs set_output_delay -clock CPUCLK -max 3.0 [get_ports {SRAM_DQ[*]}] set_output_delay -clock CPUCLK -min 0.5 [get_ports {SRAM_DQ[*]}] set_multicycle_path -rise_from CPUCLK -to [get_ports {SRAM_DQ[*]}] -setup 2 set_multicycle_path -rise_from CPUCLK -to [get_ports {SRAM_DQ[*]}] -hold 2 # read data inputs set_input_delay -clock CPUCLK -max 10.0 [get_ports {SRAM_DQ[*]}] set_input_delay -clock CPUCLK -min 3.0 [get_ports {SRAM_DQ[*]}] set_multicycle_path -from [get_ports {SRAM_DQ[*]}] -rise_to CPUCLK -setup 2 set_multicycle_path -from [get_ports {SRAM_DQ[*]}] -rise_to CPUCLK -hold 2
And for reference here is the verilog implementation it references...
module mm8e_memory #( // external parameters parameter TPD = 0, // simulation delay parameter INTBANKS = 2, // memory size, 4K banks (internal memory) parameter EXTBANKS = 6 // memory size, 4K banks (external memory) ) ( // port definitions input wire clk, // system clock input wire reset, // system reset input wire init, // bus init input wire mr, // memory read input wire mw, // memory write input wire [0:2] ema, // extended memory address input wire [0:11] ma, // memory address inout wire [0:11] md, // memory data in/out output reg [14:0] ext_addr, // external memory address output reg ext_we_l, // external memory write enable output reg ext_ce_l, // external memory select output reg ext_oe_l, // external memory read enable inout wire [11:0] ext_dq // external memory data in/out ); // internal parameters localparam MEMSIZE = 4096*INTBANKS; // internal memory size // local signals wire [14:0] addr = {ema[0:2],ma[0:11]}; // full memory address reg [0:11] memory [0:MEMSIZE-1] /* synthesis ramstyle = "no_rw_check" */; reg [0:11] mdo; wire [0:11] mdi = md; reg mrd; wire enb_int = (INTBANKS > 0) && (ema <= INTBANKS-1); wire enb_ext = (EXTBANKS > 0) && (ema >= INTBANKS) && (ema <= INTBANKS+EXTBANKS-1); // internal memory initial $readmemb("meminit.txt", memory, 0, MEMSIZE-1); always @(posedge clk) mrd <= #TPD mr & enb_int; wire mwr = mw & enb_int; always @(posedge clk) begin if (mwr) memory[addr] <= #TPD mdi; mdo <= #TPD memory[addr]; end assign md = mr & mrd ? mdo : {12{1'bz}}; // external memory always @(negedge clk) begin ext_addr <= #TPD addr; ext_ce_l <= #TPD 1'b0; ext_we_l <= #TPD ~( mw & ~mr) | ~ext_we_l; ext_oe_l <= #TPD ~(~mw & mr); end assign ext_dq = mw & ~mr ? mdi : {12{1'bz}}; assign md = mr & ~mw & enb_ext ? ext_dq : {12{1'bz}}; endmodule // mm8e_memory