├── Documentation └── Layout_generator.pdf ├── Execution ├── EulerTrail.cpp ├── analog.sp ├── analog.sp.spice_final ├── fn.sp ├── fn.sp.spice_final ├── inputs.txt └── read_spice.sh ├── Images ├── ALIGN_layout.png ├── Screenshot 2020-08-11 at 4.35.09 PM.png ├── Untitled presentation-2.jpg ├── ckt_layout.png ├── ckt_layout1.png ├── ckt_layout2.png ├── ckt_layout3.png ├── ckt_layout4.png ├── compare.png ├── compare1.png ├── flow_with_PDK.png ├── ota.png ├── output1.png ├── output2.png └── tool_flow-2.jpg ├── Inputs ├── analog.sp └── fn.sp ├── Intermediate_Outputs ├── sample_output.png ├── sample_output1.jpg ├── spice_final_analog.sp └── spice_intermediate.sp ├── LICENSE.md ├── README.md ├── References └── reference_links └── Source_Code ├── EulerPath.cpp ├── EulerTrail.cpp ├── inputs.txt ├── read_spice.sh └── read_spice1.tcl /Documentation/Layout_generator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Documentation/Layout_generator.pdf -------------------------------------------------------------------------------- /Execution/EulerTrail.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // EulerTrail 4 | // 5 | // Created by Sethupathi on 31/07/20. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | class Graph 15 | { 16 | private: 17 | int V; 18 | list *adj; 19 | 20 | public: 21 | Graph(int V){ 22 | this->V= V; 23 | adj = new list[V]; 24 | } 25 | 26 | ~Graph() 27 | { 28 | delete [] adj; 29 | 30 | } 31 | 32 | void addEdge(int u, int v) 33 | { 34 | adj[u]. push_back(v); 35 | adj[v]. push_back(u); 36 | } 37 | 38 | void removeEdge(int u, int v); 39 | 40 | void printEulerTour(); 41 | void printEulerUtil(int s); 42 | 43 | //counting the no. of vertices reachable from v 44 | int DFSCount(int v, bool visited[]); 45 | 46 | //function to check if edge u-v is a valid next edge in euler trail 47 | bool isValidNextEdge(int u, int v); 48 | }; 49 | 50 | 51 | void Graph::printEulerTour() 52 | { 53 | int u=0; 54 | for (int i=0;i:: iterator i; 70 | for(i=adj[u].begin();i!=adj[u].end();++i) 71 | { 72 | int v = *i; 73 | 74 | 75 | if(v!= -1 && isValidNextEdge(u, v)) 76 | { 77 | cout<::iterator i; 94 | 95 | for (i=adj[u].begin();i!=adj[u].end();++i) 96 | { 97 | if(*i!=-1) 98 | count++; 99 | } 100 | 101 | if (count ==1) 102 | return true; 103 | 104 | 105 | // 2) If there are multiple adjacents, then u-v is not a bridge 106 | // Do following steps to check if u-v is a bridge 107 | 108 | // 2.a) count of vertices reachable from u 109 | 110 | bool visited[V]; 111 | memset(visited,false,V); 112 | int count1 = DFSCount(u, visited); 113 | 114 | // 2.b) Remove edge (u, v) and after removing the edge, count 115 | // vertices reachable from u 116 | 117 | removeEdge(u, v); 118 | memset(visited, false, V); 119 | 120 | int count2 = DFSCount(u, visited); 121 | // 2.c) Add the edge back to the graph 122 | addEdge(u, v); 123 | 124 | // 2.d) If count1 is greater, then edge (u, v) is a bridge 125 | return (count1 > count2)? false: true; 126 | } 127 | 128 | 129 | 130 | void Graph:: removeEdge(int u, int v) 131 | { 132 | 133 | // Find v in adjacency list of u and replace it with -1 134 | list::iterator iv = find(adj[u].begin(),adj[u].end(),v); 135 | *iv =-1; 136 | 137 | list::iterator iu = find(adj[v].begin(),adj[v].end(),u); 138 | *iu =-1; 139 | 140 | } 141 | 142 | 143 | // A DFS based function to count reachable vertices from v 144 | int Graph::DFSCount(int v, bool visited[]) 145 | { 146 | visited[v] = true; 147 | int count=1; 148 | 149 | list:: iterator i; 150 | for(i=adj[v].begin();i!=adj[v].end();++i) 151 | { 152 | if(*i!=-1 && !visited[*i]) 153 | count += DFSCount(*i, visited); 154 | } 155 | 156 | return count; 157 | } 158 | 159 | 160 | // Driver program to test above function 161 | int main() 162 | { 163 | 164 | 165 | int e,node1,node2; 166 | //cout<<"Enter no. of edges"; 167 | cin>>e; 168 | 169 | Graph g1(e); 170 | 171 | for(int i=0;i>node1>>node2; 174 | g1.addEdge(node1,node2); 175 | } 176 | 177 | g1.printEulerTour(); 178 | 179 | 180 | // Let us first create and test graphs shown in above figure 181 | // Graph g1(4); 182 | // g1.addEdge(0, 1); 183 | // g1.addEdge(0, 2); 184 | // g1.addEdge(1, 2); 185 | // g1.addEdge(2, 3); 186 | // g1.printEulerTour(); 187 | // 188 | // Graph g2(3); 189 | // g2.addEdge(0, 1); 190 | // g2.addEdge(1, 2); 191 | // g2.addEdge(0, 2); 192 | // g2.printEulerTour(); 193 | // 194 | // Graph g3(4); 195 | // g3.addEdge(0, 1); 196 | // g3.addEdge(1, 2); 197 | // g3.addEdge(2, 3); 198 | // g3.addEdge(3, 0); 199 | // 200 | // g3.printEulerTour(); 201 | // 202 | // //starts from '0' 203 | // 204 | // Graph g4(6); 205 | // g4.addEdge(1, 2); 206 | // g4.addEdge(2, 4); 207 | // g4.addEdge(4, 3); 208 | // g4.addEdge(3, 1); 209 | // g4.addEdge(4, 5); 210 | // g4.addEdge(5, 4); 211 | // g4.addEdge(7, 5); 212 | // g4.addEdge(7, 0); 213 | // g4.addEdge(6, 0); 214 | // g4.addEdge(6, 0); 215 | // g4.addEdge(5, 6); 216 | // g4.addEdge(5, 6); 217 | 218 | 219 | // g4.addEdge(0, 1); 220 | // g4.addEdge(0, 1); 221 | // g4.addEdge(1, 2); 222 | // g4.addEdge(1, 2); 223 | // g4.addEdge(2, 3); 224 | // g4.addEdge(3, 0); 225 | // 226 | // g4.printEulerTour(); 227 | // 228 | // Graph g5(5); 229 | // g5.addEdge(0, 1); 230 | // g5.addEdge(0, 1); 231 | // g5.addEdge(2, 3); 232 | // g5.addEdge(3, 4); 233 | // g5.addEdge(1, 2); 234 | // g5.printEulerTour(); 235 | // 236 | // 237 | // cout<<"Euler Path for the boolean function "< $file.tmp 16 | grep 'M\|D\|R' $file | grep -v '.include\|.end\|.dc\|.control\|.endc\|.model\|plot\|*\|run\|Vd_\|cload\|pulse\|Rl'> $file.tmp 17 | awk '{print $2,$4}' $file.tmp > $file.spice_final 18 | sed -i 's/vdd/1/g' $file.spice_final 19 | echo -e "Converted $file to $file.spice_final" 20 | rm -rf $file.tmp 21 | echo -e "\n" 22 | done 23 | echo -e "Converted all the spice files" 24 | -------------------------------------------------------------------------------- /Images/ALIGN_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ALIGN_layout.png -------------------------------------------------------------------------------- /Images/Screenshot 2020-08-11 at 4.35.09 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/Screenshot 2020-08-11 at 4.35.09 PM.png -------------------------------------------------------------------------------- /Images/Untitled presentation-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/Untitled presentation-2.jpg -------------------------------------------------------------------------------- /Images/ckt_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ckt_layout.png -------------------------------------------------------------------------------- /Images/ckt_layout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ckt_layout1.png -------------------------------------------------------------------------------- /Images/ckt_layout2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ckt_layout2.png -------------------------------------------------------------------------------- /Images/ckt_layout3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ckt_layout3.png -------------------------------------------------------------------------------- /Images/ckt_layout4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ckt_layout4.png -------------------------------------------------------------------------------- /Images/compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/compare.png -------------------------------------------------------------------------------- /Images/compare1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/compare1.png -------------------------------------------------------------------------------- /Images/flow_with_PDK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/flow_with_PDK.png -------------------------------------------------------------------------------- /Images/ota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/ota.png -------------------------------------------------------------------------------- /Images/output1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/output1.png -------------------------------------------------------------------------------- /Images/output2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/output2.png -------------------------------------------------------------------------------- /Images/tool_flow-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Images/tool_flow-2.jpg -------------------------------------------------------------------------------- /Inputs/analog.sp: -------------------------------------------------------------------------------- 1 | **Reference Spice File 2 | ***Pre Layout On- Off Current*** 3 | M1 3 2 1 1 pfet W=28u L=1u 4 | M2 2 2 1 1 pfet W=28u L=1u 5 | M3 5 2 1 1 pfet W=28u L=1u 6 | M4 6 4 3 1 pfet W=28u L=1u 7 | M5 4 4 2 1 pfet W=28u L=1u 8 | M6 vref 4 5 1 pfet W=28u L=1u 9 | M7 6 6 7 0 nfet W=14u L=1u 10 | M8 4 6 8 0 nfet W=14u L=1u 11 | M_pd 6 en_bar 0 0 nfet W=8U L=1U 12 | M_pass 4 en 4x 0 nfet W=2U L=2U 13 | Minv_p en_bar en 1 1 pfet W=3U L=1U 14 | Minv_n en_bar en 0 0 nfet W=2U L=1U 15 | 16 | M9 a 2 1 1 pfet W=12U L=1u 17 | M10 6 a 4x 1 pfet W=12U L=0.8U 18 | M11 a a b 0 nfet W=1U L=3u 19 | M12 b b c 0 nfet W=1U L=3u 20 | M13 c c 0 0 nfet W=1U L=3U 21 | D1 7 0 PNPDIODE 22 | R1 8 9 9K 23 | D2 9 0 PNPDIODE 8 24 | R2 vref 10 76K 25 | D3 10 0 PNPDIODE 8 26 | 27 | 28 | .model PNPDIODE D is=1e-18 n=1 29 | Vdd 1 0 dc 3.3V 30 | Vd_en en 0 pulse(0 3.3V 0 0 0 1s 2s) 31 | bv en_bar 0 V=V(1)-V(en) 32 | Rl vref 0 100MEG 33 | .tran 0.1s 2s 34 | .include osu018.lib 35 | 36 | .control 37 | run 38 | plot V(en) 39 | plot -I(Vdd) 40 | .endc 41 | .end 42 | -------------------------------------------------------------------------------- /Inputs/fn.sp: -------------------------------------------------------------------------------- 1 | 2 | ***Netlist description*** 3 | M1 3 a vdd vdd pmos W=1.53u L=0.18u 4 | M2 2 b vdd vdd pmos W=1.53u L=0.18u 5 | M3 4 d 2 2 pmos W=1.53u L=0.18u 6 | M4 4 c 3 3 pmos W=1.53u L=0.18u 7 | M5 out e 4 4 pmos W=1.53u L=0.18u 8 | M6 out f 4 4 pmos W=1.53u L=0.18u 9 | 10 | M7 out a 6 6 nmos W=1.53u L=0.18u 11 | M8 out c 6 6 nmos W=1.53u L=0.18u 12 | M9 out e 7 7 nmos W=1.53u L=0.18u 13 | M10 6 b 0 0 nmos W=1.53u L=0.18u 14 | M11 6 d 0 0 nmos W=1.53u L=0.18u 15 | M12 7 f 0 0 nmos W=1.53u L=0.18u 16 | 17 | cload out 0 10f 18 | 19 | Vdd vdd 0 2.5 20 | V1 a 0 0 pulse 0 2.5 0.1n 10p 10p 1n 2n 21 | V2 b 0 0 pulse 0 2.5 0.2n 10p 10p 1n 2n 22 | V3 c 0 0 pulse 0 2.5 0.3n 10p 10p 1n 2n 23 | V4 d 0 0 pulse 0 2.5 0.4n 10p 10p 1n 2n 24 | V5 e 0 0 pulse 0 2.5 0.5n 10p 10p 1n 2n 25 | V6 f 0 0 pulse 0 2.5 0.6n 10p 10p 1n 2n 26 | 27 | ***Simulation commands*** 28 | .op 29 | .tran 10p 4n 30 | 31 | *** .include model file *** 32 | .LIB "my_model_file.tech" CMOS_MODELS 33 | .end 34 | -------------------------------------------------------------------------------- /Intermediate_Outputs/sample_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Intermediate_Outputs/sample_output.png -------------------------------------------------------------------------------- /Intermediate_Outputs/sample_output1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethupathib/vsdGraphExtractor/a955f530a60585e625f7fa835c3e8b13558a6750/Intermediate_Outputs/sample_output1.jpg -------------------------------------------------------------------------------- /Intermediate_Outputs/spice_final_analog.sp: -------------------------------------------------------------------------------- 1 | 3 1 2 | 2 1 3 | 5 1 4 | 6 3 5 | 4 2 6 | vref 5 7 | 6 7 8 | 4 8 9 | 6 0 10 | 4 4x 11 | en_bar 1 12 | en_bar 0 13 | a 1 14 | 6 4x 15 | a b 16 | b c 17 | c 0 18 | 7 PNPDIODE 19 | 8 9K 20 | 9 PNPDIODE 21 | vref 76K 22 | 10 PNPDIODE 23 | vref 100MEG 24 | -------------------------------------------------------------------------------- /Intermediate_Outputs/spice_intermediate.sp: -------------------------------------------------------------------------------- 1 | 3 1 2 | 2 1 3 | 4 2 4 | 4 3 5 | out 4 6 | out 4 7 | out 6 8 | out 6 9 | out 7 10 | 6 0 11 | 6 0 12 | 7 0 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2020] [Sethupathi Balakrishnan] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Graph Extractor from Spice Netlist & Euler Path Generator 2 | 3 | This repository cointains source code that is aimed at converting a spice netlist (digital) to its corresponding layout. The layout is generated using the Open Source Tool named Magic. Here, **fn.sp** contains the spice netlist of the function **Y = ~[(B+D).(A+C)+(E.F)].** 4 | We're trying to automatically generate layout for this boolean fuction. This project is initially built for digital circuits and later to be scaled/modified for analog circuits. 5 | 6 | **The project is in a very early stage and might not yet be ready for productive use.** 7 | 8 | **Assumptions** -- The Design Engineer/Characterization Engineer has designed the circuit and performed the circuit simulations and has generated the spice netlist. A tutorial on how to perform circuit simulations using **ngspice** can be found [**here**](https://github.com/sherylcorina/avsdbgp_3v3). 9 | 10 | A very brief description of this project can be found [**here**](https://www.vlsisystemdesign.com/ip/). This work has been carried out by going through Mr. Kunal Ghosh's lecture on Custom Layout. You can have a look at the course [**here**](https://www.udemy.com/course/vlsi-academy-custom-layout/). 11 | 12 | ## Description 13 | 14 | 1. The first step is to extract the graph information (edges & nodes etc) from the spice netlist. (The ideal way to do this is to **tree-ify** the netlist. This is a very hard problem and it falls under Tech Mapping Problem.) 15 |

