2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include <backends/cxxrtl/cxxrtl.h>
32 debug_outline
*outline
;
36 std::vector
<std::string
> current_scope
;
37 std::map
<debug_outline
*, bool> outlines
;
38 std::vector
<variable
> variables
;
39 std::vector
<chunk_t
> cache
;
40 std::map
<chunk_t
*, size_t> aliases
;
41 bool streaming
= false;
43 void emit_timescale(unsigned number
, const std::string
&unit
) {
45 assert(number
== 1 || number
== 10 || number
== 100);
46 assert(unit
== "s" || unit
== "ms" || unit
== "us" ||
47 unit
== "ns" || unit
== "ps" || unit
== "fs");
48 buffer
+= "$timescale " + std::to_string(number
) + " " + unit
+ " $end\n";
51 void emit_scope(const std::vector
<std::string
> &scope
) {
53 while (current_scope
.size() > scope
.size() ||
54 (current_scope
.size() > 0 &&
55 current_scope
[current_scope
.size() - 1] != scope
[current_scope
.size() - 1])) {
56 buffer
+= "$upscope $end\n";
57 current_scope
.pop_back();
59 while (current_scope
.size() < scope
.size()) {
60 buffer
+= "$scope module " + scope
[current_scope
.size()] + " $end\n";
61 current_scope
.push_back(scope
[current_scope
.size()]);
65 void emit_ident(size_t ident
) {
67 buffer
+= '!' + ident
% 94; // "base94"
72 void emit_var(const variable
&var
, const std::string
&type
, const std::string
&name
,
73 size_t lsb_at
, bool multipart
) {
75 buffer
+= "$var " + type
+ " " + std::to_string(var
.width
) + " ";
76 emit_ident(var
.ident
);
78 if (multipart
|| name
.back() == ']' || lsb_at
!= 0) {
80 buffer
+= " [" + std::to_string(lsb_at
) + "]";
82 buffer
+= " [" + std::to_string(lsb_at
+ var
.width
- 1) + ":" + std::to_string(lsb_at
) + "]";
87 void emit_enddefinitions() {
89 buffer
+= "$enddefinitions $end\n";
93 void emit_time(uint64_t timestamp
) {
95 buffer
+= "#" + std::to_string(timestamp
) + "\n";
98 void emit_scalar(const variable
&var
) {
100 assert(var
.width
== 1);
101 buffer
+= (*var
.curr
? '1' : '0');
102 emit_ident(var
.ident
);
106 void emit_vector(const variable
&var
) {
109 for (size_t bit
= var
.width
- 1; bit
!= (size_t)-1; bit
--) {
110 bool bit_curr
= var
.curr
[bit
/ (8 * sizeof(chunk_t
))] & (1 << (bit
% (8 * sizeof(chunk_t
))));
111 buffer
+= (bit_curr
? '1' : '0');
114 emit_ident(var
.ident
);
118 void reset_outlines() {
119 for (auto &outline_it
: outlines
)
120 outline_it
.second
= /*warm=*/(outline_it
.first
== nullptr);
123 variable
®ister_variable(size_t width
, chunk_t
*curr
, bool constant
= false, debug_outline
*outline
= nullptr) {
124 if (aliases
.count(curr
)) {
125 return variables
[aliases
[curr
]];
127 auto outline_it
= outlines
.emplace(outline
, /*warm=*/(outline
== nullptr)).first
;
128 const size_t chunks
= (width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
129 aliases
[curr
] = variables
.size();
131 variables
.emplace_back(variable
{ variables
.size(), width
, curr
, (size_t)-1, outline_it
->first
, &outline_it
->second
});
133 variables
.emplace_back(variable
{ variables
.size(), width
, curr
, cache
.size(), outline_it
->first
, &outline_it
->second
});
134 cache
.insert(cache
.end(), &curr
[0], &curr
[chunks
]);
136 return variables
.back();
140 bool test_variable(const variable
&var
) {
141 if (var
.cache_offset
== (size_t)-1)
142 return false; // constant
143 if (!*var
.outline_warm
) {
145 *var
.outline_warm
= true;
147 const size_t chunks
= (var
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
148 if (std::equal(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.cache_offset
])) {
151 std::copy(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.cache_offset
]);
156 static std::vector
<std::string
> split_hierarchy(const std::string
&hier_name
) {
157 std::vector
<std::string
> hierarchy
;
160 size_t curr
= hier_name
.find_first_of(' ', prev
);
161 if (curr
== std::string::npos
) {
162 hierarchy
.push_back(hier_name
.substr(prev
));
165 hierarchy
.push_back(hier_name
.substr(prev
, curr
- prev
));
175 void timescale(unsigned number
, const std::string
&unit
) {
176 emit_timescale(number
, unit
);
179 void add(const std::string
&hier_name
, const debug_item
&item
, bool multipart
= false) {
180 std::vector
<std::string
> scope
= split_hierarchy(hier_name
);
181 std::string name
= scope
.back();
186 // Not the best naming but oh well...
187 case debug_item::VALUE
:
188 emit_var(register_variable(item
.width
, item
.curr
, /*constant=*/item
.next
== nullptr),
189 "wire", name
, item
.lsb_at
, multipart
);
191 case debug_item::WIRE
:
192 emit_var(register_variable(item
.width
, item
.curr
),
193 "reg", name
, item
.lsb_at
, multipart
);
195 case debug_item::MEMORY
: {
196 const size_t stride
= (item
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
197 for (size_t index
= 0; index
< item
.depth
; index
++) {
198 chunk_t
*nth_curr
= &item
.curr
[stride
* index
];
199 std::string nth_name
= name
+ '[' + std::to_string(index
) + ']';
200 emit_var(register_variable(item
.width
, nth_curr
),
201 "reg", nth_name
, item
.lsb_at
, multipart
);
205 case debug_item::ALIAS
:
206 // Like VALUE, but, even though `item.next == nullptr` always holds, the underlying value
207 // can actually change, and must be tracked. In most cases the VCD identifier will be
208 // unified with the aliased reg, but we should handle the case where only the alias is
209 // added to the VCD writer, too.
210 emit_var(register_variable(item
.width
, item
.curr
),
211 "wire", name
, item
.lsb_at
, multipart
);
213 case debug_item::OUTLINE
:
214 emit_var(register_variable(item
.width
, item
.curr
, /*constant=*/false, item
.outline
),
215 "wire", name
, item
.lsb_at
, multipart
);
220 template<class Filter
>
221 void add(const debug_items
&items
, const Filter
&filter
) {
222 // `debug_items` is a map, so the items are already sorted in an order optimal for emitting
223 // VCD scope sections.
224 for (auto &it
: items
.table
)
225 for (auto &part
: it
.second
)
226 if (filter(it
.first
, part
))
227 add(it
.first
, part
, it
.second
.size() > 1);
230 void add(const debug_items
&items
) {
231 this->add(items
, [](const std::string
&, const debug_item
&) {
236 void add_without_memories(const debug_items
&items
) {
237 this->add(items
, [](const std::string
&, const debug_item
&item
) {
238 return item
.type
!= debug_item::MEMORY
;
242 void sample(uint64_t timestamp
) {
243 bool first_sample
= !streaming
;
246 emit_enddefinitions();
249 emit_time(timestamp
);
250 for (auto var
: variables
)
251 if (test_variable(var
) || first_sample
) {