]> git.the-white-hart.net Git - vhdl/commitdiff
Organize simulation library
authorRyan <>
Wed, 17 Sep 2025 21:06:04 +0000 (16:06 -0500)
committerRyan <>
Wed, 17 Sep 2025 21:06:04 +0000 (16:06 -0500)
31 files changed:
libraries/simulated/assert_pulsewidth.vhd [deleted file]
libraries/simulated/assert_setuphold.vhd [deleted file]
libraries/simulated/assert_setuphold_vec.vhd [deleted file]
libraries/simulated/changed_within_t.vhd [deleted file]
libraries/simulated/changed_within_t_vec.vhd [deleted file]
libraries/simulated/delay_edges.vhd [deleted file]
libraries/simulated/dev_genericmem.vhd [new file with mode: 0644]
libraries/simulated/dev_js28f128j3d75.vhd [new file with mode: 0644]
libraries/simulated/dev_mt45w8mw16bgx.vhd [new file with mode: 0644]
libraries/simulated/js28f128j3d75.vhd [deleted file]
libraries/simulated/mt45w8mw16bgx.vhd [deleted file]
libraries/simulated/proto_ps2.vhd [new file with mode: 0644]
libraries/simulated/ps2_device.vhd [deleted file]
libraries/simulated/sim_js28f128j3d75.vhd [deleted file]
libraries/simulated/sim_utility.vhd [deleted file]
libraries/simulated/tests/test_attrs.vhd [deleted file]
libraries/simulated/tests/test_genericmem.vhd [new file with mode: 0644]
libraries/simulated/tests/test_js28f128j3d75.vhd
libraries/simulated/tests/test_mt45w8mw16bgx.vhd
libraries/simulated/tests/test_ps2.vhd [new file with mode: 0644]
libraries/simulated/tests/test_ps2_device.vhd [deleted file]
libraries/simulated/tests/test_simflash.vhd [deleted file]
libraries/simulated/tests/test_simmem.vhd [deleted file]
libraries/simulated/util_assert_pulsewidth.vhd [new file with mode: 0644]
libraries/simulated/util_assert_setuphold.vhd [new file with mode: 0644]
libraries/simulated/util_assert_setuphold_vec.vhd [new file with mode: 0644]
libraries/simulated/util_changed_within_t.vhd [new file with mode: 0644]
libraries/simulated/util_changed_within_t_vec.vhd [new file with mode: 0644]
libraries/simulated/util_delay_edges.vhd [new file with mode: 0644]
libraries/simulated/util_pkg.vhd [new file with mode: 0644]
libraries/simulated/wb_memory.vhd [deleted file]

