From 9c361026698eb52ebbeda8cf39f9d907bb640f39 Mon Sep 17 00:00:00 2001
From: whitequark <whitequark@whitequark.org>
Date: Sat, 6 Jun 2020 20:37:29 +0000
Subject: [PATCH 1/4] cxxrtl: add a VCD writer using debug information.

---
 backends/cxxrtl/cxxrtl_vcd.h | 194 +++++++++++++++++++++++++++++++++++
 1 file changed, 194 insertions(+)
 create mode 100644 backends/cxxrtl/cxxrtl_vcd.h

diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
new file mode 100644
index 000000000..537bd5661
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -0,0 +1,194 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2020  whitequark <whitequark@whitequark.org>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CXXRTL_VCD_H
+#define CXXRTL_VCD_H
+
+#include <backends/cxxrtl/cxxrtl.h>
+
+namespace cxxrtl {
+
+class vcd_writer {
+	struct variable {
+		size_t ident;
+		size_t width;
+		chunk_t *curr;
+	};
+
+	std::vector<std::string> current_scope;
+	std::vector<variable> variables;
+	bool streaming = false;
+
+	void emit_timescale(unsigned number, const std::string &unit) {
+		assert(!streaming);
+		assert(number == 1 || number == 10 || number == 100);
+		assert(unit == "s" || unit == "ms" || unit == "us" ||
+		       unit == "ns" || unit == "ps" || unit == "fs");
+		buffer += "$timescale " + std::to_string(number) + " " + unit + " $end\n";
+	}
+
+	void emit_scope(const std::vector<std::string> &scope) {
+		assert(!streaming);
+		while (current_scope.size() > scope.size() ||
+		       (current_scope.size() > 0 &&
+			current_scope[current_scope.size() - 1] != scope[current_scope.size() - 1])) {
+			buffer += "$upscope $end\n";
+			current_scope.pop_back();
+		}
+		while (current_scope.size() < scope.size()) {
+			buffer += "$scope module " + scope[current_scope.size()] + " $end\n";
+			current_scope.push_back(scope[current_scope.size()]);
+		}
+	}
+
+	void emit_ident(size_t ident) {
+		do {
+			buffer += '!' + ident % 94; // "base94"
+			ident /= 94;
+		} while (ident != 0);
+	}
+
+	void emit_var(const variable &var, const std::string &type, const std::string &name) {
+		assert(!streaming);
+		buffer += "$var " + type + " " + std::to_string(var.width) + " ";
+		emit_ident(var.ident);
+		buffer += " " + name + " $end\n";
+	}
+
+	void emit_enddefinitions() {
+		assert(!streaming);
+		buffer += "$enddefinitions $end\n";
+		streaming = true;
+	}
+
+	void emit_time(uint64_t timestamp) {
+		assert(streaming);
+		buffer += "#" + std::to_string(timestamp) + "\n";
+	}
+
+	void emit_scalar(const variable &var) {
+		assert(streaming);
+		assert(var.width == 1);
+		buffer += (*var.curr ? '1' : '0');
+		emit_ident(var.ident);
+		buffer += '\n';
+	}
+
+	void emit_vector(const variable &var) {
+		assert(streaming);
+		buffer += 'b';
+		for (size_t bit = var.width - 1; bit != (size_t)-1; bit--) {
+			bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t))));
+			buffer += (bit_curr ? '1' : '0');
+		}
+		buffer += ' ';
+		emit_ident(var.ident);
+		buffer += '\n';
+	}
+
+	static std::vector<std::string> split_hierarchy(const std::string &hier_name) {
+		std::vector<std::string> hierarchy;
+		size_t prev = 0;
+		while (true) {
+			size_t curr = hier_name.find_first_of(' ', prev + 1);
+			if (curr > hier_name.size())
+				curr = hier_name.size();
+			if (curr > prev + 1)
+				hierarchy.push_back(hier_name.substr(prev, curr - prev));
+			if (curr == hier_name.size())
+				break;
+			prev = curr + 1;
+		}
+		return hierarchy;
+	}
+
+public:
+	std::string buffer;
+
+	void timescale(unsigned number, const std::string &unit) {
+		emit_timescale(number, unit);
+	}
+
+	void add(const std::string &hier_name, const debug_item &item) {
+		std::vector<std::string> scope = split_hierarchy(hier_name);
+		std::string name = scope.back();
+		scope.pop_back();
+
+		emit_scope(scope);
+		switch (item.type) {
+			// Not the best naming but oh well...
+			case debug_item::VALUE:
+				variables.emplace_back(variable { variables.size(), item.width, item.curr });
+				emit_var(variables.back(), "wire", name);
+				break;
+			case debug_item::WIRE:
+				variables.emplace_back(variable { variables.size(), item.width, item.curr });
+				emit_var(variables.back(), "reg", name);
+				break;
+			case debug_item::MEMORY: {
+				const size_t stride = (item.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
+				for (size_t index = 0; index < item.depth; index++) {
+					chunk_t *nth_curr = &item.curr[stride * index];
+					std::string nth_name = name + '[' + std::to_string(index) + ']';
+					variables.emplace_back(variable { variables.size(), item.width, nth_curr });
+					emit_var(variables.back(), "reg", nth_name);
+				}
+				break;
+			}
+		}
+	}
+
+	template<class Filter>
+	void add(const debug_items &items, const Filter &filter) {
+		// `debug_items` is a map, so the items are already sorted in an order optimal for emitting
+		// VCD scope sections.
+		for (auto &it : items)
+			if (filter(it.first, it.second))
+				add(it.first, it.second);
+	}
+
+	void add(const debug_items &items) {
+		this->template add(items, [](const std::string &, const debug_item &) {
+			return true;
+		});
+	}
+
+	void add_without_memories(const debug_items &items) {
+		this->template add(items, [](const std::string &, const debug_item &item) {
+			return item.type == debug_item::VALUE || item.type == debug_item::WIRE;
+		});
+	}
+
+	void sample(uint64_t timestamp) {
+		if (!streaming) {
+			emit_scope({});
+			emit_enddefinitions();
+		}
+		emit_time(timestamp);
+		for (auto var : variables) {
+			if (var.width == 1)
+				emit_scalar(var);
+			else
+				emit_vector(var);
+		}
+	}
+};
+
+}
+
+#endif

