text: /*
   * BLIF Netlist Loader
   * ===================
   *
   * This loader handles loading a post-technology mapping fully flattened (i.e not
   * hierarchical) netlist in Berkely Logic Interchange Format (BLIF) file, and
   * builds a netlist data structure (AtomNetlist) from it.
   *
   * BLIF text parsing is handled by the blifparse library, while this file is responsible
   * for creating the netlist data structure.
   *
   * The main object of interest is the BlifAllocCallback struct, which implements the
   * blifparse callback interface.  The callback methods are then called when basic blif
   * primitives are encountered by the parser.  The callback methods then create the associated
   * netlist data structures.
   *
   */
  #include <cstdio>
  #include <cstring>
  #include <ctime>
  #include <sstream>
  #include <unordered_set>
  #include <cctype> //std::isdigit
  using namespace std;
  
  #include "blifparse.hpp"
  #include "atom_netlist.h"
  
  #include "vtr_assert.h"
  #include "vtr_util.h"
  #include "vtr_log.h"
  #include "vtr_logic.h"
  #include "vtr_time.h"
  #include "vtr_digest.h"
  
  #include "vpr_types.h"
  #include "vpr_error.h"
  #include "globals.h"
  #include "read_blif.h"
  #include "arch_types.h"
  #include "echo_files.h"
  #include "../util/hash.h" // specified import more precisely because of header file with same name in tensorflow package
  
  vtr::LogicValue to_vtr_logic_value(blifparse::LogicValue);
  
  struct BlifAllocCallback : public blifparse::Callback {
      public:
          BlifAllocCallback(e_circuit_format blif_format, AtomNetlist& main_netlist, 
                            const std::string netlist_id, 
                            const t_model* user_models, const t_model* library_models)
              : main_netlist_(main_netlist)
              , netlist_id_(netlist_id)
              , user_arch_models_(user_models)
              , library_arch_models_(library_models)
              , blif_format_(blif_format) {
              VTR_ASSERT(blif_format_ == e_circuit_format::BLIF
                         || blif_format_ == e_circuit_format::EBLIF);
          }
  
          static constexpr const char* OUTPAD_NAME_PREFIX = "out:";
  
      public: //Callback interface
          void start_parse() override {}
  
          void finish_parse() override {
              //When parsing is finished we *move* the main netlist
              //into the user object. This ensures we never have two copies
              //(consuming twice the memory).
              size_t main_netlist_idx = determine_main_netlist_index();
              main_netlist_ = std::move(blif_models_[main_netlist_idx]);
          }
  
          void begin_model(std::string model_name) override {
              //Create a new model, and set it's name
  
              blif_models_.emplace_back(model_name, netlist_id_);
              blif_models_black_box_.emplace_back(false);
              ended_ = false;
              set_curr_block(AtomBlockId::INVALID()); //This statement doesn't define a block, so mark invalid
          }
  
          void inputs(std::vector<std::string> input_names) override {
              const t_model* blk_model = find_model(MODEL_INPUT);
  
              VTR_ASSERT_MSG(!blk_model->inputs, "Inpad model has an input port");
              VTR_ASSERT_MSG(blk_model->outputs, "Inpad model has no output port");
              VTR_ASSERT_MSG(blk_model->outputs->size == 1, "Inpad model has non-single-bit output port");
              VTR_ASSERT_MSG(!blk_model->outputs->next, "Inpad model has multiple output ports");
  
              std::string pin_name = blk_model->outputs->name;
              for(const auto& input : input_names) {
                  AtomBlockId blk_id = curr_model().create_block(input, blk_model);
                  AtomPortId port_id = curr_model().create_port(blk_id, blk_model->outputs);
                  AtomNetId net_id = curr_model().create_net(input);
                  curr_model().create_pin(port_id, 0, net_id, PinType::DRIVER);
              }
              set_curr_block(AtomBlockId::INVALID()); //This statement doesn't define a block, so mark invalid
          }
  
          void outputs(std::vector<std::string> output_names) override {
              const t_model* blk_model = find_model(MODEL_OUTPUT);
  
              VTR_ASSERT_MSG(!blk_model->outputs, "Outpad model has an output port");
              VTR_ASSERT_MSG(blk_model->inputs, "Outpad model has no input port");
              VTR_ASSERT_MSG(blk_model->inputs->size == 1, "Outpad model has non-single-bit input port");
              VTR_ASSERT_MSG(!blk_model->inputs->next, "Outpad model has multiple input ports");
  
              std::string pin_name = blk_model->inputs->name;
              for(const auto& output : output_names) {
                  //Since we name blocks based on their drivers we need to uniquify outpad names,
                  //which we do with a prefix
                  AtomBlockId blk_id = curr_model().create_block(OUTPAD_NAME_PREFIX + output, blk_model);
                  AtomPortId port_id = curr_model().create_port(blk_id, blk_model->inputs);
                  AtomNetId net_id = curr_model().create_net(output);
                  curr_model().create_pin(port_id, 0, net_id, PinType::SINK);
              }
              set_curr_block(AtomBlockId::INVALID()); //This statement doesn't define a block, so mark invalid
          }
  
          void names(std::vector<std::string> nets, std::vector<std::vector<blifparse::LogicValue>> so_cover) override {
              const t_model* blk_model = find_model(MODEL_NAMES);
  
              VTR_ASSERT_MSG(nets.size() > 0, "BLIF .names has no connections");
  
              VTR_ASSERT_MSG(blk_model->inputs, ".names model has no input port");
              VTR_ASSERT_MSG(!blk_model->inputs->next, ".names model has multiple input ports");
              if (static_cast<int>(nets.size()) - 1 > blk_model->inputs->size) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "BLIF .names input size (%zu) greater than .names model input size (%d)",
                              nets.size() - 1, blk_model->inputs->size);
              }
  
              VTR_ASSERT_MSG(blk_model->outputs, ".names has no output port");
              VTR_ASSERT_MSG(!blk_model->outputs->next, ".names model has multiple output ports");
              VTR_ASSERT_MSG(blk_model->outputs->size == 1, ".names model has non-single-bit output");
  
              //Convert the single-output cover to a netlist truth table
              AtomNetlist::TruthTable truth_table;
              for(const auto& row : so_cover) {
                  truth_table.emplace_back();
                  for(auto val : row) {
                      truth_table[truth_table.size()-1].push_back(to_vtr_logic_value(val));
                  }
              }
  
              AtomBlockId blk_id = curr_model().create_block(nets[nets.size()-1], blk_model, truth_table);
              set_curr_block(blk_id);
  
              //Create inputs
              AtomPortId input_port_id = curr_model().create_port(blk_id, blk_model->inputs);
              for(size_t i = 0; i < nets.size() - 1; ++i) {
                  AtomNetId net_id = curr_model().create_net(nets[i]);
  
                  curr_model().create_pin(input_port_id, i, net_id, PinType::SINK);
              }
  
              //Figure out if the output is a constant generator
              bool output_is_const = false;
              if(truth_table.empty()
                  || (truth_table.size() == 1 && truth_table[0].size() == 1 && truth_table[0][0] == vtr::LogicValue::FALSE)) {
                  //An empty truth table in BLIF corresponds to a constant-zero
                  //  e.g.
                  //
                  //  #gnd is a constant 0 generator
                  //  .names gnd
                  //
                  //An single entry truth table with value '0' also corresponds to a constant-zero
                  //  e.g.
                  //
                  //  #gnd2 is a constant 0 generator
                  //  .names gnd2
                  //  0
                  //
                  output_is_const = true;
                  VTR_LOG("Found constant-zero generator '%s'\n", nets[nets.size()-1].c_str());
              } else if(truth_table.size() == 1 && truth_table[0].size() == 1 && truth_table[0][0] == vtr::LogicValue::TRUE) {
                  //A single-entry truth table with value '1' in BLIF corresponds to a constant-one
                  //  e.g.
                  //
                  //  #vcc is a constant 1 generator
                  //  .names vcc
                  //  1
                  //
                  output_is_const = true;
                  VTR_LOG("Found constant-one generator '%s'\n", nets[nets.size()-1].c_str());
              }
  
              //Create output
              AtomNetId net_id = curr_model().create_net(nets[nets.size()-1]);
              AtomPortId output_port_id = curr_model().create_port(blk_id, blk_model->outputs);
              curr_model().create_pin(output_port_id, 0, net_id, PinType::DRIVER, output_is_const);
          }
  
          void latch(std::string input, std::string output, blifparse::LatchType type, std::string control, blifparse::LogicValue init) override {
              if(type == blifparse::LatchType::UNSPECIFIED) {
                  VTR_LOGF_WARN(filename_.c_str(), lineno_, "Treating latch '%s' of unspecified type as rising edge triggered\n", output.c_str());
              } else if(type != blifparse::LatchType::RISING_EDGE) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Only rising edge latches supported\n");
              }
  
              if (control.empty()) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Latch must have a clock\n");
              }
  
              const t_model* blk_model = find_model(MODEL_LATCH);
  
              VTR_ASSERT_MSG(blk_model->inputs, "Has one input port");
              VTR_ASSERT_MSG(blk_model->inputs->next, "Has two input port");
              VTR_ASSERT_MSG(!blk_model->inputs->next->next, "Has no more than two input port");
              VTR_ASSERT_MSG(blk_model->outputs, "Has one output port");
              VTR_ASSERT_MSG(!blk_model->outputs->next, "Has no more than one input port");
  
              const t_model_ports* d_model_port = blk_model->inputs;
              const t_model_ports* clk_model_port = blk_model->inputs->next;
              const t_model_ports* q_model_port = blk_model->outputs;
  
              VTR_ASSERT(d_model_port->name == std::string("D"));
              VTR_ASSERT(clk_model_port->name == std::string("clk"));
              VTR_ASSERT(q_model_port->name == std::string("Q"));
              VTR_ASSERT(d_model_port->size == 1);
              VTR_ASSERT(clk_model_port->size == 1);
              VTR_ASSERT(q_model_port->size == 1);
              VTR_ASSERT(clk_model_port->is_clock);
  
              //We set the initital value as a single entry in the 'truth_table' field
              AtomNetlist::TruthTable truth_table(1);
              truth_table[0].push_back(to_vtr_logic_value(init));
  
              AtomBlockId blk_id = curr_model().create_block(output, blk_model, truth_table);
              set_curr_block(blk_id);
  
              //The input
              AtomPortId d_port_id = curr_model().create_port(blk_id, d_model_port);
              AtomNetId d_net_id = curr_model().create_net(input);
              curr_model().create_pin(d_port_id, 0, d_net_id, PinType::SINK);
  
              //The output
              AtomPortId q_port_id = curr_model().create_port(blk_id, q_model_port);
              AtomNetId q_net_id = curr_model().create_net(output);
              curr_model().create_pin(q_port_id, 0, q_net_id, PinType::DRIVER);
  
              //The clock
              AtomPortId clk_port_id = curr_model().create_port(blk_id, clk_model_port);
              AtomNetId clk_net_id = curr_model().create_net(control);
              curr_model().create_pin(clk_port_id, 0, clk_net_id, PinType::SINK);
          }
  
          void subckt(std::string subckt_model, std::vector<std::string> ports, std::vector<std::string> nets) override {
              VTR_ASSERT(ports.size() == nets.size());
  
              const t_model* blk_model = find_model(subckt_model);
  
              //We name the subckt based on the net it's first output pin drives
              std::string subckt_name;
              for(size_t i = 0; i < ports.size(); ++i) {
                  const t_model_ports* model_port = find_model_port(blk_model, ports[i]);
                  VTR_ASSERT(model_port);
  
                  if(model_port->dir == OUT_PORT) {
                      subckt_name = nets[i];
                      break;
                  }
              }
  
              if(subckt_name.empty()) {
                  //We need to name the block uniquely ourselves since there is no connected output
                  subckt_name = unique_subckt_name();
  
                  //Since this is unusual, warn the user
                  VTR_LOGF_WARN(filename_.c_str(), lineno_,
                          "Subckt of type '%s' at %s:%d has no output pins, and has been named '%s'\n",
                          blk_model->name, filename_.c_str(), lineno_, subckt_name.c_str());
              }
  
              //The name for every block should be unique, check that there is no name conflict
              AtomBlockId blk_id = curr_model().find_block(subckt_name);
              if(blk_id) {
                  const t_model* conflicting_model = curr_model().block_model(blk_id);
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_,
                            "Duplicate blocks named '%s' found in netlist."
                            " Existing block of type '%s' conflicts with subckt of type '%s'.",
                            subckt_name.c_str(), conflicting_model->name, subckt_model.c_str());
              }
  
              //Create the block
              blk_id = curr_model().create_block(subckt_name, blk_model);
              set_curr_block(blk_id);
  
              for(size_t i = 0; i < ports.size(); ++i) {
                  //Check for consistency between model and ports
                  const t_model_ports* model_port = find_model_port(blk_model, ports[i]);
                  VTR_ASSERT(model_port);
  
                  //Determine the pin type
                  PinType pin_type = PinType::SINK;
                  if(model_port->dir == OUT_PORT) {
                      pin_type = PinType::DRIVER;
  				} else {
                      VTR_ASSERT_MSG(model_port->dir == IN_PORT, "Unexpected port type");
                  }
  
                  //Make the port
                  std::string port_base;
                  size_t port_bit;
                  std::tie(port_base, port_bit) = split_index(ports[i]);
  
                  AtomPortId port_id = curr_model().create_port(blk_id, find_model_port(blk_model, port_base));
  
                  //Make the net
                  AtomNetId net_id = curr_model().create_net(nets[i]);
  
                  //Make the pin
                  curr_model().create_pin(port_id, port_bit, net_id, pin_type);
              }
          }
  
          void blackbox() override {
              //We treat black-boxes as netlists during parsing so they should contain
              //only inpads/outpads
              for(const auto& blk_id : curr_model().blocks()) {
                  auto blk_type = curr_model().block_type(blk_id);
                  if(!(blk_type == AtomBlockType::INPAD || blk_type == AtomBlockType::OUTPAD)) {
                      vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Unexpected primitives in blackbox model");
                  }
              }
              set_curr_model_blackbox(true);
              set_curr_block(AtomBlockId::INVALID()); //This statement doesn't define a block, so mark invalid
          }
  
          void end_model() override {
              if(ended_) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Unexpected .end");
              }
  
              merge_conn_nets();
  
              //Mark as ended
              ended_ = true;
  
              set_curr_block(AtomBlockId::INVALID()); //This statement doesn't define a block, so mark invalid
          }
  
          //BLIF Extensions
          void conn(std::string src, std::string dst) override {
              if (blif_format_ != e_circuit_format::EBLIF) {
                  parse_error(lineno_, ".conn", "Supported only in extended BLIF format");
              }
  
              //We allow the .conn to create the nets if they don't exist,
              //however typically they will have already been defined.
              AtomNetId driver_net = curr_model().create_net(src);
              AtomNetId sink_net = curr_model().create_net(dst);
  
              //We eventually need to merge the driver and sink nets,
              //however we must defer that until all the net drivers 
              //and sinks have been created (otherwise they may not 
              //be properly merged depending on where the .conn is
              //delcared).
              //
              //As a result we record the nets to merge and do the actual merging at
              //the end of the .model
              curr_nets_to_merge_.emplace_back(driver_net, sink_net);
  
              set_curr_block(AtomBlockId::INVALID());
          }
  
          void cname(std::string cell_name) override {
              if (blif_format_ != e_circuit_format::EBLIF) {
                  parse_error(lineno_, ".cname", "Supported only in extended BLIF format");
              }
  
              //Re-name the block
              curr_model().set_block_name(curr_block(), cell_name);
          }
  
          void attr(std::string name, std::string value) override {
              if (blif_format_ != e_circuit_format::EBLIF) {
                  parse_error(lineno_, ".attr", "Supported only in extended BLIF format");
              }
  
              curr_model().set_block_attr(curr_block(), name, value);
          }
  
          void param(std::string name, std::string value) override {
              if (blif_format_ != e_circuit_format::EBLIF) {
                  parse_error(lineno_, ".param", "Supported only in extended BLIF format");
              }
  
              curr_model().set_block_param(curr_block(), name, value);
          }
  
  
  
          //Utilities
          void filename(std::string fname) override { filename_ = fname; }
  
          void lineno(int line_num) override { lineno_ = line_num; }
  
          void parse_error(const int curr_lineno, const std::string& near_text, const std::string& msg) override {
              vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), curr_lineno,
                      "Error in blif file near '%s': %s\n", near_text.c_str(), msg.c_str());
          }
      public:
          //Retrieve the netlist
          size_t determine_main_netlist_index() {
              //Look through all the models loaded, to find the one which is non-blackbox (i.e. has real blocks
              //and is not a blackbox).  To check for errors we look at all models, even if we've already
              //found a non-blackbox model.
              int top_model_idx = -1; //Not valid
  
              for(int i = 0; i < static_cast<int>(blif_models_.size()); ++i) {
                  if(!blif_models_black_box_[i]) {
                      //A non-blackbox model
                      if(top_model_idx == -1) {
                          //This is the top model
                          top_model_idx = i;
                      } else {
                          //We already have a top model
                          vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_,
                                  "Found multiple models with primitives. "
                                  "Only one model can contain primitives, the others must be blackboxes.");
                      }
                  } else {
                      //Verify blackbox models match the architecture
                      verify_blackbox_model(blif_models_[i]);
                  }
              }
  
              if(top_model_idx == -1) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_,
                          "No non-blackbox models found. The main model must not be a blackbox.");
              }
  
              //Return the main model
              VTR_ASSERT(top_model_idx >= 0);
              return static_cast<size_t>(top_model_idx);
          }
  
      private:
          const t_model* find_model(std::string name) {
              const t_model* arch_model = nullptr;
              for(const t_model* arch_models : {user_arch_models_, library_arch_models_}) {
                  arch_model = arch_models;
                  while(arch_model) {
                      if(name == arch_model->name) {
                          //Found it
                          break;
                      }
                      arch_model = arch_model->next;
                  }
                  if(arch_model) {
                      //Found it
                      break;
                  }
              }
              if(!arch_model) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Failed to find matching architecture model for '%s'\n",
                            name.c_str());
              }
              return arch_model;
          }
  
          const t_model_ports* find_model_port(const t_model* blk_model, std::string port_name) {
              //We need to handle both single, and multi-bit port names
              //
              //By convention multi-bit port names have the bit index stored in square brackets
              //at the end of the name. For example:
              //
              //   my_signal_name[2]
              //
              //indicates the 2nd bit of the port 'my_signal_name'.
              std::string trimmed_port_name;
              int bit_index;
  
              //Extract the index bit
              std::tie(trimmed_port_name, bit_index) = split_index(port_name);
  
              //We now have the valid bit index and port_name is the name excluding the index info
              VTR_ASSERT(bit_index >= 0);
  
              //We now look through all the ports on the model looking for the matching port
              for(const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) {
  
                  const t_model_ports* curr_port = ports;
                  while(curr_port) {
                      if(trimmed_port_name == curr_port->name) {
                          //Found a matching port, we need to verify the index
                          if(bit_index < curr_port->size) {
                              //Valid port index
                              return curr_port;
                          } else {
                              //Out of range
                              vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_,
                                       "Port '%s' on architecture model '%s' exceeds port width (%d bits)\n",
                                        port_name.c_str(), blk_model->name, curr_port->size);
                          }
                      }
                      curr_port = curr_port->next;
                  }
              }
  
              //No match
              vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_,
                       "Found no matching port '%s' on architecture model '%s'\n",
                        port_name.c_str(), blk_model->name);
              return nullptr;
          }
  
          //Splits the index off a signal name and returns the base signal name (excluding
          //the index) and the index as an integer. For example
          //
          //  "my_signal_name[2]"   -> "my_signal_name", 2
          std::pair<std::string, int> split_index(const std::string& signal_name) {
              int bit_index = 0;
  
              std::string trimmed_signal_name = signal_name;
  
              auto iter = --signal_name.end(); //Initialized to the last char
              if(*iter == ']') {
                  //The name may end in an index
                  //
                  //To verify, we iterate back through the string until we find
                  //an open square brackets, or non digit character
                  --iter; //Advance past ']'
                  while(iter != signal_name.begin() && std::isdigit(*iter)) --iter;
  
                  //We are at the first non-digit character from the end (or the beginning of the string)
                  if(*iter == '[') {
                      //We have a valid index in the open range (iter, --signal_name.end())
                      std::string index_str(iter+1, --signal_name.end());
  
                      //Convert to an integer
                      std::stringstream ss(index_str);
                      ss >> bit_index;
                      VTR_ASSERT_MSG(!ss.fail() && ss.eof(), "Failed to extract signal index");
  
                      //Trim the signal name to exclude the final index
                      trimmed_signal_name = std::string(signal_name.begin(), iter);
                  }
              }
              return std::make_pair(trimmed_signal_name, bit_index);
          }
  
          //Retieves a reference to the currently active .model
          AtomNetlist& curr_model() {
              if(blif_models_.empty() || ended_) {
                  vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Expected .model");
              }
  
              return blif_models_[blif_models_.size()-1];
          }
  
          void set_curr_model_blackbox(bool val) {
              VTR_ASSERT(blif_models_.size() == blif_models_black_box_.size());
              blif_models_black_box_[blif_models_black_box_.size()-1] = val;
          }
  
          bool verify_blackbox_model(AtomNetlist& blif_model) {
              const t_model* arch_model = find_model(blif_model.netlist_name());
  
              //Verify each port on the model
              //
              // We parse each .model as it's own netlist so the IOs
              // get converted to blocks
              for(auto blk_id : blif_model.blocks()) {
  
  
                  //Check that the port directions match
                  if(blif_model.block_type(blk_id) == AtomBlockType::INPAD) {
  
                      const auto& input_name = blif_model.block_name(blk_id);
  
                      //Find model port already verifies the port widths
                      const t_model_ports* arch_model_port = find_model_port(arch_model, input_name);
                      VTR_ASSERT(arch_model_port);
                      VTR_ASSERT(arch_model_port->dir == IN_PORT);
  
                  } else {
                      VTR_ASSERT(blif_model.block_type(blk_id) == AtomBlockType::OUTPAD);
  
                      auto raw_output_name = blif_model.block_name(blk_id);
  
                      std::string output_name = vtr::replace_first(raw_output_name, OUTPAD_NAME_PREFIX, "");
  
                      //Find model port already verifies the port widths
                      const t_model_ports* arch_model_port = find_model_port(arch_model, output_name);
                      VTR_ASSERT(arch_model_port);
  
                      VTR_ASSERT(arch_model_port->dir == OUT_PORT);
                  }
              }
              return true;
          }
  
          //Returns a different unique subck name each time it is called
          std::string unique_subckt_name() {
              return "unnamed_subckt" + std::to_string(unique_subckt_name_counter_++);
          }
  
          //Sets the current block which is being processed
          // Used to determine which block a .cname, .param, .attr apply to
          void set_curr_block(AtomBlockId blk) {
              curr_block_ = blk;
          }
  
          //Gets the current block which is being processed
          AtomBlockId curr_block() const {
              return curr_block_;
          }
  
          //Merges all the recorded net pairs which need to be merged
          //
          //This should only be called at the end of a .model to ensure that
          //all the associated driver/sink pins have been delcared and connected
          //to their nets
          void merge_conn_nets() {
              for (auto net_pair : curr_nets_to_merge_) {
                  curr_model().merge_nets(net_pair.first, net_pair.second);
              }
  
              curr_nets_to_merge_.clear();
          }
  
      private:
          bool ended_ = true; //Initially no active .model
          std::string filename_ = "";
          int lineno_ = -1;
  
          std::vector<AtomNetlist> blif_models_;
          std::vector<bool> blif_models_black_box_;
  
          AtomNetlist& main_netlist_; //User object we fill
          const std::string netlist_id_; //Unique identifier based on the contents of the blif file
          const t_model* user_arch_models_ = nullptr;
          const t_model* library_arch_models_ = nullptr;
  
          size_t unique_subckt_name_counter_ = 0;
  
          AtomBlockId curr_block_;
          std::vector<std::pair<AtomNetId,AtomNetId>> curr_nets_to_merge_;
  
          e_circuit_format blif_format_ = e_circuit_format::BLIF;
  
  };
  
  
  vtr::LogicValue to_vtr_logic_value(blifparse::LogicValue val) {
      vtr::LogicValue new_val = vtr::LogicValue::UNKOWN;
      switch(val) {
          case blifparse::LogicValue::TRUE: new_val = vtr::LogicValue::TRUE; break;
          case blifparse::LogicValue::FALSE: new_val = vtr::LogicValue::FALSE; break;
          case blifparse::LogicValue::DONT_CARE: new_val = vtr::LogicValue::DONT_CARE; break;
          case blifparse::LogicValue::UNKOWN: new_val = vtr::LogicValue::UNKOWN; break;
          default: VTR_ASSERT_OPT_MSG(false, "Unkown logic value");
      }
      return new_val;
  }
  
  AtomNetlist read_blif(e_circuit_format circuit_format,
                        const char *blif_file,
                        const t_model *user_models,
                        const t_model *library_models) {
  
  
      AtomNetlist netlist;
      std::string netlist_id = vtr::secure_digest_file(blif_file);
  
      BlifAllocCallback alloc_callback(circuit_format, netlist, netlist_id, user_models, library_models);
      blifparse::blif_parse_filename(blif_file, alloc_callback);
  
      return netlist;
  }
  
  