Papilio Plus/SRAM test 2

From Hamsterworks Wiki!

Jump to: navigation, search

This Papilio Plus project is the most complex of the test suite - it turns on one or two LEDs on the Arcade Megawing, Hopefully just LED1 when the test completes. LED2 will also light should the test fail.

Contents

Project files

It's all in File:Sram memtest2.zip

Timing requirements

Unlike Papilio Plus/SRAM test (which runs the RAM at 80Mhz) this runs the SRAM at 100MHz. It may not seem like a big difference, but when the cycle time goes from 12.5ns to 10.0ns it requires a lot of design work.

The SDRAM (an ISSI IS61WV25616ALL-10T) is a 10ns part, but as per note one of page 7 of the data sheet "when operated in the range of 3.3V +/- 5% the device meets 8ns".

Here are the important timings that have to be meet:

Parameter value
Read access time min 8ns
Read address access time max 8ns
Read output hold time min 2ns
Write cycle time min 8ns
Write address setup to Write end min 6.5ns
Write Enable pulse width min 6.5ns
Data setup to write end min 5.0ns
Write Address setup time min 0ns.

Constraints

To minimise signal skew on the signals all "mem_*" pins should also be set to "FAST" this takes the IOB delay from 2.92ns to 2.17ns, giving use 0.75ns additional slack.

There is no timing advantage to using higher drive levels than the default of 12mA.

You must also set the mapping properties for "Pack I/O Registers/Latches into IOBs" to "For Inputs and Outputs". I tried to do this with the IOB=TRUE constrant, but it didn't work.

Read cycle timing

Given that at 100MHz there is there isn't much time for the following events to occur:

  • The address signals are generated by the IOB (synced to clk_mem)
  • these signals to run across the PCB to the SRAM - approximately 0.15ns per inch
  • then 8ns is required for the SRAM to access the address and produce valid data
  • this then needs to get back across the PCB - approximately 0.15ns per inch
  • and the the 1.60ns pad input delay before the value can be latched.


To make this work, the memory data_bus is sampled on the rising edge of 'clk_wen', which is delayed 1.5ns after clk_mem. This gives 11.5ns to fit in the known and inescapable 9.6ns plus the routing delays to/from the SDRAM of 0.6ns (allowing for two inches). This delayed latching of the data bus only works as the read output hold time is 2ns. This slightly conflicts with the tri-state on mem_data, but as the tri-state time is 2.17ns this gives a little slack.

The downside of this scheme is that during the clock cycle following the read, there is only 8ns of the following cycle to use the received value.

Write cycle timing

The tricky bits of the write cycle are:

  • Generating write enable pulse somewhere between 6.5ns and 10ns.
  • Ensuring that the address and data signals meet setup times for the write enable
  • Ensuring that the hold time required on the rising side of the write enable pulse is met.

The setup and hold times depends somewhat on PCB trace lengths and capacitance, which is out of my control.

My solution is to use a DDR output and three clocks for the project:

  • clk_mem - The system clock at 100MHz
  • clk_we - the write enable signal returns to '1' on the rising edge of this clock. It should rise a little before mem_clk, ensuring that the 0ns signal holdup times meet, but late enough to ensure that the length requirement of the Write Enable pulse is met.
  • clk_wen - This is used to lower the write enable pulse. This should occur as early in the cycle as possible giving just enough time after clk_mem rises to ensure that the address and data lines are stable when the Write enable pulse occurs. As this clock is also used to sampling mem_data it needs to be at most 2ns after clk_mem.

The final timing is a phase angle for clk_wen is 50 degrees (for 1.5ns after clk_mem) and 342 degrees for clk_we (0.5ns ahead of clk_mem).

The constraining factor in the timing of clk_wen is the delay required for the flip-flop holding the signal for mem_we to propogate through the FPGA fabric the ODDR2 output (around 1.3ns).

Constraints File

papilio_plus.ucf

NET "clk_32" TNM_NET = clk;
NET "clk_32" LOC = "P94" | IOSTANDARD = LVTTL | PERIOD = 31.25ns;

## Prohibit the automatic placement of pins that are connected to VCC or GND for configuration.
CONFIG PROHIBIT=P144;
CONFIG PROHIBIT=P69;
CONFIG PROHIBIT=P60;

