diff --git a/.github/kokoro/continuous/strong_sanitized.cfg b/.github/kokoro/continuous/strong_sanitized.cfg index 746a97558da..e2595c0e84c 100644 --- a/.github/kokoro/continuous/strong_sanitized.cfg +++ b/.github/kokoro/continuous/strong_sanitized.cfg @@ -2,8 +2,8 @@ build_file: "vtr-verilog-to-routing/.github/kokoro/run-vtr.sh" -# 2 hour -timeout_mins: 120 +# 4 hour +timeout_mins: 240 action { define_artifacts { diff --git a/.github/kokoro/presubmit/strong_sanitized.cfg b/.github/kokoro/presubmit/strong_sanitized.cfg index 5600e5e2de8..2387193045d 100644 --- a/.github/kokoro/presubmit/strong_sanitized.cfg +++ b/.github/kokoro/presubmit/strong_sanitized.cfg @@ -2,8 +2,8 @@ build_file: "vtr-verilog-to-routing/.github/kokoro/run-vtr.sh" -# 2 hour -timeout_mins: 120 +# 4 hour +timeout_mins: 240 action { define_artifacts { diff --git a/.github/kokoro/steps/hostsetup.sh b/.github/kokoro/steps/hostsetup.sh index 54fdeeeaf5e..23c1b3f0c4b 100755 --- a/.github/kokoro/steps/hostsetup.sh +++ b/.github/kokoro/steps/hostsetup.sh @@ -36,6 +36,7 @@ echo "Host adding PPAs" echo "----------------------------------------" wget --no-check-certificate -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial main' +sudo add-apt-repository ppa:ubuntu-toolchain-r/test echo "----------------------------------------" echo @@ -55,20 +56,30 @@ sudo apt-get install -y \ bison \ build-essential \ ca-certificates \ + clang \ cmake \ colordiff \ coreutils \ curl \ flex \ + gawk \ + gcc-9 \ + g++-9 \ git \ graphviz \ inkscape \ jq \ + libboost-filesystem-dev \ + libboost-python-dev \ + libboost-system-dev \ + libffi-dev \ libgtk-3-dev \ + libreadline-dev \ libx11-dev \ make \ ninja-build \ nodejs \ + pkg-config \ psmisc \ python \ python3 \ @@ -76,27 +87,22 @@ sudo apt-get install -y \ python3-virtualenv \ python3-yaml \ qt5-default \ - virtualenv \ - clang \ - libreadline-dev \ - gawk \ tcl-dev \ - libffi-dev \ + virtualenv \ xdot \ - pkg-config \ - libboost-system-dev \ - libboost-python-dev \ - libboost-filesystem-dev \ - zlib1g-dev \ + zlib1g-dev #Don't include libtbb-dev since it may increase memory usage #libtbb-dev \ export PATH="$PATH:/home/kbuilder/.local/bin" +export CC=gcc-9 +export CXX=g++-9 + pyenv install -f 3.6.3 pyenv global 3.6.3 curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -python3 get-pip.py +python3 get-pip.py rm get-pip.py python3 -m pip install -r requirements.txt diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 8b789bfb9d4..ebfe950afc9 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1732,6 +1732,12 @@ struct t_clock_arch_spec { std::vector clock_connections_arch; }; +struct t_lut_cell { + std::string name; + std::string init_param; + std::vector inputs; +}; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; @@ -1750,11 +1756,38 @@ struct t_arch { int num_switches; t_direct_inf* Directs = nullptr; int num_directs = 0; + t_model* models = nullptr; t_model* model_library = nullptr; + t_power_arch* power = nullptr; t_clock_arch* clocks = nullptr; + // Constants + // VCC and GND cells are special virtual cells that are + // used to handle the constant network of the device. + // + // Similarly, the constant nets are defined to identify + // the generic name for the constant network. + // + // Given that usually, the constants have a dedicated network in + // real FPGAs, this information becomes relevant to identify which + // nets from the circuit netlist are belonging to the constant network, + // and assigned to it accordingly. + // + // NOTE: At the moment, the constant cells and nets are primarly used + // for the interchange netlist format, to determine which are the constants + // net names and which virtual cell is responsible to generate them. + // The information is present in the device database. + std::string gnd_cell; + std::string vcc_cell; + + std::string gnd_net = "$__gnd_net"; + std::string vcc_net = "$__vcc_net"; + + // Luts + std::vector lut_cells; + //The name of the switch used for the input connection block (i.e. to //connect routing tracks to block pins). //This should correspond to a switch in Switches diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 8b15d44e459..fffd3e864d2 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -122,6 +122,7 @@ struct ArchReader { } void read_arch() { + process_luts(); process_models(); process_device(); @@ -232,6 +233,14 @@ struct ArchReader { "Model output ports can not have combinational sink ports"); } + model_port->min_size = 1; + model_port->size = 1; + if (port.isBus()) { + int s = port.getBus().getBusStart(); + int e = port.getBus().getBusEnd(); + model_port->size = std::abs(e - s) + 1; + } + port_names.insert(std::pair(model_port->name, dir)); //Add the port if (dir == IN_PORT) { @@ -245,6 +254,25 @@ struct ArchReader { } } + void process_luts() { + // Add LUT Cell definitions + // This is helpful to understand which cells are LUTs + auto lut_def = ar_.getLutDefinitions(); + + for (auto lut_cell : lut_def.getLutCells()) { + t_lut_cell cell; + cell.name = lut_cell.getCell().cStr(); + for (auto input : lut_cell.getInputPins()) + cell.inputs.push_back(input.cStr()); + + auto equation = lut_cell.getEquation(); + if (equation.isInitParam()) + cell.init_param = equation.getInitParam().cStr(); + + arch_->lut_cells.push_back(cell); + } + } + // Layout Processing void process_layout() { auto strList = ar_.getStrList(); diff --git a/libs/libvtrutil/src/vtr_hash.h b/libs/libvtrutil/src/vtr_hash.h index 99fd55807ac..7e8e6fa42d7 100644 --- a/libs/libvtrutil/src/vtr_hash.h +++ b/libs/libvtrutil/src/vtr_hash.h @@ -15,6 +15,16 @@ inline void hash_combine(std::size_t& seed, const T& v) { seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } +struct hash_pair { + template + std::size_t operator()(const std::pair& pair) const noexcept { + auto hash1 = std::hash{}(pair.first); + auto hash2 = std::hash{}(pair.second); + + return hash1 ^ hash2; + } +}; + } // namespace vtr #endif diff --git a/vpr/src/base/atom_netlist_utils.cpp b/vpr/src/base/atom_netlist_utils.cpp index 9f73cb84426..cbfed079c25 100644 --- a/vpr/src/base/atom_netlist_utils.cpp +++ b/vpr/src/base/atom_netlist_utils.cpp @@ -95,7 +95,10 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { AtomPinId pin = *netlist.block_pins(blk_id).begin(); std::string blk_name = netlist.block_name(blk_id); - std::string out_name(blk_name.begin() + 4, blk_name.end()); //+4 to trim out: prefix + + std::string out_prefix("out:"); + int strip_size = blk_name.substr(0, out_prefix.size()) == out_prefix ? out_prefix.size() : 0; + std::string out_name(blk_name.begin() + strip_size, blk_name.end()); //+4 to trim out: prefix if present fprintf(f, "%s%s", INDENT, out_name.c_str()); @@ -307,7 +310,7 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { ports.push_back(port_id); } - fprintf(f, ".subckt %s \\\n", blk_model->name); + fprintf(f, ".subckt %s \\\n", netlist.block_name(blk_id).c_str()); for (size_t i = 0; i < ports.size(); i++) { auto width = netlist.port_width(ports[i]); for (size_t j = 0; j < width; ++j) { diff --git a/vpr/src/base/read_circuit.cpp b/vpr/src/base/read_circuit.cpp index cee5fbe9e9a..04ca872cdec 100644 --- a/vpr/src/base/read_circuit.cpp +++ b/vpr/src/base/read_circuit.cpp @@ -1,5 +1,6 @@ #include "read_circuit.h" #include "read_blif.h" +#include "read_interchange_netlist.h" #include "atom_netlist.h" #include "atom_netlist_utils.h" #include "echo_files.h" @@ -21,20 +22,23 @@ static void process_circuit(AtomNetlist& netlist, static void show_circuit_stats(const AtomNetlist& netlist); -AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, - const char* circuit_file, - const t_model* user_models, - const t_model* library_models, - e_const_gen_inference const_gen_inference, - bool should_absorb_buffers, - bool should_sweep_dangling_primary_ios, - bool should_sweep_dangling_nets, - bool should_sweep_dangling_blocks, - bool should_sweep_constant_primary_outputs, - int verbosity) { +AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setup& vpr_setup, t_arch& arch) { + // Options + const char* circuit_file = vpr_setup.PackerOpts.circuit_file_name.c_str(); + const t_model* user_models = vpr_setup.user_models; + const t_model* library_models = vpr_setup.library_models; + e_const_gen_inference const_gen_inference = vpr_setup.NetlistOpts.const_gen_inference; + bool should_absorb_buffers = vpr_setup.NetlistOpts.absorb_buffer_luts; + bool should_sweep_dangling_primary_ios = vpr_setup.NetlistOpts.sweep_dangling_primary_ios; + bool should_sweep_dangling_nets = vpr_setup.NetlistOpts.sweep_dangling_nets; + bool should_sweep_dangling_blocks = vpr_setup.NetlistOpts.sweep_dangling_blocks; + bool should_sweep_constant_primary_outputs = vpr_setup.NetlistOpts.sweep_constant_primary_outputs; + bool verbosity = vpr_setup.NetlistOpts.netlist_verbosity; + if (circuit_format == e_circuit_format::AUTO) { auto name_ext = vtr::split_ext(circuit_file); + VTR_LOGV(verbosity, "Circuit file: %s\n", circuit_file); if (name_ext[1] == ".blif") { circuit_format = e_circuit_format::BLIF; } else if (name_ext[1] == ".eblif") { @@ -49,10 +53,20 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, { vtr::ScopedStartFinishTimer t("Load circuit"); - VTR_ASSERT(circuit_format == e_circuit_format::BLIF - || circuit_format == e_circuit_format::EBLIF); - - netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + switch (circuit_format) { + case e_circuit_format::BLIF: + case e_circuit_format::EBLIF: + netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + break; + case e_circuit_format::FPGA_INTERCHANGE: + netlist = read_interchange_netlist(circuit_file, arch); + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ATOM_NETLIST, + "Unable to identify circuit file format for '%s'. Expect [blif|eblif|fpga-interchange]!\n", + circuit_file); + break; + } } if (isEchoFileEnabled(E_ECHO_ATOM_NETLIST_ORIG)) { diff --git a/vpr/src/base/read_circuit.h b/vpr/src/base/read_circuit.h index a7935aa99e4..90be01a3891 100644 --- a/vpr/src/base/read_circuit.h +++ b/vpr/src/base/read_circuit.h @@ -5,20 +5,11 @@ #include "vpr_types.h" enum class e_circuit_format { - AUTO, /// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" + +#include "atom_netlist.h" + +#include "vtr_assert.h" +#include "vtr_hash.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_interchange_netlist.h" +#include "arch_types.h" + +struct NetlistReader { + public: + NetlistReader(AtomNetlist& main_netlist, + LogicalNetlist::Netlist::Reader& netlist_reader, + const std::string netlist_id, + const char* netlist_file, + const t_arch& arch) + : main_netlist_(main_netlist) + , nr_(netlist_reader) + , netlist_file_(netlist_file) + , arch_(arch) { + // Define top module + top_cell_instance_ = nr_.getTopInst(); + + auto str_list = nr_.getStrList(); + main_netlist_ = AtomNetlist(str_list[top_cell_instance_.getName()], netlist_id); + + inpad_model_ = find_model(MODEL_INPUT); + outpad_model_ = find_model(MODEL_OUTPUT); + main_netlist_.set_block_types(inpad_model_, outpad_model_); + + VTR_LOG("Reading IOs...\n"); + read_ios(); + VTR_LOG("Reading names...\n"); + read_names(); + VTR_LOG("Reading blocks...\n"); + read_blocks(); + } + + private: + AtomNetlist& main_netlist_; + // Netlist Reader + LogicalNetlist::Netlist::Reader& nr_; + + const char* netlist_file_; + + const t_model* inpad_model_; + const t_model* outpad_model_; + const t_arch& arch_; + + LogicalNetlist::Netlist::CellInstance::Reader top_cell_instance_; + + void read_ios() { + const t_model* input_model = find_model(MODEL_INPUT); + const t_model* output_model = find_model(MODEL_OUTPUT); + + auto str_list = nr_.getStrList(); + + auto top_cell_decl = nr_.getCellDecls()[top_cell_instance_.getCell()]; + for (auto top_port : top_cell_decl.getPorts()) { + auto port = nr_.getPortList()[top_port]; + auto name = std::string(str_list[port.getName()].cStr()); + auto dir = port.getDir(); + + int bus_size, start_bit; + std::tie(bus_size, start_bit) = get_bus_size(port); + + for (int bit = start_bit; bit < start_bit + bus_size; bit++) { + auto port_name = name; + if (bus_size > 1) + port_name = name + "[" + std::to_string(bit) + "]"; + + AtomBlockId blk_id; + AtomPortId port_id; + AtomNetId net_id; + + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + blk_id = main_netlist_.create_block(port_name, input_model); + port_id = main_netlist_.create_port(blk_id, input_model->outputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::DRIVER); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + blk_id = main_netlist_.create_block(port_name, output_model); + port_id = main_netlist_.create_port(blk_id, output_model->inputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::SINK); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_names() { + const t_model* blk_model = find_model(MODEL_NAMES); + + // Set the max size of the LUT + int lut_size = 0; + for (auto lut : arch_.lut_cells) + lut_size = std::max((int)lut.inputs.size(), lut_size); + blk_model->inputs[0].size = lut_size; + + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + auto cell = decl_list[inst_list[cell_inst].getCell()]; + + bool is_lut; + int width; + std::string init_param; + std::tie(is_lut, width, init_param) = is_lut_cell(str_list[cell.getName()].cStr()); + + if (is_lut) + insts.emplace_back(cell_inst, width, init_param); + } + + for (auto inst : insts) { + unsigned int inst_idx; + int lut_width; + std::string init_param; + std::tie(inst_idx, lut_width, init_param) = inst; + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + + auto props = inst_list[inst_idx].getPropMap().getEntries(); + std::vector init; + for (auto entry : props) { + if (std::string(str_list[entry.getKey()].cStr()) != init_param) + continue; + + // TODO: export this to a library function to have generic parameter decoding + if (entry.which() == LogicalNetlist::Netlist::PropertyMap::Entry::TEXT_VALUE) { + const std::regex vhex_regex("[0-9]+'h([0-9A-Z]+)"); + const std::regex vbit_regex("[0-9]+'b([0-9A-Z]+)"); + const std::regex bit_regex("[0-1]+"); + std::string init_str = std::string(str_list[entry.getTextValue()].cStr()); + std::smatch regex_matches; + + // Fill the init vector + if (std::regex_match(init_str, regex_matches, vhex_regex)) + for (const char& c : regex_matches[1].str()) { + int value = std::stoi(std::string(1, c), 0, 16); + for (int bit = 3; bit >= 0; bit--) + init.push_back((value >> bit) & 1); + } + else if (std::regex_match(init_str, regex_matches, vbit_regex)) + for (const char& c : regex_matches[1].str()) + init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); + else if (std::regex_match(init_str, regex_matches, bit_regex)) + for (const char& c : init_str) + init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); + } + } + + // Add proper LUT mapping function here based on LUT size and init value + AtomNetlist::TruthTable truth_table; + bool is_const = false; + for (int bit = 0; bit < (int)init.size(); bit++) { + bool bit_set = init[init.size() - bit - 1]; + + if (bit_set == 0) + continue; + + is_const = bit == 0; + + truth_table.emplace_back(); + for (int row_bit = lut_width - 1; row_bit >= 0; row_bit--) { + bool row_bit_set = (bit >> row_bit) & 1; + auto log_value = row_bit_set ? vtr::LogicValue::TRUE : vtr::LogicValue::FALSE; + + truth_table[truth_table.size() - 1].push_back(log_value); + } + truth_table[truth_table.size() - 1].push_back(vtr::LogicValue::TRUE); + } + + //Figure out if the output is a constant generator + bool output_is_const = false; + if (truth_table.empty()) { + //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", inst_name.c_str()); + } else if (truth_table.size() == 1 && is_const) { + //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", inst_name.c_str()); + } + + AtomBlockId blk_id = main_netlist_.create_block(inst_name, blk_model, truth_table); + + AtomPortId iport_id = main_netlist_.create_port(blk_id, blk_model->inputs); + AtomPortId oport_id = main_netlist_.create_port(blk_id, blk_model->outputs); + + auto cell_lib = decl_list[inst_list[inst_idx].getCell()]; + std::unordered_map port_net_map; + + for (auto net : top_cell.getNets()) { + std::string net_name = str_list[net.getName()].cStr(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + port_net_map.emplace(port.getPort(), net_name); + } + } + + int inum = 0; + for (auto port : cell_lib.getPorts()) { + auto net_name = port_net_map.at(port); + AtomNetId net_id = main_netlist_.create_net(net_name); + + auto dir = port_list[port].getDir(); + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + if (!output_is_const) main_netlist_.create_pin(iport_id, inum++, net_id, PinType::SINK); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + main_netlist_.create_pin(oport_id, 0, net_id, PinType::DRIVER, output_is_const); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_blocks() { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + auto cell = decl_list[inst_list[cell_inst].getCell()]; + + bool is_lut; + std::tie(is_lut, std::ignore, std::ignore) = is_lut_cell(str_list[cell.getName()].cStr()); + + if (!is_lut) + insts.emplace_back(cell_inst, inst_list[cell_inst].getCell()); + } + + for (auto inst_pair : insts) { + auto inst_idx = inst_pair.first; + auto cell_idx = inst_pair.second; + + auto model_name = str_list[decl_list[cell_idx].getName()].cStr(); + const t_model* blk_model = find_model(model_name); + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + VTR_ASSERT(inst_name.empty() == 0); + + //The name for every block should be unique, check that there is no name conflict + AtomBlockId blk_id = main_netlist_.find_block(inst_name); + if (blk_id) { + const t_model* conflicting_model = main_netlist_.block_model(blk_id); + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Duplicate blocks named '%s' found in netlist." + " Existing block of type '%s' conflicts with subckt of type '%s'.", + inst_name.c_str(), conflicting_model->name, blk_model->name); + } + + auto port_net_map = get_port_net_map(inst_idx); + + auto cell = decl_list[inst_list[inst_idx].getCell()]; + if (std::string(str_list[cell.getName()].cStr()) == arch_.vcc_cell) + inst_name = arch_.vcc_cell; + else if (std::string(str_list[cell.getName()].cStr()) == arch_.gnd_cell) + inst_name = arch_.gnd_cell; + + if (main_netlist_.find_block(inst_name)) + continue; + + //Create the block + blk_id = main_netlist_.create_block(inst_name, blk_model); + + std::unordered_set added_ports; + for (auto port_net : port_net_map) { + auto port_idx = port_net.first.first; + auto port_bit = port_net.first.second; + + auto net_name = port_net.second; + if (inst_name == arch_.vcc_cell) + net_name = arch_.vcc_net; + else if (inst_name == arch_.gnd_cell) + net_name = arch_.gnd_net; + + auto port = port_list[port_idx]; + auto port_name = str_list[port.getName()].cStr(); + + //Check for consistency between model and ports + const t_model_ports* model_port = find_model_port(blk_model, std::string(port_name)); + 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"); + } + + AtomPortId port_id = main_netlist_.create_port(blk_id, model_port); + + //Make the net + AtomNetId net_id = main_netlist_.create_net(std::string(net_name)); + + //Make the pin + main_netlist_.create_pin(port_id, port_bit, net_id, pin_type); + + added_ports.emplace(port_id); + } + + // Bind unconnected ports to VCC by default + for (const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) { + for (const t_model_ports* port = ports; port != nullptr; port = port->next) { + AtomPortId port_id = main_netlist_.create_port(blk_id, port); + + if (added_ports.count(port_id)) + continue; + + if (port->dir != IN_PORT) + continue; + + //Make the net + AtomNetId net_id = main_netlist_.create_net(arch_.vcc_net); + + PinType pin_type = PinType::SINK; + //Make the pin + for (int i = 0; i < port->size; i++) + main_netlist_.create_pin(port_id, i, net_id, pin_type); + } + } + } + } + + // + // Utilities + // + const t_model* find_model(std::string name) { + for (const auto models : {arch_.models, arch_.model_library}) + for (const t_model* model = models; model != nullptr; model = model->next) + if (name == model->name) + return model; + + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, "Failed to find matching architecture model for '%s'\n", name.c_str()); + } + + const t_model_ports* find_model_port(const t_model* blk_model, std::string name) { + //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}) + for (const t_model_ports* port = ports; port != nullptr; port = port->next) + if (name == std::string(port->name)) + return port; + + //No match + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Found no matching port '%s' on architecture model '%s'\n", + name.c_str(), blk_model->name); + return nullptr; + } + + std::pair get_bus_size(LogicalNetlist::Netlist::Port::Reader port_reader) { + if (port_reader.isBus()) { + int s = port_reader.getBus().getBusStart(); + int e = port_reader.getBus().getBusEnd(); + + if (e < s) + return std::make_pair(s - e + 1, e); + else + return std::make_pair(e - s + 1, s); + } + + return std::make_pair(1, 0); + } + + unsigned int get_port_bit(LogicalNetlist::Netlist::PortInstance::Reader port_inst_reader) { + if (port_inst_reader.getBusIdx().which() == LogicalNetlist::Netlist::PortInstance::BusIdx::IDX) + return port_inst_reader.getBusIdx().getIdx(); + + return 0; + } + + std::unordered_map, std::string, vtr::hash_pair> get_port_net_map(unsigned int inst_idx) { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto str_list = nr_.getStrList(); + std::unordered_map, std::string, vtr::hash_pair> map; + for (auto net : top_cell.getNets()) { + std::string net_name = str_list[net.getName()].cStr(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + unsigned int port_bit = get_port_bit(port); + auto pair = std::make_pair(port.getPort(), port_bit); + map.emplace(pair, net_name); + } + } + + return map; + } + + std::tuple is_lut_cell(std::string cell_name) { + for (auto lut_cell : arch_.lut_cells) { + if (cell_name == lut_cell.name) { + auto init_param = lut_cell.init_param; + + // Assign default value for the LUT init parameter, if inexistent + if (init_param.empty()) + init_param = "INIT"; + + return std::make_tuple(true, lut_cell.inputs.size(), init_param); + } + } + + return std::make_tuple(false, 0, ""); + } +}; + +AtomNetlist read_interchange_netlist(const char* ic_netlist_file, + t_arch& arch) { + AtomNetlist netlist; + std::string netlist_id = vtr::secure_digest_file(ic_netlist_file); + + // Decompress GZipped capnproto device file + gzFile file = gzopen(ic_netlist_file, "r"); + VTR_ASSERT(file != Z_NULL); + + std::vector output_data; + output_data.resize(4096); + std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); + while (true) { + int ret = gzread(file, output_data.data(), output_data.size()); + VTR_ASSERT(ret >= 0); + if (ret > 0) { + sstream.write((const char*)output_data.data(), ret); + VTR_ASSERT(sstream); + } else { + VTR_ASSERT(ret == 0); + int error; + gzerror(file, &error); + VTR_ASSERT(error == Z_OK); + break; + } + } + + VTR_ASSERT(gzclose(file) == Z_OK); + + sstream.seekg(0); + kj::std::StdInputStream istream(sstream); + + // Reader options + capnp::ReaderOptions reader_options; + reader_options.nestingLimit = std::numeric_limits::max(); + reader_options.traversalLimitInWords = std::numeric_limits::max(); + + capnp::InputStreamMessageReader message_reader(istream, reader_options); + + auto netlist_reader = message_reader.getRoot(); + + NetlistReader reader(netlist, netlist_reader, netlist_id, ic_netlist_file, arch); + + return netlist; +} diff --git a/vpr/src/base/read_interchange_netlist.h b/vpr/src/base/read_interchange_netlist.h new file mode 100644 index 00000000000..1b17b86ea5d --- /dev/null +++ b/vpr/src/base/read_interchange_netlist.h @@ -0,0 +1,10 @@ +#ifndef READ_INTERCHANGE_NETLIST_H +#define READ_INTERCHANGE_NETLIST_H +#include "logic_types.h" +#include "atom_netlist_fwd.h" +#include "read_circuit.h" + +AtomNetlist read_interchange_netlist(const char* ic_netlist_file, + t_arch& arch); + +#endif /*READ_INTERCHANGE_NETLIST_H*/ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index f6726d6689d..45ed25c4faa 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -101,6 +101,8 @@ struct ParseCircuitFormat { conv_value.set_value(e_circuit_format::BLIF); else if (str == "eblif") conv_value.set_value(e_circuit_format::EBLIF); + else if (str == "fpga-interchange") + conv_value.set_value(e_circuit_format::FPGA_INTERCHANGE); else { std::stringstream msg; msg << "Invalid conversion from '" << str << "' to e_circuit_format (expected one of: " << argparse::join(default_choices(), ", ") << ")"; @@ -116,16 +118,18 @@ struct ParseCircuitFormat { conv_value.set_value("auto"); else if (val == e_circuit_format::BLIF) conv_value.set_value("blif"); - else { - VTR_ASSERT(val == e_circuit_format::EBLIF); + else if (val == e_circuit_format::EBLIF) conv_value.set_value("eblif"); + else { + VTR_ASSERT(val == e_circuit_format::FPGA_INTERCHANGE); + conv_value.set_value("fpga-interchange"); } return conv_value; } std::vector default_choices() { - return {"auto", "blif", "eblif"}; + return {"auto", "blif", "eblif", "fpga-interchange"}; } }; struct ParseRoutePredictor { @@ -1423,7 +1427,8 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg " .conn - Connection between two wires\n" " .cname - Custom name for atom primitive\n" " .param - Parameter on atom primitive\n" - " .attr - Attribute on atom primitive\n") + " .attr - Attribute on atom primitive\n" + " * fpga-interchage: Logical netlist in FPGA Interchange schema format\n") .default_value("auto") .show_in(argparse::ShowIn::HELP_ONLY); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index e52f812c437..3b3b8dbe15e 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -313,17 +313,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a /* Read blif file and sweep unused components */ auto& atom_ctx = g_vpr_ctx.mutable_atom(); - atom_ctx.nlist = read_and_process_circuit(options->circuit_format, - vpr_setup->PackerOpts.circuit_file_name.c_str(), - vpr_setup->user_models, - vpr_setup->library_models, - vpr_setup->NetlistOpts.const_gen_inference, - vpr_setup->NetlistOpts.absorb_buffer_luts, - vpr_setup->NetlistOpts.sweep_dangling_primary_ios, - vpr_setup->NetlistOpts.sweep_dangling_nets, - vpr_setup->NetlistOpts.sweep_dangling_blocks, - vpr_setup->NetlistOpts.sweep_constant_primary_outputs, - vpr_setup->NetlistOpts.netlist_verbosity); + atom_ctx.nlist = read_and_process_circuit(options->circuit_format, *vpr_setup, *arch); if (vpr_setup->PowerOpts.do_power) { //Load the net activity file for power estimation diff --git a/vpr/src/util/vpr_error.h b/vpr/src/util/vpr_error.h index 5820ebc0aae..0c3d5a1cbf8 100644 --- a/vpr/src/util/vpr_error.h +++ b/vpr/src/util/vpr_error.h @@ -9,6 +9,8 @@ enum e_vpr_error { VPR_ERROR_UNKNOWN = 0, + + // Flow errors VPR_ERROR_ARCH, VPR_ERROR_PACK, VPR_ERROR_PLACE, @@ -16,9 +18,13 @@ enum e_vpr_error { VPR_ERROR_TIMING, VPR_ERROR_POWER, VPR_ERROR_SDC, - VPR_ERROR_NET_F, - VPR_ERROR_PLACE_F, - VPR_ERROR_BLIF_F, + + // File parsing errors + VPR_ERROR_NET_F, // Error while parsing the packed netlist file + VPR_ERROR_PLACE_F, // Error while parsning the placement file + VPR_ERROR_BLIF_F, // Error while parsing the blif file + VPR_ERROR_IC_NETLIST_F, // Error while parsing the interchange netlist file + VPR_ERROR_IMPL_NETLIST_WRITER, VPR_ERROR_NETLIST, VPR_ERROR_ATOM_NETLIST, diff --git a/vpr/test/lut.netlist b/vpr/test/lut.netlist new file mode 100644 index 00000000000..d8a49d6feab Binary files /dev/null and b/vpr/test/lut.netlist differ diff --git a/vpr/test/test_interchange_netlist.cpp b/vpr/test/test_interchange_netlist.cpp new file mode 100644 index 00000000000..2af1b33a96f --- /dev/null +++ b/vpr/test/test_interchange_netlist.cpp @@ -0,0 +1,33 @@ +#include "catch2/catch_test_macros.hpp" + +#include "read_circuit.h" +#include "read_fpga_interchange_arch.h" +#include "arch_util.h" +#include "vpr_api.h" +#include +#include +#include + +namespace { + +static constexpr const char kArchFile[] = "testarch.device"; + +TEST_CASE("read_interchange_netlist", "[vpr]") { + t_arch arch; + t_vpr_setup vpr_setup; + + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + vpr_setup.user_models = arch.models; + vpr_setup.library_models = arch.model_library; + vpr_setup.PackerOpts.circuit_file_name = "lut.netlist"; + + /* Read blif file and sweep unused components */ + auto& atom_ctx = g_vpr_ctx.mutable_atom(); + atom_ctx.nlist = read_and_process_circuit(e_circuit_format::FPGA_INTERCHANGE, vpr_setup, arch); +} + +} // namespace