!! FLATTEN program to expand ESDL descriptions 05/11/79 !! V3.2 [01/03/83]: mods for IMP8 %BEGIN %CONSTSTRING(31) VERSION="FLATTEN version 3.2 (APM)" %systemstring (255) %fnspec itos(%integer v,p) %EXTERNALINTEGERFNSPEC DEF STREAMS(%STRING(127) STREAMS, DEFAULTS) %INTEGER RETURN CODE %OWNSTRING(23) DEFAULTS=".EIC,.LIB/%I1.FIC" !!******************************************************* !! Program to expand and link an ESDL I-code description* !! The description to be expanded is read from stream 1 * !! Any references to UNITs not described in this I-code * !! can be resolved from a library of UNITs provided on * !! stream 2. Only those UNITs actually referenced will * !! be stored or used. * !!******************************************************* %recordformatspec ftagdef %RECORDFORMAT FTAG(%STRING(255)%NAME NAME, %RECORD(FTAGDEF)%NAME TAGDEF, %INTEGER MODIFIER, HNEXT) %CONSTINTEGER TAGLEN=4 %recordformatspec funit %RECORDFORMAT FTAGDEF(%INTEGER LEVEL, REFS, %RECORD(FUNIT)%NAME UNIT, %RECORD(FTAGDEF)%NAME PREV) %CONSTINTEGER TAGDEFLEN=4 %RECORDFORMAT FCOMMENT(%RECORD(FCOMMENT)%NAME NEXT, %STRING(255) TEXT) %CONSTINTEGER COMMENTLEN=1 %recordformatspec fnet %RECORDFORMAT FTERMINAL(%INTEGER INFO, %RECORD(FNET)%NAME NET, %RECORD(FTAG)%NAME PIN, NAME) %CONSTINTEGER TERMINALLEN=4 %CONSTINTEGER MAX PARMS=8 %RECORDFORMAT FHEAD(%RECORD(FTAGDEF)%NAME TAGDEF, %RECORD(FCOMMENT)%NAME COMMENTS, %INTEGER FLAGS, OFFSET, %INTEGER OPTIONS, NIN, NOUT, NIO, NT, %RECORD(FTAG)%NAME UNAME, NAME, %RECORD(FTAG)%NAME %ARRAY PARM(1:MAX PARMS), %RECORD(FTERMINAL)%ARRAY T(1:999)) %CONSTINTEGER HEADLEN=11+MAXPARMS %RECORDFORMAT FFANEL(%INTEGER SUBNO, TNO) %CONSTINTEGER FANLEN=2 %RECORDFORMAT FNET(%RECORD(FNET)%NAME NEXT, SAME, %RECORD(FTAG)%NAME NAME, %INTEGER FAN, %RECORD(FFANEL)%ARRAY F(1:1000)) %CONSTINTEGER NETLEN=4 %RECORDFORMAT FUNIT(%RECORD(FUNIT)%NAME NEXT, %INTEGER TYPE, NSUBS, %RECORD(FNET)%NAME NETS, %RECORD(FHEAD)%NAME %ARRAY H(0:999)) %CONSTINTEGER UNITLEN=5 !! working space %CONSTINTEGER STACKLEN=45000 %INTEGERARRAY STACK(0:STACKLEN) %OWNINTEGER TOS, STACKTOP, MAXTOS !! i/o streams and associated constants %CONSTINTEGER ENDFILE=9, END OF FILE=-1, CNTRL=128 %CONSTINTEGER CNTRL CHAR='^' %CONSTINTEGER CONSOLE=0, MIN=1, SIN=2, MOUT=1, DUMP=2 %OWNINTEGER CH !! The lexical level at which to declare a TAG %OWNINTEGER LEVEL !! useful constants %CONSTINTEGER NULL=0, ONE=256, FLATTENED=1, NO EXPAND=1 %CONSTINTEGER DISCARD=-1, EXPANDIT=16_8000 %CONSTINTEGER PULLED UP=16_2000, WIRED=16_8000, COUNTED=16_1000 %CONSTINTEGER YES=0, NO=-1 %CONSTINTEGER MORE THAN ONE=2, MULTIREF=16_4000 !! machine dependent constants %CONSTINTEGER CPW=4; !! characters per word %CONSTINTEGER BPW=32; !! bits per word %CONSTINTEGER LCPW=2; !! log characters per word %CONSTINTEGER AUPW=4; !! addressing units per word %CONSTINTEGER LAUPW=2; !! log addressing units per word %ROUTINE CLAIM(%INTEGER NWORDS) !! claim NWORDS words of the workspace (stack). TOS=(TOS>>LAUPW)+NWORDS %IF TOS>STACKTOP %START SELECTOUTPUT(CONSOLE) PRINTSTRING("Workspace full."); NEWLINE %STOP %FINISH MAXTOS=TOS %IF TOS>MAXTOS TOS=TOS<OUT %FINISH START: %CYCLE READSYMBOL(CH) %REPEAT %UNTIL CH#NL %IF CH=CNTRL CHAR %START READSYMBOL(CH) CH=CH+CNTRL %FINISH %IF CH=CNTRL+'K' %START !! read an I-code comment and add to the list of comments COMMENT_NEXT==RECORD(TOS) CLAIM(COMMENTLEN) COMMENT==COMMENT_NEXT COMMENT_NEXT==RECORD(NULL) !! Read the comment text (self defining string) READ(L); RCH comment_text = "" %FOR I=1,1,L %CYCLE RCH comment_text = comment_text.tostring(ch) %REPEAT !! Claim space for the string CLAIM((L+CPW)>>LCPW) ->START %FINISH OUT: %END %ROUTINE ZERO(%INTEGER NWORDS) !! ZERO the top NWORDS of the stack %INTEGER I, P P=TOS %FOR I=1,1,NWORDS %CYCLE INTEGER(P)=0 P=P+AUPW %REPEAT %END !! hashtable for cross referencing UNIT names, NET names, PIN names, etc. %CONSTINTEGER HASHTABLE LEN=127; !! must be 2**N-1 %OWNINTEGERARRAY HASHTABLE(0:HASHTABLE LEN)=NULL(*) !DIAGNOSTICS !%ROUTINE PHEX(%INTEGER H) ! ! routine to print a hexadecimal number ! %INTEGER I,C ! %FOR I=1,1,(BPW>>2) %CYCLE ! C=H>>(BPW-4) ! %IF C<10 %THEN C=C+'0' %ELSE C=C+'A'-10 ! PRINTSYMBOL(C) ! H=H<<4 ! %REPEAT !%END ! !%ROUTINE DUMP STACK ! ! dump the stack in hex format, 8-words to the line ! ! followed by character equivalent ! %INTEGER P, I, Q, L ! %CONSTINTEGER MASK=(AUPW<<3)-1 ! ! %OWNBYTEINTEGERARRAY ASCII(0:127)='.'(32), ! '.', '!','"','#','$','%','&', '''', '(', ! ')', '*', '+', ',', '-', '.', '/', '0','1', ! '2','3','4','5','6','7','8','9', ':', ';', ! '<', '=', '>', '?', '@','A','B','C','D', ! 'E','F','G','H','I','J','K','L','M','N','O', ! 'P','Q','R','S','T','U','V','W','X','Y','Z', ! '[', '^', ']', '^', '.', '.','a','b','c', ! 'd','e','f','g','h','i','j','k','l','m','n', ! 'o','p','q','r','s','t','u','v','w','x','y', ! 'z', '.'(5) ! ! SELECTOUTPUT(DUMP) ! L=0 ! %FOR I=0,1,HASHTABLELEN %CYCLE ! %IF HASHTABLE(I)#NULL %START ! L=L+1 ! NEWLINE %AND L=0 %IF L&7=0 ! WRITE(I,3);PRINTSYMBOL(':') ! PHEX(HASHTABLE(I)) ! %FINISH ! %REPEAT ! NEWLINE ! P=ADDR(STACK(0)); Q=P ! ! print the address ! PHEX(P); PRINTSYMBOL(':') ! %WHILE P#TOS %CYCLE ! %IF P&MASK=0 %START ! SPACES(4) ! ! output character equivalent of line ! L=(((P-Q)>>LAUPW)<>LAUPW %FOR I=0,1,HASHTABLELEN %CYCLE H==HASHTABLE(I) %WHILE H#NULL %CYCLE TAG==RECORD(H) %IF (H>>LAUPW)>STACKTOP %START H=TAG_HNEXT %ELSE H==TAG_HNEXT %FINISH %REPEAT %REPEAT TOS=STACKTOP<0 %START !! Not a null string HASH=LEN+MODIFIER S==STRING(TOS); s = "" %FOR I=1,1,LEN %CYCLE RCH s = s.tostring(ch) HASH=HASH+CH %REPEAT %FINISH !! No TAG if name is null or to be thrown away. %RESULT==RECORD(NULL) %IF LEN=0 %OR MODIFIER=DISCARD !! Lookup the name H==HASHTABLE(HASH & HASHTABLE LEN) %WHILE H#NULL %CYCLE !! for each name that hashes to the same entry OLD==RECORD(H) %RESULT==OLD %IF OLD_NAME=S %AND OLD_MODIFIER=MODIFIER H==OLD_HNEXT %REPEAT !! name not found, so enter into table and create a new TAG. CLAIM((LEN+CPW)>>LCPW); !! Space for string NEW==RECORD(TOS); !! new TAG H=TOS ZERO(TAGLEN) CLAIM(TAGLEN) NEW_NAME==S NEW_MODIFIER=MODIFIER %RESULT==NEW %END %RECORD(FTAGDEF)%MAP REF OR DEF(%RECORD(FTAG)%NAME TAG, %INTEGER TYPE) %RECORD(FTAGDEF)%NAME TAGDEF, PTAGDEF %INTEGER L !! Create a TAG DEFinition for the given TAG, or reference the !! existing definition as appropriate. TAGDEF_LEVEL is set to !! (lexical level)<<8+(no of UNITs of same name currently in scope) !! so that UNIQUE TAG can create unique names for NETs. PTAGDEF==TAG_TAGDEF; ! Existing TAGDEF %IF PTAGDEF==RECORD(NULL) %OR (TYPE#0 %AND %C (PTAGDEF_LEVEL0 %CYCLE READ(Q) %FOR I=1,1,4 NQ=NQ-1 %REPEAT RCH %REPEAT %RESULT==UNIT %END !! length and max length of an I-code line (for output) %OWNINTEGER LINECT=0 %CONSTINTEGER LINELEN=60 %ROUTINE PCH(%INTEGER CHAR) !! put a character to the I-code. Take care of control !! characters and increment the current line length. %IF CHAR>=CNTRL %START PRINTSYMBOL(CNTRL CHAR) LINECT=LINECT+1 %FINISH PRINTSYMBOL(CHAR&16_7F) LINECT=LINECT+1 %END %ROUTINE SEPARATE !! put out a newline to the I-code if required NEWLINE %AND LINECT=0 %IF LINECT>=LINELEN %END %ROUTINE PDEC(%INTEGER N) !! put a decimal number to the I-code WRITE(N,0) LINECT=LINECT+2; ! approximate length SEPARATE %END %ROUTINE BLANK PCH(' ') %END %ROUTINE PUT STR(%STRING(255)%NAME S) %INTEGER L !! print out a string in I-code format %IF S==STRING(NULL) %THEN L=0 %ELSE L=LENGTH(S) WRITE(L,0); PCH(':') PRINTSTRING(S) %UNLESS L=0 LINECT=LINECT+L+2 SEPARATE %END %ROUTINE PUT TAG(%RECORD(FTAG)%NAME T) %STRING(255)%NAME NAME !! print out a TAG's name in I-code format %IF T==RECORD(NULL) %THEN NAME==STRING(NULL) %ELSE NAME==T_NAME PUT STR(NAME) %END %ROUTINE PUT HEAD(%RECORD(FHEAD)%NAME H) !! output a UNIT or instance header in I-code format %INTEGER I %RECORD(FTERMINAL)%NAME T %RECORD(FTAG)%NAME P %RECORD(FCOMMENT)%NAME COMMENT !! Output the chain of comments, if any COMMENT==H_COMMENTS %WHILE %NOT COMMENT==RECORD(NULL) %CYCLE PCH(CNTRL+'K') PUT STR(COMMENT_TEXT) COMMENT==COMMENT_NEXT %REPEAT PCH(CNTRL+'H') PDEC(H_OPTIONS); BLANK PDEC(H_NIN); BLANK PDEC(H_NOUT); BLANK PDEC(H_NIO); BLANK PDEC(H_NT); BLANK PUT TAG(H_UNAME); PUT TAG(H_NAME) !! output the TERMINALs %FOR I=1,1,H_NT %CYCLE T==H_T(I) PCH(CNTRL+'T') PDEC(T_INFO&16_3FFF); BLANK PUT TAG(T_PIN); PUT TAG(T_NAME) %REPEAT !! output the parameters, if present %FOR I=1,1,MAX PARMS %CYCLE P==H_PARM(I) %UNLESS P==RECORD(NULL) %START PCH(CNTRL+'P') PDEC(I); BLANK PUT TAG(P) %FINISH %REPEAT PCH(CNTRL+'G'); !! End of header %END %INTEGERFN COUNT SUBINSTS(%RECORD(FUNIT)%NAME UNIT) %RECORD(FHEAD)%NAME H %RECORD(FUNIT)%NAME DEFN %INTEGER N, I !! Count the number of subinstances to be output (recursive). !! Set flags in each INSTANCE (not UNIT header) to say !! (a) whether to expand this instance, and !! (b) whether it instances a UNIT which is multiply instanced N=0; ! offset of first subinstance within the UNIT %FOR I=1,1,UNIT_NSUBS %CYCLE H==UNIT_H(I) H_OFFSET=N DEFN==H_TAGDEF_UNIT %IF %NOT DEFN==RECORD(NULL) %AND %C H_OPTIONS & NO EXPAND = 0 %AND %C DEFN_NSUBS>0 %AND DEFN_H(0)_OPTIONS & NO EXPAND = 0 %START !! expand this instance H_FLAGS=H_FLAGS!EXPANDIT %IF H_TAGDEF_REFS>1 %START !! multiply instanced H_FLAGS=H_FLAGS!MULTIREF %FINISH !! to recurse is divine N=N+COUNT SUBINSTS(DEFN) %ELSE !! instance not expanded - offset incremented by 1. N=N+1 %FINISH %REPEAT %RESULT=N %END %ROUTINE EXPAND(%RECORD(FHEAD)%NAME H) %RECORD(FUNIT)%NAME DEFN %INTEGER I %STRING(255) COMMENT !! output headers for all lowest level subinstances. %IF H_FLAGS&EXPANDIT=0 %START !! No expansion of this instance, so output its header PUT HEAD(H) %ELSE H_TAGDEF_REFS=0; H_FLAGS=H_FLAGS&16_E000 DEFN==H_TAGDEF_UNIT !! output a comment to say where come from COMMENT=H_NAME_NAME PCH(CNTRL+'K'); PUT STR(COMMENT) !! output headers for all lowest level subinstances !! of this subinstance's definition. %FOR I=1,1,DEFN_NSUBS %CYCLE EXPAND(DEFN_H(I)) %REPEAT COMMENT="End of ".COMMENT PCH(CNTRL+'K'); PUT STR(COMMENT) %FINISH %END %ROUTINE SET REFERENCE COUNTS(%RECORD(FUNIT)%NAME UNIT) %INTEGER I %INTEGERNAME REFS, FLAGS %RECORD(FHEAD)%NAME H FLAGS==UNIT_H(0)_FLAGS %FOR I=1,1,UNIT_NSUBS %CYCLE H==UNIT_H(I) %IF H_FLAGS&EXPANDIT#0 %START REFS==H_TAGDEF_REFS REFS=REFS+1 !! only remember count if not been down this path before H_FLAGS=H_FLAGS!REFS %IF FLAGS&COUNTED=0 SET REFERENCE COUNTS(H_TAGDEF_UNIT) %FINISH %REPEAT FLAGS=FLAGS!COUNTED; !! delat with this subtree %END %PREDICATE SAME(%RECORD(FNET)%NAME FRAG1, FRAG2) !! decide whether FRAG1 and FRAG2 belong to the same NET %RECORD(FNET)%NAME N N==FRAG1 %CYCLE %TRUE %IF N==FRAG2 N==N_SAME %REPEAT %UNTIL N==FRAG1 %FALSE %END %ROUTINE PULL UP WIRES(%RECORD(FUNIT)%NAME UNIT) %RECORD(FHEAD)%NAME H, DH %RECORD(FUNIT)%NAME DEFN %RECORD(FTERMINAL)%NAME TJ, T, TTNO %RECORD(FNET)%NAME JNET, NET %INTEGER SUBNO, TNO, J, NT !! this routine merges nets that become joined by a WIRE !! or an input-output (equivalently) in a lower level !! UNIT. WIREs are in effect "pulled up". The routine is !! recursive and pulls up WIREs from the lowest levels first. %ROUTINE MERGE NETS(%RECORD(FNET)%NAME A, B) %RECORD(FNET)%NAME N, P !! Remove B from this list and add it to the side-chain of A. !! Each terminal referenced by B is made to point at A !! find B N==UNIT_NETS %WHILE %NOT N==RECORD(NULL) %CYCLE %EXIT %IF N==B P==N N==N_NEXT %REPEAT %RETURN %IF N==RECORD(NULL); !! not found !! unchain B %IF B==UNIT_NETS %THEN UNIT_NETS==B_NEXT %ELSE P_NEXT==B_NEXT B_NEXT==RECORD(NULL) !! get to end of side-chain of A N==A %CYCLE %EXIT %IF N_SAME==A N==N_SAME %REPEAT !! make end of A-chain point at B N_SAME==B !! get to end of B %CYCLE %EXIT %IF B_SAME==N_SAME; !! end of B B==B_SAME %REPEAT !! B points at end of list B_SAME==A %END !! start of PULL UP WIRES %FOR SUBNO=1,1,UNIT_NSUBS %CYCLE H==UNIT_H(SUBNO) %IF H_FLAGS&EXPANDIT#0 %START DEFN==H_TAGDEF_UNIT DH==DEFN_H(0) %IF DH_FLAGS&PULLED UP=0 %START PULL UP WIRES(DEFN) DH_FLAGS=DH_FLAGS!PULLED UP %FINISH %FINISH %REPEAT !! merge all nets that have been WIREd together from below %FOR SUBNO=1,1,UNIT_NSUBS %CYCLE H==UNIT_H(SUBNO) %CONTINUE %IF H_FLAGS&EXPANDIT=0 DEFN==H_TAGDEF_UNIT; !! definition of subunit DH==DEFN_H(0) NT=DH_NIN+DH_NOUT %FOR J=1,1,NT %CYCLE JNET==H_T(J)_NET %CONTINUE %IF JNET==RECORD(NULL) TJ==DH_T(J) %FOR TNO=J+1,1,NT %CYCLE TTNO==H_T(TNO) NET==TTNO_NET !! Ignore ? terminals %CONTINUE %IF NET==RECORD(NULL) %OR SAME(NET,JNET) T==DH_T(TNO) %CONTINUE %UNLESS SAME(T_NET,TJ_NET) !! two nets NET and JNET must be merged T_INFO=T_INFO!WIRED TTNO_INFO=TTNO_INFO!WIRED MERGE NETS(JNET,NET) %REPEAT %REPEAT %REPEAT %END %RECORD(FHEAD)%NAME %ARRAY INST STACK(0:25) %RECORD(FTAG)%MAP UNIQUE TAG(%RECORD(FTAG)%NAME TAG) %RECORD(FTAGDEF)%NAME TAGDEF %RECORD(FHEAD)%NAME INSTANCE %STRING(255)%NAME S %INTEGER COPY, L !! generate an unambiguous prefix for a NET name. Use the name !! of the enclosing UNIT, modified in 2 ways !! (1) add #n if this is the nth UNIT in scope with the same name. !! (2) add [m] if this is the mth instance of the UNIT. S==STRING(TOS); S="" %FOR L=LEVEL,-1,1 %CYCLE INSTANCE==INST STACK(L) TAGDEF==INSTANCE_TAGDEF COPY=TAGDEF_LEVEL&16_FF %CONTINUE %IF L0 %THEN S="#".ITOS(COPY+1,0).S S=INSTANCE_NAME_NAME.S %REPEAT S=S.TAG_NAME CLAIM((LENGTH(S)+CPW)>>LCPW) TAG==RECORD(TOS) ZERO(TAGLEN); CLAIM(TAGLEN) TAG_NAME==S %RESULT==TAG %END !! head of list of Global nets (names begin with '.') %RECORD(FNET)%NAME GLOBALS !! current NET used as workspace, or to store part of a Global NET. %RECORD(FNET)%NAME OUT, WORK %OWNINTEGER GLOBAL NET %ROUTINE ADD FANELS(%RECORD(FNET)%NAME NET, %C %RECORD(FUNIT)%NAME UNIT, %INTEGER OFFSET) %RECORD(FFANEL)%NAME F, O %RECORD(FHEAD)%NAME H %RECORD(FUNIT)%NAME DEFN %INTEGER I !! build up a connection list. Elements of the form [0,n] !! are ignored and handled specially by OUTPUT NETS. !! Elements of the form [m,n] refer to subinstance m of UNIT. !! OFFSET is used to relocate subinstance numbers as we traverse the tree. %FOR I=1,1,NET_FAN %CYCLE F==NET_F(I) %CONTINUE %IF F_SUBNO=0 !! Connection is to a subinstance H==UNIT_H(F_SUBNO) %IF H_FLAGS&EXPANDIT#0 %START !! subinstance must be expanded DEFN==H_TAGDEF_UNIT !! locate the NET referred to by this connection by !! using the appropriate TERMINAL of the UNIT being instanced. ADD FANELS(DEFN_H(0)_T(F_TNO)_NET,DEFN,OFFSET+H_OFFSET) %ELSE WORK_FAN=WORK_FAN+1 !! no expansion of the referenced subinstance so add !! the connection to the NET, relocating subinstance !! numbers as we go. O==WORK_F(WORK_FAN) O_SUBNO=H_OFFSET+OFFSET+1 O_TNO=F_TNO CLAIM(FANLEN) %FINISH %REPEAT %END %ROUTINE PUT FAN(%RECORD(FNET)%NAME NET) !! outut the list of connections from a NET. %INTEGER I %RECORD(FFANEL)%NAME F %FOR I=1,1,NET_FAN %CYCLE F==NET_F(I) BLANK; PDEC(F_SUBNO) BLANK; PDEC(F_TNO) %REPEAT %END %ROUTINE EXPAND NET(%RECORD(FNET)%NAME NET, %RECORD(FUNIT)%NAME UNIT, %RECORD(FHEAD)%NAME INSTANCE, %INTEGER OFFSET) %RECORD(FNET)%NAME N %RECORD(FTAG)%NAME TAG %RECORD(FUNIT)%NAME DEFN %RECORD(FHEAD)%NAME H %RECORD(FFANEL)%NAME F %INTEGER FAN, OLDTOS INST STACK(LEVEL)==INSTANCE !! first expand the directly reachable portion N==NET; OLDTOS=TOS %CYCLE !! for each fragment of the net WORK==RECORD(TOS); ZERO(NETLEN) !! see if net already processed from level above %FOR FAN=1,1,N_FAN %CYCLE F==N_F(FAN) %IF F_SUBNO=0 %START !! ref to containing UNIT %IF LEVEL>0 %START !! not most global level ->NEXT FRAGMENT %UNLESS INSTANCE_T(F_TNO)_NET==RECORD(NULL) %ELSE !! add connection to net WORK_FAN=WORK_FAN+1 WORK_F(WORK_FAN)=F %FINISH %FINISH %REPEAT CLAIM(NETLEN+OUT_FAN*FANLEN) WORK_SAME==OUT_SAME; OUT_SAME==WORK ADD FANELS(N,UNIT,OFFSET) TAG==N_NAME %IF CHARNO(TAG_NAME,1)='.' %START GLOBAL NET=YES %ELSE TAG==UNIQUE TAG(TAG) %IF LEVEL>0 %FINISH WORK_NAME==TAG N_FAN=0-N_FAN; !! mark as needing attention NEXT FRAGMENT: N==N_SAME %REPEAT %UNTIL N==NET !! delete whole net unless it contains more than one !! terminal reference, is at the outermost level, or !! is a fragment of a global net. TOS=OLDTOS %UNLESS WORK_FAN>1 %OR LEVEL=0 %OR GLOBAL NET=YES !! expand indirectly reachable parts of net LEVEL=LEVEL+1 %CYCLE ->DONE %IF N_FAN>=0 N_FAN=0-N_FAN %FOR FAN=1,1,N_FAN %CYCLE F==N_F(FAN) %CONTINUE %IF F_SUBNO=0 INSTANCE==UNIT_H(F_SUBNO) %CONTINUE %IF INSTANCE_FLAGS&EXPANDIT=0 !! ignore if ref to terminal is WIRED at higher level %CONTINUE %IF INSTANCE_T(F_TNO)_INFO&WIRED#0 DEFN==INSTANCE_TAGDEF_UNIT H==DEFN_H(0) EXPAND NET(H_T(F_TNO)_NET,DEFN,INSTANCE,OFFSET+INSTANCE_OFFSET) %REPEAT DONE: N==N_SAME %REPEAT %UNTIL N==NET LEVEL=LEVEL-1 %END %ROUTINE MERGE GLOBALS(%RECORD(FNET)%NAME NET) %RECORD(FNET)%NAME N, G, GLOBAL, P %RECORD(FTAG)%NAME TAG !! merge all global nets referred to from NET !! add NET to list of globals (or to merged NET) %ROUTINE SIDECHAIN(%RECORD(FNET)%NAME NET, TO) %RECORD(FNET)%NAME N %RECORD(FTAG)%NAME TAG !! sidechain the net NET to the net TO N==NET %CYCLE TAG==N_NAME TAG_MODIFIER=ADDR(TO) %IF CHARNO(TAG_NAME,1)='.' %EXIT %IF N_SAME==NET N==N_SAME %REPEAT !! N points at end of NET N_SAME==TO_SAME; TO_SAME==NET %END !! start of merge globals N==NET; GLOBAL==RECORD(NULL) %CYCLE !! for each net fragment TAG==N_NAME N==N_SAME -> repeat %UNLESS CHARNO(TAG_NAME,1)='.' !! got a global fragment G==RECORD(TAG_MODIFIER) TAG_MODIFIER=ADDR(NET) %AND %CONTINUE %IF G==RECORD(NULL) !! got some fragments to merge? %IF GLOBAL==RECORD(NULL) %START GLOBAL==G %FINISH %ELSE %IF %NOT G==GLOBAL %START !! fragments to merge !! first unhook G %IF GLOBALS==G %START GLOBALS==G_NEXT %ELSE P==GLOBALS_NEXT P==P_NEXT %WHILE %NOT P_NEXT==G !! P is predecessor of G P_NEXT==G_NEXT %FINISH G_NEXT==RECORD(NULL) SIDECHAIN(G,GLOBAL) %FINISH repeat: %REPEAT %UNTIL N==NET !! now add NET to GLOBALS %IF GLOBAL==RECORD(NULL) %START NET_NEXT==GLOBALS GLOBALS==NET %ELSE SIDECHAIN(NET,GLOBAL) %FINISH %END %ROUTINE OUTPUT NETS(%RECORD(FHEAD)%NAME INSTANCE, %INTEGER OFFSET, %RECORD(FUNIT)%NAME UNIT) %RECORD(FNET)%NAME NET, N %INTEGER SUBNO, OLDTOS NET==UNIT_NETS %WHILE %NOT NET==RECORD(NULL) %CYCLE GLOBAL NET=NO OLDTOS=TOS OUT==RECORD(TOS); ZERO(NETLEN) EXPAND NET(NET,UNIT,INSTANCE,OFFSET) NET==NET_NEXT %CONTINUE %IF TOS=OLDTOS; !! no net built %IF GLOBAL NET=YES %START MERGE GLOBALS(OUT) %ELSE PCH(CNTRL+'N') N==OUT %CYCLE PCH(CNTRL+'A') PUT TAG(N_NAME) PDEC(N_FAN) PUT FAN(N) N==N_SAME %REPEAT %UNTIL N==OUT TOS=OLDTOS; !! reclaim store used for OUT %FINISH %REPEAT LEVEL=LEVEL+1 %FOR SUBNO=1,1,UNIT_NSUBS %CYCLE INSTANCE==UNIT_H(SUBNO) %CONTINUE %IF INSTANCE_FLAGS&EXPANDIT=0 OUTPUT NETS(INSTANCE,OFFSET+INSTANCE_OFFSET,INSTANCE_TAGDEF_UNIT) %REPEAT LEVEL=LEVEL-1 %END %ROUTINE PUT GLOBAL NETS(%RECORD(FNET)%NAME HEAD) !! output the list of global NETs. %RECORD(FNET)%NAME NET, N %RECORD(FTAG)%NAME NETNAME %INTEGER FAN %WHILE %NOT HEAD==RECORD(NULL) %CYCLE !! for each differently named NET PCH(CNTRL+'N') NET==HEAD %CYCLE PCH(CNTRL+'A') NETNAME==NET_NAME PUT TAG(NETNAME) NETNAME_MODIFIER=NULL FAN=0; N==NET !! count the fanout %CYCLE FAN=FAN+N_FAN %IF N_NAME==NETNAME N==N_SAME %REPEAT %UNTIL N==NET PDEC(FAN) %CYCLE PUT FAN(N) %AND N_NAME==RECORD(NULL) %IF N_NAME==NETNAME N==N_SAME %REPEAT %UNTIL N==NET %WHILE NET_NAME==RECORD(NULL) %CYCLE NET==NET_SAME %EXIT %IF NET==HEAD %REPEAT %REPEAT %UNTIL NET==HEAD HEAD==HEAD_NEXT %REPEAT %END !!********************************************** !!* MAINLINE * !!********************************************** %RECORD(FHEAD)%NAME H %RECORD(FUNIT)%NAME PUNITS, UNIT %INTEGER FLAGS, I, OLDTOS RETURN CODE=DEF STREAMS(CLIPARAM,DEFAULTS) ->DONE %UNLESS RETURN CODE=1 !! Initialisation SELECTOUTPUT(CONSOLE) PRINTSTRING(VERSION); NEWLINE SELECTINPUT(MIN); SELECTOUTPUT(MOUT) TOS=ADDR(STACK(0)); STACKTOP=(ADDR(STACK(STACKLEN)))>>LAUPW MAXTOS=TOS>>LAUPW LEVEL=ONE; PUNITS==RECORD(NULL) COMMENTS_NEXT==RECORD(NULL); COMMENT==COMMENTS !! read the UNITs to be expanded from stream MIN %CYCLE RCH READ(FLAGS) %AND RCH %IF CH=CNTRL+'S' %EXIT %UNLESS CH=CNTRL+'U' !! chain most global UNITs together %IF PUNITS==RECORD(NULL) %START PUNITS==READ A UNIT UNIT==PUNITS %ELSE UNIT_NEXT==READ A UNIT UNIT==UNIT_NEXT %FINISH %REPEAT UNIT_NEXT==RECORD(NULL) FAIL(MIN) %UNLESS CH=END OF FILE !! Read library input from stream SIN - only keep referenced UNITs. SELECTINPUT(SIN) %CYCLE RCH READ(FLAGS) %AND RCH %IF CH=CNTRL+'S' %EXIT %UNLESS CH=CNTRL+'U' !! throw away the UNIT (by resetting TOS) if it isn't referenced. OLDTOS=TOS UNIT==READ A UNIT CLEANUP(OLDTOS) %UNLESS UNIT_H(0)_TAGDEF_REFS>0 %REPEAT FAIL(SIN) %UNLESS CH=END OF FILE !! output the flattened description PCH(CNTRL+'S'); PDEC(FLATTENED) UNIT==PUNITS OLDTOS=TOS; LEVEL=0 %CYCLE %EXIT %IF UNIT==RECORD(NULL) H==UNIT_H(0) !! for each most global UNIT that isn't instanced %IF H_OPTIONS & NO EXPAND = 0 %AND H_TAGDEF_REFS=0 %START !! output the UNIT header PCH(CNTRL+'U'); PDEC(UNIT_TYPE) PUT HEAD(H) !! count and output the number of subinstances. !! COUNT SUBINSTS sets expansion and reference flags !! and calculates subinstance offsets. PCH(CNTRL+'J') PDEC(COUNT SUBINSTS(UNIT)) !! expand each subinstance %FOR I=1,1,UNIT_NSUBS %CYCLE EXPAND(UNIT_H(I)) %REPEAT !! Initialisation for OUTPUT NETS SET REFERENCE COUNTS(UNIT) GLOBALS==RECORD(NULL) OUT==RECORD(TOS) ZERO(NETLEN) PULL UP WIRES(UNIT) LEVEL=0 OUTPUT NETS(RECORD(NULL),0,UNIT) !! output the Global NETs, if present PUT GLOBAL NETS(GLOBALS) !! Reclaim the workspace used for Global NETs TOS=OLDTOS PCH(CNTRL+'E') %FINISH UNIT==UNIT_NEXT %REPEAT NEWLINE SELECTOUTPUT(CONSOLE) MAXTOS = MAXTOS - ADDR(STACK(0)) >> LAUPW PRINTSTRING("Used "); WRITE(MAXTOS,0) PRINTSTRING(" out of "); WRITE(STACKLEN,0) PRINTSTRING(" words."); newline DONE: %ENDOFPROGRAM