# Address lines
NET "mem_addr<0>"  LOC = "P6"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<1>"  LOC = "P7"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<2>"  LOC = "P9"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<3>"  LOC = "P10"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<4>"  LOC = "P11"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<5>"  LOC = "P141" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<6>"  LOC = "P140" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<7>"  LOC = "P139" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<8>"  LOC = "P138" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<9>"  LOC = "P137" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<10>" LOC = "P46"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<11>" LOC = "P45"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<12>" LOC = "P44"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<13>" LOC = "P43"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<14>" LOC = "P41"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<15>" LOC = "P29"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<16>" LOC = "P30"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_addr<17>" LOC = "P32"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST; 

NET "mem_data<0>"  LOC = "P14"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<1>"  LOC = "P15"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<2>"  LOC = "P16"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<3>"  LOC = "P17"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<4>"  LOC = "P5"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<5>"  LOC = "P2"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<6>"  LOC = "P1"   | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<7>"  LOC = "P143" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<8>"  LOC = "P40"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<9>"  LOC = "P35"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<10>" LOC = "P34"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<11>" LOC = "P33"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<12>" LOC = "P21"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<13>" LOC = "P22"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<14>" LOC = "P23"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_data<15>" LOC = "P24"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;

# Control lines
NET "mem_nCE" LOC = "P12"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_nWE" LOC = "P142" | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_nOE" LOC = "P27"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;
NET "mem_nBE" LOC = "P26"  | IOSTANDARD=LVTTL | IOB=TRUE | FAST;

NET memcheck_done    LOC="P75"  | IOSTANDARD=LVTTL; # A7  LED1
NET memcheck_failed  LOC="P67"  | IOSTANDARD=LVTTL; # A6  LED2

Test pattern generator

To make things easier to debug I've separated the generation of test patterns from the memory interface. Here's the module that generates the test patterns:

test_generator.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
-- 
-- Description: 
--   Generates 16 test patterns for testing SDRAM
--
--   Test partterns are:
--    - fill with zeros, then verify
--    - fill with ones, then verify
--    - fill with even bits, then verify
--    - fill with ddd bits, then verify
--    - fill with the low 16 bits of the address, then verify
--    - fill with the high 16 bits of the address, then verify
--    - fill with odd bits on odd addresses, even bits on even addresses, then verify
--    - fill with even bits on odd addresses, odd bits on even addresses, then verify
--    - Word at a time with zeros, verifying immediately after write
--    - Word at a time with ones, verifying immediately after write
--    - Word at a time with even bits, verifying immediately after write
--    - Word at a time with odd bits, verifying immediately after write
--    - Word at a time with the low 16 bits of the address, verifying immediately after write
--    - Word at a time with the high 16 bits of the address, verifying immediately after write
--    - Word at a time with odd bits on odd addresses, even bits on even addresses, verifying immediately after write
--    - Word at a time with even bits on odd addresses, odd bits on even addresses, verifying immediately after write
--
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity test_generator is
   Generic (addr_width : natural := 17;
            data_width : natural := 16);
    Port ( clk       : in  STD_LOGIC;
           address   : out  STD_LOGIC_VECTOR (addr_width-1 downto 0);
           data      : out  STD_LOGIC_VECTOR (data_width-1 downto 0);
           readback   : out  STD_LOGIC;
           test_cycle : out  STD_LOGIC;
           completed : out  STD_LOGIC
           );
end test_generator;

architecture Behavioral of test_generator is
   constant final_address : STD_LOGIC_VECTOR(addr_width-1 downto 0) := "111111111111111111";
   constant first_address : STD_LOGIC_VECTOR(addr_width-1 downto 0) := "000000000000000000";

   signal completed_flag  : std_logic := '0';

   signal addr_counter    : STD_LOGIC_VECTOR(addr_width-1 downto 0) := first_address;
   signal pattern_counter : STD_LOGIC_VECTOR(2 downto 0)            := (others => '0');
   signal interleave      : std_logic := '0';
   signal reset_counter   : STD_LOGIC_VECTOR(24 downto 0) := (others => '1');
   
   signal next_data    : STD_LOGIC_VECTOR(data_width-1 downto 0);
   signal next_do_read : STD_LOGIC := '0';
   
   constant zeros      : std_logic_vector(31 downto 0) := x"00000000";
   constant odd_bits   : std_logic_vector(31 downto 0) := x"AAAAAAAA";
   constant even_bits  : std_logic_vector(31 downto 0) := x"55555555";
   constant ones       : std_logic_vector(31 downto 0) := x"FFFFFFFF";

