------------------------------------------------------------------------------ -- INTEL DEVELOPER'S SOFTWARE LICENSE AGREEMENT -- -- BY USING THIS SOFTWARE, YOU ARE AGREEING TO BE BOUND -- BY THE TERMS OF THIS AGREEMENT. DO NOT USE THE SOFTWARE -- UNTIL YOU HAVE CAREFULLY READ AND AGREED TO THE FOLLOWING -- TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO THE TERMS -- OF THIS AGREEMENT, PROMPTLY RETURN THE SOFTWARE PACKAGE -- AND ANY ACCOMPANYING ITEMS. -- -- IF YOU USE THIS SOFTWARE, YOU WILL BE BOUND BY THE TERMS -- OF THIS AGREEMENT. -- -- LICENSE: Intel Corporation ("Intel") grants you -- the non-exclusive right to use the enclosed software -- program ("Software"). You will not use, copy, modify, -- display, rent, sell or transfer the Software or any portion -- thereof, except as provided in this Agreement. -- -- System OEM Developers may: -- 1. copy the Software for internal support, backup -- or archival purposes; -- 2. internally install, use, display, or distribute -- Intel owned Software in object code format; -- 3. internally modify Software source code that -- Intel makes available to you for internal use -- only as an OEM Developer; -- 4. internally install, use, display, modify, distribute, -- and/or make derivatives ("Derivatives") of Intel owned -- Software ONLY if you are a System OEM Developer and -- NOT an end-user. -- -- RESTRICTIONS: -- -- YOU WILL NOT: -- 1. copy the Software, in whole or in part, except as -- provided for in this Agreement; -- 2. decompile or reverse engineer any Software provided -- in object code format; -- 3. distribute any Software or Derivative code to any -- end-users, unless approved by Intel in a prior writing. -- -- TRANSFER: You may not transfer the Software to any third -- party without Intel's prior written consent. -- -- OWNERSHIP AND COPYRIGHT OF SOFTWARE: Title to the Software -- and all copies thereof remain with Intel or its vendors. -- The Software is copyrighted and is protected by United States -- and international copyright laws. You will not remove the -- copyright notice from the Software. You agree to prevent -- any unauthorized copying of the Software. -- -- DERIVATIVE WORK: OEM Developers that make Derivatives will -- not be required to provide Intel with a copy of the source -- or object code. Any modification of Software shall be at -- your sole risk and expense. No Software or Derivative -- distribution to any third party is permitted under this -- Agreement. -- -- DUAL MEDIA SOFTWARE: If the Software package contains -- multiple media, you may only use the medium appropriate -- for your system. -- -- WARRANTY: Intel warrants that it has the right to license -- you to use, modify, display, or distribute the Software as -- provided in this Agreement. The Software is provided "AS IS" -- without WARRANTY of any kind. Intel makes no representations -- to upgrade, maintain, or support the Software at any time. -- -- -- THE ABOVE WARRANTIES ARE THE ONLY WARRANTIES OF ANY -- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES -- OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE -- OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER -- INTELLECTUAL PROPERTY RIGHT. -- -- LIMITATION OF LIABILITY: NEITHER INTEL NOR ITS -- VENDORS OR AGENTS SHALL BE LIABLE FOR ANY LOSS -- OF PROFITS, LOSS OF USE, LOSS OF DATA, INTERRUPTION -- OF BUSINESS, NOR FOR INDIRECT, SPECIAL, INCIDENTAL -- OR CONSEQUENTIAL DAMAGES OF ANY KIND WHETHER UNDER -- THIS AGREEMENT OR OTHERWISE, EVEN IF ADVISED OF THE -- POSSIBILITY OF SUCH DAMAGES. -- -- TERMINATION OF THIS LICENSE: Intel reserves the right -- to conduct or have conducted audits to verify your -- compliance with this Agreement. Intel may terminate -- this Agreement at any time if you are in breach of -- any of its terms and conditions. Upon termination, -- you will immediately destroy, and certify in writing -- the destruction of, the Software or return all copies -- of the Software and documentation to Intel. -- -- U.S. GOVERNMENT RESTRICTED RIGHTS: The Software and -- documentation were developed at private expense and -- are provided with "RESTRICTED RIGHTS". Use, duplication -- or disclosure by the Government is subject to restrictions -- as set forth in FAR52.227-14 and DFAR252.227-7013 et seq. -- or its successor. -- -- EXPORT LAWS: You agree that the distribution and -- export/re-export of the Software is in compliance -- with the laws, regulations, orders or other restrictions -- of the U.S. Export Administration Regulations. -- -- APPLICABLE LAW: This Agreement is governed by the -- laws of the State of California and the United States, -- including patent and copyright laws. Any claim -- arising out of this Agreement will be brought in -- Santa Clara County, California. -- -- Copyright 1996, Intel Corporation, All Rights Reserved ------------------------------------------------------------------------------ -- Copyright 1997, Intel Corporation -- Functionality and specifications based on Smart 3 Advanced Boot Block Word-Wide -- 28F160B3 specifications (order number 290580-002) -- Refer to Intel Literature Center (800) 548-4725 for specifications. -- Release History -- -- Date Rev Comments library ieee; use std.textio.all; use ieee.std_logic_1164.all; ----------------------------------- entity F160B3T120 is port ( dq : inout std_logic_vector(15 downto 0); addr : in std_logic_vector(19 downto 0); wpb : in bit; ceb : in bit; rpb : in bit; oeb : in bit; web : in bit; vpp : in real; vcc : in real; vccq : in real ); end F160B3T120; architecture behavior of F160B3T120 is -- These define the size of the device. -- MaxAddrLine is the number of the highest-order address line, when the byte-order line -- is numbered A0. A20, then, is the MaxAddrLine for a 16-meg device. CONSTANT MaxAddrLine : INTEGER := 17; -- 2 megabit -- The real MaxAddrLine for the 16-meg part is below. It is commented out to allow us to simulate a -- smaller part, so our simulation runs faster. -- CONSTANT MaxAddrLine : INTEGER := 20; -- Note - this is obviously size-related CONSTANT length : INTEGER := (2**(MaxAddrLine) - 1); -- These commands form the Intel Standard Command Set / Basic Command Set. CONSTANT ClearSRCmd : INTEGER := 16#50# ; CONSTANT EraseSingleBlockCmd : INTEGER := 16#20# ; CONSTANT ProgramCmd : INTEGER := 16#10# ; CONSTANT Program2Cmd : INTEGER := 16#40# ; CONSTANT ReadArrayCmd : INTEGER := 16#FF# ; CONSTANT ReadCSRCmd : INTEGER := 16#70# ; CONSTANT ReadIDCmd : INTEGER := 16#90# ; CONSTANT ResumeCmd : INTEGER := 16#D0# ; CONSTANT ConfirmCmd : INTEGER := 16#D0# ; CONSTANT SuspendCmd : INTEGER := 16#B0# ; -- These are values for the ReadType variable. CONSTANT ReadArray : INTEGER := 1; CONSTANT ReadIDCodes : INTEGER := 2; CONSTANT ReadStatReg : INTEGER := 3; -- These are values for the WriteType variable. -- They are also used for the Running variable, which stores the type of operation currently -- occupying the device. CONSTANT WriteCmd : INTEGER := 0; CONSTANT WriteProgram : INTEGER := 1; CONSTANT WriteBlkErase : INTEGER := 2; CONSTANT SuspensionLatency : INTEGER := 8; -- Note: While SuspensionLatency isn't an operation or a valid value for WriteType, it is -- placed here because it is a valid value for the Running variable. When an operation is -- suspended, the device acts as it does when a program or erase is executed; the status -- register is placed on the bus during read cycles, and SR.7 is low -- until the suspension latency time is elapsed. CONSTANT ResetLatency : INTEGER := 9; -- These are the voltage ranges in the 3 volt spec (28FxxxB3). CONSTANT Vcc3v10pcMin : REAL := 2.7; CONSTANT Vcc3v10pcMax : REAL := 3.3; CONSTANT Vcc3v5pcMin : REAL := 2.7; CONSTANT Vcc3v5pcMax : REAL := 2.85; CONSTANT Vpp5vLockout : REAL := 1.5; CONSTANT Vpp3vMin : REAL := 2.7; CONSTANT Vpp3vMax : REAL := 3.6; CONSTANT Vpp12vMin : REAL := 11.4; CONSTANT Vpp12vMax : REAL := 12.6; -- These specs are the 3V AC write specs. constant TPHWL : time := 600 ns; constant TELWL : time := 0 ns; constant TWLEL : time := 0 ns; constant TWLWH : time := 90 ns; constant TELEH : time := 90 ns; constant TSHWH : time := 200 ns; constant TVPWH : time := 200 ns; constant TAVWH : time := 90 ns; constant TDVWH : time := 70 ns; constant TWHDX : time := 0 ns; constant TWHAX : time := 0 ns; constant TEHAX : time := TWHAX; constant TEHDX : time := TWHDX; constant TWHEH : time := 0 ns; constant TEHWH : time := 0 ns; constant TWHWL : time := 30 ns; constant TEHEL : time := 30 ns; constant TWHGL : time := 0 ns; constant TQVVL : time := 0 ns; constant TQVSL : time := 0 ns; constant TPHEL : time := TPHWL; constant TSHEH : time := TSHWH; constant TDVEH : time := TDVWH; constant TAVEH : time := TAVWH; constant TVPEH : time := TVPWH; constant TPLPH : time := 100 ns; constant TPLRH : time := 22 us; -- These are the values for state and new-state. They help us keep track of what's happening -- on the bus, and what sigs and transitions we're looking at. constant Powerup : integer := 0; constant OutputDisable : integer := 1; constant Powerdown : integer := 2; constant ReadCycleZ : integer := 3; constant ReadCycleX : integer := 4; constant ReadCycleV : integer := 5; constant WriteCycle : integer := 8; constant WEActive : integer := 9; -- This signal exists because it is convenient to view Vpp as a logic signal rather than a -- continuous variable to calculate Vpp setup and hold times. SIGNAL VppSig : STD_LOGIC; SIGNAL VccSig : STD_LOGIC; -- This signal exists to clock the main state machine. SIGNAL clock1 : bit := '0'; SIGNAL clock2 : bit := '1'; --*************************************************************** TYPE word IS ARRAY(15 downto 0) OF STD_ULOGIC; TYPE memarray IS ARRAY(0 TO length) OF word; BEGIN MAIN:PROCESS(clock2) -- Scratchpad for converting STD_LOGIC_VECTOR to INTEGER VARIABLE adder :INTEGER := 0; VARIABLE twosum :INTEGER := 1; -- Integer copies of the latched data and the low byte of the latched data VARIABLE data : INTEGER; VARIABLE datalo :INTEGER; -- The entire memory VARIABLE memory :memarray; -- Scratchpad for the integer value of the block number of the latched address VARIABLE BlockNum : INTEGER; -- The Status Register goes here. VARIABLE SR :STD_LOGIC_VECTOR (7 downto 0) := "10000000"; -- This is used to keep track of what information the device will give on the next -- READ bus cycle. See the table in the constants section for examples. VARIABLE ReadType :INTEGER := ReadArray; -- This is used to keep track of what information the device expects to receive on -- the next WRITE bus cycle. See the table in the constants section for examples. VARIABLE WriteType :INTEGER := WriteCmd; -- This is used to store the state the main state machine of the model will enter at -- the next pseudo-clock (0.5 ns). VARIABLE next_state :INTEGER := 0; -- This stores the current state of the main state machine of the model. VARIABLE state:INTEGER := Powerup; -- Scratchpad to compare the value on the address bus to the latched address, to -- watch for transitions. Perhaps unnecessary. VARIABLE equal :BIT; -- One-timer used to initialize all the other variables. VARIABLE loader :BIT:= '0'; -- Latched data and address values go here VARIABLE hold_data : word; VARIABLE hold_add : STD_ULOGIC_VECTOR (MaxAddrLine downto 0); -- When an operation is suspended, the latched data and address values before the -- suspension go here. VARIABLE susp_add : STD_ULOGIC_VECTOR (MaxAddrLine downto 0); -- These variables store the time of the last transition of the respective line -- to the respective state. This is necessary to verify pulse width timing -- requirements, those which measure the time between successive transitions of the -- same signal. VARIABLE timeWH : TIME := 0 ns; VARIABLE timeWL : TIME := 0 ns; VARIABLE timeEH : TIME := 0 ns; VARIABLE timeEL : TIME := 0 ns; VARIABLE timeSRValid : TIME := 0 ns; VARIABLE timeRPL : TIME := 0 ns; -- This holds the WriteType constant of the operation currently busying the device. VARIABLE Running : INTEGER := 0; -- This holds the future time of the pending completion of the operation noted in -- Running. VARIABLE Completion : TIME := 0 ns; -- This variable holds the value of the SR when we move from ReadStateX to ReadStateV. -- This is important for compliance to spec, because it explicitly mentions that the SR is latched -- upon read, and is not refreshed until OE or CE transitions. VARIABLE SRLatch : STD_LOGIC_VECTOR (7 downto 0); -- These variables hold the block number of the block being erased, and the address of the word -- being programmed. If we attempt to read from either of these locations, we return Xes. VARIABLE ErasingBlk : INTEGER; VARIABLE ProgrammingAddr : INTEGER; -- These are flags to determine if a hold time error has been reported. Bad form to report -- the same error every billionth of a second. VARIABLE AddrHoldFlag : bit := '0'; VARIABLE DataHoldFlag : bit := '0'; VARIABLE VppHoldFlag : bit := '0'; VARIABLE WPHoldFlag : bit := '0'; VARIABLE VppMaintLFlag : bit := '0'; VARIABLE VppMaintSFlag : bit := '0'; VARIABLE VccMaintLFlag : bit := '0'; VARIABLE VccMaintSFlag : bit := '0'; -- These are the read timing specs. They became variables to allow multiple Vcc ranges in a given -- model. VARIABLE TAVQV : time; VARIABLE TELQV : time; VARIABLE TPHQV : time; VARIABLE TGLQV : time; VARIABLE TELQX : time; VARIABLE TEHQZ : time; VARIABLE TGLQX : time; VARIABLE TGHQZ : time; VARIABLE TOH : time; VARIABLE TELFL : time; VARIABLE TELFH : time; VARIABLE TFLQV : time; VARIABLE TFHQV : time; VARIABLE TFLQZ : time; -- These times are the latency times. They specify how long each operation leaves the -- device busy (as manifested by SR.7). Adding NOW to -- these times gives a time of completion in the future - comparing subsequent NOWs to -- that time will allow us to note when an operation finishes (and then update the Running, -- and SR.7 variables). VARIABLE W16_ProgWordWOBuffer : time; VARIABLE W16_ParmBlockErase : time; VARIABLE W16_MainBlockErase : time; VARIABLE W16_ProgSuspToRead : time; VARIABLE W16_EraseSuspToRead : time; -- Procedure CalcBlockNum - calculates BlockNum associated with the address in the latch -- This is specific to the top boot subfamily PROCEDURE CalcBlockNum IS BEGIN BlockNum := 0; -- Figure the number of the twosum := 1; -- block to which the latched address FOR j IN 15 TO (MaxAddrLine - 1) LOOP -- corresponds, and store it in IF hold_add(j) = '1' THEN -- BlockNum BlockNum := BlockNum + twosum; END IF; twosum := twosum * 2; END LOOP; IF BlockNum = 2**(MaxAddrLine-15) - 1 THEN -- The address is in a parameter block. twosum := 1; FOR j IN 12 TO 14 LOOP IF hold_add(j) = '1' THEN BlockNum := BlockNum + twosum; END IF; twosum := twosum * 2; END LOOP; END IF; END CalcBlockNum; -- Procedure CalcAdder - calculates adder value associated with address in the latch PROCEDURE CalcAdder IS BEGIN adder := 0; -- This is generic code to take twosum := 1; -- the address in the latch FOR j IN 0 to (MaxAddrLine - 1) LOOP IF (hold_add(j) = '1') THEN -- and calculate adder := adder + twosum; -- the corresponding integer address END IF; -- value in adder. twosum := twosum * 2; -- Remember, hold_add is an array of END LOOP; -- STD_ULOGIC. END CalcAdder; -- MAIN ROUTINE STARTS HERE! BEGIN -- VppSig is high if Vpp is in the valid range IF ((Vpp > Vpp3vMin) AND (Vpp < Vpp3vMax)) THEN VppSig <= '1'; W16_ProgWordWOBuffer := 22 us; -- tWHQV1 / tEHQV1 -- This is the real Parm Block Erase time. It's too long to use practically in a VHDL model. -- W16_ParmBlockErase := 1 sec; -- tWHQV2 / tEHQV2 -- This is a fake ParmBlockErase time to fit simulation capabilities W16_ParmBlockErase := 100 us; -- This is the real Main Block Erase time. It's too long to use practically in a VHDL model. -- W16_MainBlockErase := 1 sec; -- tWHQV2 / tEHQV2 -- This is a fake MainBlockErase time to fit simulation capabilities W16_MainBlockErase := 100 us; W16_ProgSuspToRead := 5 us; -- tWHRH1 / tEHRH1 W16_EraseSuspToRead := 5 us; -- tWHRH2 / tEHRH2 ELSIF ((Vpp > Vpp12vMin) AND (Vpp < Vpp12vMax)) THEN VppSig <= '1'; W16_ProgWordWOBuffer := 8 us; -- tWHQV1 / tEHQV1 -- This is the real Parm Block Erase time. It's too long to use practically in a VHDL model. -- W16_ParmBlockErase := 0.8 sec; -- tWHQV2 / tEHQV2 -- This is a fake ParmBlockErase time to fit simulation capabilities W16_ParmBlockErase := 10 us; -- This is the real Main Block Erase time. It's too long to use practically in a VHDL model. -- W16_MainBlockErase := 1.1 sec; -- tWHQV2 / tEHQV2 -- This is a fake MainBlockErase time to fit simulation capabilities W16_MainBlockErase := 10 us; W16_ProgSuspToRead := 5 us; -- tWHRH1 / tEHRH1 W16_EraseSuspToRead := 6 us; -- tWHRH2 / tEHRH2 ELSE VppSig <= '0'; END IF; -- VccSig is high if Vcc is in the valid range IF ((Vcc > Vcc3v10pcMin) AND (Vcc < Vcc3v10pcMax)) THEN VccSig <= '1'; tAVQV := 120 ns; tELQV := 120 ns; tPHQV := 600 ns; tGLQV := 65 ns; tELQX := 0 ns; TEHQZ := 40 ns; tGLQX := 0 ns; TGHQZ := 40 ns; TOH := 0 ns; ELSE VccSig <= '0'; END IF; -- This section intializes the memory array which contains all 1's. -- It also initializes the BlockStatus memory, marking each block unlocked, -- and sets up the CFI Query table. IF (loader = '0') THEN FOR i IN 0 TO length LOOP memory(i) := "1111111111111111"; END LOOP; SR := "10000000"; loader := '1'; END IF; -- Setup one-shot stuff ends here -- One implicit assumption of the state machine is that no two control signals will -- transition within half a nanosecond of each other. It simplifies design greatly to -- assume that only one transition occurs during any given pseudo-clock. CASE state IS WHEN Powerup => -- This state corresponds to CE, WE, OE, and RP all high (inactive) dq <= "ZZZZZZZZZZZZZZZZ"; -- Outputs are off IF (rpb = '0') THEN -- RP is asserted TimeRPL := NOW; -- Note when rpb went low next_state := Powerdown; -- Go to Powerdown ELSIF (ceb = '0') THEN -- CE is asserted timeEL := NOW; -- Note when ceb went low next_state := OutputDisable; -- Go to OutputDisable ELSIF (web = '0') THEN -- WE is asserted timeWL := NOW; -- Note when web went low next_state := WEActive; -- Go to WEActive ELSE next_state := Powerup; -- Nothing happened END IF; -- END STATE Powerup WHEN OutputDisable => -- This state has CE low/asserted, and WE, OE, and RP high/unasserted. -- From here, we can do a ReadCycle, a WriteCycle, a PowerDown, or -- even go back to PowerUp. IF (rpb = '0') THEN -- RP is asserted TimeRPL := NOW; -- note when RP went low next_state := Powerdown; -- Go to PowerDown ELSIF (ceb = '1') THEN -- CE is unasserted timeEH := NOW; -- Note when CE went high ASSERT (web'LAST_EVENT >= tWHEH) -- Check how long from WE REPORT "Enable Hold violation (tWHEH)." -- going high to CE going high SEVERITY ERROR; -- Enable Hold time next_state := Powerup; -- Return to Powerup ELSIF (oeb = '0') THEN -- OE is asserted - we're -- beginning a read cycle. next_state := ReadCycleZ; -- Go to ReadCycleZ state -- and wait for outputs to -- turn on ELSIF (web = '0') THEN -- WE is asserted - we're -- beginning a write cycle. timeWL := NOW; -- Note when WE went low ASSERT (ceb'LAST_EVENT >= tELWL) -- Verify that CE has been REPORT "Enable Setup violation (tELWL)" -- asserted long enough - SEVERITY ERROR; -- Enable Setup time ASSERT (rpb'LAST_EVENT >= tPHWL) -- Verify that RP has been REPORT "Powerdown Recovery Time violation (tPHWL)" -- unasserted long enough - SEVERITY ERROR; -- Powerdown Recovery time ASSERT (((NOW - web'LAST_EVENT) - timeWH) >= tWHWL) -- Verify that the WE line REPORT "WE Pulse High Time violation (tWHWL)" -- was high long enough - SEVERITY ERROR; -- WE Pulse High time next_state := WriteCycle; --Go to WriteCycle ELSE next_state := OutputDisable; --Nothing happened END IF; -- END STATE OutputDisable -- This state corresponds to WE# asserted (low), and OE# and CE# unasserted (high). -- The only reason to come here is to set up or leave a CE-controlled write. WHEN WEActive => -- WE# Asserted State - for CE-controlled writes IF (rpb = '0') THEN -- RP is asserted TimeRPL := NOW; -- note when rpb became asserted next_state := Powerdown; -- Goes to Powerdown ELSIF (web = '1') THEN -- WE is unasserted again -- Go back to Powerup timeWH := NOW; -- Note when web went high ASSERT (ceb'LAST_EVENT >= tEHWH) -- Verify there was enough time REPORT "Enable Hold violation (tEHWH)." -- from CE high to WE high - SEVERITY ERROR; -- Enable Hold time next_state := Powerup; -- Returns to powerup ELSIF (ceb = '0') THEN -- CE is asserted - about to -- do a WriteCycle timeEL := NOW; -- Note when CE went low ASSERT (web'LAST_EVENT >= tWLEL) -- Verify WE was low long enough REPORT "Enable Setup violation (tWLEL)" -- before CE went low - SEVERITY ERROR; -- Enable Setup time ASSERT (rpb'LAST_EVENT >= tPHEL) -- Verify RP was unasserted long REPORT "Powerdown Recovery Time violation (tPHEL)" -- enough before CE went low - SEVERITY ERROR; -- Powerdown Recovery time ASSERT (((NOW - ceb'LAST_EVENT) - timeEH) >= tEHEL) -- Verify CE was high long REPORT "CE Pulse High Time violation (tEHEL)" -- enough - CE Pulse Width High SEVERITY ERROR; next_state := WriteCycle; -- Goes to WriteCycle ELSE next_state := WEActive; -- Nothing happened END IF; -- END STATE WEActive WHEN ReadCycleZ => -- OE and CE either just became asserted or just became unasserted. -- We're in that portion of a read cycle right before the outputs -- turn on or right after they shut off DQ <= "ZZZZZZZZZZZZZZZZ"; -- All high-Z on the data outputs ASSERT (web'LAST_EVENT >= tWHGL) -- Verify that WE hasn't been asserted in REPORT "Violation of tWHGL (Write recovery before read)." SEVERITY ERROR; -- awhile - Write Recovery Before Read IF (rpb = '0') THEN -- rpb became asserted - go to Powerdown TimeRPL := NOW; -- note when rpb became asserted next_state := PowerDown; ELSIF ((ceb = '0' AND ceb'LAST_EVENT >= tELQX) AND -- If all the hold time requirements (oeb = '0' AND oeb'LAST_EVENT >= tGLQX)) THEN -- are met, turn on the outputs with next_state := ReadCycleX; -- invalid data - go to ReadCycleX SRLatch := SR; -- and latch the SR. ELSIF (ceb = '1') THEN next_state := PowerUp; -- CE became unasserted somehow - -- go to Powerup ELSIF (oeb = '1') THEN next_state := OutputDisable; -- OE became unasserted - we're on -- the back edge of a read cycle. -- Go to OutputDisable ELSE next_state := ReadCycleZ; -- Nothing important happened - -- stay where we are. END IF; -- END STATE ReadCycleZ WHEN ReadCycleX => -- This state corresponds to the two places on the read cycle timing diagram -- where the data is hatched to show that it isn't valid yet. Either we're -- just beginning a read, or we're just leaving it. From here, we go either -- to ReadCycleV, if the data's about to become valid, or to ReadCycleZ, if -- the outputs are about to shut off. DQ <= "XXXXXXXXXXXXXXXX"; -- Place all Xes on the outputs IF (rpb = '0') THEN -- rpb asserted - go to Powerdown state TimeRPL := NOW; next_state := PowerDown; ELSIF (addr'LAST_EVENT >= tAVQV AND rpb'LAST_EVENT >= tPHQV AND ceb'LAST_EVENT >= tELQV AND oeb'LAST_EVENT >= tGLQV) THEN -- If all the setup conditions are met, -- place valid data on the outputs and go to -- ReadCycleV FOR j IN MaxAddrLine downto 0 LOOP -- Latch the address that's on the bus when hold_add(j) := addr(j); -- we go to ReadCycleV, to check to see if END LOOP; -- it changes; if it does, the data goes -- invalid. next_state := ReadCycleV; ELSIF ((oeb = '1' AND oeb'LAST_EVENT >= tGHQZ) -- If one of the control lines has gone high, OR (ceb = '1' AND -- go the other way - turn off the data bus, ceb'LAST_EVENT >= tEHQZ)) THEN -- and go to ReadCycleZ (after a certain lag next_state := ReadCycleZ; -- time) ELSE next_state := ReadCycleX; -- Nothing happened. END IF; -- END STATE ReadCycleX WHEN ReadCycleV => -- This is the moment we've been waiting for - in ReadCycleV, -- all the setup conditions are finally met, and we place -- valid data on the data outputs. The only place to go from here -- is to ReadCycleX, when one of the control inputs changes -- (oeb, ceb, and addr all cause this) CalcAdder; CASE ReadType IS -- This case statement splits us into -- the different read modes. -- The value of ReadType is set -- through sending various commands -- to the device. WHEN ReadArray => -- Reading from the memory itself CalcBlockNum; hold_data := memory(adder); -- Place the contents of the mem -- in a holding area IF (SR(6) = '1') AND BlockNum = ErasingBlk THEN -- If the read is in a block with hold_data := "XXXXXXXXXXXXXXXX"; -- erase suspended, the value's END IF; -- anyone's guess IF (SR(2) = '1') AND adder = ProgrammingAddr -- If there's a write suspended, THEN hold_data := "XXXXXXXXXXXXXXXX"; END IF; -- END ReadArray subcase -- Holding area's set up - we're done WHEN ReadIDCodes => -- Reading from a subset of the IF hold_add(0) = '0' THEN hold_data := "0000000010001001"; -- Intel MfrID ELSE hold_data := "1000100010010000"; -- 16meg Top Boot DevID END IF; -- END ReadIDCodes subcase WHEN ReadStatReg => -- Reading the Status Register, -- regardless of the address FOR j IN 15 DOWNTO 8 LOOP -- Set the top byte of the holding hold_data(j) := '0'; -- area to $00 - the SR is a byte END LOOP; -- value, and the top byte is defined -- $00. FOR j IN 7 DOWNTO 0 LOOP -- If SR.7 = 1, then the data in the IF (j = 7) OR (SRLatch(7) = '1') THEN -- SR is valid, and we put it all in hold_data(j) := SRLatch(j); -- the holding area. If SR.7 = 0, ELSE hold_data(j) := 'X'; -- the SR is not valid (except for END IF; -- bit 7), and we set it to Xes. END LOOP; -- END ReadStatReg subcase WHEN OTHERS => -- If we get here, something went ASSERT (0>1) -- dramatically, horribly wrong. REPORT "Model failure - unknown ReadType" -- This simply can't happen. SEVERITY FAILURE; END CASE; -- WE HAVE NOW LEFT THE ReadType -- CASE STATEMENT! FOR j IN 15 DOWNTO 0 LOOP -- Put the stuff in the holding area dq(j) <= hold_data(j); -- onto the data outputs. END LOOP; equal := '1'; -- This is generic code to see if FOR j IN MaxAddrLine downto 0 LOOP -- the address on the bus has changed IF (hold_add(j) /= addr(j)) THEN -- from the one we latched when we equal := '0'; -- left ReadCycleX. If it has, END IF; -- equal will be 0. END LOOP; IF (rpb = '0') THEN -- RP is asserted. TimeRPL := NOW; -- Note when RP went low, next_state := PowerDown; -- and go to PowerDown. ELSIF ((oeb = '1' AND oeb'LAST_EVENT >= tOH) OR -- If one of the control lines has (ceb = '1' AND ceb'LAST_EVENT >= tOH) OR -- transitioned, or the address has (equal = '0' AND addr'LAST_EVENT >= tOH)) THEN -- changed, the data goes invalid next_state := ReadCycleX; -- and we go to ReadCycleX ELSE next_state := ReadCycleV; -- Otherwise, stay here END IF; -- END STATE ReadCycleV WHEN WriteCycle => -- Unlike the read process, which goes through many substates, the -- write process only uses one monolithic state. To be here is to have -- WE and CE low/asserted, and OE and RP high/unasserted. Even then, the -- only interesting things happen on the rising edge of the controlling -- signal (usually WE, but not always.) IF (web = '1') THEN -- Rising edge, and by implication, -- a WE-controlled write. next_state := OutputDisable; -- Go to OutputDisable next timeWH := NOW; -- Note when WE went high ASSERT (((NOW - web'LAST_EVENT) - timeWL) >= tWLWH) REPORT "WE Pulse Width violation (tWLWH)." -- Make sure WE was low long enough SEVERITY ERROR; -- WE Pulse Width ASSERT ((wpb = '0') OR (wpb'LAST_EVENT >= tSHWH)) -- If WP is unasserted, make REPORT "WP High Setup violation (tSHWH)." -- sure it's been that way for tSHWH SEVERITY ERROR; -- WP setup to WE high ASSERT (dq'LAST_EVENT >= tDVWH) -- Check Data Setup time REPORT "Data setup time violation (tDVWH)." SEVERITY ERROR; ASSERT (addr'LAST_EVENT >= tAVWH) -- Check Address Setup time REPORT "Address setup time violation (tAVWH)." SEVERITY ERROR; ELSIF (ceb = '1') THEN -- Rising edge on CE - by implication -- a CE-controlled write next_state := WEActive; -- Go to WEActive next timeEH := NOW; -- Note when CE went high ASSERT (((NOW - ceb'LAST_EVENT) - timeEL) >= tELEH) REPORT "CE Pulse Width violation (tELEH)." -- Make sure CE was low long enough SEVERITY ERROR; -- CE Pulse Width ASSERT ((wpb = '0') OR (wpb'LAST_EVENT >= tSHEH)) -- If WP is unasserted, make REPORT "WP High Setup violation (tSHEH)." -- sure it's been that way for tSHEH SEVERITY ERROR; -- WP Setup to CE High ASSERT (dq'LAST_EVENT >= tDVEH) -- Check Data Setup time REPORT "Data setup time violation (tDVEH)." SEVERITY ERROR; ASSERT (addr'LAST_EVENT >= tAVEH) -- Check Address Setup time REPORT "Address setup time violation (tAVEH)." SEVERITY ERROR; END IF; -- End the if about control lines -- going back to high IF (rpb = '0') THEN -- RP is asserted. next_state := PowerDown; -- Go to Powerdown TimeRPL := NOW; -- and note when RP went low ELSIF (web = '1' OR ceb = '1') THEN -- If either of the control lines -- went high, we do all sorts of stuff FOR j IN 0 to MaxAddrLine LOOP -- Latch the address when a line goes hold_add(j) := addr(j); -- high END LOOP; FOR j IN 0 to 15 LOOP -- Latch the data when a line goes hold_data(j) := dq(j); -- high END LOOP; CalcAdder; data := 0; -- Integerize the data, and store datalo := 0; -- it in data, but also store the twosum := 1; -- low byte's integer value in datalo FOR j IN 0 to 15 LOOP IF (hold_data(j) = '1') THEN data := data + twosum; IF (j<8) THEN datalo := datalo + twosum; END IF; END IF; twosum := twosum * 2; END LOOP; CalcBlockNum; CASE WriteType IS -- The idea here's similar to the -- idea with the ReadType subcase. -- WriteType stores what sort of data -- the device expects to see on a -- write cycle. WHEN WriteCmd => -- Case 1 - The data's a command. CASE datalo IS -- On a command, the low byte holds -- the important stuff. WHEN ClearSRCmd => -- Got the command to clear the SR. IF ((running = 0) AND -- If the device isn't busy, and -- there isn't anything suspended, (SR(6) = '0' AND SR(2) = '0')) THEN SR := SR AND "11000101"; --Clear the status register. ELSE ASSERT(0>1) -- Otherwise, protest vigorously REPORT -- that the command wasn't valid. "Invalid Command While Device Busy (ClearSR)" SEVERITY ERROR; END IF; WHEN EraseSingleBlockCmd => -- Command to erase one block IF ((running = 0) AND -- If the device isn't busy, and -- there isn't anything suspended, (SR(6) = '0' AND SR(2) = '0')) THEN WriteType := WriteBlkErase; -- Go to WriteBlkErase mode -- and wait for confirmation. ELSE ASSERT(0>1) -- Otherwise, the command wasn't good REPORT "Invalid Command While Device Busy (EraseBlk)" SEVERITY ERROR; END IF; WHEN ProgramCmd => -- Command to program a byte/word. -- If the device isn't busy, and IF ((running = 0) AND (SR(2) = '0')) THEN -- no erase is suspended, WriteType := WriteProgram; -- Go to WriteProgram mode, -- and wait for the data byte/word. ELSE ASSERT(0>1) -- Otherwise... yeah. REPORT "Invalid Command While Device Busy (Program)" SEVERITY ERROR; END IF; WHEN Program2Cmd => -- Ditto the above - this is the -- alternate value for the same thing IF ((running = 0) AND (SR(2) = '0')) THEN WriteType := WriteProgram; ELSE ASSERT(0>1) REPORT "Invalid Command While Device Busy (Program)" SEVERITY ERROR; END IF; WHEN ReadArrayCmd => -- Command to change the read mode -- to read the memory array. IF (running = 0) THEN -- If nothing's running ReadType := ReadArray; -- Change the read mode ELSE ASSERT(0>1) -- Otherwise, no dice. REPORT "Invalid Command While Device Busy (ReadArray)" SEVERITY ERROR; END IF; WHEN ReadCSRCmd => -- Command to change the read mode -- to read the Status Register IF (running = 0) THEN -- If nothing's running ReadType := ReadStatReg; -- change the read mode ELSE ASSERT(0>1) -- Otherwise, complain. REPORT "Invalid Command While Device Busy (ReadSR)" SEVERITY ERROR; END IF; WHEN ReadIDCmd => -- Command to change the read mode -- to read the ID Codes IF ((running = 0) AND -- If nothing's running, and -- nothing's suspended, (SR(6) = '0' AND SR(2) = '0')) THEN ReadType := ReadIDCodes; -- change the read mode. ELSE ASSERT(0>1) -- Otherwise, gripe. REPORT "Invalid Command While Device Busy (ReadIDCodes)" SEVERITY ERROR; END IF; WHEN ResumeCmd => -- Command to resume a suspended -- operation IF (running = 0) THEN -- If there's nothing running, IF (SR(2) = '1') THEN -- and there's a program suspended, SR(2) := '0'; -- Update the SR that there's -- no program suspended now. -- Change the ReadType to -- read the SR, and note that -- a program attempt is -- what's running. ReadType := ReadStatReg; running := WriteProgram; Completion := NOW + W16_ProgWordWOBuffer; SR(7) := '0'; -- And note the device is busy ELSIF (SR(6) = '1') THEN -- An erase was suspended. SR(6) := '0'; -- Clear the Erase Suspended flag SR(7) := '0'; -- Clear the Device Ready flag ReadType := ReadStatReg; -- Update the ReadType -- to read the Status Register running := WriteBlkErase; -- Note that a Block Erase is running IF BlockNum <= (2**(MaxAddrLine-15) -2) THEN Completion := NOW + W16_MainBlockErase; -- And note when the erase will -- complete. ELSE Completion := NOW + W16_ParmBlockErase; END IF; ELSE ASSERT (0>1) -- There's nothing to resume! -- The command's invalid. Complain. REPORT "Invalid Attempt To Resume" SEVERITY ERROR; END IF; ELSE ASSERT (0>1) -- The device is busy -- which makes the command invalid. REPORT "Invalid Attempt To Resume" SEVERITY ERROR; END IF; WHEN SuspendCmd => -- Command to suspend whatever is -- running. IF (running = WriteProgram) THEN -- a simple byte/word program -- is being suspended running := SuspensionLatency; -- Update running to note -- that we're waiting for a -- suspension to kick in. Completion := NOW + W16_ProgSuspToRead; -- Update the completion time -- to when we expect the -- suspension to suspend. susp_add := hold_add; -- Save the address that's -- currently latched. SR(7) := '0'; -- Clear the device ready -- flag in the SR SR(2) := '1'; -- Set the Program Suspend -- flag in the SR ELSIF (running = WriteBlkErase) THEN -- A block erase is running running := SuspensionLatency; -- Update running to note -- that we're waiting for a -- suspension Completion := NOW + W16_EraseSuspToRead; -- Update the completion time -- to the new estimate susp_add := hold_add; -- Save off the address now -- in the latch SR(7) := '0'; -- Clear the Device Ready -- flag SR(6) := '1'; -- Set the Erase Suspended -- flag ELSIF (running = 0) THEN -- If nothing's running, ASSERT(0>1) -- the command isn't valid. REPORT "Invalid Command While Device Not Busy (Suspend)" SEVERITY ERROR; ELSE ASSERT(0>1) -- This may never happen. REPORT -- It means the device is "Invalid Attempt To Suspend" -- busy with something that SEVERITY ERROR; -- can't be suspended. END IF; -- Full Chip Erase, perhaps. WHEN OTHERS => -- This is an error condition ASSERT (0>1) -- corresponding to some REPORT "Unrecognized command written." -- command being sent which SEVERITY ERROR; -- isn't actually a command. -- Don't yet know what the END CASE; -- device really does here. WHEN WriteProgram => -- This subcase means that -- the device is expecting -- the second byte of the -- Program a Byte/Word cmd. ReadType := ReadStatReg; -- A read gives the SR now equal := '0'; IF (SR(6) = '1') THEN IF BlockNum = ErasingBlk THEN equal := '1'; END IF; END IF; -- Check versus block being erased -- If equal = 1, then the IF (equal = '1') THEN -- address is part of an SR := SR OR "00010000"; -- erase, and the prog fails END IF; -- Check versus lockbits IF ((wpb = '0') AND (BlockNum <= 2**(MaxAddrLine-15)+6) AND (BlockNum >= 2**(MaxAddrLine-15)+5)) THEN SR := SR OR "00010010"; -- If WP is asserted, and this END IF; -- block is lockable, -- then the prog fails -- Check versus powerdown IF (rpb = '0') THEN SR := SR OR "00010000"; -- If we have entered END IF; -- powerdown, the prog fails -- Check Vcc IF (VccSig = '0') THEN SR := SR OR "00010000"; -- If Vcc is out of range, END IF; -- the prog fails -- Check Vpp IF (vpp < Vpp5vLockout) THEN -- If Vpp is below the SR := SR OR "00011000"; -- lockout voltage, the END IF; -- prog fails IF (VppSig = '0') THEN SR := SR OR "00011000"; -- If Vpp is out of range, END IF; -- the prog fails IF NOT ((equal = '1') OR ((wpb = '0') AND (BlockNum >= 2**(MaxAddrLine-15) + 5) AND (BlockNum <= 2**(MaxAddrLine-15) + 6)) OR (rpb = '0') OR (VccSig = '0') OR (VppSig = '0')) THEN -- If nothing's gone wrong, -- program the memory. IF (web = '1') THEN ASSERT (VppSig'LAST_EVENT >= tVPWH) REPORT "Violation of Vpp setup time (tVPWH)" SEVERITY ERROR; -- If it's a WE-controlled -- write, verify that Vpp was -- high early enough -- Vpp Setup time ELSIF (ceb = '1') THEN ASSERT (VppSig'LAST_EVENT >= tVPEH) REPORT "Violation of Vpp setup time (tVPEH)" SEVERITY ERROR; -- If it's a CE-controlled -- write, verify that Vcc was -- high early enough -- Vcc Setup time END IF; ProgrammingAddr := adder; equal := '1'; -- This loop serves to check -- if the memory can be -- programmed with the -- intended value, or if an -- erase would be needed FOR j IN 0 TO 15 LOOP IF ((hold_data(j) AND (NOT(memory(adder)(j)))) = '1') THEN equal := '0'; END IF; END LOOP; IF (equal = '0') -- Equal = 0 means that the -- value couldn't be -- programmed as desired -- without first erasing the -- cell. THEN SR := SR OR "00010000"; -- Set the prog fail flag ELSE memory(adder) := hold_data; -- Otherwise, write the value -- to the memory array. END IF; running := WriteProgram; -- Note that a program op -- is what's making the dev -- busy. SR(7) := '0'; -- Clear the Dev Ready flag -- Update the expected time -- that the busyness will end Completion := NOW + W16_ProgWordWOBuffer; END IF; -- The one starting with (equal ?= '1') WriteType := WriteCmd; -- And after all that, the -- next thing written is a -- command. WHEN WriteBlkErase => -- Command to erase one blk ReadType := ReadStatReg; -- Next read shall be reading -- the status register. IF (datalo /= ConfirmCmd) THEN -- If the second byte wasn't SR := SR OR "00110000"; -- a CONFIRM, set the invalid END IF; -- command flags -- Check versus lockbits IF ((wpb = '0') AND (BlockNum <= 2**(MaxAddrLine-15)+6) AND (BlockNum >= 2**(MaxAddrLine-15)+5)) THEN SR := SR OR "00100010"; -- If WP is asserted, and this END IF; -- block is lockable, -- then the prog fails -- Check versus powerdown -- If RP's asserted, the IF (rpb = '0') THEN SR := SR OR "00100000"; -- device should be powering END IF; -- down, not erasing stuff. -- The erase fails. -- Set Erase Fail flag. -- Check Vcc -- If Vcc is out of range, IF (VccSig = '0') THEN SR := SR OR "00100000"; -- the erase certainly fails. END IF; -- Set Erase Fail flag. -- Check Vpp -- If Vpp is below lockout, IF (vpp < Vpp5vLockout) THEN -- the erase fails. SR := SR OR "00101000"; -- Set Erase Fail flag END IF; -- and Vpp Low flag IF (VppSig = '0') THEN SR := SR OR "00101000"; -- If Vpp is out of range, END IF; -- the erase fails. -- Set Erase Fail flag -- and Vpp Low flag IF NOT ((datalo /= ConfirmCmd) OR ((wpb = '0') AND (BlockNum <= 2**(MaxAddrLine-15) + 6) AND (BlockNum >= 2**(MaxAddrLine-15) + 5)) OR -- If none of those things (rpb = '0') OR (VccSig = '0') OR -- happened, then erase the (VppSig = '0')) THEN -- block IF (web = '1') THEN -- If the write was -- WE-controlled, check that -- Vpp was high early enough. -- Vpp Setup time ASSERT (VppSig'LAST_EVENT >= tVPWH) REPORT "Violation of Vpp setup time (tVPWH)" SEVERITY ERROR; ELSIF (ceb = '1') THEN -- If the write was -- CE-controlled, check that -- Vpp was high early enough. -- Vpp Setup Time ASSERT (VppSig'LAST_EVENT >= tVPEH) REPORT "Violation of Vpp setup time (tVPEH)" SEVERITY ERROR; END IF; ErasingBlk := BlockNum; IF BlockNum <= 2**(MaxAddrLine - 15) - 2 THEN -- main block -- Here, we write all 1's to FOR j IN 0 TO 32767 LOOP -- the block, which erases it memory((BlockNum * 32768) + j) := "1111111111111111"; END LOOP; ELSE -- parm block FOR j IN 0 TO 4095 LOOP memory((2**MaxAddrLine - 32768) + (BlockNum - 2**(MaxAddrLine-15)+1) * 4096 + j) := "1111111111111111"; END LOOP; -- Don't laugh - it works. END IF; running := WriteBlkErase; -- Mark that a block erase is -- busying the device SR(7) := '0'; -- Clear the Dev Ready flag IF BlockNum <= (2**(MaxAddrLine-15) - 2) THEN Completion := NOW + W16_MainBlockErase; -- And note when the erase will -- complete. ELSE Completion := NOW + W16_ParmBlockErase; END IF; END IF; WriteType := WriteCmd; -- And the next write will be -- a command. WHEN OTHERS => -- This should never happen ASSERT (0>1) -- If we're here, the REPORT "Unrecognized WriteType achieved." -- WriteType state machine SEVERITY FAILURE; -- left all the valid states END CASE; -- Case of write types -- This is kinda sloppy -- After every write cycle, -- we check the memory -- location in the latch to -- see if we wrote Zs to it -- by mistake. If we did, FOR j IN 15 DOWNTO 0 LOOP -- turn the Zs into Xs. IF (memory(adder)(j) = 'Z') THEN memory(adder)(j) := 'X'; END IF; END LOOP; END IF; -- If WE rising edge encountered WHEN PowerDown => -- This state corresponds to RP asserted, and who cares about the rest of -- the lines? RP asserted means we're resetting and powered down. IF (running /= 0) AND (running /= ResetLatency) THEN -- If something's running, running := ResetLatency; -- it takes longer to reset. Completion := NOW + tPLRH; -- Note we're resetting, -- mark STS busy. ELSE -- If nothing's running SR := "10000000"; -- clear the status register dq <= "ZZZZZZZZZZZZZZZZ"; -- turn off the outputs ReadType := ReadArray; -- and get ready to read from END IF; -- memory. IF (rpb = '1') THEN -- RP is no longer asserted next_state := Powerup; -- We're going to powerup ASSERT ((NOW - TimeRPL) >= TPLPH) OR (TimeRPL = 0 ns) -- Check that RP was asserted REPORT "Reset Pulse Width violation (TPLPH)" -- long enough SEVERITY ERROR; -- Reset Pulse width ASSERT (running = 0) -- Check that, if something -- was running, that RP was REPORT "Reset Pulse Width violation (TPLRH)" -- asserted long enough SEVERITY ERROR; -- Reset Pulse width running := 0; -- If we're leaving powerdown completion := 0 ns; -- by defn, nothing's running ELSE next_state := PowerDown; -- Nothing happened END IF; TimeWH := 0 ns; -- Clear the times which TimeWL := 0 ns; -- check WE and CE pulse TimeEH := 0 ns; -- widths TimeEL := 0 ns; WHEN OTHERS => -- If we got here, the main ASSERT (0>1) -- state machine crashed. REPORT "Unrecognized main state achieved" -- Won't happen. SEVERITY FAILURE; END CASE; -- END OF THE BIG STATE MACHINE -- ********************************************** -- Everything below here gets executed every pass. -- ********************************************** state := next_state; -- Update the big state machine clock1 <= NOT(clock1) AFTER 0.5 ns; -- Clock the other process equal := '1'; -- Check if the address on the FOR j IN 0 to MaxAddrLine LOOP -- inputs equals the one in the IF hold_add(j) /= addr(j) THEN equal := '0'; -- latch END IF; END LOOP; IF timeEL >= timeWL THEN -- If the last write was -- CE-controlled, then IF (equal = '0') AND (timeEL /= 0 ns) AND (ceb = '1') AND (ceb'LAST_EVENT < tEHAX) THEN IF AddrHoldFlag = '0' THEN -- Check the Address Hold time ASSERT (0>1) -- Gripe where needed REPORT "Violation of address hold time on write (tEHAX)" SEVERITY ERROR; AddrHoldFlag := '1'; -- Set a flag if AddrHold was -- violated END IF; ELSE AddrHoldFlag := '0'; -- And clear the flag if it END IF; -- wasn't violated. -- The flag serves to suppress all but the first instance of a particular error. -- Without that logic, every nanosecond there would be an assertion, till the -- hold period was complete. ELSE -- If the last write was -- WE-controlled, then IF (equal = '0') AND (timeWL /= 0 ns) AND (web = '1') AND (web'LAST_EVENT < tWHAX) THEN IF AddrHoldFlag = '0' THEN -- Check the address hold time ASSERT (0>1) -- Gripe where needed REPORT "Violation of address hold time on write (tWHAX)" SEVERITY ERROR; AddrHoldFlag := '1'; -- Set a flag if it was violated END IF; ELSE AddrHoldFlag := '0'; -- and clear it if it wasn't. END IF; END IF; equal := '1'; -- Check if the data inputs have FOR j IN 0 to 15 LOOP -- changed since latching IF hold_data(j) /= dq(j) THEN equal := '0'; END IF; END LOOP; IF timeEL >= timeWL THEN -- If it's a CE-controlled write, -- The IF statement here breaks -- out to: -- 1) Data has changed since latch -- 2) Not immediately following -- powerup -- 3) CE is unasserted -- 4) CE has been unasserted less -- than the data hold time. IF (equal = '0') AND (timeEL /= 0 ns) AND (ceb = '1') AND (ceb'LAST_EVENT < tEHDX) THEN IF DataHoldFlag = '0' THEN -- Set a flag if Data Hold time ASSERT (0>1) -- was violated, and gripe. REPORT "Violation of data hold time on write (tEHDX)" SEVERITY ERROR; DataHoldFlag := '1'; END IF; ELSE DataHoldFlag := '0'; -- Clear if it wasn't END IF; ELSE -- On WE-controlled write, IF (equal = '0') AND (timeWL /= 0 ns) AND (web = '1') AND (web'LAST_EVENT < tWHDX) THEN IF DataHoldFlag = '0' THEN -- Gripe and set flag when Data Hold ASSERT (0>1) -- is violated REPORT "Violation of data hold time on write (tWHDX)" SEVERITY ERROR; DataHoldFlag := '1'; END IF; ELSE DataHoldFlag := '0'; -- and clear the flag when it's not END IF; END IF; IF NOT(((NOW - TimeSRValid) >= tQVVL) OR (VppSig = '1')) -- If Vpp is out of range, and THEN IF VppholdFlag = '0' THEN -- the device became free in the ASSERT (0>1) -- last tQVVL, there's a Vpp Hold REPORT "Violation of Vpp Hold time on write (tQVVL)" -- violation. SEVERITY ERROR; VppHoldFlag := '1'; END IF; ELSE VppHoldFlag := '0'; END IF; IF NOT (((NOW - TimeSRValid) >= tQVSL) OR (wpb = '1') OR -- If the device became free in ((wpb = '0') AND (wpb'LAST_EVENT >= (NOW - TimeSRValid)))) -- the last tQVSL, and WP went THEN IF WPHoldFlag = '0' THEN -- low since the device became free ASSERT (0>1) -- there's a WP High Hold time REPORT "Violation of WP# High Hold time on write (tQVSL)" -- violation. SEVERITY ERROR; WPHoldFlag := '1'; END IF; ELSE WPHoldFlag := '0'; END IF; IF NOT ((Running = 0) OR (VccSig = '1')) -- If Vcc is out of range, and the THEN IF VccMaintLFlag = '0' THEN -- device is busy, that's bad. ASSERT (0>1) REPORT "Vcc not maintained through latency period" SEVERITY ERROR; VccMaintLFlag := '1'; END IF; ELSE VccMaintLFlag := '0'; END IF; IF NOT (((SR(6) = '0') AND (SR(2) = '0')) OR (VccSig = '1')) -- If Vcc is out of range, and THEN IF VccMaintSFlag = '0' THEN -- something is suspended, ASSERT (0>1) -- that's also bad. REPORT "Vcc not maintained through suspended operation" SEVERITY ERROR; VccMaintSFlag := '1'; END IF; ELSE VccMaintSFlag := '0'; END IF; IF NOT ((Running = 0) OR (VppSig = '1')) -- If Vpp is out of range, and THEN IF VppMaintLFlag = '0' THEN -- something is running, that's bad ASSERT (0>1) REPORT "Vpp not maintained through latency period" SEVERITY ERROR; VppMaintLFlag := '1'; END IF; ELSE VppMaintLFlag := '0'; END IF; IF NOT (((SR(6) = '0') AND (SR(2) = '0')) OR (VppSig = '1')) -- If Vpp is out of range, and THEN IF VppMaintSFlag = '0' THEN -- something is suspended, that's ASSERT (0>1) -- bad, too. REPORT "Vpp not maintained through suspended operation" SEVERITY ERROR; VppMaintSFlag := '1'; END IF; ELSE VppMaintSFlag := '0'; END IF; -- This code runs when something gets finished (as determined by the time we wrote in the Completion -- variable when the something in question started. IF ((Completion /= 0 ns) AND (NOW >= Completion)) THEN -- a latency just finished TimeSRValid := NOW; -- Note when the SR became valid. -- A couple hold times need this, -- theoretically. SR(7) := '1'; -- Set the Dev Free flag Running := 0; -- Nothing is running, Completion := 0 ns; -- so there's no time at which it -- will complete. END IF; END PROCESS MAIN; EL_GUAPO:PROCESS -- This process exists solely to clock the main process. BEGIN WAIT ON clock1; clock2 <= not clock2 after 0.5 ns; END PROCESS EL_GUAPO; END behavior;