-
Notifications
You must be signed in to change notification settings - Fork 549
/
V3LinkDot.cpp
3174 lines (3098 loc) · 157 KB
/
V3LinkDot.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Resolve module/signal name references
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// LinkDot TRANSFORMATIONS:
// Top-down traversal in LinkDotFindVisitor
// Cells:
// Make graph of cell hierarchy
// Var/Funcs's:
// Collect all names into symtable under appropriate cell
// Top-down traversal in LinkDotScopeVisitor
// Find VarScope versions of signals (well past original link)
// Top-down traversal in LinkDotParamVisitor
// Create implicit signals
// Top-down traversal in LinkDotResolveVisitor
// VarXRef/Func's:
// Find appropriate named cell and link to var they reference
//*************************************************************************
// Interfaces:
// CELL (.port (ifref)
// ^--- cell -> IfaceDTypeRef(iface)
// ^--- cell.modport -> IfaceDTypeRef(iface,modport)
// ^--- varref(input_ifref) -> IfaceDTypeRef(iface)
// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport)
// FindVisitor:
// #1: Insert interface Vars
// #2: Insert ModPort names
// IfaceVisitor:
// #3: Update ModPortVarRef to point at interface vars (after #1)
// #4: Create ModPortVarRef symbol table entries
// FindVisitor-insertIfaceRefs()
// #5: Resolve IfaceRefDtype modport names (after #2)
// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5)
// insertAllScopeAliases():
// #8: Insert modport's symbols under IfaceRefDType (after #7)
// ResolveVisitor:
// #9: Resolve general variables, which may point into the interface or modport (after #8)
// LinkResolve:
// #10: Unlink modports, not needed later except for XML/Lint
//*************************************************************************
// TOP
// {name-of-top-modulename}
// a (VSymEnt->AstCell)
// {name-of-cell}
// {name-of-cell-module}
// aa (VSymEnt->AstCell)
// var (AstVar) -- no sub symbol table needed
// beg (VSymEnt->AstBegin) -- can see "upper" a's symbol table
// a__DOT__aa (VSymEnt->AstCellInline) -- points to a.aa's symbol table
// b (VSymEnt->AstCell)
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3LinkDot.h"
#include "V3SymTable.h"
#include "V3Graph.h"
#include "V3Ast.h"
#include "V3String.h"
#include <algorithm>
#include <map>
#include <vector>
//######################################################################
// Matcher classes (for suggestion matching)
class LinkNodeMatcherClass final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Class); }
};
class LinkNodeMatcherFTask final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, NodeFTask); }
};
class LinkNodeMatcherModport final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Modport); }
};
class LinkNodeMatcherVar final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override {
return VN_IS(nodep, Var) || VN_IS(nodep, LambdaArgRef);
}
};
class LinkNodeMatcherVarIO final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override {
const AstVar* const varp = VN_CAST(nodep, Var);
if (!varp) return false;
return varp->isIO();
}
};
class LinkNodeMatcherVarParam final : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override {
const AstVar* const varp = VN_CAST(nodep, Var);
if (!varp) return false;
return varp->isParam();
}
};
//######################################################################
// LinkDot state, as a visitor of each AstNode
class LinkDotState final {
private:
// NODE STATE
// Cleared on Netlist
// AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node
// AstNodeModule::user2() // bool. Currently processing for recursion check
// ... Note maybe more than one, as can be multiple hierarchy places
// AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal
// AstVar::user2p() // AstFTask*. If a function variable, the task
// that links to the variable
// AstVar::user4() // bool. True if port set for this variable
// AstNodeBlock::user4() // bool. Did name processing
// AstNodeModule::user4() // bool. Live module
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser4InUse m_inuser4;
public:
// ENUMS
// In order of priority, compute first ... compute last
enum SAMNum : uint8_t { SAMN_MODPORT, SAMN_IFTOP, SAMN__MAX }; // Values for m_scopeAliasMap
private:
// TYPES
using ScopeAliasMap = std::unordered_map<VSymEnt*, VSymEnt*>;
using IfaceModSyms = std::vector<std::pair<AstIface*, VSymEnt*>>;
static LinkDotState* s_errorThisp; // Last self, for error reporting only
// MEMBERS
VSymGraph m_syms; // Symbol table
VSymEnt* m_dunitEntp = nullptr; // $unit entry
std::multimap<std::string, VSymEnt*>
m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name
std::set<std::pair<AstNodeModule*, std::string>>
m_implicitNameSet; // For [module][signalname] if we can implicitly create it
std::array<ScopeAliasMap, SAMN__MAX> m_scopeAliasMap; // Map of <lhs,rhs> aliases
std::vector<VSymEnt*> m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported
IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed
bool m_forPrimary; // First link
bool m_forPrearray; // Compress cell__[array] refs
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
public:
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void dump(const string& nameComment = "linkdot", bool force = false) {
if (debug() >= 6 || force) {
const string filename = v3Global.debugFilename(nameComment) + ".txt";
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
if (logp->fail()) v3fatal("Can't write " << filename);
std::ostream& os = *logp;
m_syms.dump(os);
bool first = true;
for (int samn = 0; samn < SAMN__MAX; ++samn) {
if (!m_scopeAliasMap[samn].empty()) {
if (first) os << "\nScopeAliasMap:\n";
first = false;
for (ScopeAliasMap::iterator it = m_scopeAliasMap[samn].begin();
it != m_scopeAliasMap[samn].end(); ++it) {
// left side is what we will import into
os << "\t" << samn << "\t" << it->first << " ("
<< it->first->nodep()->typeName() << ") <- " << it->second << " "
<< it->second->nodep() << '\n';
}
}
}
}
}
static void preErrorDumpHandler() {
if (s_errorThisp) s_errorThisp->preErrorDump();
}
void preErrorDump() {
static bool diddump = false;
if (!diddump && v3Global.opt.dumpTree()) {
diddump = true;
dump("linkdot-preerr", true);
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree"));
}
}
// CONSTRUCTORS
LinkDotState(AstNetlist* rootp, VLinkDotStep step)
: m_syms{rootp} {
UINFO(4, __FUNCTION__ << ": " << endl);
m_forPrimary = (step == LDS_PRIMARY);
m_forPrearray = (step == LDS_PARAMED || step == LDS_PRIMARY);
m_forScopeCreation = (step == LDS_SCOPED);
s_errorThisp = this;
V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self
}
~LinkDotState() {
V3Error::errorExitCb(nullptr);
s_errorThisp = nullptr;
}
// ACCESSORS
VSymGraph* symsp() { return &m_syms; }
bool forPrimary() const { return m_forPrimary; }
bool forPrearray() const { return m_forPrearray; }
bool forScopeCreation() const { return m_forScopeCreation; }
// METHODS
static string nodeTextType(AstNode* nodep) {
if (VN_IS(nodep, Var)) {
return "variable";
} else if (VN_IS(nodep, Cell)) {
return "instance";
} else if (VN_IS(nodep, Task)) {
return "task";
} else if (VN_IS(nodep, Func)) {
return "function";
} else if (VN_IS(nodep, Begin)) {
return "block";
} else if (VN_IS(nodep, Iface)) {
return "interface";
} else if (VN_IS(nodep, ParamTypeDType)) {
return "parameter type";
} else {
return nodep->prettyTypeName();
}
}
VSymEnt* rootEntp() const { return m_syms.rootp(); }
VSymEnt* dunitEntp() const { return m_dunitEntp; }
void checkDuplicate(VSymEnt* lookupSymp, AstNode* nodep, const string& name) {
// Lookup the given name under current symbol table
// Insert if not found
// Report error if there's a duplicate
//
// Note we only check for conflicts at the same level; it's ok if one block hides another
// We also wouldn't want to not insert it even though it's lower down
const VSymEnt* const foundp = lookupSymp->findIdFlat(name);
AstNode* const fnodep = foundp ? foundp->nodep() : nullptr;
if (!fnodep) {
// Not found, will add in a moment.
} else if (nodep == fnodep) { // Already inserted.
// Good.
} else if (foundp->imported()) { // From package
// We don't throw VARHIDDEN as if the import is later the symbol
// table's import wouldn't warn
} else if (VN_IS(nodep, Begin) && VN_IS(fnodep, Begin)
&& VN_AS(nodep, Begin)->generate()) {
// Begin: ... blocks often replicate under genif/genfor, so simply
// suppress duplicate checks. See t_gen_forif.v for an example.
} else {
UINFO(4, "name " << name << endl); // Not always same as nodep->name
UINFO(4, "Var1 " << nodep << endl);
UINFO(4, "Var2 " << fnodep << endl);
if (nodep->type() == fnodep->type()) {
nodep->v3error("Duplicate declaration of "
<< nodeTextType(fnodep) << ": " << nodep->prettyNameQ() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< fnodep->warnOther() << "... Location of original declaration\n"
<< fnodep->warnContextSecondary());
} else {
nodep->v3error("Unsupported in C: "
<< ucfirst(nodeTextType(nodep)) << " has the same name as "
<< nodeTextType(fnodep) << ": " << nodep->prettyNameQ() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< fnodep->warnOther() << "... Location of original declaration\n"
<< fnodep->warnContextSecondary());
}
}
}
void insertDUnit(AstNetlist* nodep) {
// $unit on top scope
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9, " INSERTdunit se" << cvtToHex(symp) << endl);
symp->parentp(rootEntp()); // Needed so backward search can find name of top module
symp->fallbackp(nullptr);
rootEntp()->insert("$unit ", symp); // Space so can never name conflict with user code
//
UASSERT_OBJ(!m_dunitEntp, nodep, "Call insertDUnit only once");
m_dunitEntp = symp;
}
VSymEnt* insertTopCell(AstNodeModule* nodep, const string& scopename) {
// Only called on the module at the very top of the hierarchy
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9,
" INSERTtop se" << cvtToHex(symp) << " " << scopename << " " << nodep << endl);
symp->parentp(rootEntp()); // Needed so backward search can find name of top module
symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff
nodep->user1p(symp);
checkDuplicate(rootEntp(), nodep, nodep->origName());
rootEntp()->insert(nodep->origName(), symp);
if (forScopeCreation()) m_nameScopeSymMap.emplace(scopename, symp);
return symp;
}
VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp, AstCell* nodep,
const string& scopename) {
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9, " INSERTcel se" << cvtToHex(symp) << " " << scopename << " above=se"
<< cvtToHex(abovep) << " mods=se" << cvtToHex(modSymp)
<< " node=" << nodep << endl);
symp->parentp(abovep);
symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff
nodep->user1p(symp);
if (nodep->modp()) nodep->modp()->user1p(symp);
checkDuplicate(abovep, nodep, nodep->origName());
abovep->reinsert(nodep->origName(), symp);
if (forScopeCreation() && abovep != modSymp && !modSymp->findIdFlat(nodep->name())) {
// If it's foo_DOT_bar, we need to be able to find it under "foo_DOT_bar" too.
// Duplicates are possible, as until resolve generates might
// have 2 same cells under an if
modSymp->reinsert(nodep->name(), symp);
}
if (forScopeCreation()) m_nameScopeSymMap.emplace(scopename, symp);
return symp;
}
void insertMap(VSymEnt* symp, const string& scopename) {
if (forScopeCreation()) m_nameScopeSymMap.emplace(scopename, symp);
}
VSymEnt* insertInline(VSymEnt* abovep, VSymEnt* modSymp, AstCellInline* nodep,
const string& basename) {
// A fake point in the hierarchy, corresponding to an inlined module
// This references to another Sym, and eventually resolves to a module with a prefix
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9, " INSERTinl se" << cvtToHex(symp) << " " << basename << " above=se"
<< cvtToHex(abovep) << " mods=se" << cvtToHex(modSymp)
<< " node=" << nodep << endl);
symp->parentp(abovep);
symp->fallbackp(modSymp);
symp->symPrefix(nodep->name() + "__DOT__");
nodep->user1p(symp);
checkDuplicate(abovep, nodep, nodep->name());
abovep->reinsert(basename, symp);
if (abovep != modSymp && !modSymp->findIdFlat(nodep->name())) {
// If it's foo_DOT_bar, we need to be able to find it under that too.
modSymp->reinsert(nodep->name(), symp);
}
return symp;
}
VSymEnt* insertBlock(VSymEnt* abovep, const string& name, AstNode* nodep,
AstNodeModule* classOrPackagep) {
// A fake point in the hierarchy, corresponding to a begin or function/task block
// After we remove begins these will go away
// Note we fallback to the symbol table of the parent, as we want to find variables there
// However, cells walk the graph, so cells will appear under the begin/ftask itself
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9, " INSERTblk se" << cvtToHex(symp) << " above=se" << cvtToHex(abovep)
<< " pkg=" << cvtToHex(classOrPackagep) << " node=" << nodep
<< endl);
symp->parentp(abovep);
symp->classOrPackagep(classOrPackagep);
symp->fallbackp(abovep);
nodep->user1p(symp);
if (name != "") checkDuplicate(abovep, nodep, name);
// Duplicates are possible, as until resolve generates might have 2 same cells under an if
abovep->reinsert(name, symp);
return symp;
}
VSymEnt* insertSym(VSymEnt* abovep, const string& name, AstNode* nodep,
AstNodeModule* classOrPackagep) {
UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node");
VSymEnt* const symp = new VSymEnt(&m_syms, nodep);
UINFO(9, " INSERTsym se" << cvtToHex(symp) << " name='" << name << "' above=se"
<< cvtToHex(abovep) << " pkg=" << cvtToHex(classOrPackagep)
<< " node=" << nodep << endl);
// We don't remember the ent associated with each node, because we
// need a unique scope entry for each instantiation
symp->classOrPackagep(classOrPackagep);
symp->parentp(abovep);
symp->fallbackp(abovep);
nodep->user1p(symp);
checkDuplicate(abovep, nodep, name);
abovep->reinsert(name, symp);
return symp;
}
static bool existsModScope(AstNodeModule* nodep) { return nodep->user1p() != nullptr; }
static VSymEnt* getNodeSym(AstNode* nodep) {
// Don't use this in ResolveVisitor, as we need to pick up the proper
// reference under each SCOPE
VSymEnt* const symp = nodep->user1u().toSymEnt();
UASSERT_OBJ(symp, nodep, "Module/etc never assigned a symbol entry?");
return symp;
}
VSymEnt* getScopeSym(AstScope* nodep) {
const auto it = m_nameScopeSymMap.find(nodep->name());
UASSERT_OBJ(it != m_nameScopeSymMap.end(), nodep,
"Scope never assigned a symbol entry '" << nodep->name() << "'");
return it->second;
}
void implicitOkAdd(AstNodeModule* nodep, const string& varname) {
// Mark the given variable name as being allowed to be implicitly declared
if (nodep) {
const auto it = m_implicitNameSet.find(std::make_pair(nodep, varname));
if (it == m_implicitNameSet.end()) m_implicitNameSet.emplace(nodep, varname);
}
}
bool implicitOk(AstNodeModule* nodep, const string& varname) {
return nodep
&& (m_implicitNameSet.find(std::make_pair(nodep, varname))
!= m_implicitNameSet.end());
}
// Track and later recurse interface modules
void insertIfaceModSym(AstIface* nodep, VSymEnt* symp) {
m_ifaceModSyms.push_back(std::make_pair(nodep, symp));
}
void computeIfaceModSyms();
// Track and later insert interface references
void insertIfaceVarSym(VSymEnt* symp) { // Where sym is for a VAR of dtype IFACEREFDTYPE
m_ifaceVarSyms.push_back(symp);
}
// Iface for a raw or arrayed iface
static AstIfaceRefDType* ifaceRefFromArray(AstNodeDType* nodep) {
AstIfaceRefDType* ifacerefp = VN_CAST(nodep, IfaceRefDType);
if (!ifacerefp) {
if (const AstBracketArrayDType* const arrp = VN_CAST(nodep, BracketArrayDType)) {
ifacerefp = VN_CAST(arrp->subDTypep(), IfaceRefDType);
} else if (const AstUnpackArrayDType* const arrp = VN_CAST(nodep, UnpackArrayDType)) {
ifacerefp = VN_CAST(arrp->subDTypep(), IfaceRefDType);
}
}
return ifacerefp;
}
void computeIfaceVarSyms() {
for (VSymEnt* varSymp : m_ifaceVarSyms) {
const AstVar* const varp = varSymp ? VN_AS(varSymp->nodep(), Var) : nullptr;
UINFO(9, " insAllIface se" << cvtToHex(varSymp) << " " << varp << endl);
AstIfaceRefDType* const ifacerefp = ifaceRefFromArray(varp->subDTypep());
UASSERT_OBJ(ifacerefp, varp, "Non-ifacerefs on list!");
if (!ifacerefp->ifaceViaCellp()) {
if (!ifacerefp->cellp()) { // Probably a NotFoundModule, or a normal module if
// made mistake
UINFO(1, "Associated cell " << AstNode::prettyNameQ(ifacerefp->cellName())
<< endl);
ifacerefp->v3error("Cannot find file containing interface: "
<< AstNode::prettyNameQ(ifacerefp->ifaceName()));
continue;
} else {
ifacerefp->v3fatalSrc("Unlinked interface");
}
} else if (ifacerefp->ifaceViaCellp()->dead()) {
ifacerefp->v3error("Parent instance's interface is not found: "
<< AstNode::prettyNameQ(ifacerefp->ifaceName()));
continue;
}
VSymEnt* const ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
VSymEnt* ifOrPortSymp = ifaceSymp;
// Link Modport names to the Modport Node under the Interface
if (ifacerefp->isModport()) {
VSymEnt* const foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
bool ok = false;
if (foundp) {
if (AstModport* const modportp = VN_CAST(foundp->nodep(), Modport)) {
UINFO(4, "Link Modport: " << modportp << endl);
ifacerefp->modportp(modportp);
ifOrPortSymp = foundp;
ok = true;
}
}
if (!ok) {
const string suggest = suggestSymFallback(ifaceSymp, ifacerefp->modportName(),
LinkNodeMatcherModport());
ifacerefp->modportFileline()->v3error(
"Modport not found under interface "
<< ifacerefp->prettyNameQ(ifacerefp->ifaceName()) << ": "
<< ifacerefp->prettyNameQ(ifacerefp->modportName()) << '\n'
<< (suggest.empty() ? "" : ifacerefp->warnMore() + suggest));
}
}
// Alias won't expand until interfaces and modport names are known; see notes at top
insertScopeAlias(SAMN_IFTOP, varSymp, ifOrPortSymp);
}
m_ifaceVarSyms.clear();
}
void insertScopeAlias(SAMNum samn, VSymEnt* lhsp, VSymEnt* rhsp) {
// Track and later insert scope aliases; an interface referenced by
// a child cell connecting to that interface
// Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
UINFO(9, " insertScopeAlias se" << cvtToHex(lhsp) << " se" << cvtToHex(rhsp) << endl);
UASSERT_OBJ(
!(VN_IS(rhsp->nodep(), Cell) && !VN_IS(VN_AS(rhsp->nodep(), Cell)->modp(), Iface)),
rhsp->nodep(), "Got a non-IFACE alias RHS");
m_scopeAliasMap[samn].emplace(lhsp, rhsp);
}
void computeScopeAliases() {
UINFO(9, "computeIfaceAliases\n");
for (int samn = 0; samn < SAMN__MAX; ++samn) {
for (ScopeAliasMap::iterator it = m_scopeAliasMap[samn].begin();
it != m_scopeAliasMap[samn].end(); ++it) {
VSymEnt* const lhsp = it->first;
VSymEnt* srcp = lhsp;
while (true) { // Follow chain of aliases up to highest level non-alias
const auto it2 = m_scopeAliasMap[samn].find(srcp);
if (it2 != m_scopeAliasMap[samn].end()) {
srcp = it2->second;
continue;
} else
break;
}
UINFO(9, " iiasa: Insert alias se" << lhsp << " (" << lhsp->nodep()->typeName()
<< ") <- se" << srcp << " " << srcp->nodep()
<< endl);
// srcp should be an interface reference pointing to the interface we want to
// import
lhsp->importFromIface(symsp(), srcp);
// Allow access to objects not permissible to be listed in a modport
if (VN_IS(srcp->nodep(), Modport)) {
lhsp->importFromIface(symsp(), srcp->parentp(), true);
}
}
// m_scopeAliasMap[samn].clear(); // Done with it, but put into debug file
}
}
private:
VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) {
VSymEnt* findp = symp->findIdFallback(name);
if (findp) return findp;
if (altname != "") {
UINFO(8, " alt fallback\n");
findp = symp->findIdFallback(altname);
}
return findp;
}
public:
VSymEnt* findDotted(FileLine* refLocationp, VSymEnt* lookupSymp, const string& dotname,
string& baddot, VSymEnt*& okSymp) {
// Given a dotted hierarchy name, return where in scope it is
// Note when dotname=="" we just fall through and return lookupSymp
UINFO(8, " dottedFind se" << cvtToHex(lookupSymp) << " '" << dotname << "'" << endl);
bool firstId = true;
string leftname = dotname;
okSymp = lookupSymp; // So can list bad scopes
while (leftname != "") { // foreach dotted part of xref name
string::size_type pos;
string ident;
if ((pos = leftname.find('.')) != string::npos) {
ident = leftname.substr(0, pos);
leftname = leftname.substr(pos + 1);
} else {
ident = leftname;
leftname = "";
}
baddot = ident; // So user can see where they botched it
okSymp = lookupSymp;
string altIdent;
if (m_forPrearray) {
// GENFOR Begin is foo__BRA__##__KET__ after we've genloop unrolled,
// but presently should be just "foo".
// Likewise cell foo__[array] before we've expanded arrays is just foo
if ((pos = ident.rfind("__BRA__")) != string::npos) {
altIdent = ident.substr(0, pos);
}
}
UINFO(8, " id " << ident << " alt " << altIdent << " left " << leftname
<< " at se" << lookupSymp << endl);
// Spec says; Look at existing module (cellnames then modname),
// then look up (inst name or modname)
if (firstId) {
// Check this module - subcellnames
const AstCell* cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell)
: nullptr; // Replicated below
const AstCellInline* inlinep = lookupSymp
? VN_CAST(lookupSymp->nodep(), CellInline)
: nullptr; // Replicated below
if (VSymEnt* const findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
}
// Check this module - cur modname
else if ((cellp && cellp->modp()->origName() == ident)
|| (inlinep && inlinep->origModName() == ident)) {
}
// $root we walk up to Netlist
else if (ident == "$root") {
lookupSymp = rootEntp();
// We've added the '$root' module, now everything else is one lower
if (!m_forPrearray) {
lookupSymp = lookupSymp->findIdFlat(ident);
UASSERT(lookupSymp, "Cannot find $root module under netlist");
}
}
// Move up and check cellname + modname
else {
bool crossedCell = false; // Crossed a cell boundary
while (lookupSymp) {
lookupSymp = lookupSymp->parentp();
cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell)
: nullptr; // Replicated above
inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline)
: nullptr; // Replicated above
if (lookupSymp) {
UINFO(9, " Up to " << lookupSymp << endl);
if (cellp || inlinep) crossedCell = true;
if ((cellp && cellp->modp()->origName() == ident)
|| (inlinep && inlinep->origModName() == ident)) {
break;
} else if (VSymEnt* const findSymp
= findWithAltFallback(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
if (crossedCell && VN_IS(lookupSymp->nodep(), Var)) {
UINFO(9, " Not found but matches var name in parent "
<< lookupSymp << endl);
return nullptr;
}
break;
}
} else
break;
}
if (!lookupSymp) return nullptr; // Not found
}
} else { // Searching for middle submodule, must be a cell name
if (VSymEnt* const findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
} else {
return nullptr; // Not found
}
}
if (lookupSymp) {
if (const AstCell* const cellp = VN_CAST(lookupSymp->nodep(), Cell)) {
if (const AstNodeModule* const modp = cellp->modp()) {
if (modp->hierBlock()) {
refLocationp->v3error("Cannot access inside hierarchical block");
} else if (VN_IS(modp, NotFoundModule)) {
refLocationp->v3error("Dotted reference to instance that refers to "
"missing module/interface: "
<< modp->prettyNameQ());
}
}
}
}
firstId = false;
}
return lookupSymp;
}
static string removeLastInlineScope(const string& name) {
string out = name;
const string dot = "__DOT__";
const size_t dotPos = out.rfind(dot, out.size() - dot.length() - 2);
if (dotPos == string::npos) {
return "";
} else {
return out.erase(dotPos + dot.length(), string::npos);
}
}
VSymEnt* findSymPrefixed(VSymEnt* lookupSymp, const string& dotname, string& baddot) {
// Find symbol in given point in hierarchy, allowing prefix (post-Inline)
// For simplicity lookupSymp may be passed nullptr result from findDotted
if (!lookupSymp) return nullptr;
UINFO(8, " findSymPrefixed "
<< dotname << " under se" << cvtToHex(lookupSymp)
<< ((lookupSymp->symPrefix() == "") ? "" : " as ")
<< ((lookupSymp->symPrefix() == "") ? "" : lookupSymp->symPrefix() + dotname)
<< " at se" << lookupSymp << endl);
const string prefix = lookupSymp->symPrefix();
VSymEnt* foundp = nullptr;
while (!foundp) {
foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be nullptr
if (prefix.empty()) break;
const string nextPrefix = removeLastInlineScope(prefix);
if (prefix == nextPrefix) break;
}
if (!foundp) baddot = dotname;
return foundp;
}
string suggestSymFallback(VSymEnt* lookupSymp, const string& name,
const VNodeMatcher& matcher) {
// Suggest alternative symbol in given point in hierarchy
// Does not support inline, as we find user-level errors before inlining
// For simplicity lookupSymp may be passed nullptr result from findDotted
if (!lookupSymp) return "";
VSpellCheck speller;
lookupSymp->candidateIdFallback(&speller, &matcher);
return speller.bestCandidateMsg(name);
}
string suggestSymFlat(VSymEnt* lookupSymp, const string& name, const VNodeMatcher& matcher) {
if (!lookupSymp) return "";
VSpellCheck speller;
lookupSymp->candidateIdFlat(&speller, &matcher);
return speller.bestCandidateMsg(name);
}
};
LinkDotState* LinkDotState::s_errorThisp = nullptr;
//======================================================================
class LinkDotFindVisitor final : public VNVisitor {
// STATE
LinkDotState* const m_statep; // State to pass between visitors, including symbol table
AstNodeModule* m_classOrPackagep = nullptr; // Current package
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
string m_scope; // Scope text
const AstNodeBlock* m_blockp = nullptr; // Current Begin/end block
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
bool m_inRecursion = false; // Inside a recursive module
int m_paramNum = 0; // Parameter number, for position based connection
bool m_explicitNew = false; // Hit a "new" function
int m_modBlockNum = 0; // Begin block number in module, 0=none seen
int m_modWithNum = 0; // With block number, 0=none seen
// METHODS
static int debug() { return LinkDotState::debug(); }
void makeImplicitNew(AstClass* nodep) {
AstFunc* const newp = new AstFunc(nodep->fileline(), "new", nullptr, nullptr);
newp->isConstructor(true);
nodep->addMembersp(newp);
UINFO(8, "Made implicit new for " << nodep->name() << ": " << nodep << endl);
m_statep->insertBlock(m_curSymp, newp->name(), newp, m_classOrPackagep);
}
bool isHierBlockWrapper(const string& name) const {
const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
return hierBlocks.find(name) != hierBlocks.end();
}
// VISITs
virtual void visit(AstNetlist* nodep) override {
// Process $unit or other packages
// Not needed - dotted references not allowed from inside packages
// for (AstNodeModule* nodep = v3Global.rootp()->modulesp();
// nodep; nodep=VN_AS(nodep->nextp(), NodeModule)) {
// if (VN_IS(nodep, Package)) {}}
m_statep->insertDUnit(nodep);
// First back iterate, to find all packages. Backward as must do base
// packages before using packages
iterateChildrenBackwards(nodep);
// The first modules in the list are always the top modules
// (sorted before this is called).
// This may not be the module with isTop() set, as early in the steps,
// wrapTop may have not been created yet.
if (!nodep->modulesp()) nodep->v3error("No top level module found");
for (AstNodeModule* modp = nodep->modulesp(); modp && modp->level() <= 2;
modp = VN_AS(modp->nextp(), NodeModule)) {
UINFO(8, "Top Module: " << modp << endl);
m_scope = "TOP";
m_curSymp = m_modSymp = m_statep->insertTopCell(modp, m_scope);
{ iterate(modp); }
m_scope = "";
m_curSymp = m_modSymp = nullptr;
}
}
virtual void visit(AstTypeTable*) override {}
virtual void visit(AstConstPool*) override {}
virtual void visit(AstNodeModule* nodep) override {
// Called on top module from Netlist, other modules from the cell creating them,
// and packages
UINFO(8, " " << nodep << endl);
// m_curSymp/m_modSymp maybe nullptr for packages and non-top modules
// Packages will be under top after the initial phases, but until then
// need separate handling
const bool standalonePkg
= !m_modSymp && (m_statep->forPrearray() && VN_IS(nodep, Package));
const bool doit = (m_modSymp || standalonePkg);
VL_RESTORER(m_scope);
VL_RESTORER(m_classOrPackagep);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
if (doit && nodep->user2()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Identically recursive module (module instantiates "
"itself, without changing parameters): "
<< AstNode::prettyNameQ(nodep->origName()));
} else if (doit) {
UINFO(4, " Link Module: " << nodep << endl);
UASSERT_OBJ(!nodep->dead(), nodep, "Module in instance tree mislabeled as dead?");
VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
AstPackage* const pkgp = VN_CAST(nodep, Package);
m_classOrPackagep = pkgp;
if (standalonePkg) {
if (pkgp->isDollarUnit()) {
m_curSymp = m_modSymp = m_statep->dunitEntp();
nodep->user1p(m_curSymp);
} else {
m_scope = nodep->name();
m_curSymp = m_modSymp = m_statep->insertBlock(
upperSymp, nodep->name() + "::", nodep, m_classOrPackagep);
UINFO(9, "New module scope " << m_curSymp << endl);
}
}
//
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
nodep->user2(true);
iterateChildren(nodep);
nodep->user2(false);
nodep->user4(true);
// Interfaces need another pass when signals are resolved
if (AstIface* const ifacep = VN_CAST(nodep, Iface)) {
m_statep->insertIfaceModSym(ifacep, m_curSymp);
}
} else if (isHierBlockWrapper(nodep->name())) {
UINFO(5, "Module is hierarchical block, must not be dead: " << nodep << endl);
m_scope = nodep->name();
VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
m_curSymp = m_modSymp
= m_statep->insertBlock(upperSymp, nodep->name() + "::", nodep, m_classOrPackagep);
iterateChildren(nodep);
nodep->user4(true);
} else { // !doit
// Will be optimized away later
// Can't remove now, as our backwards iterator will throw up
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
}
}
virtual void visit(AstClass* nodep) override {
UASSERT_OBJ(m_curSymp, nodep, "Class not under module/package/$unit");
UINFO(8, " " << nodep << endl);
VL_RESTORER(m_scope);
VL_RESTORER(m_classOrPackagep);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
{
UINFO(4, " Link Class: " << nodep << endl);
VSymEnt* const upperSymp = m_curSymp;
m_scope = m_scope + "." + nodep->name();
m_classOrPackagep = nodep;
m_curSymp = m_modSymp
= m_statep->insertBlock(upperSymp, nodep->name(), nodep, m_classOrPackagep);
m_statep->insertMap(m_curSymp, m_scope);
UINFO(9, "New module scope " << m_curSymp << endl);
//
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
m_explicitNew = false;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
iterateChildren(nodep);
nodep->user4(true);
// Implicit new needed?
if (!m_explicitNew && m_statep->forPrimary()) makeImplicitNew(nodep);
}
}
virtual void visit(AstScope* nodep) override {
UASSERT_OBJ(m_statep->forScopeCreation(), nodep,
"Scopes should only exist right after V3Scope");
// Ignored. Processed in next step
}
virtual void visit(AstCell* nodep) override {
UINFO(5, " CELL under " << m_scope << " is " << nodep << endl);
// Process XREFs/etc inside pins
if (nodep->recursive() && m_inRecursion) return;
iterateChildren(nodep);
// Recurse in, preserving state
VL_RESTORER(m_scope);
VL_RESTORER(m_blockp);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_paramNum);
VL_RESTORER(m_inRecursion);
// Where do we add it?
VSymEnt* aboveSymp = m_curSymp;
const string origname = AstNode::dedotName(nodep->name());
string::size_type pos;
if ((pos = origname.rfind('.')) != string::npos) {
// Flattened, find what CellInline it should live under
const string scope = origname.substr(0, pos);
string baddot;
VSymEnt* okSymp;
aboveSymp = m_statep->findDotted(nodep->fileline(), aboveSymp, scope, baddot, okSymp);
UASSERT_OBJ(aboveSymp, nodep,
"Can't find instance insertion point at "
<< AstNode::prettyNameQ(baddot) << " in: " << nodep->prettyNameQ());
}
{
m_scope = m_scope + "." + nodep->name();
m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope);
m_blockp = nullptr;
m_inRecursion = nodep->recursive();
// We don't report NotFoundModule, as may be a unused module in a generate
if (nodep->modp()) iterate(nodep->modp());
}
}
virtual void visit(AstCellInline* nodep) override {
UINFO(5, " CELLINLINE under " << m_scope << " is " << nodep << endl);
VSymEnt* aboveSymp = m_curSymp;
// If baz__DOT__foo__DOT__bar, we need to find baz__DOT__foo and add bar to it.
const string dottedname = nodep->name();
string::size_type pos;
if ((pos = dottedname.rfind("__DOT__")) != string::npos) {
const string dotted = dottedname.substr(0, pos);
const string ident = dottedname.substr(pos + strlen("__DOT__"));
string baddot;
VSymEnt* okSymp;
aboveSymp = m_statep->findDotted(nodep->fileline(), aboveSymp, dotted, baddot, okSymp);
UASSERT_OBJ(aboveSymp, nodep,
"Can't find cellinline insertion point at "
<< AstNode::prettyNameQ(baddot) << " in: " << nodep->prettyNameQ());
m_statep->insertInline(aboveSymp, m_modSymp, nodep, ident);
} else { // No __DOT__, just directly underneath
m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name());
}
}
virtual void visit(AstDefParam* nodep) override {
nodep->user1p(m_curSymp);
iterateChildren(nodep);
}
virtual void visit(AstNodeBlock* nodep) override {
UINFO(5, " " << nodep << endl);
if (nodep->name() == "" && nodep->unnamed()) {
// Unnamed blocks are only important when they contain var
// decls, so search for them. (Otherwise adding all the
// unnamed#'s would just confuse tracing variables in
// places such as tasks, where "task ...; begin ... end"
// are common.
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (VN_IS(stmtp, Var)) {
++m_modBlockNum;
nodep->name("unnamedblk" + cvtToStr(m_modBlockNum));
break;
}
}
}
if (nodep->name() == "") {
iterateChildren(nodep);
} else {
VL_RESTORER(m_blockp);
VL_RESTORER(m_curSymp);
VSymEnt* const oldCurSymp = m_curSymp;
{
m_blockp = nodep;
m_curSymp
= m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
m_curSymp->fallbackp(oldCurSymp);
// Iterate
iterateChildren(nodep);
}
}
}
virtual void visit(AstNodeFTask* nodep) override {
// NodeTask: Remember its name for later resolution
UINFO(5, " " << nodep << endl);
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Function/Task not under module?");
if (nodep->name() == "new") m_explicitNew = true;
// Remember the existing symbol table scope
VL_RESTORER(m_classOrPackagep);
VL_RESTORER(m_curSymp);
VSymEnt* upSymp = m_curSymp;
{
// Change to appropriate package if extern declaration (vs definition)
if (nodep->classOrPackagep()) {
AstClassOrPackageRef* const cpackagerefp
= VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef);
if (!cpackagerefp) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: extern function definition with class-in-class");
} else {
AstClass* const classp = VN_CAST(cpackagerefp->classOrPackagep(), Class);
if (!classp) {
nodep->v3error("Extern declaration's scope is not a defined class");
} else {
m_curSymp = m_statep->getNodeSym(classp);
upSymp = m_curSymp;
if (!nodep->isExternDef()) {
// Move it to proper spot under the target class
nodep->unlinkFrBack();
classp->addStmtp(nodep);
nodep->isExternDef(true); // So we check there's a matching extern
nodep->classOrPackagep()->unlinkFrBack()->deleteTree();
}
}
}
}
// Set the class as package for iteration