]> git.the-white-hart.net Git - vhdl/commitdiff
Add RO simulated flash and simulation utilities
authorrs <>
Thu, 28 Aug 2025 18:37:19 +0000 (13:37 -0500)
committerrs <>
Thu, 28 Aug 2025 18:37:19 +0000 (13:37 -0500)
libraries/simulated/assert_pulsewidth.vhd [new file with mode: 0644]
libraries/simulated/assert_setuphold.vhd [new file with mode: 0644]
libraries/simulated/assert_setuphold_vec.vhd [new file with mode: 0644]
libraries/simulated/changed_within_t.vhd [new file with mode: 0644]
libraries/simulated/changed_within_t_vec.vhd [new file with mode: 0644]
libraries/simulated/delay_edges.vhd
libraries/simulated/sim_js28f128j3d75.vhd [new file with mode: 0644]
libraries/simulated/sim_utility.vhd [new file with mode: 0644]
libraries/simulated/tests/test_asserts.vhd [new file with mode: 0644]
libraries/simulated/tests/test_simflash.vhd [new file with mode: 0644]

diff --git a/libraries/simulated/assert_pulsewidth.vhd b/libraries/simulated/assert_pulsewidth.vhd
new file mode 100644 (file)
index 0000000..a0741e3
--- /dev/null
@@ -0,0 +1,41 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity assert_pulsewidth is
+       generic (
+               LEVEL: std_logic := '1';
+               T_MIN: time      := time'low;
+               T_MAX: time      := time'high;
+               NAME:  string    := "<sig>"
+       );
+       port (
+               sig: in std_logic
+       );
+end assert_pulsewidth;
+
+
+architecture behavioral of assert_pulsewidth is
+begin
+
+       process (sig)
+               variable width: time;
+       begin
+               -- Detect the end of a pulse of type LEVEL
+               if sig /= LEVEL and sig'last_value = LEVEL then
+                       -- Delayed by a delta cycle since sig'last_event is now
+                       width := sig'delayed'last_event;
+
+                       assert width <= T_MAX
+                               report "Maximum " & std_logic'image(LEVEL) & " pulse width violation for '" & NAME &
+                                      "' (width: " & time'image(width) & ", max: " & time'image(T_MAX) & ")"
+                               severity error;
+
+                       assert width >= T_MIN
+                               report "Minimum " & std_logic'image(LEVEL) & " pulse width violation for '" & NAME &
+                                      "' (width: " & time'image(width) & ", min: " & time'image(T_MIN) & ")"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/assert_setuphold.vhd b/libraries/simulated/assert_setuphold.vhd
new file mode 100644 (file)
index 0000000..664d165
--- /dev/null
@@ -0,0 +1,66 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity assert_setuphold is
+       generic (
+               LEVEL:    std_logic := '1';
+               T_SETUP:  time      := time'low;
+               T_HOLD:   time      := time'low;
+               NAME_REF: string    := "<ref>";
+               NAME_SIG: string    := "<sig>"
+       );
+       port (
+               ref: in std_logic;  -- Reference signal for setup/hold times, e.g. clock
+               sig: in std_logic
+       );
+end assert_setuphold;
+
+
+architecture behavioral of assert_setuphold is
+begin
+
+       process (ref, sig)
+               -- Absolute time of most recent edge-of-interest
+               -- Initial value is negative so hold checks can be skipped if "sig" changes before "ref"
+               variable last_edge:  time := -1 ps;
+
+               variable setup_time: time;
+               variable hold_time:  time;
+       begin
+               -- Detect edges where "ref" transitions to LEVEL
+               if ref'event and ref = LEVEL then
+                       -- Keep track of the last edge for the hold time test
+                       -- Normally I hate that variables retain values between process invocations, but here it's useful!
+                       last_edge := now;
+
+                       -- If sig changed at same time as ref, assume its new value is not the value being referenced by the edge
+                       -- (Allow the hold-time constraint to catch problems there)
+                       setup_time := sig'last_event;
+                       if setup_time = 0 ns then
+                               setup_time := sig'delayed'last_event;
+                       end if;
+
+                       -- Check setup time constraint unless this is the beginning of the simulation
+                       if now > 0 ps then
+                               assert setup_time >= T_SETUP
+                                       report "Setup time to '" & NAME_REF & "' <= " & std_logic'image(LEVEL) & " violation for " & NAME_SIG &
+                                              " (actual: " & time'image(setup_time) & ", required: " & time'image(T_SETUP) & ")"
+                                       severity error;
+                       end if;
+               end if;
+
+               -- Detect changes in "sig" but only if "ref" has had an edge before
+               if sig'event and last_edge > 0 ps then
+                       -- Checking against ref'last_event is tempting, but it might catch the wrong kind of transition
+                       hold_time := now - last_edge;
+
+                       -- Check hold time constraint
+                       assert hold_time >= T_HOLD
+                               report "Hold time from '" & NAME_REF & "' <= " & std_logic'image(LEVEL) & " violation for " & NAME_SIG &
+                                      " (actual: " & time'image(hold_time) & ", required: " & time'image(T_HOLD) & ")"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/assert_setuphold_vec.vhd b/libraries/simulated/assert_setuphold_vec.vhd
new file mode 100644 (file)
index 0000000..e362d4b
--- /dev/null
@@ -0,0 +1,66 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity assert_setuphold_vec is
+       generic (
+               LEVEL:    std_logic := '1';
+               T_SETUP:  time      := time'low;
+               T_HOLD:   time      := time'low;
+               NAME_REF: string    := "<ref>";
+               NAME_SIG: string    := "<sig>"
+       );
+       port (
+               ref: in std_logic;  -- Reference signal for setup/hold times, e.g. clock
+               sig: in std_logic_vector
+       );
+end assert_setuphold_vec;
+
+
+architecture behavioral of assert_setuphold_vec is
+begin
+
+       process (ref, sig)
+               -- Absolute time of most recent edge-of-interest
+               -- Initial value is negative so hold checks can be skipped if "sig" changes before "ref"
+               variable last_edge:  time := -1 ps;
+
+               variable setup_time: time;
+               variable hold_time:  time;
+       begin
+               -- Detect edges where "ref" transitions to LEVEL
+               if ref'event and ref = LEVEL then
+                       -- Keep track of the last edge for the hold time test
+                       -- Normally I hate that variables retain values between process invocations, but here it's useful!
+                       last_edge := now;
+
+                       -- If sig changed at same time as ref, assume its new value is not the value being referenced by the edge
+                       -- (Allow the hold-time constraint to catch problems there)
+                       setup_time := sig'last_event;
+                       if setup_time = 0 ns then
+                               setup_time := sig'delayed'last_event;
+                       end if;
+
+                       -- Check setup time constraint unless this is the beginning of the simulation
+                       if now > 0 ps then
+                               assert setup_time >= T_SETUP
+                                   report "Setup time to '" & NAME_REF & "' <= " & std_logic'image(LEVEL) & " violation for " & NAME_SIG &
+                                          " (actual: " & time'image(setup_time) & ", required: " & time'image(T_SETUP) & ")"
+                                   severity error;
+                       end if;
+               end if;
+
+               -- Detect changes in "sig" but only if "ref" has had an edge before
+               if sig'event and last_edge > 0 ps then
+                       -- Checking against ref'last_event is tempting, but it might catch the wrong kind of transition
+                       hold_time := now - last_edge;
+
+                       -- Check hold time constraint
+                       assert hold_time >= T_HOLD
+                               report "Hold time from '" & NAME_REF & "' <= " & std_logic'image(LEVEL) & " violation for " & NAME_SIG &
+                                      " (actual: " & time'image(hold_time) & ", required: " & time'image(T_HOLD) & ")"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/changed_within_t.vhd b/libraries/simulated/changed_within_t.vhd
new file mode 100644 (file)
index 0000000..6a6b5a8
--- /dev/null
@@ -0,0 +1,24 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity changed_within_t is
+       generic (
+               T:      time := 0 ns;
+               T_HOLD: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic;
+               sig_out: out std_logic
+       );
+end changed_within_t;
+
+
+architecture behavioral of changed_within_t is
+       signal sig: std_logic := '1';
+begin
+
+       sig <= '1' after T_HOLD when not sig_in'stable(T - T_HOLD) else '0' after T_HOLD;
+       sig_out <= sig;
+
+end behavioral;
diff --git a/libraries/simulated/changed_within_t_vec.vhd b/libraries/simulated/changed_within_t_vec.vhd
new file mode 100644 (file)
index 0000000..f94232c
--- /dev/null
@@ -0,0 +1,24 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity changed_within_t_vec is
+       generic (
+               T:      time := 0 ns;
+               T_HOLD: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic_vector;
+               sig_out: out std_logic
+       );
+end changed_within_t_vec;
+
+
+architecture behavioral of changed_within_t_vec is
+       signal sig: std_logic := '1';
+begin
+
+       sig <= '1' after T_HOLD when not sig_in'stable(T - T_HOLD) else '0' after T_HOLD;
+       sig_out <= sig;
+
+end behavioral;
index 42ffd412763004297db04be18112c9472afa527b..8cb08564389b022689aaa8f45f88b2b3f0a618e5 100644 (file)
@@ -14,15 +14,18 @@ entity delay_edges is
 end delay_edges;
 
 architecture behavioral of delay_edges is