begin
   completed <= completed_flag;

   next_data <= zeros(data_width-1 downto 0)                      when pattern_counter = "000"
                else ones(data_width-1 downto 0)                  when pattern_counter = "001"
                else even_bits(data_width-1 downto 0)             when pattern_counter = "010"
                else odd_bits(data_width-1 downto 0)              when pattern_counter = "011"
                else addr_counter(data_width-1 downto 0)          when pattern_counter = "100"
                else addr_counter(addr_width-1 downto (addr_width - data_width)) when pattern_counter = "101"
                else even_bits(data_width-1 downto 0)             when pattern_counter = "110" and addr_counter(0) <= '0'
                else odd_bits(data_width-1 downto 0)              when pattern_counter = "110" and addr_counter(0) <= '1'
                else odd_bits(data_width-1 downto 0)              when pattern_counter = "111" and addr_counter(0) <= '0'
                else even_bits(data_width-1 downto 0)             when pattern_counter = "111" and addr_counter(0) <= '1'
                else zeros(data_width-1 downto 0);
                
   process(clk)
   begin
      if rising_edge(clk) then
         if reset_counter = 0 then
            address    <= addr_counter;
            data       <= next_data;
            readback   <= next_do_read;
            test_cycle <= next_do_read;
            
            if completed_flag = '0' then
               if interleave = '0' then
                  -- write all addresses, read all addresses, then all patterns
                  if addr_counter = final_address then
                     if next_do_read = '0' then
                        next_do_read <= '1';
                     else
                        next_do_read <= '0';
                        if pattern_counter  = "111" then
                           interleave <= '1';
                        end if;
                        pattern_counter <= pattern_counter + 1;
                     end if;
                     addr_counter <= first_address;
                  else
                     addr_counter <= addr_counter + 1;
                  end if;
               else
                  -- write then read, all addresses, then all patterns
                  if next_do_read = '0' then
                     next_do_read <= '1';
                  else
                     next_do_read <= '0';
                     if addr_counter = final_address  then
                        if pattern_counter = "111" then
                           completed_flag <= '1';
                        end if;
                        pattern_counter <= pattern_counter + 1;
                        addr_counter <= first_address;
                     else
                        addr_counter <= addr_counter + 1;
                     end if;
                  end if;
               end if;
            end if;
         else
            reset_counter <= reset_counter - 1;
            address       <= (others => '0');
            data            <= (others => '0');
            addr_counter  <= first_address;
            readback      <= '0';
            test_cycle    <= '0';
         end if;
      end if;

   end process;
end Behavioral;

SRAM interface

This module does the hard work of interfacing with the SDRAM:

sram_memtest.vhd

----------------------------------------------------------------------------------
-- Engineer: Mike Field (hamster@snap.net.nz)
-- 
-- Create Date:    20:30:34 02/15/2012 
-- Module Name:    sram_memtest - Behavioral 
-- Description:    Memory interface for SRAM tester for the Papilio Plus board.
-- 
--
-- Requirements: 
--   - All "mem_*" signals must have IOB=TRUE set in the Implementation Constraints file.
--   - Maximum clock is 100MHz 
--   - The memtest_clk component must generate three signals
--       - clk_mem - System clock
--       - clk_we  - this should occur late enough to raise "mem_we" just before mem_clk, but
--                   meeting the length requirments of the Write Enable pulse.
--       - clk_wen - This is used to lower the write enable pulse, and latch the data bus. This
--                   should occur as early in the cycle as possible. It needs to occur soon enough
--                   after clk_mem to capture the data before outputs change, but late enough to 
--                   ensure that the address and data lines are stable when the Write enable pulse
--                   occurs.
--  At 100MHz the phase angle for clk_we is 50 degrees (for 1.5ns after clk_mem) and 342 degrees for 
--  clk_we (0.5ns ahead of clk_mem).
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

entity sram_memtest is
   GENERIC (
      addr_width : natural := 18;
      data_width : natural := 16
   );
   PORT (
      clk_32     : IN    STD_LOGIC;
      mem_addr : OUT   STD_LOGIC_VECTOR(addr_width-1 downto 0);
      mem_data : INOUT STD_LOGIC_VECTOR(data_width-1 downto 0);
      mem_nCE  : OUT   STD_LOGIC;
      mem_nWE  : OUT   STD_LOGIC;
      mem_nOE  : OUT   STD_LOGIC;
      mem_nBE  : OUT   STD_LOGIC;
      memcheck_done : OUT STD_LOGIC;
      memcheck_failed : OUT STD_LOGIC
   );   
end sram_memtest;

