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 bool streaming
= false;
39 void emit_timescale(unsigned number
, const std::string
&unit
) {
41 assert(number
== 1 || number
== 10 || number
== 100);
42 assert(unit
== "s" || unit
== "ms" || unit
== "us" ||
43 unit
== "ns" || unit
== "ps" || unit
== "fs");
44 buffer
+= "$timescale " + std::to_string(number
) + " " + unit
+ " $end\n";
47 void emit_scope(const std::vector
<std::string
> &scope
) {
49 while (current_scope
.size() > scope
.size() ||
50 (current_scope
.size() > 0 &&
51 current_scope
[current_scope
.size() - 1] != scope
[current_scope
.size() - 1])) {
52 buffer
+= "$upscope $end\n";
53 current_scope
.pop_back();
55 while (current_scope
.size() < scope
.size()) {
56 buffer
+= "$scope module " + scope
[current_scope
.size()] + " $end\n";
57 current_scope
.push_back(scope
[current_scope
.size()]);
61 void emit_ident(size_t ident
) {
63 buffer
+= '!' + ident
% 94; // "base94"
68 void emit_var(const variable
&var
, const std::string
&type
, const std::string
&name
) {
70 buffer
+= "$var " + type
+ " " + std::to_string(var
.width
) + " ";
71 emit_ident(var
.ident
);
72 buffer
+= " " + name
+ " $end\n";
75 void emit_enddefinitions() {
77 buffer
+= "$enddefinitions $end\n";
81 void emit_time(uint64_t timestamp
) {
83 buffer
+= "#" + std::to_string(timestamp
) + "\n";
86 void emit_scalar(const variable
&var
) {
88 assert(var
.width
== 1);
89 buffer
+= (*var
.curr
? '1' : '0');
90 emit_ident(var
.ident
);
94 void emit_vector(const variable
&var
) {
97 for (size_t bit
= var
.width
- 1; bit
!= (size_t)-1; bit
--) {
98 bool bit_curr
= var
.curr
[bit
/ (8 * sizeof(chunk_t
))] & (1 << (bit
% (8 * sizeof(chunk_t
))));
99 buffer
+= (bit_curr
? '1' : '0');
102 emit_ident(var
.ident
);
106 void append_variable(size_t width
, chunk_t
*curr
) {
107 const size_t chunks
= (width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
108 variables
.emplace_back(variable
{ variables
.size(), width
, curr
, cache
.size() });
109 cache
.insert(cache
.end(), &curr
[0], &curr
[chunks
]);
112 bool test_variable(const variable
&var
) {
113 const size_t chunks
= (var
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
114 if (std::equal(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.prev_off
])) {
117 std::copy(&var
.curr
[0], &var
.curr
[chunks
], &cache
[var
.prev_off
]);
122 static std::vector
<std::string
> split_hierarchy(const std::string
&hier_name
) {
123 std::vector
<std::string
> hierarchy
;
126 size_t curr
= hier_name
.find_first_of(' ', prev
+ 1);
127 if (curr
> hier_name
.size())
128 curr
= hier_name
.size();
130 hierarchy
.push_back(hier_name
.substr(prev
, curr
- prev
));
131 if (curr
== hier_name
.size())
141 void timescale(unsigned number
, const std::string
&unit
) {
142 emit_timescale(number
, unit
);
145 void add(const std::string
&hier_name
, const debug_item
&item
) {
146 std::vector
<std::string
> scope
= split_hierarchy(hier_name
);
147 std::string name
= scope
.back();
152 // Not the best naming but oh well...
153 case debug_item::VALUE
:
154 append_variable(item
.width
, item
.curr
);
155 emit_var(variables
.back(), "wire", name
);
157 case debug_item::WIRE
:
158 append_variable(item
.width
, item
.curr
);
159 emit_var(variables
.back(), "reg", name
);
161 case debug_item::MEMORY
: {
162 const size_t stride
= (item
.width
+ (sizeof(chunk_t
) * 8 - 1)) / (sizeof(chunk_t
) * 8);
163 for (size_t index
= 0; index
< item
.depth
; index
++) {
164 chunk_t
*nth_curr
= &item
.curr
[stride
* index
];
165 std::string nth_name
= name
+ '[' + std::to_string(index
) + ']';
166 append_variable(item
.width
, nth_curr
);
167 emit_var(variables
.back(), "reg", nth_name
);
174 template<class Filter
>
175 void add(const debug_items
&items
, const Filter
&filter
) {
176 // `debug_items` is a map, so the items are already sorted in an order optimal for emitting
177 // VCD scope sections.
178 for (auto &it
: items
)
179 if (filter(it
.first
, it
.second
))
180 add(it
.first
, it
.second
);
183 void add(const debug_items
&items
) {
184 this->template add(items
, [](const std::string
&, const debug_item
&) {
189 void add_without_memories(const debug_items
&items
) {
190 this->template add(items
, [](const std::string
&, const debug_item
&item
) {
191 return item
.type
== debug_item::VALUE
|| item
.type
== debug_item::WIRE
;
195 void sample(uint64_t timestamp
) {
196 bool first_sample
= !streaming
;
199 emit_enddefinitions();
201 emit_time(timestamp
);
202 for (auto var
: variables
)
203 if (test_variable(var
) || first_sample
) {