From 9893c0c73459241d667a2bba962dd29ecb29f392 Mon Sep 17 00:00:00 2001 From: rs <> Date: Thu, 28 Aug 2025 13:37:19 -0500 Subject: [PATCH] Add RO simulated flash and simulation utilities --- libraries/simulated/assert_pulsewidth.vhd | 41 +++ libraries/simulated/assert_setuphold.vhd | 66 ++++ libraries/simulated/assert_setuphold_vec.vhd | 66 ++++ libraries/simulated/changed_within_t.vhd | 24 ++ libraries/simulated/changed_within_t_vec.vhd | 24 ++ libraries/simulated/delay_edges.vhd | 7 +- libraries/simulated/sim_js28f128j3d75.vhd | 316 +++++++++++++++++++ libraries/simulated/sim_utility.vhd | 205 ++++++++++++ libraries/simulated/tests/test_asserts.vhd | 101 ++++++ libraries/simulated/tests/test_simflash.vhd | 67 ++++ 10 files changed, 915 insertions(+), 2 deletions(-) create mode 100644 libraries/simulated/assert_pulsewidth.vhd create mode 100644 libraries/simulated/assert_setuphold.vhd create mode 100644 libraries/simulated/assert_setuphold_vec.vhd create mode 100644 libraries/simulated/changed_within_t.vhd create mode 100644 libraries/simulated/changed_within_t_vec.vhd create mode 100644 libraries/simulated/sim_js28f128j3d75.vhd create mode 100644 libraries/simulated/sim_utility.vhd create mode 100644 libraries/simulated/tests/test_asserts.vhd create mode 100644 libraries/simulated/tests/test_simflash.vhd diff --git a/libraries/simulated/assert_pulsewidth.vhd b/libraries/simulated/assert_pulsewidth.vhd new file mode 100644 index 0000000..a0741e3 --- /dev/null +++ b/libraries/simulated/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/assert_setuphold.vhd b/libraries/simulated/assert_setuphold.vhd new file mode 100644 index 0000000..664d165 --- /dev/null +++ b/libraries/simulated/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/assert_setuphold_vec.vhd b/libraries/simulated/assert_setuphold_vec.vhd new file mode 100644 index 0000000..e362d4b --- /dev/null +++ b/libraries/simulated/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/changed_within_t.vhd b/libraries/simulated/changed_within_t.vhd new file mode 100644 index 0000000..6a6b5a8 --- /dev/null +++ b/libraries/simulated/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/changed_within_t_vec.vhd b/libraries/simulated/changed_within_t_vec.vhd new file mode 100644 index 0000000..f94232c --- /dev/null +++ b/libraries/simulated/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/delay_edges.vhd b/libraries/simulated/delay_edges.vhd index 42ffd41..8cb0856 100644 --- a/libraries/simulated/delay_edges.vhd +++ b/libraries/simulated/delay_edges.vhd @@ -14,15 +14,18 @@ entity delay_edges is end delay_edges; architecture behavioral of delay_edges is + signal mask: std_logic; begin delayed_a: if D_RISE > D_FALL generate - sig_out <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL); + mask <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL); end generate; delayed_b: if D_RISE <= D_FALL generate - sig_out <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL); + mask <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL); end generate; + sig_out <= sig_in when mask = 'U' else mask; + end behavioral; diff --git a/libraries/simulated/sim_js28f128j3d75.vhd b/libraries/simulated/sim_js28f128j3d75.vhd new file mode 100644 index 0000000..03d042c --- /dev/null +++ b/libraries/simulated/sim_js28f128j3d75.vhd @@ -0,0 +1,316 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.sim_utility.all; + + +entity sim_js28f128j3d75 is + generic ( + FILENAME: string := ""; + BASEADDR: natural := 0 + ); + port ( + a: in std_logic_vector(23 downto 0); + d: inout std_logic_vector(15 downto 0); + ce: in std_logic_vector(2 downto 0); + rp_n: in std_logic; + oe_n: in std_logic; + we_n: in std_logic; + sts: out std_logic; -- open drain + byte_n: in std_logic; + vpen: in std_logic + ); +end sim_js28f128j3d75; + + +architecture behavioral of sim_js28f128j3d75 is + ---------------------------------------------------------------------------- + -- Timings for 128Mbit, J3D-75 parts + + -- Read timings + constant T_AVAV: time := 75 ns; -- R1, Read/Write Cycle Time + constant T_AVQV: time := 75 ns; -- R2, Address to Output Delay + constant T_ELQV: time := 75 ns; -- R3, CEx to Output Delay + constant T_GLQV_NA: time := 25 ns; -- R4, OE# to Non-Array Output Delay + constant T_PHQV: time := 210 ns; -- R5, RP# High to Output Delay + constant T_ELQX: time := 0 ns; -- R6, CEx to Output Low Z + constant T_GLQX: time := 0 ns; -- R7, OE# to Output Low Z + constant T_EHQZ: time := 25 ns; -- R8, CEx High to Output in High Z + constant T_GHQZ: time := 15 ns; -- R9, OE# High to Output in High Z + constant T_OH: time := 0 ns; -- R10, Output Hold from Address, CEx, or OE# change + constant T_ELFL: time := 10 ns; -- R11, CEx Low to BYTE# High or Low + constant T_ELFH: time := 10 ns; -- R11, (Same as Above) + constant T_FLQV: time := 1 us; -- R12, BYTE# to Output Delay + constant T_FHQV: time := 1 us; -- R12, (Same as Above) + constant T_FLQZ: time := 1 us; -- R13, BYTE# to Output in High Z + constant T_EHEL: time := 0 ns; -- R14, CEx High to CEx Low + constant T_APA: time := 25 ns; -- R15, Page Address Access Time + constant T_GLQV_A: time := 25 ns; -- R16, OE# to Array Output Delay + + -- Write timings + constant T_PHWL: time := 210 ns; -- W1, RP# High Recovery to WE# (CEx) Going Low + constant T_PHEL: time := 210 ns; -- W1, (Same as Above) + constant T_ELWL: time := 0 ns; -- W2, CEx (WE#) Low to WE# (CEx) Going Low + constant T_WLEL: time := 0 ns; -- W2, (Same as Above) + constant T_WP: time := 60 ns; -- W3, Write Pulse Width + constant T_DVWH: time := 50 ns; -- W4, Data Setup to WE# (CEx) Going High + constant T_DVEH: time := 50 ns; -- W4, (Same as Above) + constant T_AVWH: time := 55 ns; -- W5, Address Setup to WE# (CEx) Going High + constant T_AVEH: time := 55 ns; -- W5, (Same as Above) + constant T_WHEH: time := 0 ns; -- W6, CEx (WE#) Hold from WE# (CEx) High + constant T_EHWH: time := 0 ns; -- W6, (Same as Above) + constant T_WHDX: time := 0 ns; -- W7, Data Hold from WE# (CEx) High + constant T_EHDX: time := 0 ns; -- W7, (Same as Above) + constant T_WHAX: time := 0 ns; -- W8, Address Hold from WE# (CEx) High + constant T_EHAX: time := 0 ns; -- W8, (Same as Above) + constant T_WPH: time := 30 ns; -- W9, Write Pulse Width High + constant T_VPWH: time := 0 ns; -- W11, Vpen Setup to WE# (CEx) Going High + constant T_VPEH: time := 0 ns; -- W11, (Same as Above) + constant T_WHGL: time := 35 ns; -- W12, Write Recovery before Read + constant T_EHGL: time := 35 ns; -- W12, (Same as Above) + constant T_WHRL: time := 500 ns; -- W13, WE# (CEx) High to STS Going Low + constant T_EHRL: time := 500 ns; -- W13, (Same as Above) + constant T_QVVL: time := 0 ns; -- W15, Vpen Hold from Valid SRD, STS Going High + + -- Configuration timings + constant T_WHQV3: time := 175 us; -- W16, Byte Program Time (Using Word/Byte Program Command) + constant T_EHQV3: time := 175 us; -- W16, (Same as above) + constant T_WHQV4: time := 4 sec; -- W16, Block Erase Time + constant T_EHQV4: time := 4 sec; -- W16, (Same as above) + constant T_WHQV5: time := 60 us; -- W16, Set Lock-Bit Time + constant T_EHQV5: time := 60 us; -- W16, (Same as above) + constant T_WHQV6: time := 0.7 sec; -- W16, Clear Block Lock-Bits Time + constant T_EHQV6: time := 0.7 sec; -- W16, (Same as above) + constant T_WHRH1: time := 20 us; -- W16, Program Suspend Latency Time to Read + constant T_EHRH1: time := 20 us; -- W16, (Same as above) + constant T_WHRH: time := 20 us; -- W16, Erase Suspend Latency Time to Read + constant T_EHRH: time := 20 us; -- W16, (Same as above) + constant T_STS: time := 500 ns; -- WY, STS Pulse Width Low Time + + -- Reset timings + constant T_PLPH: time := 25 us; -- P1, RP# Pulse Low Time + constant T_PHRH: time := 100 ns; -- P2, RP# High to Reset During Block-Erase, Program, or Lock-Bit Configuration + constant T_VCCPH: time := 60 us; -- P3, Vcc Power Valid to RP# de-assertion (high) + + ---------------------------------------------------------------------------- + -- Useful internal signals + + signal internal_ce_n: std_logic; + signal internal_we_n: std_logic; + + ---------------------------------------------------------------------------- + -- Status signals + + signal sts_ready: std_logic; + + ---------------------------------------------------------------------------- + -- Array value + + shared variable flash_array: sparse_t; + signal array_word: std_logic_vector(15 downto 0); + + ---------------------------------------------------------------------------- + -- Output value + + signal word: std_logic_vector(15 downto 0); + signal word_x: std_logic_vector(15 downto 0); + signal word_xz: std_logic_vector(15 downto 0); + + signal xmask_rp: std_logic; + signal xmask_byte: std_logic; + signal xmask_ce: std_logic; + signal xmask_oe: std_logic; + signal xmask_addr_h: std_logic; + signal xmask_addr_l: std_logic; + + signal zmask_ce: std_logic; + signal zmask_oe: std_logic; + signal zmask_byte: std_logic; + +begin + + ---------------------------------------------------------------------------- + -- Useful internal signals composed from external signals + + -- Decode multiple chip enable inputs into single chip enable signal + with ce select internal_ce_n <= + '0' when "000", + '1' when "001", + '1' when "010", + '1' when "011", + '0' when "100", + '0' when "101", + '0' when "110", + '1' when "111", + 'X' when others; + + -- Write latch is triggered by either WEx or CEx going high, whichever is first + internal_we_n <= we_n or internal_ce_n; + + -- STS signal represents the state of the WSM + sts <= 'H' when sts_ready = '1' else '0'; + + + ---------------------------------------------------------------------------- + -- Array value + + process + begin + if FILENAME = "" then + flash_array := sparse_create(default => x"FF"); + else + flash_array := sparse_create; + sparse_load(flash_array, FILENAME, BASEADDR); + end if; + wait; + end process; + + array_word <= sparse_get(flash_array, to_integer(unsigned(a(a'high downto 1) & '0'))) & + sparse_get(flash_array, to_integer(unsigned(a(a'high downto 1) & '1'))) after T_OH; + + + ---------------------------------------------------------------------------- + -- Output value + + word <= array_word; + + e_xmask_rp: entity work.changed_within_t + generic map (T => T_PHQV) + port map (sig_in => rp_n, sig_out => xmask_rp); + + e_xmask_byte: entity work.changed_within_t + generic map (T => T_FLQV) + port map (sig_in => byte_n, sig_out => xmask_byte); + + e_xmask_ce: entity work.delay_edges + generic map (D_RISE => T_OH, D_FALL => T_ELQV) + port map (sig_in => internal_ce_n, sig_out => xmask_ce); + + e_xmask_oe: entity work.delay_edges + generic map (D_RISE => T_OH, D_FALL => T_GLQV_A) + port map (sig_in => oe_n, sig_out => xmask_oe); + + e_xmask_addr_h: entity work.changed_within_t_vec + generic map (T => T_AVQV, T_HOLD => T_OH) + port map (sig_in => a(a'high downto 3), sig_out => xmask_addr_h); + + e_xmask_addr_l: entity work.changed_within_t_vec + generic map (T => T_APA, T_HOLD => T_OH) + port map (sig_in => a(2 downto 0), sig_out => xmask_addr_l); + + word_x <= (others => 'X') when (xmask_rp or xmask_ce or xmask_oe or xmask_addr_h or xmask_addr_l) = '1' else word; + + e_zmask_ce: entity work.delay_edges + generic map (D_RISE => T_EHQZ, D_FALL => T_ELQX) + port map (sig_in => internal_ce_n, sig_out => zmask_ce); + + e_zmask_oe: entity work.delay_edges + generic map (D_RISE => T_GHQZ, D_FALL => T_GLQX) + port map (sig_in => oe_n, sig_out => zmask_oe); + + word_xz <= (others => 'Z') when (zmask_ce or zmask_oe) = '1' else word_x; + + zmask_byte <= not byte_n after T_FLQZ; + + d(15 downto 8) <= (others => 'Z') when zmask_byte = '1' else word_xz(15 downto 8); + d( 7 downto 0) <= word_xz( 7 downto 0) when byte_n = '1' else + word_xz( 7 downto 0) when a(0) = '0' else + word_xz(15 downto 8); + + + ---------------------------------------------------------------------------- + -- Check for erroneous usages + + -- Should not try to read and write at the same time + process (oe_n, we_n) + begin + assert not (oe_n = '0' and we_n = '0') + report "OE# and WE# should never be enabled simultaneously." + severity error; + end process; + + -- Address must be valid for a full read or write cycle + R1: process (oe_n, a(23 downto 4)) + begin + -- FIXME: Check more address bits if in non-array or 4-word array modes + if a(23 downto 4)'event and oe_n = '0' then + assert a(23 downto 4)'last_event >= T_AVAV; + report "T_AVAV not met (Read/Write Cycle Time)" + severity error; + end if; + end process; + + -- BYTE# cannot change more than T_ELFL/T_ELFH after CEx goes low + -- This might be another way of describing a negative setup time requirement + R11: process (byte_n, internal_ce_n) + begin + if byte_n'event and internal_ce_n = '0' then + assert internal_ce_n'last_event <= T_ELFL; + report "T_ELFL/H not met (CEx Low to BYTE# High or Low)" + severity error; + end if; + end process; + + W1a: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_PHWL, NAME_SIG => "WE#", NAME_REF => "RP#") + port map (sig => we_n, ref => rp_n); + + W1b: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_PHEL, NAME_SIG => "CEx", NAME_REF => "RP#") + port map (sig => internal_ce_n, ref => rp_n); + + W2a: entity work.assert_setuphold + generic map (LEVEL => '0', T_SETUP => T_ELWL, NAME_SIG => "CEx", NAME_REF => "WE#") + port map (sig => internal_ce_n, ref => we_n); + + W2b: entity work.assert_setuphold + generic map (LEVEL => '0', T_SETUP => T_WLEL, NAME_SIG => "WE#", NAME_REF => "CEx") + port map (sig => we_n, ref => internal_ce_n); + + W3: entity work.assert_pulsewidth + generic map (LEVEL => '0', T_MIN => T_WP, NAME => "WE#") + port map (sig => we_n); + + W4: entity work.assert_setuphold_vec + generic map (LEVEL => '1', T_SETUP => T_DVWH, NAME_SIG => "Data", NAME_REF => "WE#/CEx") + port map (sig => d, ref => internal_we_n); + + W5: entity work.assert_setuphold_vec + generic map (LEVEL => '1', T_SETUP => T_AVWH, NAME_SIG => "Addr", NAME_REF => "WE#/CEx") + port map (sig => a, ref => internal_we_n); + + W6a: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_WHEH, NAME_SIG => "CEx", NAME_REF => "WE#") + port map (sig => internal_ce_n, ref => we_n); + + W6b: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_EHWH, NAME_SIG => "WE#", NAME_REF => "CEx") + port map (sig => we_n, ref => internal_ce_n); + + W7: entity work.assert_setuphold_vec + generic map (LEVEL => '1', T_HOLD => T_WHDX, NAME_SIG => "Data", NAME_REF => "WE#/CEx") + port map (sig => d, ref => internal_we_n); + + W8: entity work.assert_setuphold_vec + generic map (LEVEL => '1', T_HOLD => T_WHAX, NAME_SIG => "Addr", NAME_REF => "WE#/CEx") + port map (sig => a, ref => internal_we_n); + + W9: entity work.assert_pulsewidth + generic map (LEVEL => '1', T_MIN => T_WPH, NAME => "WE#") + port map (sig => we_n); + + W11: entity work.assert_setuphold + generic map (LEVEL => '1', T_SETUP => T_VPWH, NAME_SIG => "Vpen", NAME_REF => "WE#/CEx") + port map (sig => vpen, ref => internal_we_n); + + W12: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_WHGL, NAME_SIG => "OE#", NAME_REF => "WE#/CEx") + port map (sig => oe_n, ref => internal_we_n); + + W15: entity work.assert_setuphold + generic map (LEVEL => '1', T_HOLD => T_QVVL, NAME_SIG => "Vpen", NAME_REF => "sts") + port map (sig => vpen, ref => sts_ready); + +end behavioral; diff --git a/libraries/simulated/sim_utility.vhd b/libraries/simulated/sim_utility.vhd new file mode 100644 index 0000000..2fc2bb0 --- /dev/null +++ b/libraries/simulated/sim_utility.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/tests/test_asserts.vhd b/libraries/simulated/tests/test_asserts.vhd new file mode 100644 index 0000000..a891789 --- /dev/null +++ b/libraries/simulated/tests/test_asserts.vhd @@ -0,0 +1,101 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_asserts is +end test_asserts; + + +architecture behavior of test_asserts is + + constant T: time := 10 ns; + + signal sig: std_logic; + signal stb: std_logic; + signal clk: std_logic; + +begin + + p_test_setuphold: process is + begin + stb <= '0'; + wait for 3 ns; + stb <= '1'; -- Make sure the beginning of simulation isn't treated as a transition for setup/hold time + wait for 12 ns; + stb <= '0'; + wait for 12 ns; + stb <= '1'; + wait for 3 ns; + report "Setup time violation expected now"; + wait for 7 ns; + stb <= '0'; + wait for 25 ns; + stb <= '1'; -- Make sure only rising edge, not falling edge, is checked for setup/hold time + wait for 30 ns; + report "Hold time violation expected now"; + stb <= '0'; + + wait; + end process; + + + p_test_width: process is + begin + sig <= '0'; + wait for 5 ns; + sig <= '1'; + report "Minimum pulse width violation expected now"; + wait for 30 ns; + sig <= '1'; + wait for 30 ns; + sig <= '0'; + report "Maximum pulse width violation expected now"; + wait for 30 ns; + sig <= '1'; + wait for 10 ns; + sig <= '0'; + + wait; + end process; + + + e_uut_0: entity work.assert_pulsewidth + generic map ( + LEVEL => '0', + T_MIN => 10 ns, + NAME => "test_sig" + ) + port map (sig => sig); + + + e_uut_1: entity work.assert_pulsewidth + generic map ( + LEVEL => '1', + T_MAX => 30 ns, + NAME => "test_sig" + ) + port map (sig => sig); + + + e_uut_2: entity work.assert_setuphold + generic map ( + LEVEL => '1', + T_SETUP => 5 ns, + T_HOLD => 5 ns, + NAME_REF => "clk", + NAME_SIG => "stb" + ) + port map (sig => stb, ref => clk); + + + p_clk: process is + begin + clk <= '0'; + wait for 30 ns; + clk <= '1'; + wait for 30 ns; + end process; + +end; diff --git a/libraries/simulated/tests/test_simflash.vhd b/libraries/simulated/tests/test_simflash.vhd new file mode 100644 index 0000000..b8ce84c --- /dev/null +++ b/libraries/simulated/tests/test_simflash.vhd @@ -0,0 +1,67 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity test_simflash is +end test_simflash; + + +architecture behavior of test_simflash is + + signal a: std_logic_vector(23 downto 0); + signal d: std_logic_vector(15 downto 0); + signal ce: std_logic_vector(2 downto 0); + signal rp_n: std_logic := '0'; + signal oe_n: std_logic; + signal we_n: std_logic; + signal sts: std_logic; + signal byte_n: std_logic; + signal vpen: std_logic; + +begin + + p_test: process + begin + -- Initial values and reset + a <= x"000000"; + ce <= "111"; + rp_n <= '0'; + oe_n <= '1'; + we_n <= '1'; + byte_n <= '1'; + vpen <= '1'; + wait for 10 ns; + rp_n <= '1'; + wait for 1 us; + ce <= "000"; + oe_n <= '0'; + wait for 100 ns; + a <= x"000002"; + wait for 50 ns; + a <= x"000004"; + wait for 50 ns; + a <= x"000006"; + wait for 50 ns; + oe_n <= '1'; + ce <= "111"; + + wait; + end process; + + e_dut: entity work.sim_js28f128j3d75 + generic map (FILENAME => "/home/ryan/Dropbox/Projects/VHDL/libraries/simulated/tests/test.bin") + port map ( + a => a, + d => d, + ce => ce, + rp_n => rp_n, + oe_n => oe_n, + we_n => we_n, + sts => sts, + byte_n => byte_n, + vpen => vpen + ); +end; -- 2.43.0