-------------------------------------------------------------------------------- -- File Name: ad5254.vhd -------------------------------------------------------------------------------- -- Copyright (C) 2005 Free Model Foundry; http://www.FreeModelFoundry.com -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation. -- -- MODIFICATION HISTORY: -- -- version: | author: | mod date: | changes made: -- V1.0 Dj.Tanasijevic 05 Apr 06 Initial Release -- -- This model must be compiled without VITAL compliance checking -------------------------------------------------------------------------------- -- PART DESCRIPTION: -- -- Library: MISC -- Technology: -- Part: AD5254 -- Description: Quad 256-Position I2C Nonvolatile Memory Digital Potentiometer -------------------------------------------------------------------------------- LIBRARY IEEE; USE IEEE.std_logic_1164.ALL; USE IEEE.VITAL_timing.ALL; USE IEEE.VITAL_primitives.ALL; USE STD.textio.ALL; LIBRARY FMF; USE FMF.gen_utils.all; USE FMF.conversions.all; -------------------------------------------------------------------------------- -- ENTITY DECLARATION -------------------------------------------------------------------------------- ENTITY ad5254 IS GENERIC ( -- tipd delays: interconnect path delays tipd_SCL : VitalDelayType01 := VitalZeroDelay01; tipd_SDA : VitalDelayType01 := VitalZeroDelay01; tipd_WPNeg : VitalDelayType01 := VitalZeroDelay01; -- tsetup values: setup times tsetup_SDA_SCL : VitalDelayType := UnitDelay; tsetup_SCL_SDAS : VitalDelayType := UnitDelay; tsetup_SCL_SDAP : VitalDelayType := UnitDelay; -- thold values: hold times thold_SDA_SCL : VitalDelayType := UnitDelay; thold_SCL_SDAS : VitalDelayType := UnitDelay; -- tpw values: pulse widths tpw_SCL_posedge : VitalDelayType := UnitDelay; tpw_SCL_negedge : VitalDelayType := UnitDelay; tperiod_SCL : VitalDelayType := UnitDelay; -- tdevice values: values for internal delays tdevice_TBUF : VitalDelayType := 1.3 us; -- generic control parameters InstancePath : STRING := DefaultInstancePath; TimingChecksOn : BOOLEAN := DefaultTimingChecks; MsgOn : BOOLEAN := DefaultMsgOn; XOn : BOOLEAN := DefaultXon; -- memory file to be loaded eemem_file_name : STRING := "none"; UserPreload : BOOLEAN := FALSE; -- For FMF SDF technology file usage TimingModel : STRING := DefaultTimingModel ); PORT ( SCL : IN std_logic := 'U'; SDA : INOUT std_logic := 'U'; WPNeg : IN std_logic := 'U'; AD0 : IN std_logic := 'U'; AD1 : IN std_logic := 'U'; A0 : IN real := 0.0; A1 : IN real := 0.0; A2 : IN real := 0.0; A3 : IN real := 0.0; B0 : IN real := 0.0; B1 : IN real := 0.0; B2 : IN real := 0.0; B3 : IN real := 0.0; W0 : OUT real := 0.0; W1 : OUT real := 0.0; W2 : OUT real := 0.0; W3 : OUT real := 0.0 ); ATTRIBUTE VITAL_LEVEL0 of ad5254 : ENTITY IS TRUE; END ad5254; -------------------------------------------------------------------------------- -- ARCHITECTURE DECLARATION -------------------------------------------------------------------------------- ARCHITECTURE vhdl_behavioral of ad5254 IS ATTRIBUTE VITAL_LEVEL0 of vhdl_behavioral : ARCHITECTURE IS TRUE; CONSTANT partID : STRING := "ad5254"; CONSTANT tEEMEM_STORE : TIME := 26 ms; CONSTANT HiRdacBit : NATURAL := 7; CONSTANT Resolution : NATURAL := 256; SIGNAL SCL_ipd : std_logic := 'U'; SIGNAL SDA_ipd : std_logic := 'U'; SIGNAL WPNeg_ipd : std_logic := 'U'; SIGNAL buf_in : std_logic := '0'; SIGNAL buf_out : std_logic := '0'; SIGNAL UpdateVoltage : BOOLEAN := FALSE; SIGNAL InitialVoltage : BOOLEAN := FALSE; BEGIN ---------------------------------------------------------------------------- -- Internal Delays ---------------------------------------------------------------------------- -- Artificial VITAL primitives to incorporate internal delays TBUF : VitalBuf (buf_out, buf_in, (VitalZeroDelay,tdevice_TBUF)); ---------------------------------------------------------------------------- -- Wire Delays ---------------------------------------------------------------------------- WireDelay : BLOCK BEGIN w_1 : VitalWireDelay (SCL_ipd, SCL, tipd_SCL); w_2 : VitalWireDelay (SDA_ipd, SDA, tipd_SDA); w_3 : VitalWireDelay (WPNeg_ipd, WPNeg, tipd_WPNeg); END BLOCK; ---------------------------------------------------------------------------- -- Main Behavior Block ---------------------------------------------------------------------------- Behavior: BLOCK PORT ( SCLIn : IN std_logic := 'U'; SDAIn : IN std_logic := 'U'; SDAOut : OUT std_logic := 'Z'; WPNegIn : IN std_logic := 'U'; AD0In : IN std_logic := 'U'; AD1In : IN std_logic := 'U'; A0In : IN real := 0.0; A1In : IN real := 0.0; A2In : IN real := 0.0; A3In : IN real := 0.0; B0In : IN real := 0.0; B1In : IN real := 0.0; B2In : IN real := 0.0; B3In : IN real := 0.0; W0Out : OUT real := 0.0; W1Out : OUT real := 0.0; W2Out : OUT real := 0.0; W3Out : OUT real := 0.0 ); PORT MAP ( SCLIn => SCL_ipd, SDAIn => SDA_ipd, SDAOut => SDA, WPNegIn => WPNeg_ipd, AD0In => AD0, AD1In => AD1, A0In => A0, A1In => A1, A2In => A2, A3In => A3, B0In => B0, B1In => B1, B2In => B2, B3In => B3, W0Out => W0, W1Out => W1, W2Out => W2, W3Out => W3 ); TYPE RDACStore IS ARRAY (0 to 3) OF INTEGER RANGE 0 TO Resolution-1; TYPE EEMEMStore IS ARRAY (0 to 15) OF INTEGER RANGE 0 TO 255; SHARED VARIABLE RDAC : RDACStore; SHARED VARIABLE EEMEM : EEMEMStore := (OTHERS => 0); SIGNAL TimeOut : BOOLEAN := false; SIGNAL Start : std_ulogic := 'U'; BEGIN ---------------------------------------------------------------------------- -- Main Behavior Process ---------------------------------------------------------------------------- Behavior : PROCESS (SCLIn, SDAIn) PROCEDURE decrement_6dB( VARIABLE RDAC_val : INOUT NATURAL RANGE 0 to Resolution-1) IS VARIABLE shift_reg : std_logic_vector(HiRdacBit downto 0); VARIABLE i : NATURAL; BEGIN shift_reg := to_slv(RDAC_val, HiRdacBit+1); FOR i IN 0 TO HiRdacBit-1 LOOP shift_reg(i) := shift_reg(i+1); END LOOP; shift_reg(HiRdacBit) := '0'; RDAC_val := to_nat(shift_reg); END decrement_6dB; PROCEDURE increment_6dB( VARIABLE RDAC_val : INOUT NATURAL RANGE 0 to Resolution-1) IS VARIABLE shift_reg : std_logic_vector(HiRdacBit downto 0); VARIABLE i : NATURAL; BEGIN shift_reg := to_slv(RDAC_val, HiRdacBit+1); FOR i IN HiRdacBit DOWNTO 1 LOOP shift_reg(i) := shift_reg(i-1); END LOOP; shift_reg(0) := '0'; RDAC_val := to_nat(shift_reg); END increment_6dB; -- Timing Check Variables VARIABLE Tviol_SDAS_SCL : X01 := '0'; VARIABLE TD_SDAS_SCL : VitalTimingDataType; VARIABLE Tviol_SDAH_SCL : X01 := '0'; VARIABLE TD_SDAH_SCL : VitalTimingDataType; VARIABLE Tviol_SCL_SDAS : X01 := '0'; VARIABLE TD_SCL_SDAS : VitalTimingDataType; VARIABLE Tviol_SCL_SDAP : X01 := '0'; VARIABLE TD_SCL_SDAP : VitalTimingDataType; VARIABLE Pviol_SCL : X01 := '0'; VARIABLE PD_SCL : VitalPeriodDataType := VitalPeriodDataInit; VARIABLE Violation : X01 := '0'; VARIABLE command : std_logic_vector(3 downto 0); VARIABLE OutputReg : std_logic_vector(7 downto 0); VARIABLE AddressReg : std_logic_vector(6 downto 0); VARIABLE TmpReg : std_logic_vector(7 downto 0); VARIABLE R_WNeg : std_logic; VARIABLE CMD_REGNeg : std_logic; VARIABLE EE_RDACNeg : std_logic; VARIABLE counter : NATURAL RANGE 0 TO 8; VARIABLE addr : NATURAL; VARIABLE addr1 : NATURAL; VARIABLE Addreceived : BOOLEAN := false; VARIABLE Selected : BOOLEAN := false; VARIABLE Commreceived : BOOLEAN := false; VARIABLE Datareceived : BOOLEAN := false; VARIABLE Read : BOOLEAN := false; VARIABLE ackslave : BOOLEAN := false; VARIABLE data : BOOLEAN := false; VARIABLE a4 : std_logic; BEGIN -------------------------------------------------------------------- -- Timing Check Section -------------------------------------------------------------------- IF (TimingChecksOn) THEN VitalSetupHoldCheck ( TestSignal => SDA, TestSignalName => "SDA", RefSignal => SCL, RefSignalName => "SCL", SetupHigh => tsetup_SDA_SCL, SetupLow => tsetup_SDA_SCL, CheckEnabled => (Start = '1'), RefTransition => '/', HeaderMsg => InstancePath & PartID, TimingData => TD_SDAS_SCL, XOn => XOn, MsgOn => MsgOn, Violation => Tviol_SDAS_SCL ); VitalSetupHoldCheck ( TestSignal => SDA, TestSignalName => "SDA", RefSignal => SCL, RefSignalName => "SCL", HoldHigh => thold_SDA_SCL, HoldLow => thold_SDA_SCL, CheckEnabled => (Start = '1'), RefTransition => '\', HeaderMsg => InstancePath & PartID, TimingData => TD_SDAH_SCL, XOn => XOn, MsgOn => MsgOn, Violation => Tviol_SDAH_SCL ); VitalSetupHoldCheck ( TestSignal => SCL, TestSignalName => "SCL", RefSignal => SDA, RefSignalName => "SDA", SetupHigh => tsetup_SCL_SDAS, HoldHigh => thold_SCL_SDAS, CheckEnabled => true, RefTransition => '\', HeaderMsg => InstancePath & PartID, TimingData => TD_SCL_SDAS, XOn => XOn, MsgOn => MsgOn, Violation => Tviol_SCL_SDAS ); VitalPeriodPulseCheck ( TestSignal => SCL, TestSignalName => "SCL", PulseWidthLow => tpw_SCL_negedge, PulseWidthHigh => tpw_SCL_posedge, Period => tperiod_SCL, PeriodData => PD_SCL, XOn => XOn, MsgOn => MsgOn, Violation => Pviol_SCL, HeaderMsg => InstancePath & PartID, CheckEnabled => true ); Violation := Tviol_SDAS_SCL OR Tviol_SDAH_SCL OR Tviol_SCL_SDAS OR Tviol_SCL_SDAP OR Pviol_SCL; ASSERT Violation = '0' REPORT InstancePath & partID & ": simulation may be" & " inaccurate due to timing violations" SEVERITY WARNING; END IF; ---------------------------------------------------------------------------- -- Functional Section ---------------------------------------------------------------------------- IF SCLIn = '1' AND TimeOut = false THEN IF falling_edge(SDAIn) THEN ASSERT buf_out = '0' REPORT InstancePath & partID & ": Bus free time beetwen STOP" & " and START condition " SEVERITY WARNING; Start <= '1'; counter := 0; Selected := false; Addreceived := false; Commreceived := false; Datareceived := false; Read := false; ackslave := false; data := false; ELSIF rising_edge(SDAIn) THEN buf_in <= '1', '0' AFTER 1.3 us; Start <= '0'; END IF; END IF; IF TimeOut = true THEN Start <= '0'; ELSIF rising_edge(SCLIn) AND Start = '1' THEN IF Addreceived = false THEN IF counter < 7 THEN AddressReg(6 - counter) := SDAIn; counter := counter + 1; ELSIF counter = 7 THEN IF AddressReg(6 downto 2) = "01011" AND AddressReg(1) = AD1In AND AddressReg(0) = AD0In THEN Selected := true; R_WNeg := SDAIn; IF R_WNeg = '1' THEN Read := true; IF (EE_RDACNeg = '0') THEN IF a4 = '0' AND addr < 4 THEN OutputReg := to_slv(RDAC(addr), 8); END IF; ELSE IF a4 = '0' THEN OutputReg := to_slv(EEMEM(addr), 8); ELSE OutputReg := "00000000"; END IF; END IF; END IF; END IF; counter := counter + 1; ELSE counter := 0; Addreceived := true; END IF; ELSIF Selected AND R_WNeg = '1' THEN IF counter < 8 THEN counter := counter + 1; ELSE counter := 0; IF (SDAIn = '0') THEN IF (EE_RDACNeg = '0' AND addr < 3) OR (EE_RDACNeg = '1' AND addr < 15) THEN addr := addr + 1; ELSE addr := 0; END IF; IF (EE_RDACNeg = '0') THEN IF a4 = '0' AND addr < 4 THEN OutputReg := to_slv(RDAC(addr), 8); END IF; ELSE IF a4 = '0' THEN OutputReg := to_slv(EEMEM(addr), 8); ELSE OutputReg := "00000000"; END IF; END IF; ELSE Read := false; END IF; END IF; ELSIF Selected AND R_WNeg = '0' AND Commreceived = false THEN IF counter < 8 THEN TmpReg(7 - counter) := SDAIn; counter := counter + 1; ELSE counter := 0; Commreceived := true; CMD_REGNeg := TmpReg(7); EE_RDACNeg := TmpReg(5); IF CMD_REGNeg = '0' THEN EE_RDACNeg := TmpReg(5); addr := to_nat(TmpReg(3 downto 0)); a4 := TmpReg(4); ELSIF CMD_REGNeg = '1' AND TmpReg(2) = '0' THEN command := TmpReg(6 downto 3); addr1 := to_nat(TmpReg(1 downto 0)); CASE command IS WHEN "0000" => NULL; WHEN "0001" => RDAC(addr1) := EEMEM(addr1) mod Resolution; UpdateVoltage <= NOT UpdateVoltage; WHEN "0010" => IF WPNegIn = '1' THEN EEMEM(addr1) := RDAC(addr1); Timeout <= true, false AFTER tEEMEM_STORE; END IF; WHEN "0011" => IF WPNegIn = '1' THEN decrement_6dB(RDAC(addr1)); UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "0100" => IF WPNegIn = '1' THEN decrement_6dB(RDAC(0)); decrement_6dB(RDAC(1)); decrement_6dB(RDAC(2)); decrement_6dB(RDAC(3)); UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "0101" => IF RDAC(addr1) > 0 AND WPNegIn = '1' THEN RDAC(addr1) := RDAC(addr1) - 1; UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "0110" => IF RDAC(0) > 0 AND WPNegIn = '1' THEN RDAC(0) := RDAC(0) - 1; END IF; IF RDAC(1) > 0 AND WPNegIn = '1' THEN RDAC(1) := RDAC(1) - 1; END IF; IF RDAC(2) > 0 AND WPNegIn = '1' THEN RDAC(2) := RDAC(2) - 1; END IF; IF RDAC(3) > 0 AND WPNegIn = '1' THEN RDAC(3) := RDAC(3) - 1; END IF; UpdateVoltage <= NOT UpdateVoltage; WHEN "0111" => RDAC(0) := EEMEM(0) mod Resolution; RDAC(1) := EEMEM(1) mod Resolution; RDAC(2) := EEMEM(2) mod Resolution; RDAC(3) := EEMEM(3) mod Resolution; UpdateVoltage <= NOT UpdateVoltage; WHEN "1000" => IF WPNegIn = '1' THEN increment_6dB(RDAC(addr1)); UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "1001" => IF WPNegIn = '1' THEN increment_6dB(RDAC(0)); increment_6dB(RDAC(1)); increment_6dB(RDAC(2)); increment_6dB(RDAC(3)); UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "1010" => IF RDAC(addr1) < Resolution-1 AND WPNegIn = '1' THEN RDAC(addr1) := RDAC(addr1) + 1; UpdateVoltage <= NOT UpdateVoltage; END IF; WHEN "1011" => IF RDAC(0) < Resolution-1 AND WPNegIn = '1' THEN RDAC(0) := RDAC(0) + 1; END IF; IF RDAC(1) < Resolution-1 AND WPNegIn = '1' THEN RDAC(1) := RDAC(1) + 1; END IF; IF RDAC(2) < Resolution-1 AND WPNegIn = '1' THEN RDAC(2) := RDAC(2) + 1; END IF; IF RDAC(3) < Resolution-1 AND WPNegIn = '1' THEN RDAC(3) := RDAC(3) + 1; END IF; UpdateVoltage <= NOT UpdateVoltage; WHEN others => NULL; END CASE; END IF; END IF; ELSIF Selected AND CMD_REGNeg = '0' AND R_WNeg = '0' AND counter < 8 THEN IF counter = 0 AND Datareceived THEN IF EE_RDACNeg = '0' THEN IF addr < 7 THEN addr := addr + 1; ELSE addr := 0; END IF; END IF; END IF; TmpReg(7 - counter) := SDAIn; counter := counter + 1; ELSIF Selected AND counter = 8 THEN counter := 0; IF a4 = '0' AND WPNegIn = '1' THEN IF EE_RDACNeg = '0' THEN IF addr < 4 THEN RDAC(addr) := to_nat(TmpReg(HiRdacBit downto 0)); UpdateVoltage <= NOT UpdateVoltage; END IF; ELSE EEMEM(addr) := to_nat(TmpReg(7 downto 0)); Timeout <= true, false AFTER tEEMEM_STORE; END IF; Datareceived := true; END IF; END IF; ELSIF falling_edge(SCLIn) AND Start = '1' AND Selected THEN IF Addreceived = false OR R_WNeg = '0' THEN IF counter = 8 THEN ackslave := true; ELSIF counter = 0 THEN ackslave := false; END IF; ELSE ackslave := false; IF counter < 8 THEN IF Read THEN data := true; END IF; ELSE data := false; END IF; END IF; END IF; IF TimeOut = true THEN SDAOut <= 'Z'; ELSIF Start = '1' THEN IF ackslave = true THEN IF Commreceived = true AND R_WNeg = '0' AND EE_RDACNeg = '0' AND addr = 3 THEN SDAOut <= '1' AFTER 1 us; ELSE SDAOut <= '0' AFTER 1 us; END IF; ELSIF data = true THEN IF falling_edge(SCLIn) THEN SDAOut <= OutputReg(7 - counter) AFTER 1 us; END IF; ELSE SDAOut <= 'Z'; END IF; END IF; END PROCESS; OutVoltage : PROCESS (UpdateVoltage, InitialVoltage, A0In, A1In, A2In, A3In, B0In, B1In, B2In, B3In) BEGIN W0Out <= B0In + real(RDAC(0))*(A0In-B0In)/real(Resolution); W1Out <= B1In + real(RDAC(1))*(A1In-B1In)/real(Resolution); W2Out <= B2In + real(RDAC(2))*(A2In-B2In)/real(Resolution); W3Out <= B3In + real(RDAC(3))*(A3In-B3In)/real(Resolution); END PROCESS OutVoltage; ------------------------------------------------------------------------------- -- File Read Section ------------------------------------------------------------------------------- Preloadfile: PROCESS FILE eemem_file : text is eemem_file_name; VARIABLE ind : NATURAL := 0; VARIABLE buf : line; BEGIN ------------------------------------------------------------------------------- -----ad5254 eemem preload file format ---------------------------- ------------------------------------------------------------------------------- -- / - comment -- @a - stands for address -- bb - is byte to be written at EEMEM(a++) -- (a is incremented at every load) -- only first 1-2 columns are loaded. NO empty lines !!!!!!!!!!!!!!!! ------------------------------------------------------------------------------- IF (eemem_file_name /= "none" AND UserPreload ) THEN ind := 0; WHILE (not ENDFILE (eemem_file)) LOOP READLINE (eemem_file, buf); IF buf(1) = '/' THEN NEXT; ELSIF buf(1) = '@' THEN ind := h(buf(2 to 2)); --address ELSE EEMEM(ind) := h(buf(1 to 2)); ind := ind + 1; END IF; END LOOP; END IF; RDAC(0) := EEMEM(0) mod Resolution; RDAC(1) := EEMEM(1) mod Resolution; RDAC(2) := EEMEM(2) mod Resolution; RDAC(3) := EEMEM(3) mod Resolution; InitialVoltage <= NOT InitialVoltage; WAIT; END PROCESS Preloadfile; END BLOCK; END vhdl_behavioral;