From 8a34e73b4933ea5f6919d6562180b4421371e4a8 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Mon, 11 Oct 2021 13:31:46 +0200 Subject: [PATCH 1/6] vpr: interchange: add device data reading This PR adds basic support for the architecture reading: - Device - Layout - Switches Signed-off-by: Alessandro Comodi Co-authored-by: Maciej Dudek Co-authored-by: Alessandro Comodi --- .../src/read_fpga_interchange_arch.cpp | 285 ++++++++++++++++++ vpr/test/test_interchange_device.cpp | 26 ++ 2 files changed, 311 insertions(+) diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 5aa9181003f..fee979ea9f7 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -34,6 +34,72 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { + bool slow_model = std::string(speed_model) == std::string("slow"); + bool fast_model = std::string(speed_model) == std::string("fast"); + + bool min_corner = std::string(value) == std::string("min"); + bool typ_corner = std::string(value) == std::string("typ"); + bool max_corner = std::string(value) == std::string("max"); + + if (!slow_model && !fast_model) { + archfpga_throw("", __LINE__, + "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model); + } + + if (!min_corner && !typ_corner && !max_corner) { + archfpga_throw("", __LINE__, + "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value); + } + + bool has_fast = model.getFast().hasFast(); + bool has_slow = model.getSlow().hasSlow(); + + if (slow_model && has_slow) { + auto half = model.getSlow().getSlow(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } else if (fast_model && has_fast) { + auto half = model.getFast().getFast(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } + + return 0.; +} + struct ArchReader { public: ArchReader(t_arch* arch, Device::Reader& arch_reader, const char* arch_file, std::vector& phys_types, std::vector& logical_types) @@ -47,6 +113,11 @@ struct ArchReader { void read_arch() { process_models(); + process_device(); + + process_layout(); + process_switches(); + process_segments(); } private: @@ -163,6 +234,220 @@ struct ArchReader { } } } + + // Layout Processing + void process_layout() { + auto strList = ar_.getStrList(); + auto tileList = ar_.getTileList(); + auto tileTypeList = ar_.getTileTypeList(); + t_grid_def grid_def; + + grid_def.width = grid_def.height = 0; + for (auto tile : tileList) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } + + grid_def.grid_type = GridDefType::FIXED; + std::string name = std::string(ar_.getName()); + if (name == "auto") { + archfpga_throw(arch_file_, __LINE__, + "The name auto is reserved for auto-size layouts; please choose another name"); + } + grid_def.name = name; + for (auto tile : tileList) { + t_metadata_dict data; + std::string tile_prefix(strList[tile.getName()].cStr()); + auto tileType = tileTypeList[tile.getType()]; + std::string tile_type(strList[tileType.getName()].cStr()); + + size_t pos = tile_prefix.find(tile_type); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type.length() + 1); + data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), + arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); + t_grid_loc_def single(tile_type, 1); + single.x.start_expr = tile.getCol(); + single.y.start_expr = tile.getRow(); + single.x.end_expr = single.x.start_expr + " + w - 1"; + single.y.end_expr = single.y.start_expr + " + h - 1"; + single.owned_meta = std::make_unique(data); + single.meta = single.owned_meta.get(); + grid_def.loc_defs.emplace_back(std::move(single)); + } + + arch_->grid_layouts.emplace_back(std::move(grid_def)); + } + + void process_device() { + /* + * The generic architecture data is not currently available in the interchange format + * therefore, for a very initial implementation, the values are taken from the ones + * used primarly in the Xilinx series7 devices, generated using SymbiFlow. + * + * As the interchange format develops further, with possibly more details, this function can + * become dynamic, allowing for different parameters for the different architectures. + */ + arch_->R_minW_nmos = 6065.520020; + arch_->R_minW_pmos = 18138.500000; + arch_->grid_logic_tile_area = 14813.392; + arch_->Chans.chan_x_dist.type = UNIFORM; + arch_->Chans.chan_x_dist.peak = 1; + arch_->Chans.chan_x_dist.width = 0; + arch_->Chans.chan_x_dist.xpeak = 0; + arch_->Chans.chan_x_dist.dc = 0; + arch_->Chans.chan_y_dist.type = UNIFORM; + arch_->Chans.chan_y_dist.peak = 1; + arch_->Chans.chan_y_dist.width = 0; + arch_->Chans.chan_y_dist.xpeak = 0; + arch_->Chans.chan_y_dist.dc = 0; + arch_->ipin_cblock_switch_name = std::string("generic"); + arch_->SBType = WILTON; + arch_->Fs = 3; + default_fc_.specified = true; + default_fc_.in_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.in_value = 1.0; + default_fc_.out_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.out_value = 1.0; + } + + void process_switches() { + std::set> pip_timing_models; + for (auto tile_type : ar_.getTileTypeList()) { + for (auto pip : tile_type.getPips()) { + pip_timing_models.insert(std::pair(pip.getBuffered21(), pip.getTiming())); + if (!pip.getDirectional()) + pip_timing_models.insert(std::pair(pip.getBuffered20(), pip.getTiming())); + } + } + + auto timing_data = ar_.getPipTimings(); + + std::vector> pip_timing_models_list; + pip_timing_models_list.reserve(pip_timing_models.size()); + + for (auto entry : pip_timing_models) { + pip_timing_models_list.push_back(entry); + } + + auto num_switches = pip_timing_models.size() + 2; + std::string switch_name; + + arch_->num_switches = num_switches; + auto* switches = arch_->Switches; + + if (num_switches > 0) { + switches = new t_arch_switch_inf[num_switches]; + } + + float R, Cin, Cint, Cout, Tdel; + for (int i = 0; i < (int)num_switches; ++i) { + t_arch_switch_inf& as = switches[i]; + + R = Cin = Cint = Cout = Tdel = 0.0; + SwitchType type; + + if (i == 0) { + switch_name = "short"; + type = SwitchType::SHORT; + R = 0.0; + } else if (i == 1) { + switch_name = "generic"; + type = SwitchType::MUX; + R = 0.0; + } else { + auto entry = pip_timing_models_list[i - 2]; + auto model = timing_data[entry.second]; + std::stringstream name; + std::string mux_type_string = entry.first ? "mux_" : "passGate_"; + name << mux_type_string; + + R = get_corner_value(model.getOutputResistance(), "slow", "min"); + name << "R" << std::scientific << R; + + Cin = get_corner_value(model.getInputCapacitance(), "slow", "min"); + name << "Cin" << std::scientific << Cin; + + Cout = get_corner_value(model.getOutputCapacitance(), "slow", "min"); + name << "Cout" << std::scientific << Cout; + + if (entry.first) { + Cint = get_corner_value(model.getInternalCapacitance(), "slow", "min"); + name << "Cinternal" << std::scientific << Cint; + } + + Tdel = get_corner_value(model.getInternalDelay(), "slow", "min"); + name << "Tdel" << std::scientific << Tdel; + + switch_name = name.str(); + type = entry.first ? SwitchType::MUX : SwitchType::PASS_GATE; + } + + /* Should never happen */ + if (switch_name == std::string(VPR_DELAYLESS_SWITCH_NAME)) { + archfpga_throw(arch_file_, __LINE__, + "Switch name '%s' is a reserved name for VPR internal usage!", switch_name.c_str()); + } + + as.name = vtr::strdup(switch_name.c_str()); + as.set_type(type); + as.mux_trans_size = as.type() == SwitchType::MUX ? 1 : 0; + + as.R = R; + as.Cin = Cin; + as.Cout = Cout; + as.Cinternal = Cint; + as.set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); + + if (as.type() == SwitchType::SHORT || as.type() == SwitchType::PASS_GATE) { + as.buf_size_type = BufferSize::ABSOLUTE; + as.buf_size = 0; + as.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; + as.power_buffer_size = 0.; + } else { + as.buf_size_type = BufferSize::AUTO; + as.buf_size = 0.; + as.power_buffer_type = POWER_BUFFER_TYPE_AUTO; + } + } + } + + void process_segments() { + auto strList = ar_.getStrList(); + + // Segment names will be taken from wires connected to pips + // They are good representation for nodes + std::set wire_names; + for (auto tile_type : ar_.getTileTypeList()) { + auto wires = tile_type.getWires(); + for (auto pip : tile_type.getPips()) { + wire_names.insert(wires[pip.getWire0()]); + wire_names.insert(wires[pip.getWire1()]); + } + } + int num_seg = wire_names.size(); + arch_->Segments.resize(num_seg); + uint32_t index = 0; + for (auto i : wire_names) { + // Use default values as we will populate rr_graph with correct values + // This segments are just declaration of future use + arch_->Segments[index].name = std::string(strList[i]); + arch_->Segments[index].length = 1; + arch_->Segments[index].frequency = 1; + arch_->Segments[index].Rmetal = 0; + arch_->Segments[index].Cmetal = 0; + arch_->Segments[index].parallel_axis = BOTH_AXIS; + arch_->Segments[index].directionality = BI_DIRECTIONAL; + arch_->Segments[index].arch_wire_switch = 1; + arch_->Segments[index].arch_opin_switch = 1; + arch_->Segments[index].cb.resize(1); + arch_->Segments[index].cb[0] = true; + arch_->Segments[index].sb.resize(2); + arch_->Segments[index].sb[0] = true; + arch_->Segments[index].sb[1] = true; + ++index; + } + } }; void FPGAInterchangeReadArch(const char* FPGAInterchangeDeviceFile, diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 268e6135085..49302728d19 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -41,4 +41,30 @@ TEST_CASE("read_interchange_models", "[vpr]") { REQUIRE(lib_models.size() == 0); } +TEST_CASE("read_interchange_layout", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + auto& gd = arch.grid_layouts[0]; + REQUIRE(gd.grid_type == GridDefType::FIXED); + REQUIRE(gd.height == 10); + REQUIRE(gd.width == 10); + + std::unordered_map tile_types({{"NULL", false}, {"PWR", false}, {"IOB", false}, {"CLB", false}}); + for (auto& loc : gd.loc_defs) { + auto ret = tile_types.find(loc.block_type); + REQUIRE(ret != tile_types.end()); + REQUIRE(loc.priority == 1); + + ret->second = true; + } + + for (auto type : tile_types) { + CHECK(type.second); + } +} + } // namespace From c119afb6e542a688d8fce6c3d301987c9d7c2163 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Thu, 14 Oct 2021 18:22:35 +0200 Subject: [PATCH 2/6] vpr: interchange: add interchange netlist reading support Signed-off-by: Alessandro Comodi --- libs/libarchfpga/src/physical_types.h | 18 + .../src/read_fpga_interchange_arch.cpp | 28 + libs/libvtrutil/src/vtr_hash.h | 10 + vpr/src/base/atom_netlist_utils.cpp | 7 +- vpr/src/base/read_circuit.cpp | 42 +- vpr/src/base/read_circuit.h | 19 +- vpr/src/base/read_interchange_netlist.cpp | 521 ++++++++++++++++++ vpr/src/base/read_interchange_netlist.h | 10 + vpr/src/base/read_options.cpp | 13 +- vpr/src/base/vpr_api.cpp | 12 +- vpr/src/util/vpr_error.h | 1 + vpr/test/lut.netlist | Bin 0 -> 1493 bytes vpr/test/test_interchange_netlist.cpp | 33 ++ 13 files changed, 668 insertions(+), 46 deletions(-) create mode 100644 vpr/src/base/read_interchange_netlist.cpp create mode 100644 vpr/src/base/read_interchange_netlist.h create mode 100644 vpr/test/lut.netlist create mode 100644 vpr/test/test_interchange_netlist.cpp diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 8b789bfb9d4..4880d5b9989 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,23 @@ 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 + 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 fee979ea9f7..b2f4ad2b044 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -112,6 +112,7 @@ struct ArchReader { } void read_arch() { + process_luts(); process_models(); process_device(); @@ -222,6 +223,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) { @@ -235,6 +244,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..9bc171fe2ad 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_LOG("%s\n", circuit_file); if (name_ext[1] == ".blif") { circuit_format = e_circuit_format::BLIF; } else if (name_ext[1] == ".eblif") { @@ -49,10 +53,18 @@ 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: + VTR_ASSERT(false); + 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..f4087b64c82 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-interchange: 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..d293a03b94f 100644 --- a/vpr/src/util/vpr_error.h +++ b/vpr/src/util/vpr_error.h @@ -19,6 +19,7 @@ enum e_vpr_error { VPR_ERROR_NET_F, VPR_ERROR_PLACE_F, VPR_ERROR_BLIF_F, + VPR_ERROR_IC_NETLIST_F, 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 0000000000000000000000000000000000000000..d8a49d6feab4741fad8157eb20ec2eba7c7a4cc1 GIT binary patch literal 1493 zcmV;`1uFU0t|7>-1E^cLXY-w|J0JWG)Y#T)!#>bA6IB5zbq&!LjZE@*? z`mt*#aYBH)G%c2zCP=MBDUosPO}Zj}$lf&Kgpi1Y5C=H)f>a?86#~%c4rdC7<;daA%)o}zaQb(p8?VV zHW+5?T#&JExjM$#Adlbh>uxV&L7r~}1i!-B`}}e$Ro7{lQGSq-b;x}d>1X)<5)fs= z$nQn|AISfU#{vG-ujR{r{f+wjwfwut-{SEU`l05J1V#SgpvW6`mFyj3C-`HL$KQCz zzX(vCNDIBf=Sr{ek~J=azX2|Xauz%b9s-{M&wwY;em4{SnnwRmqwOA7?dE+<#Mi-P z+6OhS@d3mO8h;e=n#LbPENXlZ@g0p1A-=8g$D!VAz#I7tK*P5235`#K zzo_wfaB6<1uVOTIMsX(*lkbFDPUj(y^1(fFUCZyn!^-Ku=MlwUP4xcspe}t=`h51I zRZZU~`OY;fC;LL@GXFRSk)^PucvE69^ zUG8Cwsb6zdcQLs?3oZ8x-9OR5KMenFrM~o(y4%CgY~rW(Pkzg#?(y(T`Rt$W*X!Xo z?$b{nzp#g2x`|((QlC=4qU9M>_nY$R7u5XvJ^ZG9`gLi3av#Z}FH#xhy!vgPI=F%AM58c}3lCkzWh^`kZTv$9{gT^6M+! z@0&b+%=xW=_>SE|`5zH~N4pe%;N`cVx1dLZLQ^C?13d#>f-XVdfW85}0lfiz5BeVT z{w^_&6iJUkQ~ZhR1?UCnb?9~I+t9b6H=#G-cOUvb@{fjurbv1=B>F><=6My`Po(lU zq18z9U#}J1brdh=oqXIbJ65f5Eni-?;!D-#d{hBZ$ErK^xVSG_d1tj|)#EFx4qZ9I zd0sMheQGjhM9p-}aE}LTmD;-$<^QM3ma}5l9cE+wRuHQ-yJS1|b*nD)`HSN1m*<@< z4pv#{dBh8sFA07z>po;~D9Pa9nZcnVg9GOr>dnC~l3y%D(uqiZ)u}`lSL~%~zE-zl zg~C)aZA`f$M>vuehNVhzb;Zh+D^9Lx)p&o4!Y9!@$z$+d$GK1`l`7@$ z>JBs?WW$J==_tQX8oSyz3}gFf7Rlw7%f-F?PIHgrA_mr8%q7~qj*L_+73H_2A^eSY z`;U?T*tWmfZvS!eAK&&*w%dP#{3o{kQ|e-q=M$gSBmtF7a2#K!3jH)!G5MkoA@F>1i%BV(JL@HbQBPXlBpJK>*9l7EV? zOgq&H|CH(aPxAHCf%!FQeoZmIl@83W>CUg|&Tpjy^J}{ED_#KmYGR?y@7E;R^PBrG vA^WPaAfkzwL`=D{(uwv{3EqCn5dH?*5$%h8HOG%LVxRpV0z-W>&>H{%G7KM= literal 0 HcmV?d00001 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 From cb085f7412c78363a4c651b2a08a80c37c971eea Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Mon, 29 Nov 2021 14:02:21 +0100 Subject: [PATCH 3/6] libs: arch: move common functions to util and check source files This commit also reworks the way the name is assigned to the null types Signed-off-by: Alessandro Comodi --- libs/libarchfpga/src/arch_util.cpp | 211 ++++++++++++++++++- libs/libarchfpga/src/arch_util.h | 8 +- libs/libarchfpga/src/read_xml_arch_file.cpp | 214 +------------------- vpr/src/base/SetupVPR.cpp | 7 +- vpr/src/base/vpr_types.h | 2 - 5 files changed, 221 insertions(+), 221 deletions(-) diff --git a/libs/libarchfpga/src/arch_util.cpp b/libs/libarchfpga/src/arch_util.cpp index 64c16bbd093..4932d9fb2dc 100644 --- a/libs/libarchfpga/src/arch_util.cpp +++ b/libs/libarchfpga/src/arch_util.cpp @@ -555,9 +555,9 @@ t_port* findPortByName(const char* name, t_pb_type* pb_type, int* high_index, in return port; } -t_physical_tile_type SetupEmptyPhysicalType() { +t_physical_tile_type get_empty_physical_type(std::string name) { t_physical_tile_type type; - type.name = vtr::strdup("EMPTY"); + type.name = vtr::strdup(name.c_str()); type.num_pins = 0; type.width = 1; type.height = 1; @@ -573,9 +573,9 @@ t_physical_tile_type SetupEmptyPhysicalType() { return type; } -t_logical_block_type SetupEmptyLogicalType() { +t_logical_block_type get_empty_logical_type(std::string name) { t_logical_block_type type; - type.name = vtr::strdup("EMPTY"); + type.name = vtr::strdup(name.c_str()); type.pb_type = nullptr; return type; @@ -1392,5 +1392,208 @@ const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb } } } + return nullptr; } + +void link_physical_logical_types(std::vector& PhysicalTileTypes, + std::vector& LogicalBlockTypes) { + for (auto& physical_tile : PhysicalTileTypes) { + if (physical_tile.index == EMPTY_TYPE_INDEX) continue; + + auto eq_sites_set = get_equivalent_sites_set(&physical_tile); + auto equivalent_sites = std::vector(eq_sites_set.begin(), eq_sites_set.end()); + + auto criteria = [&physical_tile](const t_logical_block_type* lhs, const t_logical_block_type* rhs) { + int num_pins = physical_tile.num_inst_pins; + + int lhs_num_logical_pins = lhs->pb_type->num_pins; + int rhs_num_logical_pins = rhs->pb_type->num_pins; + + int lhs_diff_num_pins = num_pins - lhs_num_logical_pins; + int rhs_diff_num_pins = num_pins - rhs_num_logical_pins; + + return lhs_diff_num_pins < rhs_diff_num_pins; + }; + + std::sort(equivalent_sites.begin(), equivalent_sites.end(), criteria); + + for (auto& logical_block : LogicalBlockTypes) { + for (auto site : equivalent_sites) { + if (0 == strcmp(logical_block.name, site->pb_type->name)) { + logical_block.equivalent_tiles.push_back(&physical_tile); + break; + } + } + } + } + + for (auto& logical_block : LogicalBlockTypes) { + if (logical_block.index == EMPTY_TYPE_INDEX) continue; + + auto& equivalent_tiles = logical_block.equivalent_tiles; + + if ((int)equivalent_tiles.size() <= 0) { + archfpga_throw(__FILE__, __LINE__, + "Logical Block %s does not have any equivalent tiles.\n", logical_block.name); + } + + std::unordered_map ignored_pins_check_map; + std::unordered_map global_pins_check_map; + + auto criteria = [&logical_block](const t_physical_tile_type* lhs, const t_physical_tile_type* rhs) { + int num_logical_pins = logical_block.pb_type->num_pins; + + int lhs_num_pins = lhs->num_inst_pins; + int rhs_num_pins = rhs->num_inst_pins; + + int lhs_diff_num_pins = lhs_num_pins - num_logical_pins; + int rhs_diff_num_pins = rhs_num_pins - num_logical_pins; + + return lhs_diff_num_pins < rhs_diff_num_pins; + }; + + std::sort(equivalent_tiles.begin(), equivalent_tiles.end(), criteria); + + for (int pin = 0; pin < logical_block.pb_type->num_pins; pin++) { + for (auto& tile : equivalent_tiles) { + auto direct_maps = tile->tile_block_pin_directs_map.at(logical_block.index); + + for (auto& sub_tile : tile->sub_tiles) { + auto equiv_sites = sub_tile.equivalent_sites; + if (std::find(equiv_sites.begin(), equiv_sites.end(), &logical_block) == equiv_sites.end()) { + continue; + } + + auto direct_map = direct_maps.at(sub_tile.index); + + auto result = direct_map.find(t_logical_pin(pin)); + if (result == direct_map.end()) { + archfpga_throw(__FILE__, __LINE__, + "Logical pin %d not present in pin mapping between Tile %s and Block %s.\n", + pin, tile->name, logical_block.name); + } + + int sub_tile_pin_index = result->second.pin; + int phy_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; + + bool is_ignored = tile->is_ignored_pin[phy_index]; + bool is_global = tile->is_pin_global[phy_index]; + + auto ignored_result = ignored_pins_check_map.insert(std::pair(pin, is_ignored)); + if (!ignored_result.second && ignored_result.first->second != is_ignored) { + archfpga_throw(__FILE__, __LINE__, + "Physical Tile %s has a different value for the ignored pin (physical pin: %d, logical pin: %d) " + "different from the corresponding pins of the other equivalent site %s\n.", + tile->name, phy_index, pin, logical_block.name); + } + + auto global_result = global_pins_check_map.insert(std::pair(pin, is_global)); + if (!global_result.second && global_result.first->second != is_global) { + archfpga_throw(__FILE__, __LINE__, + "Physical Tile %s has a different value for the global pin (physical pin: %d, logical pin: %d) " + "different from the corresponding pins of the other equivalent sites\n.", + tile->name, phy_index, pin); + } + } + } + } + } +} + +/* Sets up the pin classes for the type. */ +void setup_pin_classes(t_physical_tile_type* type) { + int i, k; + int pin_count; + int num_class; + + for (i = 0; i < type->num_pins; i++) { + type->pin_class.push_back(OPEN); + type->is_ignored_pin.push_back(true); + type->is_pin_global.push_back(true); + } + + pin_count = 0; + + t_class_range class_range; + + /* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */ + for (auto& sub_tile : type->sub_tiles) { + int capacity = sub_tile.capacity.total(); + class_range.low = type->class_inf.size(); + class_range.high = class_range.low - 1; + for (i = 0; i < capacity; ++i) { + for (const auto& port : sub_tile.ports) { + if (port.equivalent != PortEquivalence::NONE) { + t_class class_inf; + num_class = (int)type->class_inf.size(); + class_inf.num_pins = port.num_pins; + class_inf.equivalence = port.equivalent; + + if (port.type == IN_PORT) { + class_inf.type = RECEIVER; + } else { + VTR_ASSERT(port.type == OUT_PORT); + class_inf.type = DRIVER; + } + + for (k = 0; k < port.num_pins; ++k) { + class_inf.pinlist.push_back(pin_count); + type->pin_class[pin_count] = num_class; + // clock pins and other specified global ports are initially specified + // as ignored pins (i.e. connections are not created in the rr_graph and + // nets connected to the port are ignored as well). + type->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; + // clock pins and other specified global ports are flaged as global + type->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; + + if (port.is_clock) { + type->clock_pin_indices.push_back(pin_count); + } + + pin_count++; + } + + type->class_inf.push_back(class_inf); + class_range.high++; + } else if (port.equivalent == PortEquivalence::NONE) { + for (k = 0; k < port.num_pins; ++k) { + t_class class_inf; + num_class = (int)type->class_inf.size(); + class_inf.num_pins = 1; + class_inf.pinlist.push_back(pin_count); + class_inf.equivalence = port.equivalent; + + if (port.type == IN_PORT) { + class_inf.type = RECEIVER; + } else { + VTR_ASSERT(port.type == OUT_PORT); + class_inf.type = DRIVER; + } + + type->pin_class[pin_count] = num_class; + // clock pins and other specified global ports are initially specified + // as ignored pins (i.e. connections are not created in the rr_graph and + // nets connected to the port are ignored as well). + type->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; + // clock pins and other specified global ports are flaged as global + type->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; + + if (port.is_clock) { + type->clock_pin_indices.push_back(pin_count); + } + + pin_count++; + + type->class_inf.push_back(class_inf); + class_range.high++; + } + } + } + } + + type->sub_tiles[sub_tile.index].class_range = class_range; + } + + VTR_ASSERT(pin_count == type->num_pins); +} diff --git a/libs/libarchfpga/src/arch_util.h b/libs/libarchfpga/src/arch_util.h index b67240b82d5..d5519f5684c 100644 --- a/libs/libarchfpga/src/arch_util.h +++ b/libs/libarchfpga/src/arch_util.h @@ -60,8 +60,8 @@ void free_type_descriptors(std::vector& type_descriptors); t_port* findPortByName(const char* name, t_pb_type* pb_type, int* high_index, int* low_index); -t_physical_tile_type SetupEmptyPhysicalType(); -t_logical_block_type SetupEmptyLogicalType(); +t_physical_tile_type get_empty_physical_type(std::string name); +t_logical_block_type get_empty_logical_type(std::string name); std::unordered_set get_equivalent_sites_set(t_physical_tile_type_ptr type); @@ -100,4 +100,8 @@ bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::string& bl const t_pin_to_pin_annotation* find_sequential_annotation(const t_pb_type* pb_type, const t_model_ports* port, enum e_pin_to_pin_delay_annotations annot_type); const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb_type, std::string in_port, std::string out_port); +void link_physical_logical_types(std::vector& PhysicalTileTypes, + std::vector& LogicalBlockTypes); + +void setup_pin_classes(t_physical_tile_type* type); #endif diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 563c2be6bbb..ba6b82f28a9 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -109,7 +109,6 @@ struct t_pin_locs { /* Function prototypes */ /* Populate data */ -static void SetupPinClasses(t_physical_tile_type* PhysicalTileType); static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, @@ -264,9 +263,6 @@ int find_switch_by_name(const t_arch& arch, std::string switch_name); e_side string_to_side(std::string side_str); -static void link_physical_logical_types(std::vector& PhysicalTileTypes, - std::vector& LogicalBlockTypes); - template static T* get_type_by_name(const char* type_name, std::vector& types); @@ -450,105 +446,6 @@ void XmlReadArch(const char* ArchFile, * */ -/* Sets up the pin classes for the type. */ -static void SetupPinClasses(t_physical_tile_type* PhysicalTileType) { - int i, k; - int pin_count; - int num_class; - - pugi::xml_node Cur; - - for (i = 0; i < PhysicalTileType->num_pins; i++) { - PhysicalTileType->pin_class.push_back(OPEN); - PhysicalTileType->is_ignored_pin.push_back(true); - PhysicalTileType->is_pin_global.push_back(true); - } - - pin_count = 0; - - t_class_range class_range; - - /* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */ - for (auto& sub_tile : PhysicalTileType->sub_tiles) { - int capacity = sub_tile.capacity.total(); - class_range.low = PhysicalTileType->class_inf.size(); - class_range.high = class_range.low - 1; - for (i = 0; i < capacity; ++i) { - for (const auto& port : sub_tile.ports) { - if (port.equivalent != PortEquivalence::NONE) { - t_class class_inf; - num_class = (int)PhysicalTileType->class_inf.size(); - class_inf.num_pins = port.num_pins; - class_inf.equivalence = port.equivalent; - - if (port.type == IN_PORT) { - class_inf.type = RECEIVER; - } else { - VTR_ASSERT(port.type == OUT_PORT); - class_inf.type = DRIVER; - } - - for (k = 0; k < port.num_pins; ++k) { - class_inf.pinlist.push_back(pin_count); - PhysicalTileType->pin_class[pin_count] = num_class; - // clock pins and other specified global ports are initially specified - // as ignored pins (i.e. connections are not created in the rr_graph and - // nets connected to the port are ignored as well). - PhysicalTileType->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; - // clock pins and other specified global ports are flaged as global - PhysicalTileType->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; - - if (port.is_clock) { - PhysicalTileType->clock_pin_indices.push_back(pin_count); - } - - pin_count++; - } - - PhysicalTileType->class_inf.push_back(class_inf); - class_range.high++; - } else if (port.equivalent == PortEquivalence::NONE) { - for (k = 0; k < port.num_pins; ++k) { - t_class class_inf; - num_class = (int)PhysicalTileType->class_inf.size(); - class_inf.num_pins = 1; - class_inf.pinlist.push_back(pin_count); - class_inf.equivalence = port.equivalent; - - if (port.type == IN_PORT) { - class_inf.type = RECEIVER; - } else { - VTR_ASSERT(port.type == OUT_PORT); - class_inf.type = DRIVER; - } - - PhysicalTileType->pin_class[pin_count] = num_class; - // clock pins and other specified global ports are initially specified - // as ignored pins (i.e. connections are not created in the rr_graph and - // nets connected to the port are ignored as well). - PhysicalTileType->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; - // clock pins and other specified global ports are flaged as global - PhysicalTileType->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; - - if (port.is_clock) { - PhysicalTileType->clock_pin_indices.push_back(pin_count); - } - - pin_count++; - - PhysicalTileType->class_inf.push_back(class_inf); - class_range.high++; - } - } - } - } - - PhysicalTileType->sub_tiles[sub_tile.index].class_range = class_range; - } - - VTR_ASSERT(pin_count == PhysicalTileType->num_pins); -} - static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, t_pin_locs* pin_locs, @@ -2883,7 +2780,7 @@ static void ProcessTiles(pugi::xml_node Node, /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ - t_physical_tile_type EMPTY_PHYSICAL_TILE_TYPE = SetupEmptyPhysicalType(); + t_physical_tile_type EMPTY_PHYSICAL_TILE_TYPE = get_empty_physical_type(std::string("EMPTY")); EMPTY_PHYSICAL_TILE_TYPE.index = 0; PhysicalTileTypes.push_back(EMPTY_PHYSICAL_TILE_TYPE); @@ -3542,7 +3439,7 @@ static void ProcessSubTiles(pugi::xml_node Node, int num_pins = PhysicalTileType->num_pins; PhysicalTileType->pinloc.resize({width, height, num_sides}, std::vector(num_pins, false)); - SetupPinClasses(PhysicalTileType); + setup_pin_classes(PhysicalTileType); LoadPinLoc(Cur, PhysicalTileType, &pin_locs, loc_data); } @@ -3556,7 +3453,7 @@ static void ProcessComplexBlocks(vtr::string_internment* strings, pugi::xml_node /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ - t_logical_block_type EMPTY_LOGICAL_BLOCK_TYPE = SetupEmptyLogicalType(); + t_logical_block_type EMPTY_LOGICAL_BLOCK_TYPE = get_empty_logical_type(std::string("EMPTY")); EMPTY_LOGICAL_BLOCK_TYPE.index = 0; LogicalBlockTypes.push_back(EMPTY_LOGICAL_BLOCK_TYPE); @@ -4669,111 +4566,6 @@ e_side string_to_side(std::string side_str) { return side; } -static void link_physical_logical_types(std::vector& PhysicalTileTypes, - std::vector& LogicalBlockTypes) { - for (auto& physical_tile : PhysicalTileTypes) { - if (physical_tile.index == EMPTY_TYPE_INDEX) continue; - - auto eq_sites_set = get_equivalent_sites_set(&physical_tile); - auto equivalent_sites = std::vector(eq_sites_set.begin(), eq_sites_set.end()); - - auto criteria = [&physical_tile](const t_logical_block_type* lhs, const t_logical_block_type* rhs) { - int num_pins = physical_tile.num_inst_pins; - - int lhs_num_logical_pins = lhs->pb_type->num_pins; - int rhs_num_logical_pins = rhs->pb_type->num_pins; - - int lhs_diff_num_pins = num_pins - lhs_num_logical_pins; - int rhs_diff_num_pins = num_pins - rhs_num_logical_pins; - - return lhs_diff_num_pins < rhs_diff_num_pins; - }; - - std::sort(equivalent_sites.begin(), equivalent_sites.end(), criteria); - - for (auto& logical_block : LogicalBlockTypes) { - for (auto site : equivalent_sites) { - if (0 == strcmp(logical_block.name, site->pb_type->name)) { - logical_block.equivalent_tiles.push_back(&physical_tile); - break; - } - } - } - } - - for (auto& logical_block : LogicalBlockTypes) { - if (logical_block.index == EMPTY_TYPE_INDEX) continue; - - auto& equivalent_tiles = logical_block.equivalent_tiles; - - if ((int)equivalent_tiles.size() <= 0) { - archfpga_throw(__FILE__, __LINE__, - "Logical Block %s does not have any equivalent tiles.\n", logical_block.name); - } - - std::unordered_map ignored_pins_check_map; - std::unordered_map global_pins_check_map; - - auto criteria = [&logical_block](const t_physical_tile_type* lhs, const t_physical_tile_type* rhs) { - int num_logical_pins = logical_block.pb_type->num_pins; - - int lhs_num_pins = lhs->num_inst_pins; - int rhs_num_pins = rhs->num_inst_pins; - - int lhs_diff_num_pins = lhs_num_pins - num_logical_pins; - int rhs_diff_num_pins = rhs_num_pins - num_logical_pins; - - return lhs_diff_num_pins < rhs_diff_num_pins; - }; - - std::sort(equivalent_tiles.begin(), equivalent_tiles.end(), criteria); - - for (int pin = 0; pin < logical_block.pb_type->num_pins; pin++) { - for (auto& tile : equivalent_tiles) { - auto direct_maps = tile->tile_block_pin_directs_map.at(logical_block.index); - - for (auto& sub_tile : tile->sub_tiles) { - auto equiv_sites = sub_tile.equivalent_sites; - if (std::find(equiv_sites.begin(), equiv_sites.end(), &logical_block) == equiv_sites.end()) { - continue; - } - - auto direct_map = direct_maps.at(sub_tile.index); - - auto result = direct_map.find(t_logical_pin(pin)); - if (result == direct_map.end()) { - archfpga_throw(__FILE__, __LINE__, - "Logical pin %d not present in pin mapping between Tile %s and Block %s.\n", - pin, tile->name, logical_block.name); - } - - int sub_tile_pin_index = result->second.pin; - int phy_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; - - bool is_ignored = tile->is_ignored_pin[phy_index]; - bool is_global = tile->is_pin_global[phy_index]; - - auto ignored_result = ignored_pins_check_map.insert(std::pair(pin, is_ignored)); - if (!ignored_result.second && ignored_result.first->second != is_ignored) { - archfpga_throw(__FILE__, __LINE__, - "Physical Tile %s has a different value for the ignored pin (physical pin: %d, logical pin: %d) " - "different from the corresponding pins of the other equivalent site %s\n.", - tile->name, phy_index, pin, logical_block.name); - } - - auto global_result = global_pins_check_map.insert(std::pair(pin, is_global)); - if (!global_result.second && global_result.first->second != is_global) { - archfpga_throw(__FILE__, __LINE__, - "Physical Tile %s has a different value for the global pin (physical pin: %d, logical pin: %d) " - "different from the corresponding pins of the other equivalent sites\n.", - tile->name, phy_index, pin); - } - } - } - } - } -} - template static T* get_type_by_name(const char* type_name, std::vector& types) { for (auto& type : types) { diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 794abdbe6d2..8d3d415340d 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -139,8 +139,9 @@ void SetupVPR(const t_options* Options, int num_inputs = 0; int num_outputs = 0; for (auto& type : device_ctx.physical_tile_types) { - if (strcmp(type.name, EMPTY_BLOCK_NAME) == 0) { + if (type.index == 0) { VTR_ASSERT(device_ctx.EMPTY_PHYSICAL_TILE_TYPE == nullptr); + VTR_ASSERT(type.num_pins == 0); device_ctx.EMPTY_PHYSICAL_TILE_TYPE = &type; } @@ -156,7 +157,9 @@ void SetupVPR(const t_options* Options, device_ctx.EMPTY_LOGICAL_BLOCK_TYPE = nullptr; int max_equivalent_tiles = 0; for (const auto& type : device_ctx.logical_block_types) { - if (0 == strcmp(type.name, EMPTY_BLOCK_NAME)) { + if (type.index == 0) { + VTR_ASSERT(device_ctx.EMPTY_LOGICAL_BLOCK_TYPE == nullptr); + VTR_ASSERT(type.pb_type == nullptr); device_ctx.EMPTY_LOGICAL_BLOCK_TYPE = &type; } diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 944cc9da9c2..322b1b711a0 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -90,8 +90,6 @@ enum class ScreenUpdatePriority { constexpr auto EMPTY_BLOCK_ID = ClusterBlockId(-1); constexpr auto INVALID_BLOCK_ID = ClusterBlockId(-2); -constexpr const char* EMPTY_BLOCK_NAME = "EMPTY"; - /* * Files */ From d3cb23973e1b96d232bbe8f168574cad5de3620c Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Mon, 29 Nov 2021 14:03:35 +0100 Subject: [PATCH 4/6] libs: arch: interchange: add logical blocks and physical tile parsing Signed-off-by: Alessandro Comodi --- libs/libarchfpga/src/physical_types.h | 19 + .../src/read_fpga_interchange_arch.cpp | 993 ++++++++++++++++-- 2 files changed, 946 insertions(+), 66 deletions(-) diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 4880d5b9989..811e2f67a65 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1738,6 +1738,20 @@ struct t_lut_cell { std::vector inputs; }; +struct t_lut_bel { + std::string name; + + std::vector input_pins; + std::string output_pin; +}; + +struct t_package_pin { + std::string name; + + std::string site_name; + std::string bel_name; +}; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; @@ -1772,6 +1786,11 @@ struct t_arch { // Luts std::vector lut_cells; + std::vector lut_bels; + + // Package pins + // TODO: add possibility to have multiple packages + std::vector pad_bels; //The name of the switch used for the input connection block (i.e. to //connect routing tracks to block pins). diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index b2f4ad2b044..13fe3282c3f 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -11,6 +11,7 @@ #include "vtr_assert.h" #include "vtr_digest.h" #include "vtr_log.h" +#include "vtr_memory.h" #include "vtr_util.h" #include "arch_check.h" @@ -34,6 +35,8 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +/****************** Utility functions ******************/ + static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { bool slow_model = std::string(speed_model) == std::string("slow"); bool fast_model = std::string(speed_model) == std::string("fast"); @@ -100,22 +103,115 @@ static float get_corner_value(Device::CornerModel::Reader model, const char* spe return 0.; } +static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port) { + for (t_model* m : {arch->models, arch->model_library}) { + for (; m != nullptr; m = m->next) { + if (std::string(m->name) != model) + continue; + + for (t_model_ports* p : {m->inputs, m->outputs}) + for (; p != nullptr; p = p->next) + if (std::string(p->name) == port) + return p; + } + } + + archfpga_throw(__FILE__, __LINE__, + "Could not find model port: %s (%s)\n", port.c_str(), model.c_str()); +} + +static t_model* get_model(t_arch* arch, std::string model) { + for (t_model* m : {arch->models, arch->model_library}) + for (; m != nullptr; m = m->next) + if (std::string(m->name) == model) + return m; + + archfpga_throw(__FILE__, __LINE__, + "Could not find model: %s\n", model.c_str()); +} + +template +static T* get_type_by_name(const char* type_name, std::vector& types) { + for (auto& type : types) { + if (0 == strcmp(type.name, type_name)) { + return &type; + } + } + + archfpga_throw(__FILE__, __LINE__, + "Could not find type: %s\n", type_name); +} + +static t_port get_generic_port(t_arch* arch, + t_pb_type* pb_type, + PORTS dir, + std::string name, + std::string model = "", + int num_pins = 1) { + t_port port; + port.parent_pb_type = pb_type; + port.name = vtr::strdup(name.c_str()); + port.num_pins = num_pins; + port.index = 0; + port.absolute_first_pin_index = 0; + port.port_index_by_type = 0; + port.equivalent = PortEquivalence::NONE; + port.type = dir; + port.is_clock = false; + port.model_port = nullptr; + port.port_class = vtr::strdup(nullptr); + port.port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); + + if (!model.empty()) + port.model_port = get_model_port(arch, model, name); + + return port; +} + +/****************** End Utility functions ******************/ + struct ArchReader { public: - ArchReader(t_arch* arch, Device::Reader& arch_reader, const char* arch_file, std::vector& phys_types, std::vector& logical_types) + ArchReader(t_arch* arch, + Device::Reader& arch_reader, + const char* arch_file, + std::vector& phys_types, + std::vector& logical_types) : arch_(arch) , arch_file_(arch_file) , ar_(arch_reader) , ptypes_(phys_types) , ltypes_(logical_types) { set_arch_file_name(arch_file); + + for (auto cell_bel : ar_.getCellBelMap()) { + auto name = str(cell_bel.getCell()); + for (auto site_bels : cell_bel.getCommonPins()[0].getSiteTypes()) { + auto site_type = str(site_bels.getSiteType()); + for (auto bel : site_bels.getBels()) { + auto bel_name = str(bel); + std::pair key(site_type, bel_name); + bel_cell_mapping_[key].push_back(name); + } + } + } } void read_arch() { + // Preprocess arch information process_luts(); + process_package_pins(); + process_models(); process_device(); + process_blocks(); + process_tiles(); + link_physical_logical_types(ptypes_, ltypes_); + + SyncModelsPbTypes(arch_, ltypes_); + check_models(arch_); + process_layout(); process_switches(); process_segments(); @@ -130,6 +226,152 @@ struct ArchReader { t_default_fc_spec default_fc_; + // siteTypeName, belName , list of cell names + std::map, std::vector> bel_cell_mapping_; + + // Utils + std::string str(int idx) { + return std::string(ar_.getStrList()[idx].cStr()); + } + + int get_bel_type_count(Device::SiteType::Reader& site, Device::BELCategory category) { + int count = 0; + for (auto bel : site.getBels()) + if (bel.getCategory() == category) + count++; + + return count; + } + + Device::BEL::Reader get_bel_reader(Device::SiteType::Reader& site, std::string bel_name) { + for (auto bel : site.getBels()) { + if (str(bel.getName()) == bel_name) + return bel; + } + VTR_ASSERT(0); + } + + std::string get_ic_prefix(Device::SiteType::Reader& site, Device::BEL::Reader& bel) { + return bel.getCategory() == Device::BELCategory::SITE_PORT ? str(site.getName()) : str(bel.getName()); + } + + std::unordered_map> get_ics(Device::SiteType::Reader& site) { + // dictionary: + // - key: interconnect name + // - value: (inputs string, outputs string, interconnect type) + std::unordered_map> ics; + for (auto wire : site.getSiteWires()) { + std::string wire_name = str(wire.getName()); + + // pin name, bel name + std::tuple out_pin; + bool is_mux = false; + for (auto pin : wire.getPins()) { + auto bel_pin = site.getBelPins()[pin]; + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto bel_name = get_ic_prefix(site, bel); + + bool is_output = bel_pin.getDir() == LogicalNetlist::Netlist::Direction::OUTPUT; + if (is_output) { + VTR_ASSERT(std::get<1>(out_pin).empty()); + out_pin = std::make_tuple(pin, str(bel_pin.getName()), bel_name); + is_mux = bel.getCategory() == Device::BELCategory::ROUTING; + } + } + VTR_ASSERT(!std::get<1>(out_pin).empty()); + + // Stores all output BELs connected to the same out_pin + std::string pad_bel_name; + std::string pad_bel_pin_name; + bool is_pad = false; + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + for (auto pad_bel : arch_->pad_bels) { + is_pad = pad_bel.bel_name == out_bel_name || is_pad; + pad_bel_name = pad_bel.bel_name == out_bel_name ? out_bel_name : pad_bel_name; + pad_bel_pin_name = pad_bel.bel_name == out_bel_name ? out_bel_pin_name : pad_bel_pin_name; + } + } + + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + auto in_bel_name = std::get<2>(out_pin); + auto in_bel_pin_name = std::get<1>(out_pin); + + std::string ostr = out_bel_name + "." + out_bel_pin_name; + std::string istr = in_bel_name + "." + in_bel_pin_name; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + if (is_mux) { + auto ic_name = in_bel_name; + auto res = ics.emplace(ic_name, std::make_tuple(std::string(), ostr, MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + outputs += " " + ostr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else if (bel.getCategory() == Device::BELCategory::ROUTING) { + auto ic_name = str(bel.getName()); + auto res = ics.emplace(ic_name, std::make_tuple(istr, std::string(), MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + inputs += " " + istr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else { + auto ic_name = wire_name + "_" + out_bel_pin_name; + if (is_pad && bel.getCategory() == Device::BELCategory::LOGIC) { + if (out_bel_name == pad_bel_name) + ostr += "_in"; + else { // Create new wire to connect PAD output to the BELs input + ic_name = wire_name + "_" + pad_bel_pin_name + "_out"; + istr = pad_bel_name + "." + pad_bel_pin_name + "_out"; + } + } + + auto res = ics.emplace(ic_name, std::make_tuple(istr, ostr, DIRECT_INTERC, is_pad)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + if (inputs.empty()) + inputs = istr; + else + inputs += " " + istr; + + if (outputs.empty()) + outputs = ostr; + else + outputs += " " + ostr; + + res.first->second = std::make_tuple(inputs, outputs, ic_type, is_pad); + } + } + } + } + + return ics; + } + // Model processing void process_models() { // Populate the common library, namely .inputs, .outputs, .names, .latches @@ -142,16 +384,25 @@ struct ArchReader { int model_index = NUM_MODELS_IN_LIBRARY; arch_->models = nullptr; - auto strList = ar_.getStrList(); auto primLib = ar_.getPrimLibs(); for (auto primitive : primLib.getCellDecls()) { - if (std::string(strList[primitive.getLib()]) == std::string("primitives")) { + if (str(primitive.getLib()) == std::string("primitives")) { + std::string prim_name = str(primitive.getName()); + + bool is_lut = false; + for (auto lut_cell : arch_->lut_cells) + is_lut = lut_cell.name == prim_name || is_lut; + + if (is_lut) + continue; + try { temp = new t_model; temp->index = model_index++; temp->never_prune = true; - temp->name = vtr::strdup(std::string(strList[primitive.getName()]).c_str()); + temp->name = vtr::strdup(str(primitive.getName()).c_str()); + ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); if (!ret_map_name.second) { archfpga_throw(arch_file_, __LINE__, @@ -172,11 +423,9 @@ struct ArchReader { arch_->models = temp; } } - return; } void process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { - auto strList = ar_.getStrList(); auto primLib = ar_.getPrimLibs(); auto portList = primLib.getPortList(); @@ -200,7 +449,7 @@ struct ArchReader { } t_model_ports* model_port = new t_model_ports; model_port->dir = dir; - model_port->name = vtr::strdup(std::string(strList[port.getName()]).c_str()); + model_port->name = vtr::strdup(str(port.getName()).c_str()); // TODO: add parsing of clock port types when the interchange schema allows for it: // https://github.com/chipsalliance/fpga-interchange-schema/issues/66 @@ -244,6 +493,571 @@ struct ArchReader { } } + // Complex Blocks + void process_blocks() { + auto siteTypeList = ar_.getSiteTypeList(); + + int index = 0; + auto EMPTY = get_empty_logical_type(std::string("NULL")); + EMPTY.index = index; + ltypes_.push_back(EMPTY); + + for (auto site : siteTypeList) { + t_logical_block_type ltype; + + std::string name = str(site.getName()); + + // Check for duplicates + auto is_duplicate = [name](t_logical_block_type l) { return std::string(l.name) == name; }; + VTR_ASSERT(std::find_if(ltypes_.begin(), ltypes_.end(), is_duplicate) == ltypes_.end()); + + ltype.name = vtr::strdup(name.c_str()); + ltype.index = ++index; + + auto pb_type = new t_pb_type; + ltype.pb_type = pb_type; + + pb_type->name = vtr::strdup(name.c_str()); + pb_type->num_pb = 1; + process_block_ports(pb_type, name, site); + + // Process modes (for simplicity, only the default mode is allowed for the time being) + pb_type->num_modes = 1; + pb_type->modes = new t_mode[pb_type->num_modes]; + + auto bels = site.getBels(); + auto mode = &pb_type->modes[0]; + mode->parent_pb_type = pb_type; + mode->index = 0; + mode->name = vtr::strdup("default"); + mode->disable_packing = false; + + int bel_count = get_bel_type_count(site, Device::BELCategory::LOGIC); + mode->num_pb_type_children = bel_count; + mode->pb_type_children = new t_pb_type[bel_count]; + + int count = 0; + for (auto bel : bels) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + auto bel_name = str(bel.getName()); + std::pair key(name, bel_name); + + auto cell_name = bel_name; + + if (bel_cell_mapping_.find(key) != bel_cell_mapping_.end()) { + VTR_ASSERT(bel_cell_mapping_[key].size() == 1); + cell_name = bel_cell_mapping_[key][0]; + } + + auto leaf_pb_type = new t_pb_type; + leaf_pb_type->name = vtr::strdup(bel_name.c_str()); + leaf_pb_type->num_pb = 1; + leaf_pb_type->parent_mode = mode; + + // TODO: fix this to make it dynamic. This will need the usage of CellBelMapping + + auto find_lut = [cell_name](t_lut_cell l) { return l.name == cell_name; }; + bool is_lut = std::find_if(arch_->lut_cells.begin(), arch_->lut_cells.end(), find_lut) != arch_->lut_cells.end(); + + auto find_pad = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; + bool is_pad = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), find_pad) != arch_->pad_bels.end(); + + if (!is_pad) + process_block_ports(leaf_pb_type, cell_name, site, false, is_lut); + + if (is_lut) { + leaf_pb_type->blif_model = nullptr; + process_lut_block(leaf_pb_type); + } else if (is_pad) { + leaf_pb_type->blif_model = nullptr; + process_pad_block(leaf_pb_type, bel, site); + } else { + leaf_pb_type->blif_model = vtr::strdup((std::string(".subckt ") + cell_name).c_str()); + leaf_pb_type->model = get_model(arch_, cell_name); + } + + mode->pb_type_children[count++] = *leaf_pb_type; + } + + process_interconnects(mode, site); + ltypes_.push_back(ltype); + } + } + + void process_lut_block(t_pb_type* lut) { + lut->num_modes = 1; + lut->modes = new t_mode[1]; + + // Check for duplicates + std::string lut_name = lut->name; + auto find_lut = [lut_name](t_lut_bel l) { return l.name == lut_name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), find_lut); + VTR_ASSERT(res != arch_->lut_bels.end()); + auto lut_bel = *res; + + auto mode = &lut->modes[0]; + mode->name = vtr::strdup("lut"); + mode->parent_pb_type = lut; + mode->index = 0; + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[1]; + + auto new_leaf = new t_pb_type; + new_leaf->name = vtr::strdup("lut_child"); + new_leaf->num_pb = 1; + new_leaf->parent_mode = mode; + + int num_ports = 2; + new_leaf->num_ports = num_ports; + new_leaf->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + new_leaf->blif_model = vtr::strdup(MODEL_NAMES); + new_leaf->model = get_model(arch_, std::string(MODEL_NAMES)); + + auto in_size = lut_bel.input_pins.size(); + new_leaf->ports[0] = get_generic_port(arch_, new_leaf, IN_PORT, "in", MODEL_NAMES, in_size); + new_leaf->ports[1] = get_generic_port(arch_, new_leaf, OUT_PORT, "out", MODEL_NAMES); + + mode->pb_type_children[0] = *new_leaf; + + // Num inputs + 1 (output pin) + int num_pins = in_size + 1; + + mode->num_interconnect = num_pins; + mode->interconnect = new t_interconnect[num_pins]; + + for (int i = 0; i < num_pins; i++) { + auto ic = new t_interconnect; + + std::stringstream istr; + std::stringstream ostr; + std::string input_string; + std::string output_string; + + if (i < num_pins - 1) { + istr << lut_bel.input_pins[i]; + ostr << "in[" << i << "]"; + input_string = std::string(lut->name) + std::string(".") + istr.str(); + output_string = std::string(new_leaf->name) + std::string(".") + ostr.str(); + } else { + istr << "out"; + ostr << lut_bel.output_pin; + input_string = std::string(new_leaf->name) + std::string(".") + istr.str(); + output_string = std::string(lut->name) + std::string(".") + ostr.str(); + } + std::string name = istr.str() + std::string("_") + ostr.str(); + ic->name = vtr::strdup(name.c_str()); + ic->type = DIRECT_INTERC; + ic->parent_mode_index = 0; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(input_string.c_str()); + ic->output_string = vtr::strdup(output_string.c_str()); + + mode->interconnect[i] = *ic; + } + } + + void process_pad_block(t_pb_type* pad, Device::BEL::Reader& bel, Device::SiteType::Reader& site) { + // For now, hard-code two modes for pads, so that PADs can either be IPADs or OPADs + pad->num_modes = 2; + pad->modes = new t_mode[2]; + + // Add PAD pb_type ports + VTR_ASSERT(bel.getPins().size() == 1); + std::string pin = str(site.getBelPins()[bel.getPins()[0]].getName()); + std::string ipin = pin + "_in"; + std::string opin = pin + "_out"; + + auto num_ports = 2; + auto ports = new t_port[num_ports]; + pad->ports = ports; + pad->num_ports = pad->num_pins = num_ports; + pad->num_input_pins = 1; + pad->num_output_pins = 1; + + int pin_abs = 0; + int pin_count = 0; + for (auto dir : {IN_PORT, OUT_PORT}) { + int pins_dir_count = 0; + t_port* port = &ports[pin_count]; + + port->parent_pb_type = pad; + port->index = pin_count++; + port->port_index_by_type = pins_dir_count++; + port->absolute_first_pin_index = pin_abs++; + + port->equivalent = PortEquivalence::NONE; + port->num_pins = 1; + port->type = dir; + port->is_clock = false; + + bool is_input = dir == IN_PORT; + port->name = is_input ? vtr::strdup(ipin.c_str()) : vtr::strdup(opin.c_str()); + port->model_port = nullptr; + port->port_class = vtr::strdup(nullptr); + port->port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); + } + + // OPAD mode + auto omode = &pad->modes[0]; + omode->name = vtr::strdup("opad"); + omode->parent_pb_type = pad; + omode->index = 0; + omode->num_pb_type_children = 1; + omode->pb_type_children = new t_pb_type[1]; + + auto opad = new t_pb_type; + opad->name = vtr::strdup("opad"); + opad->num_pb = 1; + opad->parent_mode = omode; + + num_ports = 1; + opad->num_ports = num_ports; + opad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + opad->blif_model = vtr::strdup(MODEL_OUTPUT); + opad->model = get_model(arch_, std::string(MODEL_OUTPUT)); + + opad->ports[0] = get_generic_port(arch_, opad, IN_PORT, "outpad", MODEL_OUTPUT); + omode->pb_type_children[0] = *opad; + + // IPAD mode + auto imode = &pad->modes[1]; + imode->name = vtr::strdup("ipad"); + imode->parent_pb_type = pad; + imode->index = 1; + imode->num_pb_type_children = 1; + imode->pb_type_children = new t_pb_type[1]; + + auto ipad = new t_pb_type; + ipad->name = vtr::strdup("ipad"); + ipad->num_pb = 1; + ipad->parent_mode = imode; + + num_ports = 1; + ipad->num_ports = num_ports; + ipad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + ipad->blif_model = vtr::strdup(MODEL_INPUT); + ipad->model = get_model(arch_, std::string(MODEL_INPUT)); + + ipad->ports[0] = get_generic_port(arch_, ipad, OUT_PORT, "inpad", MODEL_INPUT); + imode->pb_type_children[0] = *ipad; + + // Handle interconnects + int num_pins = 1; + + omode->num_interconnect = num_pins; + omode->interconnect = new t_interconnect[num_pins]; + + imode->num_interconnect = num_pins; + imode->interconnect = new t_interconnect[num_pins]; + + std::string opad_istr = std::string(pad->name) + std::string(".") + ipin; + std::string opad_ostr = std::string(opad->name) + std::string(".outpad"); + std::string o_ic_name = std::string(pad->name) + std::string("_") + std::string(opad->name); + + std::string ipad_istr = std::string(ipad->name) + std::string(".inpad"); + std::string ipad_ostr = std::string(pad->name) + std::string(".") + opin; + std::string i_ic_name = std::string(ipad->name) + std::string("_") + std::string(pad->name); + + auto o_ic = new t_interconnect[num_pins]; + auto i_ic = new t_interconnect[num_pins]; + + o_ic->name = vtr::strdup(o_ic_name.c_str()); + o_ic->type = DIRECT_INTERC; + o_ic->parent_mode_index = 0; + o_ic->parent_mode = omode; + o_ic->input_string = vtr::strdup(opad_istr.c_str()); + o_ic->output_string = vtr::strdup(opad_ostr.c_str()); + + i_ic->name = vtr::strdup(i_ic_name.c_str()); + i_ic->type = DIRECT_INTERC; + i_ic->parent_mode_index = 0; + i_ic->parent_mode = imode; + i_ic->input_string = vtr::strdup(ipad_istr.c_str()); + i_ic->output_string = vtr::strdup(ipad_ostr.c_str()); + + omode->interconnect[0] = *o_ic; + imode->interconnect[0] = *i_ic; + } + + void process_block_ports(t_pb_type* pb_type, std::string cell_name, Device::SiteType::Reader& site, bool is_root = true, bool is_model_library = false) { + std::unordered_set names; + + // Prepare data based on pb_type level + std::unordered_map pins; + if (is_root) { + for (auto pin : site.getPins()) { + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), dir); + } + } else { + for (auto bel : site.getBels()) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + if (std::string(pb_type->name) != str(bel.getName())) + continue; + + for (auto bel_pin : bel.getPins()) { + auto pin = site.getBelPins()[bel_pin]; + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), dir); + } + } + } + + auto num_ports = pins.size(); + auto ports = new t_port[num_ports]; + pb_type->ports = ports; + pb_type->num_ports = pb_type->num_pins = num_ports; + pb_type->num_input_pins = 0; + pb_type->num_output_pins = 0; + + int pin_abs = 0; + int pin_count = 0; + for (auto dir : {IN_PORT, OUT_PORT}) { + int pins_dir_count = 0; + for (auto pin_pair : pins) { + auto pin_name = pin_pair.first; + auto pin_dir = pin_pair.second; + + if (pin_dir != dir) + continue; + + VTR_ASSERT(names.insert(pin_name).second); + + bool is_input = dir == IN_PORT; + pb_type->num_input_pins += is_input ? 1 : 0; + pb_type->num_output_pins += is_input ? 0 : 1; + + auto port = get_generic_port(arch_, pb_type, dir, pin_name); + ports[pin_count] = port; + port.index = pin_count++; + port.port_index_by_type = pins_dir_count++; + port.absolute_first_pin_index = pin_abs++; + + if (!is_root && !is_model_library) + port.model_port = get_model_port(arch_, cell_name, pin_name); + } + } + } + + void process_interconnects(t_mode* mode, Device::SiteType::Reader& site) { + auto ics = get_ics(site); + auto num_ic = ics.size(); + + mode->num_interconnect = num_ic; + mode->interconnect = new t_interconnect[num_ic]; + + int curr_ic = 0; + std::unordered_set names; + + // Handle site wires, namely direct interconnects + for (auto ic_pair : ics) { + std::string ic_name = ic_pair.first; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + bool add_pack_pattern; + + std::tie(inputs, outputs, ic_type, add_pack_pattern) = ic_pair.second; + + t_interconnect* ic = &mode->interconnect[curr_ic++]; + + if (add_pack_pattern) { + ic->num_annotations = 1; + // pack pattern + auto pp = new t_pin_to_pin_annotation; + + pp->prop = (int*)vtr::calloc(1, sizeof(int)); + pp->value = (char**)vtr::calloc(1, sizeof(char*)); + + pp->type = E_ANNOT_PIN_TO_PIN_PACK_PATTERN; + pp->format = E_ANNOT_PIN_TO_PIN_CONSTANT; + pp->prop[0] = (int)E_ANNOT_PIN_TO_PIN_PACK_PATTERN_NAME; + pp->value[0] = vtr::strdup(ic_name.c_str()); + pp->input_pins = vtr::strdup(inputs.c_str()); + pp->output_pins = vtr::strdup(outputs.c_str()); + pp->num_value_prop_pairs = 1; + pp->clock = nullptr; + ic->annotations = pp; + } + + // No line num for interconnects, as line num is XML specific + // TODO: probably line_num should be deprecated as it is dependent + // on the input architecture format. + ic->line_num = 0; + ic->type = ic_type; + ic->parent_mode_index = mode->index; + ic->parent_mode = mode; + + VTR_ASSERT(names.insert(ic_name).second); + ic->name = vtr::strdup(ic_name.c_str()); + ic->input_string = vtr::strdup(inputs.c_str()); + ic->output_string = vtr::strdup(outputs.c_str()); + } + } + + // Physical Tiles + void process_tiles() { + auto EMPTY = get_empty_physical_type(std::string("NULL")); + int index = 0; + EMPTY.index = index; + ptypes_.push_back(EMPTY); + + auto tileTypeList = ar_.getTileTypeList(); + + for (auto tile : tileTypeList) { + t_physical_tile_type ptype; + auto name = str(tile.getName()); + + if (name == std::string("NULL")) + continue; + + ptype.name = vtr::strdup(name.c_str()); + ptype.index = ++index; + ptype.width = ptype.height = ptype.area = 1; + ptype.capacity = 1; + + process_sub_tiles(ptype, tile); + + setup_pin_classes(&ptype); + + bool is_pad = false; + for (auto site : tile.getSiteTypes()) { + auto site_type = ar_.getSiteTypeList()[site.getPrimaryType()]; + + for (auto bel : site_type.getBels()) { + auto bel_name = str(bel.getName()); + auto is_pad_func = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; + auto res = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), is_pad_func); + + is_pad = res != arch_->pad_bels.end() || is_pad; + } + } + + ptype.is_input_type = ptype.is_output_type = is_pad; + + ptypes_.push_back(ptype); + } + } + + void process_sub_tiles(t_physical_tile_type& type, Device::TileType::Reader& tile) { + // TODO: only one subtile at the moment + auto siteTypeList = ar_.getSiteTypeList(); + for (auto site_in_tile : tile.getSiteTypes()) { + t_sub_tile sub_tile; + + auto site = siteTypeList[site_in_tile.getPrimaryType()]; + + sub_tile.index = 0; + sub_tile.name = vtr::strdup(str(site.getName()).c_str()); + sub_tile.capacity.set(0, 0); + + int port_idx = 0; + int abs_first_pin_idx = 0; + int icount = 0; + int ocount = 0; + for (auto dir : {LogicalNetlist::Netlist::Direction::INPUT, LogicalNetlist::Netlist::Direction::OUTPUT}) { + int port_idx_by_type = 0; + for (auto pin : site.getPins()) { + if (pin.getDir() != dir) + continue; + + t_physical_tile_port port; + + port.name = vtr::strdup(str(pin.getName()).c_str()); + port.equivalent = PortEquivalence::NONE; + port.num_pins = 1; + + sub_tile.sub_tile_to_tile_pin_indices.push_back(port_idx); + port.index = port_idx++; + + port.absolute_first_pin_index = abs_first_pin_idx++; + port.port_index_by_type = port_idx_by_type++; + + if (dir == LogicalNetlist::Netlist::Direction::INPUT) { + port.type = IN_PORT; + icount++; + } else { + port.type = OUT_PORT; + ocount++; + } + + sub_tile.ports.push_back(port); + } + } + + auto pins_size = site.getPins().size(); + sub_tile.num_phy_pins += pins_size * type.capacity; + type.num_pins += pins_size * type.capacity; + type.num_inst_pins += pins_size; + + type.num_input_pins += icount; + type.num_output_pins += ocount; + type.num_receivers += icount * type.capacity; + type.num_drivers += ocount * type.capacity; + + type.pin_width_offset.resize(type.num_pins, 0); + type.pin_height_offset.resize(type.num_pins, 0); + + type.pinloc.resize({1, 1, 4}, std::vector(type.num_pins, false)); + for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (int pin = 0; pin < type.num_pins; pin++) { + type.pinloc[0][0][side][pin] = true; + type.pin_width_offset[pin] = 0; + type.pin_height_offset[pin] = 0; + } + } + + auto ltype = get_type_by_name(sub_tile.name, ltypes_); + vtr::bimap directs_map; + + for (int npin = 0; npin < type.num_pins; npin++) { + t_physical_pin physical_pin(npin); + t_logical_pin logical_pin(npin); + + directs_map.insert(logical_pin, physical_pin); + } + + sub_tile.equivalent_sites.push_back(ltype); + + type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + + // Assign FC specs + int iblk_pin = 0; + for (const auto& port : sub_tile.ports) { + t_fc_specification fc_spec; + + fc_spec.seg_index = 0; + + //Apply type and defaults + if (port.type == IN_PORT) { + fc_spec.fc_type = e_fc_type::IN; + fc_spec.fc_value_type = default_fc_.in_value_type; + fc_spec.fc_value = default_fc_.in_value; + } else { + VTR_ASSERT(port.type == OUT_PORT); + fc_spec.fc_type = e_fc_type::OUT; + fc_spec.fc_value_type = default_fc_.out_value_type; + fc_spec.fc_value = default_fc_.out_value; + } + + //Add all the pins from this port + for (int iport_pin = 0; iport_pin < port.num_pins; ++iport_pin) { + int true_physical_blk_pin = sub_tile.sub_tile_to_tile_pin_indices[iblk_pin++]; + fc_spec.pins.push_back(true_physical_blk_pin); + } + + type.fc_specs.push_back(fc_spec); + } + + type.sub_tiles.push_back(sub_tile); + } + } + void process_luts() { // Add LUT Cell definitions // This is helpful to understand which cells are LUTs @@ -261,50 +1075,100 @@ struct ArchReader { arch_->lut_cells.push_back(cell); } + + for (auto lut_elem : lut_def.getLutElements()) { + for (auto lut : lut_elem.getLuts()) { + for (auto bel : lut.getBels()) { + t_lut_bel lut_bel; + + std::string name = bel.getName().cStr(); + lut_bel.name = name; + + // Check for duplicates + auto is_duplicate = [name](t_lut_bel l) { return l.name == name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), is_duplicate); + if (res != arch_->lut_bels.end()) + continue; + + std::vector ipins; + for (auto pin : bel.getInputPins()) + ipins.push_back(pin.cStr()); + + lut_bel.input_pins = ipins; + lut_bel.output_pin = bel.getOutputPin().cStr(); + + arch_->lut_bels.push_back(lut_bel); + } + } + } + } + + void process_package_pins() { + for (auto package : ar_.getPackages()) { + for (auto pin : package.getPackagePins()) { + t_package_pin pckg_pin; + pckg_pin.name = str(pin.getPackagePin()); + + if (pin.getBel().isBel()) + pckg_pin.bel_name = str(pin.getBel().getBel()); + + if (pin.getSite().isSite()) + pckg_pin.site_name = str(pin.getSite().getSite()); + + arch_->pad_bels.push_back(pckg_pin); + } + } } // Layout Processing void process_layout() { - auto strList = ar_.getStrList(); auto tileList = ar_.getTileList(); auto tileTypeList = ar_.getTileTypeList(); - t_grid_def grid_def; - grid_def.width = grid_def.height = 0; - for (auto tile : tileList) { - grid_def.width = std::max(grid_def.width, tile.getCol() + 1); - grid_def.height = std::max(grid_def.height, tile.getRow() + 1); - } + std::vector packages; + for (auto package : ar_.getPackages()) + packages.push_back(str(package.getName())); - grid_def.grid_type = GridDefType::FIXED; - std::string name = std::string(ar_.getName()); - if (name == "auto") { - archfpga_throw(arch_file_, __LINE__, - "The name auto is reserved for auto-size layouts; please choose another name"); - } - grid_def.name = name; - for (auto tile : tileList) { - t_metadata_dict data; - std::string tile_prefix(strList[tile.getName()].cStr()); - auto tileType = tileTypeList[tile.getType()]; - std::string tile_type(strList[tileType.getName()].cStr()); - - size_t pos = tile_prefix.find(tile_type); - if (pos != std::string::npos && pos == 0) - tile_prefix.erase(pos, tile_type.length() + 1); - data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), - arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); - t_grid_loc_def single(tile_type, 1); - single.x.start_expr = tile.getCol(); - single.y.start_expr = tile.getRow(); - single.x.end_expr = single.x.start_expr + " + w - 1"; - single.y.end_expr = single.y.start_expr + " + h - 1"; - single.owned_meta = std::make_unique(data); - single.meta = single.owned_meta.get(); - grid_def.loc_defs.emplace_back(std::move(single)); - } + for (auto name : packages) { + t_grid_def grid_def; + grid_def.width = grid_def.height = 0; + for (auto tile : tileList) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } + + grid_def.grid_type = GridDefType::FIXED; + + if (name == "auto") { + archfpga_throw(arch_file_, __LINE__, + "The name auto is reserved for auto-size layouts; please choose another name"); + } + grid_def.name = name; + for (auto tile : tileList) { + t_metadata_dict data; + std::string tile_prefix = str(tile.getName()); + auto tileType = tileTypeList[tile.getType()]; + std::string tile_type = str(tileType.getName()); + + size_t pos = tile_prefix.find(tile_type); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type.length() + 1); + data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), + arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); + t_grid_loc_def single(tile_type, 1); + single.x.start_expr = std::to_string(tile.getCol()); + single.y.start_expr = std::to_string(tile.getRow()); + + single.x.end_expr = single.x.start_expr + " + w - 1"; + single.y.end_expr = single.y.start_expr + " + h - 1"; + + single.owned_meta = std::make_unique(data); + single.meta = single.owned_meta.get(); + grid_def.loc_defs.emplace_back(std::move(single)); + } - arch_->grid_layouts.emplace_back(std::move(grid_def)); + arch_->grid_layouts.emplace_back(std::move(grid_def)); + } } void process_device() { @@ -362,15 +1226,14 @@ struct ArchReader { std::string switch_name; arch_->num_switches = num_switches; - auto* switches = arch_->Switches; if (num_switches > 0) { - switches = new t_arch_switch_inf[num_switches]; + arch_->Switches = new t_arch_switch_inf[num_switches]; } float R, Cin, Cint, Cout, Tdel; for (int i = 0; i < (int)num_switches; ++i) { - t_arch_switch_inf& as = switches[i]; + t_arch_switch_inf* as = &arch_->Switches[i]; R = Cin = Cint = Cout = Tdel = 0.0; SwitchType type; @@ -417,32 +1280,30 @@ struct ArchReader { "Switch name '%s' is a reserved name for VPR internal usage!", switch_name.c_str()); } - as.name = vtr::strdup(switch_name.c_str()); - as.set_type(type); - as.mux_trans_size = as.type() == SwitchType::MUX ? 1 : 0; - - as.R = R; - as.Cin = Cin; - as.Cout = Cout; - as.Cinternal = Cint; - as.set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); - - if (as.type() == SwitchType::SHORT || as.type() == SwitchType::PASS_GATE) { - as.buf_size_type = BufferSize::ABSOLUTE; - as.buf_size = 0; - as.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; - as.power_buffer_size = 0.; + as->name = vtr::strdup(switch_name.c_str()); + as->set_type(type); + as->mux_trans_size = as->type() == SwitchType::MUX ? 1 : 0; + + as->R = R; + as->Cin = Cin; + as->Cout = Cout; + as->Cinternal = Cint; + as->set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); + + if (as->type() == SwitchType::SHORT || as->type() == SwitchType::PASS_GATE) { + as->buf_size_type = BufferSize::ABSOLUTE; + as->buf_size = 0; + as->power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; + as->power_buffer_size = 0.; } else { - as.buf_size_type = BufferSize::AUTO; - as.buf_size = 0.; - as.power_buffer_type = POWER_BUFFER_TYPE_AUTO; + as->buf_size_type = BufferSize::AUTO; + as->buf_size = 0.; + as->power_buffer_type = POWER_BUFFER_TYPE_AUTO; } } } void process_segments() { - auto strList = ar_.getStrList(); - // Segment names will be taken from wires connected to pips // They are good representation for nodes std::set wire_names; @@ -459,7 +1320,7 @@ struct ArchReader { for (auto i : wire_names) { // Use default values as we will populate rr_graph with correct values // This segments are just declaration of future use - arch_->Segments[index].name = std::string(strList[i]); + arch_->Segments[index].name = str(i); arch_->Segments[index].length = 1; arch_->Segments[index].frequency = 1; arch_->Segments[index].Rmetal = 0; From 7341abc32ec61feb319df998495acf7f6b5333a6 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Mon, 29 Nov 2021 14:04:16 +0100 Subject: [PATCH 5/6] vpr: tests: improve interchange tests Signed-off-by: Alessandro Comodi --- vpr/test/test_interchange_device.cpp | 122 ++++++++++++++++++++++++++- vpr/test/testarch.device | Bin 49806 -> 51495 bytes 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 49302728d19..53d9a5edacd 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -18,7 +18,7 @@ TEST_CASE("read_interchange_models", "[vpr]") { FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); - std::unordered_set models = {"IB", "OB", "LUT", "DFF", "GND", "VCC"}; + std::unordered_set models = {"IB", "OB", "DFF", "GND", "VCC"}; // Check that there are exactly the expected models for (auto* model = arch.models; model != nullptr; model = model->next) { @@ -67,4 +67,124 @@ TEST_CASE("read_interchange_layout", "[vpr]") { } } +TEST_CASE("read_interchange_luts", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set lut_cell_pins = {"A0", "A1", "A2", "A3"}; + std::unordered_set lut_bel_pins = {"I0", "I1", "I2", "I3"}; + + REQUIRE(arch.lut_cells.size() == 1); + REQUIRE(arch.lut_bels.size() == 1); + + auto lut_cell = arch.lut_cells[0]; + REQUIRE(lut_cell.name == std::string("LUT")); + REQUIRE(lut_cell.init_param == std::string("INIT")); + for (auto lut_pin : lut_cell_pins) + CHECK(std::find(lut_cell.inputs.begin(), lut_cell.inputs.end(), lut_pin) != lut_cell.inputs.end()); + + auto lut_bel = arch.lut_bels[0]; + REQUIRE(lut_bel.name == std::string("LUT")); + REQUIRE(lut_bel.output_pin == std::string("O")); + for (auto lut_pin : lut_bel_pins) + CHECK(std::find(lut_bel.input_pins.begin(), lut_bel.input_pins.end(), lut_pin) != lut_bel.input_pins.end()); +} + +TEST_CASE("read_interchange_pin_packages", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + // The device architecture file contains 35 perimetral PADs + REQUIRE(arch.pad_bels.size() == 35); + + int ipad = 0; + for (auto pad_bel : arch.pad_bels) { + REQUIRE(pad_bel.name == std::string("A") + std::to_string(ipad++)); + REQUIRE(pad_bel.bel_name == std::string("PAD")); + } +} + +TEST_CASE("read_interchange_tiles", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set ptypes = {"NULL", "IOB", "PWR", "CLB"}; + + // Check that there are exactly the expected models + for (auto ptype : physical_tile_types) { + std::string name = ptype.name; + REQUIRE(ptypes.find(name) != ptypes.end()); + ptypes.erase(name); + + if (name == std::string("IOB")) { + CHECK(ptype.is_input_type); + CHECK(ptype.is_output_type); + } + } + + REQUIRE(ptypes.size() == 0); +} + +TEST_CASE("read_interchange_pb_types", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set ltypes = {"NULL", "IOPAD", "SLICE", "POWER"}; + + std::unordered_map slice_ports = { + {"L0", PORTS::IN_PORT}, + {"L1", PORTS::IN_PORT}, + {"L2", PORTS::IN_PORT}, + {"L3", PORTS::IN_PORT}, + {"R", PORTS::IN_PORT}, + {"C", PORTS::IN_PORT}, + {"D", PORTS::IN_PORT}, + {"O", PORTS::OUT_PORT}, + {"Q", PORTS::OUT_PORT}}; + + // Check that there are exactly the expected models + for (auto ltype : logical_block_types) { + std::string name = ltype.name; + REQUIRE(ltypes.find(name) != ltypes.end()); + ltypes.erase(name); + + if (ltype.pb_type == nullptr) { + REQUIRE(name == std::string("NULL")); + continue; + } + + bool check_pb_type = name == std::string("SLICE"); + size_t num_visited = 0; + for (auto iport = 0; iport < ltype.pb_type->num_ports; iport++) { + auto port = ltype.pb_type->ports[iport]; + + REQUIRE(port.name != nullptr); + + if (!check_pb_type) + continue; + + auto res = slice_ports.find(std::string(port.name)); + REQUIRE(res != slice_ports.end()); + REQUIRE(res->second == port.type); + num_visited++; + } + + REQUIRE((num_visited == slice_ports.size() || !check_pb_type)); + } + + REQUIRE(ltypes.size() == 0); +} + } // namespace diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device index e9b12b1ac89a3fd52be432b3b106e8ec10f5a3dc..da02924ee50225e4549f816dd3bdf5b071e66348 100644 GIT binary patch literal 51495 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 pWJgTsy9BTg85`;KG{;ew0I$fFqNRa From c35d60dabd3c4804f34a7a719d735ab0357cb5a4 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Wed, 17 Nov 2021 17:31:15 +0100 Subject: [PATCH 6/6] libs: arch: interchange: improve bel to cell mapping Signed-off-by: Alessandro Comodi --- libs/libarchfpga/src/physical_types.h | 12 +- .../src/read_fpga_interchange_arch.cpp | 447 ++++++++++++------ vpr/test/lut.netlist | Bin 1493 -> 1683 bytes vpr/test/test_interchange_device.cpp | 75 ++- vpr/test/testarch.device | Bin 51495 -> 100983 bytes 5 files changed, 337 insertions(+), 197 deletions(-) diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 811e2f67a65..22319ffb063 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1745,16 +1745,10 @@ struct t_lut_bel { std::string output_pin; }; -struct t_package_pin { - std::string name; - - std::string site_name; - std::string bel_name; -}; - /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; + std::vector interned_strings; char* architecture_id; //Secure hash digest of the architecture file to uniquely identify this architecture @@ -1788,10 +1782,6 @@ struct t_arch { std::vector lut_cells; std::vector lut_bels; - // Package pins - // TODO: add possibility to have multiple packages - std::vector pad_bels; - //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 13fe3282c3f..153ae4e6a37 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,18 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +struct t_package_pin { + std::string name; + + std::string site_name; + std::string bel_name; +}; + +struct t_bel_cell_mapping { + int cell; + std::vector> pins; +}; + /****************** Utility functions ******************/ static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { @@ -103,7 +116,7 @@ static float get_corner_value(Device::CornerModel::Reader model, const char* spe return 0.; } -static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port) { +static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port, bool fail = true) { for (t_model* m : {arch->models, arch->model_library}) { for (; m != nullptr; m = m->next) { if (std::string(m->name) != model) @@ -116,8 +129,11 @@ static t_model_ports* get_model_port(t_arch* arch, std::string model, std::strin } } - archfpga_throw(__FILE__, __LINE__, - "Could not find model port: %s (%s)\n", port.c_str(), model.c_str()); + if (fail) + archfpga_throw(__FILE__, __LINE__, + "Could not find model port: %s (%s)\n", port.c_str(), model.c_str()); + + return nullptr; } static t_model* get_model(t_arch* arch, std::string model) { @@ -158,6 +174,7 @@ static t_port get_generic_port(t_arch* arch, port.equivalent = PortEquivalence::NONE; port.type = dir; port.is_clock = false; + port.is_non_clock_global = false; port.model_port = nullptr; port.port_class = vtr::strdup(nullptr); port.port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); @@ -184,16 +201,9 @@ struct ArchReader { , ltypes_(logical_types) { set_arch_file_name(arch_file); - for (auto cell_bel : ar_.getCellBelMap()) { - auto name = str(cell_bel.getCell()); - for (auto site_bels : cell_bel.getCommonPins()[0].getSiteTypes()) { - auto site_type = str(site_bels.getSiteType()); - for (auto bel : site_bels.getBels()) { - auto bel_name = str(bel); - std::pair key(site_type, bel_name); - bel_cell_mapping_[key].push_back(name); - } - } + for (std::string str : ar_.getStrList()) { + auto interned_string = arch_->strings.intern_string(vtr::string_view(str.c_str())); + arch_->interned_strings.push_back(interned_string); } } @@ -201,6 +211,8 @@ struct ArchReader { // Preprocess arch information process_luts(); process_package_pins(); + process_cell_bel_mappings(); + process_constants(); process_models(); process_device(); @@ -226,12 +238,17 @@ struct ArchReader { t_default_fc_spec default_fc_; - // siteTypeName, belName , list of cell names - std::map, std::vector> bel_cell_mapping_; + // Package pins + + // TODO: add possibility to have multiple packages + std::vector pad_bels_; + + // Bel Cell mappings + std::unordered_map> bel_cell_mappings_; // Utils std::string str(int idx) { - return std::string(ar_.getStrList()[idx].cStr()); + return arch_->interned_strings[idx].get(&arch_->strings); } int get_bel_type_count(Device::SiteType::Reader& site, Device::BELCategory category) { @@ -255,6 +272,26 @@ struct ArchReader { return bel.getCategory() == Device::BELCategory::SITE_PORT ? str(site.getName()) : str(bel.getName()); } + bool is_lut(std::string name) { + for (auto cell : arch_->lut_cells) + if (cell.name == name) + return true; + + for (auto bel : arch_->lut_bels) + if (bel.name == name) + return true; + + return false; + } + + bool is_pad(std::string name) { + for (auto pad : pad_bels_) + if (pad.bel_name == name) + return true; + + return false; + } + std::unordered_map> get_ics(Device::SiteType::Reader& site) { // dictionary: // - key: interconnect name @@ -278,6 +315,7 @@ struct ArchReader { is_mux = bel.getCategory() == Device::BELCategory::ROUTING; } } + VTR_ASSERT(!std::get<1>(out_pin).empty()); // Stores all output BELs connected to the same out_pin @@ -294,7 +332,7 @@ struct ArchReader { auto bel = get_bel_reader(site, str(bel_pin.getBel())); auto out_bel_name = get_ic_prefix(site, bel); - for (auto pad_bel : arch_->pad_bels) { + for (auto pad_bel : pad_bels_) { is_pad = pad_bel.bel_name == out_bel_name || is_pad; pad_bel_name = pad_bel.bel_name == out_bel_name ? out_bel_name : pad_bel_name; pad_bel_pin_name = pad_bel.bel_name == out_bel_name ? out_bel_pin_name : pad_bel_pin_name; @@ -352,18 +390,17 @@ struct ArchReader { auto res = ics.emplace(ic_name, std::make_tuple(istr, ostr, DIRECT_INTERC, is_pad)); if (!res.second) { - std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; - if (inputs.empty()) - inputs = istr; - else - inputs += " " + istr; + std::string ins, outs; + std::tie(ins, outs, ic_type, std::ignore) = res.first->second; + + VTR_ASSERT(ins == istr); - if (outputs.empty()) - outputs = ostr; + if (outs.empty()) + outs = ostr; else - outputs += " " + ostr; + outs += " " + ostr; - res.first->second = std::make_tuple(inputs, outputs, ic_type, is_pad); + res.first->second = std::make_tuple(ins, outs, ic_type, is_pad); } } } @@ -372,6 +409,119 @@ struct ArchReader { return ics; } + /** + * Preprocessors: + * - process_luts: processes information on which cells and bels are LUTs + * - process_package_pins: processes information on the device's pinout and which sites and bels + * contain IO pads + * - process_cell_bel_mapping: processes mappings between a cell and the possible BELs location for that cell + */ + + 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); + } + + for (auto lut_elem : lut_def.getLutElements()) { + for (auto lut : lut_elem.getLuts()) { + for (auto bel : lut.getBels()) { + t_lut_bel lut_bel; + + std::string name = bel.getName().cStr(); + lut_bel.name = name; + + // Check for duplicates + auto is_duplicate = [name](t_lut_bel l) { return l.name == name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), is_duplicate); + if (res != arch_->lut_bels.end()) + continue; + + std::vector ipins; + for (auto pin : bel.getInputPins()) + ipins.push_back(pin.cStr()); + + lut_bel.input_pins = ipins; + lut_bel.output_pin = bel.getOutputPin().cStr(); + + arch_->lut_bels.push_back(lut_bel); + } + } + } + } + + void process_package_pins() { + for (auto package : ar_.getPackages()) { + for (auto pin : package.getPackagePins()) { + t_package_pin pckg_pin; + pckg_pin.name = str(pin.getPackagePin()); + + if (pin.getBel().isBel()) + pckg_pin.bel_name = str(pin.getBel().getBel()); + + if (pin.getSite().isSite()) + pckg_pin.site_name = str(pin.getSite().getSite()); + + pad_bels_.push_back(pckg_pin); + } + } + } + + void process_cell_bel_mappings() { + for (auto cell_mapping : ar_.getCellBelMap()) { + int cell_name = cell_mapping.getCell(); + + for (auto common_pins : cell_mapping.getCommonPins()) { + std::vector> pins; + + for (auto pin_map : common_pins.getPins()) + pins.emplace_back(pin_map.getCellPin(), pin_map.getBelPin()); + + for (auto site_type_entry : common_pins.getSiteTypes()) { + for (auto bel : site_type_entry.getBels()) { + t_bel_cell_mapping mapping; + + mapping.cell = cell_name; + mapping.pins = pins; + + std::vector maps{mapping}; + + auto res = bel_cell_mappings_.emplace(bel, maps); + if (!res.second) + res.first->second.push_back(mapping); + } + } + } + } + } + + void process_constants() { + auto consts = ar_.getConstants(); + + arch_->gnd_cell = str(consts.getGndCellType()); + arch_->vcc_cell = str(consts.getVccCellType()); + + if (consts.getGndNetName().isName()) + arch_->gnd_net = str(consts.getGndNetName().getName()); + + if (consts.getVccNetName().isName()) + arch_->vcc_net = str(consts.getVccNetName().getName()); + } + + /* end preprocessors */ + // Model processing void process_models() { // Populate the common library, namely .inputs, .outputs, .names, .latches @@ -389,11 +539,7 @@ struct ArchReader { if (str(primitive.getLib()) == std::string("primitives")) { std::string prim_name = str(primitive.getName()); - bool is_lut = false; - for (auto lut_cell : arch_->lut_cells) - is_lut = lut_cell.name == prim_name || is_lut; - - if (is_lut) + if (is_lut(prim_name)) continue; try { @@ -519,7 +665,7 @@ struct ArchReader { pb_type->name = vtr::strdup(name.c_str()); pb_type->num_pb = 1; - process_block_ports(pb_type, name, site); + process_block_ports(pb_type, site); // Process modes (for simplicity, only the default mode is allowed for the time being) pb_type->num_modes = 1; @@ -544,41 +690,23 @@ struct ArchReader { auto bel_name = str(bel.getName()); std::pair key(name, bel_name); - auto cell_name = bel_name; - - if (bel_cell_mapping_.find(key) != bel_cell_mapping_.end()) { - VTR_ASSERT(bel_cell_mapping_[key].size() == 1); - cell_name = bel_cell_mapping_[key][0]; - } - - auto leaf_pb_type = new t_pb_type; - leaf_pb_type->name = vtr::strdup(bel_name.c_str()); - leaf_pb_type->num_pb = 1; - leaf_pb_type->parent_mode = mode; - - // TODO: fix this to make it dynamic. This will need the usage of CellBelMapping + auto mid_pb_type = new t_pb_type; + mid_pb_type->name = vtr::strdup(bel_name.c_str()); + mid_pb_type->num_pb = 1; + mid_pb_type->parent_mode = mode; + mid_pb_type->blif_model = nullptr; - auto find_lut = [cell_name](t_lut_cell l) { return l.name == cell_name; }; - bool is_lut = std::find_if(arch_->lut_cells.begin(), arch_->lut_cells.end(), find_lut) != arch_->lut_cells.end(); + if (!is_pad(bel_name)) + process_block_ports(mid_pb_type, site, false); - auto find_pad = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; - bool is_pad = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), find_pad) != arch_->pad_bels.end(); + if (is_lut(bel_name)) + process_lut_block(mid_pb_type); + else if (is_pad(bel_name)) + process_pad_block(mid_pb_type, bel, site); + else + process_generic_block(mid_pb_type, bel); - if (!is_pad) - process_block_ports(leaf_pb_type, cell_name, site, false, is_lut); - - if (is_lut) { - leaf_pb_type->blif_model = nullptr; - process_lut_block(leaf_pb_type); - } else if (is_pad) { - leaf_pb_type->blif_model = nullptr; - process_pad_block(leaf_pb_type, bel, site); - } else { - leaf_pb_type->blif_model = vtr::strdup((std::string(".subckt ") + cell_name).c_str()); - leaf_pb_type->model = get_model(arch_, cell_name); - } - - mode->pb_type_children[count++] = *leaf_pb_type; + mode->pb_type_children[count++] = *mid_pb_type; } process_interconnects(mode, site); @@ -781,15 +909,103 @@ struct ArchReader { imode->interconnect[0] = *i_ic; } - void process_block_ports(t_pb_type* pb_type, std::string cell_name, Device::SiteType::Reader& site, bool is_root = true, bool is_model_library = false) { - std::unordered_set names; + void process_generic_block(t_pb_type* pb_type, Device::BEL::Reader& bel) { + std::string pb_name = std::string(pb_type->name); + + auto maps = bel_cell_mappings_[bel.getName()]; + int num_modes = maps.size(); + + pb_type->num_modes = num_modes; + pb_type->modes = new t_mode[num_modes]; + + int count = 0; + for (auto map : maps) { + int idx = count++; + auto mode = &pb_type->modes[idx]; + auto name = str(map.cell); + mode->name = vtr::strdup(name.c_str()); + mode->parent_pb_type = pb_type; + mode->index = idx; + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[1]; + + auto leaf = &mode->pb_type_children[0]; + std::string leaf_name = name == std::string(pb_type->name) ? name + std::string("_leaf") : name; + leaf->name = vtr::strdup(leaf_name.c_str()); + leaf->num_pb = 1; + leaf->parent_mode = mode; + + int num_ports = map.pins.size(); + leaf->num_ports = num_ports; + leaf->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + leaf->blif_model = vtr::strdup((std::string(".subckt ") + name).c_str()); + leaf->model = get_model(arch_, name); + + mode->num_interconnect = num_ports; + mode->interconnect = new t_interconnect[num_ports]; + std::unordered_map> pins; + int ic_count = 0; + for (auto pin_map : map.pins) { + auto cell_pin = str(pin_map.first); + auto bel_pin = str(pin_map.second); + + if (cell_pin == arch_->vcc_cell || cell_pin == arch_->gnd_cell) + continue; + + std::smatch regex_matches; + std::string pin_suffix; + const std::regex port_regex("([0-9A-Za-z-]+)\\[([0-9]+)\\]"); + if (std::regex_match(cell_pin, regex_matches, port_regex)) { + cell_pin = regex_matches[1].str(); + pin_suffix = std::string("[") + regex_matches[2].str() + std::string("]"); + } + + auto model_port = get_model_port(arch_, name, cell_pin, false); + + if (model_port == nullptr) + continue; + + auto size = model_port->size; + auto dir = model_port->dir; + + pins.emplace(cell_pin, std::make_pair(dir, size)); + + std::string istr, ostr, ic_name; + switch (dir) { + case IN_PORT: + istr = pb_name + std::string(".") + bel_pin; + ostr = leaf_name + std::string(".") + cell_pin + pin_suffix; + break; + case OUT_PORT: + istr = leaf_name + std::string(".") + cell_pin + pin_suffix; + ostr = pb_name + std::string(".") + bel_pin; + break; + default: + VTR_ASSERT(0); + } + ic_name = istr + std::string("_") + ostr; + + auto ic = &mode->interconnect[ic_count++]; + ic->name = vtr::strdup(ic_name.c_str()); + ic->type = DIRECT_INTERC; + ic->parent_mode_index = idx; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(istr.c_str()); + ic->output_string = vtr::strdup(ostr.c_str()); + } + + create_ports(leaf, pins, name); + } + } + + void process_block_ports(t_pb_type* pb_type, Device::SiteType::Reader& site, bool is_root = true) { // Prepare data based on pb_type level - std::unordered_map pins; + std::unordered_map> pins; if (is_root) { for (auto pin : site.getPins()) { auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; - pins.emplace(str(pin.getName()), dir); + pins.emplace(str(pin.getName()), std::make_pair(dir, 1)); } } else { for (auto bel : site.getBels()) { @@ -802,11 +1018,17 @@ struct ArchReader { for (auto bel_pin : bel.getPins()) { auto pin = site.getBelPins()[bel_pin]; auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; - pins.emplace(str(pin.getName()), dir); + pins.emplace(str(pin.getName()), std::make_pair(dir, 1)); } } } + create_ports(pb_type, pins); + } + + void create_ports(t_pb_type* pb_type, std::unordered_map>& pins, std::string model = "") { + std::unordered_set names; + auto num_ports = pins.size(); auto ports = new t_port[num_ports]; pb_type->ports = ports; @@ -820,7 +1042,9 @@ struct ArchReader { int pins_dir_count = 0; for (auto pin_pair : pins) { auto pin_name = pin_pair.first; - auto pin_dir = pin_pair.second; + PORTS pin_dir; + int num_pins; + std::tie(pin_dir, num_pins) = pin_pair.second; if (pin_dir != dir) continue; @@ -831,14 +1055,14 @@ struct ArchReader { pb_type->num_input_pins += is_input ? 1 : 0; pb_type->num_output_pins += is_input ? 0 : 1; - auto port = get_generic_port(arch_, pb_type, dir, pin_name); + auto port = get_generic_port(arch_, pb_type, dir, pin_name, /*string_model=*/"", num_pins); ports[pin_count] = port; port.index = pin_count++; port.port_index_by_type = pins_dir_count++; port.absolute_first_pin_index = pin_abs++; - if (!is_root && !is_model_library) - port.model_port = get_model_port(arch_, cell_name, pin_name); + if (!model.empty()) + port.model_port = get_model_port(arch_, model, pin_name); } } } @@ -925,20 +1149,15 @@ struct ArchReader { setup_pin_classes(&ptype); - bool is_pad = false; + bool is_io = false; for (auto site : tile.getSiteTypes()) { auto site_type = ar_.getSiteTypeList()[site.getPrimaryType()]; - for (auto bel : site_type.getBels()) { - auto bel_name = str(bel.getName()); - auto is_pad_func = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; - auto res = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), is_pad_func); - - is_pad = res != arch_->pad_bels.end() || is_pad; - } + for (auto bel : site_type.getBels()) + is_io = is_pad(str(bel.getName())) || is_io; } - ptype.is_input_type = ptype.is_output_type = is_pad; + ptype.is_input_type = ptype.is_output_type = is_io; ptypes_.push_back(ptype); } @@ -1058,68 +1277,6 @@ 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); - } - - for (auto lut_elem : lut_def.getLutElements()) { - for (auto lut : lut_elem.getLuts()) { - for (auto bel : lut.getBels()) { - t_lut_bel lut_bel; - - std::string name = bel.getName().cStr(); - lut_bel.name = name; - - // Check for duplicates - auto is_duplicate = [name](t_lut_bel l) { return l.name == name; }; - auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), is_duplicate); - if (res != arch_->lut_bels.end()) - continue; - - std::vector ipins; - for (auto pin : bel.getInputPins()) - ipins.push_back(pin.cStr()); - - lut_bel.input_pins = ipins; - lut_bel.output_pin = bel.getOutputPin().cStr(); - - arch_->lut_bels.push_back(lut_bel); - } - } - } - } - - void process_package_pins() { - for (auto package : ar_.getPackages()) { - for (auto pin : package.getPackagePins()) { - t_package_pin pckg_pin; - pckg_pin.name = str(pin.getPackagePin()); - - if (pin.getBel().isBel()) - pckg_pin.bel_name = str(pin.getBel().getBel()); - - if (pin.getSite().isSite()) - pckg_pin.site_name = str(pin.getSite().getSite()); - - arch_->pad_bels.push_back(pckg_pin); - } - } - } - // Layout Processing void process_layout() { auto tileList = ar_.getTileList(); @@ -1153,8 +1310,6 @@ struct ArchReader { size_t pos = tile_prefix.find(tile_type); if (pos != std::string::npos && pos == 0) tile_prefix.erase(pos, tile_type.length() + 1); - data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), - arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); t_grid_loc_def single(tile_type, 1); single.x.start_expr = std::to_string(tile.getCol()); single.y.start_expr = std::to_string(tile.getRow()); diff --git a/vpr/test/lut.netlist b/vpr/test/lut.netlist index d8a49d6feab4741fad8157eb20ec2eba7c7a4cc1..7f2833b66d134875089fce39603bcd60cad55af8 100644 GIT binary patch literal 1683 zcmV;E25k8siwFo``;=h-|7>-1E^cLXY-w|J0M(dJY!p=-$KP)M(3TbjL7`Il<05}_ z|IEy8yIZLWWr5WeDi%ycve|B@Wx}>w_Ycqn5=}T@B6l=2Q3Hk;jft@bJ$aBL(SzJ1 z(UT_7gOkks_WOD7ec#Od-kW)|j4}2{8^hMc2KaG1-^cu< zeQcncu}}E#U%1Nq7+cHZuYCWli!nbhzrxw;d|%}33%-}~3%1i{M&*7+*6sFhu|?sV z;Bmd(qbOg6^1~<}=P|&4^(p12Q9j3GFaPz3Ql3NkTkv0}l%GfWyMA$w8;M)}b zC-`DgyA&?x>mG&6dHS5f-vkdSd;vTHF30U9 za9!b7!J`WQ44kfiOXb?c-B`KnD;qI6cb)ZA-tJQ0UxUkMTSt}hMzzdiy?XS!%VYn} z`}G(T&o_0Wc}{a$&fi9_gXVkRfACr3(!XTt{qID*F6A1W{U3!UA9`*NRJnfIo~pv= zer>73|Bt>yOx&k>zE8Wfua`%!{yQ5yzugVCf40H)!3Nuh8*HyN*xqQceXI%_V`6laW?=g|@IQZY--9FLZF!*M?=Yl>_e=m3(JO%y&_$0Uq{u1~C_&ebH!R0z)3H$)~ zP4KV54}yOOeh2(8_|M?KgTD;^4|tzn@K?Y$;=Q#Sd<QXkA6PIPkuY7eurxKbvW9$DSmQYp!%h1_{rAo4qk8kdGTv= z@OtCVi=W@Y>y1Azem)1UH~zf%IeESDceI9|JU8jx=kRkIe_s6LIXeB^#-A5IxjFr+ zt&3&5HI8~K-+}i(N2|5#bkyr$a$K#n6Uy&7$~E@=oA>Xpai915c)Xuap;TC6v^)f^bM5XhQ1B`SGzbriZp%(I)wHT zseTgL8EIUl5Q~MZJx;^ZnNlWf&6mtV_H<@`)(p=q%w~cP5G?YR7KJ`= z@Swd^lj~EtZjn2^u1 zqwqN~DtbHt9>`2(193f&DVOqrshl;lkSP?+P&PXp(_+K6ND~gkjnJ$)Kb1S9RbqNF zIeK_JHIY7+oES@uCez$+EHE%BRmm<}I6_o6FCKbJkQb zTrlS{3wGjo(hMV{4+i;hNPHFX`;&aQ>y3K4UhjS}Pku8q=}42uuMr9HI}r)R=o>DO zPM^(Y?KnHnb7M_4y+17$iU#??prmqtt=ayAME*k+f1}y{aq^E>{G-kG zPmq5?_~)CSUtQz={2=|ConKuif4$=0?ELBm`5VHY&$n|{!EEaNH~4(x2aN`f6=}Y| zM*bT7wO06RG3r3)ciz%l;jc%@pAujht?)Mt@{jV7Z$(?-AJuLD1RtL*$gfWM)kS{u zEy%BK=U2D$n{PpWbvwUe0$V2|r0t|7>-1E^cLXY-w|J0JWG)Y#T)!#>bA6IB5zbq&!LjZE@*? z`mt*#aYBH)G%c2zCP=MBDUosPO}Zj}$lf&Kgpi1Y5C=H)f>a?86#~%c4rdC7<;daA%)o}zaQb(p8?VV zHW+5?T#&JExjM$#Adlbh>uxV&L7r~}1i!-B`}}e$Ro7{lQGSq-b;x}d>1X)<5)fs= z$nQn|AISfU#{vG-ujR{r{f+wjwfwut-{SEU`l05J1V#SgpvW6`mFyj3C-`HL$KQCz zzX(vCNDIBf=Sr{ek~J=azX2|Xauz%b9s-{M&wwY;em4{SnnwRmqwOA7?dE+<#Mi-P z+6OhS@d3mO8h;e=n#LbPENXlZ@g0p1A-=8g$D!VAz#I7tK*P5235`#K zzo_wfaB6<1uVOTIMsX(*lkbFDPUj(y^1(fFUCZyn!^-Ku=MlwUP4xcspe}t=`h51I zRZZU~`OY;fC;LL@GXFRSk)^PucvE69^ zUG8Cwsb6zdcQLs?3oZ8x-9OR5KMenFrM~o(y4%CgY~rW(Pkzg#?(y(T`Rt$W*X!Xo z?$b{nzp#g2x`|((QlC=4qU9M>_nY$R7u5XvJ^ZG9`gLi3av#Z}FH#xhy!vgPI=F%AM58c}3lCkzWh^`kZTv$9{gT^6M+! z@0&b+%=xW=_>SE|`5zH~N4pe%;N`cVx1dLZLQ^C?13d#>f-XVdfW85}0lfiz5BeVT z{w^_&6iJUkQ~ZhR1?UCnb?9~I+t9b6H=#G-cOUvb@{fjurbv1=B>F><=6My`Po(lU zq18z9U#}J1brdh=oqXIbJ65f5Eni-?;!D-#d{hBZ$ErK^xVSG_d1tj|)#EFx4qZ9I zd0sMheQGjhM9p-}aE}LTmD;-$<^QM3ma}5l9cE+wRuHQ-yJS1|b*nD)`HSN1m*<@< z4pv#{dBh8sFA07z>po;~D9Pa9nZcnVg9GOr>dnC~l3y%D(uqiZ)u}`lSL~%~zE-zl zg~C)aZA`f$M>vuehNVhzb;Zh+D^9Lx)p&o4!Y9!@$z$+d$GK1`l`7@$ z>JBs?WW$J==_tQX8oSyz3}gFf7Rlw7%f-F?PIHgrA_mr8%q7~qj*L_+73H_2A^eSY z`;U?T*tWmfZvS!eAK&&*w%dP#{3o{kQ|e-q=M$gSBmtF7a2#K!3jH)!G5MkoA@F>1i%BV(JL@HbQBPXlBpJK>*9l7EV? zOgq&H|CH(aPxAHCf%!FQeoZmIl@83W>CUg|&Tpjy^J}{ED_#KmYGR?y@7E;R^PBrG vA^WPaAfkzwL`=D{(uwv{3EqCn5dH?*5$%h8HOG%LVxRpV0z-W>&>H{%G7KM= diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 53d9a5edacd..2d6bd4ae64f 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -18,7 +18,7 @@ TEST_CASE("read_interchange_models", "[vpr]") { FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); - std::unordered_set models = {"IB", "OB", "DFF", "GND", "VCC"}; + std::unordered_set models = {"IB", "OB", "DFFR", "DFFS", "GND", "VCC"}; // Check that there are exactly the expected models for (auto* model = arch.models; model != nullptr; model = model->next) { @@ -74,39 +74,26 @@ TEST_CASE("read_interchange_luts", "[vpr]") { FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); - std::unordered_set lut_cell_pins = {"A0", "A1", "A2", "A3"}; - std::unordered_set lut_bel_pins = {"I0", "I1", "I2", "I3"}; + std::unordered_set lut_cells = {"LUT1", "LUT2", "LUT3", "LUT4"}; + std::unordered_set lut_bels = {"ALUT", "BLUT"}; + std::unordered_set lut_cell_pins = {"I0", "I1", "I2", "I3"}; + std::unordered_set lut_bel_pins = {"A1", "A2", "A3", "A4"}; - REQUIRE(arch.lut_cells.size() == 1); - REQUIRE(arch.lut_bels.size() == 1); + REQUIRE(arch.lut_cells.size() == 4); + REQUIRE(arch.lut_bels.size() == 2); - auto lut_cell = arch.lut_cells[0]; - REQUIRE(lut_cell.name == std::string("LUT")); - REQUIRE(lut_cell.init_param == std::string("INIT")); - for (auto lut_pin : lut_cell_pins) - CHECK(std::find(lut_cell.inputs.begin(), lut_cell.inputs.end(), lut_pin) != lut_cell.inputs.end()); - - auto lut_bel = arch.lut_bels[0]; - REQUIRE(lut_bel.name == std::string("LUT")); - REQUIRE(lut_bel.output_pin == std::string("O")); - for (auto lut_pin : lut_bel_pins) - CHECK(std::find(lut_bel.input_pins.begin(), lut_bel.input_pins.end(), lut_pin) != lut_bel.input_pins.end()); -} - -TEST_CASE("read_interchange_pin_packages", "[vpr]") { - t_arch arch; - std::vector physical_tile_types; - std::vector logical_block_types; - - FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); - - // The device architecture file contains 35 perimetral PADs - REQUIRE(arch.pad_bels.size() == 35); + for (auto lut_cell : arch.lut_cells) { + CHECK(std::find(lut_cells.begin(), lut_cells.end(), lut_cell.name) != lut_cells.end()); + REQUIRE(lut_cell.init_param == std::string("INIT")); + for (auto lut_pin : lut_cell.inputs) + CHECK(std::find(lut_cell_pins.begin(), lut_cell_pins.end(), lut_pin) != lut_cell_pins.end()); + } - int ipad = 0; - for (auto pad_bel : arch.pad_bels) { - REQUIRE(pad_bel.name == std::string("A") + std::to_string(ipad++)); - REQUIRE(pad_bel.bel_name == std::string("PAD")); + for (auto lut_bel : arch.lut_bels) { + CHECK(std::find(lut_bels.begin(), lut_bels.end(), lut_bel.name) != lut_bels.end()); + REQUIRE(lut_bel.output_pin == std::string("O")); + for (auto lut_pin : lut_bel.input_pins) + CHECK(std::find(lut_bel_pins.begin(), lut_bel_pins.end(), lut_pin) != lut_bel_pins.end()); } } @@ -144,15 +131,23 @@ TEST_CASE("read_interchange_pb_types", "[vpr]") { std::unordered_set ltypes = {"NULL", "IOPAD", "SLICE", "POWER"}; std::unordered_map slice_ports = { - {"L0", PORTS::IN_PORT}, - {"L1", PORTS::IN_PORT}, - {"L2", PORTS::IN_PORT}, - {"L3", PORTS::IN_PORT}, - {"R", PORTS::IN_PORT}, - {"C", PORTS::IN_PORT}, - {"D", PORTS::IN_PORT}, - {"O", PORTS::OUT_PORT}, - {"Q", PORTS::OUT_PORT}}; + {"L0_0", PORTS::IN_PORT}, + {"L1_0", PORTS::IN_PORT}, + {"L2_0", PORTS::IN_PORT}, + {"L3_0", PORTS::IN_PORT}, + {"R_0", PORTS::IN_PORT}, + {"D_0", PORTS::IN_PORT}, + {"O_0", PORTS::OUT_PORT}, + {"Q_0", PORTS::OUT_PORT}, + {"L0_1", PORTS::IN_PORT}, + {"L1_1", PORTS::IN_PORT}, + {"L2_1", PORTS::IN_PORT}, + {"L3_1", PORTS::IN_PORT}, + {"R_1", PORTS::IN_PORT}, + {"D_1", PORTS::IN_PORT}, + {"O_1", PORTS::OUT_PORT}, + {"Q_1", PORTS::OUT_PORT}, + {"CLK", PORTS::IN_PORT}}; // Check that there are exactly the expected models for (auto ltype : logical_block_types) { diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device index da02924ee50225e4549f816dd3bdf5b071e66348..59cb916bfb082ad5d1942ff3a00e686c360b343b 100644 GIT binary patch literal 100983 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

eCx)|bLoxh3Lf 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