/*------------------------------------------------------------------------------*/
/*                    @(#) MBK TO EDIF Driver                                   */
/*------------------------------------------------------------------------------*/
/*version 1.0 : Implemented by Fred Petrot                                      */
/*version 1.1 : Extended by Mokhtar Hirech (January, 92)                        */
/*version 1.2 : Extended by Olivier BEAURIN (September, 93)                     */
/*version 1.2 : Extended by Olivier BEAURIN (November, 93)                      */
/*------------------------------------------------------------------------------*/

#include MUT_H
#include MLO_H
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

/* defines */
#define SCHEMATIC "schematic"
#define SYMBOL    "symbol"

#define MBK_EDIF_LIBNAME "MBK_EDIF_LIBNAME"

/* Global variables*/

static FILE       *edif_file;
static char       *EDIF_LIBNAME;
static chain_list *already_drived = NULL;

/* Global Functions*/

static void         port_ext         ( );
static void         edit_sig         ( );
static void         edit_ins         ( );
static void         edit_con         ( );
static void         connect          ( );
static void         view             ( );
static void         drive_cell       ( );
static void         r_drive_cell     ( );
static int          edif_busindex    ( );
static int          drive_basic_cells( );
static locon_list  *edif_found_array ( );
static void         check_fig_portref( );

       char        *edif_busname     ( );

/*---------------------------------------------------------\
                     edifsavelofig
\---------------------------------------------------------*/
void edifsavelofig( firstlofig )
lofig_list *firstlofig;  
{
	chain_list *chainl;
	chain_list *chainp;
	char        filename[ 255 ];
	char       *pc;

	EDIF_LIBNAME = getenv( MBK_EDIF_LIBNAME );
	if( !EDIF_LIBNAME )
		EDIF_LIBNAME = namealloc( "alliance" );

	if( strcmp( EDIF_LIBNAME, "" ) == 0 )
		EDIF_LIBNAME = namealloc( "alliance" );


	for( pc = EDIF_LIBNAME; pc[0] != '\0'; pc ++ )
		if( ( pc[0] == '(' ) || ( pc[0] == ')' ) || ( pc[0] == ' ' ) )
		{
			fprintf( stderr, "*** mbk error *** edifloadlofig : environment variable 'MBK_EDIF_LIBNAME' invalid.\n" );
			exit( 1 );
		}

	if (firstlofig == NULL)
	{
		fprintf( stderr, "*** mbk error *** edifsavelofig : edif shall not drive a NULL figure\n");
		exit( 1 );
	}

	sprintf( filename, "%s/%s.%s", WORK_LIB, firstlofig->NAME, OUT_LO );

	if( ( edif_file = mbkfopen( firstlofig->NAME, OUT_LO, WRITE_TEXT ) ) == NULL )
	{
		fprintf( stderr, "*** mbk error *** edifsavelofig : the file %s cannot be opened\n", filename );
		exit( 1 );
	}

	fprintf( edif_file, "(edif %s\n",                                        firstlofig -> NAME );
	fprintf( edif_file, " (edifversion 2 0 0)\n"                                                );
	fprintf( edif_file, " (ediflevel 0)\n"                                                      );
	fprintf( edif_file, " (keywordMap (keywordLevel 0))\n"                                      );
	fprintf( edif_file, " (status\n"                                                            );
	fprintf( edif_file, "  (written\n"                                                          );
	fprintf( edif_file, "   (timeStamp %s)\n",                               EdifTime( )        );
	fprintf( edif_file, "   (program \"Driver mbk2edif\")\n"                                    );
	fprintf( edif_file, "   (author \"FP & HM & OB for : %s\")\n",           getenv( "USER" )   );
	fprintf( edif_file, "   (dataOrigin \"VLSI-CAD : Masi Lab. UPMC\")))\n"                     );
	fprintf( edif_file, " (library %s\n",                                    EDIF_LIBNAME       ); 
	fprintf( edif_file, "  (ediflevel 0)\n"                                                     );
	fprintf( edif_file, "  (technology (numberDefinition))"                                     );

	r_drive_cell( firstlofig );

	fprintf( edif_file, "))\n");

	if( fclose( edif_file ) !=0 )
	{
		fprintf( stderr, "*** mbk error *** edifsavelofig : Cannot close file '%s'\n", filename );
		exit( 1 );
	}

	freechain( already_drived );
	already_drived = (chain_list *)NULL;

} /* end of edifsavelofig */

