My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
schematic_file.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  schematic_file.cpp
3  --------------------
4  begin : Sat Mar 27 2004
5  copyright : (C) 2003 by Michael Margraf
6  email : michael.margraf@alumni.tu-berlin.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <qmessagebox.h>
23 #include <qdir.h>
24 #include <qstringlist.h>
25 #include <qregexp.h>
26 #include <qprocess.h>
27 #include <qtextedit.h>
28 #include <qptrlist.h>
29 
30 #include "main.h"
31 #include "node.h"
32 #include "schematic.h"
33 #include "diagrams/diagrams.h"
34 #include "paintings/paintings.h"
35 #include "components/spicefile.h"
36 #include "components/vhdlfile.h"
37 #include "components/verilogfile.h"
38 #include "components/libcomp.h"
39 
40 
41 extern QDir QucsWorkDir;
42 
43 // Here the subcircuits, SPICE components etc are collected. It must be
44 // global to also work within the subcircuits.
46 
47 
48 // -------------------------------------------------------------
49 // Creates a Qucs file format (without document properties) in the returning
50 // string. This is used to copy the selected elements into the clipboard.
51 QString Schematic::createClipboardFile()
52 {
53  int z=0; // counts selected elements
54  Wire *pw;
55  Diagram *pd;
56  Painting *pp;
57  Component *pc;
58 
59  QString s("<Qucs Schematic " PACKAGE_VERSION ">\n");
60 
61  // Build element document.
62  s += "<Components>\n";
63  for(pc = Components->first(); pc != 0; pc = Components->next())
64  if(pc->isSelected) {
65  s += pc->save()+"\n"; z++; }
66  s += "</Components>\n";
67 
68  s += "<Wires>\n";
69  for(pw = Wires->first(); pw != 0; pw = Wires->next())
70  if(pw->isSelected) {
71  z++;
72  if(pw->Label) if(!pw->Label->isSelected) {
73  s += pw->save().section('"', 0, 0)+"\"\" 0 0 0>\n";
74  continue;
75  }
76  s += pw->save()+"\n";
77  }
78  for(Node *pn = Nodes->first(); pn != 0; pn = Nodes->next())
79  if(pn->Label) if(pn->Label->isSelected) {
80  s += pn->Label->save()+"\n"; z++; }
81  s += "</Wires>\n";
82 
83  s += "<Diagrams>\n";
84  for(pd = Diagrams->first(); pd != 0; pd = Diagrams->next())
85  if(pd->isSelected) {
86  s += pd->save()+"\n"; z++; }
87  s += "</Diagrams>\n";
88 
89  s += "<Paintings>\n";
90  for(pp = Paintings->first(); pp != 0; pp = Paintings->next())
91  if(pp->isSelected)
92  if(pp->Name.at(0) != '.') { // subcircuit specific -> do not copy
93  s += "<"+pp->save()+">\n"; z++; }
94  s += "</Paintings>\n";
95 
96  if(z == 0) return ""; // return empty if no selection
97 
98  return s;
99 }
100 
101 // -------------------------------------------------------------
102 // Only read fields without loading them.
103 bool Schematic::loadIntoNothing(QTextStream *stream)
104 {
105  QString Line, cstr;
106  while(!stream->atEnd()) {
107  Line = stream->readLine();
108  if(Line.at(0) == '<') if(Line.at(1) == '/') return true;
109  }
110 
111  QMessageBox::critical(0, QObject::tr("Error"),
112  QObject::tr("Format Error:\n'Painting' field is not closed!"));
113  return false;
114 }
115 
116 // -------------------------------------------------------------
117 // Paste from clipboard.
118 bool Schematic::pasteFromClipboard(QTextStream *stream, QPtrList<Element> *pe)
119 {
120  QString Line;
121 
122  Line = stream->readLine();
123  if(Line.left(16) != "<Qucs Schematic ") // wrong file type ?
124  return false;
125 
126  QString s = PACKAGE_VERSION;
127  Line = Line.mid(16, Line.length()-17);
128  if(Line != s) { // wrong version number ?
129  QMessageBox::critical(0, QObject::tr("Error"),
130  QObject::tr("Wrong document version: ")+Line);
131  return false;
132  }
133 
134  // read content in symbol edit mode *************************
135  if(symbolMode) {
136  while(!stream->atEnd()) {
137  Line = stream->readLine();
138  if(Line == "<Components>") {
139  if(!loadIntoNothing(stream)) return false; }
140  else
141  if(Line == "<Wires>") {
142  if(!loadIntoNothing(stream)) return false; }
143  else
144  if(Line == "<Diagrams>") {
145  if(!loadIntoNothing(stream)) return false; }
146  else
147  if(Line == "<Paintings>") {
148  if(!loadPaintings(stream, (QPtrList<Painting>*)pe)) return false; }
149  else {
150  QMessageBox::critical(0, QObject::tr("Error"),
151  QObject::tr("Clipboard Format Error:\nUnknown field!"));
152  return false;
153  }
154  }
155 
156  return true;
157  }
158 
159  // read content in schematic edit mode *************************
160  while(!stream->atEnd()) {
161  Line = stream->readLine();
162  if(Line == "<Components>") {
163  if(!loadComponents(stream, (QPtrList<Component>*)pe)) return false; }
164  else
165  if(Line == "<Wires>") {
166  if(!loadWires(stream, pe)) return false; }
167  else
168  if(Line == "<Diagrams>") {
169  if(!loadDiagrams(stream, (QPtrList<Diagram>*)pe)) return false; }
170  else
171  if(Line == "<Paintings>") {
172  if(!loadPaintings(stream, (QPtrList<Painting>*)pe)) return false; }
173  else {
174  QMessageBox::critical(0, QObject::tr("Error"),
175  QObject::tr("Clipboard Format Error:\nUnknown field!"));
176  return false;
177  }
178  }
179 
180  return true;
181 }
182 
183 // -------------------------------------------------------------
185 {
186  QFileInfo info (DocName);
187  QString cppfile = info.dirPath () + QDir::separator() + DataSet;
188  QFile file (cppfile);
189 
190  if (!file.open (IO_WriteOnly)) {
191  QMessageBox::critical (0, QObject::tr("Error"),
192  QObject::tr("Cannot save C++ file \"%1\"!").arg(cppfile));
193  return -1;
194  }
195 
196  QTextStream stream (&file);
197 
198  // automatically compute boundings of drawing
199  int xmin = INT_MAX;
200  int ymin = INT_MAX;
201  int xmax = INT_MIN;
202  int ymax = INT_MIN;
203  int x1, y1, x2, y2;
204  int maxNum = 0;
205  Painting * pp;
206 
207  stream << " // symbol drawing code\n";
208  for (pp = SymbolPaints.first (); pp != 0; pp = SymbolPaints.next ()) {
209  if (pp->Name == ".ID ") continue;
210  if (pp->Name == ".PortSym ") {
211  if (((PortSymbol*)pp)->numberStr.toInt() > maxNum)
212  maxNum = ((PortSymbol*)pp)->numberStr.toInt();
213  continue;
214  }
215  pp->Bounding (x1, y1, x2, y2);
216  if (x1 < xmin) xmin = x1;
217  if (x2 > xmax) xmax = x2;
218  if (y1 < ymin) ymin = y1;
219  if (y2 > ymax) ymax = y2;
220  stream << " " << pp->saveCpp () << "\n";
221  }
222 
223  stream << "\n // terminal definitions\n";
224  for (int i = 1; i <= maxNum; i++) {
225  for (pp = SymbolPaints.first (); pp != 0; pp = SymbolPaints.next ()) {
226  if (pp->Name == ".PortSym ")
227  if (((PortSymbol*)pp)->numberStr.toInt() == i)
228  stream << " " << pp->saveCpp () << "\n";
229  }
230  }
231 
232  stream << "\n // symbol boundings\n"
233  << " x1 = " << xmin << "; " << " y1 = " << ymin << ";\n"
234  << " x2 = " << xmax << "; " << " y2 = " << ymax << ";\n";
235 
236  stream << "\n // property text position\n";
237  for (pp = SymbolPaints.first (); pp != 0; pp = SymbolPaints.next ())
238  if (pp->Name == ".ID ")
239  stream << " " << pp->saveCpp () << "\n";
240 
241  file.close ();
242  return 0;
243 }
244 
245 // -------------------------------------------------------------
246 // Returns the number of subcircuit ports.
247 int Schematic::saveDocument()
248 {
249  QFile file(DocName);
250  if(!file.open(IO_WriteOnly)) {
251  QMessageBox::critical(0, QObject::tr("Error"),
252  QObject::tr("Cannot save document!"));
253  return -1;
254  }
255 
256  QTextStream stream(&file);
257 
258  stream << "<Qucs Schematic " << PACKAGE_VERSION << ">\n";
259 
260  stream << "<Properties>\n";
261  if(symbolMode) {
262  stream << " <View=" << tmpViewX1<<","<<tmpViewY1<<","
263  << tmpViewX2<<","<<tmpViewY2<< ",";
264  stream <<tmpScale<<","<<tmpPosX<<","<<tmpPosY << ">\n";
265  }
266  else {
267  stream << " <View=" << ViewX1<<","<<ViewY1<<","
268  << ViewX2<<","<<ViewY2<< ",";
269  stream << Scale <<","<<contentsX()<<","<<contentsY() << ">\n";
270  }
271  stream << " <Grid=" << GridX<<","<<GridY<<","
272  << GridOn << ">\n";
273  stream << " <DataSet=" << DataSet << ">\n";
274  stream << " <DataDisplay=" << DataDisplay << ">\n";
275  stream << " <OpenDisplay=" << SimOpenDpl << ">\n";
276  stream << " <Script=" << Script << ">\n";
277  stream << " <RunScript=" << SimRunScript << ">\n";
278  stream << " <showFrame=" << showFrame << ">\n";
279 
280  QString t;
282  stream << " <FrameText0=" << t << ">\n";
284  stream << " <FrameText1=" << t << ">\n";
286  stream << " <FrameText2=" << t << ">\n";
288  stream << " <FrameText3=" << t << ">\n";
289  stream << "</Properties>\n";
290 
291  Painting *pp;
292  stream << "<Symbol>\n"; // save all paintings for symbol
293  for(pp = SymbolPaints.first(); pp != 0; pp = SymbolPaints.next())
294  stream << " <" << pp->save() << ">\n";
295  stream << "</Symbol>\n";
296 
297  stream << "<Components>\n"; // save all components
298  for(Component *pc = DocComps.first(); pc != 0; pc = DocComps.next())
299  stream << " " << pc->save() << "\n";
300  stream << "</Components>\n";
301 
302  stream << "<Wires>\n"; // save all wires
303  for(Wire *pw = DocWires.first(); pw != 0; pw = DocWires.next())
304  stream << " " << pw->save() << "\n";
305 
306  // save all labeled nodes as wires
307  for(Node *pn = DocNodes.first(); pn != 0; pn = DocNodes.next())
308  if(pn->Label) stream << " " << pn->Label->save() << "\n";
309  stream << "</Wires>\n";
310 
311  stream << "<Diagrams>\n"; // save all diagrams
312  for(Diagram *pd = DocDiags.first(); pd != 0; pd = DocDiags.next())
313  stream << " " << pd->save() << "\n";
314  stream << "</Diagrams>\n";
315 
316  stream << "<Paintings>\n"; // save all paintings
317  for(pp = DocPaints.first(); pp != 0; pp = DocPaints.next())
318  stream << " <" << pp->save() << ">\n";
319  stream << "</Paintings>\n";
320 
321  file.close();
322 
323  // additionally save symbol C++ code if in a symbol drawing and the
324  // associated file is a Verilog-A file
325  if (fileSuffix () == "sym") {
326  if (fileSuffix (DataDisplay) == "va") {
327  saveSymbolCpp ();
328  }
329  }
330 
331  return 0;
332 }
333 
334 // -------------------------------------------------------------
335 bool Schematic::loadProperties(QTextStream *stream)
336 {
337  bool ok = true;
338  QString Line, cstr, nstr;
339  while(!stream->atEnd()) {
340  Line = stream->readLine();
341  if(Line.at(0) == '<') if(Line.at(1) == '/') return true; // field end ?
342  Line = Line.stripWhiteSpace();
343  if(Line.isEmpty()) continue;
344 
345  if(Line.at(0) != '<') {
346  QMessageBox::critical(0, QObject::tr("Error"),
347  QObject::tr("Format Error:\nWrong property field limiter!"));
348  return false;
349  }
350  if(Line.at(Line.length()-1) != '>') {
351  QMessageBox::critical(0, QObject::tr("Error"),
352  QObject::tr("Format Error:\nWrong property field limiter!"));
353  return false;
354  }
355  Line = Line.mid(1, Line.length()-2); // cut off start and end character
356 
357  cstr = Line.section('=',0,0); // property type
358  nstr = Line.section('=',1,1); // property value
359  if(cstr == "View") {
360  ViewX1 = nstr.section(',',0,0).toInt(&ok); if(ok) {
361  ViewY1 = nstr.section(',',1,1).toInt(&ok); if(ok) {
362  ViewX2 = nstr.section(',',2,2).toInt(&ok); if(ok) {
363  ViewY2 = nstr.section(',',3,3).toInt(&ok); if(ok) {
364  Scale = nstr.section(',',4,4).toDouble(&ok); if(ok) {
365  tmpViewX1 = nstr.section(',',5,5).toInt(&ok); if(ok)
366  tmpViewY1 = nstr.section(',',6,6).toInt(&ok); }}}}} }
367  else if(cstr == "Grid") {
368  GridX = nstr.section(',',0,0).toInt(&ok); if(ok) {
369  GridY = nstr.section(',',1,1).toInt(&ok); if(ok) {
370  if(nstr.section(',',2,2).toInt(&ok) == 0) GridOn = false;
371  else GridOn = true; }} }
372  else if(cstr == "DataSet") DataSet = nstr;
373  else if(cstr == "DataDisplay") DataDisplay = nstr;
374  else if(cstr == "OpenDisplay")
375  if(nstr.toInt(&ok) == 0) SimOpenDpl = false;
376  else SimOpenDpl = true;
377  else if(cstr == "Script") Script = nstr;
378  else if(cstr == "RunScript")
379  if(nstr.toInt(&ok) == 0) SimRunScript = false;
380  else SimRunScript = true;
381  else if(cstr == "showFrame")
382  showFrame = nstr.at(0).latin1() - '0';
383  else if(cstr == "FrameText0") convert2Unicode(Frame_Text0 = nstr);
384  else if(cstr == "FrameText1") convert2Unicode(Frame_Text1 = nstr);
385  else if(cstr == "FrameText2") convert2Unicode(Frame_Text2 = nstr);
386  else if(cstr == "FrameText3") convert2Unicode(Frame_Text3 = nstr);
387  else {
388  QMessageBox::critical(0, QObject::tr("Error"),
389  QObject::tr("Format Error:\nUnknown property: ")+cstr);
390  return false;
391  }
392  if(!ok) {
393  QMessageBox::critical(0, QObject::tr("Error"),
394  QObject::tr("Format Error:\nNumber expected in property field!"));
395  return false;
396  }
397  }
398 
399  QMessageBox::critical(0, QObject::tr("Error"),
400  QObject::tr("Format Error:\n'Property' field is not closed!"));
401  return false;
402 }
403 
404 // ---------------------------------------------------
405 // Inserts a component without performing logic for wire optimization.
406 void Schematic::simpleInsertComponent(Component *c)
407 {
408  Node *pn;
409  int x, y;
410  // connect every node of component
411  for(Port *pp = c->Ports.first(); pp != 0; pp = c->Ports.next()) {
412  x = pp->x+c->cx;
413  y = pp->y+c->cy;
414 
415  // check if new node lies upon existing node
416  for(pn = DocNodes.first(); pn != 0; pn = DocNodes.next())
417  if(pn->cx == x) if(pn->cy == y) {
418  if (!pn->DType.isEmpty()) {
419  pp->Type = pn->DType;
420  }
421  if (!pp->Type.isEmpty()) {
422  pn->DType = pp->Type;
423  }
424  break;
425  }
426 
427  if(pn == 0) { // create new node, if no existing one lies at this position
428  pn = new Node(x, y);
429  DocNodes.append(pn);
430  }
431  pn->Connections.append(c); // connect schematic node to component node
432  if (!pp->Type.isEmpty()) {
433  pn->DType = pp->Type;
434  }
435 
436  pp->Connection = pn; // connect component node to schematic node
437  }
438 
439  DocComps.append(c);
440 }
441 
442 // -------------------------------------------------------------
443 bool Schematic::loadComponents(QTextStream *stream, QPtrList<Component> *List)
444 {
445  QString Line, cstr;
446  Component *c;
447  while(!stream->atEnd()) {
448  Line = stream->readLine();
449  if(Line.at(0) == '<') if(Line.at(1) == '/') return true;
450  Line = Line.stripWhiteSpace();
451  if(Line.isEmpty()) continue;
452 
453  c = getComponentFromName(Line);
454  if(!c) return false;
455 
456  if(List) { // "paste" ?
457  int z;
458  for(z=c->Name.length()-1; z>=0; z--) // cut off number of component name
459  if(!c->Name.at(z).isDigit()) break;
460  c->Name = c->Name.left(z+1);
461  List->append(c);
462  }
463  else simpleInsertComponent(c);
464  }
465 
466  QMessageBox::critical(0, QObject::tr("Error"),
467  QObject::tr("Format Error:\n'Component' field is not closed!"));
468  return false;
469 }
470 
471 // -------------------------------------------------------------
472 // Inserts a wire without performing logic for optimizing.
473 void Schematic::simpleInsertWire(Wire *pw)
474 {
475  Node *pn;
476  // check if first wire node lies upon existing node
477  for(pn = DocNodes.first(); pn != 0; pn = DocNodes.next())
478  if(pn->cx == pw->x1) if(pn->cy == pw->y1) break;
479 
480  if(!pn) { // create new node, if no existing one lies at this position
481  pn = new Node(pw->x1, pw->y1);
482  DocNodes.append(pn);
483  }
484 
485  if(pw->x1 == pw->x2) if(pw->y1 == pw->y2) {
486  pn->Label = pw->Label; // wire with length zero are just node labels
487  if (pn->Label) {
488  pn->Label->Type = isNodeLabel;
489  pn->Label->pOwner = pn;
490  }
491  delete pw; // delete wire because this is not a wire
492  return;
493  }
494  pn->Connections.append(pw); // connect schematic node to component node
495  pw->Port1 = pn;
496 
497  // check if second wire node lies upon existing node
498  for(pn = DocNodes.first(); pn != 0; pn = DocNodes.next())
499  if(pn->cx == pw->x2) if(pn->cy == pw->y2) break;
500 
501  if(!pn) { // create new node, if no existing one lies at this position
502  pn = new Node(pw->x2, pw->y2);
503  DocNodes.append(pn);
504  }
505  pn->Connections.append(pw); // connect schematic node to component node
506  pw->Port2 = pn;
507 
508  DocWires.append(pw);
509 }
510 
511 // -------------------------------------------------------------
512 bool Schematic::loadWires(QTextStream *stream, QPtrList<Element> *List)
513 {
514  Wire *w;
515  QString Line;
516  while(!stream->atEnd()) {
517  Line = stream->readLine();
518  if(Line.at(0) == '<') if(Line.at(1) == '/') return true;
519  Line = Line.stripWhiteSpace();
520  if(Line.isEmpty()) continue;
521 
522  // (Node*)4 = move all ports (later on)
523  w = new Wire(0,0,0,0, (Node*)4,(Node*)4);
524  if(!w->load(Line)) {
525  QMessageBox::critical(0, QObject::tr("Error"),
526  QObject::tr("Format Error:\nWrong 'wire' line format!"));
527  delete w;
528  return false;
529  }
530  if(List) {
531  if(w->x1 == w->x2) if(w->y1 == w->y2) if(w->Label) {
532  w->Label->Type = isMovingLabel;
533  List->append(w->Label);
534  delete w;
535  continue;
536  }
537  List->append(w);
538  if(w->Label) List->append(w->Label);
539  }
540  else simpleInsertWire(w);
541  }
542 
543  QMessageBox::critical(0, QObject::tr("Error"),
544  QObject::tr("Format Error:\n'Wire' field is not closed!"));
545  return false;
546 }
547 
548 // -------------------------------------------------------------
549 bool Schematic::loadDiagrams(QTextStream *stream, QPtrList<Diagram> *List)
550 {
551  Diagram *d;
552  QString Line, cstr;
553  while(!stream->atEnd()) {
554  Line = stream->readLine();
555  if(Line.at(0) == '<') if(Line.at(1) == '/') return true;
556  Line = Line.stripWhiteSpace();
557  if(Line.isEmpty()) continue;
558 
559  cstr = Line.section(' ',0,0); // diagram type
560  if(cstr == "<Rect") d = new RectDiagram();
561  else if(cstr == "<Polar") d = new PolarDiagram();
562  else if(cstr == "<Tab") d = new TabDiagram();
563  else if(cstr == "<Smith") d = new SmithDiagram();
564  else if(cstr == "<ySmith") d = new SmithDiagram(0,0,false);
565  else if(cstr == "<PS") d = new PSDiagram();
566  else if(cstr == "<SP") d = new PSDiagram(0,0,false);
567  else if(cstr == "<Rect3D") d = new Rect3DDiagram();
568  else if(cstr == "<Curve") d = new CurveDiagram();
569  else if(cstr == "<Time") d = new TimingDiagram();
570  else if(cstr == "<Truth") d = new TruthDiagram();
571  else {
572  QMessageBox::critical(0, QObject::tr("Error"),
573  QObject::tr("Format Error:\nUnknown diagram!"));
574  return false;
575  }
576 
577  if(!d->load(Line, stream)) {
578  QMessageBox::critical(0, QObject::tr("Error"),
579  QObject::tr("Format Error:\nWrong 'diagram' line format!"));
580  delete d;
581  return false;
582  }
583  List->append(d);
584  }
585 
586  QMessageBox::critical(0, QObject::tr("Error"),
587  QObject::tr("Format Error:\n'Diagram' field is not closed!"));
588  return false;
589 }
590 
591 // -------------------------------------------------------------
592 bool Schematic::loadPaintings(QTextStream *stream, QPtrList<Painting> *List)
593 {
594  Painting *p=0;
595  QString Line, cstr;
596  while(!stream->atEnd()) {
597  Line = stream->readLine();
598  if(Line.at(0) == '<') if(Line.at(1) == '/') return true;
599 
600  Line = Line.stripWhiteSpace();
601  if(Line.isEmpty()) continue;
602  if( (Line.at(0) != '<') || (Line.at(Line.length()-1) != '>')) {
603  QMessageBox::critical(0, QObject::tr("Error"),
604  QObject::tr("Format Error:\nWrong 'painting' line delimiter!"));
605  return false;
606  }
607  Line = Line.mid(1, Line.length()-2); // cut off start and end character
608 
609  cstr = Line.section(' ',0,0); // painting type
610  if(cstr == "Line") p = new GraphicLine();
611  else if(cstr == "EArc") p = new EllipseArc();
612  else if(cstr == ".PortSym") p = new PortSymbol();
613  else if(cstr == ".ID") p = new ID_Text();
614  else if(cstr == "Text") p = new GraphicText();
615  else if(cstr == "Rectangle") p = new Rectangle();
616  else if(cstr == "Arrow") p = new Arrow();
617  else if(cstr == "Ellipse") p = new Ellipse();
618  else {
619  QMessageBox::critical(0, QObject::tr("Error"),
620  QObject::tr("Format Error:\nUnknown painting!"));
621  return false;
622  }
623 
624  if(!p->load(Line)) {
625  QMessageBox::critical(0, QObject::tr("Error"),
626  QObject::tr("Format Error:\nWrong 'painting' line format!"));
627  delete p;
628  return false;
629  }
630  List->append(p);
631  }
632 
633  QMessageBox::critical(0, QObject::tr("Error"),
634  QObject::tr("Format Error:\n'Painting' field is not closed!"));
635  return false;
636 }
637 
638 // -------------------------------------------------------------
640 {
641  QFile file(DocName);
642  if(!file.open(IO_ReadOnly)) {
643  QMessageBox::critical(0, QObject::tr("Error"),
644  QObject::tr("Cannot load document: ")+DocName);
645  return false;
646  }
647 
648  QString Line;
649  QTextStream stream(&file);
650 
651  // read header **************************
652  do {
653  if(stream.atEnd()) {
654  file.close();
655  return true;
656  }
657 
658  Line = stream.readLine();
659  } while(Line.isEmpty());
660 
661  if(Line.left(16) != "<Qucs Schematic ") { // wrong file type ?
662  file.close();
663  QMessageBox::critical(0, QObject::tr("Error"),
664  QObject::tr("Wrong document type: ")+DocName);
665  return false;
666  }
667 
668  Line = Line.mid(16, Line.length()-17);
669  if(!checkVersion(Line)) { // wrong version number ?
670  file.close();
671  QMessageBox::critical(0, QObject::tr("Error"),
672  QObject::tr("Wrong document version: ")+Line);
673  return false;
674  }
675 
676  // read content *************************
677  while(!stream.atEnd()) {
678  Line = stream.readLine();
679  Line = Line.stripWhiteSpace();
680  if(Line.isEmpty()) continue;
681 
682  if(Line == "<Symbol>") {
683  if(!loadPaintings(&stream, &SymbolPaints)) {
684  file.close();
685  return false;
686  }
687  }
688  else
689  if(Line == "<Properties>") {
690  if(!loadProperties(&stream)) { file.close(); return false; } }
691  else
692  if(Line == "<Components>") {
693  if(!loadComponents(&stream)) { file.close(); return false; } }
694  else
695  if(Line == "<Wires>") {
696  if(!loadWires(&stream)) { file.close(); return false; } }
697  else
698  if(Line == "<Diagrams>") {
699  if(!loadDiagrams(&stream, &DocDiags)) { file.close(); return false; } }
700  else
701  if(Line == "<Paintings>") {
702  if(!loadPaintings(&stream, &DocPaints)) { file.close(); return false; } }
703  else {
704  QMessageBox::critical(0, QObject::tr("Error"),
705  QObject::tr("File Format Error:\nUnknown field!"));
706  file.close();
707  return false;
708  }
709  }
710 
711  file.close();
712  return true;
713 }
714 
715 // -------------------------------------------------------------
716 // Creates a Qucs file format (without document properties) in the returning
717 // string. This is used to save state for undo operation.
718 QString Schematic::createUndoString(char Op)
719 {
720  Wire *pw;
721  Diagram *pd;
722  Painting *pp;
723  Component *pc;
724 
725  // Build element document.
726  QString s = " \n";
727  s.at(0) = Op;
728  for(pc = DocComps.first(); pc != 0; pc = DocComps.next())
729  s += pc->save()+"\n";
730  s += "</>\n"; // short end flag
731 
732  for(pw = DocWires.first(); pw != 0; pw = DocWires.next())
733  s += pw->save()+"\n";
734  // save all labeled nodes as wires
735  for(Node *pn = DocNodes.first(); pn != 0; pn = DocNodes.next())
736  if(pn->Label) s += pn->Label->save()+"\n";
737  s += "</>\n";
738 
739  for(pd = DocDiags.first(); pd != 0; pd = DocDiags.next())
740  s += pd->save()+"\n";
741  s += "</>\n";
742 
743  for(pp = DocPaints.first(); pp != 0; pp = DocPaints.next())
744  s += "<"+pp->save()+">\n";
745  s += "</>\n";
746 
747  return s;
748 }
749 
750 // -------------------------------------------------------------
751 // Same as "createUndoString(char Op)" but for symbol edit mode.
752 QString Schematic::createSymbolUndoString(char Op)
753 {
754  Painting *pp;
755 
756  // Build element document.
757  QString s = " \n";
758  s.at(0) = Op;
759  s += "</>\n"; // short end flag for components
760  s += "</>\n"; // short end flag for wires
761  s += "</>\n"; // short end flag for diagrams
762 
763  for(pp = SymbolPaints.first(); pp != 0; pp = SymbolPaints.next())
764  s += "<"+pp->save()+">\n";
765  s += "</>\n";
766 
767  return s;
768 }
769 
770 // -------------------------------------------------------------
771 // Is quite similiar to "loadDocument()" but with less error checking.
772 // Used for "undo" function.
773 bool Schematic::rebuild(QString *s)
774 {
775  DocWires.clear(); // delete whole document
776  DocNodes.clear();
777  DocComps.clear();
778  DocDiags.clear();
779  DocPaints.clear();
780 
781  QString Line;
782  QTextStream stream(s, IO_ReadOnly);
783  Line = stream.readLine(); // skip identity byte
784 
785  // read content *************************
786  if(!loadComponents(&stream)) return false;
787  if(!loadWires(&stream)) return false;
788  if(!loadDiagrams(&stream, &DocDiags)) return false;
789  if(!loadPaintings(&stream, &DocPaints)) return false;
790 
791  return true;
792 }
793 
794 // -------------------------------------------------------------
795 // Same as "rebuild(QString *s)" but for symbol edit mode.
796 bool Schematic::rebuildSymbol(QString *s)
797 {
798  SymbolPaints.clear(); // delete whole document
799 
800  QString Line;
801  QTextStream stream(s, IO_ReadOnly);
802  Line = stream.readLine(); // skip identity byte
803 
804  // read content *************************
805  Line = stream.readLine(); // skip components
806  Line = stream.readLine(); // skip wires
807  Line = stream.readLine(); // skip diagrams
808  if(!loadPaintings(&stream, &SymbolPaints)) return false;
809 
810  return true;
811 }
812 
813 
814 // ***************************************************************
815 // ***** *****
816 // ***** Functions to create netlist *****
817 // ***** *****
818 // ***************************************************************
819 
820 void Schematic::createNodeSet(QStringList& Collect, int& countInit,
821  Conductor *pw, Node *p1)
822 {
823  if(pw->Label)
824  if(!pw->Label->initValue.isEmpty())
825  Collect.append("NodeSet:NS" + QString::number(countInit++) + " " +
826  p1->Name + " U=\"" + pw->Label->initValue + "\"");
827 }
828 
829 // ---------------------------------------------------
830 void Schematic::throughAllNodes(bool User, QStringList& Collect,
831  int& countInit)
832 {
833  Node *pn;
834  int z=0;
835 
836  for(pn = DocNodes.first(); pn != 0; pn = DocNodes.next()) {
837  if(pn->Name.isEmpty() == User) {
838  continue; // already named ?
839  }
840  if(!User) {
841  if(isAnalog)
842  pn->Name = "_net";
843  else
844  pn->Name = "net_net"; // VHDL names must not begin with '_'
845  pn->Name += QString::number(z++); // create numbered node name
846  }
847  else if(pn->State) {
848  continue; // already worked on
849  }
850 
851  if(isAnalog) createNodeSet(Collect, countInit, pn, pn);
852 
853  pn->State = 1;
854  propagateNode(Collect, countInit, pn);
855  }
856 }
857 
858 // ---------------------------------------------------
859 // Collects the signal names for digital simulations.
860 void Schematic::collectDigitalSignals(void)
861 {
862  Node *pn;
863 
864  for(pn = DocNodes.first(); pn != 0; pn = DocNodes.next()) {
865  DigMap::Iterator it = Signals.find(pn->Name);
866  if(it == Signals.end()) { // avoid redeclaration of signal
867  Signals.insert(pn->Name, DigSignal(pn->Name, pn->DType));
868  } else if (!pn->DType.isEmpty()) {
869  it.data().Type = pn->DType;
870  }
871  }
872 }
873 
874 // ---------------------------------------------------
875 // Propagates the given node to connected component ports.
876 void Schematic::propagateNode(QStringList& Collect,
877  int& countInit, Node *pn)
878 {
879  bool setName=false;
880  QPtrList<Node> Cons;
881  Node *p2;
882  Wire *pw;
883  Element *pe;
884 
885  Cons.append(pn);
886  for(p2 = Cons.first(); p2 != 0; p2 = Cons.next())
887  for(pe = p2->Connections.first(); pe != 0; pe = p2->Connections.next())
888  if(pe->Type == isWire) {
889  pw = (Wire*)pe;
890  if(p2 != pw->Port1) {
891  if(pw->Port1->Name.isEmpty()) {
892  pw->Port1->Name = pn->Name;
893  pw->Port1->State = 1;
894  Cons.append(pw->Port1);
895  setName = true;
896  }
897  }
898  else {
899  if(pw->Port2->Name.isEmpty()) {
900  pw->Port2->Name = pn->Name;
901  pw->Port2->State = 1;
902  Cons.append(pw->Port2);
903  setName = true;
904  }
905  }
906  if(setName) {
907  Cons.findRef(p2); // back to current Connection
908  if (isAnalog) createNodeSet(Collect, countInit, pw, pn);
909  setName = false;
910  }
911  }
912  Cons.clear();
913 }
914 
915 
916 // ---------------------------------------------------
917 // Goes through all schematic components and allows special component
918 // handling, e.g. like subcircuit netlisting.
919 bool Schematic::throughAllComps(QTextStream *stream, int& countInit,
920  QStringList& Collect, QTextEdit *ErrText, int NumPorts)
921 {
922  bool r;
923  QString s;
924 
925  // give the ground nodes the name "gnd", and insert subcircuits etc.
926  QPtrListIterator<Component> it(DocComps);
927  Component *pc;
928  while((pc = it.current()) != 0) {
929  ++it;
930  if(pc->isActive != COMP_IS_ACTIVE) continue;
931 
932  // check analog/digital typed components
933  if(isAnalog) {
934  if((pc->Type & isAnalogComponent) == 0) {
935  ErrText->insert(QObject::tr("ERROR: Component \"%1\" has no analog model.").arg(pc->Name));
936  return false;
937  }
938  } else {
939  if((pc->Type & isDigitalComponent) == 0) {
940  ErrText->insert(QObject::tr("ERROR: Component \"%1\" has no digital model.").arg(pc->Name));
941  return false;
942  }
943  }
944 
945  // handle ground symbol
946  if(pc->Model == "GND") {
947  pc->Ports.getFirst()->Connection->Name = "gnd";
948  continue;
949  }
950 
951  // handle subcircuits
952  if(pc->Model == "Sub") {
953  int i;
954  QString f = pc->getSubcircuitFile();
955  SubMap::Iterator it = FileList.find(f);
956  if(it != FileList.end()) {
957  if (!it.data().PortTypes.isEmpty()) {
958  i = 0;
959  // apply in/out signal types of subcircuit
960  for(Port *pp = pc->Ports.first(); pp; pp = pc->Ports.next(), i++) {
961  pp->Type = it.data().PortTypes[i];
962  pp->Connection->DType = pp->Type;
963  }
964  }
965  continue; // insert each subcircuit just one time
966  }
967  SubFile sub = SubFile("SCH", f);
968  FileList.insert(f, sub);
969 
970  // load subcircuit schematic
971  s = pc->Props.first()->Value;
972  Schematic *d = new Schematic(0, QucsWorkDir.filePath(s));
973  if(!d->loadDocument()) { // load document if possible
974  delete d;
975  ErrText->insert(QObject::tr("ERROR: Cannot load subcircuit \"%1\".").arg(s));
976  return false;
977  }
978  d->DocName = s;
979  d->isVerilog = isVerilog;
980  d->isAnalog = isAnalog;
982  r = d->createSubNetlist(stream, countInit, Collect, ErrText, NumPorts);
983  if (r) {
984  i = 0;
985  // save in/out signal types of subcircuit
986  for(Port *pp = pc->Ports.first(); pp; pp = pc->Ports.next(), i++) {
987  pp->Type = d->PortTypes[i];
988  pp->Connection->DType = pp->Type;
989  }
990  sub.PortTypes = d->PortTypes;
991  FileList.replace(f, sub);
992  }
993  delete d;
994  if(!r) return false;
995  continue;
996  }
997 
998  // handle library symbols
999  if(pc->Model == "Lib") {
1000  if(creatingLib) {
1001  ErrText->insert(
1002  QObject::tr("WARNING: Skipping library component \"%1\".").
1003  arg(pc->Name));
1004  continue;
1005  }
1006  s = pc->getSubcircuitFile() + "/" + pc->Props.at(1)->Value;
1007  SubMap::Iterator it = FileList.find(s);
1008  if(it != FileList.end())
1009  continue; // insert each library subcircuit just one time
1010  FileList.insert(s, SubFile("LIB", s));
1011 
1012  if(isAnalog)
1013  r = ((LibComp*)pc)->createSubNetlist(stream, Collect, 1);
1014  else {
1015  if(isVerilog)
1016  r = ((LibComp*)pc)->createSubNetlist(stream, Collect, 4);
1017  else
1018  r = ((LibComp*)pc)->createSubNetlist(stream, Collect, 2);
1019  }
1020  if(!r) {
1021  ErrText->insert(
1022  QObject::tr("ERROR: Cannot load library component \"%1\".").
1023  arg(pc->Name));
1024  return false;
1025  }
1026  continue;
1027  }
1028 
1029  // handle SPICE subcircuit components
1030  if(pc->Model == "SPICE") {
1031  s = pc->Props.first()->Value;
1032  if(s.isEmpty()) {
1033  ErrText->insert(QObject::tr("ERROR: No file name in SPICE component \"%1\".").
1034  arg(pc->Name));
1035  return false;
1036  }
1037  QString f = pc->getSubcircuitFile();
1038  SubMap::Iterator it = FileList.find(f);
1039  if(it != FileList.end())
1040  continue; // insert each spice component just one time
1041  FileList.insert(f, SubFile("CIR", f));
1042 
1043  SpiceFile *sf = (SpiceFile*)pc;
1044  r = sf->createSubNetlist(stream);
1045  ErrText->insert(sf->getErrorText());
1046  if(!r) return false;
1047  continue;
1048  }
1049 
1050  // handle digital file subcircuits
1051  if(pc->Model == "VHDL" || pc->Model == "Verilog") {
1052  if(isVerilog && pc->Model == "VHDL")
1053  continue;
1054  if(!isVerilog && pc->Model == "Verilog")
1055  continue;
1056  s = pc->Props.getFirst()->Value;
1057  if(s.isEmpty()) {
1058  ErrText->insert(QObject::tr("ERROR: No file name in %1 component \"%2\".").
1059  arg(pc->Model).
1060  arg(pc->Name));
1061  return false;
1062  }
1063  QString f = pc->getSubcircuitFile();
1064  SubMap::Iterator it = FileList.find(f);
1065  if(it != FileList.end())
1066  continue; // insert each vhdl/verilog component just one time
1067  s = ((pc->Model == "VHDL") ? "VHD" : "VER");
1068  FileList.insert(f, SubFile(s, f));
1069 
1070  if(pc->Model == "VHDL") {
1071  VHDL_File *vf = (VHDL_File*)pc;
1072  r = vf->createSubNetlist(stream);
1073  ErrText->insert(vf->getErrorText());
1074  if(!r) return false;
1075  }
1076  if(pc->Model == "Verilog") {
1077  Verilog_File *vf = (Verilog_File*)pc;
1078  r = vf->createSubNetlist(stream);
1079  ErrText->insert(vf->getErrorText());
1080  if(!r) return false;
1081  }
1082  continue;
1083  }
1084  }
1085  return true;
1086 }
1087 
1088 // ---------------------------------------------------
1089 // Follows the wire lines in order to determine the node names for
1090 // each component. Output into "stream", NodeSets are collected in
1091 // "Collect" and counted with "countInit".
1092 bool Schematic::giveNodeNames(QTextStream *stream, int& countInit,
1093  QStringList& Collect, QTextEdit *ErrText, int NumPorts)
1094 {
1095  // delete the node names
1096  for(Node *pn = DocNodes.first(); pn != 0; pn = DocNodes.next()) {
1097  pn->State = 0;
1098  if(pn->Label) {
1099  if(isAnalog)
1100  pn->Name = pn->Label->Name;
1101  else
1102  pn->Name = "net" + pn->Label->Name;
1103  }
1104  else pn->Name = "";
1105  }
1106 
1107  // set the wire names to the connected node
1108  for(Wire *pw = DocWires.first(); pw != 0; pw = DocWires.next())
1109  if(pw->Label != 0) {
1110  if(isAnalog)
1111  pw->Port1->Name = pw->Label->Name;
1112  else // avoid to use reserved VHDL words
1113  pw->Port1->Name = "net" + pw->Label->Name;
1114  }
1115 
1116  // go through components
1117  if(!throughAllComps(stream, countInit, Collect, ErrText, NumPorts))
1118  return false;
1119 
1120  // work on named nodes first in order to preserve the user given names
1121  throughAllNodes(true, Collect, countInit);
1122 
1123  // give names to the remaining (unnamed) nodes
1124  throughAllNodes(false, Collect, countInit);
1125 
1126  if(!isAnalog) // collect all node names for VHDL signal declaration
1127  collectDigitalSignals();
1128 
1129  return true;
1130 }
1131 
1132 // ---------------------------------------------------
1133 bool Schematic::createLibNetlist(QTextStream *stream, QTextEdit *ErrText,
1134  int NumPorts)
1135 {
1136  int countInit = 0;
1137  QStringList Collect;
1138  Collect.clear();
1139  FileList.clear();
1140  Signals.clear();
1141 
1142  // Apply node names and collect subcircuits and file include
1143  creatingLib = true;
1144  if(!giveNodeNames(stream, countInit, Collect, ErrText, NumPorts)) {
1145  creatingLib = false;
1146  return false;
1147  }
1148  creatingLib = false;
1149 
1150  // Marking start of actual top-level subcircuit
1151  QString c;
1152  if(!isAnalog) {
1153  if (isVerilog)
1154  c = "///";
1155  else
1156  c = "---";
1157  }
1158  else c = "###";
1159  (*stream) << "\n" << c << " TOP LEVEL MARK " << c << "\n";
1160 
1161  // Emit subcircuit components
1162  createSubNetlistPlain(stream, ErrText, NumPorts);
1163 
1164  Signals.clear(); // was filled in "giveNodeNames()"
1165  return true;
1166 }
1167 
1168 //#define VHDL_SIGNAL_TYPE "bit"
1169 //#define VHDL_LIBRARIES ""
1170 #define VHDL_SIGNAL_TYPE "std_logic"
1171 #define VHDL_LIBRARIES "\nlibrary ieee;\nuse ieee.std_logic_1164.all;\n"
1172 
1173 // ---------------------------------------------------
1174 void Schematic::createSubNetlistPlain(QTextStream *stream, QTextEdit *ErrText,
1175  int NumPorts)
1176 {
1177  int i, z;
1178  QString s;
1179  QStringList SubcircuitPortNames;
1180  QStringList SubcircuitPortTypes;
1181  QStringList InPorts;
1182  QStringList OutPorts;
1183  QStringList InOutPorts;
1184  QStringList::Iterator it_name;
1185  QStringList::Iterator it_type;
1186  Component *pc;
1187 
1188  // probably creating a library currently
1189  QTextStream * tstream = stream;
1190  QFile ofile;
1191  if(creatingLib) {
1192  QString f = properAbsFileName(DocName) + ".lst";
1193  ofile.setName(f);
1194  if(!ofile.open(IO_WriteOnly)) {
1195  ErrText->insert(tr("ERROR: Cannot create library file \"%s\".").arg(f));
1196  return;
1197  }
1198  tstream = new QTextStream(&ofile);
1199  }
1200 
1201  // collect subcircuit ports and sort their node names into
1202  // "SubcircuitPortNames"
1203  PortTypes.clear();
1204  for(pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1205  if(pc->Model.at(0) == '.') { // no simulations in subcircuits
1206  ErrText->insert(
1207  QObject::tr("WARNING: Ignore simulation component in subcircuit \"%1\".").arg(DocName)+"\n");
1208  continue;
1209  }
1210  else if(pc->Model == "Port") {
1211  i = pc->Props.first()->Value.toInt();
1212  for(z=SubcircuitPortNames.size(); z<i; z++) { // add empty port names
1213  SubcircuitPortNames.append(" ");
1214  SubcircuitPortTypes.append(" ");
1215  }
1216  it_name = SubcircuitPortNames.at(i-1);
1217  it_type = SubcircuitPortTypes.at(i-1);
1218  (*it_name) = pc->Ports.getFirst()->Connection->Name;
1219  DigMap::Iterator it = Signals.find(*it_name);
1220  (*it_type) = it.data().Type;
1221  // propagate type to port symbol
1222  pc->Ports.getFirst()->Connection->DType = *it_type;
1223 
1224  if(!isAnalog) {
1225  if (isVerilog) {
1226  Signals.erase(*it_name); // remove node name
1227  switch(pc->Props.at(1)->Value.at(0).latin1()) {
1228  case 'a':
1229  InOutPorts.append(*it_name);
1230  break;
1231  case 'o':
1232  OutPorts.append(*it_name);
1233  break;
1234  default:
1235  InPorts.append(*it_name);
1236  }
1237  }
1238  else {
1239  // remove node name of output port
1240  Signals.erase(*it_name);
1241  switch(pc->Props.at(1)->Value.at(0).latin1()) {
1242  case 'a':
1243  (*it_name) += " : inout"; // attribute "analog" is "inout"
1244  break;
1245  case 'o': // output ports need workaround
1246  Signals.insert(*it_name, DigSignal(*it_name, *it_type));
1247  (*it_name) = "net_out" + (*it_name);
1248  // no "break;" here !!!
1249  default:
1250  (*it_name) += " : " + pc->Props.at(1)->Value;
1251  }
1252  (*it_name) += " " + ((*it_type).isEmpty() ?
1253  VHDL_SIGNAL_TYPE : (*it_type));
1254  }
1255  }
1256  }
1257  }
1258 
1259  // remove empty subcircuit ports (missing port numbers)
1260  for(it_name = SubcircuitPortNames.begin(),
1261  it_type = SubcircuitPortTypes.begin();
1262  it_name != SubcircuitPortNames.end(); ) {
1263  if(*it_name == " ") {
1264  it_name = SubcircuitPortNames.remove(it_name);
1265  it_type = SubcircuitPortTypes.remove(it_type);
1266  } else {
1267  PortTypes.append(*it_type);
1268  it_name++;
1269  it_type++;
1270  }
1271  }
1272 
1273  QString f = properFileName(DocName);
1274  QString Type = properName(f);
1275 
1276  Painting *pi;
1277  if(isAnalog) {
1278  // ..... analog subcircuit ...................................
1279  (*tstream) << "\n.Def:" << Type << " " << SubcircuitPortNames.join(" ");
1280  for(pi = SymbolPaints.first(); pi != 0; pi = SymbolPaints.next())
1281  if(pi->Name == ".ID ") {
1282  SubParameter *pp;
1283  ID_Text *pid = (ID_Text*)pi;
1284  for(pp = pid->Parameter.first(); pp != 0; pp = pid->Parameter.next()) {
1285  s = pp->Name; // keep 'Name' unchanged
1286  (*tstream) << " " << s.replace("=", "=\"") << '"';
1287  }
1288  break;
1289  }
1290  (*tstream) << '\n';
1291 
1292  // write all components with node names into netlist file
1293  for(pc = DocComps.first(); pc != 0; pc = DocComps.next())
1294  (*tstream) << pc->getNetlist();
1295 
1296  (*tstream) << ".Def:End\n";
1297 
1298  }
1299  else {
1300  if (isVerilog) {
1301  // ..... digital subcircuit ...................................
1302  (*tstream) << "\nmodule Sub_" << Type << " ("
1303  << SubcircuitPortNames.join(", ") << ");\n";
1304 
1305  // subcircuit in/out connections
1306  if(!InPorts.isEmpty())
1307  (*tstream) << " input " << InPorts.join(", ") << ";\n";
1308  if(!OutPorts.isEmpty())
1309  (*tstream) << " output " << OutPorts.join(", ") << ";\n";
1310  if(!InOutPorts.isEmpty())
1311  (*tstream) << " inout " << InOutPorts.join(", ") << ";\n";
1312 
1313  // subcircuit connections
1314  if(!Signals.isEmpty()) {
1315  QValueList<DigSignal> values = Signals.values();
1316  QValueList<DigSignal>::iterator it;
1317  for (it = values.begin(); it != values.end(); ++it) {
1318  (*tstream) << " wire " << (*it).Name << ";\n";
1319  }
1320  }
1321  (*tstream) << "\n";
1322 
1323  // subcircuit parameters
1324  for(pi = SymbolPaints.first(); pi != 0; pi = SymbolPaints.next())
1325  if(pi->Name == ".ID ") {
1326  SubParameter *pp;
1327  ID_Text *pid = (ID_Text*)pi;
1328  if(pid->Parameter.first()) {
1329  for(pp = pid->Parameter.first(); pp != 0;) {
1330  s = pp->Name.section('=', 0,0);
1331  QString v = Verilog_Param(pp->Name.section('=', 1,1));
1332  (*tstream) << " parameter " << s << " = " << v << ";\n";
1333  pp = pid->Parameter.next();
1334  }
1335  (*tstream) << "\n";
1336  }
1337  break;
1338  }
1339 
1340  // write all equations into netlist file
1341  for(pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1342  if(pc->Model == "Eqn") {
1343  (*tstream) << pc->get_Verilog_Code(NumPorts);
1344  }
1345  }
1346 
1347  if(Signals.find("gnd") != Signals.end())
1348  (*tstream) << " assign gnd = 0;\n"; // should appear only once
1349 
1350  // write all components into netlist file
1351  for(pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1352  if(pc->Model != "Eqn") {
1353  s = pc->get_Verilog_Code(NumPorts);
1354  if(s.at(0) == '§') {
1355  ErrText->insert(s.mid(1));
1356  }
1357  else (*tstream) << s;
1358  }
1359  }
1360 
1361  (*tstream) << "endmodule\n";
1362  } else {
1363  // ..... digital subcircuit ...................................
1364  (*tstream) << VHDL_LIBRARIES;
1365  (*tstream) << "entity Sub_" << Type << " is\n"
1366  << " port ("
1367  << SubcircuitPortNames.join(";\n ") << ");\n";
1368 
1369  for(pi = SymbolPaints.first(); pi != 0; pi = SymbolPaints.next())
1370  if(pi->Name == ".ID ") {
1371  SubParameter *pp;
1372  ID_Text *pid = (ID_Text*)pi;
1373  if(pid->Parameter.first()) {
1374  (*tstream) << " generic (";
1375  for(pp = pid->Parameter.first(); pp != 0;) {
1376  s = pp->Name;
1377  QString t = pp->Type.isEmpty() ? "real" : pp->Type;
1378  (*tstream) << s.replace("=", " : "+t+" := ");
1379  pp = pid->Parameter.next();
1380  if(pp) (*tstream) << ";\n ";
1381  }
1382  (*tstream) << ");\n";
1383  }
1384  break;
1385  }
1386 
1387  (*tstream) << "end entity;\n"
1388  << "use work.all;\n"
1389  << "architecture Arch_Sub_" << Type << " of Sub_" << Type
1390  << " is\n";
1391 
1392  if(!Signals.isEmpty()) {
1393  QValueList<DigSignal> values = Signals.values();
1394  QValueList<DigSignal>::iterator it;
1395  for (it = values.begin(); it != values.end(); ++it) {
1396  (*tstream) << " signal " << (*it).Name << " : "
1397  << ((*it).Type.isEmpty() ?
1398  VHDL_SIGNAL_TYPE : (*it).Type) << ";\n";
1399  }
1400  }
1401 
1402  // write all equations into netlist file
1403  for(pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1404  if(pc->Model == "Eqn") {
1405  ErrText->insert(
1406  QObject::tr("WARNING: Equations in \"%1\" are 'time' typed.").
1407  arg(pc->Name));
1408  (*tstream) << pc->get_VHDL_Code(NumPorts);
1409  }
1410  }
1411 
1412  (*tstream) << "begin\n";
1413 
1414  if(Signals.find("gnd") != Signals.end())
1415  (*tstream) << " gnd <= '0';\n"; // should appear only once
1416 
1417  // write all components into netlist file
1418  for(pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1419  if(pc->Model != "Eqn") {
1420  s = pc->get_VHDL_Code(NumPorts);
1421  if(s.at(0) == '§') {
1422  ErrText->insert(s.mid(1));
1423  }
1424  else (*tstream) << s;
1425  }
1426  }
1427 
1428  (*tstream) << "end architecture;\n";
1429  }
1430  }
1431 
1432  // close file
1433  if(creatingLib) {
1434  ofile.close();
1435  delete tstream;
1436  }
1437 }
1438 
1439 // ---------------------------------------------------
1440 // Write the netlist as subcircuit to the text stream 'stream'.
1441 bool Schematic::createSubNetlist(QTextStream *stream, int& countInit,
1442  QStringList& Collect, QTextEdit *ErrText, int NumPorts)
1443 {
1444 // int Collect_count = Collect.count(); // position for this subcircuit
1445 
1446  // TODO: NodeSets have to be put into the subcircuit block.
1447  if(!giveNodeNames(stream, countInit, Collect, ErrText, NumPorts))
1448  return false;
1449 
1450 /* Example for TODO
1451  for(it = Collect.at(Collect_count); it != Collect.end(); )
1452  if((*it).left(4) == "use ") { // output all subcircuit uses
1453  (*stream) << (*it);
1454  it = Collect.remove(it);
1455  }
1456  else it++;*/
1457 
1458  // Emit subcircuit components
1459  createSubNetlistPlain(stream, ErrText, NumPorts);
1460 
1461  Signals.clear(); // was filled in "giveNodeNames()"
1462  return true;
1463 }
1464 
1465 // ---------------------------------------------------
1466 // Determines the node names and writes subcircuits into netlist file.
1467 int Schematic::prepareNetlist(QTextStream& stream, QStringList& Collect,
1468  QTextEdit *ErrText)
1469 {
1470  if(showBias > 0) showBias = -1; // do not show DC bias anymore
1471 
1472  isVerilog = false;
1473  isAnalog = true;
1474  bool isTruthTable = false;
1475  int allTypes = 0, NumPorts = 0;
1476 
1477  // Detect simulation domain (analog/digital) by looking at component types.
1478  for(Component *pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1479  if(pc->isActive == COMP_IS_OPEN) continue;
1480  if(pc->Model.at(0) == '.') {
1481  if(pc->Model == ".Digi") {
1482  if(allTypes & isDigitalComponent) {
1483  ErrText->insert(
1484  QObject::tr("ERROR: Only one digital simulation allowed."));
1485  return -10;
1486  }
1487  if(pc->Props.getFirst()->Value != "TimeList")
1488  isTruthTable = true;
1489  if(pc->Props.getLast()->Value != "VHDL")
1490  isVerilog = true;
1491  allTypes |= isDigitalComponent;
1492  isAnalog = false;
1493  }
1494  else allTypes |= isAnalogComponent;
1495 
1496  if((allTypes & isComponent) == isComponent) {
1497  ErrText->insert(
1498  QObject::tr("ERROR: Analog and digital simulations cannot be mixed."));
1499  return -10;
1500  }
1501  }
1502  else if(pc->Model == "DigiSource") NumPorts++;
1503  }
1504 
1505  if((allTypes & isAnalogComponent) == 0) {
1506  if(allTypes == 0) {
1507  // If no simulation exists, assume analog simulation. There may
1508  // be a simulation within a SPICE file. Otherwise Qucsator will
1509  // output an error.
1510  isAnalog = true;
1511  allTypes |= isAnalogComponent;
1512  NumPorts = -1;
1513  }
1514  else {
1515  if(NumPorts < 1 && isTruthTable) {
1516  ErrText->insert(
1517  QObject::tr("ERROR: Digital simulation needs at least one digital source."));
1518  return -10;
1519  }
1520  if(!isTruthTable) NumPorts = 0;
1521  }
1522  }
1523  else {
1524  NumPorts = -1;
1525  isAnalog = true;
1526  }
1527 
1528  // first line is documentation
1529  if(allTypes & isAnalogComponent)
1530  stream << "#";
1531  else if (isVerilog)
1532  stream << "//";
1533  else
1534  stream << "--";
1535  stream << " Qucs " << PACKAGE_VERSION << " " << DocName << "\n";
1536 
1537  // set timescale property for verilog schematics
1538  if (isVerilog) {
1539  stream << "\n`timescale 1ps/100fs\n";
1540  }
1541 
1542  int countInit = 0; // counts the nodesets to give them unique names
1543  if(!giveNodeNames(&stream, countInit, Collect, ErrText, NumPorts))
1544  return -10;
1545 
1546  if(allTypes & isAnalogComponent)
1547  return NumPorts;
1548 
1549  if (!isVerilog) {
1550  stream << VHDL_LIBRARIES;
1551  stream << "entity TestBench is\n"
1552  << "end entity;\n"
1553  << "use work.all;\n";
1554  }
1555  return NumPorts;
1556 }
1557 
1558 // ---------------------------------------------------
1559 // Write the beginning of digital netlist to the text stream 'stream'.
1560 void Schematic::beginNetlistDigital(QTextStream& stream)
1561 {
1562  if (isVerilog) {
1563  stream << "module TestBench ();\n";
1564  QValueList<DigSignal> values = Signals.values();
1565  QValueList<DigSignal>::iterator it;
1566  for (it = values.begin(); it != values.end(); ++it) {
1567  stream << " wire " << (*it).Name << ";\n";
1568  }
1569  stream << "\n";
1570  } else {
1571  stream << "architecture Arch_TestBench of TestBench is\n";
1572  QValueList<DigSignal> values = Signals.values();
1573  QValueList<DigSignal>::iterator it;
1574  for (it = values.begin(); it != values.end(); ++it) {
1575  stream << " signal " << (*it).Name << " : "
1576  << ((*it).Type.isEmpty() ?
1577  VHDL_SIGNAL_TYPE : (*it).Type) << ";\n";
1578  }
1579  stream << "begin\n";
1580  }
1581 
1582  if(Signals.find("gnd") != Signals.end()) {
1583  if (isVerilog) {
1584  stream << " assign gnd = 0;\n";
1585  } else {
1586  stream << " gnd <= '0';\n"; // should appear only once
1587  }
1588  }
1589 }
1590 
1591 // ---------------------------------------------------
1592 // Write the end of digital netlist to the text stream 'stream'.
1593 void Schematic::endNetlistDigital(QTextStream& stream)
1594 {
1595  if (isVerilog) {
1596  } else {
1597  stream << "end architecture;\n";
1598  }
1599 }
1600 
1601 // ---------------------------------------------------
1602 // write all components with node names into the netlist file
1603 QString Schematic::createNetlist(QTextStream& stream, int NumPorts)
1604 {
1605  if(!isAnalog) {
1606  beginNetlistDigital(stream);
1607  }
1608 
1609  Signals.clear(); // was filled in "giveNodeNames()"
1610  FileList.clear();
1611 
1612  QString s, Time;
1613  for(Component *pc = DocComps.first(); pc != 0; pc = DocComps.next()) {
1614  if(isAnalog) {
1615  s = pc->getNetlist();
1616  }
1617  else {
1618  if(pc->Model == ".Digi" && pc->isActive) { // simulation component ?
1619  if(NumPorts > 0) { // truth table simulation ?
1620  if (isVerilog)
1621  Time = QString::number((1 << NumPorts));
1622  else
1623  Time = QString::number((1 << NumPorts) - 1) + " ns";
1624  } else {
1625  Time = pc->Props.at(1)->Value;
1626  if (isVerilog) {
1627  if(!Verilog_Time(Time, pc->Name)) return Time;
1628  } else {
1629  if(!VHDL_Time(Time, pc->Name)) return Time; // wrong time format
1630  }
1631  }
1632  }
1633  if (isVerilog) {
1634  s = pc->get_Verilog_Code(NumPorts);
1635  } else {
1636  s = pc->get_VHDL_Code(NumPorts);
1637  }
1638  if(s.at(0) == '§') return s; // return error
1639  }
1640  stream << s;
1641  }
1642 
1643  if(!isAnalog) {
1644  endNetlistDigital(stream);
1645  }
1646 
1647  return Time;
1648 }