/**CFile*********************************************************************** FileName [rstGroup.c] PackageName [rst] Synopsis [rst package partitioning code with user interface.] Author [Desmond A. Kirkpatrick] Copyright [Copyright (c) 1994-1996 The Regents of the Univ. of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.] ******************************************************************************/ #include #include "rstInt.h" #include "cmd.h" #include "varInt.h" static char rcsid[] UNUSED = "$Id: rstGroup.c,v 1.9 2005/05/16 06:22:35 fabio Exp $"; /*---------------------------------------------------------------------------*/ /* Constant declarations */ /*---------------------------------------------------------------------------*/ /* #define RST_GROUP_DEBUG 1 */ /*---------------------------------------------------------------------------*/ /* Structure declarations */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Type declarations */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Variable declarations */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Macro declarations */ /*---------------------------------------------------------------------------*/ #define Rst_VarForEachVarInArray( \ /* array_t* */ array, \ /* int, local */ i, \ /* Var_Variable_t* */ var \ ) \ arrayForEachItem(Var_Variable_t*, array, i, var) /**AutomaticStart*************************************************************/ /*---------------------------------------------------------------------------*/ /* Static function prototypes */ /*---------------------------------------------------------------------------*/ /**AutomaticEnd***************************************************************/ /*---------------------------------------------------------------------------*/ /* Definition of exported functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [Resets number of fanout tables.] Description [This should be moved to the Var package.] SideEffects [] SeeAlso [] ******************************************************************************/ /* This was moved to VAR package by Yuji on 11/8 and renamed to Var_VariableResetNumFanoutTables() void Var_VariableResetNumFanout( Var_Variable_t* var) { var->numFanoutTables = 0; } */ /**Function******************************************************************** Synopsis [Duplicates a table and substitutes its variables.] Description [Duplicates a table and substitutes its input and output variables with variables given in an st_table. This is a common operation when copying tables to new Nodes during hierarchy restructuring operations. VartoVar table MUST contain new variables for each variable in Table. Returns a pointer to new table. This should be moved to the Tbl package. ] SideEffects [Creates new table and potentially modifies variable types of variables in st_table.] SeeAlso [] ******************************************************************************/ Tbl_Table_t* Tbl_TableDupAndSubstVars( Tbl_Table_t* Table, st_table* VartoVar) { int tblIndx, retVal; Var_Variable_t *var, *newVar; Tbl_Table_t* newTbl = Tbl_TableHardDup(Table); Tbl_TableForEachInputVar(newTbl, tblIndx, var) { retVal = st_lookup(VartoVar, (char*) var, &newVar); assert(retVal); Tbl_TableSubstituteVar(newTbl, var, newVar); Var_VariableIncrementNumFanoutTables(newVar); } Tbl_TableForEachOutputVar(newTbl, tblIndx, var) { retVal = st_lookup(VartoVar, (char*) var,&newVar); assert(retVal); Tbl_TableSubstituteVar(newTbl, var, newVar); } return newTbl; } /**Function******************************************************************** Synopsis [Translates a set of variables given one or two st_tables.] Description [Given an array of variables, this function looks up the variable in table VartoVarOne and finds a new variable to translate to. If VartoVarTwo exist, it repeats this operation. It returns an array of new (translated) variables. Is this a good Var package utility? ] SideEffects [Creates new array_t of variables.] SeeAlso [] ******************************************************************************/ array_t* Rst_VartoVarLookup( array_t* VarArray, st_table* VartoVarOne, st_table* VartoVarTwo) { int i, retVal; Var_Variable_t *var, *newVar, *tmpVar; array_t* newVarArray = array_alloc(Var_Variable_t*, array_n(VarArray)); Rst_VarForEachVarInArray(VarArray, i, var) { retVal = st_lookup(VartoVarOne, (char*) var, &newVar); assert(retVal); if (VartoVarTwo) { tmpVar = newVar; retVal = st_lookup(VartoVarTwo, (char*) tmpVar, &newVar); } assert(retVal); array_insert(Var_Variable_t*, newVarArray, i, newVar); } return newVarArray; } /**Function******************************************************************** Synopsis [Grabs non-option strings off command line.] Description [While running getopt, this function can be used to parse strings between options off the command line. This is useful for options with multiple arguments or arguments that can be specified anywhere with respect to option flags. Perhaps this belongs in the Cmd package. ] SideEffects [Creates new array_t of allocated char*s. Presumes that getopt has been reset and is running. MODIFIES util_optind!!!] SeeAlso [util_getopt, Rst_CommandGroupComponents] ******************************************************************************/ array_t* Rst_GetNamesUntilNextOption( array_t* nameArray, int argc, char **argv) { char* name; while ((util_optind < argc) && (*argv[util_optind] != '-')) { name = util_strcat3(argv[util_optind], "", ""); array_insert_last(char*, nameArray, name); util_optind++; } return nameArray; } /**Function******************************************************************** Synopsis [Reads an array of strings from a file.] Description [Returns an array of strings read from an open file.] SideEffects [Returns allocated char*s in nameArray which must be preallocated. fp must have been opened.] SeeAlso [] ******************************************************************************/ array_t* Rst_LoadNameArrayFromFile( array_t* nameArray, FILE* fp) { char* name; char buffer[1024]; while (fscanf(fp, "%s", buffer) != EOF) { name = util_strcat3(buffer, "", ""); array_insert_last(char*, nameArray, name); } return nameArray; } /**Function******************************************************************** Synopsis [Creates a Group from user specified components] Description [Treating all components which PRODUCE variables (ie, outputs of Tables, Latches, Subcircuits) as in the group specified by a list of variables, this function builds a group and all the tables associated. Specifically, it derives the input and output variables, as well as simple tables to list the contained components. A Group can then be transformed into a model via RstGroupBuildModel. Note that a special kind of group for the remainder of the Node being partitioned is created using RstCreateParentGroup. This routine is called by Rst_GroupRestructureNode. NUMBER ONE RULE OF GROUPS: GROUP COMPONENTS are tracked by their OUTPUT VARS ] SideEffects [Allocates a Rst_Group_t.] SeeAlso [RstGroupBuildModel, RstCreateParentGroup, Rst_GroupRestructureNode] ******************************************************************************/ Rst_Group_t* RstGroupComponents( Hrc_Model_t* CurModel, char* NewName, array_t* VarGroup, array_t* Subcircuits) { int i, j, tblIndx; long inGroup; char* latchName, *subcktName; Hrc_Latch_t* latch; Hrc_Subckt_t* subckt; Hrc_Node_t* curMaster; st_generator* sgen; Tbl_Table_t* tbl; Var_Variable_t* var = NIL(Var_Variable_t); array_t* subOutputs; Rst_Group_t* group = RstGroupAlloc(NewName); curMaster = Hrc_ModelReadMasterNode(CurModel); /* Make sure there is something to group */ i=0; if (VarGroup) { i += array_n(VarGroup); } if (Subcircuits) { i += array_n(Subcircuits); } if (i == 0) { return 0; } /* Determine Input/Output Formal list from implied partitioning */ if (VarGroup) { /* Store group variables (ie outputs of tables/latches) in hash table */ Rst_VarForEachVarInArray(VarGroup, i, var) { if ((var) && (! Var_VariableTestIsPI(var))) { st_insert(group->Vars, (char*) var, (char*) (long) 1); } else if ((var) && (Var_VariableTestIsPI(var))) { (void) fprintf(vis_stderr, "Warning: can't group a PI: %s\n", Var_VariableReadName(var)); } } } if (Subcircuits) { /* Store Subckt in group Subcircuits hashtable. Store output vars of all group Subckts in var hash table */ for (i = 0; i < array_n(Subcircuits); i++) { subckt = array_fetch(Hrc_Subckt_t*, Subcircuits, i); st_insert(group->Subcircuits, (char*) subckt, (char*) (long) 1); Rst_VarForEachVarInArray(Hrc_SubcktReadActualOutputVars(subckt), j, var) { if (! st_lookup(group->Vars, (char*) var, &inGroup)) { st_insert(group->Vars, (char*) var, (char*) (long) 1); } } } } /* Table and Latch outputs which are group vars become members of the group */ Hrc_NodeForEachLatch(curMaster, sgen, latchName, latch) { if (st_lookup(group->Vars, (char*) Hrc_LatchReadOutput(latch), &inGroup)) { st_insert(group->Latches, (char*) latch, (char*) (long) 1); } } Hrc_NodeForEachNameTable(curMaster, tblIndx, tbl) { Tbl_TableForEachOutputVar(tbl, j, var) { inGroup = 0; if (st_lookup(group->Vars, (char*) var, &inGroup)) { st_insert(group->Tables, (char*) tbl, (char*) (long) 1); break; } } /* FIXME: Table Splitting here */ if ((inGroup) && (Tbl_TableReadNumOutputs(tbl) > 1)) { (void) fprintf(vis_stderr, "WARNING: multi-output table %s moved to group %s\n", Var_VariableReadName(var), group->name); } } /* If outputs of a subckt are in a group, the subckt moves into the group */ Hrc_ModelForEachSubckt(CurModel, sgen, subcktName, subckt) { Var_Variable_t* outVar = NIL(Var_Variable_t); /* initialize to pacify */ if (! st_lookup(group->Subcircuits, (char*) subckt, &inGroup)) { subOutputs = Hrc_SubcktReadActualOutputVars(subckt); Rst_VarForEachVarInArray(subOutputs, i, outVar) { if (st_lookup(group->Vars, (char*) outVar, &inGroup)) { break; } } if (i < array_n(subOutputs)) { Rst_VarForEachVarInArray(subOutputs, j, var) { if (! st_lookup(group->Vars, (char*) var, &inGroup)) { st_insert(group->Vars, (char*) var, (char*) (long) 1); } } st_insert(group->Subcircuits, (char*) subckt, (char*) (long) 1); (void) fprintf(vis_stderr, "Warning: instance %s becomes member of group %s", subcktName, group->name); (void) fprintf(vis_stderr, " because of output variable %s\n", Var_VariableReadName(outVar)); } } } /* Group Outputs: Scan parent outputs for group vars. Scan non-group component (Subcircuits/Latches/Tables) inputs for group vars */ Hrc_NodeForEachFormalOutput(curMaster, i, var) { if ((st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Outputs, (char*) var, &inGroup))) { st_insert(group->Outputs, (char*) var, (char*) (long) 1); } } Hrc_ModelForEachSubckt(CurModel, sgen, subcktName, subckt) { if (! st_lookup(group->Subcircuits, (char*) subckt, &inGroup)) { Rst_VarForEachVarInArray(Hrc_SubcktReadActualInputVars(subckt), i, var) { if ((st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Outputs, (char*) var, &inGroup))) { st_insert(group->Outputs, (char*) var, (char*) (long) 1); } } } } Hrc_NodeForEachLatch(curMaster, sgen, latchName, latch) { if (! st_lookup(group->Latches, (char*) latch, &inGroup)) { var = Hrc_LatchReadInput(latch); if ((st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Outputs, (char*) var, &inGroup))) { st_insert(group->Outputs, (char*) var, (char*) (long) 1); } } } Hrc_NodeForEachNameTable(curMaster, i, tbl) { if (! st_lookup(group->Tables, (char*) tbl, &inGroup)) { Tbl_TableForEachInputVar(tbl, j, var) { if ((st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Outputs, (char*) var, &inGroup))) { st_insert(group->Outputs, (char*) var, (char*) (long) 1); } } } } /* Group Inputs: Scan group subcomponent inputs for non-group vars */ /* Formal inputs are never vars of a group. Thus, they are always non-group vars when are inputs to group subcomponents, and are caught by the code below*/ Hrc_ModelForEachSubckt(CurModel, sgen, subcktName, subckt) { if (st_lookup(group->Subcircuits, (char*) subckt, &inGroup)) { Rst_VarForEachVarInArray(Hrc_SubcktReadActualInputVars(subckt), i, var) { if ((! st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Inputs, (char*) var, &inGroup))) { st_insert(group->Inputs, (char*) var, (char*) (long) 1); } } } } Hrc_NodeForEachLatch(curMaster, sgen, latchName, latch) { if (st_lookup(group->Latches, (char*) latch, &inGroup)) { var = Hrc_LatchReadInput(latch); if ((! st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Inputs, (char*) var, &inGroup))) { st_insert(group->Inputs, (char*) var, (char*) (long) 1); } /* Reset table here */ tbl = Hrc_LatchReadResetTable(latch); Tbl_TableForEachInputVar(tbl, j, var) { if (! st_lookup(group->Vars, (char*) var, &inGroup)) st_insert(group->Vars, (char*) var, (char*) (long) 1); } } } Hrc_NodeForEachNameTable(curMaster, tblIndx, tbl) { if (st_lookup(group->Tables, (char*) tbl, &inGroup)) { Tbl_TableForEachInputVar(tbl, j, var) { if ((! st_lookup(group->Vars, (char*) var, &inGroup)) && (! st_lookup(group->Inputs, (char*) var, &inGroup))) { st_insert(group->Inputs, (char*) var, (char*) (long) 1); } } } } return group; } /**Function******************************************************************** Synopsis [Takes a Group partitioning of a Node and builds a new Model reflecting that partition.] Description [Called by the main routine Rst_CommandGroupComponents, this routine builds the models corresponding to each group in GroupArray, builds a new Node instantiating these models as subckts. Note that one can build a Group using RstGroupComponents] SideEffects [Allocates a new Hrc_Model_t.] SeeAlso [RstGroupComponents, RstGroupBuildModel, RstCreateParentGroup, Rst_CommandGroupComponents, Rst_GroupRestructureNode] ******************************************************************************/ Hrc_Model_t* Rst_GroupRestructureModel( Hrc_Manager_t* Hmgr, Hrc_Model_t* ParentModel, array_t* GroupArray) { int i, j; char *newModelName; st_generator *sgen; Var_Variable_t *var, *newVar; Hrc_Model_t *newModel, *newParentModel; Hrc_Node_t *newMaster; Rst_Group_t *group, *parentGroup; st_table *varForwPtr; array_t *newModelArray, *newMasterArray, *newNodeArray; for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); #ifdef RST_GROUP_DEBUG RstGroupPrint(group); #endif } /* Check group components for disjointness */ if (RstGroupDisjointCheck(GroupArray)) return 0; /* Construct a level of hierarchy for each group */ newModelArray = array_alloc(Hrc_Model_t*, array_n(GroupArray)); newMasterArray = array_alloc(Hrc_Node_t*, array_n(GroupArray)); newNodeArray = array_alloc(Hrc_Node_t*, array_n(GroupArray)); for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); newModel = RstGroupBuildModel(Hmgr, group); if (! newModel) { return 0; } newMaster = Hrc_ModelReadMasterNode(newModel); array_insert(Hrc_Model_t*, newModelArray, i, newModel); array_insert(Hrc_Node_t*, newMasterArray, i, newMaster); } /* Build the new parent hierarchy */ newModelName = RstGroupNewParentName(Hrc_ModelReadName(ParentModel)); parentGroup = RstCreateParentGroup(Hmgr, ParentModel, newModelName, GroupArray); FREE(newModelName); if (! parentGroup) { return 0; } #ifdef RST_GROUP_DEBUG RstGroupPrint(parentGroup); #endif newParentModel = RstGroupBuildModel(Hmgr, parentGroup); /* Computing actuals: Group IO vars USED to be in the old parent. 1) lookup what the new IO vars used to be in the old parent 2) In the old parent, find the correspond new parent vars */ /* Build Table: Parent to New Parent Vars, by reversing back pointers */ varForwPtr = st_init_table(st_ptrcmp, st_ptrhash); st_foreach_item(parentGroup->VarBackPtr, sgen, &newVar, &var) { st_insert(varForwPtr, (char*) var, (char*) newVar); } /* Free parent group now */ RstGroupFree(parentGroup); parentGroup = 0; /* Instantiate groups as subckts */ for (i = 0; i < array_n(GroupArray); i++) { array_t* actualInputArray, *actualOutputArray; group = array_fetch(Rst_Group_t*, GroupArray, i); newModel = array_fetch(Hrc_Model_t*, newModelArray, i); newMaster = Hrc_ModelReadMasterNode(newModel); actualInputArray = Rst_VartoVarLookup(Hrc_NodeReadFormalInputs(newMaster), group->VarBackPtr, varForwPtr); actualOutputArray = Rst_VartoVarLookup(Hrc_NodeReadFormalOutputs(newMaster), group->VarBackPtr, varForwPtr); Hrc_ModelAddSubckt(newParentModel, newModel, group->name, actualInputArray, actualOutputArray); Rst_VarForEachVarInArray(actualInputArray, j, var) { Var_VariableSetSI(var); } Rst_VarForEachVarInArray(actualOutputArray, j, var) { Var_VariableSetSO(var); } } array_free(newModelArray); array_free(newMasterArray); array_free(newNodeArray); st_free_table(varForwPtr); return newParentModel; } /**Function******************************************************************** Synopsis [Takes a Group partitioning of a Node and replaces Node with the partitioned Node.] Description [Called by the main routine Rst_CommandGroupComponents, this routine builds the models corresponding to each group in GroupArray, builds a new Node instantiating these models as subckts. Note that one can build a Group using RstGroupComponents] SideEffects [Allocates a new Hrc_Node_t and replaces the CurNode with it.] SeeAlso [RstGroupComponents, RstGroupBuildModel, RstCreateParentGroup, Rst_CommandGroupComponents] ******************************************************************************/ Hrc_Node_t* Rst_GroupRestructureNode( Hrc_Manager_t* Hmgr, Hrc_Node_t* CurNode, array_t* GroupArray) { int retVal; #ifdef RST_GROUP_DEBUG st_generator* sgen; char* childName; Hrc_Node_t *child; #endif Hrc_Model_t *newParentModel; Hrc_Node_t *newNode = 0; Hrc_Model_t* CurModel= Hrc_ManagerFindModelByName(Hmgr, Hrc_NodeReadModelName(CurNode)); newParentModel = Rst_GroupRestructureModel(Hmgr, CurModel, GroupArray); if (newParentModel) { newNode = Hrc_ModelCreateHierarchy(Hmgr, newParentModel, Hrc_NodeReadInstanceName(CurNode)); #ifdef RST_GROUP_DEBUG (void) fprintf(vis_stderr, "Checking New Node\n"); assert(Hrc_NodeCheckVariableConsistency(newNode)); (void) fprintf(vis_stderr, "Checking Subcircuits\n"); Hrc_NodeForEachChild(newNode, sgen, childName, child) { assert(Hrc_NodeCheckVariableConsistency(child)); } #endif retVal = Hrc_TreeReplace(CurNode, newNode); assert(retVal); Hrc_ManagerSetCurrentNode(Hmgr, newNode); } return newNode; } /**Function******************************************************************** Synopsis [Creates a model of the combinational portion of a Node.] Description [Finds the combinational logic of a Model and groups it into a new Model.] SideEffects [Allocates a new Hrc_Model_t.] SeeAlso [RstGroupComponents, Rst_GroupRestructureNode] ******************************************************************************/ Hrc_Model_t* Rst_GroupGroupCombLogic( Hrc_Manager_t* Hmgr, Hrc_Model_t* CurModel) { long inGroup; int tblIndx, varIndx; Var_Variable_t *var; Tbl_Table_t* tbl; array_t* varArray = array_alloc(Var_Variable_t*, 0); Rst_Group_t *group; st_table *tableOutputs = st_init_table(st_ptrcmp, st_ptrhash); Hrc_Node_t *curMaster; Hrc_Model_t* newModel; /* Perhaps this is better done with the NameTables so that we drop subckts from inclusion */ curMaster = Hrc_ModelReadMasterNode(CurModel); Hrc_NodeForEachNameTable(curMaster, tblIndx, tbl) { Tbl_TableForEachOutputVar(tbl, varIndx, var) { if (! st_lookup(tableOutputs, (char*) var, &inGroup)) { st_insert(tableOutputs, (char*) var, (char*) (long) 1); array_insert_last(Var_Variable_t*, varArray, var); } } } st_free_table(tableOutputs); group = RstGroupComponents(CurModel, util_strcat3(Hrc_ModelReadName(CurModel), "_comb", ""), varArray, NULL); array_free(varArray); if (group) { array_t *groupArray = array_alloc(Rst_Group_t*, 1); array_insert_last(Rst_Group_t*, groupArray, group); newModel = Rst_GroupRestructureModel(Hmgr, CurModel, groupArray); array_free(groupArray); return newModel; } return 0; } /**Function******************************************************************** Synopsis [Creates a model of the latch portion of a Node.] Description [Finds the latches of a Model and groups them into a new Model.] SideEffects [Allocates a new Hrc_Model_t.] SeeAlso [RstGroupComponents, Rst_GroupRestructureNode] ******************************************************************************/ Hrc_Model_t* Rst_GroupGroupLatches( Hrc_Manager_t* Hmgr, Hrc_Model_t* CurModel) { char *latchName; st_generator *sgen; Hrc_Latch_t *latch; array_t* varArray = array_alloc(Var_Variable_t*, 0); Rst_Group_t *group; Hrc_Node_t* curMaster = Hrc_ModelReadMasterNode(CurModel); Hrc_Model_t* newModel; Hrc_NodeForEachLatch(curMaster, sgen, latchName, latch) { array_insert_last(Var_Variable_t*, varArray, Hrc_LatchReadOutput(latch)); } group = RstGroupComponents(CurModel, util_strcat3(Hrc_ModelReadName(CurModel), "_latch", ""), varArray, NULL); array_free(varArray); if (group) { array_t *groupArray = array_alloc(Rst_Group_t*, 1); array_insert_last(Rst_Group_t*, groupArray, group); newModel = Rst_GroupRestructureModel(Hmgr, CurModel, groupArray); array_free(groupArray); return newModel; } return 0; } /**Function******************************************************************** Synopsis [This function is the command-line interface for the user to specify groups into which to partition the components of the current level of hierarchy.] CommandName [decompose_child] CommandSynopsis [Partitions Node components into child nodes. ] CommandArguments[-n <groupname> \[-f <filename>\] \[-c\] \[-v\] \[-t\] \[-l\] \[<components>\]] CommandDescription [This command partitions the current Node into several new children nodes according to user-specified groups. The user may specify several groups by name \[-n\] sequentially on the command line. Within each group a list of components (either variables or child nodes) can be placed. The flags \[-c\] \[-v\] toggle whether element names are interpreted as child instances or variables (default is instances \[-c\]). Element names can be loaded from a file as well using the \[-f\] flag.

