cxxrtl: add a C API for writing VCD dumps.
authorwhitequark <whitequark@whitequark.org>
Sun, 7 Jun 2020 03:45:53 +0000 (03:45 +0000)
committerwhitequark <whitequark@whitequark.org>
Sun, 7 Jun 2020 03:48:00 +0000 (03:48 +0000)
This C API is fully featured.

backends/cxxrtl/cxxrtl.cc
backends/cxxrtl/cxxrtl.h
backends/cxxrtl/cxxrtl_capi.cc
backends/cxxrtl/cxxrtl_vcd_capi.cc [new file with mode: 0644]
backends/cxxrtl/cxxrtl_vcd_capi.h [new file with mode: 0644]

index 4dc534513fcf46bfd93358ef40363a6a4f6ca7c1..64af5dab8df80cf87bac381406cc99dd13349aa0 100644 (file)
@@ -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";
index aba2c77a1dd0ba764b813ecf003f740d7460e260..5f74899fd4bd5ebf5f8dabc91c9e46eb27dd0eef 100644 (file)
@@ -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),
index 0dcd64041594132d2937ef0bbd56e567713f13c4..489d72da5c82409a3d7be92052f5c5fd5a18d31d 100644 (file)
@@ -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 (file)
index 0000000..46e4f1c
--- /dev/null
@@ -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 (file)
index 0000000..6a7fb9f
--- /dev/null
@@ -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