diff --git a/cpp_to_py/common/CMakeLists.txt b/cpp_to_py/common/CMakeLists.txt index 5233d2f..0fe9813 100644 --- a/cpp_to_py/common/CMakeLists.txt +++ b/cpp_to_py/common/CMakeLists.txt @@ -1,13 +1,28 @@ -file(GLOB_RECURSE SRC_FILES_XPLACE_COMMON ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +file(GLOB_RECURSE SRC_FILES_XPLACE_COMMON ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) find_library(LIBDEF def ${PATH_THIRDPARTY_ROOT}/lefdef/def58/lib) find_library(LIBLEF lef ${PATH_THIRDPARTY_ROOT}/lefdef/lef58/lib) -add_library(xplace_common SHARED ${SRC_FILES_XPLACE_COMMON}) +# BISON/FLEX +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/io/verilog) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +find_package(BISON REQUIRED) +find_package(FLEX REQUIRED) +message(STATUS "BISON_EXECUTABLE=${BISON_EXECUTABLE}") +message(STATUS "FLEX_EXECUTABLE=${FLEX_EXECUTABLE}") + +# Verilog scan/parse. +bison_target(verilog_parser ${CMAKE_CURRENT_SOURCE_DIR}/io/verilog/verilog_parser.yy ${CMAKE_CURRENT_BINARY_DIR}/verilog_parser.tab.cc) +flex_target(verilog_lexer ${CMAKE_CURRENT_SOURCE_DIR}/io/verilog/verilog_lexer.l ${CMAKE_CURRENT_BINARY_DIR}/verilog_lexer.yy.cc) +add_flex_bison_dependency(verilog_lexer verilog_parser) + +add_library(xplace_common SHARED ${SRC_FILES_XPLACE_COMMON} ${FLEX_verilog_lexer_OUTPUTS} ${BISON_verilog_parser_OUTPUTS}) + +find_package(ZLIB) target_include_directories( xplace_common PRIVATE ${PROJECT_SOURCE_DIR}/cpp_to_py ${PATH_THIRDPARTY_ROOT}/lefdef ${TORCH_INCLUDE_DIRS}) target_link_libraries( - xplace_common PRIVATE torch ${TORCH_PYTHON_LIBRARY} ${LIBDEF} ${LIBLEF}) + xplace_common PRIVATE torch stdc++fs ${TORCH_PYTHON_LIBRARY} ${LIBDEF} ${LIBLEF} ZLIB::ZLIB) target_compile_options(xplace_common PRIVATE -fPIC) install(TARGETS diff --git a/cpp_to_py/common/db/Database.cpp b/cpp_to_py/common/db/Database.cpp index 15dff85..f0f7864 100644 --- a/cpp_to_py/common/db/Database.cpp +++ b/cpp_to_py/common/db/Database.cpp @@ -18,6 +18,20 @@ using namespace db; +string validate_token(string& name) { + // remove '\' + string::size_type pos = 0; + while ((pos = name.find('\\', pos)) != string::npos) { + name.erase(pos, 1); + } + // remove ' ' + pos = 0; + while ((pos = name.find(' ', pos)) != string::npos) { + name.erase(pos, 1); + } + return name; +} + /***** Database *****/ Database::Database() { clear(); @@ -74,8 +88,9 @@ void Database::load() { readConstraints(setting.Constraints); } + // verilog is unused now if (setting.Verilog != "") { - readVerilog(setting.Verilog); + readVerilog_yy(setting.Verilog); } logger.info("Finish loading rawdb"); @@ -690,6 +705,7 @@ void Database::setup() { Layer& Database::addLayer(const string& name, const char type) { layers.emplace_back(name, type); Layer& newlayer = layers.back(); + name_layers.emplace(name, &newlayer); if (layers.size() == 1) { if (type == 'r') { newlayer.rIndex = 0; diff --git a/cpp_to_py/common/db/Database.h b/cpp_to_py/common/db/Database.h index 36e2eaf..ba2bbc6 100644 --- a/cpp_to_py/common/db/Database.h +++ b/cpp_to_py/common/db/Database.h @@ -2,6 +2,7 @@ #include "DatabaseClass.h" #include "Setting.h" +std::string validate_token(std::string& name); namespace db { @@ -37,6 +38,11 @@ public: robin_hood::unordered_map name_nets; robin_hood::unordered_map name_iopins; robin_hood::unordered_map name_viatypes; + robin_hood::unordered_map name_layers; + bool verilog_read = false; + + string module_name = ""; + VerilogParser* verilog_parser = nullptr; vector layers; vector lefsites; @@ -176,6 +182,7 @@ public: bool writeICCAD2017(const string& inputDef, const string& outputDef); bool writeICCAD2017(const string& outputDef); bool writeComponents(std::ofstream& ofs); + bool writeNets(std::ofstream& ofs); bool writeBuffer(std::ofstream& ofs, const string& line); void writeBufferFlush(std::ofstream& ofs); @@ -190,7 +197,9 @@ public: bool writeBSPl(const std::string& file); bool readVerilog(const std::string& file); + bool readVerilog_yy(const std::string &file); bool readLiberty(const std::string& file); + bool write_verilog(const std::string& file); bool readConstraints(const std::string& file); bool readSize(const std::string& file); diff --git a/cpp_to_py/common/db/DatabaseClass.h b/cpp_to_py/common/db/DatabaseClass.h index 9af8b05..ffa791f 100644 --- a/cpp_to_py/common/db/DatabaseClass.h +++ b/cpp_to_py/common/db/DatabaseClass.h @@ -42,4 +42,5 @@ class PowerNet; class EdgeTypes; class GCellGrid; class BsRouteInfo; +class VerilogParser; } // namespace db diff --git a/cpp_to_py/common/db/Net.cpp b/cpp_to_py/common/db/Net.cpp index eda2228..cb40057 100644 --- a/cpp_to_py/common/db/Net.cpp +++ b/cpp_to_py/common/db/Net.cpp @@ -6,6 +6,7 @@ #include "SNet.h" #include "Via.h" #include "Geometry.h" +#include "Pin.h" using namespace db; @@ -121,6 +122,15 @@ void Net::addPin(Pin* pin) { pins.push_back(pin); } +int Net::getPin(const string& name) { + for (int i = 0; i < (int)pins.size(); i++) { + if (pins[i]->name == name) { + return i; + } + } + return -1; +} + /***** PowerNet *****/ void PowerNet::addRail(SNet* snet, int lx, int hx, int y) { diff --git a/cpp_to_py/common/db/Net.h b/cpp_to_py/common/db/Net.h index ede4595..c946150 100644 --- a/cpp_to_py/common/db/Net.h +++ b/cpp_to_py/common/db/Net.h @@ -75,9 +75,13 @@ public: std::vector pins; const NDR* ndr = nullptr; int gpdb_id = -1; + bool is_port = false; + // s: signal, c: clk, p: power, g: ground + char _type = 's'; Net(const string& name, const NDR* ndr = nullptr); Net(const Net& net); + int getPin(const string& name); bool globalRouted() const { return gRouted; } bool detailedRouted() const { return dRouted; } diff --git a/cpp_to_py/common/db/Pin.cpp b/cpp_to_py/common/db/Pin.cpp index 75eb052..3917694 100644 --- a/cpp_to_py/common/db/Pin.cpp +++ b/cpp_to_py/common/db/Pin.cpp @@ -47,7 +47,9 @@ void IOPin::getBounds(int& lx, int& ly, int& hx, int& hy, int& rIndex) const { /***** Pin *****/ Pin::Pin(const PinType* type) : type(type) {} -Pin::Pin(Cell* cell, int i) : cell(cell), type(cell->ctype()->pins[i]), parentCellPinId(i) {} +Pin::Pin(Cell* cell, int i) : cell(cell), type(cell->ctype()->pins[i]), parentCellPinId(i) { + name = cell->name() + ":" + type->name(); +} Pin::Pin(IOPin* iopin) : iopin(iopin), type(iopin->type) {} Pin::Pin(const Pin& pin) : cell(pin.cell), net(pin.net), type(pin.type) {} diff --git a/cpp_to_py/common/db/Pin.h b/cpp_to_py/common/db/Pin.h index 177f963..264b1f2 100644 --- a/cpp_to_py/common/db/Pin.h +++ b/cpp_to_py/common/db/Pin.h @@ -113,6 +113,7 @@ public: int gpdb_id = -1; int parentCellPinId = -1; bool is_connected = false; + string name = ""; PinSTA* staInfo = nullptr; diff --git a/cpp_to_py/common/io/bison_verilog.cpp b/cpp_to_py/common/io/bison_verilog.cpp new file mode 100644 index 0000000..61f9332 --- /dev/null +++ b/cpp_to_py/common/io/bison_verilog.cpp @@ -0,0 +1,226 @@ +#include +#include + +#include "common/db/Cell.h" +#include "common/db/Database.h" +#include "common/db/Net.h" +#include "common/db/Pin.h" +#include "verilog/verilog_driver.hpp" +namespace db { + +// helper type for the visitor #4 +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; + +class VerilogParser : public verilog::ParserVerilogInterface { +public: + Database &db; + bool lef_read = false; + bool def_read = false; + VerilogParser() = default; + VerilogParser(Database *db, bool lef_read, bool def_read) : db(*db), lef_read(lef_read), def_read(def_read) {} + virtual ~VerilogParser() {} + vector ports; + + void add_module(std::string &&name) { + std::cout << "Module name = " << name << '\n'; + db.module_name = name; + } + + void add_port(verilog::Port &&port) { + ports.push_back(port); + auto addIOPin = [&](const std::string &iopinname, const char direction) { + IOPin *iopin; + if (def_read) { + iopin = db.getIOPin(iopinname); + if (!iopin) { + logger.warning("IO pin is not defined: %s", iopinname.c_str()); + return; + } + } else { + iopin = db.addIOPin(iopinname, iopinname, direction); + } + + Pin *pin = iopin->pin; + iopin->is_connected = true; + Net *net = db.addNet(iopinname); + net->is_port = true; + + pin->net = net; + pin->is_connected = true; + net->addPin(iopin->pin); + }; + for (auto &name : port.names) { + if (port.dir == verilog::PortDirection::INPUT || port.dir == verilog::PortDirection::OUTPUT) { + char direction = 'x'; + direction = (port.dir == verilog::PortDirection::INPUT) ? 'o' : 'i'; + if ((port.beg != -1) && (port.end != -1)) { + for (int i = port.beg; i >= port.end; i--) { + // INPUT to the chip, output from external + // OUTPUT to the chip, input to external + std::string iopinname(name + "[" + std::to_string(i) + "]"); + addIOPin(iopinname, direction); + } + } else { + std::string iopinname(name); + addIOPin(iopinname, direction); + } + } + } + } + + void add_net(verilog::Net &&net) { + auto addNet = [&](const std::string &netName) { db.addNet(netName); }; + for (auto &name : net.names) { + if ((net.beg != -1) && (net.end != -1)) { + for (int i = net.beg; i >= net.end; i--) { + std::string netName(name + "[" + std::to_string(i) + "]"); + netName = validate_token(netName); + addNet(netName); + } + } else { + std::string netName(name); + netName = validate_token(netName); + addNet(netName); + } + } + } + + void add_assignment(verilog::Assignment &&ast) { + // std::cout << "Assignment: " << ast << '\n'; + // TODO: + } + + void add_instance(verilog::Instance &&inst) { + // remove '\' in the head + string cellName(inst.inst_name); + cellName = validate_token(cellName); + Cell *cell; + if (def_read) { + cell = db.getCell(cellName); + if (!cell) { + logger.error("Cell is not defined: %s", cellName.c_str()); + return; + } + } else { + // // TODO: + // string macroName(inst.module_name); + // CellType *celltype = db.getCellType(macroName); + // if (!celltype) { + // celltype = db.addCellType(macroName, db.celltypes.size()); + // for (auto [cellpin_name, cellpin] : db.cell_libs_[0]->cell(macroName)->cellpins) { + // char direction = 'x'; + // char type = 's'; + // switch (*cellpin.direction) { + // case gt::CellPortDirection::input: + // direction = 'i'; + // break; + // case gt::CellPortDirection::output: + // direction = 'o'; + // break; + // case gt::CellPortDirection::inout: + // if (cellpin_name != "VDD" && cellpin_name != "vdd" && cellpin_name != "VSS" && cellpin_name != "vss") { + // logger.warning("unknown pin %s.%s direction: %s", macroName.c_str(), cellpin_name.c_str(), "INOUT"); + // } + // break; + // default: + // logger.error("unknown pin %s.%s direction: %s", macroName.c_str(), cellpin_name.c_str(), "UNKNOWN"); + // break; + // } + // PinType* pintype = celltype->addPin(cellpin_name, direction, type); + // } + // } + + // cell = db.addCell(cellName, celltype); + } + for (size_t i = 0; i < inst.pin_names.size(); i++) { + if (inst.net_names[i].size() > 1) logger.error("Bus net name is not supported\n"); + // define std::string and NetBit visit methods + std::string pin_name = + std::visit(overloaded{ + [](std::string &v) { return v; }, + [](verilog::NetBit &v) { return v.name + '[' + std::to_string(v.bit) + ']'; }, + [](verilog::NetRange &v) { return v.name + '[' + std::to_string(v.beg) + ':' + std::to_string(v.end) + ']'; }, + [](verilog::Constant &v) { return v.value; }, + }, + inst.pin_names[i]); + // printf("gate:pin %s:%s\n", cellName.c_str(), pin_name.c_str()); + + std::string net_name = + std::visit(overloaded{ + [](std::string &v) { return v; }, + [](verilog::NetBit &v) { return v.name + '[' + std::to_string(v.bit) + ']'; }, + [](verilog::NetRange &v) { return v.name + '[' + std::to_string(v.beg) + ':' + std::to_string(v.end) + ']'; }, + [](verilog::Constant &v) { return v.value; }, + }, + inst.net_names[i][0]); + + std::string pinName(pin_name); + std::string netName(net_name); + netName = validate_token(netName); + Net *net = db.getNet(netName); + if (!net) logger.error("Net is not defined: %s", netName.c_str()); + Pin *pin; + if (def_read) { + pin = cell->pin(pinName); + if (!pin) logger.error("Pin is not defined: %s", pinName.c_str()); + } else { + pin = cell->pin(pinName); + if (!pin) logger.error("Pin is not defined: %s", pinName.c_str()); + } + pin->net = net; + net->addPin(pin); + pin->is_connected = true; + } + cell->is_connected = true; + } +}; + +template +void removeDuplicates(std::vector &vec) { + std::unordered_set uniqueElements; + vec.erase(std::remove_if(vec.begin(), vec.end(), [&uniqueElements](const T &element) { return !uniqueElements.insert(element).second; }), + vec.end()); +} + +bool Database::readVerilog_yy(const std::string &file) { + verilog_parser = new VerilogParser(this, lef_read, def_read); + verilog_parser->read(file); + + // remove empty nets + for (int i = 0; i < (int)nets.size(); i++) { + if (nets[i]->pins.size() == 0) { + nets.erase(nets.begin() + i); + logger.warning("Empty net %s", nets[i]->name.c_str()); + i--; + } + } + + // remove duplicates in net pins + for (auto &net : nets) { + auto &pins = net->pins; + removeDuplicates(net->pins); + } + + return true; +} + +string tokenize_name(const string& name) { + // if '[' or ']' in the name, add '\' in the front + string new_name; + for (auto &c : name) { + if (c == '[' || c == ']' || c == '$' || c == '.') { + new_name += '\\'; + break; + } + } + new_name += name; + return new_name; +} + +} \ No newline at end of file diff --git a/cpp_to_py/common/io/file_lefdef_db.cpp b/cpp_to_py/common/io/file_lefdef_db.cpp index 1833ffc..065b4d7 100644 --- a/cpp_to_py/common/io/file_lefdef_db.cpp +++ b/cpp_to_py/common/io/file_lefdef_db.cpp @@ -393,6 +393,18 @@ bool Database::readDEFPG(const std::string& file) { return true; } +string expand_name(const std::string& name) { + // add '\' before '[' or ']' + std::string result; + for (char c : name) { + if (c == '[' || c == ']') { + result.push_back('\\'); + } + result.push_back(c); + } + return result; +} + bool Database::writeComponents(std::ofstream& ofs) { int nCells = cells.size(); ofs << "COMPONENTS " << nCells << " ;" << std::endl; @@ -437,6 +449,40 @@ bool Database::writeComponents(std::ofstream& ofs) { return true; } +bool Database::writeNets(std::ofstream& ofs) { + int nNets = nets.size(); + ofs << "NETS " << nNets << " ;" << std::endl; + for (int i = 0; i < nNets; i++) { + Net* net = nets[i]; + std::ofstream& oss = ofs; + + oss << " - " << expand_name(net->name) << " \n"; + for (Pin* pin : net->pins) { + if (pin->iopin) + oss << " ( PIN " + << " " << pin->type->name() << " )"; + else + oss << " ( " << expand_name(pin->cell->name()) << " " << pin->type->name() << " )"; + } + + const char use(net->_type); + if (use == 'p') { + oss << " + USE POWER ;\n"; + } else if (use == 'g') { + oss << " + USE GROUND ;\n"; + } else if (use == 's') { + oss << " + USE SIGNAL ;\n"; + } else if (use == 'c') { + oss << " + USE CLOCK ;\n"; + } else { + oss << ";\n"; + } + } + + ofs << "END NETS\n\n"; + return true; +} + bool Database::writeICCAD2017(const string& inputDef, const string& outputDef) { std::ifstream ifs(inputDef.c_str()); if (!ifs.good()) { @@ -781,6 +827,15 @@ int readLefLayer(lefrCallbackType_e c, lefiLayer* leflayer, lefiUserData ud) { return 0; } + if (db->name_layers.find(name) != db->name_layers.end()) { + logger.warning("layer type re-defined: %s", name.c_str()); + return 0; + } + + if (type != 'r' && type != 'c') { + logger.warning("unsupported layer type: %s", name.c_str()); + return 0; + } Layer& layer = db->addLayer(name, type); switch (type) { @@ -1502,7 +1557,10 @@ int readDefComponentStart(defrCallbackType_e c, int num, defiUserData ud) { int readDefComponent(defrCallbackType_e c, defiComponent* co, defiUserData ud) { Database* db = (Database*)ud; CellType* celltype = db->getCellType(co->name()); - Cell* cell = db->addCell(co->id(), celltype); + + string cellName(co->id()); + cellName = validate_token(cellName); + Cell* cell = db->addCell(cellName, celltype); if (co->isUnplaced()) { cell->fixed(false); @@ -1787,7 +1845,29 @@ int readDefNet(defrCallbackType_e c, defiNet* dnet, defiUserData ud) { return 0; } - Net* net = db->addNet(dnet->name(), ndr); + string netName(dnet->name()); + netName = validate_token(netName); + // exclude VDD and VSS TODO: + if (netName == "VDD" || netName == "VSS") { + return 0; + } + + Net* net = db->addNet(netName, ndr); + + if (dnet->hasUse()) { + const string use(dnet->use()); + if (use == "POWER") { + net->_type = 'p'; + } else if (use == "GROUND") { + net->_type = 'g'; + } else if (use == "SIGNAL") { + net->_type = 's'; + } else if (use == "CLOCK") { + net->_type = 'c'; + } else { + logger.error("unknown use: %s", use.c_str()); + } + } for (unsigned i = 0; i != (unsigned)dnet->numConnections(); ++i) { Pin* pin = nullptr; @@ -1805,6 +1885,7 @@ int readDefNet(defrCallbackType_e c, defiNet* dnet, defiUserData ud) { iopin->is_connected = true; } else { string cellname(dnet->instance(i)); + cellname = validate_token(cellname); string pinname(dnet->pin(i)); Cell* cell = db->getCell(cellname); if (!cell) { diff --git a/cpp_to_py/common/io/verilog/verilog_data.hpp b/cpp_to_py/common/io/verilog/verilog_data.hpp new file mode 100644 index 0000000..59366e6 --- /dev/null +++ b/cpp_to_py/common/io/verilog/verilog_data.hpp @@ -0,0 +1,254 @@ +#ifndef DATA_VERILOG_HPP_ +#define DATA_VERILOG_HPP_ + +#include +#include +#include +#include +#include +#include + + +namespace verilog { + + enum class ConstantType { + NONE, + INTEGER, + BINARY, + OCTAL, + DECIMAL, + HEX, + REAL, + EXP + }; + + inline std::ostream& operator<<(std::ostream& os, const ConstantType& t) { + switch(t) { + case ConstantType::NONE: os << "NONE"; break; + case ConstantType::INTEGER: os << "INTEGER"; break; + case ConstantType::BINARY: os << "BINARY"; break; + case ConstantType::OCTAL: os << "OCTAL"; break; + case ConstantType::DECIMAL: os << "DECIMAL"; break; + case ConstantType::HEX: os << "HEX"; break; + case ConstantType::REAL: os << "REAL"; break; + case ConstantType::EXP: os << "EXP"; break; + } + return os; + } + + struct Constant { + Constant() = default; // Need this default constructor for return token + Constant(std::string&& v, ConstantType t) : value(std::move(v)), type(t) {} + std::string value; + ConstantType type {ConstantType::NONE}; + }; + + inline std::ostream& operator<<(std::ostream& os, const Constant& c) { + std::cout << " constant value: " << c.value << " type: " << c.type; + return os; + } + + enum class PortDirection { + INPUT, + OUTPUT, + INOUT + }; + + inline std::ostream& operator<<(std::ostream& os, const PortDirection& dir) { + switch(dir){ + case PortDirection::INPUT: os << "INPUT"; break; + case PortDirection::OUTPUT: os << "OUTPUT"; break; + case PortDirection::INOUT: os << "INOUT"; break; + } + return os; + } + + enum class ConnectionType { + NONE, + WIRE, + REG + }; + + inline std::ostream& operator<<(std::ostream& os, const ConnectionType& ct) { + switch(ct){ + case ConnectionType::NONE: os << "NONE"; break; + case ConnectionType::WIRE: os << "WIRE"; break; + case ConnectionType::REG: os << "REG"; break; + } + return os; + } + + struct Port { + std::vector names; + int beg {-1}; + int end {-1}; + PortDirection dir; + ConnectionType type {ConnectionType::NONE}; + }; + + inline std::ostream& operator<<(std::ostream& os, const Port& port) { + os << "beg: " << port.beg << " end: " << port.end << '\n'; + os << "Dir: " << port.dir << " type: " << port.type << '\n'; + for(const auto& n: port.names){ + os << n << '\n'; + } + return os; + } + + enum class NetType { + NONE, + REG, + WIRE, + WAND, + WOR, + TRI, + TRIOR, + TRIAND, + SUPPLY0, + SUPPLY1 + }; + + inline std::ostream& operator<<(std::ostream& os, const NetType& t) { + switch(t){ + case NetType::NONE: os << "NONE"; break; + case NetType::REG: os << "REG"; break; + case NetType::WIRE: os << "WIRE"; break; + case NetType::WAND: os << "WAND"; break; + case NetType::WOR: os << "WOR"; break; + case NetType::TRI: os << "TRI"; break; + case NetType::TRIAND: os << "TRIAND"; break; + case NetType::TRIOR: os << "TRIOR"; break; + case NetType::SUPPLY0: os << "SUPPLY0"; break; + case NetType::SUPPLY1: os << "SUPPLY1"; break; + } + return os; + } + + struct Net { + std::vector names; + int beg {-1}; + int end {-1}; + NetType type {NetType::NONE}; + }; + + inline std::ostream& operator<<(std::ostream& os, const Net& net) { + os << "beg: " << net.beg << " end: " << net.end << '\n'; + os << "type: " << net.type << '\n'; + for(const auto& n: net.names){ + os << n << '\n'; + } + return os; + } + + struct NetBit { + NetBit(std::string&& n, int b): name(std::move(n)), bit(b) {} + std::string name; + int bit {-1}; + }; + + inline std::ostream& operator<<(std::ostream& os, const NetBit& n) { + os << n.name << '[' << n.bit << "] "; + return os; + } + + struct NetRange { + NetRange(std::string&& n, int b, int e): name(std::move(n)), beg(b), end(e) {} + std::string name; + int beg {-1}; + int end {-1}; + }; + + inline std::ostream& operator<<(std::ostream& os, const NetRange& n) { + os << n.name << '[' << n.beg << ':' << n.end << "] "; + return os; + } + + struct Assignment { + // Left hand side can be: a wire, a bit in a wire, a part of a wire + std::vector> lhs; + + // Right hand side can be: a wire, a bit in a wire, a part of a wire, a constant + std::vector> rhs; + }; + + inline std::ostream& operator<<(std::ostream& os, const Assignment& ast) { + os << "LHS: "; + for(const auto& l: ast.lhs){ + switch(l.index()){ + case 0: os << std::get<0>(l) << ' '; break; + case 1: os << std::get<1>(l).name << '/' << std::get<1>(l).bit << ' '; + break; + case 2: os << std::get<2>(l).name << '/' << std::get<2>(l).beg << '/' << std::get<2>(l).end << ' '; + break; + } + } + os << '\n'; + os << "RHS: "; + for(const auto& r: ast.rhs){ + switch(r.index()){ + case 0: os << std::get<0>(r) << ' '; break; + case 1: os << std::get<1>(r).name << '/' << std::get<1>(r).bit << ' '; + break; + case 2: os << std::get<2>(r).name << '/' << std::get<2>(r).beg << '/' << std::get<2>(r).end << ' '; + break; + case 3: os << std::get<3>(r) << ' '; break; + } + } + + return os; + } + + using NetConcat = std::variant; + + struct Instance { + std::string module_name; + std::string inst_name; + + // pin_names might be empty. e.g. my_module m1(net1, net2); + std::vector> pin_names; + std::vector> net_names; + }; + + inline std::ostream& operator<<(std::ostream& os, const Instance& inst) { + os << inst.module_name << ' ' << inst.inst_name << '('; + + if(!inst.pin_names.empty()){ + for(size_t i=0; i 1){ + std::cout << '{'; + } + for(const auto& v: inst.net_names[i]){ + std::visit([](const auto& v){ std::cout << v << ' '; }, v); + } + if(inst.net_names[i].size() > 1){ + std::cout << '}'; + } + std::cout << ") " ; + } + } + else{ + for(size_t i=0; i 1){ + std::cout << '{'; + } + for(const auto& v: inst.net_names[i]){ + std::visit([](const auto& v){ std::cout << v << ' '; }, v); + } + if(inst.net_names[i].size() > 1){ + std::cout << '}'; + } + std::cout << ','; + } + } + os << ')'; + return os; + } + + + +} +#endif + + diff --git a/cpp_to_py/common/io/verilog/verilog_driver.hpp b/cpp_to_py/common/io/verilog/verilog_driver.hpp new file mode 100644 index 0000000..af9e36b --- /dev/null +++ b/cpp_to_py/common/io/verilog/verilog_driver.hpp @@ -0,0 +1,54 @@ +#ifndef PARSER_VERILOG_HPP_ +#define PARSER_VERILOG_HPP_ + +#include +#include +#include +#include +#include +#include + +#include "verilog_scanner.hpp" +#include "verilog_parser.tab.hh" + +namespace verilog { + +class ParserVerilogInterface { + public: + virtual ~ParserVerilogInterface(){ + if(_scanner) delete _scanner; + if(_parser) delete _parser; + } + virtual void add_module(std::string&&) = 0; + // port names, begin index, end index, port type (IOB), connection type (wire, reg) + virtual void add_port(Port&&) = 0; + virtual void add_net(Net&&) = 0; + virtual void add_assignment(Assignment&&) = 0; + virtual void add_instance(Instance&&) = 0; + + void read(const std::filesystem::path&); + + private: + VerilogScanner* _scanner {nullptr}; + VerilogParser* _parser {nullptr}; +}; + +inline void ParserVerilogInterface::read(const std::filesystem::path& p){ + if(! std::filesystem::exists(p)){ + return ; + } + + std::ifstream ifs(p); + + if(!_scanner){ + _scanner = new VerilogScanner(&ifs); + } + if(!_parser){ + _parser = new VerilogParser(*_scanner, this); + } + _parser->parse(); +} + + +} +#endif diff --git a/cpp_to_py/common/io/verilog/verilog_lexer.l b/cpp_to_py/common/io/verilog/verilog_lexer.l new file mode 100644 index 0000000..ce7f194 --- /dev/null +++ b/cpp_to_py/common/io/verilog/verilog_lexer.l @@ -0,0 +1,184 @@ +%{ +/* C++ string header, for string ops below */ +#include +#include + +/* Implementation of yyFlexScanner */ +#include "verilog_scanner.hpp" +#undef YY_DECL +#define YY_DECL int verilog::VerilogScanner::yylex(verilog::VerilogParser::semantic_type * const lval, verilog::VerilogParser::location_type *loc ) + +/* typedef to make the returns for the tokens shorter */ +using token = verilog::VerilogParser::token; + +/* define yyterminate as this instead of NULL */ +#define yyterminate() return( token::END ) + +/* msvc2010 requires that we exclude this header file. */ +#define YY_NO_UNISTD_H + +/* update location on matching */ +#define YY_USER_ACTION loc->step(); loc->columns(yyleng); +%} + +/* Make the generated scanner run in debug mode. */ +%option debug + +/* Cause the default rule (that unmatched scanner input is echoed to stdout) to be suppressed. */ +%option nodefault + +/* Inform flex that a derived NAME is implemented as a subclass of yyFlexLexer, so flex will place your actions in the member function foo::yylex() instead of yyFlexLexer::yylex(). */ +%option yyclass="verilog::VerilogScanner" + +/* Set yywrap always returns 1. yywrap is called when reaching EOF */ +%option noyywrap + +/* Suppress the warning message yyunput is defined but not used */ +%option nounput +%option never-interactive +%option c++ + +/* Define inclusive/exclusive states */ +%x in_comment +%x in_attribute + +/* Predefined rules */ +NEWLINE "\n"|"\r\n" +SPACE " "|"\t"|"\f" +COMMENT_BEGIN "/*" +COMMENT_END "*/" +COMMENT_LINE "//".*\n + +ATTRIBUTE_BEGIN "(*" +ATTRIBUTE_END "*)" + +/* Name rule http://www.asic-world.com/verilog/syntax1.html */ +NAME [_a-zA-Z][$_a-zA-Z0-9]*{0,1023} +/* + Check the escape rule inside character class + https://www.regular-expressions.info/charclass.html + */ +ESCAPED_NAME \\[\\^!"#$%&',()*+\-.a-zA-Z0-9/{|}~[\]_:;<=>?@]+[\t\f ] +INTEGER [1-9][0-9]*|0 +BINARY [+-]?[0-9]*"'"[Bb][01_xXzZ]+ +OCTAL [+-]?[0-9]*"'"[Oo][0-7_xXzZ]+ +DECIMAL [+-]?[0-9]*"'"[Dd][0-9_xXzZ]+ +HEX [+-]?[0-9]*"'"[Hh][0-9a-fA-F_xXzZ]+ +REAL [-+]?([0-9]*\.[0-9]+|[0-9]+) +EXP [-+]?[0-9]+"."?[0-9]*"E"[-+]?[0-9]+ + + +/* Token rule section begins */ +%% +%{ /** Code executed at the beginning of yylex **/ + yylval = lval; +%} + +<*>{SPACE} { /* ignore any space */ } +{NEWLINE} { // Update line numbers + loc->lines(); + // return( token::NEWLINE ); + } + + +{COMMENT_LINE} { loc->lines(); } + +{COMMENT_BEGIN} { BEGIN(in_comment); } +<> { + BEGIN(INITIAL); + std::cerr << "Unclosed comment at line " << loc->end.line << " col " << loc->end.column << '\n'; + yyterminate(); + } +{NEWLINE} { loc->lines(); } +. { /* ignore characters in comment */ } +{COMMENT_END} { BEGIN(INITIAL); } + + +{ATTRIBUTE_BEGIN} { BEGIN(in_attribute); } +<> { + BEGIN(INITIAL); + std::cerr << "Unclosed attribute at line " << loc->end.line << " col " << loc->end.column << '\n'; + yyterminate(); + } +{NEWLINE} { loc->lines(); } +. { /* ignore characters in comment */ } +{ATTRIBUTE_END} { BEGIN(INITIAL); } + + +":"|"."|"{"|"}"|"["|"]"|","|"*"|";"|"="|"-"|"+"|"|"|"("|")" { + return yytext[0]; +} + +module { return token::MODULE; } +endmodule { return token::ENDMODULE; } +input { return token::INPUT; } +output { return token::OUTPUT; } +inout { return token::INOUT; } +reg { return token::REG; } +wire { return token::WIRE; } +wor { return token::WOR; } +wand { return token::WAND; } +tri { return token::TRI; } +trior { return token::TRIOR; } +triand { return token::TRIAND; } +SUPPLY0 { return token::SUPPLY0; } +SUPPLY1 { return token::SUPPLY1; } +assign { return token::ASSIGN; } + + +{ESCAPED_NAME} { + yylval->build(yytext); + return token::ESCAPED_NAME; +} + +{NAME} { + yylval->build( yytext ); + return token::NAME; +} + +{INTEGER} { + yylval->build(verilog::Constant(yytext, verilog::ConstantType::INTEGER)); + return token::INTEGER; + } + +{BINARY} { + yylval->build(verilog::Constant(yytext, verilog::ConstantType::BINARY)); + return token::BINARY; + } + +{OCTAL} { + yylval->build(verilog::Constant(yytext, verilog::ConstantType::OCTAL)); + return token::OCTAL; + } + +{DECIMAL} { + yylval->build(verilog::Constant(yytext, verilog::ConstantType::DECIMAL)); + return token::DECIMAL; + } + +{HEX} { + yylval->build(verilog::Constant(yytext, verilog::ConstantType::HEX)); + return token::HEX; + } + +{REAL} { + verilog::Constant c(yytext, verilog::ConstantType::REAL); + return token::REAL; + } + +{EXP} { + verilog::Constant c(yytext, verilog::ConstantType::EXP); + return token::EXP; + } + + + + +. { + /* Last rule catches any unmatched character */ + std::cerr << "Failed to match : " << yytext << '\n'; + yyterminate(); + } +%% + + diff --git a/cpp_to_py/common/io/verilog/verilog_parser.yy b/cpp_to_py/common/io/verilog/verilog_parser.yy new file mode 100644 index 0000000..10c2737 --- /dev/null +++ b/cpp_to_py/common/io/verilog/verilog_parser.yy @@ -0,0 +1,484 @@ +%skeleton "lalr1.cc" +%require "3.0" +%debug +%defines +%define api.namespace {verilog} +%define parser_class_name {VerilogParser} + +%define parse.error verbose + +%code requires{ + #include "verilog_data.hpp" + + namespace verilog { + class ParserVerilogInterface; + class VerilogScanner; + } + +// The following definitions is missing when %locations isn't used +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif + +} + +%parse-param { VerilogScanner &scanner } +%parse-param { ParserVerilogInterface *driver } + +%code { + #include + #include + #include + #include + #include + + /* include for all driver functions */ + #include "verilog_driver.hpp" + +#undef yylex +#define yylex scanner.yylex +} + +%define api.value.type variant +%define parse.assert + + +%left '-' '+' +%left '*' '/' +%left UMINUS + +%token END 0 "end of file" +%token NEWLINE +%token UNDEFINED + +/* Valid name (Identifiers) */ +%token NAME +%token ESCAPED_NAME + +%token INTEGER BINARY OCTAL DECIMAL HEX REAL EXP + +/* Keyword tokens */ +%token MODULE ENDMODULE INPUT OUTPUT INOUT REG WIRE WAND WOR TRI TRIOR TRIAND SUPPLY0 SUPPLY1 ASSIGN + + +/* Nonterminal Symbols */ +%type valid_name + +%type> port_type +%type port_declarations port_decl port_decl_statements + +%type net_type +%type net_decl_statements net_decl + +%type constant +%type assignment +%type>> lhs lhs_concat lhs_exprs lhs_expr +%type> rhs rhs_concat rhs_exprs rhs_expr + +%type instance +%type>, std::vector>>> inst_pins nets_by_name + +%type>> nets_by_position + +%type, std::vector>> net_by_name + +%locations +%start design + +%% + +valid_name + : NAME { $$ = $1; } + | ESCAPED_NAME { $$ = $1; } + ; + +design + : modules; + +modules + : + | modules module + ; + +module + : MODULE valid_name ';' + { + driver->add_module(std::move($2)); + } + statements ENDMODULE + | MODULE valid_name '(' ')' ';' + { + driver->add_module(std::move($2)); + } + statements ENDMODULE + | MODULE valid_name '(' port_names ')' ';' + { + driver->add_module(std::move($2)); + } + statements ENDMODULE + | MODULE valid_name '(' + { + driver->add_module(std::move($2)); + } + port_declarations ')' + { + driver->add_port(std::move($5)); + } + ';' statements ENDMODULE + ; + +// port names are ignored as they will be parsed later in declaration +port_names + : valid_name { } + | port_names ',' valid_name { } + ; + + +port_type + : INPUT { $$ = std::make_pair(verilog::PortDirection::INPUT, verilog::ConnectionType::NONE); } + | INPUT WIRE { $$ = std::make_pair(verilog::PortDirection::INPUT, verilog::ConnectionType::WIRE); } + | OUTPUT { $$ = std::make_pair(verilog::PortDirection::OUTPUT,verilog::ConnectionType::NONE); } + | OUTPUT REG { $$ = std::make_pair(verilog::PortDirection::OUTPUT,verilog::ConnectionType::REG); } + | INOUT { $$ = std::make_pair(verilog::PortDirection::INOUT, verilog::ConnectionType::NONE); } + | INOUT WIRE { $$ = std::make_pair(verilog::PortDirection::INOUT, verilog::ConnectionType::WIRE); } + | INOUT REG { $$ = std::make_pair(verilog::PortDirection::INOUT, verilog::ConnectionType::REG); } + ; + +// e.g. "input a, b, output c, d" is allowed in port declarations +port_declarations + : port_decl + { + $$ = $1; + } + | port_declarations ',' port_decl + { + driver->add_port(std::move($1)); + $$ = $3; + } + | port_declarations ',' valid_name + { + $1.names.emplace_back(std::move($3)); + $$ = $1; + } + ; + +port_decl + : port_type valid_name + { + $$.dir = std::get<0>($1); + $$.type = std::get<1>($1); + $$.names.emplace_back(std::move($2)); + } + | port_type '[' INTEGER ':' INTEGER ']' valid_name + { + $$.dir = std::get<0>($1); + $$.type = std::get<1>($1); + $$.beg = std::stoi($3.value); + $$.end = std::stoi($5.value); + $$.names.push_back(std::move($7)); + } + ; + +statements + : // empty + | statements statement + | statements statement_assign + ; + +statement + : declaration + | instance + ; + + +declaration + : port_decl_statements ';' { driver->add_port(std::move($1)); } + | net_decl_statements ';' { driver->add_net(std::move($1)); } + ; + +// e.g. "input a, b, output c, d" is not allowed in port declaration statements +port_decl_statements + : port_decl + { + $$ = $1; + } + | port_decl_statements ',' valid_name + { + $1.names.emplace_back(std::move($3)); + $$ = $1; + } + ; + + +net_type + : WIRE { $$ = verilog::NetType::WIRE; } + | WAND { $$ = verilog::NetType::WAND; } + | WOR { $$ = verilog::NetType::WOR; } + | TRI { $$ = verilog::NetType::TRI; } + | TRIOR { $$ = verilog::NetType::TRIOR; } + | TRIAND { $$ = verilog::NetType::TRIAND; } + | SUPPLY0 { $$ = verilog::NetType::SUPPLY0; } + | SUPPLY1 { $$ = verilog::NetType::SUPPLY1; } + ; + +net_decl_statements + : net_decl + { + $$ = $1; + } + | net_decl_statements ',' valid_name + { + $1.names.push_back(std::move($3)); + $$ = $1; + } + ; + +net_decl + : net_type valid_name + { + $$.type = $1; + $$.names.push_back(std::move($2)); + } + | net_type '[' INTEGER ':' INTEGER ']' valid_name + { + $$.type = $1; + $$.beg = std::stoi($3.value); + $$.end = std::stoi($5.value); + $$.names.push_back(std::move($7)); + } + ; + + +statement_assign + : ASSIGN assignments ';' + +assignments + : assignment + | assignments ',' assignment + ; + +assignment + : lhs '=' rhs { $$.lhs = $1; $$.rhs = $3; driver->add_assignment(std::move($$)); } + ; + + +// Should try to merge lhs & rhs definition +lhs + : valid_name { $$.push_back(std::move($1)); } + | valid_name '[' INTEGER ']' + { $$.emplace_back(verilog::NetBit(std::move($1), std::stoi($3.value))); } + | valid_name '[' INTEGER ':' INTEGER ']' + { $$.emplace_back(verilog::NetRange(std::move($1), std::stoi($3.value), std::stoi($5.value))); } + | lhs_concat { $$ = $1; } + ; + +lhs_concat + : '{' lhs_exprs '}' { std::move($2.begin(), $2.end(), std::back_inserter($$)); } + ; + +lhs_exprs + : lhs_expr { std::move($1.begin(), $1.end(), std::back_inserter($$)); } + | lhs_exprs ',' lhs_expr + { + std::move($1.begin(), $1.end(), std::back_inserter($$)); + std::move($3.begin(), $3.end(), std::back_inserter($$)); + } + ; + +lhs_expr + : valid_name { $$.push_back(std::move($1)); } + | valid_name '[' INTEGER ']' + { $$.emplace_back(verilog::NetBit(std::move($1), std::stoi($3.value))); } + | valid_name '[' INTEGER ':' INTEGER ']' + { $$.emplace_back(verilog::NetRange(std::move($1), std::stoi($3.value), std::stoi($5.value))); } + | lhs_concat + { std::move($1.begin(), $1.end(), std::back_inserter($$)); } + ; + + + +constant + : INTEGER { $$=$1; } + | BINARY { $$=$1; } + | OCTAL { $$=$1; } + | DECIMAL { $$=$1; } + | HEX { $$=$1; } + | REAL { $$=$1; } + | EXP { $$=$1; } + ; + +rhs + : valid_name { $$.emplace_back($1); } + | valid_name '[' INTEGER ']' + { $$.emplace_back(verilog::NetBit(std::move($1), std::stoi($3.value))); } + | valid_name '[' INTEGER ':' INTEGER ']' + { $$.emplace_back(verilog::NetRange(std::move($1), std::stoi($3.value), std::stoi($5.value))); } + | constant { $$.push_back(std::move($1)); } + | rhs_concat { $$ = $1; } + ; + +rhs_concat + : '{' rhs_exprs '}' { std::move($2.begin(), $2.end(), std::back_inserter($$)); } + ; + +rhs_exprs + : rhs_expr { std::move($1.begin(), $1.end(), std::back_inserter($$)); } + | rhs_exprs ',' rhs_expr + { + std::move($1.begin(), $1.end(), std::back_inserter($$)); + std::move($3.begin(), $3.end(), std::back_inserter($$)); + } + ; + +rhs_expr + : valid_name { $$.push_back(std::move($1)); } + | valid_name '[' INTEGER ']' + { $$.emplace_back(verilog::NetBit(std::move($1), std::stoi($3.value))); } + | valid_name '[' INTEGER ':' INTEGER ']' + { $$.emplace_back(verilog::NetRange(std::move($1), std::stoi($3.value), std::stoi($5.value))); } + | constant { $$.push_back(std::move($1)); } + | rhs_concat + { std::move($1.begin(), $1.end(), std::back_inserter($$)); } + ; + + + + + +instance + : valid_name valid_name '(' inst_pins ')' ';' + { + std::swap($$.module_name, $1); + std::swap($$.inst_name, $2); + std::swap($$.pin_names, std::get<0>($4)); + std::swap($$.net_names, std::get<1>($4)); + driver->add_instance(std::move($$)); + } + | valid_name parameters valid_name '(' inst_pins ')' ';' + { + std::swap($$.module_name, $1); + std::swap($$.inst_name, $3); + std::swap($$.pin_names, std::get<0>($5)); + std::swap($$.net_names, std::get<1>($5)); + driver->add_instance(std::move($$)); + } + ; + +inst_pins + : { } // empty + | nets_by_position { std::swap(std::get<1>($$), $1); } + | nets_by_name + { + std::swap(std::get<0>($$), std::get<0>($1)); + std::swap(std::get<1>($$), std::get<1>($1)); + } + ; + +nets_by_position + : rhs { $$.emplace_back(std::move($1)); } + | nets_by_position ',' rhs + { + std::move($1.begin(), $1.end(), std::back_inserter($$)); + $$.push_back(std::move($3)); + } + ; + + +nets_by_name + : net_by_name + { + std::get<0>($$).push_back(std::move(std::get<0>($1))); + std::get<1>($$).push_back(std::move(std::get<1>($1))); + } + | nets_by_name ',' net_by_name + { + auto &pin_names = std::get<0>($1); + auto &net_names = std::get<1>($1); + std::move(pin_names.begin(), pin_names.end(), std::back_inserter(std::get<0>($$))); + std::move(net_names.begin(), net_names.end(), std::back_inserter(std::get<1>($$))); + + std::get<0>($$).push_back(std::move(std::get<0>($3))); + std::get<1>($$).push_back(std::move(std::get<1>($3))); + } + ; + + +net_by_name + : '.' valid_name '(' ')' + { std::get<0>($$) = $2; } + | '.' valid_name '(' valid_name ')' + { + std::get<0>($$) = $2; + std::get<1>($$).push_back(std::move($4)); + } + | '.' valid_name '(' valid_name '[' INTEGER ']' ')' + { + std::get<0>($$) = $2; + std::get<1>($$).emplace_back(verilog::NetBit(std::move($4), std::stoi($6.value))); + } + // The previous two rules are also in rhs. But I don't want to create special rule just for this case + | '.' valid_name '(' rhs ')' + { + std::get<0>($$) = $2; + std::get<1>($$) = $4; + } + // Bus port bit + | '.' valid_name '[' INTEGER ']' '(' ')' + { + std::get<0>($$) = verilog::NetBit(std::move($2), std::stoi($4.value)); + } + | '.' valid_name '[' INTEGER ']' '(' rhs ')' + { + std::get<0>($$) = verilog::NetBit(std::move($2), std::stoi($4.value)); + std::get<1>($$) = $7; + } + // Bus port part + | '.' valid_name '[' INTEGER ':' INTEGER ']' '(' ')' + { + std::get<0>($$) = verilog::NetRange(std::move($2), std::stoi($4.value), std::stoi($6.value)); + } + | '.' valid_name '[' INTEGER ':' INTEGER ']' '(' rhs ')' + { + std::get<0>($$) = verilog::NetRange(std::move($2), std::stoi($4.value), std::stoi($6.value)); + std::get<1>($$) = $9; + } + ; + + +// parameters are ignored for now +parameters + : '#' '(' param_exprs ')' + ; + +param_exprs + : param_expr + | param_exprs ',' param_expr + ; + +param_expr + : valid_name + | '`' valid_name + | constant + | '-' param_expr %prec UMINUS + | param_expr '+' param_expr + | param_expr '-' param_expr + | param_expr '*' param_expr + | param_expr '/' param_expr + | '(' param_expr ')' + ; + + +%% + +void verilog::VerilogParser::error(const location_type &l, const std::string &err_message) { + std::cerr << "Parser error: " << err_message << '\n' + << " begin at line " << l.begin.line << " col " << l.begin.column << '\n' + << " end at line " << l.end.line << " col " << l.end.column << "\n"; + std::abort(); +} + + diff --git a/cpp_to_py/common/io/verilog/verilog_scanner.hpp b/cpp_to_py/common/io/verilog/verilog_scanner.hpp new file mode 100644 index 0000000..ba68af2 --- /dev/null +++ b/cpp_to_py/common/io/verilog/verilog_scanner.hpp @@ -0,0 +1,36 @@ +#ifndef SCANNER_VERILOG_HPP_ +#define SCANNER_VERILOG_HPP_ + +#if ! defined(yyFlexLexerOnce) +#include +#endif + +#include "verilog_parser.tab.hh" +#include "location.hh" + +namespace verilog { + +class VerilogScanner : public yyFlexLexer{ + public: + + VerilogScanner(std::istream *in) : yyFlexLexer(in) { + }; + virtual ~VerilogScanner() {}; + + //get rid of override virtual function warning + using FlexLexer::yylex; + + virtual + int yylex( verilog::VerilogParser::semantic_type * const lval, + verilog::VerilogParser::location_type *location ); + // YY_DECL defined in mc_lexer.l + // Method body created by flex in mc_lexer.yy.cc + + private: + /* yyval ptr */ + verilog::VerilogParser::semantic_type *yylval = nullptr; +}; + +} /* end namespace MC */ + +#endif diff --git a/cpp_to_py/common/utils/geo.h b/cpp_to_py/common/utils/geo.h index aa8d5cc..7bbb9fb 100644 --- a/cpp_to_py/common/utils/geo.h +++ b/cpp_to_py/common/utils/geo.h @@ -49,6 +49,11 @@ public: bool operator==(const PointT& rhs) const { return x == rhs.x && y == rhs.y; } bool operator!=(const PointT& rhs) const { return !(*this == rhs); } + bool operator<(const PointT& rhs) const { return (x < rhs.x) || ((x == rhs.x) && (y < rhs.y)); } + bool operator>(const PointT& rhs) const { return (x > rhs.x) || ((x == rhs.x) && (y > rhs.y)); } + bool operator<=(const PointT& rhs) const { return !(*this > rhs); } + bool operator>=(const PointT& rhs) const { return !(*this < rhs); } + friend inline std::ostream& operator<<(std::ostream& os, const PointT& pt) { os << "(" << pt.x << ", " << pt.y << ")"; return os;