From c388cebf7f530129d91a2a99936116a258c168d1 Mon Sep 17 00:00:00 2001
From: gatecat <gatecat@ds0.me>
Date: Thu, 25 Mar 2021 16:39:24 +0000
Subject: nexus: Add support for get_pins PDC command

Signed-off-by: gatecat <gatecat@ds0.me>
---
 nexus/pdc.cc | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 7 deletions(-)

diff --git a/nexus/pdc.cc b/nexus/pdc.cc
index 4aef354c..1ffe0a64 100644
--- a/nexus/pdc.cc
+++ b/nexus/pdc.cc
@@ -32,32 +32,55 @@ struct TCLEntity
         ENTITY_CELL,
         ENTITY_PORT,
         ENTITY_NET,
+        ENTITY_PIN,
     } type;
     IdString name;
+    IdString pin; // for cell pins only
 
     TCLEntity(EntityType type, IdString name) : type(type), name(name) {}
+    TCLEntity(EntityType type, IdString name, IdString pin) : type(type), name(name), pin(pin) {}
 
     const std::string &to_string(Context *ctx) { return name.str(ctx); }
 
-    CellInfo *get_cell(Context *ctx)
+    CellInfo *get_cell(Context *ctx) const
     {
         if (type != ENTITY_CELL)
             return nullptr;
         return ctx->cells.at(name).get();
     }
 
-    PortInfo *get_port(Context *ctx)
+    PortInfo *get_port(Context *ctx) const
     {
         if (type != ENTITY_PORT)
             return nullptr;
         return &ctx->ports.at(name);
     }
 
-    NetInfo *get_net(Context *ctx)
+    NetInfo *get_net(Context *ctx) const
     {
-        if (type != ENTITY_NET)
+        if (type == ENTITY_PIN) {
+            CellInfo *cell = nullptr;
+            if (ctx->cells.count(name)) {
+                cell = ctx->cells.at(name).get();
+            } else {
+                const std::string &n = name.str(ctx);
+                auto pos = n.rfind('.');
+                if (pos == std::string::npos)
+                    return nullptr;
+                // remove one hierarchy layer due to Radiant weirdness around PLLs etc
+                IdString stripped_name = ctx->id(n.substr(0, pos));
+                if (!ctx->cells.count(stripped_name))
+                    return nullptr;
+                cell = ctx->cells.at(stripped_name).get();
+            }
+            if (!cell->ports.count(pin))
+                return nullptr;
+            return cell->ports.at(pin).net;
+        } else if (type == ENTITY_NET) {
+            return ctx->nets.at(name).get();
+        } else {
             return nullptr;
-        return ctx->nets.at(name).get();
+        }
     }
 };
 
@@ -201,6 +224,8 @@ struct PDCParser
             return cmd_get_cells(arguments);
         else if (cmd == "get_nets")
             return cmd_get_nets(arguments);
+        else if (cmd == "get_pins")
+            return cmd_get_pins(arguments);
         else if (cmd == "create_clock")
             return cmd_create_clock(arguments);
         else if (cmd == "ldc_set_location")
@@ -286,6 +311,28 @@ struct PDCParser
         return cells;
     }
 
+    TCLValue cmd_get_pins(const std::vector<TCLValue> &arguments)
+    {
+        std::vector<TCLEntity> pins;
+        for (int i = 1; i < int(arguments.size()); i++) {
+            auto &arg = arguments.at(i);
+            if (!arg.is_string)
+                log_error("get_pins expected string arguments (line %d)\n", lineno);
+            std::string s = arg.str;
+            if (s.at(0) == '-')
+                log_error("unsupported argument '%s' to get_pins (line %d)\n", s.c_str(), lineno);
+            auto pos = s.rfind('/');
+            if (pos == std::string::npos)
+                log_error("expected / in cell pin name '%s' (line %d)\n", s.c_str(), lineno);
+            pins.emplace_back(TCLEntity::ENTITY_PIN, ctx->id(s.substr(0, pos)), ctx->id(s.substr(pos + 1)));
+            if (pins.back().get_net(ctx) == nullptr) {
+                log_warning("cell pin '%s' not found\n", s.c_str());
+                pins.pop_back();
+            }
+        }
+        return pins;
+    }
+
     TCLValue cmd_create_clock(const std::vector<TCLValue> &arguments)
     {
         float period = 10;
@@ -311,12 +358,14 @@ struct PDCParser
             } else {
                 for (const auto &ety : arg.list) {
                     NetInfo *net = nullptr;
-                    if (ety.type == TCLEntity::ENTITY_NET)
+                    if (ety.type == TCLEntity::ENTITY_PIN)
+                        net = ety.get_net(ctx);
+                    else if (ety.type == TCLEntity::ENTITY_NET)
                         net = ctx->nets.at(ety.name).get();
                     else if (ety.type == TCLEntity::ENTITY_PORT)
                         net = ctx->ports.at(ety.name).net;
                     else
-                        log_error("create_clock applies only to cells or IO ports (line %d)\n", lineno);
+                        log_error("create_clock applies only to cells, cell pins, or IO ports (line %d)\n", lineno);
                     ctx->addClock(net->name, 1000.0f / period);
                 }
             }
-- 
cgit v1.2.3