diff --git a/libraries/simulated/assert_pulsewidth.vhd b/libraries/simulated/assert_pulsewidth.vhd
deleted file mode 100644 (file)
index a0741e3..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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
deleted file mode 100644 (file)
index 664d165..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-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
deleted file mode 100644 (file)
index e362d4b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-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
deleted file mode 100644 (file)
index 6a6b5a8..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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
deleted file mode 100644 (file)
index f94232c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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;
diff --git a/libraries/simulated/delay_edges.vhd b/libraries/simulated/delay_edges.vhd
deleted file mode 100644 (file)
index 5bf472e..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-
-
-entity delay_edges is
-       generic (
-               D_RISE: time := 0 ns;
-               D_FALL: time := 0 ns
-       );
-       port (
-               sig_in:  in  std_logic;
-               sig_out: out std_logic
-       );
-end delay_edges;
-
-architecture behavioral of delay_edges is
-       signal mask: std_logic;
-begin
-
-       process (sig_in)
-       begin
-               if mask = 'U' then
-                       mask <= sig_in;
-               end if;
-               if sig_in'event and sig_in = '1' then
-                       mask <= '1' after D_RISE;
-               end if;
-               if sig_in'event and sig_in = '0' then
-                       mask <= '0' after D_FALL;
-               end if;
-       end process;
-       sig_out <= mask;
-
-end behavioral;
-
diff --git a/libraries/simulated/dev_genericmem.vhd b/libraries/simulated/dev_genericmem.vhd
new file mode 100644 (file)
index 0000000..ed5b6d9
--- /dev/null
@@ -0,0 +1,49 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity genericmem is
+       generic (
+               INIT: std_logic_vector(32*8-1 downto 0) := (others => '0')
+       );
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+
+               cyc_i: in  std_logic;
+               stb_i: in  std_logic;
+               we_i:  in  std_logic;
+               ack_o: out std_logic;
+               adr_i: in  std_logic_vector(4 downto 0);
+               dat_i: in  std_logic_vector(7 downto 0);
+               dat_o: out std_logic_vector(7 downto 0)
+       );
+end genericmem;
+
+
+architecture behavioral of genericmem is
+
+       type mem_array_t is array(natural range <>) of std_logic_vector(7 downto 0);
+
+       signal mem_array: mem_array_t(31 downto 0);
+
+begin
+
+       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               for i in 0 to 31 loop
+                                       mem_array(31-i) <= INIT((i+1)*8-1 downto i*8);
+                               end loop;
+                       elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                               mem_array(to_integer(unsigned(adr_i))) <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       ack_o <= '1';
+       dat_o <= mem_array(to_integer(unsigned(adr_i)));
+
+end behavioral;
diff --git a/libraries/simulated/dev_js28f128j3d75.vhd b/libraries/simulated/dev_js28f128j3d75.vhd
new file mode 100644 (file)
index 0000000..5a0beb3
--- /dev/null
@@ -0,0 +1,719 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+use work.sim_utility.all;
+
+
+entity 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 js28f128j3d75;
+
+
+architecture behavioral of 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;
+
+       ----------------------------------------------------------------------------
+       -- Array value
+
+       shared variable flash_array: sparse_t;
+       signal array_word:      std_logic_vector(15 downto 0);
+
+       ----------------------------------------------------------------------------
+       -- Device Information
+
+       signal id_word:         std_logic_vector(15 downto 0);
+
+       ----------------------------------------------------------------------------
+       -- Status Register
+
+       signal sts_word:        std_logic_vector(15 downto 0);
+
+       signal sts_ready:       std_logic;
+       signal sts_susp_erase:  std_logic;
+       signal sts_error_erase: std_logic;
+       signal sts_error_prog:  std_logic;
+       signal sts_error_vpen:  std_logic;
+       signal sts_susp_prog:   std_logic;
+       signal sts_error_lock:  std_logic;
+
+       ----------------------------------------------------------------------------
+       -- CFI Query
+
+       signal cfi_word:        std_logic_vector(15 downto 0);
+
+       ----------------------------------------------------------------------------
+       -- Output value
+
+       type read_state_t is (R_ARRAY, R_ID, R_STATUS, R_CFI);
+       signal read_state_cur:  read_state_t;
+
+       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;
+
+       ----------------------------------------------------------------------------
+       -- Other registers
+
+       signal ecr_reg:         std_logic_vector(23 downto 0);
+       signal sts_config_reg:  std_logic_vector(7 downto 0);
+
+       ----------------------------------------------------------------------------
+       -- CUI state machine
+
+       type cui_state_t is (
+               C_IDLE,
+               C_PROG_STS,
+               C_PROG_OTP,
+               C_PROG_WORD,
+               C_PROG_BUF_COUNT,
+               C_PROG_BUF_DATA,
+               C_ERASE,
+               C_LOCK_OR_ECR
+       );
+
+       signal cui_state_cur:   cui_state_t;
+
+       ----------------------------------------------------------------------------
+       -- Write state machine
+
+       type wsm_state_t is (
+               W_IDLE,
+               W_PROG_OTP,
+               W_PROG_WORD,
+               W_PROG_WORD_FINISH,
+               W_PROG_BUF,
+               W_ERASE,
+               W_ERASE_FINISH,
+               W_LOCK,
+               W_UNLOCK
+       );
+
+       signal wsm_state_cur:   wsm_state_t;
+
+       signal trig_clrsts:     std_logic;
+       signal trig_susp:       std_logic;
+       signal trig_resume:     std_logic;
+       signal trig_cmderr:     std_logic;
+       signal trig_otp:        std_logic;
+       signal trig_word:       std_logic;
+       signal trig_buf:        std_logic;
+       signal trig_erase:      std_logic;
+       signal trig_lock:       std_logic;
+       signal trig_unlock:     std_logic;
+
+       signal prog_word:       std_logic_vector(15 downto 0);
+       signal prog_addr:       std_logic_vector(23 downto 0);
+       signal erase_addr:      std_logic_vector(23 downto 0);
+       signal lock_addr:       std_logic_vector(23 downto 0);
+       signal unlock_addr:     std_logic_vector(23 downto 0);
+
+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;
+
+       process (a, sts_ready)
+       begin
+               -- sts_ready is in the sensitivity list because flash_array is a variable and cannot be
+               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;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Device Information
+
+       id_word <= (others => 'X');
+
+
+       ----------------------------------------------------------------------------
+       -- Status Register
+
+       sts_word <= "00000000"      &
+                   sts_ready       &
+                   sts_susp_erase  &
+                   sts_error_erase &
+                   sts_error_prog  &
+                   sts_error_vpen  &
+                   sts_susp_prog   &
+                   sts_error_lock  &
+                               '0';
+
+
+       ----------------------------------------------------------------------------
+       -- CFI Query
+
+       cfi_word <= (others => 'X');
+
+
+       ----------------------------------------------------------------------------
+       -- Output value
+
+       with read_state_cur select word <=
+               array_word      when R_ARRAY,
+               id_word         when R_ID,
+               sts_word        when R_STATUS,
+               cfi_word        when R_CFI,
+               (others => 'X') when others;
+
+       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);
+
+
+       ----------------------------------------------------------------------------
+       -- Command User Interface (CUI)
+
+       process (rp_n, internal_we_n, a, d)
+       begin
+               if rp_n = '0' then
+                       cui_state_cur <= C_IDLE;
+                       trig_clrsts   <= '0';
+                       trig_susp     <= '0';
+                       trig_resume   <= '0';
+                       trig_cmderr   <= '0';
+                       trig_otp      <= '0';
+                       trig_word     <= '0';
+                       trig_buf      <= '0';
+                       trig_erase    <= '0';
+                       trig_lock     <= '0';
+                       trig_unlock   <= '0';
+               elsif falling_edge(internal_we_n) then
+                       trig_clrsts <= '0';
+                       trig_susp   <= '0';
+                       trig_resume <= '0';
+                       trig_cmderr <= '0';
+                       trig_otp    <= '0';
+                       trig_word   <= '0';
+                       trig_buf    <= '0';
+                       trig_erase  <= '0';
+                       trig_lock   <= '0';
+                       trig_unlock <= '0';
+               elsif rising_edge(internal_we_n) then
+                       case cui_state_cur is
+                               when C_IDLE =>
+                                       case d(7 downto 0) is
+                                               when x"FF" =>  -- Read Array
+                                                       read_state_cur <= R_ARRAY;
+
+                                               when x"70" =>  -- Read Status Register
+                                                       read_state_cur <= R_STATUS;
+
+                                               when x"90" =>  -- Read Identifier Codes/Device Information
+                                                       report "TODO: Read identifier codes and device information" severity error;
+                                                       read_state_cur <= R_ID;
+
+                                               when x"98" =>  -- CFI Query
+                                                       report "TODO: CFI query" severity error;
+                                                       read_state_cur <= R_CFI;
+
+                                               when x"50" =>  -- Clear Status Register
+                                                       trig_clrsts <= '1';
+
+                                               when x"B8" =>  -- Program STS Configuration Register
+                                                       cui_state_cur <= C_PROG_STS;
+
+                                               when x"c0" =>  -- Program OTP Register
+                                                       cui_state_cur <= C_PROG_OTP;
+
+                                               when x"40" | x"10" =>  -- Word/Byte Program
+                                                       cui_state_cur <= C_PROG_WORD;
+
+                                               when x"E8" =>  -- Buffered Program
+                                                       read_state_cur <= R_STATUS;
+                                                       cui_state_cur  <= C_PROG_BUF_COUNT;
+
+                                               when x"20" =>  -- Block Erase
+                                                       cui_state_cur <= C_ERASE;
+
+                                               when x"60" =>  -- Lock/Unlock Block or Program Enhanced Configuration Register
+                                                       cui_state_cur <= C_LOCK_OR_ECR;
+
+                                               when x"B0" =>  -- Program/Erase Suspend
+                                                       trig_susp      <= '1';
+                                                       read_state_cur <= R_STATUS;
+
+                                               when x"D0" =>  -- Program/Erase Resume
+                                                       trig_resume    <= '1';
+                                                       read_state_cur <= R_STATUS;
+
+                                               when others =>
+                                                       trig_cmderr <= '1';
+                                       end case;
+
+                               when C_PROG_STS =>
+                                       assert d(7 downto 0) = x"00" report "TODO: STS pulse config" severity error;
+                                       sts_config_reg <= d(7 downto 0);
+                                       cui_state_cur  <= C_IDLE;
+
+                               when C_PROG_OTP =>
+                                       trig_otp       <= '1';
+                                       read_state_cur <= R_STATUS;
+                                       cui_state_cur  <= C_IDLE;
+
+                               when C_PROG_WORD =>
+                                       prog_word      <= d;
+                                       prog_addr      <= a;
+                                       trig_word      <= '1';
+                                       read_state_cur <= R_STATUS;
+                                       cui_state_cur  <= C_IDLE;
+
+                               when C_PROG_BUF_COUNT =>
+                                       report "TODO: buffered program" severity error;
+                                       cui_state_cur <= C_PROG_BUF_DATA;
+
+                               when C_PROG_BUF_DATA =>
+                                       cui_state_cur <= C_IDLE;
+
+                               when C_ERASE =>
+                                       case d(7 downto 0) is
+                                               when x"D0" =>  -- Erase Block
+                                                       erase_addr     <= a;
+                                                       trig_erase     <= '1';
+                                                       read_state_cur <= R_STATUS;
+                                                       cui_state_cur  <= C_IDLE;
+
+                                               when others =>
+                                                       trig_cmderr    <= '1';
+                                                       cui_state_cur  <= C_IDLE;
+                                       end case;
+
+                               when C_LOCK_OR_ECR =>
+                                       case d(7 downto 0) is
+                                               when x"04" =>  -- Program Enhanced Configuration Register
+                                                       assert a = x"000000" report "TODO: ECR configuration" severity error;
+                                                       ecr_reg       <= a;
+                                                       cui_state_cur <= C_IDLE;
+
+                                               when x"01" =>  -- Lock Block
+                                                       lock_addr      <= a;
+                                                       trig_lock      <= '1';
+                                                       read_state_cur <= R_STATUS;
+                                                       cui_state_cur  <= C_IDLE;
+
+                                               when x"0D" =>  -- Unlock block
+                                                       unlock_addr    <= a;
+                                                       trig_unlock    <= '1';
+                                                       read_state_cur <= R_STATUS;
+                                                       cui_state_cur  <= C_IDLE;
+
+                                               when others =>
+                                                       trig_cmderr   <= '1';
+                                                       cui_state_cur <= C_IDLE;
+                                       end case;
+
+                               when others =>
+                                       report "Unhandled CUI state" severity error;
+                       end case;
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Write State Machine (WSM)
+
+       process (wsm_state_cur, rp_n, vpen,
+                trig_clrsts, trig_susp, trig_resume, trig_cmderr, trig_otp,
+                trig_word, trig_buf, trig_erase, trig_lock, trig_unlock)
+       begin
+               if rp_n = '0' then
+                       wsm_state_cur   <= W_IDLE;
+
+                       -- Clear all error and status bits and initialize to ready
+                       sts_ready       <= '1';
+                       sts_susp_erase  <= '0';
+                       sts_error_erase <= '0';
+                       sts_error_prog  <= '0';
+                       sts_error_vpen  <= '0';
+                       sts_susp_prog   <= '0';
+                       sts_error_lock  <= '0';
+               else
+                       -- Clearing error status bits should probably be allowed in any WSM state
+                       if rising_edge(trig_clrsts) then
+                               sts_error_erase <= '0';
+                               sts_error_prog  <= '0';
+                               sts_error_vpen  <= '0';
+                               sts_error_lock  <= '0';
+                       end if;
+
+                       -- Command errors should probably be latched in any WSM state
+                       if rising_edge(trig_cmderr) then
+                               sts_error_erase <= '1';
+                               sts_error_prog  <= '1';
+                       end if;
+
+                       -- Handle each state's logic
+                       -- Keep in mind this is not only run when wsm_state_cur changes,
+                       -- but also rerun when vpen or any trigger signal change
+                       sts_ready <= '0';
+                       case wsm_state_cur is
+                               when W_IDLE =>
+                                       sts_ready <= '1';
+
+                                       -- After these state transitions, this process will be rerun
+                                       -- in the next delta cycle with the new state
+                                       if rising_edge(trig_resume) then
+                                               report "TODO: resume program/erase operation" severity error;
+                                       elsif rising_edge(trig_otp) then
+                                               wsm_state_cur <= W_PROG_OTP;
+                                       elsif rising_edge(trig_word) then
+                                               wsm_state_cur <= W_PROG_WORD;
+                                       elsif rising_edge(trig_buf) then
+                                               wsm_state_cur <= W_PROG_BUF;
+                                       elsif rising_edge(trig_erase) then
+                                               wsm_state_cur <= W_ERASE;
+                                       elsif rising_edge(trig_lock) then
+                                               wsm_state_cur <= W_LOCK;
+                                       elsif rising_edge(trig_unlock) then
+                                               wsm_state_cur <= W_UNLOCK;
+                                       end if;
+
+                               when W_PROG_OTP =>
+                                       report "TODO: program OTP" severity error;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when W_PROG_WORD =>
+                                       -- TODO: check lock bits
+
+                                       -- If vpen is low or drops during programming, halt with error
+                                       if vpen = '0' then
+                                               sts_error_prog <= '1';
+                                               sts_error_vpen <= '1';
+                                               wsm_state_cur  <= W_IDLE;
+                                       end if;
+
+                                       -- If suspend command is sent, pause programming
+                                       if rising_edge(trig_susp) then
+                                               report "TODO: program suspend" severity error;
+                                       end if;
+
+                                       -- Invalidate the data and schedule programming completion
+                                       if byte_n = '0' then
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr)), "XXXXXXXX");
+                                       else
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '0')), "XXXXXXXX");
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '1')), "XXXXXXXX");
+                                       end if;
+                                       wsm_state_cur <= W_PROG_WORD_FINISH after T_WHQV3;
+
+                               when W_PROG_WORD_FINISH =>
+                                       if byte_n = '0' then
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr)), prog_word(7 downto 0));
+                                       else
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '0')), prog_word(15 downto 8));
+                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '1')), prog_word( 7 downto 0));
+                                       end if;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when W_PROG_BUF =>
+                                       report "TODO: buffered program" severity error;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when W_ERASE =>
+                                       -- TODO: check lock bits
+
+                                       -- If vpen is low or drops during erase, halt with error
+                                       if vpen = '0' then
+                                               sts_error_erase <= '1';
+                                               sts_error_vpen  <= '1';
+                                               wsm_state_cur   <= W_IDLE;
+                                       end if;
+
+                                       -- If suspend command is sent, pause programming
+                                       if rising_edge(trig_susp) then
+                                               report "TODO: erase suspend" severity error;
+                                       end if;
+
+                                       -- Invalidate the data and schedule the erase completion
+                                       for i in 0 to 16#1ffff# loop
+                                               sparse_set(flash_array, to_integer(unsigned(erase_addr(erase_addr'high downto 16)) & to_unsigned(i, 17)), x"FF");
+                                       end loop;
+                                       wsm_state_cur <= W_ERASE_FINISH after T_WHQV4;
+
+                               when W_ERASE_FINISH =>
+                                       for i in 0 to 16#1ffff# loop
+                                               sparse_set(flash_array, to_integer(unsigned(erase_addr(erase_addr'high downto 16)) & to_unsigned(i, 17)), x"FF");
+                                       end loop;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when W_LOCK =>
+                                       report "TODO: program lock bits" severity error;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when W_UNLOCK =>
+                                       report "TODO: erase lock bits" severity error;
+                                       wsm_state_cur <= W_IDLE;
+
+                               when others =>
+                                       report "Unhandled WSM state" severity error;
+
+                       end case;
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- 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/dev_mt45w8mw16bgx.vhd b/libraries/simulated/dev_mt45w8mw16bgx.vhd
new file mode 100644 (file)
index 0000000..c1e176a
--- /dev/null
@@ -0,0 +1,333 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+
+
+entity mt45w8mw16bgx is
+       port (
+               a:     in    std_logic_vector(22 downto 0);
+               dq:    inout std_logic_vector(15 downto 0);
+               clk:   in    std_logic;
+               adv_n: in    std_logic;
+               cre:   in    std_logic;
+               ce_n:  in    std_logic;
+               oe_n:  in    std_logic;
+               we_n:  in    std_logic;
+               lb_n:  in    std_logic;
+               ub_n:  in    std_logic;
+               rwait: out   std_logic
+       );
+end mt45w8mw16bgx;
+
+
+architecture behavioral of mt45w8mw16bgx is
+
+       -- Timings (-708/80MHz)
+       constant T_AA:   time := 70 ns;   -- Address access time
+       constant T_AADV: time := 70 ns;   -- ADV# access time
+       constant T_ABA:  time := 46.5 ns; --
+       constant T_ACLK: time := 9 ns;    --
+       constant T_APA:  time := 20 ns;   -- Page access time
+       constant T_AS:   time := 0 ns;    --
+       constant T_AVH:  time := 2 ns;    -- Address hold from ADV# HIGH
+       constant T_AVS:  time := 5 ns;    -- Address setup to ADV# HIGH
+       constant T_AW:   time := 70 ns;   --
+       constant T_BA:   time := 70 ns;   -- LB#/UB# access time
+       constant T_BHZ:  time := 8 ns;    -- LB#/UB# disable to DQ High-Z output
+       constant T_BLZ:  time := 10 ns;   -- LB#/UB# enable to Low-Z output
+       constant T_BOE:  time := 20 ns;   --
+       constant T_BW:   time := 70 ns;   --
+       constant T_CBPH: time := 6 ns;    --
+       constant T_CEM:  time := 4 us;    -- Maximum CE# pulse width
+       constant T_CEW:  time := 7.5 ns;  -- CE# LOW to WAIT valid
+       constant T_CLK:  time := 12.5 ns; --
+       constant T_CO:   time := 70 ns;   -- Chip select access time
+       constant T_CPH:  time := 5 ns;    --
+       constant T_CSP:  time := 4 ns;    --
+       constant T_CVS:  time := 7 ns;    -- CE# LOW to ADV# HIGH
+       constant T_CW:   time := 70 ns;   --
+       constant T_DH:   time := 0 ns;    --
+       constant T_DPD:  time := 10 us;   --
+       constant T_DPDX: time := 10 us;   --
+       constant T_DW:   time := 20 ns;   --
+       constant T_HD:   time := 2 ns;    --
+       constant T_HZ:   time := 8 ns;    -- Chip disable to DQ and WAIT High-Z output
+       constant T_KHKL: time := 1.8 ns;  --
+       constant T_KHTL: time := 9 ns;    --
+       constant T_KOH:  time := 2 ns;    --
+       constant T_KP:   time := 4 ns;    --
+       constant T_LZ:   time := 10 ns;   -- Chip enable to Low-Z output
+       constant T_OE:   time := 20 ns;   -- Output enable to valid output
+       constant T_OH:   time := 5 ns;    -- Output hold from address change
+       constant T_OHZ:  time := 8 ns;    -- Output disable to DQ High-Z output
+       constant T_OLZ:  time := 3 ns;    -- Output enable to Low-Z output
+       constant T_OW:   time := 5 ns;    --
+       constant T_PC:   time := 20 ns;   -- Page READ cycle time
+       constant T_PU:   time := 150 us;  --
+       constant T_RC:   time := 70 ns;   -- READ cycle time
+       constant T_SP:   time := 3 ns;    --
+       constant T_VP:   time := 5 ns;    -- ADV# pulse width LOW
+       constant T_VS:   time := 70 ns;   --
+       constant T_WC:   time := 70 ns;   --
+       constant T_WHZ:  time := 8 ns;    --
+       constant T_WP:   time := 45 ns;   --
+       constant T_WPH:  time := 10 ns;   --
+       constant T_WR:   time := 0 ns;    --
+
+       -- Internal types
+       type word_array is array(natural range <>) of std_logic_vector(15 downto 0);
+
+       -- Data array read signals
+       signal array_reg:  word_array(15 downto 0) := (others => (others => '1'));
+       signal array_word: std_logic_vector(15 downto 0);
+       signal read_word:  std_logic_vector(15 downto 0);
+       signal xmask_addr: std_logic;
+       signal xmask_ce_n: std_logic;
+       signal xmask_oe_n: std_logic;
+       signal xmask_ub_n: std_logic;
+       signal xmask_lb_n: std_logic;
+       signal xmask_hi:   std_logic;
+       signal xmask_lo:   std_logic;
+       signal zmask_ce_n: std_logic;
+       signal zmask_oe_n: std_logic;
+       signal zmask_ub_n: std_logic;
+       signal zmask_lb_n: std_logic;
+       signal zmask_hi:   std_logic;
+       signal zmask_lo:   std_logic;
+
+       -- Data array write signals
+       signal internal_we_ub_n: std_logic;
+       signal internal_we_lb_n: std_logic;
+
+       -- Configuration registers
+       signal bcr_reg:  std_logic_vector(15 downto 0) := x"9d1f";
+       signal rcr_reg:  std_logic_vector(15 downto 0) := x"0010";
+       signal didr_reg: std_logic_vector(15 downto 0) := x"0343";
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- Asynchronous array reads
+
+       -- Look up value from memory array, delay to allow for hold time
+       array_word <= array_reg(to_integer(unsigned(a))) after T_HZ;  -- T_OHZ, T_BHZ
+
+       -- Generate mask for periods of time with invalid data
+       -- FIXME: this may break hold time
+       xm_addr: entity work.changed_within_t_vec generic map (T => T_AA, T_HOLD => T_OH)
+               port map (sig_in => a, sig_out => xmask_addr);
+       xm_ce: entity work.changed_within_t generic map (T => T_CO, T_HOLD => T_HZ)
+               port map (sig_in => ce_n, sig_out => xmask_ce_n);
+       xm_oe: entity work.changed_within_t generic map (T => T_OE, T_HOLD => T_OHZ)
+               port map (sig_in => oe_n, sig_out => xmask_oe_n);
+       xm_ub: entity work.changed_within_t generic map (T => T_BA, T_HOLD => T_BHZ)
+               port map (sig_in => ub_n, sig_out => xmask_ub_n);
+       xm_lb: entity work.changed_within_t generic map (T => T_BA, T_HOLD => T_BHZ)
+               port map (sig_in => lb_n, sig_out => xmask_lb_n);
+       xmask_hi <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_ub_n;
+       xmask_lo <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_lb_n;
+
+       -- Invalidate data for some periods of time
+       read_word(15 downto 8) <= (others => 'X') when xmask_hi = '1' else array_word(15 downto 8);
+       read_word( 7 downto 0) <= (others => 'X') when xmask_lo = '1' else array_word( 7 downto 0);
+
+       -- Generate mask for periods of time where data bus is high-z
+       zm_ce: entity work.delay_edges generic map (D_RISE => T_HZ,  D_FALL => T_LZ)
+               port map (sig_in => ce_n, sig_out => zmask_ce_n);
+       zm_oe: entity work.delay_edges generic map (D_RISE => T_OHZ, D_FALL => T_OLZ)
+               port map (sig_in => oe_n, sig_out => zmask_oe_n);
+       zm_ub: entity work.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
+               port map (sig_in => ub_n, sig_out => zmask_ub_n);
+       zm_lb: entity work.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
+               port map (sig_in => lb_n, sig_out => zmask_lb_n);
+       zmask_hi <= zmask_ce_n or zmask_oe_n or zmask_ub_n;
+       zmask_lo <= zmask_ce_n or zmask_oe_n or zmask_lb_n;
+
+       -- Generate output data signals
+       dq(15 downto 8) <= (others => 'Z') when zmask_hi = '1' or we_n = '0'
+                                          else read_word(15 downto 8);
+       dq( 7 downto 0) <= (others => 'Z') when zmask_lo = '1' or we_n = '0'
+                                          else read_word( 7 downto 0);
+
+
+       ----------------------------------------------------------------------------
+       -- Asynchronous array writes
+
+       -- Internal signals to trigger latching of data
+       internal_we_ub_n <= ce_n or we_n or ub_n;
+       internal_we_lb_n <= ce_n or we_n or lb_n;
+
+       -- Latch data on rising edge of write signal
+       process (internal_we_ub_n, internal_we_lb_n)
+       begin
+               -- Changing (or tristating) DQ at the same moment as the write rising-edge should
+               -- be allowed (T_DH, data hold time, = 0 ns), but this breaks in ISim 14.7, hence
+               -- the 1 ps delay.
+               if rising_edge(internal_we_ub_n) then
+                       array_reg(to_integer(unsigned(a)))(15 downto 8) <= dq(15 downto 8)'delayed(1 ps);
+               end if;
+
+               if rising_edge(internal_we_lb_n) then
+                       array_reg(to_integer(unsigned(a)))( 7 downto 0) <= dq( 7 downto 0)'delayed(1 ps);
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Check for erroneous usage
+
+       -- TODO: check T_AVH, T_AVS, T_CVS, T_VP, T_AS
+       assert adv_n = '0'
+               report "ADV# not yet supported"
+               severity error;
+
+       assert clk = '0'
+               report "Synchronous operation not yet supported"
+               severity error;
+
+
+       -- Should (probably) not assert oe_n and we_n at the same time
+       process (oe_n, we_n)
+       begin
+               assert not (oe_n = '0' and we_n = '0')
+                       report "OE# and WE# are active simultaneously (write takes precedence)"
+                       severity warning;
+       end process;
+
+       -- Maximum CE# pulse width (to allow internal refresh)
+       process (ce_n)
+       begin
+               if rising_edge(ce_n) then
+                       assert ce_n'last_event < T_CEM
+                               report "T_CEM not met (Maximum CE# pulse width)"
+                               severity error;
+               end if;
+       end process;
+
+
+       -- Write recovery time (address hold time)
+       process (a, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if a'event then
+                       if internal_we_ub_n = '1' then
+                               assert internal_we_ub_n'last_event >= T_WR
+                                       report "T_WR not met (Write recovery time)"
+                                       severity error;
+                       end if;
+                       if internal_we_lb_n = '1' then
+                               assert internal_we_lb_n'last_event >= T_WR
+                                       report "T_WR not met (Write recovery time)"
+                                       severity error;
+                       end if;
+               end if;
+       end process;
+
+       -- Address valid to end of WRITE
+       process (a, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert a'last_event >= T_AW
+                               report "T_AW not met (Address valid to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert a'last_event >= T_AW
+                               report "T_AW not met (Address valid to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Chip enable to end of WRITE
+       process (ce_n, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert ce_n'delayed'delayed'last_event >= T_CW
+                               report "T_CW not met (Chip enable to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert ce_n'delayed'delayed'last_event >= T_CW
+                               report "T_CW not met (Chip enable to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- CE# HIGH between subsequent async operations
+       process (ce_n)
+       begin
+               if falling_edge(ce_n) then
+                       assert ce_n'delayed'last_event >= T_CPH
+                               report "T_CPH not met (CE# HIGH between subsequent async operations)"
+                               severity error;
+               end if;
+       end process;
+
+       -- LB#/UB# select to end of WRITE
+       process (ub_n, lb_n, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert ub_n'last_event >= T_BW
+                               report "T_BW not met (UB# select to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert lb_n'last_event >= T_BW
+                               report "T_BW not met (LB# select to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- WRITE pulse width
+       process (we_n, internal_we_ub_n, internal_we_lb_n)
+               variable t: time;
+       begin
+               -- Datasheet specifies WE# HIGH pulse width strictly on WE# signal
+               -- FIXME: should this be strictly on internal we signals?
+               if falling_edge(we_n) then
+                       assert we_n'delayed'last_event >= T_WPH
+                               report "T_WPH not met (WRITE pulse width HIGH)"
+                               severity error;
+               end if;
+               -- Datasheet specifies WE# LOW pulse width against whatever signal triggers write
+               -- FIXME: should this be strictly on internal we signals?
+               if rising_edge(internal_we_ub_n) then
+                       assert we_n'delayed'delayed'last_event >= T_WP
+                               report "T_WP not met (WRITE pulse width)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       t := we_n'delayed'delayed'last_event;
+                       assert we_n'delayed'delayed'last_event >= T_WP
+                               report "T_WP not met (WRITE pulse width)" & time'image(t)
+                               severity error;
+               end if;
+       end process;
+
+       -- Data setup and hold times
+       process (dq, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert dq(15 downto 8)'delayed'delayed'last_event >= T_DW
+                               report "T_DW not met (Data upper byte WRITE setup time)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert dq(7 downto 0)'delayed'delayed'last_event >= T_DW
+                               report "T_DW not met (Data lower byte WRITE setup time)"
+                               severity error;
+               end if;
+               if dq(15 downto 8)'event then
+                       assert internal_we_ub_n'last_event >= T_DH
+                               report "T_DH not met (Data upper byte HOLD from WRITE time)"
+                               severity error;
+               end if;
+               if dq(7 downto 0)'event then
+                       assert internal_we_lb_n'last_event >= T_DH
+                               report "T_DH not met (Data lower byte HOLD from WRITE time)"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/js28f128j3d75.vhd b/libraries/simulated/js28f128j3d75.vhd
deleted file mode 100644 (file)
index c3ace7b..0000000
+++ /dev/null
@@ -1,1142 +0,0 @@
---------------------------------------------------------------------------------
--- Notes:
---
--- In ISim 14.7, it appears that the 'stable attribute doesn't work
--- Checking if 'last_event >= T_x doesn't seem to work with no previous events
---------------------------------------------------------------------------------
-
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-
-
-entity js28f128j3d75 is
-       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 js28f128j3d75;
-
-
-architecture behavioral of 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 ns;     -- R12, BYTE# to Output Delay
-       constant T_FHQV: time := 1 ns;     -- R12, (Same as Above)
-       constant T_FLQZ: time := 1 ns;     -- 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)
-
-       pure function block_index_of(address: std_logic_vector(a'high downto 0)) return integer is
-       begin
-               return to_integer(unsigned(address(address'high downto 17)));
-       end function;
-
-       pure function word_index_of(address: std_logic_vector(a'high downto 0)) return integer is
-       begin
-               return to_integer(unsigned(address(16 downto 1)));
-       end function;
-
-       -- Internal types
-       type word_array is array(natural range <>) of std_logic_vector(15 downto 0);
-       type block_array is array(natural range<>) of word_array(65535 downto 0);
-
-       type cui_state is (
-               C_IDLE,
-               C_PECR,
-               C_POTP,
-               C_STSC,
-               C_PROG,
-               C_BUF_PROG_COUNT,
-               C_BUF_PROG_DATA,
-               C_BUF_PROG_CONF,
-               C_ERASE,
-               C_LOCK
-       );
-       type read_state is (R_ARRAY, R_ID, R_STATUS, R_CFI);
-       type wsm_state is (W_READY, W_PROG, W_BUF_PROG, W_ERASE, W_LOCK_PROG, W_LOCK_ERASE, W_OTP_PROG, W_RESUME);
-
-       -- Internal signals
-       signal internal_ce_n: std_logic;  -- Composite chip-enable from multiple CE input signals
-       signal internal_we_n: std_logic;  -- Composite write-latch from WE# and CEx
-
-       signal array_pre_word: std_logic_vector(15 downto 0);
-       signal array_word: std_logic_vector(15 downto 0);
-       signal cfi_pre_word: std_logic_vector(15 downto 0);
-       signal cfi_word: std_logic_vector(15 downto 0);
-       signal id_pre_word: std_logic_vector(15 downto 0);
-       signal id_word: std_logic_vector(15 downto 0);
-       signal status_pre_word: std_logic_vector(15 downto 0);
-       signal status_word: std_logic_vector(15 downto 0);
-       signal read_pre_word: std_logic_vector(15 downto 0);
-       signal read_word: std_logic_vector(15 downto 0);
-       signal last_word: std_logic_vector(15 downto 0);
-
-       -- Delay mask signals for array reads
-       signal addr_hi_change_4: std_logic;
-       signal addr_hi_change_8: std_logic;
-       signal addr_hi_change: std_logic;
-       signal is_sensing: std_logic;
-       signal addr_lo_change_4: std_logic;
-       signal addr_lo_change_8: std_logic;
-       signal addr_lo_change: std_logic;
-       signal oe_change_array: std_logic;
-
-       -- Delay mask signals for non-array reads
-       signal addr_change: std_logic;
-       signal oe_change_nonarray: std_logic;
-
-       -- Delay mask signals for all reads
-       signal rp_change: std_logic;
-       signal ce_change: std_logic;
-       signal byte_change: std_logic;
-
-       -- Delay mask signals for tristate logic
-       signal ce_n_gate: std_logic;
-       signal oe_n_gate: std_logic;
-       signal final_oe_n: std_logic;
-
-       signal cur_cui_state: cui_state;
-       signal cur_read_state: read_state;
-       signal cur_wsm_state: wsm_state;
-       signal next_wsm_state: wsm_state;
-       signal wsm_trigger: std_logic;
-
-       -- Status signals
-       signal sts_ready: std_logic := '1';
-       signal sts_susp_erase: std_logic := '0';
-       signal sts_error_code: std_logic_vector(1 downto 0) := "00";
-       signal sts_error_volt: std_logic := '0';
-       signal sts_susp_prog: std_logic := '0';
-       signal sts_error_lock: std_logic := '0';
-       signal sts_config: std_logic_vector(7 downto 0) := (others => '0');
-
-       -- Flash array data
-       -- Only two blocks to save the simulator
-       signal flash_array: block_array(1 downto 0) := (others => (others => (others => '1')));
-
-       -- Lock operation state
-       signal lock_address: std_logic_vector(a'range);
-       signal lock_reg: std_logic_vector(127 downto 0) := (others => '0');
-
-       -- Program array operation state
-       signal prog_count: integer;
-       signal prog_address: std_logic_vector(a'range);
-       signal prog_buf: word_array(31 downto 0);
-       signal prog_word: std_logic_vector(15 downto 0);
-
-       -- Erase operation state
-       signal erase_address: std_logic_vector(a'range);
-
-       -- Enhanced Configuration Register
-       signal ecr_reg: std_logic_vector(16 downto 0);
-       signal ecr_8word_page: std_logic;
-
-       -- OTP Protection Register
-       signal otp_prog_offset: std_logic_vector(a'range);
-       signal otp_prog_data: std_logic_vector(15 downto 0);
-       signal plr_reg: std_logic_vector(15 downto 0) := x"FFFE";
-       signal otp_reg: word_array(7 downto 0) := (others => (others => '1'));
-
-       -- Debug
-       signal ce_stable: boolean;
-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';
-
-
-       ----------------------------------------------------------------------------
-       -- Reset logic
-
-       reset: process (rp_n)
-       begin
-               if rp_n = '0' then
-                       -- reset all state machines
-                       -- cur_cui_state <= C_IDLE;
-                       -- cur_read_state <= R_ARRAY;
-                       -- cur_wsm_state <= W_READY;
-
-                       -- reset status register and status config
-                       --sts_ready <= '1';
-                       --sts_susp_erase <= '0';
-                       --sts_error_code <= "00";
-                       --sts_error_volt <= '0';
-                       --sts_susp_prog <= '0';
-                       --sts_error_lock <= '0';
-                       sts_config <= (others => '0');
-
-                       -- reset ECR to 4-word page mode
-                       ecr_reg <= (others => '0');
-
-                       -- abort all program, erase, lock-bit operations
-               end if;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- Read logic
-
-       -- Look up value from flash array
-       array_pre_word <= flash_array(block_index_of(a))(word_index_of(a));
-
-       -- Generate mask signal representing sensing delay
-       -- ce_change is incorporated here because the datasheet says ce falling triggers sensing,
-       -- but the same delay also listed for all other reads, so it is incorporated again below.
-       addr_hi_change_4 <= '1' when (a(a'high downto 3) /= a(a'high downto 3)'delayed(T_AVQV)) or
-                                    (a(a'high downto 3)'last_event < T_AVQV)
-                               else '0';
-       addr_hi_change_8 <= '1' when (a(a'high downto 4) /= a(a'high downto 4)'delayed(T_AVQV)) or
-                                    (a(a'high downto 4)'last_event < T_AVQV)
-                               else '0';
-       with ecr_8word_page select addr_hi_change <=
-               addr_hi_change_8 when '1',
-               addr_hi_change_4 when others;
-       ce_change <= '1' when (internal_ce_n /= internal_ce_n'delayed(T_ELQV)) or
-                             (internal_ce_n'last_event < T_ELQV)
-                        else '0';
-       is_sensing <= addr_hi_change or ce_change;
-
-       -- Generate mask signal representing word select delay
-       addr_lo_change_4 <= '1' when (a(2 downto 0) /= a(2 downto 0)'delayed(T_APA)) or
-                                    (a(2 downto 0)'last_event < T_APA)
-                               else '0';
-       addr_lo_change_8 <= '1' when (a(3 downto 0) /= a(3 downto 0)'delayed(T_APA)) or
-                                    (a(3 downto 0)'last_event < T_APA)
-                               else '0';
-       with ecr_8word_page select addr_lo_change <=
-               addr_lo_change_8 when '1',
-               addr_lo_change_4 when others;
-
-       -- Generate mask signal representing OE# delay for array reads
-       oe_change_array <= '1' when (oe_n /= oe_n'delayed(T_GLQV_A)) or
-                                   (oe_n'last_event < T_GLQV_A)
-                              else '0';
-
-       array_word <= (others => 'X') when is_sensing = '1' or
-                                          addr_lo_change = '1' or
-                                                                          oe_change_array = '1' else array_pre_word;
-
-
-       -- Generate mask signals representing delays for non-array reads (and use ce_change from above)
-       -- Not sure if T_AVAV applies to non-array reads, so apply it anyway just in case
-       addr_change <= '1' when (a /= a'delayed(T_AVQV)) or
-                               (a'last_event < T_AVQV)
-                          else '0';
-       oe_change_nonarray <= '1' when (oe_n /= oe_n'delayed(T_GLQV_NA)) or
-                                      (oe_n'last_event < T_GLQV_NA)
-                                 else '0';
-
-       -- Look up CFI query value
-       do_cfi_read: process (a)
-       begin
-               case a(8 downto 1) is
-                       when x"10" => cfi_pre_word <= x"0051";
-                       when x"11" => cfi_pre_word <= x"0052";
-                       when x"12" => cfi_pre_word <= x"0059";
-                       when others => cfi_pre_word <= (others => '0');
-               end case;
-       end process;
-
-       cfi_word <= (others => 'X') when addr_change = '1' or
-                                       oe_change_nonarray = '1' else cfi_pre_word;
-
-       -- Look up ID register value
-       do_id_read: process (a)
-       begin
-               case to_integer(unsigned(a(16 downto 1))) is
-                       -- Device ID
-                       when 16#0001# =>
-                               id_pre_word <= x"0018";
-
-                       -- Lock bit for this block
-                       when 16#0002# =>
-                               id_pre_word <= (others => '0');
-                               id_pre_word(0) <= lock_reg(to_integer(unsigned(a(a'high downto 17))));
-
-                       -- OTP protection lock register
-                       when 16#0080# => id_pre_word <= plr_reg;
-
-                       -- OTP register
-                       when 16#0081# to 16#88# =>
-                               id_pre_word <= otp_reg(to_integer(unsigned(a)) - 16#81#);
-
-                       when others => id_pre_word <= (others => 'X');
-               end case;
-       end process;
-
-       id_word <= (others => 'X') when addr_change = '1' or
-                                       oe_change_nonarray = '1' else id_pre_word;
-
-       -- Compute status register value
-       do_status: process (internal_ce_n, oe_n,
-                           sts_ready, sts_susp_erase, sts_error_code, sts_error_volt, sts_susp_prog, sts_error_lock)
-       begin
-               -- Status register value is latched on CEx or OE# going low
-               if falling_edge(internal_ce_n) or (falling_edge(oe_n) and internal_ce_n = '0') then
-                       status_pre_word(15 downto 8) <= x"00";
-                       status_pre_word(7) <= sts_ready;
-                       status_pre_word(6) <= sts_susp_erase;
-                       status_pre_word(5 downto 4) <= sts_error_code;
-                       status_pre_word(3) <= sts_error_volt;
-                       status_pre_word(2) <= sts_susp_prog;
-                       status_pre_word(1) <= sts_error_lock;
-                       status_pre_word(0) <= '0';
-               end if;
-       end process;
-
-       status_word <= (others => 'X') when addr_change = '1' or
-                                           oe_change_nonarray = '1' else status_pre_word;
-
-
-       -- Choose a value based on read mode
-       do_read: process (cur_read_state, array_word, cfi_word, id_word, status_word)
-       begin
-               case cur_read_state is
-                       when R_ARRAY =>
-                               read_pre_word <= array_word;
-                       when R_ID =>
-                               read_pre_word <= id_word;
-                       when R_STATUS =>
-                               read_pre_word <= status_word;
-                       when R_CFI =>
-                               read_pre_word <= cfi_word;
-                       when others =>
-                               read_pre_word <= (others => 'X');
-               end case;
-       end process;
-
-       -- Generate mask signals for delays that apply to any read
-       rp_change <= '1' when (rp_n /= rp_n'delayed(T_PHQV)) or
-                             (rp_n'last_event < T_PHQV)
-                        else '0';
-       byte_change <= '1' when (byte_n /= byte_n'delayed(T_FLQV)) or
-                               (byte_n'last_event < T_FLQV)
-                          else '0';
-       -- ce_change from above
-
-       -- Handle byte/word modes
-       byte_select: process (read_pre_word, a(0), byte_n)
-       begin
-               if byte_n = '0' then
-                       -- Oops, extra tristating to handle here instead of in tristate logic
-                       -- Good thing is is sim-only!
-                       read_word(15 downto 8) <= (others => 'Z');
-                       if a(0) = '0' then
-                               read_word(7 downto 0) <= read_pre_word(7 downto 0);
-                       else
-                               read_word(7 downto 0) <= read_pre_word(15 downto 8);
-                       end if;
-               else
-                       read_word <= read_pre_word;
-               end if;
-       end process;
-
-       -- Equal parts gin, green chartreuse, maraschino liqueur, lime; shaken and served up
-       last_word <= (others => 'X') when rp_change = '1' or
-                                         byte_change = '1' else read_word;
-
-
-       -- Handle delays between CEx/OE# and transitions between low/high-Z output
-       -- The front and back porch have different delay specs, so to get the front porch
-       -- with one delay and the back porch with the other we may need to either and or
-       -- or the delayed signals depending on which delay is longer.
-       delayed_ce_n_a: if T_ELQX > T_EHQZ generate
-               ce_n_gate <= internal_ce_n'delayed(T_ELQX) or internal_ce_n'delayed(T_EHQZ);
-       end generate;
-       delayed_ce_n_b: if T_ELQX <= T_EHQZ generate
-               ce_n_gate <= internal_ce_n'delayed(T_ELQX) and internal_ce_n'delayed(T_EHQZ);
-       end generate;
-       delayed_oe_n_a: if T_GLQX > T_GHQZ generate
-               oe_n_gate <= oe_n'delayed(T_GLQX) or oe_n'delayed(T_GHQZ);
-       end generate;
-       delayed_oe_n_b: if T_GLQX <= T_GHQZ generate
-               oe_n_gate <= oe_n'delayed(T_GLQX) and oe_n'delayed(T_GHQZ);
-       end generate;
-       final_oe_n <= ce_n_gate or oe_n_gate;
-       d <= (others => 'Z') when rp_n = '0' or final_oe_n = '1' else last_word'delayed(T_OH);
-
-
-       ecr_8word_page <= ecr_reg(13);
-
-
-       ----------------------------------------------------------------------------
-       -- Write logic
-
-       -- Latch CUI on rising edge of we_n or ce_n
-       -- CUI logic is responsible for:
-       -- * checking sts_ready before triggering
-       -- * checking sts_susp_prog/erase before triggering if relevant
-       -- * setting sts_susp_prog/erase when suspending
-       -- * setting next_wsm_state and wsm_trigger to begin operations
-       cui: process (internal_we_n, a, d, rp_n,
-                     sts_ready, sts_susp_prog, sts_susp_erase, cur_wsm_state)
-               variable prog_i: integer;
-       begin
-               if rp_n = '0' then
-                       cur_cui_state <= C_IDLE;
-                       cur_read_state <= R_ARRAY;
-                       wsm_trigger <= '0';
-               elsif rising_edge(internal_we_n) then
-                       case cur_cui_state is
-                               when C_IDLE =>
-                                       wsm_trigger <= '0';
-
-                                       -- Idle, not waiting for data or confirmation commands
-                                       case d(7 downto 0) is
-                                               -- Program Enhanced Configuration Register (Setup)
-                                               --when x"60" =>
-                                               --      cur_cui_state <= C_PECR;
-
-                                               -- Program OTP Register (Setup)
-                                               when x"c0" =>
-                                                       cur_cui_state <= C_POTP;
-
-                                               -- Clear Status Register
-                                               when x"50" =>
-                                                       -- Only clear error flags, not status
-                                                       sts_error_code <= "00";
-                                                       sts_error_volt <= '0';
-                                                       sts_error_lock <= '0';
-                                                       cur_read_state <= R_STATUS;
-
-                                               -- Program STS Configuration Register (Setup)
-                                               when x"b8" =>
-                                                       -- FIXME: enter R_STATUS?
-                                                       cur_cui_state <= C_STSC;
-
-                                               -- Read Array
-                                               when x"ff" =>
-                                                       assert sts_ready = '1'
-                                                               report "Entering read-array mode while WSM is busy"
-                                                               severity warning;
-                                                       cur_read_state <= R_ARRAY;
-
-                                               -- Read Status Register
-                                               when x"70" =>
-                                                       cur_read_state <= R_STATUS;
-
-                                               -- Read Identifier Codes
-                                               when x"90" =>
-                                                       cur_read_state <= R_ID;
-
-                                               -- CFI Query
-                                               when x"98" =>
-                                                       cur_read_state <= R_CFI;
-
-                                               -- Word/Byte Program (Setup)
-                                               when x"40" | x"10" =>
-                                                       cur_cui_state <= C_PROG;
-
-                                               -- Buffered Program (Setup)
-                                               when x"e8" =>
-                                                       cur_read_state <= R_STATUS;
-                                                       if sts_ready = '1' then
-                                                               cur_cui_state <= C_BUF_PROG_COUNT;
-                                                       end if;
-
-                                               -- BLock Erase (Setup)
-                                               when x"20" =>
-                                                       cur_cui_state <= C_ERASE;
-
-                                               -- Program/Erase Suspend
-                                               when x"b0" =>
-                                                       if sts_ready = '1' then
-                                                               -- No-op
-                                                               null;
-                                                       elsif cur_wsm_state = W_PROG or cur_wsm_state = W_BUF_PROG then
-                                                               -- Suspend program operation
-                                                               sts_susp_prog <= '1';
-                                                       elsif cur_wsm_state = W_ERASE then
-                                                               -- Suspend erase operation
-                                                               sts_susp_erase <= '1';
-                                                       end if;
-                                                       -- Suspend does not change read mode
-
-                                               -- Program/Erase Resume
-                                               when x"d0" =>
-                                                       next_wsm_state <= W_RESUME;
-                                                       wsm_trigger <= '1';
-                                                       cur_read_state <= R_STATUS;
-
-                                               -- Lock/Unlock Block (Setup) and Program Enhanced Configuration Register (Setup)
-                                               when x"60" =>
-                                                       cur_read_state <= R_STATUS;
-                                                       cur_cui_state <= C_LOCK;
-
-                                               when others =>
-                                                       sts_error_code <= "11";
-                                       end case;
-
-                               -- Started Program Enhanced Configuration Register command
-                               when C_PECR =>
-                                       case d(7 downto 0) is
-                                               when x"04" =>
-                                                       ecr_reg <= a(ecr_reg'high downto 0);
-                                                       -- Datasheet says it returns to read-array mode...
-                                                       -- FIXME: Does it just not change the mode?
-                                                       cur_read_state <= R_ARRAY;
-
-                                               when others =>
-                                                       sts_error_code <= "11";
-                                       end case;
-                                       cur_cui_state <= C_IDLE;
-
-                               -- Started Program OTP Register command
-                               when C_POTP =>
-                                       if sts_ready = '0' then
-                                               -- Not allowed while WSM is busy
-                                               sts_error_code <= "11";
-                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
-                                               -- Not allowed while program/erase suspended
-                                               sts_error_code <= "11";
-                                       else
-                                               -- Latch address and data and trigger WSM
-                                               otp_prog_offset <= a;
-                                               otp_prog_data <= d;
-                                               next_wsm_state <= W_OTP_PROG;
-                                               wsm_trigger <= '1';
-                                       end if;
-
-                                       cur_read_state <= R_STATUS;
-                                       cur_cui_state <= C_IDLE;
-
-                               -- Started Program STS Configuration Register command
-                               when C_STSC =>
-                                       if sts_ready = '0' then
-                                               -- Not allowed while WSM is busy
-                                               sts_error_code <= "11";
-                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
-                                               -- Not allowed while program/erase suspended
-                                               sts_error_code <= "11";
-                                       elsif (d and x"00fc") /= x"0000" then
-                                               -- Invalid configuration value
-                                               sts_error_code <= "11";
-                                       else
-                                               assert d(1) = '0' and d(0) = '0'
-                                                       report "TODO: STS pulse config"
-                                                       severity warning;
-                                               sts_config <= d(7 downto 0);
-                                       end if;
-                                       cur_read_state <= R_STATUS;
-                                       cur_cui_state <= C_IDLE;
-
-                               -- Started Word/Byte Program command
-                               when C_PROG =>
-                                       if sts_ready = '0' then
-                                               -- Not allowed while WSM is busy
-                                               sts_error_code <= "11";
-                                       elsif sts_susp_prog = '1' then
-                                               -- Not allowed while program suspended
-                                               sts_error_code <= "11";
-                                       else
-                                               -- Latch address and data and trigger WSM
-                                               prog_address <= a;
-                                               prog_word <= d;
-                                               next_wsm_state <= W_PROG;
-                                               wsm_trigger <= '1';
-                                       end if;
-                                       cur_read_state <= R_STATUS;
-                                       cur_cui_state <= C_IDLE;
-
-                               -- Started Buffer Program command
-                               when C_BUF_PROG_COUNT =>
-                                       if sts_ready = '0' then
-                                               -- sts_ready already checked in previous state, but just in case
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       elsif sts_susp_prog = '1' then
-                                               -- Do not allow while programming operation is suspended
-                                               -- Should this check have been rolled into the status check in
-                                               -- the previous state?  Or should this check be done in the previous
-                                               -- state?  TODO: check on hardware
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       elsif (d(7 downto 0) and x"e0") /= x"0000" then
-                                               -- Word count must be <= 31
-                                               report "Buffer program sent with word count greater than 31" severity warning;
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       else
-                                               -- Latch start address and word count
-                                               prog_address <= a;
-                                               prog_count <= to_integer(unsigned(d));
-                                               prog_i := to_integer(unsigned(d));
-                                               cur_cui_state <= C_BUF_PROG_DATA;
-                                       end if;
-
-                               when C_BUF_PROG_DATA =>
-                                       -- Latch data into buffer at address
-                                       prog_buf(to_integer(unsigned(a(5 downto 1)))) <= d;
-                                       if prog_i = 0 then
-                                               cur_cui_state <= C_BUF_PROG_CONF;
-                                       else
-                                               prog_i := prog_i - 1;
-                                       end if;
-
-                               when C_BUF_PROG_CONF =>
-                                       if d(7 downto 0) /= x"d0" then
-                                               -- Failed to confirm write, abort
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       else
-                                               -- Trigger WSM
-                                               next_wsm_state <= W_BUF_PROG;
-                                               wsm_trigger <= '1';
-                                               cur_cui_state <= C_IDLE;
-                                       end if;
-
-                               -- Started Block Erase command
-                               when C_ERASE =>
-                                       -- TODO: not allowed when erase or program suspended
-                                       if sts_ready = '0' then
-                                               -- Not allowed while WSM is busy
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
-                                               -- Not allowed while program/erase suspended
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       elsif d(7 downto 0) /= x"d0" then
-                                               -- Failed to confirm erase
-                                               sts_error_code <= "11";
-                                               cur_cui_state <= C_IDLE;
-                                       else
-                                               -- Latch block address and trigger WSM
-                                               erase_address <= a;
-                                               next_wsm_state <= W_ERASE;
-                                               wsm_trigger <= '1';
-                                               cur_cui_state <= C_IDLE;
-                                       end if;
-
-                               -- Started Lock/Unlock Block command
-                               when C_LOCK =>
-                                       -- Read state already set to R_STATUS in previous cycle
-                                       case d(7 downto 0) is
-                                               -- Program Enhanced Configuration Register
-                                               when x"04" =>
-                                                       ecr_reg <= a(ecr_reg'high downto 0);
-                                                       -- Datasheet says it returns to read-array mode
-                                                       cur_read_state <= R_ARRAY;
-
-                                               -- Lock Block
-                                               when x"01" =>
-                                                       -- address in a
-                                                       if sts_ready = '0' then
-                                                               -- Cannot program when WSM is busy
-                                                               sts_error_code <= "11";
-                                                       elsif sts_susp_erase = '1' or sts_susp_prog = '1' then
-                                                               -- Cannot program when erase or program suspended
-                                                               sts_error_code <= "11";
-                                                       else
-                                                               lock_address <= a;
-                                                               next_wsm_state <= W_LOCK_PROG;
-                                                               wsm_trigger <= '1';
-                                                       end if;
-
-                                               -- Unlock Blocks
-                                               when x"d0" =>
-                                                       if sts_ready = '0' then
-                                                               -- Cannot erase when WSM is busy
-                                                               sts_error_code <= "11";
-                                                       elsif sts_susp_erase = '1' or sts_susp_prog = '1' then
-                                                               -- Cannot erase when erase or program suspended
-                                                               sts_error_code <= "11";
-                                                       else
-                                                               next_wsm_state <= W_LOCK_ERASE;
-                                                               wsm_trigger <= '1';
-                                                       end if;
-
-                                               when others =>
-                                                       sts_error_code <= "11";
-                                       end case;
-                                       cur_cui_state <= C_IDLE;
-
-                               when others =>
-                                       cur_cui_state <= C_IDLE;
-                       end case;
-               end if;
-       end process;
-
-       -- Write state machine is responsible for checking:
-       -- * Vpen state
-       -- * valid offset/address
-       -- * lock protection
-       -- * checking wsm_trigger and next_wsm_state to begin operations
-       -- * checking sts_susp_* state to suspend operations
-       wsm: process (rp_n, vpen, sts_susp_erase, sts_susp_prog, wsm_trigger, cur_wsm_state, next_wsm_state)
-               variable otp_prog_idx: integer;
-               variable prog_buf_addr: integer;
-       begin
-               if rp_n = '0' then
-                       -- TODO: hold ready low for some time after reset
-                       cur_wsm_state <= W_READY;
-                       sts_ready <= '1';
-                       sts_susp_erase <= '0';
-                       sts_error_code <= "00";
-                       sts_error_volt <= '0';
-                       sts_susp_prog <= '0';
-                       sts_error_lock <= '0';
-               end if;
-
-               sts_ready <= '0';  -- Default to not-ready
-
-               -- Process triggers from the CUI FSM
-               if rising_edge(wsm_trigger) then
-                       cur_wsm_state <= next_wsm_state;
-               end if;
-
-               case cur_wsm_state is
-                       when W_READY =>
-                               sts_ready <= '1';  -- Ready status only in ready state
-
-                       when W_PROG =>
-                               -- Abort with voltage error if Vpen drops
-                               sts_ready <= '0';
-                               if vpen = '0' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Suspend programming operation when signaled
-                               if sts_susp_prog = '1' then
-                                       cur_wsm_state <= W_READY after T_WHRH1;  -- T_EHRH1
-                               end if;
-                               -- Check lock protection bit for block
-                               if lock_reg(block_index_of(prog_address)) = '1' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_lock <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               flash_array(block_index_of(prog_address))(word_index_of(prog_address)) <=
-                                       flash_array(block_index_of(prog_address))(word_index_of(prog_address))
-                                       and prog_word;
-                               cur_wsm_state <= W_READY after T_WHQV3;  -- T_EHQV3
-
-                       when W_BUF_PROG =>
-                               -- Abort with voltage error if Vpen drops
-                               if vpen = '0' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Suspend programming operation when signaled
-                               if sts_susp_prog = '1' then
-                                       cur_wsm_state <= W_READY after T_WHRH1;  -- T_EHRH1
-                               end if;
-                               -- Check lock protection bit for block
-                               if lock_reg(block_index_of(prog_address)) = '1' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_lock <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- TODO: write zeroes
-                               prog_buf_addr := to_integer(unsigned(prog_address));
-                               for addr in prog_buf_addr to prog_buf_addr + prog_count + 1 loop
-                                       flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24)))) <=
-                                               flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24))))
-                                               and prog_buf(addr mod 32);
-                               end loop;
-                               -- TODO: get correct timing
-                               cur_wsm_state <= W_READY after T_WHQV3;  -- T_EHQV3
-
-                       when W_ERASE =>
-                               -- Abort with voltage error if Vpen drops
-                               if vpen = '0' then
-                                       sts_error_code(1) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Suspend erase operation when signaled
-                               if sts_susp_erase = '1' then
-                                       cur_wsm_state <= W_READY after T_WHRH;  -- T_EHRH
-                               end if;
-                               -- Check lock protection bit for block
-                               if lock_reg(block_index_of(erase_address)) = '1' then
-                                       sts_error_code(1) <= '1';
-                                       sts_error_lock <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Erase block and return to ready
-                               flash_array(block_index_of(erase_address)) <= (others => (others => '1'));
-                               cur_wsm_state <= W_READY after T_WHQV4;  -- T_EHQV4
-
-                       when W_LOCK_PROG =>
-                               -- Abort with voltage error if Vpen drops
-                               if vpen = '0' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Write lock bit and return to ready
-                               lock_reg(block_index_of(lock_address)) <= '1';
-                               cur_wsm_state <= W_READY after T_WHQV5; -- T_EHQV5
-
-                       when W_LOCK_ERASE =>
-                               -- Abort with voltage error if Vpen drops
-                               if vpen = '0' then
-                                       sts_error_code(1) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Erase all lock bits and return to ready
-                               lock_reg <= (others => '0');
-                               cur_wsm_state <= W_READY after T_WHQV6;  -- T_EHQV6
-
-                       when W_OTP_PROG =>
-                               -- Abort with voltage error if Vpen drops
-                               if vpen = '0' then
-                                       sts_error_code(0) <= '1';
-                                       sts_error_volt <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Check for out-of-range address
-                               otp_prog_idx := to_integer(unsigned(otp_prog_offset(otp_prog_offset'high downto 1)));
-                               if otp_prog_idx < 16#80# or otp_prog_idx > 16#88# then
-                                       sts_error_code(0) <= '1';
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-                               -- Check for protection bits
-                               if otp_prog_idx >= 16#81# and otp_prog_idx <= 16#84# then
-                                       if plr_reg(0) = '0' then
-                                               sts_error_code(0) <= '1';
-                                               sts_error_lock <= '1';
-                                               cur_wsm_state <= W_READY after T_STS;
-                                       end if;
-                               end if;
-                               if otp_prog_idx >= 16#85# and otp_prog_idx <= 16#88# then
-                                       if plr_reg(1) = '0' then
-                                               sts_error_code(0) <= '1';
-                                               sts_error_lock <= '1';
-                                               cur_wsm_state <= W_READY after T_STS;
-                                       end if;
-                               end if;
-                               -- Write zeroes and return to ready
-                               if otp_prog_idx = 16#80# then
-                                       plr_reg <= plr_reg and otp_prog_data;
-                               else
-                                       otp_prog_idx := otp_prog_idx - 16#81#;
-                                       otp_reg(otp_prog_idx) <= otp_reg(otp_prog_idx) and otp_prog_data;
-                               end if;
-                               cur_wsm_state <= W_READY after T_WHQV5;  -- Best guess for timing
-
-                       when W_RESUME =>
-                               if sts_susp_prog = '1' then
-                                       -- Precedence goes to resuimg programming operation
-                                       sts_susp_prog <= '0';
-                                       cur_wsm_state <= W_PROG after T_STS;
-                               elsif sts_susp_erase = '1' then
-                                       -- Resume erase operation if no programming operation pending
-                                       sts_susp_erase <= '0';
-                                       cur_wsm_state <= W_ERASE after T_STS;
-                               else
-                                       -- Nothing to resume, return to ready
-                                       cur_wsm_state <= W_READY after T_STS;
-                               end if;
-
-                       when others =>
-                               cur_wsm_state <= W_READY after T_STS;
-               end case;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- 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
-       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;
-
-       -- WE# and CEx cannot become active less than T_PHWL/EL after RP# goes high
-       W1: process (rp_n, we_n, internal_ce_n)
-       begin
-               if rp_n = '1' then
-                       if falling_edge(we_n) then
-                               assert rp_n'last_event >= T_PHWL
-                                       report "T_PHWL not met (RP# High Recovery to WE# Going Low)"
-                                       severity error;
-                       end if;
-                       if falling_edge(internal_ce_n) then
-                               assert rp_n'last_event >= T_PHEL
-                                       report "T_PHEL not met (RP# High Recovery to CEx Going Low)"
-                                       severity error;
-                       end if;
-               end if;
-       end process;
-
-       -- CEx and WE# may have set-up timing constraints relative to each other
-       W2: process (we_n, internal_ce_n)
-       begin
-               if falling_edge(we_n) and internal_ce_n = '0' then
-                       assert internal_ce_n'last_event >= T_ELWL
-                               report "T_ELWL not met (CEx Low to WE# Going Low)"
-                               severity error;
-               end if;
-               if falling_edge(internal_ce_n) and we_n = '0' then
-                       assert we_n'last_event >= T_WLEL
-                               report "T_WLEL not met (WE# Low to CEx Going Low)"
-                               severity error;
-               end if;
-       end process;
-
-       -- WE# has a minimum low pulse width
-       W3: process (we_n)
-       begin
-               if rising_edge(we_n) then
-                       --assert we_n'last_event >= T_WP
-                       assert we_n'delayed(T_WP) = '0'
-                               report "T_WP not met (Write Pulse Width)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Data-in set-up time (no need to use internal_we_n here)
-       W4: process (internal_we_n, d)
-       begin
-               if rising_edge(internal_we_n) then
-                       -- T_DVEH and T_DVWH are the same in the datasheet, so they probably
-                       -- refer to the internal write-latch signal composed of CEx and WE#.
-                       -- Checking against the internal signal prevents us from checking
-                       -- timing between our own output and CEx during write cycles.
-                       assert d'delayed'last_event >= T_DVWH  -- Also T_DVEH
-                               report "T_DVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)" & time'image(d'last_event)
-                               severity error;
-               end if;
-               --if rising_edge(we_n) then
-               --      assert d'last_event >= T_DVWH
-               --              report "T_DVWH not met (Data Setup to WE# Going High)"
-               --              severity error;
-               --end if;
-               --if rising_edge(internal_ce_n) then
-               --      -- Have seen some false-positives where d'last_event = 0 ps, but d'event = False
-               --      -- Delaying 1 ps will get past any event in the current simulation cycle
-               --      assert d'delayed(1 ps)'last_event >= T_DVEH
-               --              report "T_DVEH not met (Data Setup to CEx Going High)"
-               --              severity error;
-               --end if;
-       end process;
-
-       -- Address set-up time (no need to use internal_we_n here)
-       W5: process (internal_we_n, a)
-       begin
-               if rising_edge(internal_we_n) then
-                       -- T_AVEH and T_AVWH are the same in the datasheet, so they probably
-                       -- refer to the internal write-latch signal composed of CEx and WE#.
-                       -- Checking against the internal signal prevents us from checking
-                       -- timing against CEx during read cycles.
-                       assert a'last_event >= T_AVWH  -- Also T_DVEH
-                               report "T_AVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)"
-                               severity error;
-               end if;
-               --if rising_edge(we_n) then
-               --      assert a'last_event >= T_AVWH
-               --              report "T_AVWH not met (Address Setup to WE# Going High)"
-               --              severity error;
-               --end if;
-               --if rising_edge(internal_ce_n) then
-               --      assert a'last_event >= T_AVEH
-               --              report "T_AVEH not met (Address Setup to CEx Going High)"
-               --              severity error;
-               --end if;
-       end process;
-
-       -- CEx and WE# may have hold timing constraints relative to each other
-       W6: process (we_n, internal_ce_n)
-       begin
-               if rising_edge(we_n) and internal_ce_n = '1' then
-                       assert internal_ce_n'last_event >= T_EHWH
-                               report "T_EHWH not met (WE# Hold from CEx High)"
-                               severity error;
-               end if;
-               if rising_edge(internal_ce_n) and we_n = '1' then
-                       assert we_n'last_event >= T_WHEH
-                               report "T_WHEH not met (CEx Hold from WE# High)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Data-in hold time
-       W7: process (internal_we_n, d)
-       begin
-               if d'event and internal_we_n = '1' then
-                       assert internal_we_n'last_event >= T_WHDX  -- T_EHDX
-                               report "T_W/EHDX not met (Data Hold from WE# (CEx) High)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Address hold time
-       W8: process (internal_we_n, a)
-       begin
-               if a'event and internal_we_n = '1' then
-                       assert internal_we_n'last_event >= T_WHAX  -- T_EHAX
-                               report "T_W/EHAX not met (Address Hold from WE# (CEx) High)"
-                               severity error;
-               end if;
-       end process;
-
-       -- WE# has a minimum high pulse width
-       W9: process (we_n)
-       begin
-               if falling_edge(we_n) then
-                       --assert we_n'last_event >= T_WPH
-                       assert we_n'delayed(T_WPH) = '1'
-                               report "T_WPH not met (Write Pulse Width High)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Vpen set-up time before write latch (don't need to use internal_we_n here)
-       W11: process (we_n, internal_ce_n, vpen)
-       begin
-               if rising_edge(we_n) and vpen = '1' then
-                       assert vpen'last_event >= T_VPWH
-                               report "T_VPWH not met (Vpen Setup to WE# High)"
-                               severity error;
-               end if;
-               if rising_edge(internal_ce_n) and vpen = '1' then
-                       assert vpen'last_event >= T_VPEH
-                               report "T_VPEH not met (Vpen Setup to CEx High)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Write recovery before Read
-       W12: process (internal_we_n, oe_n)
-       begin
-               if falling_edge(oe_n) and internal_we_n = '1' then
-                       assert internal_we_n'last_event >= T_WHGL  -- T_EHGL
-                               report "T_W/EHGL not met (Write Recovery before Read)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Vpen hold time
-       W15: process (vpen)
-       begin
-               -- TODO: T_QVVL never shown on any waveform diagrams
-       end process;
-
-       -- RP# pulse time
-       P1: process (rp_n)
-       begin
-               if rising_edge(rp_n) then
-                       -- If the simulations tarts with rp_n = '0' then last_event won't work
-                       -- assert rp_n'last_event >= T_PLPH
-                       assert rp_n'delayed(T_PLPH) = '0'
-                               report "T_PLPH not met (RP# Pulse Low Time)"
-                               severity error;
-               end if;
-       end process;
-
-end behavioral;
diff --git a/libraries/simulated/mt45w8mw16bgx.vhd b/libraries/simulated/mt45w8mw16bgx.vhd
deleted file mode 100644 (file)
index fbcbb9c..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-
-library simulated;
-
-
-entity mt45w8mw16bgx is
-       port (
-               a:     in    std_logic_vector(22 downto 0);
-               dq:    inout std_logic_vector(15 downto 0);
-               clk:   in    std_logic;
-               adv_n: in    std_logic;
-               cre:   in    std_logic;
-               ce_n:  in    std_logic;
-               oe_n:  in    std_logic;
-               we_n:  in    std_logic;
-               lb_n:  in    std_logic;
-               ub_n:  in    std_logic;
-               rwait: out   std_logic
-       );
-end mt45w8mw16bgx;
-
-
-architecture behavioral of mt45w8mw16bgx is
-
-       -- Timings (-708/80MHz)
-       constant T_AA:   time := 70 ns;   -- Address access time
-       constant T_AADV: time := 70 ns;   -- ADV# access time
-       constant T_ABA:  time := 46.5 ns; --
-       constant T_ACLK: time := 9 ns;    --
-       constant T_APA:  time := 20 ns;   -- Page access time
-       constant T_AS:   time := 0 ns;    --
-       constant T_AVH:  time := 2 ns;    -- Address hold from ADV# HIGH
-       constant T_AVS:  time := 5 ns;    -- Address setup to ADV# HIGH
-       constant T_AW:   time := 70 ns;   --
-       constant T_BA:   time := 70 ns;   -- LB#/UB# access time
-       constant T_BHZ:  time := 8 ns;    -- LB#/UB# disable to DQ High-Z output
-       constant T_BLZ:  time := 10 ns;   -- LB#/UB# enable to Low-Z output
-       constant T_BOE:  time := 20 ns;   --
-       constant T_BW:   time := 70 ns;   --
-       constant T_CBPH: time := 6 ns;    --
-       constant T_CEM:  time := 4 us;    -- Maximum CE# pulse width
-       constant T_CEW:  time := 7.5 ns;  -- CE# LOW to WAIT valid
-       constant T_CLK:  time := 12.5 ns; --
-       constant T_CO:   time := 70 ns;   -- Chip select access time
-       constant T_CPH:  time := 5 ns;    --
-       constant T_CSP:  time := 4 ns;    --
-       constant T_CVS:  time := 7 ns;    -- CE# LOW to ADV# HIGH
-       constant T_CW:   time := 70 ns;   --
-       constant T_DH:   time := 0 ns;    --
-       constant T_DPD:  time := 10 us;   --
-       constant T_DPDX: time := 10 us;   --
-       constant T_DW:   time := 20 ns;   --
-       constant T_HD:   time := 2 ns;    --
-       constant T_HZ:   time := 8 ns;    -- Chip disable to DQ and WAIT High-Z output
-       constant T_KHKL: time := 1.8 ns;  --
-       constant T_KHTL: time := 9 ns;    --
-       constant T_KOH:  time := 2 ns;    --
-       constant T_KP:   time := 4 ns;    --
-       constant T_LZ:   time := 10 ns;   -- Chip enable to Low-Z output
-       constant T_OE:   time := 20 ns;   -- Output enable to valid output
-       constant T_OH:   time := 5 ns;    -- Output hold from address change
-       constant T_OHZ:  time := 8 ns;    -- Output disable to DQ High-Z output
-       constant T_OLZ:  time := 3 ns;    -- Output enable to Low-Z output
-       constant T_OW:   time := 5 ns;    --
-       constant T_PC:   time := 20 ns;   -- Page READ cycle time
-       constant T_PU:   time := 150 us;  --
-       constant T_RC:   time := 70 ns;   -- READ cycle time
-       constant T_SP:   time := 3 ns;    --
-       constant T_VP:   time := 5 ns;    -- ADV# pulse width LOW
-       constant T_VS:   time := 70 ns;   --
-       constant T_WC:   time := 70 ns;   --
-       constant T_WHZ:  time := 8 ns;    --
-       constant T_WP:   time := 45 ns;   --
-       constant T_WPH:  time := 10 ns;   --
-       constant T_WR:   time := 0 ns;    --
-
-       -- Internal types
-       type word_array is array(natural range <>) of std_logic_vector(15 downto 0);
-
-       -- Data array read signals
-       signal array_reg:  word_array(15 downto 0) := (others => (others => '1'));
-       signal array_word: std_logic_vector(15 downto 0);
-       signal read_word:  std_logic_vector(15 downto 0);
-       signal xmask_addr: std_logic;
-       signal xmask_ce_n: std_logic;
-       signal xmask_oe_n: std_logic;
-       signal xmask_ub_n: std_logic;
-       signal xmask_lb_n: std_logic;
-       signal xmask_hi:   std_logic;
-       signal xmask_lo:   std_logic;
-       signal zmask_ce_n: std_logic;
-       signal zmask_oe_n: std_logic;
-       signal zmask_ub_n: std_logic;
-       signal zmask_lb_n: std_logic;
-       signal zmask_hi:   std_logic;
-       signal zmask_lo:   std_logic;
-
-       -- Data array write signals
-       signal internal_we_ub_n: std_logic;
-       signal internal_we_lb_n: std_logic;
-
-       -- Configuration registers
-       signal bcr_reg:  std_logic_vector(15 downto 0) := x"9d1f";
-       signal rcr_reg:  std_logic_vector(15 downto 0) := x"0010";
-       signal didr_reg: std_logic_vector(15 downto 0) := x"0343";
-
-begin
-
-       ----------------------------------------------------------------------------
-       -- Asynchronous array reads
-
-       -- Look up value from memory array, delay to allow for hold time
-       array_word <= array_reg(to_integer(unsigned(a))) after T_HZ;  -- T_OHZ, T_BHZ
-
-       -- Generate mask for periods of time with invalid data
-       -- FIXME: this may break hold time
-       xm_addr: entity simulated.changed_within_t_vec generic map (T => T_AA, T_HOLD => T_OH)
-               port map (sig_in => a, sig_out => xmask_addr);
-       xm_ce: entity simulated.changed_within_t generic map (T => T_CO, T_HOLD => T_HZ)
-               port map (sig_in => ce_n, sig_out => xmask_ce_n);
-       xm_oe: entity simulated.changed_within_t generic map (T => T_OE, T_HOLD => T_OHZ)
-               port map (sig_in => oe_n, sig_out => xmask_oe_n);
-       xm_ub: entity simulated.changed_within_t generic map (T => T_BA, T_HOLD => T_BHZ)
-               port map (sig_in => ub_n, sig_out => xmask_ub_n);
-       xm_lb: entity simulated.changed_within_t generic map (T => T_BA, T_HOLD => T_BHZ)
-               port map (sig_in => lb_n, sig_out => xmask_lb_n);
-       xmask_hi <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_ub_n;
-       xmask_lo <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_lb_n;
-
-       -- Invalidate data for some periods of time
-       read_word(15 downto 8) <= (others => 'X') when xmask_hi = '1' else array_word(15 downto 8);
-       read_word( 7 downto 0) <= (others => 'X') when xmask_lo = '1' else array_word( 7 downto 0);
-
-       -- Generate mask for periods of time where data bus is high-z
-       zm_ce: entity simulated.delay_edges generic map (D_RISE => T_HZ,  D_FALL => T_LZ)
-               port map (sig_in => ce_n, sig_out => zmask_ce_n);
-       zm_oe: entity simulated.delay_edges generic map (D_RISE => T_OHZ, D_FALL => T_OLZ)
-               port map (sig_in => oe_n, sig_out => zmask_oe_n);
-       zm_ub: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
-               port map (sig_in => ub_n, sig_out => zmask_ub_n);
-       zm_lb: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
-               port map (sig_in => lb_n, sig_out => zmask_lb_n);
-       zmask_hi <= zmask_ce_n or zmask_oe_n or zmask_ub_n;
-       zmask_lo <= zmask_ce_n or zmask_oe_n or zmask_lb_n;
-
-       -- Generate output data signals
-       dq(15 downto 8) <= (others => 'Z') when zmask_hi = '1' or we_n = '0'
-                                          else read_word(15 downto 8);
-       dq( 7 downto 0) <= (others => 'Z') when zmask_lo = '1' or we_n = '0'
-                                          else read_word( 7 downto 0);
-
-
-       ----------------------------------------------------------------------------
-       -- Asynchronous array writes
-
-       -- Internal signals to trigger latching of data
-       internal_we_ub_n <= ce_n or we_n or ub_n;
-       internal_we_lb_n <= ce_n or we_n or lb_n;
-
-       -- Latch data on rising edge of write signal
-       process (internal_we_ub_n, internal_we_lb_n)
-       begin
-               -- Changing (or tristating) DQ at the same moment as the write rising-edge should
-               -- be allowed (T_DH, data hold time, = 0 ns), but this breaks in ISim 14.7, hence
-               -- the 1 ps delay.
-               if rising_edge(internal_we_ub_n) then
-                       array_reg(to_integer(unsigned(a)))(15 downto 8) <= dq(15 downto 8)'delayed(1 ps);
-               end if;
-
-               if rising_edge(internal_we_lb_n) then
-                       array_reg(to_integer(unsigned(a)))( 7 downto 0) <= dq( 7 downto 0)'delayed(1 ps);
-               end if;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- Check for erroneous usage
-
-       -- TODO: check T_AVH, T_AVS, T_CVS, T_VP, T_AS
-       assert adv_n = '0'
-               report "ADV# not yet supported"
-               severity error;
-
-       assert clk = '0'
-               report "Synchronous operation not yet supported"
-               severity error;
-
-
-       -- Should (probably) not assert oe_n and we_n at the same time
-       process (oe_n, we_n)
-       begin
-               assert not (oe_n = '0' and we_n = '0')
-                       report "OE# and WE# are active simultaneously (write takes precedence)"
-                       severity warning;
-       end process;
-
-       -- Maximum CE# pulse width (to allow internal refresh)
-       process (ce_n)
-       begin
-               if rising_edge(ce_n) then
-                       assert ce_n'last_event < T_CEM
-                               report "T_CEM not met (Maximum CE# pulse width)"
-                               severity error;
-               end if;
-       end process;
-
-
-       -- Write recovery time (address hold time)
-       process (a, internal_we_ub_n, internal_we_lb_n)
-       begin
-               if a'event then
-                       if internal_we_ub_n = '1' then
-                               assert internal_we_ub_n'last_event >= T_WR
-                                       report "T_WR not met (Write recovery time)"
-                                       severity error;
-                       end if;
-                       if internal_we_lb_n = '1' then
-                               assert internal_we_lb_n'last_event >= T_WR
-                                       report "T_WR not met (Write recovery time)"
-                                       severity error;
-                       end if;
-               end if;
-       end process;
-
-       -- Address valid to end of WRITE
-       process (a, internal_we_ub_n, internal_we_lb_n)
-       begin
-               if rising_edge(internal_we_ub_n) then
-                       assert a'last_event >= T_AW
-                               report "T_AW not met (Address valid to end of WRITE)"
-                               severity error;
-               end if;
-               if rising_edge(internal_we_lb_n) then
-                       assert a'last_event >= T_AW
-                               report "T_AW not met (Address valid to end of WRITE)"
-                               severity error;
-               end if;
-       end process;
-
-       -- Chip enable to end of WRITE
-       process (ce_n, internal_we_ub_n, internal_we_lb_n)
-       begin
-               if rising_edge(internal_we_ub_n) then
-                       assert ce_n'delayed'delayed'last_event >= T_CW
-                               report "T_CW not met (Chip enable to end of WRITE)"
-                               severity error;
-               end if;
-               if rising_edge(internal_we_lb_n) then
-                       assert ce_n'delayed'delayed'last_event >= T_CW
-                               report "T_CW not met (Chip enable to end of WRITE)"
-                               severity error;
-               end if;
-       end process;
-
-       -- CE# HIGH between subsequent async operations
-       process (ce_n)
-       begin
-               if falling_edge(ce_n) then
-                       assert ce_n'delayed'last_event >= T_CPH
-                               report "T_CPH not met (CE# HIGH between subsequent async operations)"
-                               severity error;
-               end if;
-       end process;
-
-       -- LB#/UB# select to end of WRITE
-       process (ub_n, lb_n, internal_we_ub_n, internal_we_lb_n)
-       begin
-               if rising_edge(internal_we_ub_n) then
-                       assert ub_n'last_event >= T_BW
-                               report "T_BW not met (UB# select to end of WRITE)"
-                               severity error;
-               end if;
-               if rising_edge(internal_we_lb_n) then
-                       assert lb_n'last_event >= T_BW
-                               report "T_BW not met (LB# select to end of WRITE)"
-                               severity error;
-               end if;
-       end process;
-
-       -- WRITE pulse width
-       process (we_n, internal_we_ub_n, internal_we_lb_n)
-               variable t: time;
-       begin
-               -- Datasheet specifies WE# HIGH pulse width strictly on WE# signal
-               -- FIXME: should this be strictly on internal we signals?
-               if falling_edge(we_n) then
-                       assert we_n'delayed'last_event >= T_WPH
-                               report "T_WPH not met (WRITE pulse width HIGH)"
-                               severity error;
-               end if;
-               -- Datasheet specifies WE# LOW pulse width against whatever signal triggers write
-               -- FIXME: should this be strictly on internal we signals?
-               if rising_edge(internal_we_ub_n) then
-                       assert we_n'delayed'delayed'last_event >= T_WP
-                               report "T_WP not met (WRITE pulse width)"
-                               severity error;
-               end if;
-               if rising_edge(internal_we_lb_n) then
-                       t := we_n'delayed'delayed'last_event;
-                       assert we_n'delayed'delayed'last_event >= T_WP
-                               report "T_WP not met (WRITE pulse width)" & time'image(t)
-                               severity error;
-               end if;
-       end process;
-
-       -- Data setup and hold times
-       process (dq, internal_we_ub_n, internal_we_lb_n)
-       begin
-               if rising_edge(internal_we_ub_n) then
-                       assert dq(15 downto 8)'delayed'delayed'last_event >= T_DW
-                               report "T_DW not met (Data upper byte WRITE setup time)"
-                               severity error;
-               end if;
-               if rising_edge(internal_we_lb_n) then
-                       assert dq(7 downto 0)'delayed'delayed'last_event >= T_DW
-                               report "T_DW not met (Data lower byte WRITE setup time)"
-                               severity error;
-               end if;
-               if dq(15 downto 8)'event then
-                       assert internal_we_ub_n'last_event >= T_DH
-                               report "T_DH not met (Data upper byte HOLD from WRITE time)"
-                               severity error;
-               end if;
-               if dq(7 downto 0)'event then
-                       assert internal_we_lb_n'last_event >= T_DH
-                               report "T_DH not met (Data lower byte HOLD from WRITE time)"
-                               severity error;
-               end if;
-       end process;
-
-end behavioral;
diff --git a/libraries/simulated/proto_ps2.vhd b/libraries/simulated/proto_ps2.vhd
new file mode 100644 (file)
index 0000000..b0cffb4
--- /dev/null
@@ -0,0 +1,209 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+
+entity proto_ps2 is
+       port (
+               tx_byte:  in    std_logic_vector(7 downto 0);
+
+               ps2_clk:  inout std_logic;
+               ps2_data: inout std_logic
+       );
+end proto_ps2;
+
+
+architecture behavioral of proto_ps2 is
+
+       type state_t is (
+               S_IDLE,     S_INHIBIT,
+               S_TX_BIT_L, S_TX_BIT_H, S_TX_STOP_L, S_TX_STOP_H,
+               S_RX_BIT_L, S_RX_BIT_H, S_RX_STOP_L
+       );
+
+       -- Timings from http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
+       -- Slowest timing:
+       --constant T1: time := 25 us;  -- Time from DATA transition to falling edge of CLK
+       --constant T2: time := 45 us;  -- Time from rising edge of CLK to DATA transition
+       --constant T3: time := 50 us;  -- Duration of CLK inactive (Tx)
+       --constant T4: time := 50 us;  -- Duration of CLK active   (Tx)
+       --constant T5: time := 50 us;
+       --constant T7: time := 50 us;  -- Duration of CLK inactive (Rx)
+       --constant T8: time := 50 us;  -- Duration of CLK active   (Rx)
+       --constant T9: time := 25 us;  -- Time after CLK rise to sample
+
+       -- Fastest timing:
+       constant T1: time := 5 us;   -- Time from DATA transition to falling edge of CLK
+       constant T2: time := 5 us;   -- Time from rising edge of CLK to DATA transition
+       constant T3: time := 30 us;  -- Duration of CLK inactive (Tx)
+       constant T4: time := 30 us;  -- Duration of CLK active   (Tx)
+       --constant T5: time := 0 us;
+       constant T7: time := 30 us;  -- Duration of CLK inactive (Rx)
+       constant T8: time := 30 us;  -- Duration of CLK active   (Rx)
+       constant T9: time := 5 us;   -- Time after CLK rise to sample
+
+       signal cur_state: state_t := S_IDLE;
+
+       signal tx_req:    boolean;
+
+       -- CLK and DATA that the device is *trying* to drive, can be overridden by the host
+       signal dev_clk:   std_logic := '1';
+       signal dev_data:  std_logic := '1';
+
+       -- For detecting when the host overrides device's CLK and DATA
+       signal inhibit:   std_logic;
+       signal host_tx:   std_logic;
+
+       signal test_sample: std_logic;
+
+       signal data: std_logic_vector(10 downto 0);
+       signal i: integer;
+
+begin
+
+       test_sample <= ps2_data'delayed(T8 - T9);
+
+       process (cur_state, inhibit, host_tx, tx_req, tx_byte)
+               --variable data: std_logic_vector(10 downto 0);
+               --variable i: integer;
+               variable temp: std_logic;
+       begin
+               if inhibit'event then
+                       if inhibit = '1' then
+                               -- Abort any transmissions when inhibited
+                               if cur_state /= S_IDLE then
+                                       report "PS2 device inhibited (transmission aborted)";
+                               else
+                                       report "PS2 device inhibited";
+                               end if;
+                               if cur_state = S_TX_STOP_L or cur_state = S_TX_STOP_H then
+                                       -- FIXME: prevent inhibition during the stop bit, reporting is fine for now
+                                       report "PS2 device inhibited during stop bit" severity error;
+                               end if;
+                               cur_state <= S_INHIBIT;
+                       else
+                               report "PS2 device uninhibited";
+                               if host_tx = '1' then
+                                       i <= 0;
+                                       data <= (others => '0');
+                                       cur_state <= S_RX_BIT_L after T8;
+                               else
+                                       cur_state <= S_IDLE;
+                               end if;
+                       end if;
+               elsif tx_req'event and tx_req then
+                       report "PS2 device beginning transmission";
+                       data <= '1' & '1' & (not xor_reduce(tx_byte)) & tx_byte;
+                       i <= 0;
+                       dev_data <= '0';
+                       cur_state <= S_TX_BIT_L after T1;
+               elsif host_tx'event and host_tx = '1' then
+                       if not (cur_state = S_IDLE or cur_state = S_INHIBIT or
+                               cur_state = S_RX_BIT_L or cur_state = S_RX_BIT_H or cur_state = S_RX_STOP_L) then
+                               report "PS2 device saw unexpected drop in DATA line" severity error;
+                       elsif cur_state = S_IDLE then
+                               -- http://www.mcamafia.de/pdf/ibm_hitrc07.pdf *implies* that the
+                               -- device should still receive data without the host pulling the
+                               -- clock low first, but this is unusual and could cause problems
+                               -- if the device is about to begin a transmission.
+                               report "PS2 device saw drop in DATA line while not inhibited" severity warning;
+                               i <= 0;
+                               data <= (others => '0');
+                               cur_state <= S_RX_BIT_L after T8;
+                       end if;
+               elsif cur_state'event then
+                       -- Self-driving state machine, can be pushed into states by the rest of the process
+                       case cur_state is
+                               when S_IDLE =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1';
+
+                               when S_TX_BIT_L =>
+                                       dev_clk <= '0';
+                                       if i < 9 then
+                                               cur_state <= S_TX_BIT_H after T3;
+                                       else
+                                               cur_state <= S_TX_STOP_H after T3;
+                                       end if;
+
+                               when S_TX_BIT_H =>
+                                       dev_clk <= '1';
+                                       dev_data <= data(i) after T2;
+                                       i <= i + 1;
+                                       cur_state <= S_TX_BIT_L after T4;
+
+                               when S_TX_STOP_H =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1' after T2;
+                                       cur_state <= S_TX_STOP_L after T4;
+
+                               when S_TX_STOP_L =>
+                                       dev_clk <= '0';
+                                       cur_state <= S_IDLE after T3;
+
+                               when S_INHIBIT =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1';
+
+                               when S_RX_BIT_L =>
+                                       dev_clk <= '0';
+                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
+                                       i <= i + 1;
+                                       cur_state <= S_RX_BIT_H after T7;
+
+                               when S_RX_BIT_H =>
+                                       dev_clk <= '1';
+                                       if i <= 9 then
+                                               cur_state <= S_RX_BIT_L after T8;
+                                       else
+                                               cur_state <= S_RX_STOP_L after T8;
+                                       end if;
+
+                               when S_RX_STOP_L =>
+                                       dev_clk <= '0';
+                                       dev_data <= '0';  -- Pull DATA low to acknowledge
+                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
+                                       temp := ps2_data'delayed(T8 - T9);
+                                       report "PS2 device recieved " & integer'image(to_integer(unsigned(data(8 downto 1))));
+                                       -- Spec says device should continue clocking and request a
+                                       -- resend if it gets a zero stop bit, but that's already an
+                                       -- error condition so reporting is sufficient.
+                                       assert temp /= '0'
+                                               report "PS2 device got a 0 stop bit"
+                                               severity error;
+                                       assert xor_reduce(data(9 downto 1)) = '1'
+                                               report "PS2 device received wrong parity"
+                                               severity error;
+                                       cur_state <= S_IDLE after T7;
+
+                               when others =>
+                                       cur_state <= S_IDLE;
+                       end case;
+               end if;
+       end process;
+
+       -- Check for anything driving the CLK and DATA lines too hard
+       process (ps2_clk, ps2_data)
+       begin
+               if ps2_clk = '1' then
+                       report "PS2 clock line driven harder than expected" severity error;
+               end if;
+               if ps2_data = '1' then
+                       report "PS2 data line driven harder than expected" severity error;
+               end if;
+       end process;
+
+       -- Weaken clock and data high signals
+       ps2_clk  <= '0' when dev_clk  = '0' else 'H';
+       ps2_data <= '0' when dev_data = '0' else 'H';
+
+       -- Detect host signaling
+       -- Needs delayed dev_clk/data to account for "gate delay" from computing ps2_clk/data from dev_clk/data
+       inhibit <= '1' when ps2_clk  = '0' and dev_clk'delayed  = '1' else '0';
+       host_tx <= '1' when ps2_data = '0' and dev_data'delayed = '1' else '0';
+
+       -- Detect test process transmit request
+       tx_req <= not tx_byte'quiet;
+
+end behavioral;
diff --git a/libraries/simulated/ps2_device.vhd b/libraries/simulated/ps2_device.vhd
deleted file mode 100644 (file)
index fe79f68..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-use ieee.std_logic_misc.all;
-
-
-entity ps2_device is
-       port (
-               tx_byte:  in    std_logic_vector(7 downto 0);
-
-               ps2_clk:  inout std_logic;
-               ps2_data: inout std_logic
-       );
-end ps2_device;
-
-
-architecture behavioral of ps2_device is
-
-       type state_t is (
-               S_IDLE,     S_INHIBIT,
-               S_TX_BIT_L, S_TX_BIT_H, S_TX_STOP_L, S_TX_STOP_H,
-               S_RX_BIT_L, S_RX_BIT_H, S_RX_STOP_L
-       );
-
-       -- Timings from http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
-       -- Slowest timing:
-       --constant T1: time := 25 us;  -- Time from DATA transition to falling edge of CLK
-       --constant T2: time := 45 us;  -- Time from rising edge of CLK to DATA transition
-       --constant T3: time := 50 us;  -- Duration of CLK inactive (Tx)
-       --constant T4: time := 50 us;  -- Duration of CLK active   (Tx)
-       --constant T5: time := 50 us;
-       --constant T7: time := 50 us;  -- Duration of CLK inactive (Rx)
-       --constant T8: time := 50 us;  -- Duration of CLK active   (Rx)
-       --constant T9: time := 25 us;  -- Time after CLK rise to sample
-
-       -- Fastest timing:
-       constant T1: time := 5 us;   -- Time from DATA transition to falling edge of CLK
-       constant T2: time := 5 us;   -- Time from rising edge of CLK to DATA transition
-       constant T3: time := 30 us;  -- Duration of CLK inactive (Tx)
-       constant T4: time := 30 us;  -- Duration of CLK active   (Tx)
-       --constant T5: time := 0 us;
-       constant T7: time := 30 us;  -- Duration of CLK inactive (Rx)
-       constant T8: time := 30 us;  -- Duration of CLK active   (Rx)
-       constant T9: time := 5 us;   -- Time after CLK rise to sample
-
-       signal cur_state: state_t := S_IDLE;
-
-       signal tx_req:    boolean;
-
-       -- CLK and DATA that the device is *trying* to drive, can be overridden by the host
-       signal dev_clk:   std_logic := '1';
-       signal dev_data:  std_logic := '1';
-
-       -- For detecting when the host overrides device's CLK and DATA
-       signal inhibit:   std_logic;
-       signal host_tx:   std_logic;
-
-       signal test_sample: std_logic;
-
-       signal data: std_logic_vector(10 downto 0);
-       signal i: integer;
-
-begin
-
-       test_sample <= ps2_data'delayed(T8 - T9);
-
-       process (cur_state, inhibit, host_tx, tx_req, tx_byte)
-               --variable data: std_logic_vector(10 downto 0);
-               --variable i: integer;
-               variable temp: std_logic;
-       begin
-               if inhibit'event then
-                       if inhibit = '1' then
-                               -- Abort any transmissions when inhibited
-                               if cur_state /= S_IDLE then
-                                       report "PS2 device inhibited (transmission aborted)";
-                               else
-                                       report "PS2 device inhibited";
-                               end if;
-                               if cur_state = S_TX_STOP_L or cur_state = S_TX_STOP_H then
-                                       -- FIXME: prevent inhibition during the stop bit, reporting is fine for now
-                                       report "PS2 device inhibited during stop bit" severity error;
-                               end if;
-                               cur_state <= S_INHIBIT;
-                       else
-                               report "PS2 device uninhibited";
-                               if host_tx = '1' then
-                                       i <= 0;
-                                       data <= (others => '0');
-                                       cur_state <= S_RX_BIT_L after T8;
-                               else
-                                       cur_state <= S_IDLE;
-                               end if;
-                       end if;
-               elsif tx_req'event and tx_req then
-                       report "PS2 device beginning transmission";
-                       data <= '1' & '1' & (not xor_reduce(tx_byte)) & tx_byte;
-                       i <= 0;
-                       dev_data <= '0';
-                       cur_state <= S_TX_BIT_L after T1;
-               elsif host_tx'event and host_tx = '1' then
-                       if not (cur_state = S_IDLE or cur_state = S_INHIBIT or
-                               cur_state = S_RX_BIT_L or cur_state = S_RX_BIT_H or cur_state = S_RX_STOP_L) then
-                               report "PS2 device saw unexpected drop in DATA line" severity error;
-                       elsif cur_state = S_IDLE then
-                               -- http://www.mcamafia.de/pdf/ibm_hitrc07.pdf *implies* that the
-                               -- device should still receive data without the host pulling the
-                               -- clock low first, but this is unusual and could cause problems
-                               -- if the device is about to begin a transmission.
-                               report "PS2 device saw drop in DATA line while not inhibited" severity warning;
-                               i <= 0;
-                               data <= (others => '0');
-                               cur_state <= S_RX_BIT_L after T8;
-                       end if;
-               elsif cur_state'event then
-                       -- Self-driving state machine, can be pushed into states by the rest of the process
-                       case cur_state is
-                               when S_IDLE =>
-                                       dev_clk <= '1';
-                                       dev_data <= '1';
-
-                               when S_TX_BIT_L =>
-                                       dev_clk <= '0';
-                                       if i < 9 then
-                                               cur_state <= S_TX_BIT_H after T3;
-                                       else
-                                               cur_state <= S_TX_STOP_H after T3;
-                                       end if;
-
-                               when S_TX_BIT_H =>
-                                       dev_clk <= '1';
-                                       dev_data <= data(i) after T2;
-                                       i <= i + 1;
-                                       cur_state <= S_TX_BIT_L after T4;
-
-                               when S_TX_STOP_H =>
-                                       dev_clk <= '1';
-                                       dev_data <= '1' after T2;
-                                       cur_state <= S_TX_STOP_L after T4;
-
-                               when S_TX_STOP_L =>
-                                       dev_clk <= '0';
-                                       cur_state <= S_IDLE after T3;
-
-                               when S_INHIBIT =>
-                                       dev_clk <= '1';
-                                       dev_data <= '1';
-
-                               when S_RX_BIT_L =>
-                                       dev_clk <= '0';
-                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
-                                       i <= i + 1;
-                                       cur_state <= S_RX_BIT_H after T7;
-
-                               when S_RX_BIT_H =>
-                                       dev_clk <= '1';
-                                       if i <= 9 then
-                                               cur_state <= S_RX_BIT_L after T8;
-                                       else
-                                               cur_state <= S_RX_STOP_L after T8;
-                                       end if;
-
-                               when S_RX_STOP_L =>
-                                       dev_clk <= '0';
-                                       dev_data <= '0';  -- Pull DATA low to acknowledge
-                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
-                                       temp := ps2_data'delayed(T8 - T9);
-                                       report "PS2 device recieved " & integer'image(to_integer(unsigned(data(8 downto 1))));
-                                       -- Spec says device should continue clocking and request a
-                                       -- resend if it gets a zero stop bit, but that's already an
-                                       -- error condition so reporting is sufficient.
-                                       assert temp /= '0'
-                                               report "PS2 device got a 0 stop bit"
-                                               severity error;
-                                       assert xor_reduce(data(9 downto 1)) = '1'
-                                               report "PS2 device received wrong parity"
-                                               severity error;
-                                       cur_state <= S_IDLE after T7;
-
-                               when others =>
-                                       cur_state <= S_IDLE;
-                       end case;
-               end if;
-       end process;
-
-       -- Check for anything driving the CLK and DATA lines too hard
-       process (ps2_clk, ps2_data)
-       begin
-               if ps2_clk = '1' then
-                       report "PS2 clock line driven harder than expected" severity error;
-               end if;
-               if ps2_data = '1' then
-                       report "PS2 data line driven harder than expected" severity error;
-               end if;
-       end process;
-
-       -- Weaken clock and data high signals
-       ps2_clk  <= '0' when dev_clk  = '0' else 'H';
-       ps2_data <= '0' when dev_data = '0' else 'H';
-
-       -- Detect host signaling
-       -- Needs delayed dev_clk/data to account for "gate delay" from computing ps2_clk/data from dev_clk/data
-       inhibit <= '1' when ps2_clk  = '0' and dev_clk'delayed  = '1' else '0';
-       host_tx <= '1' when ps2_data = '0' and dev_data'delayed = '1' else '0';
-
-       -- Detect test process transmit request
-       tx_req <= not tx_byte'quiet;
-
-end behavioral;
diff --git a/libraries/simulated/sim_js28f128j3d75.vhd b/libraries/simulated/sim_js28f128j3d75.vhd
deleted file mode 100644 (file)
index a024d7e..0000000
+++ /dev/null
@@ -1,719 +0,0 @@
-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;
-
-       ----------------------------------------------------------------------------
-       -- Array value
-
-       shared variable flash_array: sparse_t;
-       signal array_word:      std_logic_vector(15 downto 0);
-
-       ----------------------------------------------------------------------------
-       -- Device Information
-
-       signal id_word:         std_logic_vector(15 downto 0);
-
-       ----------------------------------------------------------------------------
-       -- Status Register
-
-       signal sts_word:        std_logic_vector(15 downto 0);
-
-       signal sts_ready:       std_logic;
-       signal sts_susp_erase:  std_logic;
-       signal sts_error_erase: std_logic;
-       signal sts_error_prog:  std_logic;
-       signal sts_error_vpen:  std_logic;
-       signal sts_susp_prog:   std_logic;
-       signal sts_error_lock:  std_logic;
-
-       ----------------------------------------------------------------------------
-       -- CFI Query
-
-       signal cfi_word:        std_logic_vector(15 downto 0);
-
-       ----------------------------------------------------------------------------
-       -- Output value
-
-       type read_state_t is (R_ARRAY, R_ID, R_STATUS, R_CFI);
-       signal read_state_cur:  read_state_t;
-
-       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;
-
-       ----------------------------------------------------------------------------
-       -- Other registers
-
-       signal ecr_reg:         std_logic_vector(23 downto 0);
-       signal sts_config_reg:  std_logic_vector(7 downto 0);
-
-       ----------------------------------------------------------------------------
-       -- CUI state machine
-
-       type cui_state_t is (
-               C_IDLE,
-               C_PROG_STS,
-               C_PROG_OTP,
-               C_PROG_WORD,
-               C_PROG_BUF_COUNT,
-               C_PROG_BUF_DATA,
-               C_ERASE,
-               C_LOCK_OR_ECR
-       );
-
-       signal cui_state_cur:   cui_state_t;
-
-       ----------------------------------------------------------------------------
-       -- Write state machine
-
-       type wsm_state_t is (
-               W_IDLE,
-               W_PROG_OTP,
-               W_PROG_WORD,
-               W_PROG_WORD_FINISH,
-               W_PROG_BUF,
-               W_ERASE,
-               W_ERASE_FINISH,
-               W_LOCK,
-               W_UNLOCK
-       );
-
-       signal wsm_state_cur:   wsm_state_t;
-
-       signal trig_clrsts:     std_logic;
-       signal trig_susp:       std_logic;
-       signal trig_resume:     std_logic;
-       signal trig_cmderr:     std_logic;
-       signal trig_otp:        std_logic;
-       signal trig_word:       std_logic;
-       signal trig_buf:        std_logic;
-       signal trig_erase:      std_logic;
-       signal trig_lock:       std_logic;
-       signal trig_unlock:     std_logic;
-
-       signal prog_word:       std_logic_vector(15 downto 0);
-       signal prog_addr:       std_logic_vector(23 downto 0);
-       signal erase_addr:      std_logic_vector(23 downto 0);
-       signal lock_addr:       std_logic_vector(23 downto 0);
-       signal unlock_addr:     std_logic_vector(23 downto 0);
-
-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;
-
-       process (a, sts_ready)
-       begin
-               -- sts_ready is in the sensitivity list because flash_array is a variable and cannot be
-               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;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- Device Information
-
-       id_word <= (others => 'X');
-
-
-       ----------------------------------------------------------------------------
-       -- Status Register
-
-       sts_word <= "00000000"      &
-                   sts_ready       &
-                   sts_susp_erase  &
-                   sts_error_erase &
-                   sts_error_prog  &
-                   sts_error_vpen  &
-                   sts_susp_prog   &
-                   sts_error_lock  &
-                               '0';
-
-
-       ----------------------------------------------------------------------------
-       -- CFI Query
-
-       cfi_word <= (others => 'X');
-
-
-       ----------------------------------------------------------------------------
-       -- Output value
-
-       with read_state_cur select word <=
-               array_word      when R_ARRAY,
-               id_word         when R_ID,
-               sts_word        when R_STATUS,
-               cfi_word        when R_CFI,
-               (others => 'X') when others;
-
-       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);
-
-
-       ----------------------------------------------------------------------------
-       -- Command User Interface (CUI)
-
-       process (rp_n, internal_we_n, a, d)
-       begin
-               if rp_n = '0' then
-                       cui_state_cur <= C_IDLE;
-                       trig_clrsts   <= '0';
-                       trig_susp     <= '0';
-                       trig_resume   <= '0';
-                       trig_cmderr   <= '0';
-                       trig_otp      <= '0';
-                       trig_word     <= '0';
-                       trig_buf      <= '0';
-                       trig_erase    <= '0';
-                       trig_lock     <= '0';
-                       trig_unlock   <= '0';
-               elsif falling_edge(internal_we_n) then
-                       trig_clrsts <= '0';
-                       trig_susp   <= '0';
-                       trig_resume <= '0';
-                       trig_cmderr <= '0';
-                       trig_otp    <= '0';
-                       trig_word   <= '0';
-                       trig_buf    <= '0';
-                       trig_erase  <= '0';
-                       trig_lock   <= '0';
-                       trig_unlock <= '0';
-               elsif rising_edge(internal_we_n) then
-                       case cui_state_cur is
-                               when C_IDLE =>
-                                       case d(7 downto 0) is
-                                               when x"FF" =>  -- Read Array
-                                                       read_state_cur <= R_ARRAY;
-
-                                               when x"70" =>  -- Read Status Register
-                                                       read_state_cur <= R_STATUS;
-
-                                               when x"90" =>  -- Read Identifier Codes/Device Information
-                                                       report "TODO: Read identifier codes and device information" severity error;
-                                                       read_state_cur <= R_ID;
-
-                                               when x"98" =>  -- CFI Query
-                                                       report "TODO: CFI query" severity error;
-                                                       read_state_cur <= R_CFI;
-
-                                               when x"50" =>  -- Clear Status Register
-                                                       trig_clrsts <= '1';
-
-                                               when x"B8" =>  -- Program STS Configuration Register
-                                                       cui_state_cur <= C_PROG_STS;
-
-                                               when x"c0" =>  -- Program OTP Register
-                                                       cui_state_cur <= C_PROG_OTP;
-
-                                               when x"40" | x"10" =>  -- Word/Byte Program
-                                                       cui_state_cur <= C_PROG_WORD;
-
-                                               when x"E8" =>  -- Buffered Program
-                                                       read_state_cur <= R_STATUS;
-                                                       cui_state_cur  <= C_PROG_BUF_COUNT;
-
-                                               when x"20" =>  -- Block Erase
-                                                       cui_state_cur <= C_ERASE;
-
-                                               when x"60" =>  -- Lock/Unlock Block or Program Enhanced Configuration Register
-                                                       cui_state_cur <= C_LOCK_OR_ECR;
-
-                                               when x"B0" =>  -- Program/Erase Suspend
-                                                       trig_susp      <= '1';
-                                                       read_state_cur <= R_STATUS;
-
-                                               when x"D0" =>  -- Program/Erase Resume
-                                                       trig_resume    <= '1';
-                                                       read_state_cur <= R_STATUS;
-
-                                               when others =>
-                                                       trig_cmderr <= '1';
-                                       end case;
-
-                               when C_PROG_STS =>
-                                       assert d(7 downto 0) = x"00" report "TODO: STS pulse config" severity error;
-                                       sts_config_reg <= d(7 downto 0);
-                                       cui_state_cur  <= C_IDLE;
-
-                               when C_PROG_OTP =>
-                                       trig_otp       <= '1';
-                                       read_state_cur <= R_STATUS;
-                                       cui_state_cur  <= C_IDLE;
-
-                               when C_PROG_WORD =>
-                                       prog_word      <= d;
-                                       prog_addr      <= a;
-                                       trig_word      <= '1';
-                                       read_state_cur <= R_STATUS;
-                                       cui_state_cur  <= C_IDLE;
-
-                               when C_PROG_BUF_COUNT =>
-                                       report "TODO: buffered program" severity error;
-                                       cui_state_cur <= C_PROG_BUF_DATA;
-
-                               when C_PROG_BUF_DATA =>
-                                       cui_state_cur <= C_IDLE;
-
-                               when C_ERASE =>
-                                       case d(7 downto 0) is
-                                               when x"D0" =>  -- Erase Block
-                                                       erase_addr     <= a;
-                                                       trig_erase     <= '1';
-                                                       read_state_cur <= R_STATUS;
-                                                       cui_state_cur  <= C_IDLE;
-
-                                               when others =>
-                                                       trig_cmderr    <= '1';
-                                                       cui_state_cur  <= C_IDLE;
-                                       end case;
-
-                               when C_LOCK_OR_ECR =>
-                                       case d(7 downto 0) is
-                                               when x"04" =>  -- Program Enhanced Configuration Register
-                                                       assert a = x"000000" report "TODO: ECR configuration" severity error;
-                                                       ecr_reg       <= a;
-                                                       cui_state_cur <= C_IDLE;
-
-                                               when x"01" =>  -- Lock Block
-                                                       lock_addr      <= a;
-                                                       trig_lock      <= '1';
-                                                       read_state_cur <= R_STATUS;
-                                                       cui_state_cur  <= C_IDLE;
-
-                                               when x"0D" =>  -- Unlock block
-                                                       unlock_addr    <= a;
-                                                       trig_unlock    <= '1';
-                                                       read_state_cur <= R_STATUS;
-                                                       cui_state_cur  <= C_IDLE;
-
-                                               when others =>
-                                                       trig_cmderr   <= '1';
-                                                       cui_state_cur <= C_IDLE;
-                                       end case;
-
-                               when others =>
-                                       report "Unhandled CUI state" severity error;
-                       end case;
-               end if;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- Write State Machine (WSM)
-
-       process (wsm_state_cur, rp_n, vpen,
-                trig_clrsts, trig_susp, trig_resume, trig_cmderr, trig_otp,
-                trig_word, trig_buf, trig_erase, trig_lock, trig_unlock)
-       begin
-               if rp_n = '0' then
-                       wsm_state_cur   <= W_IDLE;
-
-                       -- Clear all error and status bits and initialize to ready
-                       sts_ready       <= '1';
-                       sts_susp_erase  <= '0';
-                       sts_error_erase <= '0';
-                       sts_error_prog  <= '0';
-                       sts_error_vpen  <= '0';
-                       sts_susp_prog   <= '0';
-                       sts_error_lock  <= '0';
-               else
-                       -- Clearing error status bits should probably be allowed in any WSM state
-                       if rising_edge(trig_clrsts) then
-                               sts_error_erase <= '0';
-                               sts_error_prog  <= '0';
-                               sts_error_vpen  <= '0';
-                               sts_error_lock  <= '0';
-                       end if;
-
-                       -- Command errors should probably be latched in any WSM state
-                       if rising_edge(trig_cmderr) then
-                               sts_error_erase <= '1';
-                               sts_error_prog  <= '1';
-                       end if;
-
-                       -- Handle each state's logic
-                       -- Keep in mind this is not only run when wsm_state_cur changes,
-                       -- but also rerun when vpen or any trigger signal change
-                       sts_ready <= '0';
-                       case wsm_state_cur is
-                               when W_IDLE =>
-                                       sts_ready <= '1';
-
-                                       -- After these state transitions, this process will be rerun
-                                       -- in the next delta cycle with the new state
-                                       if rising_edge(trig_resume) then
-                                               report "TODO: resume program/erase operation" severity error;
-                                       elsif rising_edge(trig_otp) then
-                                               wsm_state_cur <= W_PROG_OTP;
-                                       elsif rising_edge(trig_word) then
-                                               wsm_state_cur <= W_PROG_WORD;
-                                       elsif rising_edge(trig_buf) then
-                                               wsm_state_cur <= W_PROG_BUF;
-                                       elsif rising_edge(trig_erase) then
-                                               wsm_state_cur <= W_ERASE;
-                                       elsif rising_edge(trig_lock) then
-                                               wsm_state_cur <= W_LOCK;
-                                       elsif rising_edge(trig_unlock) then
-                                               wsm_state_cur <= W_UNLOCK;
-                                       end if;
-
-                               when W_PROG_OTP =>
-                                       report "TODO: program OTP" severity error;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when W_PROG_WORD =>
-                                       -- TODO: check lock bits
-
-                                       -- If vpen is low or drops during programming, halt with error
-                                       if vpen = '0' then
-                                               sts_error_prog <= '1';
-                                               sts_error_vpen <= '1';
-                                               wsm_state_cur  <= W_IDLE;
-                                       end if;
-
-                                       -- If suspend command is sent, pause programming
-                                       if rising_edge(trig_susp) then
-                                               report "TODO: program suspend" severity error;
-                                       end if;
-
-                                       -- Invalidate the data and schedule programming completion
-                                       if byte_n = '0' then
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr)), "XXXXXXXX");
-                                       else
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '0')), "XXXXXXXX");
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '1')), "XXXXXXXX");
-                                       end if;
-                                       wsm_state_cur <= W_PROG_WORD_FINISH after T_WHQV3;
-
-                               when W_PROG_WORD_FINISH =>
-                                       if byte_n = '0' then
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr)), prog_word(7 downto 0));
-                                       else
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '0')), prog_word(15 downto 8));
-                                               sparse_set(flash_array, to_integer(unsigned(prog_addr(prog_addr'high downto 1) & '1')), prog_word( 7 downto 0));
-                                       end if;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when W_PROG_BUF =>
-                                       report "TODO: buffered program" severity error;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when W_ERASE =>
-                                       -- TODO: check lock bits
-
-                                       -- If vpen is low or drops during erase, halt with error
-                                       if vpen = '0' then
-                                               sts_error_erase <= '1';
-                                               sts_error_vpen  <= '1';
-                                               wsm_state_cur   <= W_IDLE;
-                                       end if;
-
-                                       -- If suspend command is sent, pause programming
-                                       if rising_edge(trig_susp) then
-                                               report "TODO: erase suspend" severity error;
-                                       end if;
-
-                                       -- Invalidate the data and schedule the erase completion
-                                       for i in 0 to 16#1ffff# loop
-                                               sparse_set(flash_array, to_integer(unsigned(erase_addr(erase_addr'high downto 16)) & to_unsigned(i, 17)), x"FF");
-                                       end loop;
-                                       wsm_state_cur <= W_ERASE_FINISH after T_WHQV4;
-
-                               when W_ERASE_FINISH =>
-                                       for i in 0 to 16#1ffff# loop
-                                               sparse_set(flash_array, to_integer(unsigned(erase_addr(erase_addr'high downto 16)) & to_unsigned(i, 17)), x"FF");
-                                       end loop;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when W_LOCK =>
-                                       report "TODO: program lock bits" severity error;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when W_UNLOCK =>
-                                       report "TODO: erase lock bits" severity error;
-                                       wsm_state_cur <= W_IDLE;
-
-                               when others =>
-                                       report "Unhandled WSM state" severity error;
-
-                       end case;
-               end if;
-       end process;
-
-
-       ----------------------------------------------------------------------------
-       -- 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
deleted file mode 100644 (file)
index 2fc2bb0..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-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_attrs.vhd b/libraries/simulated/tests/test_attrs.vhd
deleted file mode 100644 (file)
index e651e3a..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-
-
-entity test_attrs is
-end test_attrs;
-
-
-architecture behavior of test_attrs is
-
-       constant T: time := 10 ns;
-
-       signal sig:               std_logic;
-       signal sig_delayed:       std_logic;
-       signal sig_stable:        boolean;
-       signal sig_quiet:         boolean;
-       signal sig_last_event:    time;
-       signal sig_last_active:   time;
-       signal sig_last_event_2:  time;
-       signal sig_last_active_2: time;
-
-begin
-
-       test: process is
-       begin
-
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-
-               sig <= '0';
-
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-               wait for 30 ns;
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-
-               sig <= '1';
-
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-               wait for 30 ns;
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-
-               sig <= '1';
-
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-               wait for 30 ns;
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-
-               sig <= '0';
-
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-               wait for 30 ns;
-               sig_last_event_2  <= sig'last_event;
-               sig_last_active_2 <= sig'last_active;
-
-               wait;
-
-       end process;
-
-       sig_delayed     <= sig'delayed(T);
-       sig_stable      <= sig'stable(T);
-       sig_quiet       <= sig'quiet(T);
-
-       -- These don't work as expected!
-       -- This also applies to equivalent processes with sensitivity lists
-       sig_last_event  <= sig'last_event;
-       sig_last_active <= sig'last_active;
-
-end;
diff --git a/libraries/simulated/tests/test_genericmem.vhd b/libraries/simulated/tests/test_genericmem.vhd
new file mode 100644 (file)
index 0000000..882fd2b
--- /dev/null
@@ -0,0 +1,70 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+
+
+entity test_genericmem is
+end test_genericmem;
+
+
+architecture behavior of test_genericmem is
+
+       signal rst_i: std_logic;
+       signal clk_i: std_logic;
+       signal cyc_i: std_logic;
+       signal stb_i: std_logic;
+       signal we_i:  std_logic;
+       signal ack_o: std_logic;
+       signal adr_i: std_logic_vector(4 downto 0);
+       signal dat_i: std_logic_vector(7 downto 0);
+       signal dat_o: std_logic_vector(7 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= (others => '0');
+               dat_i <= (others => '0');
+
+               -- Reset
+               rst_i <= '1';
+               wait for 40 ns;
+               rst_i <= '0';
+
+               -- Done
+               wait;
+       end process;
+
+
+       e_uut: entity work.genericmem
+               generic map (
+                       INIT => x"00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210"
+               )
+               port map (
+                       rst_i => rst_i,
+                       clk_i => clk_i,
+                       cyc_i => cyc_i,
+                       stb_i => stb_i,
+                       we_i  => we_i,
+                       ack_o => ack_o,
+                       adr_i => adr_i,
+                       dat_i => dat_i,
+                       dat_o => dat_o
+               );
+
+
+       p_clk: process
+       begin
+               clk_i <= '0';
+               wait for 10 ns;
+               clk_i <= '1';
+               wait for 10 ns;
+       end process;
+
+end;
index b84c0d18d2c1275a6199e08b1daff257e3cc0d71..cf65f78c69e23a3bc01b13c343c9873e6368f243 100644 (file)
@@ -2,180 +2,112 @@ library ieee;
 use ieee.std_logic_1164.all;
 use ieee.numeric_std.all;
 
-library flash_js28f128;
+library work;
 
 
-entity testbench_j28f128j3d75 is
-end testbench_j28f128j3d75;
+entity test_js28f128j3d75 is
+end test_js28f128j3d75;
 
 
-architecture behavior of testbench_js28f128j3d75 is
+architecture behavior of test_js28f128j3d75 is
 
-       type op is (O_NONE, O_READ_ARRAY, O_READ_STATUS, O_PROG_A, O_PROG_B, O_MODE_ARRAY);
-
-       -- Flash signals
-       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;
-       signal oe_n: std_logic;
-       signal we_n: std_logic;
-       signal sts: std_logic;
+       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;
-
-       -- Debug
-       signal cur_op: op;
+       signal vpen:   std_logic;
 
 begin
 
-       uut: entity flash_js28f128.js28f128j3d75 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
-       );
-
-
-       tb: process
+       p_test: process
        begin
-               -- Initial values
-               cur_op <= O_NONE;
-               a <= (others => '0');
-               d <= (others => 'Z');
-               ce <= (others => '1');
-               oe_n <= '1';
-               we_n <= '1';
+               -- Initial values and reset
+               a      <= x"000000";
+               d      <= "ZZZZZZZZZZZZZZZZ";
+               ce     <= "111";
+               rp_n   <= '0';
+               oe_n   <= '1';
+               we_n   <= '1';
                byte_n <= '1';
-               vpen <= '1';
-
-               -- Reset
-               rp_n <= '1';
+               vpen   <= '1';
+               wait for 10 ns;
+               rp_n   <= '1';
                wait for 1 us;
-               rp_n <= '0';
-               wait for 25 us;
-               rp_n <= '1';
-               wait for 210 ns;
-
-               -- Wait for long BYTE# signal propagation time
-               wait for 1 ms;
 
-               -- Asynchronous array read
-               cur_op <= O_READ_ARRAY;
-               ce(0) <= '0';
-               oe_n <= '0';
-               a <= x"000000";
-               wait for 100 ns;
-               a <= x"000002";
-               wait for 100 ns;
-               a <= x"000004";
-               wait for 100 ns;
-               a <= x"000006";
-               wait for 100 ns;
-               a <= x"000008";
-               wait for 100 ns;
-               a <= x"00000a";
-               wait for 100 ns;
-               a <= x"00000c";
+               -- Test a page read
+               ce     <= "000";
+               oe_n   <= '0';
                wait for 100 ns;
-               a <= x"00000e";
-               wait for 100 ns;
-               ce(0) <= '1';
-               oe_n <= '1';
-               cur_op <= O_NONE;
+               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 for 25 ns;
-
-               -- Program word
-               ce(0) <= '0';
+               wait for 100 ns;
 
-               cur_op <= O_PROG_A;
-               a <= x"000000";
-               d <= x"0040";
+               -- Send the write-word command
+               a    <= x"000008";
+               d    <= x"0040";
+               ce   <= "000";
                we_n <= '0';
-               wait for 60 ns;
+               wait for 80 ns;
                we_n <= '1';
-               wait for 30 ns;
+               ce   <= "111";
+               wait for 35 ns;
 
-               cur_op <= O_PROG_B;
-               a <= x"000000";
-               d <= x"abcd";
+               -- Send the word to write
+               d    <= x"1234";
+               ce   <= "000";
                we_n <= '0';
-               wait for 60 ns;
+               wait for 80 ns;
                we_n <= '1';
-               wait for 30 ns;
-
-               ce(0) <= '1';
-               d <= (others => 'Z');
-               cur_op <= O_NONE;
-               wait for 100 us;
-
-               -- Read status during program
-               cur_op <= O_READ_STATUS;
-               ce(0) <= '0';
-               oe_n <= '0';
-               a <= x"000000";
-               wait for 100 ns;
-               assert d = x"0000"
-                       report "Status read got wrong value: " & integer'image(to_integer(unsigned(d)))
-                       severity error;
-               oe_n <= '1';
-               ce(0) <= '1';
-               cur_op <= O_NONE;
-               wait for 25 ns;
+               ce   <= "111";
+               wait for 35 ns;
 
-               -- Wait for program to complete
+               -- Wait for the write to complete
                wait until sts /= '0';
-               wait for 25 ns;
+               wait for 50 ns;
 
-               -- Read status after program
-               cur_op <= O_READ_STATUS;
-               ce(0) <= '0';
-               oe_n <= '0';
-               a <= x"000000";
-               wait for 100 ns;
-               assert d = x"0080"
-                       report "Status read got wrong value: " & integer'image(to_integer(unsigned(d)))
-                       severity error;
-               oe_n <= '1';
-               ce(0) <= '1';
-               cur_op <= O_NONE;
-               wait for 25 ns;
-
-               -- Enter read-array mode
-               cur_op <= O_MODE_ARRAY;
-               a <= x"000000";
-               d <= x"00ff";
-               ce(0) <= '0';
+               -- Send the read-array command
+               d    <= x"00ff";
+               ce   <= "000";
                we_n <= '0';
-               wait for 60 ns;
+               wait for 80 ns;
                we_n <= '1';
-               wait for 30 ns;
-               ce(0) <= '1';
-               d <= (others => 'Z');
-               cur_op <= O_NONE;
-               wait for 25 ns;
-
-               -- Read flash array value
-               cur_op <= O_READ_ARRAY;
-               ce(0) <= '0';
+               ce   <= "111";
+               wait for 35 ns;
+
+               -- Read back the value we wrote
+               d    <= "ZZZZZZZZZZZZZZZZ";
+               ce   <= "000";
                oe_n <= '0';
-               a <= x"000000";
-               wait for 100 ns;
-               assert d = x"abcd"
-                       report "Array read got wrong value"
-                       severity error;
+               wait for 80 ns;
                oe_n <= '1';
-               ce(0) <= '1';
-               cur_op <= O_NONE;
-               wait for 25 ns;
+               ce   <= "111";
+               wait for 35 ns;
 
                wait;
        end process;
 
-end architecture;
+       e_dut: entity work.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;
index d045fb1abb3e7d09eeae5ad67c28dc85492c573b..c5d5a7a5de2b2d97579ecb85cbb6934da4ede021 100644 (file)
@@ -2,14 +2,14 @@ library ieee;
 use ieee.std_logic_1164.all;
 use ieee.numeric_std.all;
 
-library simulated;
+library work;
 
 
-entity testbench_ram is
-end testbench_ram;
+entity test_mt45w8mw16bgx is
+end test_mt45w8mw16bgx;
 
 
-architecture behavior of testbench_ram is
+architecture behavior of test_mt45w8mw16bgx is
 
        signal addr: std_logic_vector(23 downto 0);  -- Extra bit to allow hex literals
        signal data: std_logic_vector(15 downto 0);
@@ -74,7 +74,7 @@ begin
                wait;
        end process;
 
-       uut: entity simulated.mt45w8mw16bgx
+       uut: entity work.mt45w8mw16bgx
                port map (
                        a     => addr(22 downto 0),
                        dq    => data,
diff --git a/libraries/simulated/tests/test_ps2.vhd b/libraries/simulated/tests/test_ps2.vhd
new file mode 100644 (file)
index 0000000..7b0bdf9
--- /dev/null
@@ -0,0 +1,91 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity test_ps2 is
+end test_ps2;
+
+
+architecture behavior of test_ps2 is
+
+       signal tx_byte:  std_logic_vector(7 downto 0) := x"00";
+       signal ps2_clk:  std_logic;
+       signal ps2_data: std_logic;
+
+begin
+
+       ps2_clk <= 'H';
+       ps2_data <= 'H';
+
+       p_test: process
+       begin
+               ps2_clk <= 'Z';
+               ps2_data <= 'Z';
+
+               -- Receive bytes form device
+               wait for 100 ns;
+               tx_byte <= x"a5";
+               wait for 1 ms;
+               tx_byte <= x"a5";
+               wait for 400 us;
+               ps2_clk <= '0';  -- Cancel transmission from host
+               wait for 400 us;
+               ps2_clk <= 'Z';
+               wait for 200 us;
+               tx_byte <= x"c3";
+               wait for 1 ms;
+
+               -- Transmit byte to device
+               ps2_clk <= '0';
+               wait for 30 us;
+               ps2_data <= '0';  -- start
+               wait for 30 us;
+               ps2_clk <= 'Z';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 0
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 1
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 2
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 3
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 4
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 5
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 6
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 7
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- parity
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- stop
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- ack
+               assert ps2_data = '0'
+                       report "NACK from device"
+                       severity error;
+               wait until ps2_clk /= '0';
+               wait;
+       end process;
+
+       e_ps2_device: entity work.proto_ps2
+               port map (
+                       tx_byte  => tx_byte,
+
+                       ps2_clk  => ps2_clk,
+                       ps2_data => ps2_data
+               );
+
+end;
diff --git a/libraries/simulated/tests/test_ps2_device.vhd b/libraries/simulated/tests/test_ps2_device.vhd
deleted file mode 100644 (file)
index 46418f7..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-
-
-entity test_ps2_device is
-end test_ps2_device;
-
-
-architecture behavior of test_ps2_device is
-
-       signal tx_byte:  std_logic_vector(7 downto 0) := x"00";
-       signal ps2_clk:  std_logic;
-       signal ps2_data: std_logic;
-
-begin
-
-       ps2_clk <= 'H';
-       ps2_data <= 'H';
-
-       p_test: process
-       begin
-               ps2_clk <= 'Z';
-               ps2_data <= 'Z';
-
-               -- Receive bytes form device
-               wait for 100 ns;
-               tx_byte <= x"a5";
-               wait for 1 ms;
-               tx_byte <= x"a5";
-               wait for 400 us;
-               ps2_clk <= '0';  -- Cancel transmission from host
-               wait for 400 us;
-               ps2_clk <= 'Z';
-               wait for 200 us;
-               tx_byte <= x"c3";
-               wait for 1 ms;
-
-               -- Transmit byte to device
-               ps2_clk <= '0';
-               wait for 30 us;
-               ps2_data <= '0';  -- start
-               wait for 30 us;
-               ps2_clk <= 'Z';
-               wait until ps2_clk = '0';
-               ps2_data <= '0';  -- 0
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- 1
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= '0';  -- 2
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- 3
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= '0';  -- 4
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= '0';  -- 5
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- 6
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- 7
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- parity
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= '0';  -- stop
-               wait until ps2_clk /= '0';
-               wait until ps2_clk = '0';
-               ps2_data <= 'Z';  -- ack
-               assert ps2_data = '0'
-                       report "NACK from device"
-                       severity error;
-               wait until ps2_clk /= '0';
-               wait;
-       end process;
-
-       e_ps2_device: entity work.ps2_device
-               port map (
-                       fast     => true,
-
-                       tx_byte  => tx_byte,
-
-                       ps2_clk  => ps2_clk,
-                       ps2_data => ps2_data
-               );
-
-end;
diff --git a/libraries/simulated/tests/test_simflash.vhd b/libraries/simulated/tests/test_simflash.vhd
deleted file mode 100644 (file)
index 6cb2456..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-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";
-               d      <= "ZZZZZZZZZZZZZZZZ";
-               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;
-
-               -- Test a page read
-               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 for 100 ns;
-
-               -- Send the write-word command
-               a    <= x"000008";
-               d    <= x"0040";
-               ce   <= "000";
-               we_n <= '0';
-               wait for 80 ns;
-               we_n <= '1';
-               ce   <= "111";
-               wait for 35 ns;
-
-               -- Send the word to write
-               d    <= x"1234";
-               ce   <= "000";
-               we_n <= '0';
-               wait for 80 ns;
-               we_n <= '1';
-               ce   <= "111";
-               wait for 35 ns;
-
-               -- Wait for the write to complete
-               wait until sts /= '0';
-               wait for 50 ns;
-
-               -- Send the read-array command
-               d    <= x"00ff";
-               ce   <= "000";
-               we_n <= '0';
-               wait for 80 ns;
-               we_n <= '1';
-               ce   <= "111";
-               wait for 35 ns;
-
-               -- Read back the value we wrote
-               d    <= "ZZZZZZZZZZZZZZZZ";
-               ce   <= "000";
-               oe_n <= '0';
-               wait for 80 ns;
-               oe_n <= '1';
-               ce   <= "111";
-               wait for 35 ns;
-
-               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;
diff --git a/libraries/simulated/tests/test_simmem.vhd b/libraries/simulated/tests/test_simmem.vhd
deleted file mode 100644 (file)
index e00ba09..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-
-library work;
-
-
-entity test_simmem is
-end test_simmem;
-
-
-architecture behavior of test_simmem is
-
-       signal rst_i: std_logic;
-       signal clk_i: std_logic;
-       signal cyc_i: std_logic;
-       signal stb_i: std_logic;
-       signal we_i:  std_logic;
-       signal ack_o: std_logic;
-       signal adr_i: std_logic_vector(4 downto 0);
-       signal dat_i: std_logic_vector(7 downto 0);
-       signal dat_o: std_logic_vector(7 downto 0);
-
-begin
-
-       p_test: process
-       begin
-               -- Initial values
-               cyc_i <= '0';
-               stb_i <= '0';
-               we_i  <= '0';
-               adr_i <= (others => '0');
-               dat_i <= (others => '0');
-
-               -- Reset
-               rst_i <= '1';
-               wait for 40 ns;
-               rst_i <= '0';
-
-               -- Done
-               wait;
-       end process;
-
-
-       e_uut: entity work.wb_memory
-               generic map (
-                       INIT => x"00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210"
-               )
-               port map (
-                       rst_i => rst_i,
-                       clk_i => clk_i,
-                       cyc_i => cyc_i,
-                       stb_i => stb_i,
-                       we_i  => we_i,
-                       ack_o => ack_o,
-                       adr_i => adr_i,
-                       dat_i => dat_i,
-                       dat_o => dat_o
-               );
-
-
-       p_clk: process
-       begin
-               clk_i <= '0';
-               wait for 10 ns;
-               clk_i <= '1';
-               wait for 10 ns;
-       end process;
-
-end;
diff --git a/libraries/simulated/util_assert_pulsewidth.vhd b/libraries/simulated/util_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/util_assert_setuphold.vhd b/libraries/simulated/util_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/util_assert_setuphold_vec.vhd b/libraries/simulated/util_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/util_changed_within_t.vhd b/libraries/simulated/util_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/util_changed_within_t_vec.vhd b/libraries/simulated/util_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;
diff --git a/libraries/simulated/util_delay_edges.vhd b/libraries/simulated/util_delay_edges.vhd
new file mode 100644 (file)
index 0000000..5bf472e
--- /dev/null
@@ -0,0 +1,35 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity delay_edges is
+       generic (
+               D_RISE: time := 0 ns;
+               D_FALL: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic;
+               sig_out: out std_logic
+       );
+end delay_edges;
+
+architecture behavioral of delay_edges is
+       signal mask: std_logic;
+begin
+
+       process (sig_in)
+       begin
+               if mask = 'U' then
+                       mask <= sig_in;
+               end if;
+               if sig_in'event and sig_in = '1' then
+                       mask <= '1' after D_RISE;
+               end if;
+               if sig_in'event and sig_in = '0' then
+                       mask <= '0' after D_FALL;
+               end if;
+       end process;
+       sig_out <= mask;
+
+end behavioral;
+
diff --git a/libraries/simulated/util_pkg.vhd b/libraries/simulated/util_pkg.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/wb_memory.vhd b/libraries/simulated/wb_memory.vhd
deleted file mode 100644 (file)
index 13a8dcc..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.numeric_std.all;
-
-
-entity wb_memory is
-       generic (
-               INIT: std_logic_vector(32*8-1 downto 0) := (others => '0')
-       );
-       port (
-               rst_i: in  std_logic;
-               clk_i: in  std_logic;
-
-               cyc_i: in  std_logic;
-               stb_i: in  std_logic;
-               we_i:  in  std_logic;
-               ack_o: out std_logic;
-               adr_i: in  std_logic_vector(4 downto 0);
-               dat_i: in  std_logic_vector(7 downto 0);
-               dat_o: out std_logic_vector(7 downto 0)
-       );
-end wb_memory;
-
-
-architecture behavioral of wb_memory is
-
-       type mem_array_t is array(natural range <>) of std_logic_vector(7 downto 0);
-
-       signal mem_array: mem_array_t(31 downto 0);
-
-begin
-
-       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
-       begin
-               if rising_edge(clk_i) then
-                       if rst_i = '1' then
-                               for i in 0 to 31 loop
-                                       mem_array(31-i) <= INIT((i+1)*8-1 downto i*8);
-                               end loop;
-                       elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then
-                               mem_array(to_integer(unsigned(adr_i))) <= dat_i;
-                       end if;
-               end if;
-       end process;
-
-       ack_o <= '1';
-       dat_o <= mem_array(to_integer(unsigned(adr_i)));
-
-end behavioral;