#include "ngspice/cm.h"
extern void cm_d_tff(Mif_Private_t *);
/*.......1.........2.........3.........4.........5.........6.........7.........8
================================================================================

FILE d_tff/cfunc.mod

Public Domain

Georgia Tech Research Corporation
Atlanta, Georgia 30332
PROJECT A-8503-405
               

AUTHORS                      

   24 June 1991     Jeffrey P. Murray


MODIFICATIONS   

    12 Aug 1991    Jeffrey P. Murray
     2 Oct 1991    Jeffrey P. Murray
    29 Jan 1992    Jeffrey P. Murray
                                   

SUMMARY

    This file contains the model-specific routines used to
    functionally describe the d_tff code model.


INTERFACES       

    FILE                 ROUTINE CALLED     

    CMutil.c             void cm_toggle_bit(); 

    CMevt.c              void *cm_event_alloc()
                         void *cm_event_get_ptr()


REFERENCED FILES

    Inputs from and outputs to ARGS structure.
                     

NON-STANDARD FEATURES

    NONE

===============================================================================*/

/*=== INCLUDE FILES ====================*/


                                      

/*=== CONSTANTS ========================*/




/*=== MACROS ===========================*/



  
/*=== LOCAL VARIABLES & TYPEDEFS =======*/                         


    
           
/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/




                   
/*=============================================================================*/

/*=== CM<model_name> ROUTINE ===*/




/*==============================================================================

FUNCTION cm_toggle_bit()


AUTHORS                      

    27 Sept 1991     Jeffrey P. Murray


MODIFICATIONS   

    NONE


SUMMARY

    Alters the state of a passed digital variable to its
    complement. Thus, a ONE changes to a ZERO. A ZERO changes
    to a ONE, and an UNKNOWN remains unchanged.


INTERFACES       

    FILE                 ROUTINE CALLED     

    N/A                  N/A


RETURNED VALUE
    
    No returned value. Passed pointer to variable is used 
    to redefine the variable value.
           

GLOBAL VARIABLES
    
    NONE


NON-STANDARD FEATURES

    NONE
                                                   
===============================================================================*/

/*=== CM_TOGGLE_BIT ROUTINE ===*/

static void cm_toggle_bit(Digital_State_t *bit) 

{
    /* Toggle bit from ONE to ZERO or vice versa, unless the
       bit value is UNKNOWN. In the latter case, return 
       without changing the bit value.                      */

    if ( UNKNOWN != *bit ) {
        if ( ONE == *bit ) {
            *bit = ZERO;
        }
        else { 
            *bit = ONE;
        }
    }

}


                   
/*==============================================================================

FUNCTION cm_d_tff()


AUTHORS                      

    24 Jun 1991     Jeffrey P. Murray


MODIFICATIONS   

    29 Jan 1992     Jeffrey P. Murray


SUMMARY

    This function implements the d_tff code model.


INTERFACES       

    FILE                 ROUTINE CALLED     

    CMutil.c             void cm_toggle_bit(); 

    CMevt.c              void *cm_event_alloc()
                         void *cm_event_get_ptr()



RETURNED VALUE
    
    Returns inputs and outputs via ARGS structure.
           

GLOBAL VARIABLES
    
    NONE


NON-STANDARD FEATURES

    NONE
                                                   
===============================================================================*/

/*=== CM_D_TFF ROUTINE ===*/

/************************************************
*      The following is the model for the       *
*   digital t-type flip flop for the            *
*   ATESSE Version 2.0 system.                  *
*                                               *
*   Created 6/24/91               J.P.Murray    *
************************************************/


void cm_d_tff(Mif_Private_t *mif_private) 