16 | 17 |

18 | 19 | 2. The second step is to generate the Euler Path (common to both PMOS & NMOS networks) from the graph for optimal placement of Poly's. 20 |

21 | 22 |

23 | 24 | 3. Next, we paint the layout using the stick diagram. 25 |

26 | 27 |

28 |

29 | 30 |

31 | 4. Final Step - DRC Check/Clean 32 |

33 | 34 |

35 | 36 | **Note** -- There are some pre-requisites to understand/reproduce this project. 37 | 38 | 1. Working Knowledge of Data Structures and Algorithms in any programming language. I have used C++ here. 39 | 2. Knowledge of Graphs. 40 | 3. Knowledge of grep/awk/sed & tcl. 41 | 42 | You can get proficient in programming by following the steps given [**here**](https://medium.com/@sethupathibalakrishnan/my-experiences-on-anything-and-everything-in-life-231eb4699609) in the Competitive Programming Section. A tutorial on finding the Euler Path can be found [**here**](https://www.youtube.com/watch?v=8MpoO2zA2l4&t=11s). 43 | The layouts are drawn using Magic Layout Tool. Installation instructions can be found [**here**](https://www.udemy.com/course/vsd-a-complete-guide-to-install-open-source-eda-tools/). 44 | ## Conventional Work Flow 45 | 46 |

47 | 48 |

49 | 50 | It takes enormous amount of time to calculate the positions of each component like distance between the poly, total height & width of the cell, spacing between the cells etc and draw the layout adhering to the DRC rules. 51 | This project is an attempt to reduce the **Engineering Effort** that is put in drawing the layout. 52 | 53 | ## Layout Generation Engine (An abstract flow diagram) 54 | 55 |

56 | 57 |

58 | 59 | A brief document on this layout_generator flow can be found [**here**](Documentation/Layout_generator.pdf). 60 | 61 | ## Euler Path Existence (pseudo-code) 62 | **Input : Graph containing vertices and edges**.
63 | **Output : Returns Euler Path Existence**.
64 | There are basically 4 methods. (Note: This is the pseudo code for Euler Path Existence and not for Euler Path Generation.) 65 | 66 | 1. addEdge 67 | 2. DFS 68 | 3. isConnected 69 | 4. isEulerean 70 | 71 | --------------------- 72 | ***0. Initialize graph 'adj' and no. of Vertices V***
73 | ***1. addEdge(node1, node2)***

74 | ***2. DFS(vertex, visited[])***
75 | ***3. visited[vertex] <- true*** //Mark the current node as visited
76 | ***4. list <*** ***int> :: iterator node*** //initialize iterator to adjacency list
77 | ***5. foreach (node in adj)***
78 | ***6. if(!visited[i])***
79 | ***7. DFS(i,visited)*** //recursive call

80 | ***8. isConnected()*** //method to check if the graph is connected
81 | ***9. foreach (i in V)***
82 | ***10. visited[i] <- false*** //mark all nodes as not visited
83 | ***11. foreach(i in V)*** //Find vertex with non-zero degree
84 | ***12. if (adj[i].size() != 0) break;***
85 | ***13. if(i==V) return true*** 86 | ***14. DFS(i,visited)***
87 | ***15. foreach(i in V)***
88 | ***16. if(visited[i]==false & adj[i].size>0) return false else return true***

89 | ***17. isEulerean()***
90 | ***18. if(isConnected==false) return 0***
91 | 92 | /* 0 --> If graph is not Eulerian
93 | 1 --> If graph has an Euler path (Semi-Eulerian)
94 | 2 --> If graph has an Euler Circuit (Eulerian) */

95 | ***19. foreach(i in V)*** 96 | ***20. if(adj[i].size() & 1) odd++*** 97 | ***21. if(odd>2) return 0 else return (odd)?1:2*** 98 | 99 | --------------------- 100 | ## Details 101 | 102 | Step 0 -- Clone the repository 103 | ``` 104 | $ git clone https://github.com/sethupathib/final_layout_generator.git 105 | ``` 106 | 107 | Step 1 -- Extract edges/node information from the spice netlist. 108 | The edges and node details are extracted from the Netlist using grep+awk+sed. (There is some work that is to be done here). 109 | The shell script to extract the node/edge details can be found [**here**](Source_Code/read_spice.sh). 110 | The extracted file has to be further processed to feed the algorithm to get the Euler Trail. (Work Pending) 111 | 112 | - Change directory to "Execution" 113 | ``` 114 | $ cd Execution 115 | ``` 116 | - Run the shell script. 117 | ``` 118 | $ source read_spice.sh 119 | ``` 120 | - You get the following ouput. 121 |

122 | 123 |

124 | 125 | - Input & Output File Comparison 126 |

127 | 128 |

129 | 130 | - This output file needs some more processing to be fed to the algorithm. (I'm currently work on this right now). Comparison of the obtained file and the file format that the algorithm requires --> 131 |

132 | 133 |

134 | 135 | 136 | 137 | Step 2 -- Feed the extracted file to the Euler Trail finding algorithm. Source code for the algorithm can be found [**here**](Source_Code/EulerTrail.cpp). 138 | The algorithm takes majorly 2 values. They are --> 139 | 1. Total number of edges. 140 | 2. The Edge List. 141 | 142 | Command to run the algorithm is as follows. 143 | ``` 144 | $ g++ EulerTrail.cpp -o EulerTrail 145 | ``` 146 | 147 | ``` 148 | $ ./EulerTrail < inputs.txt 149 | ``` 150 | 151 | You get the following output. This is the Euler Path for the combined graph 152 |

153 | 154 |

155 | 156 |

157 | 158 |

159 | 160 | 161 | ## Pending Work 162 | 163 | - The file conversion needs to be worked upon. 164 | - Graph Splitting. Need to go through this. It's very important. As of this moment, the algorithm only returns the Euler Path for the Combined (PMOS + NMOS) graph. 165 | - Layouting for the given PDK's seems to be a very hard task. 166 | - Routing the Layout. (This seems to an extremely difficult task mainly because it is done after placing the poly's in the design) 167 | - Pass Transistor's. Layouting for pass transistor logic is a bit complicated as it requires adding dummy nodes/edges and forming the graph. 168 | - Random Graph Generator (This is important for testing the algorithm) 169 | - To know more about bridges and articulation points in a graph. 170 | - I need to solve a lot of problems and get to know about some advanced concepts like Dynamic Programming and Memoization applied on graphs. 171 | - Running ALIGN on the reference designs and getting a functionally correct and DRC clean layout. 172 | 173 | ## Major Contributions by the Author 174 | - There were two major things that created a lot of impact. 175 | 1. Graph Extraction and Euler Path Generation. 176 | 2. Researching about various other projects on GitHub that does "Analog Layouting". That's where I found out about [**ALIGN**](https://github.com/ALIGN-analoglayout/ALIGN-public) and [**MAGICAL**](https://github.com/magical-eda/MAGICAL) and this research led to a lot of things. It basically gave a hope to the fellow designers that analog layouts can be automatically generated. 177 | ## Note 178 | 179 | - A usable version of this software is available [**here**](https://codeberg.org/tok/librecell) (Digital Layout Generator). It has been developed by Thomas Kramer. (PhD @ ETH-Zurich). 180 | - This objective of this project is to replicate the work done by Thomas thereby getting an exponential learning curve. 181 | - The usage of the tool is quite basic. However, developing the entire toolchain from scratch was a major challenge. 182 | - A CS grad can crack this problem with a little effort. However, I am not from CS. Hence, it took me a lot of time to get acquainted with CS concepts. 183 | - Euler Path finding problem is a DIV-2/D problem on codeforces.com. 184 | - [**Euler Path Finding on an Undirected Graph.**](https://cp-algorithms.com/graph/euler_path.html) 185 | - [**Bridges in a Graph**](https://codeforces.com/blog/entry/68138) 186 | - This project was initially aimed at generating Analog Layouts. However, that seemed to be an Herculean task. So, we tried for Digital Layouts. There are still a lot of things to be done. (Layouts for various PDK's, DRC's etc). While researching about this, we discovered another project that does the exact same thing. You can find it [**here**](https://github.com/ALIGN-analoglayout/ALIGN-public). **This (ALIGN - Analog Layouts Intelligently Generated from Netlists) is a massive multi-million dollar project involving multi-org & multi-institutional NDA's and top researchers in the world and funded by DARPA, Department of Defense, USA.** 187 | - After we got to know more about ALIGN, we started pivoting and developing over **ALIGN** infrastructure. We are in touch and working together with the researchers on **ALIGN**. 188 | 189 | ## Post Script 190 | 191 | - We had a tough time understanding the usage of ALIGN. However, we were able to crack it with some efforts. We were able to get the layouts of the reference designs that came with ALIGN and we tried to run ALIGN on some of our designs. We're not yet sure about the correctness since we haven't done any post layout simulations. Some of the layouts that we obtained can be found below. A tutorial on how to use ALIGN can be found [**here.**](https://1drv.ms/u/s!Ai4WW_jutengg75ZmSpOaznZs51m7Q?e=ebEm8t) 192 | 193 | Layout of an Operational Transconductance Amplifier -- 194 |

195 | 196 |

197 | 198 | Layout of bandgap reference design -- 199 |

200 | 201 |

202 | 203 | - For more details, you can check [**here.**](https://github.com/thesourcerer8/ALIGN-public/pull/2)
204 | - Details on ALIGN architecture can be found [**here.**](http://people.ece.umn.edu/users/sachin/conf/dac19-ALIGN.pdf) 205 | 206 | ## Author 207 | 208 | **Sethupathi Balakrishnan, EDA Researcher, EDA Lab, NTU-Taiwan** 209 | 210 | ## Acknowledgments 211 | - Kunal Ghosh, Director, VSD Corp. Pvt. Ltd. 212 | - Philipp Gühring, Software Architect, LibreSilicon Assocation 213 | 214 | Contact Information 215 | =================================== 216 | - Sethupathi Balakrishnan, SoC Design Engineer, b.sethupathi@gmail.com 217 | - Kunal Ghosh, Director, VSD Corp. Pvt. Ltd. kunalghosh@gmail.com 218 | - Philipp Gühring, Software Architect, LibreSilicon Assocation, pg@futureware.at 219 | -------------------------------------------------------------------------------- /References/reference_links: -------------------------------------------------------------------------------- 1 | https://www.geeksforgeeks.org/kargers-algorithm-for-minimum-cut-set-1-introduction-and-implementation/ 2 | https://www.geeksforgeeks.org/undirected-graph-splitting-and-its-application-for-number-pairs/ 3 | https://www.geeksforgeeks.org/bridge-in-a-graph/ 4 | https://www.youtube.com/watch?v=8MpoO2zA2l4 5 | -------------------------------------------------------------------------------- /Source_Code/EulerPath.cpp: -------------------------------------------------------------------------------- 1 | //Euler Path Existence 2 | 3 | 4 | 5 | // 6 | // main.cpp 7 | // EulerPath 8 | // 9 | // Created by Sethupathi on 30/07/20. 10 | // 11 | // 12 | 13 | //This returns if the the graph contains a Euler Path. 14 | //undirected graph 15 | 16 | #include 17 | //#include 18 | #include 19 | //#include 20 | 21 | using namespace std; 22 | 23 | class Graph { 24 | 25 | private: 26 | int V; //no. of vertices 27 | list *adj; //dynamic array of adjacency list 28 | public: 29 | 30 | Graph(int V) { 31 | this->V = V; 32 | adj = new list[V]; 33 | } 34 | ~Graph() { 35 | delete [] adj; 36 | } 37 | 38 | 39 | void addEdge(int v, int w); //function to add edge (v- first node, w -second node) 40 | 41 | void DFS(int v, bool visited[]); //Depth first search 42 | 43 | bool isConnected(); //function to check if all nodes with non-zero degrees are connected 44 | 45 | int isEulerian(); //function to check if the graph is Eulerian 46 | 47 | }; 48 | 49 | 50 | void Graph::addEdge(int v, int w) 51 | { 52 | adj[v].push_back(w); 53 | adj[w].push_back(v); 54 | } 55 | 56 | 57 | void Graph::DFS(int v, bool visited[]) 58 | 59 | { 60 | //mark the current node as visited 61 | visited[v] = true; 62 | list:: iterator i; 63 | 64 | //This subroutine is very powerful. It's a recursive call. 65 | 66 | for(i=adj[v].begin(); i!= adj[v].end(); ++i) 67 | { 68 | if(!visited[*i]) 69 | 70 | DFS(*i,visited); //This line is important. Prone to mistakes 71 | } 72 | } 73 | 74 | bool Graph::isConnected() 75 | { 76 | int i; 77 | bool visited[V]; 78 | for(int i=0;i0) //very imp. condition 105 | { 106 | return false; 107 | } 108 | } 109 | 110 | return true; 111 | } 112 | 113 | 114 | /* The function returns one of the following values 115 | 0 --> If grpah is not Eulerian 116 | 1 --> If graph has an Euler path (Semi-Eulerian) 117 | 2 --> If graph has an Euler Circuit (Eulerian) */ 118 | 119 | //function test() contains calls to isEulerian 120 | //function isEulerian() contains calls to isConnected() 121 | //function isConnected() contains calls to DFS() 122 | 123 | int Graph::isEulerian() 124 | { 125 | 126 | if (isConnected() == false) 127 | { 128 | return 0; 129 | } 130 | 131 | //Count vertices with odd degrees 132 | int odd=0; 133 | for(int i=0;i2) 140 | return 0; 141 | // If odd count is 2, then semi-eulerian. 142 | // If odd count is 0, then eulerian 143 | // Note that odd count can never be 1 for undirected graph 144 | 145 | return (odd)? 1:2; 146 | } 147 | 148 | //function to test cases 149 | 150 | void test (Graph &g) 151 | { 152 | int res = g.isEulerian(); 153 | if(res==0) 154 | cout<<"Graph is not Eulerian"< 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | class Graph 15 | { 16 | private: 17 | int V; 18 | list *adj; 19 | 20 | public: 21 | Graph(int V){ 22 | this->V= V; 23 | adj = new list[V]; 24 | } 25 | 26 | ~Graph() 27 | { 28 | delete [] adj; 29 | 30 | } 31 | 32 | void addEdge(int u, int v) 33 | { 34 | adj[u]. push_back(v); 35 | adj[v]. push_back(u); 36 | } 37 | 38 | void removeEdge(int u, int v); 39 | 40 | void printEulerTour(); 41 | void printEulerUtil(int s); 42 | 43 | //counting the no. of vertices reachable from v 44 | int DFSCount(int v, bool visited[]); 45 | 46 | //function to check if edge u-v is a valid next edge in euler trail 47 | bool isValidNextEdge(int u, int v); 48 | }; 49 | 50 | 51 | void Graph::printEulerTour() 52 | { 53 | int u=0; 54 | for (int i=0;i:: iterator i; 70 | for(i=adj[u].begin();i!=adj[u].end();++i) 71 | { 72 | int v = *i; 73 | 74 | 75 | if(v!= -1 && isValidNextEdge(u, v)) 76 | { 77 | cout<::iterator i; 94 | 95 | for (i=adj[u].begin();i!=adj[u].end();++i) 96 | { 97 | if(*i!=-1) 98 | count++; 99 | } 100 | 101 | if (count ==1) 102 | return true; 103 | 104 | 105 | // 2) If there are multiple adjacents, then u-v is not a bridge 106 | // Do following steps to check if u-v is a bridge 107 | 108 | // 2.a) count of vertices reachable from u 109 | 110 | bool visited[V]; 111 | memset(visited,false,V); 112 | int count1 = DFSCount(u, visited); 113 | 114 | // 2.b) Remove edge (u, v) and after removing the edge, count 115 | // vertices reachable from u 116 | 117 | removeEdge(u, v); 118 | memset(visited, false, V); 119 | 120 | int count2 = DFSCount(u, visited); 121 | // 2.c) Add the edge back to the graph 122 | addEdge(u, v); 123 | 124 | // 2.d) If count1 is greater, then edge (u, v) is a bridge 125 | return (count1 > count2)? false: true; 126 | } 127 | 128 | 129 | 130 | void Graph:: removeEdge(int u, int v) 131 | { 132 | 133 | // Find v in adjacency list of u and replace it with -1 134 | list::iterator iv = find(adj[u].begin(),adj[u].end(),v); 135 | *iv =-1; 136 | 137 | list::iterator iu = find(adj[v].begin(),adj[v].end(),u); 138 | *iu =-1; 139 | 140 | } 141 | 142 | 143 | // A DFS based function to count reachable vertices from v 144 | int Graph::DFSCount(int v, bool visited[]) 145 | { 146 | visited[v] = true; 147 | int count=1; 148 | 149 | list:: iterator i; 150 | for(i=adj[v].begin();i!=adj[v].end();++i) 151 | { 152 | if(*i!=-1 && !visited[*i]) 153 | count += DFSCount(*i, visited); 154 | } 155 | 156 | return count; 157 | } 158 | 159 | 160 | // Driver program to test above function 161 | int main() 162 | { 163 | 164 | 165 | int e,node1,node2; 166 | //cout<<"Enter no. of edges"; 167 | cin>>e; 168 | 169 | Graph g1(e); 170 | 171 | for(int i=0;i>node1>>node2; 174 | g1.addEdge(node1,node2); 175 | } 176 | 177 | g1.printEulerTour(); 178 | 179 | 180 | // Let us first create and test graphs shown in above figure 181 | // Graph g1(4); 182 | // g1.addEdge(0, 1); 183 | // g1.addEdge(0, 2); 184 | // g1.addEdge(1, 2); 185 | // g1.addEdge(2, 3); 186 | // g1.printEulerTour(); 187 | // 188 | // Graph g2(3); 189 | // g2.addEdge(0, 1); 190 | // g2.addEdge(1, 2); 191 | // g2.addEdge(0, 2); 192 | // g2.printEulerTour(); 193 | // 194 | // Graph g3(4); 195 | // g3.addEdge(0, 1); 196 | // g3.addEdge(1, 2); 197 | // g3.addEdge(2, 3); 198 | // g3.addEdge(3, 0); 199 | // 200 | // g3.printEulerTour(); 201 | // 202 | // //starts from '0' 203 | // 204 | // Graph g4(6); 205 | // g4.addEdge(1, 2); 206 | // g4.addEdge(2, 4); 207 | // g4.addEdge(4, 3); 208 | // g4.addEdge(3, 1); 209 | // g4.addEdge(4, 5); 210 | // g4.addEdge(5, 4); 211 | // g4.addEdge(7, 5); 212 | // g4.addEdge(7, 0); 213 | // g4.addEdge(6, 0); 214 | // g4.addEdge(6, 0); 215 | // g4.addEdge(5, 6); 216 | // g4.addEdge(5, 6); 217 | 218 | 219 | // g4.addEdge(0, 1); 220 | // g4.addEdge(0, 1); 221 | // g4.addEdge(1, 2); 222 | // g4.addEdge(1, 2); 223 | // g4.addEdge(2, 3); 224 | // g4.addEdge(3, 0); 225 | // 226 | // g4.printEulerTour(); 227 | // 228 | // Graph g5(5); 229 | // g5.addEdge(0, 1); 230 | // g5.addEdge(0, 1); 231 | // g5.addEdge(2, 3); 232 | // g5.addEdge(3, 4); 233 | // g5.addEdge(1, 2); 234 | // g5.printEulerTour(); 235 | // 236 | // 237 | // cout<<"Euler Path for the boolean function "< $file.tmp 16 | grep 'M\|D\|R' $file | grep -v '.include\|.end\|.dc\|.control\|.endc\|.model\|plot\|*\|run\|Vd_\|cload\|pulse\|Rl'> $file.tmp 17 | awk '{print $2,$4}' $file.tmp > $file.spice_final 18 | sed -i 's/vdd/1/g' $file.spice_final 19 | echo -e "Converted $file to $file.spice_final" 20 | rm -rf $file.tmp 21 | echo -e "\n" 22 | done 23 | echo -e "Converted all the spice files" 24 | -------------------------------------------------------------------------------- /Source_Code/read_spice1.tcl: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/tclsh 3 | set fp [open "spice_final.sp" r] 4 | set rfp [open "spice_final.sp.tmp" w+] 5 | 6 | set i 10 7 | #while {[gets $fp line]> -1} {incr i} 8 | # return i } 9 | # puts $i 10 | 11 | 12 | #set count [llength [split [read -nonewline $fp] "\n"]] 13 | 14 | set fp [open "spice_final.sp" r] 15 | 16 | while {[gets $fp line] >= 0} { 17 | set newline [string map {vdd 1 out 5} $line] 18 | puts $rfp $newline 19 | } 20 | 21 | 22 | close $fp 23 | close $rfp 24 | 25 | #set a [open "spice_final.sp.tmp" a] 26 | # 27 | #puts $a $count 28 | #close $a 29 | # 30 | # 31 | # 32 | # Read lines of file (name in “filename” variable) into variable “lines” 33 | #set f [open "spice_final.sp.tmp" "r"] 34 | #set lines [split [read $f] "\n"] 35 | #close $f 36 | # 37 | ## Find the insertion index in the reversed list 38 | #set idx [lsearch -regexp [lreverse $lines] "^#define "] 39 | #if {$idx < 0} { 40 | # error "did not find insertion point in $f" 41 | #} 42 | # 43 | ## Insert the lines (I'm assuming they're listed in the variable “linesToInsert”) 44 | #set lines [linsert $lines end-$idx {*}$count] 45 | # 46 | ## Write the lines back to the file 47 | #set f [open "spice_final.sp.tmp" "w"] 48 | #puts $f [join $lines "\n"] 49 | #close $f 50 | 51 | file rename -force "spice_final.sp.tmp" "spice_final.sp" 52 | 53 | --------------------------------------------------------------------------------