/*---------------------------------------------------------\
                     drived_cell
\---------------------------------------------------------*/
static char drived_cell( ptchain, cell_name )
chain_list *ptchain;
char       *cell_name;
{
	chain_list *chainp;

	for(chainp = ptchain; chainp; chainp = chainp->NEXT)
		if ((char *)chainp->DATA == cell_name)
			return 1;

	return 0;
} /* end of drived_cell */

/*---------------------------------------------------------\
                     r_drive_cell
\---------------------------------------------------------*/
static void r_drive_cell(ptlofig)
lofig_list *ptlofig;
{
	chain_list *ptchain;
	lofig_list *ptlfig;

	ptlofig->MODELCHAIN = (chain_list *)reverse((chain_list *)ptlofig->MODELCHAIN);

	drive_basic_cells(ptlofig); 

	for (ptchain = ptlofig->MODELCHAIN; ptchain; ptchain = ptchain->NEXT)
	{
		if (!drived_cell(already_drived, namealloc((char*)ptchain->DATA)))
		{
			ptlfig = getlofig((char *)ptchain->DATA, 'A');
			r_drive_cell(ptlfig);
		}
	}

	ptlofig->MODELCHAIN = (chain_list *)reverse((chain_list *)ptlofig->MODELCHAIN);
	drive_cell(ptlofig->NAME, 'C');
} /* end of r_drive_cell */


/*---------------------------------------------------------\
                     drive_basic_cells
\---------------------------------------------------------*/
static int drive_basic_cells( ptlofig )
lofig_list *ptlofig;
{
	chain_list *ptchain;
	lofig_list *ptlfig;
	char       *cell_model_name;

	for (ptchain = ptlofig->MODELCHAIN; ptchain; ptchain = ptchain->NEXT)
	{
		cell_model_name = namealloc((char *)ptchain->DATA);
		if (incatalog(cell_model_name))
			drive_cell(cell_model_name, 'I');
	}
} /* end of drive_basic_cells */

/*---------------------------------------------------------\
                     drive_cell
\---------------------------------------------------------*/
static void drive_cell(figname, c)
char *figname;
char  c;
{
	chain_list *ptchain;
	lofig_list *ptlofig;

	if (drived_cell(already_drived, figname))
		return;

	already_drived = addchain(already_drived, figname);

	fprintf(edif_file, "\n   (cell %s (cellType GENERIC)", figname);

	if (c == 'C')
		view(figname, 'A');
	else
		view(figname, 'I');

	fprintf(edif_file, ")");
}

/*---------------------------------------------------------\
                     view
\---------------------------------------------------------*/
static void view(figname, viewtype)
char *figname;
char viewtype;
{
	lofig_list *ptlofig;
	char        view[20];

	strcpy(&view[0], (viewtype == 'A') ? SCHEMATIC: SYMBOL );

	fprintf(edif_file, "\n    (view %s (viewType NETLIST)", view);
	fprintf(edif_file, "\n     (interface");

	ptlofig = getlofig(figname, 'P');

	ptlofig->LOCON = (locon_list *)reverse((locon_list *)ptlofig->LOCON);

	edit_con(ptlofig->LOCON);

	fprintf(edif_file, ")");

	if( viewtype == 'A' )
	{
		fprintf(edif_file, "\n     (contents");

		ptlofig->LOINS = (loins_list *)reverse((loins_list *)ptlofig->LOINS);
		edit_ins( ptlofig->LOINS );
		ptlofig->LOINS = (loins_list *)reverse((loins_list *)ptlofig->LOINS);

		ptlofig->LOSIG = (losig_list *)reverse((losig_list *)ptlofig->LOSIG);
		edit_sig( ptlofig );
		ptlofig->LOSIG = (losig_list *)reverse((losig_list *)ptlofig->LOSIG);

		fprintf( edif_file, ")");
	}

	fprintf(edif_file, ")");

	ptlofig->LOCON = (locon_list *)reverse((locon_list *)ptlofig->LOCON);

	return;
} /* end of view */