By specifying a variable to be grouped, any object (e.g., Table, Latch, Child) that PRODUCES that variable is included in the newly created child node for that group.

Command options:

-n <groupname>
Define new group
-f <filename>
Load names from file
-c
Interpret names that follow as child instances to group
-v
Interpret names that follow as variables
-l
Include all latch output nodes (and therefore latches) in group
-t
Include all combinational tables in group. Examples:

decompose_child -n c1 -v G1 G2 -n c2 IA IB

Place variables G1 and G2 into a new child node c1 and child nodes IA and IB into a new child node c2.

Example:

decompose_child -n ckt_inst child1 -f myinstances -v var1 -n latch_inst -l

This groups the subckts child1 and those listed in the file myinstances, as well as the variable var1 into a subckt called 'ckt_inst'. It also groups all the latches into a subckt called 'latch_inst'.] SideEffects [Replaces current Node with a new Node containing children representing the partition.] ******************************************************************************/ int Rst_CommandGroupComponents( Hrc_Manager_t** Hmgr, int argc, char ** argv) { enum { SUBCKT_GROUP, VAR_GROUP } readType = SUBCKT_GROUP; int c, i, retVal, tblIndx; st_generator *sgen; Rst_Group_t* group; Var_Variable_t* var; Hrc_Subckt_t* child; char *fileName, *varName, *childName, *latchName; char* groupName = 0; array_t *vars = 0, *children = 0; Hrc_Latch_t* latch; Tbl_Table_t* tbl; array_t* nameArray = 0; array_t* groupArray = array_alloc(Rst_Group_t*, 1); Hrc_Node_t* curNode = Hrc_ManagerReadCurrentNode(*Hmgr); Hrc_Node_t* curMaster; FILE* fp; Hrc_Model_t* CurModel; if (! curNode) { /* No node has been read in. The only sane thing we support is the -h option */ array_free(groupArray); groupArray = 0; util_getopt_reset(); if ((c = util_getopt(argc,argv,"h")) != EOF) { goto usage; } (void) fprintf(vis_stderr, "ERROR: no current Node\n"); return 1; } CurModel= Hrc_ManagerFindModelByName(Hrc_NodeReadManager(curNode), Hrc_NodeReadModelName(curNode)); curMaster = Hrc_ModelReadMasterNode(CurModel); /* This command processing loop is conceptually simple, but almost 'goto' like and needs explanation. The command line is a sequence of groups between which are specified component names to group. A group is started with a Naming operation '-n'. We get a list of component names to group either from the command line or from a file. Names can be anywhere on the command line (except between -n/-f and their argument). The names are interpreted as Variables or Children depending on the last -v or -c flag. Children is the default setting on entry. After every option, we can read more names off command line, so we drop into this mode after completing a -n or -f operation. This means we always drop into the outer 'default'. The goal is to collect a set of Variable and Child names for each Group Name specified. */ util_getopt_reset(); while ((c = util_getopt(argc, argv, "n:f:cvtlh")) != EOF) { switch(c) { case 'n': /* expects a Name arg */ case 'f': /* expects a filename arg */ switch(c) { case 'n': /* Name a new group */ if (groupName != NULL) { group = RstGroupComponents(CurModel, groupName, vars, children); array_free(vars); vars = 0; array_free(children); children = 0; FREE(groupName); if (group) { array_insert_last(Rst_Group_t*, groupArray, group); } else { (void) fprintf(vis_stderr, "Warning: Incomplete group %s -- ignored\n", groupName); RstGroupFree(group); group = 0; } } vars = array_alloc(Var_Variable_t*, 0); children = array_alloc(Hrc_Subckt_t*, 0); groupName = util_strcat3(argv[util_optind -1], "",""); break; case 'f': /* Read component names from file */ fileName = argv[util_optind - 1]; fp = Cmd_FileOpen(util_optarg, "r", &fileName, 1); if (fp == NIL(FILE)) { (void) fprintf(vis_stderr, "Could not open file %s\n", fileName); return 1; } nameArray = array_alloc(char*, 0); Rst_LoadNameArrayFromFile(nameArray, fp); fclose(fp); break; } /* WE ALWAYS DROP IN HERE ON EVERY FLAG */ default: if (! groupName && (c != 'h')) { (void) fprintf(vis_stderr, "ERROR: Group not defined yet\n"); goto usage; } if (! nameArray) nameArray = array_alloc(char*, 0); switch(c) { case 'n': case 'f': break; case 'c': readType = SUBCKT_GROUP; break; case 'v': readType = VAR_GROUP; break; case 'l': /* Put latch ouput vars into group*/ Hrc_NodeForEachLatch(curMaster, sgen, latchName, latch) { var = Hrc_LatchReadOutput(latch); array_insert_last(Var_Variable_t*, vars, var); } break; case 't': /* Put table ouput vars into group*/ Hrc_NodeForEachNameTable(curMaster, tblIndx, tbl) { Tbl_TableForEachOutputVar(tbl, i, var) { array_insert_last(Var_Variable_t*, vars, var); } } break; default: goto usage; } /* Grab any more names off the command line, appending if possible to nameArray */ Rst_GetNamesUntilNextOption(nameArray, argc, argv); /* We store names read as either variable names or child names */ switch(readType) { case VAR_GROUP: for (i=0; i < array_n(nameArray); i++) { varName = array_fetch(char*, nameArray, i); var = Hrc_NodeFindVariableByName(curMaster, varName); if (var) { array_insert_last(Var_Variable_t*, vars, var); } else { (void) fprintf(vis_stderr, "Could not find variable %s\n", varName); } } break; case SUBCKT_GROUP: for (i=0; i < array_n(nameArray); i++) { childName = array_fetch(char*, nameArray, i); retVal = st_lookup(Hrc_ModelReadSubcktTable(CurModel), childName, &child); if (retVal) { array_insert_last(Hrc_Subckt_t*, children, child); } else { (void) fprintf(vis_stderr, "Could not find instance %s\n", childName); } } break; default: /* Unreached */ break; } break; } for (i=0; i < array_n(nameArray); i++) { varName = array_fetch(char*, nameArray, i); FREE(varName); } array_free(nameArray); nameArray = 0; } if (groupName) { group = RstGroupComponents(CurModel, groupName, vars, children); if (group) array_insert_last(Rst_Group_t*, groupArray, group); else (void) fprintf(vis_stderr, "Incomplete group %s\n", groupName); array_free(vars); vars = 0; array_free(children); children = 0; FREE(groupName); } if (array_n(groupArray) > 0) { Rst_GroupRestructureNode(*Hmgr, curNode, groupArray); for (i=0; i < array_n(groupArray); i++) { group = array_fetch(Rst_Group_t*, groupArray, i); RstGroupFree(group); } array_free(groupArray); groupArray = 0; } else { (void) fprintf(vis_stderr, "ERROR: no groups defined\n"); array_free(groupArray); return 1; } return 0; /* normal exit */ usage: if (vars) array_free(vars); vars = 0; if (children) array_free(children); children = 0; FREE(groupName); groupName = 0; array_free(nameArray); nameArray = 0; array_free(groupArray); (void) fprintf(vis_stderr, "usage: decompose_child -n "); (void) fprintf(vis_stderr, " [-f ] [-c] [-v] [] [-t] [-l] [-h] \n"); (void) fprintf(vis_stderr, " -n\t\tName of new child group \n"); (void) fprintf(vis_stderr, " -f\t\tLoad names from file \n"); (void) fprintf(vis_stderr, " -c\t\tGroup by children toggle (default)\n"); (void) fprintf(vis_stderr, " -v\t\tGroup by variable toggle \n"); (void) fprintf(vis_stderr, " -l\t\tGroup latches\n"); (void) fprintf(vis_stderr, " -t\t\tGroup combinational logic tables \n"); (void) fprintf(vis_stderr, " -h\t\thelp \n"); return 1; /* error exit */ } /*---------------------------------------------------------------------------*/ /* Definition of internal functions */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Definition of static functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [Allocates a group.] Description [Initializes all tables for collecting components of a group for node partitioning. The VarBackPtr table stores pointers from newly allocated variables to their original variables in the Node being partitioned.] SideEffects [Creates a new Rst_Group_t.] SeeAlso [RstGroupFree, RstGroupPrint] ******************************************************************************/ Rst_Group_t* RstGroupAlloc(char* Name) { Rst_Group_t* group = ALLOC(Rst_Group_t, 1); group->Vars = st_init_table(st_ptrcmp, st_ptrhash); group->Latches = st_init_table(st_ptrcmp, st_ptrhash); group->Tables = st_init_table(st_ptrcmp, st_ptrhash); group->Subcircuits = st_init_table(st_ptrcmp, st_ptrhash); group->Inputs = st_init_table(st_ptrcmp, st_ptrhash); group->Outputs = st_init_table(st_ptrcmp, st_ptrhash); group->VarBackPtr = st_init_table(st_ptrcmp, st_ptrhash); group->name = ALLOC(char, strlen(Name) + 1); strcpy(group->name, Name); return group; } /**Function******************************************************************** Synopsis [Frees a group.] Description [Frees all tables associated with a group. Frees the name stored with the group.] SideEffects [] SeeAlso [RstGroupAlloc, RstGroupPrint] ******************************************************************************/ void RstGroupFree(Rst_Group_t* Group) { st_free_table(Group->Vars); st_free_table(Group->Latches); st_free_table(Group->Tables); st_free_table(Group->Subcircuits); st_free_table(Group->Inputs); st_free_table(Group->Outputs); st_free_table(Group->VarBackPtr); FREE(Group->name); FREE(Group); /* Watch for dangling references */ } /**Function******************************************************************** Synopsis [Creates a new name for the node being partitioned.] Description [] SideEffects [] SeeAlso [] ******************************************************************************/ char* RstGroupNewParentName( char* Name) { char* newName = util_strcat3(Name, "_N", ""); return newName; } /**Function******************************************************************** Synopsis [Prints the tables of a group for debug purposes.] Description [] SideEffects [] SeeAlso [] ******************************************************************************/ void RstGroupPrint( Rst_Group_t* group) { st_generator* sgen; Var_Variable_t* var; Hrc_Subckt_t* subckt; Hrc_Latch_t* latch; printf("GROUP: %s\n\tInputs:\t", group->name); st_foreach_item(group->Inputs, sgen, &var, NIL(char *)) { printf("%s ", Var_VariableReadName(var)); } printf("\n\tOutputs:\t"); st_foreach_item(group->Outputs, sgen, &var, NIL(char *)) { printf("%s ", Var_VariableReadName(var)); } printf("\n\tSubcircuits:\t"); st_foreach_item(group->Subcircuits, sgen, &subckt, NIL(char *)) { printf("%s ", Hrc_SubcktReadInstanceName(subckt)); } printf("\n\tLatches:\t"); st_foreach_item(group->Latches, sgen, &latch, NIL(char *)) { printf("%s ", Var_VariableReadName(Hrc_LatchReadOutput(latch))); } printf("\n\tVars:\t"); st_foreach_item(group->Vars, sgen, &var, NIL(char *)) { printf("%s ", Var_VariableReadName(var)); } printf("\n"); } /**Function******************************************************************** Synopsis [Makes sure groups are disjoint.] Description [Checks that each Variable, Table, Latch, and Subcircuit is assigned to one and only one group.] SideEffects [] SeeAlso [Rst_GroupBuildModel] ******************************************************************************/ int RstGroupDisjointCheck( array_t* GroupArray) { int i, j, retVal; st_generator *sgen; Hrc_Subckt_t* subckt; Rst_Group_t* group1, *group2; Hrc_Latch_t* latch; Tbl_Table_t* tbl; Var_Variable_t* var; for (i = 0; i < array_n(GroupArray); i++) { group1 = array_fetch(Rst_Group_t*, GroupArray, i); for (j = i + 1; j < array_n(GroupArray); j++) { group2 = array_fetch(Rst_Group_t*, GroupArray, j); st_foreach_item(group1->Subcircuits, sgen, &subckt, NULL) { retVal = st_lookup(group2->Subcircuits, (char*) subckt, NIL(char *)); if (retVal) { (void) fprintf(vis_stderr, "ERROR: component %s assigned to groups %s and %s\n", Hrc_SubcktReadInstanceName(subckt), group1->name, group2->name); return 1; } } st_foreach_item(group1->Vars, sgen, &var, NULL) { retVal = st_lookup(group2->Vars, (char*) var, NIL(char *)); if (retVal) { (void) fprintf(vis_stderr, "ERROR: var %s assigned to groups %s and %s\n", Var_VariableReadName(var), group1->name, group2->name); return 1; } } st_foreach_item(group1->Tables, sgen, &tbl, NULL) { retVal = st_lookup(group2->Tables, (char*) tbl, NIL(char *)); if (retVal) { (void) fprintf(vis_stderr, "ERROR: table assigned to groups %s and %s\n", group1->name, group2->name); return 1; } } st_foreach_item(group1->Latches, sgen, &latch, NULL) { retVal = st_lookup(group2->Latches, (char*) latch, NIL(char *)); if (retVal) { (void) fprintf(vis_stderr, "ERROR: latch %s assigned to groups %s and %s\n", Var_VariableReadName(Hrc_LatchReadOutput(latch)), group1->name, group2->name); return 1; } } } } return 0; } /**Function******************************************************************** Synopsis [Creates the Parent Group.] Description [Creates a special Group representing components of a Node "left behind" by a partitioning of it's components into groups. This takes special care because Vars have to remain in the parent if they are on the interface of a Group or connect remaining components of the Parent.] SideEffects [Allocates a new Rst_Group_t] SeeAlso [RstGroupComponents, RstGroupBuildModel, Rst_GroupRestructureNode] ******************************************************************************/ Rst_Group_t* RstCreateParentGroup( Hrc_Manager_t* Hmgr, Hrc_Model_t* ParentModel, char* NewModelName, array_t* GroupArray) { int i, j, tblIndx, varIndx; long inGroup; char *latchName, *subcktName, *varName; st_generator* sgen; Var_Variable_t* var; Tbl_Table_t* tbl; Hrc_Latch_t* latch; Hrc_Subckt_t *subckt; Hrc_Node_t *parentMaster; Rst_Group_t *group, *parentGroup; array_t* subOutputs; /* Build a parent group */ parentGroup = RstGroupAlloc(NewModelName); parentMaster = Hrc_ModelReadMasterNode(ParentModel); /* IO of latches of parent group are parent group vars */ Hrc_NodeForEachLatch(parentMaster, sgen, latchName, latch) { for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); if (st_lookup(group->Latches, (char*) latch, &inGroup)) { break; } } if (i >= array_n(GroupArray)) { st_insert(parentGroup->Latches, (char*) latch, (char*) (long) 1); var = Hrc_LatchReadInput(latch); if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } var = Hrc_LatchReadOutput(latch); if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } } } /* IO of parent group tables (that stay in parent) are parent group vars */ Hrc_NodeForEachNameTable(parentMaster, tblIndx, tbl) { for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); if (st_lookup(group->Tables, (char*) tbl, &inGroup)) { break; } } if (i >= array_n(GroupArray)) { /* Table stays in parent */ st_insert(parentGroup->Tables, (char*) tbl, (char*) (long) 1); Tbl_TableForEachInputVar(tbl, varIndx, var) { if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } } Tbl_TableForEachOutputVar(tbl, varIndx, var) { if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } } } } /* IO of groups are group Vars of the parent. Note that inputs are never group vars of a group EXCEPT in the case of the parent group */ Hrc_NodeForEachVariable(parentMaster, sgen, varName, var) { if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); if ((st_lookup(group->Inputs, (char*) var, &inGroup)) || (st_lookup(group->Outputs, (char*) var, &inGroup))) { break; } } if (i < array_n(GroupArray)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } } } /* All formal IO of old parent remains part of the parent group */ Hrc_NodeForEachFormalInput(parentMaster, i, var) st_insert(parentGroup->Inputs, (char*) var, (char*) (long) 1); Hrc_NodeForEachFormalOutput(parentMaster, i, var) st_insert(parentGroup->Outputs, (char*) var, (char*) (long) 1); /* Outputs of subckts of parent group are parent group vars We do this last to catch this possible dangling input scenario*/ Hrc_ModelForEachSubckt(ParentModel, sgen, subcktName, subckt) { for (i = 0; i < array_n(GroupArray); i++) { group = array_fetch(Rst_Group_t*, GroupArray, i); if (st_lookup(group->Subcircuits, (char*) subckt, &inGroup)) { break; } } if (i >= array_n(GroupArray)) { st_insert(parentGroup->Subcircuits, (char*) subckt, (char*) (long) 1); subOutputs = Hrc_SubcktReadActualOutputVars(subckt); Rst_VarForEachVarInArray(subOutputs, j, var) { if (! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) { st_insert(parentGroup->Vars, (char*) var, (char*) (long) 1); } } /* FIXME: Possible bug: inputs are caught because they are outputs of other components. But a dangling input (ie, one with no connection to anything) might get dropped. But adding an input var might cause it's driving component to be incorporated! We opt for a warning here. */ /* FIXME: Hack-using same array */ subOutputs = Hrc_SubcktReadActualInputVars(subckt); Rst_VarForEachVarInArray(subOutputs, j, var) { if ((! st_lookup(parentGroup->Vars, (char*) var, &inGroup)) && (! st_lookup(parentGroup->Inputs, (char*) var, &inGroup))) { (void) fprintf(vis_stderr, "Warning: subcircuit %s has input variable %s ", subcktName, Var_VariableReadName(var)); (void) fprintf(vis_stderr, "that has no driver in parent %s\n", parentGroup->name); } } } } return parentGroup; } /**Function******************************************************************** Synopsis [Debug routine for printing a Model.] Description [Prints the IO and Vars of a new Model] SideEffects [] SeeAlso [RstGroupBuildModel] ******************************************************************************/ void RstModelPrint( Hrc_Model_t* newModel) { Hrc_Node_t* newMaster; int i; st_generator* sgen; char* varName; Var_Variable_t* var; /* Print out new model */ printf("Model %s\n", Hrc_ModelReadName(newModel)); newMaster = Hrc_ModelReadMasterNode(newModel); printf("\tInputs :\t"); Rst_VarForEachVarInArray(Hrc_NodeReadFormalInputs(newMaster), i, var) { printf("%s ", Var_VariableReadName(var)); } printf("\n\tOutputs:\t"); Rst_VarForEachVarInArray(Hrc_NodeReadFormalOutputs(newMaster), i, var) { printf("%s ", Var_VariableReadName(var)); } printf("\n\tVars:\t"); Hrc_NodeForEachVariable(Hrc_ModelReadMasterNode(newModel), sgen, varName, var) { printf("%s ", Var_VariableReadName(var)); } printf("\n"); } /**Function******************************************************************** Synopsis [Builds a Model from a Group.] Description [Uses the tables of a group to replicate the portion of a Node' structure contained by a Groups. Duplicates Variables, Tables, Latches, and Subcircuit Instances to create a Model reflecting the structure of the Group.] SideEffects [Allocates a new Hrc_Model_t.] SeeAlso [RstGroupComponents, Rst_GroupRestructureNode, Rst_CommandGroupComponents] ******************************************************************************/ Hrc_Model_t* RstGroupBuildModel( Hrc_Manager_t* Hmgr, Rst_Group_t* Group) { long inGroup; int retVal, i; st_generator* sgen; Var_Variable_t* var, *newVar, *newLatchInput, *newLatchOutput; Tbl_Table_t* tbl, *newTbl; Hrc_Latch_t* latch, *newLatch; st_table* varForwPtr; Hrc_Node_t* newMaster; Hrc_Subckt_t *subckt; Hrc_Model_t* subcktModel; Hrc_Model_t* newModel = Hrc_ModelAlloc(Hmgr, Group->name); array_t* actualInputArray, *actualOutputArray; if (! newModel) { (void) fprintf(vis_stderr, "ERROR: could not create Model %s\n", Group->name); return 0; } newMaster = Hrc_ModelReadMasterNode(newModel); assert(newMaster); varForwPtr = st_init_table(st_ptrcmp, st_ptrhash); /* Add group's Variables: Inputs, Outputs */ st_foreach_item(Group->Inputs, sgen, &var, &inGroup) { if (! st_lookup(varForwPtr, (char*) var, &newVar)) { newVar = Var_VariableDup(var, newMaster); Var_VariableResetAllTypes(newVar); Var_VariableResetNumFanoutTables(newVar); Hrc_NodeAddFormalInput(newMaster, newVar); Var_VariableSetPI(newVar); st_insert(Group->VarBackPtr, (char*) newVar, (char*) var); st_insert(varForwPtr, (char*) var, (char*) newVar); } } st_foreach_item(Group->Outputs, sgen, &var, &inGroup) { if (! st_lookup(varForwPtr, (char*) var, &newVar)) { newVar = Var_VariableDup(var, newMaster); Var_VariableResetAllTypes(newVar); Var_VariableResetNumFanoutTables(newVar); Hrc_NodeAddFormalOutput(newMaster, newVar); Var_VariableSetPO(newVar); st_insert(Group->VarBackPtr, (char*) newVar, (char*) var); st_insert(varForwPtr, (char*) var, (char*) newVar); } } st_foreach_item(Group->Vars, sgen, &var, &inGroup) { if (! st_lookup(varForwPtr, (char*) var, &newVar)) { newVar = Var_VariableDup(var, newMaster); Var_VariableResetAllTypes(newVar); Var_VariableResetNumFanoutTables(newVar); st_insert(Group->VarBackPtr, (char*) newVar, (char*) var); st_insert(varForwPtr, (char*) var, (char*) newVar); } } /* Add group's Tables */ st_foreach_item(Group->Tables, sgen, &tbl, &inGroup) { Hrc_NodeAddNameTable(newMaster, Tbl_TableDupAndSubstVars(tbl, varForwPtr)); } /* Add group's Latches */ st_foreach_item(Group->Latches, sgen, &latch, &inGroup) { retVal = st_lookup(varForwPtr, (char*) Hrc_LatchReadInput(latch), &newLatchInput); assert(retVal); retVal = st_lookup(varForwPtr, (char*) Hrc_LatchReadOutput(latch), &newLatchOutput); assert(retVal); newLatch = Hrc_LatchCreate(newModel, newLatchInput, newLatchOutput); assert(newLatch != NULL); Var_VariableSetNS(newLatchInput); Var_VariableSetPS(newLatchOutput); /* Duplicate and attach Reset Table */ tbl = Hrc_LatchReadResetTable(latch); newTbl = Tbl_TableDupAndSubstVars(tbl, varForwPtr); /* HACK: For some reason, we don't count latches in fanout count */ retVal = Hrc_LatchSetResetTable(newLatch, newTbl); /* newTbl = Hrc_LatchReadResetTable(latch); */ assert(retVal); Tbl_TableForEachInputVar(tbl, i, var) { retVal = st_lookup(varForwPtr, (char*) var, &newVar); assert(retVal); Var_VariableResetAllTypes(newVar); Var_VariableResetNumFanoutTables(newVar); } Hrc_NodeAddLatch(newMaster, newLatch); } /* Add group's Subckts */ st_foreach_item(Group->Subcircuits, sgen, &subckt, &inGroup) { actualInputArray = Rst_VartoVarLookup(Hrc_SubcktReadActualInputVars(subckt), varForwPtr, 0); actualOutputArray = Rst_VartoVarLookup(Hrc_SubcktReadActualOutputVars(subckt), varForwPtr, 0); subcktModel = Hrc_SubcktReadModel(subckt); Hrc_ModelAddSubckt(newModel, subcktModel, Hrc_SubcktReadInstanceName(subckt), actualInputArray, actualOutputArray); Rst_VarForEachVarInArray(actualInputArray, i, var) { Var_VariableSetSI(var); } Rst_VarForEachVarInArray(actualOutputArray, i, var) { Var_VariableSetSO(var); } } st_free_table(varForwPtr); #ifdef RST_GROUP_DEBUG RstModelPrint(newModel); #endif return newModel; } /* Overrides for Emacs so that we follow VIS tabbing style. * Must remain at end of file. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 2 * c-indent-level: 2 * c-brace-imaginary-offset: 0 * c-brace-offset: -2 * c-argdecl-indent: 2 * c-comment-only-line-offset: 0 * c-label-offset: -4 * c-continued-statement-offset: 2 * c-continued-brace-offset: 0 * End: */