From 36e067cbf62e5a27dfeadcb929565e11f7d46b73 Mon Sep 17 00:00:00 2001 From: shylie Date: Wed, 25 Mar 2026 00:56:44 -0400 Subject: [PATCH] Initial commit --- .clang-format | 12 +++++++++ .gitignore | 2 ++ CMakeLists.txt | 13 ++++++++++ include/vcppd/builder.h | 42 ++++++++++++++++++++++++++++++ include/vcppd/scope.h | 57 +++++++++++++++++++++++++++++++++++++++++ include/vcppd/vcd.h | 45 ++++++++++++++++++++++++++++++++ include/vcppd/vcppd.h | 8 ++++++ src/builder.cpp | 34 ++++++++++++++++++++++++ src/vcd.cpp | 37 ++++++++++++++++++++++++++ test/test.cpp | 22 ++++++++++++++++ 10 files changed, 272 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/vcppd/builder.h create mode 100644 include/vcppd/scope.h create mode 100644 include/vcppd/vcd.h create mode 100644 include/vcppd/vcppd.h create mode 100644 src/builder.cpp create mode 100644 src/vcd.cpp create mode 100644 test/test.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f1b5bbc --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: GNU +SpaceBeforeParens: ControlStatementsExceptControlMacros +PointerAlignment: Left +UseTab: Never +IndentWidth: 2 +ContinuationIndentWidth: 2 +ConstructorInitializerIndentWidth: 2 +BreakAfterReturnType: Automatic +BreakConstructorInitializers: AfterColon +PackConstructorInitializers: Never +IncludeBlocks: Regroup +BreakBeforeBraces: Allman diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc95448 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..50408ca --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(vcppd) + +add_library(vcppd src/vcd.cpp src/builder.cpp) +target_include_directories(vcppd PUBLIC include) +set_target_properties(vcppd PROPERTIES CXX_STANDARD 20) + +if(PROJECT_IS_TOP_LEVEL) + add_executable(vcppd-test test/test.cpp) + target_link_libraries(vcppd-test PUBLIC vcppd) + set_target_properties(vcppd-test PROPERTIES CXX_STANDARD 20) +endif() diff --git a/include/vcppd/builder.h b/include/vcppd/builder.h new file mode 100644 index 0000000..0c9a24a --- /dev/null +++ b/include/vcppd/builder.h @@ -0,0 +1,42 @@ +#ifndef VCPPD_VCD_BUILDER_H +#define VCPPD_VCD_BUILDER_H + +#include "vcppd/vcd.h" + +#include + +namespace vcppd +{ + +class Builder +{ + friend Vcd; + friend Scope; + +public: + Builder(std::ostream& stream); + + Vcd build() const; + + Scope scope(const std::string& name); + +private: + template + void trace(const std::string& name, const U& ref, + const std::string& short_name) + { + stream << std::format("$var wire {} {} {} $end", sizeof(U) * 8, name, + short_name) + << std::endl; + variables[name] = detail::variable_entry{ &ref, sizeof(U) }; + } + + void unscope(); + + std::ostream& stream; + detail::variable_map variables; +}; + +} + +#endif // VCPPD_VCD_BUILDER_H diff --git a/include/vcppd/scope.h b/include/vcppd/scope.h new file mode 100644 index 0000000..da676dd --- /dev/null +++ b/include/vcppd/scope.h @@ -0,0 +1,57 @@ +#ifndef VCPPD_VCD_SCOPE_H +#define VCPPD_VCD_SCOPE_H + +#include "vcppd/vcd.h" + +#include + +namespace vcppd +{ + +template class Scope +{ + friend T; + +public: + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; + + Scope scope(const std::string& name) + { + return Scope(name, *this); + } + + T& unscope() + { + parent.unscope(); + return parent; + } + + template Scope& trace(const std::string& name, const U& ref) + { + parent.trace(this->name + "_" + name, ref, name); + return *this; + } + +private: + Scope(const std::string& name, T& parent) : + name(name), + parent(parent) + { + } + + template + Scope& trace(const std::string& name, const U& ref, + const std::string& short_name) + { + parent.trace(this->name + "_" + name, ref, short_name); + return *this; + } + + std::string name; + T& parent; +}; + +} + +#endif // VCPPD_VCD_SCOPE_H diff --git a/include/vcppd/vcd.h b/include/vcppd/vcd.h new file mode 100644 index 0000000..851a9ef --- /dev/null +++ b/include/vcppd/vcd.h @@ -0,0 +1,45 @@ +#ifndef VCPPD_VCD_H +#define VCPPD_VCD_H + +#include +#include +#include +#include + +namespace vcppd +{ + +namespace detail +{ +struct variable_entry +{ + const void* ref; + size_t size; +}; +using variable_map = std::unordered_map; + +} + +template class Scope; +class Builder; + +class Vcd +{ + friend Builder; + +public: + ~Vcd(); + + void tick(); + +private: + Vcd(const Builder&); + + std::ostream& stream; + unsigned int current_time; + detail::variable_map variables; +}; + +} + +#endif // VCPPD_VCD_H diff --git a/include/vcppd/vcppd.h b/include/vcppd/vcppd.h new file mode 100644 index 0000000..739a1d2 --- /dev/null +++ b/include/vcppd/vcppd.h @@ -0,0 +1,8 @@ +#ifndef VCPPD_H +#define VCPPD_H + +#include "vcppd/builder.h" +#include "vcppd/scope.h" +#include "vcppd/vcd.h" + +#endif // VCPPD_H diff --git a/src/builder.cpp b/src/builder.cpp new file mode 100644 index 0000000..a2c8a08 --- /dev/null +++ b/src/builder.cpp @@ -0,0 +1,34 @@ +#include "vcppd/builder.h" + +#include "vcppd/scope.h" + +#include +#include + +using namespace vcppd; + +Builder::Builder(std::ostream& stream) : + stream(stream) +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + + stream << "$version Generated by vcppd $end" << std::endl; + stream << "$date " << std::put_time(&tm, "%A %B %e %T %Y") << " $end" + << std::endl; + stream << "$timescale 1ns $end" << std::endl; +} + +Vcd Builder::build() const +{ + stream << "$enddefinitions $end" << std::endl; + return Vcd(*this); +} + +Scope Builder::scope(const std::string& name) +{ + stream << "$scope module " << name << " $end" << std::endl; + return Scope(name, *this); +} + +void Builder::unscope() { stream << "$upscope $end" << std::endl; } diff --git a/src/vcd.cpp b/src/vcd.cpp new file mode 100644 index 0000000..abbff30 --- /dev/null +++ b/src/vcd.cpp @@ -0,0 +1,37 @@ +#include "vcppd/vcd.h" + +#include "vcppd/builder.h" + +#include +#include +#include + +using namespace vcppd; + +Vcd::Vcd(const Builder& builder) : + stream(builder.stream), + variables(builder.variables), + current_time(0) +{ +} + +Vcd::~Vcd() { stream << std::format("#{}", current_time) << std::endl; } + +void Vcd::tick() +{ + stream << std::format("#{}", current_time) << std::endl; + + for (const auto& [name, entry] : variables) + { + stream << "b"; + for (int i = 0; i < entry.size; i++) + { + uint8_t byte; + memcpy(&byte, static_cast(entry.ref), 1); + stream << std::format("{0:b}", byte); + } + stream << " " << name << std::endl; + } + + current_time += 1; +} diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 0000000..1a6c84f --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,22 @@ +#include "vcppd/vcppd.h" + +int main(int argc, char** argv) +{ + uint8_t eae = 0; + uint8_t eae2 = 0; + + auto vcd = vcppd::Builder(std::cout) + .scope("TOP") + .trace("eae", eae) + .trace("eae2", eae2) + .unscope() + .build(); + for (int i = 0; i < 10; i++) + { + vcd.tick(); + eae += 1; + eae2 += 2; + } + + return 0; +}