From: Ryan <> Date: Wed, 17 Sep 2025 21:06:04 +0000 (-0500) Subject: Organize simulation library X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=7c1fc341b87f2737090282618f41dff7ad832277;p=vhdl Organize simulation library --- diff --git a/libraries/simulated/assert_pulsewidth.vhd b/libraries/simulated/assert_pulsewidth.vhd deleted file mode 100644 index a0741e3..0000000 --- a/libraries/simulated/assert_pulsewidth.vhd +++ /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 := "" - ); - 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 index 664d165..0000000 --- a/libraries/simulated/assert_setuphold.vhd +++ /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 := ""; - NAME_SIG: string := "" - ); - 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 index e362d4b..0000000 --- a/libraries/simulated/assert_setuphold_vec.vhd +++ /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 := ""; - NAME_SIG: string := "" - ); - 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 index 6a6b5a8..0000000 --- a/libraries/simulated/changed_within_t.vhd +++ /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 index f94232c..0000000 --- a/libraries/simulated/changed_within_t_vec.vhd +++ /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 index 5bf472e..0000000 --- a/libraries/simulated/delay_edges.vhd +++ /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 index 0000000..ed5b6d9 --- /dev/null +++ b/libraries/simulated/dev_genericmem.vhd @@ -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 index 0000000..5a0beb3 --- /dev/null +++ b/libraries/simulated/dev_js28f128j3d75.vhd @@ -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 index 0000000..c1e176a --- /dev/null +++ b/libraries/simulated/dev_mt45w8mw16bgx.vhd @@ -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 index c3ace7b..0000000 --- a/libraries/simulated/js28f128j3d75.vhd +++ /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 index fbcbb9c..0000000 --- a/libraries/simulated/mt45w8mw16bgx.vhd +++ /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 index 0000000..b0cffb4 --- /dev/null +++ b/libraries/simulated/proto_ps2.vhd @@ -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 index fe79f68..0000000 --- a/libraries/simulated/ps2_device.vhd +++ /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 index a024d7e..0000000 --- a/libraries/simulated/sim_js28f128j3d75.vhd +++ /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 index 2fc2bb0..0000000 --- a/libraries/simulated/sim_utility.vhd +++ /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 index e651e3a..0000000 --- a/libraries/simulated/tests/test_attrs.vhd +++ /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 index 0000000..882fd2b --- /dev/null +++ b/libraries/simulated/tests/test_genericmem.vhd @@ -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; diff --git a/libraries/simulated/tests/test_js28f128j3d75.vhd b/libraries/simulated/tests/test_js28f128j3d75.vhd index b84c0d1..cf65f78 100644 --- a/libraries/simulated/tests/test_js28f128j3d75.vhd +++ b/libraries/simulated/tests/test_js28f128j3d75.vhd @@ -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; diff --git a/libraries/simulated/tests/test_mt45w8mw16bgx.vhd b/libraries/simulated/tests/test_mt45w8mw16bgx.vhd index d045fb1..c5d5a7a 100644 --- a/libraries/simulated/tests/test_mt45w8mw16bgx.vhd +++ b/libraries/simulated/tests/test_mt45w8mw16bgx.vhd @@ -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 index 0000000..7b0bdf9 --- /dev/null +++ b/libraries/simulated/tests/test_ps2.vhd @@ -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 index 46418f7..0000000 --- a/libraries/simulated/tests/test_ps2_device.vhd +++ /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 index 6cb2456..0000000 --- a/libraries/simulated/tests/test_simflash.vhd +++ /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 index e00ba09..0000000 --- a/libraries/simulated/tests/test_simmem.vhd +++ /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 index 0000000..a0741e3 --- /dev/null +++ b/libraries/simulated/util_assert_pulsewidth.vhd @@ -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 := "" + ); + 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 index 0000000..664d165 --- /dev/null +++ b/libraries/simulated/util_assert_setuphold.vhd @@ -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 := ""; + NAME_SIG: string := "" + ); + 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 index 0000000..e362d4b --- /dev/null +++ b/libraries/simulated/util_assert_setuphold_vec.vhd @@ -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 := ""; + NAME_SIG: string := "" + ); + 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 index 0000000..6a6b5a8 --- /dev/null +++ b/libraries/simulated/util_changed_within_t.vhd @@ -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 index 0000000..f94232c --- /dev/null +++ b/libraries/simulated/util_changed_within_t_vec.vhd @@ -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 index 0000000..5bf472e --- /dev/null +++ b/libraries/simulated/util_delay_edges.vhd @@ -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 index 0000000..2fc2bb0 --- /dev/null +++ b/libraries/simulated/util_pkg.vhd @@ -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 index 13a8dcc..0000000 --- a/libraries/simulated/wb_memory.vhd +++ /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;