+       signal mask: std_logic;
 begin
 
        delayed_a: if D_RISE > D_FALL generate
-               sig_out <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL);
+               mask <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL);
        end generate;
 
        delayed_b: if D_RISE <= D_FALL generate
-               sig_out <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL);
+               mask <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL);
        end generate;
 
+       sig_out <= sig_in when mask = 'U' else mask;
+
 end behavioral;
 
diff --git a/libraries/simulated/sim_js28f128j3d75.vhd b/libraries/simulated/sim_js28f128j3d75.vhd
new file mode 100644 (file)
index 0000000..03d042c
--- /dev/null
@@ -0,0 +1,316 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+use work.sim_utility.all;
+
+
+entity sim_js28f128j3d75 is
+       generic (
+               FILENAME: string  := "";
+               BASEADDR: natural := 0
+       );
+       port (
+               a:      in    std_logic_vector(23 downto 0);
+               d:      inout std_logic_vector(15 downto 0);
+               ce:     in    std_logic_vector(2 downto 0);
+               rp_n:   in    std_logic;
+               oe_n:   in    std_logic;
+               we_n:   in    std_logic;
+               sts:    out   std_logic;  -- open drain
+               byte_n: in    std_logic;
+               vpen:   in    std_logic
+       );
+end sim_js28f128j3d75;
+
+
+architecture behavioral of sim_js28f128j3d75 is
+       ----------------------------------------------------------------------------
+       -- Timings for 128Mbit, J3D-75 parts
+
+       -- Read timings
+       constant T_AVAV: time := 75 ns;    -- R1, Read/Write Cycle Time
+       constant T_AVQV: time := 75 ns;    -- R2, Address to Output Delay
+       constant T_ELQV: time := 75 ns;    -- R3, CEx to Output Delay
+       constant T_GLQV_NA: time := 25 ns; -- R4, OE# to Non-Array Output Delay
+       constant T_PHQV: time := 210 ns;   -- R5, RP# High to Output Delay
+       constant T_ELQX: time := 0 ns;     -- R6, CEx to Output Low Z
+       constant T_GLQX: time := 0 ns;     -- R7, OE# to Output Low Z
+       constant T_EHQZ: time := 25 ns;    -- R8, CEx High to Output in High Z
+       constant T_GHQZ: time := 15 ns;    -- R9, OE# High to Output in High Z
+       constant T_OH:   time := 0 ns;     -- R10, Output Hold from Address, CEx, or OE# change
+       constant T_ELFL: time := 10 ns;    -- R11, CEx Low to BYTE# High or Low
+       constant T_ELFH: time := 10 ns;    -- R11, (Same as Above)
+       constant T_FLQV: time := 1 us;     -- R12, BYTE# to Output Delay
+       constant T_FHQV: time := 1 us;     -- R12, (Same as Above)
+       constant T_FLQZ: time := 1 us;     -- R13, BYTE# to Output in High Z
+       constant T_EHEL: time := 0 ns;     -- R14, CEx High to CEx Low
+       constant T_APA:  time := 25 ns;    -- R15, Page Address Access Time
+       constant T_GLQV_A: time := 25 ns;  -- R16, OE# to Array Output Delay
+
+       -- Write timings
+       constant T_PHWL: time := 210 ns;   -- W1, RP# High Recovery to WE# (CEx) Going Low
+       constant T_PHEL: time := 210 ns;   -- W1, (Same as Above)
+       constant T_ELWL: time := 0 ns;     -- W2, CEx (WE#) Low to WE# (CEx) Going Low
+       constant T_WLEL: time := 0 ns;     -- W2, (Same as Above)
+       constant T_WP:   time := 60 ns;    -- W3, Write Pulse Width
+       constant T_DVWH: time := 50 ns;    -- W4, Data Setup to WE# (CEx) Going High
+       constant T_DVEH: time := 50 ns;    -- W4, (Same as Above)
+       constant T_AVWH: time := 55 ns;    -- W5, Address Setup to WE# (CEx) Going High
+       constant T_AVEH: time := 55 ns;    -- W5, (Same as Above)
+       constant T_WHEH: time := 0 ns;     -- W6, CEx (WE#) Hold from WE# (CEx) High
+       constant T_EHWH: time := 0 ns;     -- W6, (Same as Above)
+       constant T_WHDX: time := 0 ns;     -- W7, Data Hold from WE# (CEx) High
+       constant T_EHDX: time := 0 ns;     -- W7, (Same as Above)
+       constant T_WHAX: time := 0 ns;     -- W8, Address Hold from WE# (CEx) High
+       constant T_EHAX: time := 0 ns;     -- W8, (Same as Above)
+       constant T_WPH:  time := 30 ns;    -- W9, Write Pulse Width High
+       constant T_VPWH: time := 0 ns;     -- W11, Vpen Setup to WE# (CEx) Going High
+       constant T_VPEH: time := 0 ns;     -- W11, (Same as Above)
+       constant T_WHGL: time := 35 ns;    -- W12, Write Recovery before Read
+       constant T_EHGL: time := 35 ns;    -- W12, (Same as Above)
+       constant T_WHRL: time := 500 ns;   -- W13, WE# (CEx) High to STS Going Low
+       constant T_EHRL: time := 500 ns;   -- W13, (Same as Above)
+       constant T_QVVL: time := 0 ns;     -- W15, Vpen Hold from Valid SRD, STS Going High
+
+       -- Configuration timings
+       constant T_WHQV3: time := 175 us;  -- W16, Byte Program Time (Using Word/Byte Program Command)
+       constant T_EHQV3: time := 175 us;  -- W16, (Same as above)
+       constant T_WHQV4: time := 4 sec;   -- W16, Block Erase Time
+       constant T_EHQV4: time := 4 sec;   -- W16, (Same as above)
+       constant T_WHQV5: time := 60 us;   -- W16, Set Lock-Bit Time
+       constant T_EHQV5: time := 60 us;   -- W16, (Same as above)
+       constant T_WHQV6: time := 0.7 sec; -- W16, Clear Block Lock-Bits Time
+       constant T_EHQV6: time := 0.7 sec; -- W16, (Same as above)
+       constant T_WHRH1: time := 20 us;   -- W16, Program Suspend Latency Time to Read
+       constant T_EHRH1: time := 20 us;   -- W16, (Same as above)
+       constant T_WHRH:  time := 20 us;   -- W16, Erase Suspend Latency Time to Read
+       constant T_EHRH:  time := 20 us;   -- W16, (Same as above)
+       constant T_STS:   time := 500 ns;  -- WY, STS Pulse Width Low Time
+
+       -- Reset timings
+       constant T_PLPH: time := 25 us;    -- P1, RP# Pulse Low Time
+       constant T_PHRH: time := 100 ns;   -- P2, RP# High to Reset During Block-Erase, Program, or Lock-Bit Configuration
+       constant T_VCCPH: time := 60 us;   -- P3, Vcc Power Valid to RP# de-assertion (high)
+
+       ----------------------------------------------------------------------------
+       -- Useful internal signals
+
+       signal internal_ce_n: std_logic;
+       signal internal_we_n: std_logic;
+
+       ----------------------------------------------------------------------------
+       -- Status signals
+
+       signal sts_ready:     std_logic;
+
+       ----------------------------------------------------------------------------
+       -- Array value
+
+       shared variable flash_array: sparse_t;
+       signal array_word:    std_logic_vector(15 downto 0);
+
+       ----------------------------------------------------------------------------
+       -- Output value
+
+       signal word:          std_logic_vector(15 downto 0);
+       signal word_x:        std_logic_vector(15 downto 0);
+       signal word_xz:       std_logic_vector(15 downto 0);
+
+       signal xmask_rp:      std_logic;
+       signal xmask_byte:    std_logic;
+       signal xmask_ce:      std_logic;
+       signal xmask_oe:      std_logic;
+       signal xmask_addr_h:  std_logic;
+       signal xmask_addr_l:  std_logic;
+
+       signal zmask_ce:      std_logic;
+       signal zmask_oe:      std_logic;
+       signal zmask_byte:    std_logic;
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- Useful internal signals composed from external signals
+
+       -- Decode multiple chip enable inputs into single chip enable signal
+       with ce select internal_ce_n <=
+               '0' when "000",
+               '1' when "001",
+               '1' when "010",
+               '1' when "011",
+               '0' when "100",
+               '0' when "101",
+               '0' when "110",
+               '1' when "111",
+               'X' when others;
+
+       -- Write latch is triggered by either WEx or CEx going high, whichever is first
+       internal_we_n <= we_n or internal_ce_n;
+
+       -- STS signal represents the state of the WSM
+       sts <= 'H' when sts_ready = '1' else '0';
+
+
+       ----------------------------------------------------------------------------
+       -- Array value
+
+       process
+       begin
+               if FILENAME = "" then
+                       flash_array := sparse_create(default => x"FF");
+               else
+                       flash_array := sparse_create;
+                       sparse_load(flash_array, FILENAME, BASEADDR);
+               end if;
+               wait;
+       end process;
+
+       array_word <= sparse_get(flash_array, to_integer(unsigned(a(a'high downto 1) & '0'))) &
+                     sparse_get(flash_array, to_integer(unsigned(a(a'high downto 1) & '1'))) after T_OH;
+
+
+       ----------------------------------------------------------------------------
+       -- Output value
+
+       word <= array_word;
+
+       e_xmask_rp: entity work.changed_within_t
+               generic map (T => T_PHQV)
+               port map (sig_in => rp_n, sig_out => xmask_rp);
+
+       e_xmask_byte: entity work.changed_within_t
+               generic map (T => T_FLQV)
+               port map (sig_in => byte_n, sig_out => xmask_byte);
+
+       e_xmask_ce: entity work.delay_edges
+               generic map (D_RISE => T_OH, D_FALL => T_ELQV)
+               port map (sig_in => internal_ce_n, sig_out => xmask_ce);
+
+       e_xmask_oe: entity work.delay_edges
+               generic map (D_RISE => T_OH, D_FALL => T_GLQV_A)
+               port map (sig_in => oe_n, sig_out => xmask_oe);
+
+       e_xmask_addr_h: entity work.changed_within_t_vec
+               generic map (T => T_AVQV, T_HOLD => T_OH)
+               port map (sig_in => a(a'high downto 3), sig_out => xmask_addr_h);
+
+       e_xmask_addr_l: entity work.changed_within_t_vec
+               generic map (T => T_APA, T_HOLD => T_OH)
+               port map (sig_in => a(2 downto 0), sig_out => xmask_addr_l);
+
+       word_x <= (others => 'X') when (xmask_rp or xmask_ce or xmask_oe or xmask_addr_h or xmask_addr_l) = '1' else word;
+
+       e_zmask_ce: entity work.delay_edges
+               generic map (D_RISE => T_EHQZ, D_FALL => T_ELQX)
+               port map (sig_in => internal_ce_n, sig_out => zmask_ce);
+
+       e_zmask_oe: entity work.delay_edges
+               generic map (D_RISE => T_GHQZ, D_FALL => T_GLQX)
+               port map (sig_in => oe_n, sig_out => zmask_oe);
+
+       word_xz <= (others => 'Z') when (zmask_ce or zmask_oe) = '1' else word_x;
+
+       zmask_byte <= not byte_n after T_FLQZ;
+
+       d(15 downto 8) <= (others => 'Z') when zmask_byte = '1' else word_xz(15 downto 8);
+       d( 7 downto 0) <= word_xz( 7 downto 0) when byte_n = '1' else
+                         word_xz( 7 downto 0) when a(0) = '0'   else
+                         word_xz(15 downto 8);
+
+
+       ----------------------------------------------------------------------------
+       -- Check for erroneous usages
+
+       -- Should not try to read and write at the same time
+       process (oe_n, we_n)
+       begin
+               assert not (oe_n = '0' and we_n = '0')
+                       report "OE# and WE# should never be enabled simultaneously."
+                       severity error;
+       end process;
+
+       -- Address must be valid for a full read or write cycle
+       R1: process (oe_n, a(23 downto 4))
+       begin
+               -- FIXME: Check more address bits if in non-array or 4-word array modes
+               if a(23 downto 4)'event and oe_n = '0' then
+                       assert a(23 downto 4)'last_event >= T_AVAV;
+                               report "T_AVAV not met (Read/Write Cycle Time)"
+                               severity error;
+               end if;
+       end process;
+
+       -- BYTE# cannot change more than T_ELFL/T_ELFH after CEx goes low
+       -- This might be another way of describing a negative setup time requirement
+       R11: process (byte_n, internal_ce_n)
+       begin
+               if byte_n'event and internal_ce_n = '0' then
+                       assert internal_ce_n'last_event <= T_ELFL;
+                               report "T_ELFL/H not met (CEx Low to BYTE# High or Low)"
+                               severity error;
+               end if;
+       end process;
+
+       W1a: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_PHWL, NAME_SIG => "WE#", NAME_REF => "RP#")
+               port map (sig => we_n, ref => rp_n);
+
+       W1b: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_PHEL, NAME_SIG => "CEx", NAME_REF => "RP#")
+               port map (sig => internal_ce_n, ref => rp_n);
+
+       W2a: entity work.assert_setuphold
+               generic map (LEVEL => '0', T_SETUP => T_ELWL, NAME_SIG => "CEx", NAME_REF => "WE#")
+               port map (sig => internal_ce_n, ref => we_n);
+
+       W2b: entity work.assert_setuphold
+               generic map (LEVEL => '0', T_SETUP => T_WLEL, NAME_SIG => "WE#", NAME_REF => "CEx")
+               port map (sig => we_n, ref => internal_ce_n);
+
+       W3: entity work.assert_pulsewidth
+               generic map (LEVEL => '0', T_MIN => T_WP, NAME => "WE#")
+               port map (sig => we_n);
+
+       W4: entity work.assert_setuphold_vec
+               generic map (LEVEL => '1', T_SETUP => T_DVWH, NAME_SIG => "Data", NAME_REF => "WE#/CEx")
+               port map (sig => d, ref => internal_we_n);
+
+       W5: entity work.assert_setuphold_vec
+               generic map (LEVEL => '1', T_SETUP => T_AVWH, NAME_SIG => "Addr", NAME_REF => "WE#/CEx")
+               port map (sig => a, ref => internal_we_n);
+
+       W6a: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_WHEH, NAME_SIG => "CEx", NAME_REF => "WE#")
+               port map (sig => internal_ce_n, ref => we_n);
+
+       W6b: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_EHWH, NAME_SIG => "WE#", NAME_REF => "CEx")
+               port map (sig => we_n, ref => internal_ce_n);
+
+       W7: entity work.assert_setuphold_vec
+               generic map (LEVEL => '1', T_HOLD => T_WHDX, NAME_SIG => "Data", NAME_REF => "WE#/CEx")
+               port map (sig => d, ref => internal_we_n);
+
+       W8: entity work.assert_setuphold_vec
+               generic map (LEVEL => '1', T_HOLD => T_WHAX, NAME_SIG => "Addr", NAME_REF => "WE#/CEx")
+               port map (sig => a, ref => internal_we_n);
+
+       W9: entity work.assert_pulsewidth
+               generic map (LEVEL => '1', T_MIN => T_WPH, NAME => "WE#")
+               port map (sig => we_n);
+
+       W11: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_SETUP => T_VPWH, NAME_SIG => "Vpen", NAME_REF => "WE#/CEx")
+               port map (sig => vpen, ref => internal_we_n);
+
+       W12: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_WHGL, NAME_SIG => "OE#", NAME_REF => "WE#/CEx")
+               port map (sig => oe_n, ref => internal_we_n);
+
+       W15: entity work.assert_setuphold
+               generic map (LEVEL => '1', T_HOLD => T_QVVL, NAME_SIG => "Vpen", NAME_REF => "sts")
+               port map (sig => vpen, ref => sts_ready);
+
+end behavioral;
diff --git a/libraries/simulated/sim_utility.vhd b/libraries/simulated/sim_utility.vhd
new file mode 100644 (file)
index 0000000..2fc2bb0
--- /dev/null
@@ -0,0 +1,205 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+package sim_utility is
+
+       ----------------------------------------------------------------------------
+       -- String manipulation
+
+       function str_endswith(str: string; suffix: string) return boolean;
+
+
+       ----------------------------------------------------------------------------
+       -- Sparse byte array for simulated memory devices
+       --
+       -- Looks like protected types aren't supported by ISE 14.7, so gotta do this
+
+       subtype byte_t is std_logic_vector(7 downto 0);
+       type byte_array_t is array(natural range <>) of byte_t;
+       type p_byte_array_t is access byte_array_t;
+       type item_t;
+       type p_item_t is access item_t;
+       type item_t is record
+               base:      integer;
+               data:      p_byte_array_t;
+               next_item: p_item_t;
+       end record;
+
+       type sparse_t is record
+               default:   byte_t;
+               page_size: natural;
+               first:     p_item_t;
+       end record;
+
+
+       function  sparse_create   (default:   in    byte_t    := (others => 'U');
+                                  page_size: in    natural   := 4096)
+                 return sparse_t;
+
+
+       procedure sparse_load     (sparse:    inout sparse_t;
+                                  filename:  in    string;
+                                  base:      in    integer   := 0);
+
+
+       function  sparse_get      (sparse:    in    sparse_t;
+                                  idx:       in    natural)
+                 return byte_t;
+
+
+       procedure sparse_set      (sparse:    inout sparse_t;
+                                  idx:       in    natural;
+                                  val:       in    byte_t);
+
+
+       procedure sparse_get_slice(sparse:    in    sparse_t;
+                                  idx:       in    natural;
+                                  val:       out   byte_array_t);
+
+
+       procedure sparse_set_slice(sparse:    inout sparse_t;
+                                  idx:       in    natural;
+                                  val:       in    byte_array_t);
+
+end sim_utility;
+
+
+package body sim_utility is
+
+       ----------------------------------------------------------------------------
+       -- String manipulation
+
+       function str_endswith(str: string; suffix: string) return boolean is
+       begin
+               if str'length < suffix'length then
+                       return false;
+               else
+                       return str(str'right - (suffix'length - 1) to str'right) = suffix;
+               end if;
+       end function;
+
+
+       ----------------------------------------------------------------------------
+       -- Sparse byte array for simulated memory devices
+
+       -- Create a new sparse array
+       function sparse_create(default: in byte_t := (others => 'U'); page_size: in natural := 4096) return sparse_t is
+       begin
+               return (
+                       default   => default,
+                       page_size => page_size,
+                       first     => null
+               );
+       end function;
+
+       -- Load data from a file into a sparse byte array
+       procedure sparse_load(sparse: inout sparse_t; filename: in string; base: in integer := 0) is
+               type char_file_t is file of character;
+               subtype ibyte_t is natural range 0 to 255;
+
+               file f: char_file_t;
+               variable fstatus: FILE_OPEN_STATUS;
+
+               variable i: natural;
+               variable c: character;
+               variable b: ibyte_t;
+               variable v: byte_t;
+       begin
+               -- Load data from file
+               if str_endswith(FILENAME, ".bin") then
+                       -- Raw binary file
+
+                       file_open(fstatus, f, FILENAME, READ_MODE);
+                       assert fstatus = OPEN_OK report "Failed to open file '" & FILENAME & "'" severity failure;
+
+                       i := 0;
+                       while not endfile(f) loop
+                               read(f, c);
+                               b := character'pos(c);
+                               v := std_logic_vector(to_unsigned(b, 8));
+                               sparse_set(sparse, i+base, v);
+                               i := i + 1;
+                       end loop;
+
+                       file_close(f);
+
+               elsif str_endswith(FILENAME, ".hex") then
+                       -- Intel HEX format
+                       assert false report "Intel HEX format not yet supported" severity failure;
+               else
+                       assert false report "Filename suffix not recognized: '" & FILENAME & "'" severity failure;
+               end if;
+       end procedure;
+
+       -- Get a value from a sparse array
+       function sparse_get(sparse: in sparse_t; idx: in natural) return byte_t is
+               variable base: natural;
+               variable this: p_item_t;
+       begin
+               -- Find a page that contains idx
+               base := idx - (idx mod sparse.page_size);
+               this := sparse.first;
+               while this /= null loop
+                       if this.base = base then
+                               -- Retrieve the value and exit
+                               return this.data(idx mod sparse.page_size);
+                       end if;
+                       this := this.next_item;
+               end loop;
+
+               -- No page containing idx, return the default value
+               return sparse.default;
+       end function;
+
+       -- Set a value within a sparse array
+       procedure sparse_set(sparse: inout sparse_t; idx: in natural; val: in byte_t) is
+               variable base:     natural;
+               variable this:     p_item_t;
+               variable new_item: p_item_t;
+       begin
+               -- Find a page that contains idx
+               base := idx - (idx mod sparse.page_size);
+               this := sparse.first;
+               while this /= null loop
+                       if this.base = base then
+                               -- Write the value and exit
+                               this.data(idx mod sparse.page_size) := val;
+                               return;
+                       end if;
+                       this := this.next_item;
+               end loop;
+
+               -- No page found containing idx, create one and add it
+               new_item := new item_t;
+
+               new_item.base      := base;
+               new_item.data      := new byte_array_t(sparse.page_size-1 downto 0);
+               new_item.next_item := sparse.first;
+
+               sparse.first := new_item;
+
+               for i in 0 to sparse.page_size-1 loop
+                       new_item.data(i) := sparse.default;
+               end loop;
+               new_item.data(idx mod sparse.page_size) := val;
+       end procedure;
+
+       -- Get a slice of bytes from a sparse array
+       procedure sparse_get_slice(sparse: in sparse_t; idx: in natural; val: out byte_array_t) is
+       begin
+               for i in val'range loop
+                       val(i) := sparse_get(sparse, idx+i);
+               end loop;
+       end procedure;
+
+       -- Set a slice of bytes within a sparse array
+       procedure sparse_set_slice(sparse: inout sparse_t; idx: in natural; val: in byte_array_t) is
+       begin
+               for i in val'range loop
+                       sparse_set(sparse, idx+i, val(i));
+               end loop;
+       end procedure;
+
+end sim_utility;
diff --git a/libraries/simulated/tests/test_asserts.vhd b/libraries/simulated/tests/test_asserts.vhd
new file mode 100644 (file)
index 0000000..a891789
--- /dev/null
@@ -0,0 +1,101 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+
+
+entity test_asserts is
+end test_asserts;
+
+
+architecture behavior of test_asserts is
+
+       constant T: time := 10 ns;
+
+       signal sig: std_logic;
+       signal stb: std_logic;
+       signal clk: std_logic;
+
+begin
+
+       p_test_setuphold: process is
+       begin
+               stb <= '0';
+               wait for 3 ns;
+               stb <= '1';  -- Make sure the beginning of simulation isn't treated as a transition for setup/hold time
+               wait for 12 ns;
+               stb <= '0';
+               wait for 12 ns;
+               stb <= '1';
+               wait for 3 ns;
+               report "Setup time violation expected now";
+               wait for 7 ns;
+               stb <= '0';
+               wait for 25 ns;
+               stb <= '1';  -- Make sure only rising edge, not falling edge, is checked for setup/hold time
+               wait for 30 ns;
+               report "Hold time violation expected now";
+               stb <= '0';
+
+               wait;
+       end process;
+
+
+       p_test_width: process is
+       begin
+               sig <= '0';
+               wait for 5 ns;
+               sig <= '1';
+               report "Minimum pulse width violation expected now";
+               wait for 30 ns;
+               sig <= '1';
+               wait for 30 ns;
+               sig <= '0';
+               report "Maximum pulse width violation expected now";
+               wait for 30 ns;
+               sig <= '1';
+               wait for 10 ns;
+               sig <= '0';
+
+               wait;
+       end process;
+
+
+       e_uut_0: entity work.assert_pulsewidth
+               generic map (
+                       LEVEL => '0',
+                       T_MIN => 10 ns,
+                       NAME  => "test_sig"
+               )
+               port map (sig => sig);
+
+
+       e_uut_1: entity work.assert_pulsewidth
+               generic map (
+                       LEVEL => '1',
+                       T_MAX => 30 ns,
+                       NAME  => "test_sig"
+               )
+               port map (sig => sig);
+
+
+       e_uut_2: entity work.assert_setuphold
+               generic map (
+                       LEVEL    => '1',
+                       T_SETUP  => 5 ns,
+                       T_HOLD   => 5 ns,
+                       NAME_REF => "clk",
+                       NAME_SIG => "stb"
+               )
+               port map (sig => stb, ref => clk);
+
+
+       p_clk: process is
+       begin
+               clk <= '0';
+               wait for 30 ns;
+               clk <= '1';
+               wait for 30 ns;
+       end process;
+
+end;
diff --git a/libraries/simulated/tests/test_simflash.vhd b/libraries/simulated/tests/test_simflash.vhd
new file mode 100644 (file)
index 0000000..b8ce84c
--- /dev/null
@@ -0,0 +1,67 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+
+
+entity test_simflash is
+end test_simflash;
+
+
+architecture behavior of test_simflash is
+
+       signal a:      std_logic_vector(23 downto 0);
+       signal d:      std_logic_vector(15 downto 0);
+       signal ce:     std_logic_vector(2 downto 0);
+       signal rp_n:   std_logic := '0';
+       signal oe_n:   std_logic;
+       signal we_n:   std_logic;
+       signal sts:    std_logic;
+       signal byte_n: std_logic;
+       signal vpen:   std_logic;
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values and reset
+               a      <= x"000000";
+               ce     <= "111";
+               rp_n   <= '0';
+               oe_n   <= '1';
+               we_n   <= '1';
+               byte_n <= '1';
+               vpen   <= '1';
+               wait for 10 ns;
+               rp_n   <= '1';
+               wait for 1 us;
+               ce     <= "000";
+               oe_n   <= '0';
+               wait for 100 ns;
+               a      <= x"000002";
+               wait for 50 ns;
+               a      <= x"000004";
+               wait for 50 ns;
+               a      <= x"000006";
+               wait for 50 ns;
+               oe_n   <= '1';
+               ce     <= "111";
+
+               wait;
+       end process;
+
+       e_dut: entity work.sim_js28f128j3d75
+               generic map (FILENAME => "/home/ryan/Dropbox/Projects/VHDL/libraries/simulated/tests/test.bin")
+               port map (
+                       a      => a,
+                       d      => d,
+                       ce     => ce,
+                       rp_n   => rp_n,
+                       oe_n   => oe_n,
+                       we_n   => we_n,
+                       sts    => sts,
+                       byte_n => byte_n,
+                       vpen   => vpen
+               );
+end;