{
    /*int                    i;*/   /* generic loop counter index */
                        

    Digital_State_t     *clk,   /* current clk value    */
                    *clk_old,   /* previous clk value   */
                        *set,   /* current set value for dff    */
                    *set_old,   /* previous set value for dff   */
                      *reset,   /* current reset value for dff      */
                  *reset_old,   /* previous reset value for dff     */
                        *out,   /* current output for dff   */
                    *out_old,   /* previous output for dff  */                            

                toggle_input,   /* current toggle input value    */                            

                        temp;   /* temp storage for state values    */                            



    /*** Setup required state variables ***/

    if(mif_private->circuit.init) {  /* initial pass */ 

        /* allocate storage */
        cm_event_alloc(0,sizeof(Digital_State_t));
        cm_event_alloc(1,sizeof(Digital_State_t));
        cm_event_alloc(2,sizeof(Digital_State_t));
        cm_event_alloc(3,sizeof(Digital_State_t));

        /* declare load values */
        mif_private->conn[0]->port[0]->load = mif_private->param[6]->element[0].rvalue;
        mif_private->conn[1]->port[0]->load = mif_private->param[7]->element[0].rvalue;
        if ( !mif_private->conn[2]->is_null ) {
            mif_private->conn[2]->port[0]->load = mif_private->param[8]->element[0].rvalue;
        }
        if ( !mif_private->conn[3]->is_null ) {
        mif_private->conn[3]->port[0]->load = mif_private->param[9]->element[0].rvalue;
        }

        /* retrieve storage for the outputs */
        clk = clk_old = (Digital_State_t *) cm_event_get_ptr(0,0);
        set = set_old = (Digital_State_t *) cm_event_get_ptr(1,0);
        reset = reset_old = (Digital_State_t *) cm_event_get_ptr(2,0);
        out = out_old = (Digital_State_t *) cm_event_get_ptr(3,0);

    }
    else {      /* Retrieve previous values */
                                              
        /* retrieve storage for the outputs */
        clk = (Digital_State_t *) cm_event_get_ptr(0,0);
        clk_old = (Digital_State_t *) cm_event_get_ptr(0,1);
        set = (Digital_State_t *) cm_event_get_ptr(1,0);
        set_old = (Digital_State_t *) cm_event_get_ptr(1,1);
        reset = (Digital_State_t *) cm_event_get_ptr(2,0);
        reset_old = (Digital_State_t *) cm_event_get_ptr(2,1);
        out = (Digital_State_t *) cm_event_get_ptr(3,0);
        out_old = (Digital_State_t *) cm_event_get_ptr(3,1);
    }



    /******** load current input values if set or reset 
              are not connected, set to zero... ********/
    *clk = ((Digital_t*)(mif_private->conn[1]->port[0]->input.pvalue))->state;
    if ( mif_private->conn[2]->is_null ) {
        *set = *set_old = ZERO;
    }
    else {
        *set = ((Digital_t*)(mif_private->conn[2]->port[0]->input.pvalue))->state;
    }
    if ( mif_private->conn[3]->is_null ) {
        *reset = *reset_old = ZERO;
    }
    else {
        *reset = ((Digital_t*)(mif_private->conn[3]->port[0]->input.pvalue))->state;
    }



         
                            

    /******* Determine analysis type and output appropriate values *******/

    if (0.0 == mif_private->circuit.time) {   /****** DC analysis...output w/o delays ******/
                                                            
         temp = (Digital_State_t) mif_private->param[3]->element[0].ivalue;

        /** Modify output if set or reset lines are active **/
        if ( (*set==ONE) && (*reset==ZERO) ) temp = ONE;
        if ( (*set==ZERO) && (*reset==ONE) ) temp = ZERO;
        if ( (*set==ONE) && (*reset==ONE) ) temp = UNKNOWN;

        *out = *out_old = temp;


        if ( !mif_private->conn[4]->is_null ) {
            ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = temp;
        }

        cm_toggle_bit(&temp);

        if ( !mif_private->conn[5]->is_null ) {
            ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = temp;
        }
    }

    else {      /****** Transient Analysis ******/


        /***** Find input that has changed... *****/

        /**** Test set value for change ****/
        if ( *set != *set_old ) { /* either set or set release */
            switch ( *set ) {

            case ONE:
                if ( ONE != *reset) {
                    if (*out_old != ONE) {  /* set will change output */
                        /* output goes to ONE */
                        *out = ONE;

                        if ( !mif_private->conn[4]->is_null ) {
                            ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ONE;
                            mif_private->conn[4]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ZERO;
                            mif_private->conn[5]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                    }
                    else {
                        *out = *out_old;     /* output already set */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                    }
                }
                else {
                    if (*out_old != UNKNOWN) {  /* set will change output */
                        /* output goes to UNKNOWN */
                        *out = UNKNOWN;

                        if ( !mif_private->conn[4]->is_null ) {
                            ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = UNKNOWN;
                            mif_private->conn[4]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = UNKNOWN;
                            mif_private->conn[5]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                    }
                    else {
                        *out = *out_old;     /* output already unknown */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                    }
                }
                break;

            case ZERO:
                if ( ONE != *reset) {
                    /* output remains at current value */
                    *out = *out_old;
                    if ( !mif_private->conn[4]->is_null ) {
                        mif_private->conn[4]->port[0]->changed = FALSE;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        mif_private->conn[5]->port[0]->changed = FALSE;
                    }
                }
                else {
                    if (*out_old != ZERO) {  /* set will change output */
                        /* output returns to reset condition */
                        *out = ZERO;

                        if ( !mif_private->conn[4]->is_null ) {
                            ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ZERO;
                            mif_private->conn[4]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ONE;
                            mif_private->conn[5]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                        }
                    }
                    else {
                        *out = *out_old;     /* output already reset */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                    }
                }
                break;

            case UNKNOWN:
                if ( ONE == *reset ) {
                    /* output goes to ZERO */
                    *out = ZERO;

                    if ( !mif_private->conn[4]->is_null ) {
                        ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ZERO;
                        mif_private->conn[4]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ONE;
                        mif_private->conn[5]->port[0]->delay = mif_private->param[1]->element[0].rvalue;
                    }
                }
                else {
                    *out = *out_old;     /* output already unknown */
                    if ( !mif_private->conn[4]->is_null ) {
                        mif_private->conn[4]->port[0]->changed = FALSE;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        mif_private->conn[5]->port[0]->changed = FALSE;
                    }
                }
                break;
            }
        }
        else {

            /**** Test reset value for change ****/
            if ( *reset != *reset_old ) { /* either reset or reset release */
                switch ( *reset ) {
    
                case ONE:
                    if ( ONE != *set) {
                        if (*out_old != ZERO) {  /* reset will change output */
                            /* output goes to ZERO */
                            *out = ZERO;

                            if ( !mif_private->conn[4]->is_null ) {
                                ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ZERO;
                                mif_private->conn[4]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ONE;
                                mif_private->conn[5]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                        }
                        else {
                            *out = *out_old;     /* output already reset */
                            if ( !mif_private->conn[4]->is_null ) {
                                mif_private->conn[4]->port[0]->changed = FALSE;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                mif_private->conn[5]->port[0]->changed = FALSE;
                            }
                        }
                    }
                    else {
                        if (*out_old != UNKNOWN) {  /* reset will change output */
                            /* output goes to UNKNOWN */
                            *out = UNKNOWN;

                            if ( !mif_private->conn[4]->is_null ) {
                                ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = UNKNOWN;
                                mif_private->conn[4]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = UNKNOWN;
                                mif_private->conn[5]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                        }
                        else {
                            *out = *out_old;     /* output already unknown */
                            if ( !mif_private->conn[4]->is_null ) {
                                mif_private->conn[4]->port[0]->changed = FALSE;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                mif_private->conn[5]->port[0]->changed = FALSE;
                            }
                        }
                    }
                    break;

                case ZERO:
                    if ( ONE != *set) {
                        /* output remains at current value */
                        *out = *out_old;
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                    }
                    else {
                        if (*out_old != ONE) {  /* reset will change output */
                            /* output returns to set condition */
                            *out = ONE;

                            if ( !mif_private->conn[4]->is_null ) {
                                ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ONE;
                                mif_private->conn[4]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ZERO;
                                mif_private->conn[5]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                            }
                        }
                        else {
                            *out = *out_old;     /* output already reset */
                            if ( !mif_private->conn[4]->is_null ) {
                                mif_private->conn[4]->port[0]->changed = FALSE;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                mif_private->conn[5]->port[0]->changed = FALSE;
                            }
                        }
                    }
                    break;
    
                case UNKNOWN:
                    if ( ONE == *set ) {
                        /* output goes to ONE */
                        *out = ONE;

                        if ( !mif_private->conn[4]->is_null ) {
                            ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = ONE;
                            mif_private->conn[4]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = ZERO;
                            mif_private->conn[5]->port[0]->delay = mif_private->param[2]->element[0].rvalue;
                        }
                    }
                    else {
                        *out = *out_old;     /* output already unknown */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                    }
                    break;
                }
            }
            else {

                /**** Test clk value for change ****/
                if ( (*clk != *clk_old) && (*reset != ONE) &&
                     (*set != ONE) ) { /* clock or clock release */
                    switch ( *clk ) {          

                    case ONE:
                        /* active edge...calculate new data output  */
                        toggle_input = ((Digital_t*)(mif_private->conn[0]->port[0]->input.pvalue))->state;

                        switch (toggle_input) {
                        case ONE:
                            temp = *out_old;
                            cm_toggle_bit(&temp);
                            break;
                        case ZERO:
                            temp = *out_old;
                            break;
                        case UNKNOWN:
                        default:
                            temp = UNKNOWN;
                            break;
                        }

                        if (*out_old != temp) {  /* clk will change output */
                            *out = temp;
                            if ( !mif_private->conn[4]->is_null ) {
                                ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->state = temp;
                                mif_private->conn[4]->port[0]->delay = mif_private->param[0]->element[0].rvalue;
                            }

                            cm_toggle_bit(&temp);  

                            if ( !mif_private->conn[5]->is_null ) {
                                ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->state = temp;
                                mif_private->conn[5]->port[0]->delay = mif_private->param[0]->element[0].rvalue;
                            }
                        }
                        else {
                            *out = *out_old;     /* output same as before */
                            if ( !mif_private->conn[4]->is_null ) {
                                mif_private->conn[4]->port[0]->changed = FALSE;
                            }
                            if ( !mif_private->conn[5]->is_null ) {
                                mif_private->conn[5]->port[0]->changed = FALSE;
                            }
                        }
                        break;

                    case ZERO:
                    case UNKNOWN:
                        /* inactive edge...return previous values */
                        *out = *out_old;
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->changed = FALSE;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->changed = FALSE;
                        }
                        break;

                    }
                }

                else {   /* data value must have changed...
                            return previous output value.    */
                    *out = *out_old;
                    if ( !mif_private->conn[4]->is_null ) {
                        mif_private->conn[4]->port[0]->changed = FALSE;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        mif_private->conn[5]->port[0]->changed = FALSE;
                    }
                }
            }        
        }


        /***** Add additional rise or fall delays, if appropriate *****/

        if ( *out != *out_old ) { /*** output value is changing ***/

            switch ( *out ) {
                                                 
            /** fall to zero value **/
            case 0: 
                    if ( !mif_private->conn[4]->is_null ) {
                        mif_private->conn[4]->port[0]->delay += mif_private->param[5]->element[0].rvalue;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        mif_private->conn[5]->port[0]->delay += mif_private->param[4]->element[0].rvalue;
                    }
                    break;
    
            /** rise to one value **/
            case 1: 
                    if ( !mif_private->conn[4]->is_null ) {
                        mif_private->conn[4]->port[0]->delay += mif_private->param[4]->element[0].rvalue;
                    }
                    if ( !mif_private->conn[5]->is_null ) {
                        mif_private->conn[5]->port[0]->delay += mif_private->param[5]->element[0].rvalue;
                    }
                    break;
                                    
            /** unknown output **/
            default:
                    /* based on old value, add rise or fall delay */
                    if (0 == *out_old) {  /* add rising delay */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->delay += mif_private->param[4]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->delay += mif_private->param[5]->element[0].rvalue;
                        }
                    }
                    else {                /* add falling delay */
                        if ( !mif_private->conn[4]->is_null ) {
                            mif_private->conn[4]->port[0]->delay += mif_private->param[5]->element[0].rvalue;
                        }
                        if ( !mif_private->conn[5]->is_null ) {
                            mif_private->conn[5]->port[0]->delay += mif_private->param[4]->element[0].rvalue;
                        }
                    }   
                    break;
            }
        }
    }
              
    /*** output strength values ***/
    if ( !mif_private->conn[4]->is_null ) {
        ((Digital_t*)(mif_private->conn[4]->port[0]->output.pvalue))->strength = STRONG;
    }
    if ( !mif_private->conn[5]->is_null ) {
    ((Digital_t*)(mif_private->conn[5]->port[0]->output.pvalue))->strength = STRONG;
    }
} 

      