From 68362a90530328f15cb93a04f1b1cc65858b93c0 Mon Sep 17 00:00:00 2001
From: whitequark <whitequark@whitequark.org>
Date: Sat, 6 Jun 2020 21:55:53 +0000
Subject: [PATCH 2/4] cxxrtl: only write VCD values that were actually updated.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

On a representative design (Minerva SoC) this reduces VCD file size
by ~20× and runtime by ~3×.
---
 backends/cxxrtl/cxxrtl_vcd.h | 40 +++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
index 537bd5661..5706917ca 100644
--- a/backends/cxxrtl/cxxrtl_vcd.h
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -28,10 +28,12 @@ class vcd_writer {
 		size_t ident;
 		size_t width;
 		chunk_t *curr;
+		size_t prev_off;
 	};
 
 	std::vector<std::string> current_scope;
 	std::vector<variable> variables;
+	std::vector<chunk_t> cache;
 	bool streaming = false;
 
 	void emit_timescale(unsigned number, const std::string &unit) {
@@ -101,6 +103,22 @@ class vcd_writer {
 		buffer += '\n';
 	}
 
+	void append_variable(size_t width, chunk_t *curr) {
+		const size_t chunks = (width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
+		variables.emplace_back(variable { variables.size(), width, curr, cache.size() });
+		cache.insert(cache.end(), &curr[0], &curr[chunks]);
+	}
+
+	bool test_variable(const variable &var) {
+		const size_t chunks = (var.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
+		if (std::equal(&var.curr[0], &var.curr[chunks], &cache[var.prev_off])) {
+			return false;
+		} else {
+			std::copy(&var.curr[0], &var.curr[chunks], &cache[var.prev_off]);
+			return true;
+		}
+	}
+
 	static std::vector<std::string> split_hierarchy(const std::string &hier_name) {
 		std::vector<std::string> hierarchy;
 		size_t prev = 0;
@@ -133,11 +151,11 @@ public:
 		switch (item.type) {
 			// Not the best naming but oh well...
 			case debug_item::VALUE:
-				variables.emplace_back(variable { variables.size(), item.width, item.curr });
+				append_variable(item.width, item.curr);
 				emit_var(variables.back(), "wire", name);
 				break;
 			case debug_item::WIRE:
-				variables.emplace_back(variable { variables.size(), item.width, item.curr });
+				append_variable(item.width, item.curr);
 				emit_var(variables.back(), "reg", name);
 				break;
 			case debug_item::MEMORY: {
@@ -145,7 +163,7 @@ public:
 				for (size_t index = 0; index < item.depth; index++) {
 					chunk_t *nth_curr = &item.curr[stride * index];
 					std::string nth_name = name + '[' + std::to_string(index) + ']';
-					variables.emplace_back(variable { variables.size(), item.width, nth_curr });
+					append_variable(item.width, nth_curr);
 					emit_var(variables.back(), "reg", nth_name);
 				}
 				break;
@@ -175,17 +193,19 @@ public:
 	}
 
 	void sample(uint64_t timestamp) {
-		if (!streaming) {
+		bool first_sample = !streaming;
+		if (first_sample) {
 			emit_scope({});
 			emit_enddefinitions();
 		}
 		emit_time(timestamp);
-		for (auto var : variables) {
-			if (var.width == 1)
-				emit_scalar(var);
-			else
-				emit_vector(var);
-		}
+		for (auto var : variables)
+			if (test_variable(var) || first_sample) {
+				if (var.width == 1)
+					emit_scalar(var);
+				else
+					emit_vector(var);
+			}
 	}
 };
 

From 31f6c96b1fb0a3a6311a5aba404e96bbbe342dd9 Mon Sep 17 00:00:00 2001
From: whitequark <whitequark@whitequark.org>
Date: Sun, 7 Jun 2020 03:45:53 +0000
Subject: [PATCH 3/4] cxxrtl: add a C API for writing VCD dumps.

This C API is fully featured.
---
 backends/cxxrtl/cxxrtl.cc          |   7 +-
 backends/cxxrtl/cxxrtl.h           |   2 +
 backends/cxxrtl/cxxrtl_capi.cc     |   7 +-
 backends/cxxrtl/cxxrtl_vcd_capi.cc |  83 ++++++++++++++++++++++
 backends/cxxrtl/cxxrtl_vcd_capi.h  | 107 +++++++++++++++++++++++++++++
 5 files changed, 204 insertions(+), 2 deletions(-)
 create mode 100644 backends/cxxrtl/cxxrtl_vcd_capi.cc
 create mode 100644 backends/cxxrtl/cxxrtl_vcd_capi.h

diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc
index 4dc534513..64af5dab8 100644
--- a/backends/cxxrtl/cxxrtl.cc
+++ b/backends/cxxrtl/cxxrtl.cc
@@ -1851,10 +1851,15 @@ struct CxxrtlWorker {
 		else
 			f << "#include <backends/cxxrtl/cxxrtl.h>\n";
 		f << "\n";
-		f << "#ifdef CXXRTL_INCLUDE_CAPI_IMPL\n";
+		f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
+		f << "    defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
 		f << "#include <backends/cxxrtl/cxxrtl_capi.cc>\n";
 		f << "#endif\n";
 		f << "\n";
+		f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
+		f << "#include <backends/cxxrtl/cxxrtl_vcd_capi.cc>\n";
+		f << "#endif\n";
+		f << "\n";
 		f << "using namespace cxxrtl_yosys;\n";
 		f << "\n";
 		f << "namespace " << design_ns << " {\n";
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index aba2c77a1..5f74899fd 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -728,6 +728,8 @@ struct debug_item : ::cxxrtl_object {
 		MEMORY = CXXRTL_MEMORY,
 	};
 
+	debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {}
+
 	template<size_t Bits>
 	debug_item(value<Bits> &item) {
 		static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc
index 0dcd64041..489d72da5 100644
--- a/backends/cxxrtl/cxxrtl_capi.cc
+++ b/backends/cxxrtl/cxxrtl_capi.cc
@@ -26,6 +26,11 @@ struct _cxxrtl_handle {
 	cxxrtl::debug_items objects;
 };
 
+// Private function for use by other units of the C API.
+const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle) {
+	return handle->objects;
+}
+
 cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) {
 	cxxrtl_handle handle = new _cxxrtl_handle;
 	handle->module = std::move(design->module);
@@ -49,7 +54,7 @@ cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) {
 }
 
 void cxxrtl_enum(cxxrtl_handle handle, void *data,
-                 void (*callback)(void *data, const char *name, struct cxxrtl_object *object)) {
+                 void (*callback)(void *data, const char *name, cxxrtl_object *object)) {
 	for (auto &it : handle->objects)
 		callback(data, it.first.c_str(), static_cast<cxxrtl_object*>(&it.second));
 }
diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.cc b/backends/cxxrtl/cxxrtl_vcd_capi.cc
new file mode 100644
index 000000000..46e4f1c45
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd_capi.cc
@@ -0,0 +1,83 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2020  whitequark <whitequark@whitequark.org>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.h`.
+
+#include <backends/cxxrtl/cxxrtl_vcd.h>
+#include <backends/cxxrtl/cxxrtl_vcd_capi.h>
+
+extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle);
+
+struct _cxxrtl_vcd {
+	cxxrtl::vcd_writer writer;
+	bool flush = false;
+};
+
+cxxrtl_vcd cxxrtl_vcd_create() {
+	return new _cxxrtl_vcd;
+}
+
+void cxxrtl_vcd_destroy(cxxrtl_vcd vcd) {
+	delete vcd;
+}
+
+void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit) {
+	vcd->writer.timescale(number, unit);
+}
+
+void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, cxxrtl_object *object) {
+	// Note the copy. We don't know whether `object` came from a design (in which case it is
+	// an instance of `debug_item`), or from user code (in which case it is an instance of
+	// `cxxrtl_object`), so casting the pointer wouldn't be safe.
+	vcd->writer.add(name, debug_item(*object));
+}
+
+void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle) {
+	vcd->writer.add(cxxrtl_debug_items_from_handle(handle));
+}
+
+void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data,
+														int (*filter)(void *data, const char *name,
+														              const cxxrtl_object *object)) {
+	vcd->writer.add(cxxrtl_debug_items_from_handle(handle),
+		[=](const std::string &name, const debug_item &item) {
+			return filter(data, name.c_str(), static_cast<const cxxrtl_object*>(&item));
+		});
+}
+
+void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle) {
+	vcd->writer.add_without_memories(cxxrtl_debug_items_from_handle(handle));
+}
+
+void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time) {
+	if (vcd->flush) {
+		vcd->writer.buffer.clear();
+		vcd->flush = false;
+	}
+	vcd->writer.sample(time);
+}
+
+void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size) {
+	if (vcd->flush) {
+		vcd->writer.buffer.clear();
+		vcd->flush = false;
+	}
+	*data = vcd->writer.buffer.c_str();
+	*size = vcd->writer.buffer.size();
+	vcd->flush = true;
+}
diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.h b/backends/cxxrtl/cxxrtl_vcd_capi.h
new file mode 100644
index 000000000..6a7fb9f47
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd_capi.h
@@ -0,0 +1,107 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2020  whitequark <whitequark@whitequark.org>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CXXRTL_VCD_CAPI_H
+#define CXXRTL_VCD_CAPI_H
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.cc`.
+//
+// The CXXRTL C API for VCD writing makes it possible to insert virtual probes into designs and
+// dump waveforms to Value Change Dump files.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <backends/cxxrtl/cxxrtl_capi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque reference to a VCD writer.
+typedef struct _cxxrtl_vcd *cxxrtl_vcd;
+
+// Create a VCD writer.
+cxxrtl_vcd cxxrtl_vcd_create();
+
+// Release all resources used by a VCD writer.
+void cxxrtl_vcd_destroy(cxxrtl_vcd vcd);
+
+// Set VCD timescale.
+//
+// The `number` must be 1, 10, or 100, and the `unit` must be one of `"s"`, `"ms"`, `"us"`, `"ns"`,
+// `"ps"`, or `"fs"`.
+//
+// Timescale can only be set before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit);
+
+// Schedule a specific CXXRTL object to be sampled.
+//
+// The `name` is a full hierarchical name as described for `cxxrtl_get`; it does not need to match
+// the original name of `object`, if any. The `object` must outlive the VCD writer, but there are
+// no other requirements; if desired, it can be provided by user code, rather than come from
+// a design.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, struct cxxrtl_object *object);
+
+// Schedule all CXXRTL objects in a simulation.
+//
+// The design `handle` must outlive the VCD writer.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle);
+
+// Schedule CXXRTL objects in a simulation that match a given predicate.
+//
+// For every object in the simulation, `filter` is called with the provided `data`, the full
+// hierarchical name of the object (see `cxxrtl_get` for details), and the object description.
+// The object will be sampled if the predicate returns a non-zero value.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data,
+					  int (*filter)(void *data, const char *name,
+					                const struct cxxrtl_object *object));
+
+// Schedule all CXXRTL objects in a simulation except for memories.
+//
+// The design `handle` must outlive the VCD writer.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle);
+
+// Sample all scheduled objects.
+//
+// First, `time` is written to the internal buffer. Second, the values of every signal changed since
+// the previous call to `cxxrtl_vcd_sample` (all values if this is the first call) are written to
+// the internal buffer. The contents of the buffer can be retrieved with `cxxrtl_vcd_read`.
+void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time);
+
+// Retrieve buffered VCD data.
+//
+// The pointer to the start of the next chunk of VCD data is assigned to `*data`, and the length
+// of that chunk is assigned to `*size`. The pointer to the data is valid until the next call to
+// `cxxrtl_vcd_sample` or `cxxrtl_vcd_read`. Once all of the buffered data has been retrieved,
+// this function will always return zero sized chunks.
+void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

From ff5500f11a4512f9d4dc86f78f761b195febcaf5 Mon Sep 17 00:00:00 2001
From: whitequark <whitequark@whitequark.org>
Date: Sun, 7 Jun 2020 03:48:40 +0000
Subject: [PATCH 4/4] =?UTF-8?q?cxxrtl:=20rename=20cxxrtl.cc=E2=86=92cxxrtl?=
 =?UTF-8?q?=5Fbackend.cc.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

To avoid confusion with the C++ source files that are a part of
the simulation itself and not a part of Yosys build.
---
 backends/cxxrtl/Makefile.inc                     | 2 +-
 backends/cxxrtl/{cxxrtl.cc => cxxrtl_backend.cc} | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename backends/cxxrtl/{cxxrtl.cc => cxxrtl_backend.cc} (100%)

diff --git a/backends/cxxrtl/Makefile.inc b/backends/cxxrtl/Makefile.inc
index f93e65f85..aaa304502 100644
--- a/backends/cxxrtl/Makefile.inc
+++ b/backends/cxxrtl/Makefile.inc
@@ -1,2 +1,2 @@
 
-OBJS += backends/cxxrtl/cxxrtl.o
+OBJS += backends/cxxrtl/cxxrtl_backend.o
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl_backend.cc
similarity index 100%
rename from backends/cxxrtl/cxxrtl.cc
rename to backends/cxxrtl/cxxrtl_backend.cc