/*---------------------------------------------------------\
                     connect
\---------------------------------------------------------*/
static void connect(conn, index, instance)
locon_list *conn;
int         index;
loins_list *instance;
{
	locon_list *ptcon;
	locon_list *ptcon2;
	losig_list *sig;
	char       *busname;
    char       *name;
	char       *pt;
	int         begin;
	int         end;
	int         myindex;

	ptcon = conn;

	for( ; ptcon != (locon_list *) NULL; ptcon = ptcon -> NEXT)
	{
		sig = ptcon->SIG;
		if( sig->INDEX == index )
		{
			name = mbkalloc( strlen( ptcon->NAME ) + 1 );
			strcpy( name, ptcon->NAME );
			for( pt = name; *pt; pt++ )
				if( *pt == '/' )
					*pt = '_';

			busname = edif_busname( name );

			if( !busname )
				fprintf( edif_file,"\n		  (portref %s ", name );
			else
			{
				myindex = edif_busindex( name );
				ptcon2 = conn;
				do
				{
					ptcon2 = edif_found_array( ptcon2, busname, &begin, &end );
				} while( ( ( begin <= end ) && ( ( myindex < begin ) || ( end < myindex ) ) )
				       ||( ( begin >  end ) && ( ( myindex > begin ) || ( end > myindex ) ) ) );

				if( begin != end )
					fprintf( edif_file,"\n		  (portref (member %s_%dTO%d_ %d) ",
					         busname, begin, end,
					         ( begin <= end ) ? myindex - begin : begin - myindex );
				else
					fprintf( edif_file,"\n		  (portref %s_%d_",
					         busname, begin );
			}

            mbkfree( name );

			if( instance != (loins_list *) NULL )
			{
				name = mbkalloc( strlen( instance->INSNAME ) + 1 );
				strcpy( name, instance->INSNAME );
				for( pt = name; *pt; pt++ )
					if( *pt == '/' )
						*pt = '_';

				fprintf( edif_file, "(instanceref %s )", name );

            	mbkfree( name );
			}

			fprintf( edif_file, ")" );

		}
	}
} /* end of connect */

/*---------------------------------------------------------\
                     edit_con
\---------------------------------------------------------*/
static void edit_con( ptcon )
locon_list *ptcon;
{
	char         direction[ 7 ];
	char         cadence_array_name[ 1000 ];
	char        *array_name;
    char        *name;
    char        *pt;
	int          begin;
	int          end;

	while (ptcon != (locon_list *) NULL)
	{
		if( ptcon -> DIRECTION == IN )
			strcpy( direction, "INPUT" );
		else
			if( ( ptcon -> DIRECTION == OUT ) || ( ptcon->DIRECTION == 'Z' ) )
				strcpy(direction, "OUTPUT");
			else
				strcpy(direction, "INOUT");

		name = mbkalloc( strlen( ptcon->NAME ) + 1 );
		strcpy( name, ptcon->NAME );
		for( pt = name; *pt; pt++ )
			if( *pt == '/' )
				*pt = '_';

		if( !( array_name = edif_busname( name ) ) )
		{
			fprintf(edif_file, "\n      (port %s (direction %s))", name, direction);
			ptcon = ptcon -> NEXT;
		}
		else 
		{
			ptcon = edif_found_array( ptcon, array_name, &begin, &end );

			if( end != begin )
			{
				sprintf( cadence_array_name, "(rename %s_%dTO%d_ \"%s<%d:%d>\")",
				         array_name, begin, end,
				         array_name, begin, end );
				fprintf(edif_file, "\n      (port (array %s %d)",
					               cadence_array_name, abs( end - begin ) + 1);
				fprintf(edif_file, "(direction %s))", direction );
			}
			else
				fprintf(edif_file, "\n      (port (rename %s_%d_ \"%s<%d>\") (direction %s))", 
					               array_name, begin, array_name, begin, direction);

			if( ptcon == (locon_list *) NULL )
				break;
		}

        mbkfree( name );
	}
} /* end of edit_con */