architecture Behavioral of sram_memtest is
   component memtest_clk
   port(-- Clock in ports
      CLK_IN1 : in     std_logic;
      -- Clock out ports
      CLK_mem : out    std_logic;
      CLK_we  : out    std_logic;
      CLK_wen : out    std_logic
   );
   end component;

   COMPONENT test_generator
   GENERIC (
      data_width : NATURAL := 16;
      addr_width : NATURAL := 18
   );
   PORT(
      clk          : IN std_logic;          
      address      : OUT std_logic_vector(addr_width-1 downto 0);
      data         : OUT std_logic_vector(data_width-1 downto 0);
      readback     : OUT std_logic;
      test_cycle   : OUT std_logic;
      completed    : OUT std_logic
      );
   END COMPONENT;

   signal clk_mem : STD_LOGIC := '0';
   signal clk_we  : STD_LOGIC := '0';
   signal clk_wen : STD_LOGIC := '0';

   signal test_addr    : STD_LOGIC_VECTOR(addr_width-1 downto 0) := (others => '0');
   signal test_data    : STD_LOGIC_VECTOR(data_width-1 downto 0) := (others => '0');
   signal test_do_read : STD_LOGIC;
   signal test_is_test_cycle : STD_LOGIC;
   
   signal out_write_enable     : STD_LOGIC := '1';

   signal hold_data            : STD_LOGIC_VECTOR(data_width-1 downto 0) := (others => '0');
   signal check_data           : STD_LOGIC_VECTOR(data_width-1 downto 0) := (others => '0');
   signal check_data_against   : STD_LOGIC_VECTOR(data_width-1 downto 0) := (others => '0');
   signal mem_data_reg         : STD_LOGIC_VECTOR(data_width-1 downto 0) := (others => '0');

   signal hiz                  : STD_LOGIC := '0';
   signal running_test_cycle   : STD_LOGIC := '0';
   signal completed_flag       : STD_LOGIC;
   
   signal failed_flag          : std_logic := '0';

begin
   clk_instance : memtest_clk
      port map (
      CLK_IN1 => CLK_32,
      CLK_mem => CLK_mem,
      CLK_we  => CLK_we,
      CLK_wen => CLK_wen
   );

   Inst_test_generator: test_generator PORT MAP(
      clk       => clk_mem,
      address   => test_addr,
      data      => test_data,
      readback  => test_do_read,
      test_cycle => test_is_test_cycle,
      completed => completed_flag
   );
    
   -- Global output signals
   mem_nCE <= '0';
   mem_nBE <= '0';
   memcheck_done   <= completed_flag;
   memcheck_failed <= failed_flag;
   
   -- DDR output to generate the Write Enable (WE) pulse
   ODDR2_nWE : ODDR2
   generic map(
      DDR_ALIGNMENT => "NONE",    -- Sets output alignment to "NONE", "C0", "C1" 
      INIT          => '1',     -- Sets initial state of the Q output to '0' or '1'
      SRTYPE        => "ASYNC") -- Specifies "SYNC" or "ASYNC" set/reset
   port map (
      Q  => mem_nWE,              -- 1-bit output data
      C0 => clk_we,              -- 1-bit clock input
      C1 => clk_wen,             -- 1-bit clock input
      CE => '1',                  -- 1-bit clock enable input
      D0 => '1', -- 1-bit data input (associated with C0)
      D1 => out_write_enable, -- 1-bit data input (associated with C1)
      R  => '0',                   -- 1-bit reset input
      S  => '0'                   -- 1-bit set input
   );

   tristate_proc: process(hiz,mem_data_reg)
   begin
      if hiz = '0' then 
         mem_data <= mem_data_reg;
      else
         mem_data <= "ZZZZZZZZZZZZZZZZ";
      end if;
   end process;

   -- Constantly latch the memory values
   process(clk_wen)
   begin
      if rising_edge(clk_wen) then
         check_data <= mem_data;
      end if;
   end process;
   
   process(clk_mem)
   begin
      if rising_edge(clk_mem) then
         if running_test_cycle = '1' then
            if check_data_against /= check_data then
               failed_flag <= '1';
            end if;
         end if;
         
         mem_addr         <= test_addr;
         if test_do_read = '1' then
            -- read cycle
            mem_nOE          <= '0';
            out_write_enable <= '1';
            hiz              <= '1';
         else
            -- write
            mem_nOE          <= '1';
            out_write_enable <= '0';
            hiz              <= '0';
            mem_data_reg     <= test_data;
         end if;

         check_data_against <= hold_data;
         hold_data          <= test_data;         
         running_test_cycle <= test_Is_test_cycle;
      end if;
   end process;
end Behavioral;

Personal tools