--- /dev/null
+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;
--- /dev/null
+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;