/*---------------------------------------------------------\
                     edit_ins
\---------------------------------------------------------*/
static void edit_ins(ptins)
loins_list *ptins;
{
	char  cellref_type;
	char *name;
	char *pt;

	while (ptins != (loins_list *) NULL)
	{      
		name = mbkalloc( strlen( ptins->INSNAME ) + 1 );
		strcpy( name, ptins->INSNAME );
		for( pt = name; *pt; pt++ )
			if( *pt == '/' )
				*pt = '_';

		cellref_type = (incatalog(ptins->FIGNAME) ? 'I' : 'A');

		fprintf(edif_file, "\n      (instance %s (viewref %s (cellref %s )))",
		                   name,
		                   (cellref_type == 'I') ? SYMBOL : SCHEMATIC,
		                   ptins->FIGNAME);
		ptins = ptins -> NEXT;

        mbkfree( name );
	}
} /* end of edit_ins */

/*---------------------------------------------------------\
                     edit_sig
\---------------------------------------------------------*/
static void edit_sig(ptlofig)
lofig_list *ptlofig;
{
	losig_list *ptsig;
	loins_list *sig_ins;
	int         idx = 0;
	long        sig_index;
	char       *array_name = (char *) NULL;
	char       *name = (char *) NULL;
    char       *pt;

	for (ptsig = ptlofig->LOSIG; ptsig != (losig_list *) NULL; ptsig = ptsig->NEXT)
		check_fig_portref( ptlofig->NAME, ptlofig->LOCON, ptsig->INDEX );

	ptsig = ptlofig->LOSIG;
	while( ptsig != (losig_list *) NULL )
	{
		if( ptsig->TYPE != 'I' || ptlofig->LOINS )
		{
			sig_index = ptsig -> INDEX;

			name = getsigname( ptsig );
			for( pt = name; *pt; pt++ )
				if( *pt == '/' )
					*pt = '_';

			if( ( array_name = edif_busname( name ) ) && ( ( idx = edif_busindex( name ) ) != -1 ) )
			{
				fprintf( edif_file, "\n      (net (rename %s_%d_ \"%s<%d>\") (joined ",
				         array_name, idx, array_name, idx );
			} 
			else
			{
				if( !isdigit( name[0] ) )
					fprintf( edif_file, "\n      (net %s (joined ", name );
				else 
					fprintf( edif_file, "\n      (net &_%s (joined ", name );
			}

			if (ptsig->TYPE != 'I')
				port_ext( ptlofig->LOCON, sig_index );

			for( sig_ins = ptlofig->LOINS; sig_ins != NULL; sig_ins = sig_ins->NEXT )
				connect( sig_ins->LOCON, sig_index, sig_ins );

			fprintf(edif_file, ")");


			fprintf(edif_file, ")");
		}

		if( ptsig )
			ptsig = ptsig->NEXT;
	}
} /* end of edit_sig */

