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>
34 std::vector
<std::string
> current_scope
;
35 std::vector
<variable
> variables
;
36 std::vector
<chunk_t
> cache
;
37 std::map
<chunk_t
*, size_t> aliases
;
38 bool streaming
= false;
40 void emit_timescale(unsigned number
, const std::string
&unit
) {
42 assert(number
== 1 || number
== 10 || number
== 100);
43 assert(unit
== "s" || unit
== "ms" || unit
== "us" ||
44 unit
== "ns" || unit
== "ps" || unit
== "fs");
45 buffer
+= "$timescale " + std::to_string(number
) + " " + unit
+ " $end\n";
48 void emit_scope(const std::vector
<std::string
> &scope
) {
50 while (current_scope
.size() > scope
.size() ||
51 (current_scope
.size() > 0 &&
52 current_scope
[current_scope
.size() - 1] != scope
[current_scope
.size() - 1])) {
53 buffer
+= "$upscope $end\n";
54 current_scope
.pop_back();
56 while (current_scope
.size() < scope
.size()) {
57 buffer
+= "$scope module " + scope
[current_scope
.size()] + " $end\n";
58 current_scope
.push_back(scope
[current_scope
.size()]);
62 void emit_ident(size_t ident
) {
64 buffer
+= '!' + ident
% 94; // "base94"
69 void emit_var(const variable
&var
, const std::string
&type
, const std::string
&name
,
70 size_t lsb_at
, bool multipart
) {
72 buffer
+= "$var " + type
+ " " + std::to_string(var
.width
) + " ";
73 emit_ident(var
.ident
);
75 if (multipart
|| name
.back() == ']' || lsb_at
!= 0) {
77 buffer
+= " [" + std::to_string(lsb_at
) + "]";
79 buffer
+= " [" + std::to_string(lsb_at
+ var
.width
- 1) + ":" + std::to_string(lsb_at
) + "]";
84 void emit_enddefinitions() {
86 buffer
+= "$enddefinitions $end\n";
90 void emit_time(uint64_t timestamp
) {
92 buffer
+= "#" + std::to_string(timestamp
) + "\n";
95 void emit_scalar(const variable
&var
) {
97 assert(var
.width
== 1);
98 buffer
+= (*var
.curr
? '1' : '0');
99 emit_ident(var
.ident
);
103 void emit_vector(const variable
&var
) {
106 for (size_t bit
= var
.width
- 1; bit
!= (size_t)-1; bit
--) {
107 bool bit_curr
= var
.curr
[bit
/ (8 * sizeof(chunk_t
))] & (1 << (bit
% (8 * sizeof(chunk_t
))));
108 buffer
+= (bit_curr
? '1' : '0');
111 emit_ident(var
.ident
);
115 const variable
®ister_variable(size_t width
, chunk_t
*curr
, bool constant
= false) {
116 if (aliases
.count(curr
)) {
117 return variables
[aliases
[curr
]];
119 const size_t chunks
= (width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
120 aliases
[curr
] = variables
.size();
122 variables
.emplace_back(variable
{ variables
.size(), width
, curr
, (size_t)-1 });
124 variables
.emplace_back(variable
{ variables
.size(), width
, curr
, cache
.size() });
125 cache
.insert(cache
.end(), &curr
[0], &curr
[chunks
]);
127 return variables
.back();
131 bool test_variable(const variable
&var
) {
132 if (var
.prev_off
== (size_t)-1)
133 return false; // constant
134 const size_t chunks
= (var
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
135 if (std::equal(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.prev_off
])) {
138 std::copy(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.prev_off
]);
143 static std::vector
<std::string
> split_hierarchy(const std::string
&hier_name
) {
144 std::vector
<std::string
> hierarchy
;
147 size_t curr
= hier_name
.find_first_of(' ', prev
);
148 if (curr
== std::string::npos
) {
149 hierarchy
.push_back(hier_name
.substr(prev
));
152 hierarchy
.push_back(hier_name
.substr(prev
, curr
- prev
));
162 void timescale(unsigned number
, const std::string
&unit
) {
163 emit_timescale(number
, unit
);
166 void add(const std::string
&hier_name
, const debug_item
&item
, bool multipart
= false) {
167 std::vector
<std::string
> scope
= split_hierarchy(hier_name
);
168 std::string name
= scope
.back();
173 // Not the best naming but oh well...
174 case debug_item::VALUE
:
175 emit_var(register_variable(item
.width
, item
.curr
, /*constant=*/item
.next
== nullptr),
176 "wire", name
, item
.lsb_at
, multipart
);
178 case debug_item::WIRE
:
179 emit_var(register_variable(item
.width
, item
.curr
),
180 "reg", name
, item
.lsb_at
, multipart
);
182 case debug_item::MEMORY
: {
183 const size_t stride
= (item
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
184 for (size_t index
= 0; index
< item
.depth
; index
++) {
185 chunk_t
*nth_curr
= &item
.curr
[stride
* index
];
186 std::string nth_name
= name
+ '[' + std::to_string(index
) + ']';
187 emit_var(register_variable(item
.width
, nth_curr
),
188 "reg", nth_name
, item
.lsb_at
, multipart
);
192 case debug_item::ALIAS
:
193 // Like VALUE, but, even though `item.next == nullptr` always holds, the underlying value
194 // can actually change, and must be tracked. In most cases the VCD identifier will be
195 // unified with the aliased reg, but we should handle the case where only the alias is
196 // added to the VCD writer, too.
197 emit_var(register_variable(item
.width
, item
.curr
),
198 "wire", name
, item
.lsb_at
, multipart
);
203 template<class Filter
>
204 void add(const debug_items
&items
, const Filter
&filter
) {
205 // `debug_items` is a map, so the items are already sorted in an order optimal for emitting
206 // VCD scope sections.
207 for (auto &it
: items
.table
)
208 for (auto &part
: it
.second
)
209 if (filter(it
.first
, part
))
210 add(it
.first
, part
, it
.second
.size() > 1);
213 void add(const debug_items
&items
) {
214 this->template add(items
, [](const std::string
&, const debug_item
&) {
219 void add_without_memories(const debug_items
&items
) {
220 this->template add(items
, [](const std::string
&, const debug_item
&item
) {
221 return item
.type
!= debug_item::MEMORY
;
225 void sample(uint64_t timestamp
) {
226 bool first_sample
= !streaming
;
229 emit_enddefinitions();
231 emit_time(timestamp
);
232 for (auto var
: variables
)
233 if (test_variable(var
) || first_sample
) {