/*---------------------------------------------------------\
                     check_fig_portref
\---------------------------------------------------------*/
static void check_fig_portref( lofig_name, ptcon, sig_index )
char        *lofig_name;
locon_list  *ptcon;
int     sig_index;
{
	int nb = 0;

	for (; (ptcon != (locon_list *) NULL) && (nb <= 1); ptcon = ptcon->NEXT)
		if (ptcon->SIG->INDEX == sig_index)
			nb++;

	if (nb > 1)
	{
		fprintf( stderr, "*** mbk error *** edifsavelofig : Error in MBK Structure\n" );
		fprintf( stderr, "    multiple (portref ..) to lofig '%s' not\n" );
		fprintf( stderr, "    allowed on signal of type 'E' (cadence)\n", lofig_name );
		exit( 1 );
	}
   
} /* end of check_fig_portref */

/*---------------------------------------------------------\
                     port_ext
\---------------------------------------------------------*/
static void port_ext( sig_con, sig_index )
locon_list *sig_con;
int         sig_index;
{
	losig_list *sig_con_sig;
	locon_list *connector, *fig_con;
	char       *sig_con_name;

	connector = (locon_list *)NULL;
	fig_con = sig_con;
	sig_con_name = (char *)NULL;

	while (sig_con != (locon_list *)NULL)
	{
		sig_con_sig = sig_con -> SIG;
		if (sig_con_sig -> INDEX == sig_index)
		{
			sig_con_name = sig_con -> NAME;
			connector=sig_con;
			break;
		}
		sig_con = sig_con -> NEXT;
	}

	if (sig_con_name == NULL)
	{
		fprintf( stderr, "***  mbk error *** edifsavelofig : Error in MBK Structure\n" );
		fprintf( stderr, "    No Connector on Signal of type 'E'\n" );
		exit( 1 );
	}

	connect(fig_con, sig_index, (loins_list *)NULL);

} /* end of port_ext */

/*---------------------------------------------------------\
                     edif_found_array
\---------------------------------------------------------*/
static locon_list *edif_found_array( ptcon, name, begin, end )
locon_list *ptcon;
char       *name;
int        *begin;
int        *end;
{
	locon_list *ptcon_first_bus;
	char       *array_name;

	*begin = 0;
	*end   = 0;

	while( ( ptcon ) && (  ( array_name = edif_busname( ptcon->NAME ) ) != name ) )
		ptcon = ptcon->NEXT;

	if( !ptcon )
		return NULL;

	*begin = edif_busindex( ptcon -> NAME );
	*end   = *begin;

	ptcon_first_bus = ptcon;

	while( ( ptcon != (locon_list *) NULL ) && ( array_name == edif_busname( ptcon->NAME ) ) )
	{
		if (ptcon->DIRECTION != ptcon_first_bus->DIRECTION)
		{
			printf( stderr, "*** mbk error *** edifsavelofig : Error in MBK Structure\n" );
			printf( stderr, "    Connectors %s and %s of same bus must have same direction\n", 
			                ptcon_first_bus->NAME, *ptcon->NAME);
			exit( 1 );
		}
		*end = edif_busindex( ptcon->NAME );
		ptcon = ptcon->NEXT; 
	}

	return ptcon;

} /* fin de edif_found_array */

/*---------------------------------------------------------\
                     edif_busindex
\---------------------------------------------------------*/
static int edif_busindex(s)
char *s;
{
	char *t;

	if( !( t = strchr( s, ' ' ) ) )
		return -1;

	return atoi(t);

} /* end of edif_busindex */

/*---------------------------------------------------------\
                     edif_busname
\---------------------------------------------------------*/
char *edif_busname( name )
char *name;
{
	char *s;
	char c[100];

	if( !( s = strchr( name, ' ' ) ) )
		return (char *) NULL;

	strncpy( c, name, (int)(s - name) );
	c[ s - name ] = '\0';

	for (s++; *s != '\0'; s++) 
		if( !isdigit( *s ) )
			return (char *) NULL;

	return namealloc( c );
} /* end of edif_busname */

