diff --git a/libs/EXTERNAL/libsdcparse/src/sdc_common.cpp b/libs/EXTERNAL/libsdcparse/src/sdc_common.cpp index 4080d4bd81c..3cf8e1ac11b 100644 --- a/libs/EXTERNAL/libsdcparse/src/sdc_common.cpp +++ b/libs/EXTERNAL/libsdcparse/src/sdc_common.cpp @@ -96,6 +96,31 @@ void add_sdc_create_clock(Callback& callback, const Lexer& lexer, CreateClock& s */ callback.create_clock(sdc_create_clock); + //Prepare and create inverted version of the netlist clock. + //Use netlist clock from parsed SDC file (create_clock command) + //as a base for the inverted clock. It is created as virtual + //so that it won't interfere with existing netlist clock. + sdc_create_clock.is_virtual = true; + //Mark clock as inverted + sdc_create_clock.inverted = true; + + //Virtual clocks must have empty targets, overwrite those + //but first save the names of the targets + auto targets = sdc_create_clock.targets.strings; + sdc_create_clock.targets = StringGroup(); + + //180 degrees phase shift is done with inverting + //the rise- and fall-edge times of the original netlist clock + double rise_edge = sdc_create_clock.rise_edge; + sdc_create_clock.rise_edge = sdc_create_clock.fall_edge; + sdc_create_clock.fall_edge = rise_edge; + + //Create new inverted virtual clock for each target from original netlist clock. + for (auto &str : targets) { + //Use saved names of the original netlist clock targets as inverted clock names + sdc_create_clock.name = str + "_negedge"; + callback.create_clock(sdc_create_clock); + } } /* diff --git a/libs/EXTERNAL/libsdcparse/src/sdcparse.hpp b/libs/EXTERNAL/libsdcparse/src/sdcparse.hpp index c9333241cd5..a7cb27f46de 100644 --- a/libs/EXTERNAL/libsdcparse/src/sdcparse.hpp +++ b/libs/EXTERNAL/libsdcparse/src/sdcparse.hpp @@ -208,6 +208,7 @@ struct CreateClock { StringGroup targets; //The set of strings indicating clock sources. // May be explicit strings or regexs. bool is_virtual = false; //Identifies this as a virtual (non-netlist) clock + bool inverted = false; //Identifies this as inverted version of netlist clock }; struct SetIoDelay { diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.cpp index 510fadd9e51..159de5d18da 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.cpp @@ -20,6 +20,10 @@ std::string TimingConstraints::clock_domain_name(const DomainId id) const { return domain_names_[id]; } +bool TimingConstraints::clock_domain_inverted(const DomainId id) const { + return domain_inverted_[id]; +} + NodeId TimingConstraints::clock_domain_source_node(const DomainId id) const { return domain_sources_[id]; } @@ -292,7 +296,8 @@ DomainId TimingConstraints::create_clock_domain(const std::string name) { //Create it id = DomainId(domain_ids_.size()); domain_ids_.push_back(id); - + + domain_inverted_.push_back(false); domain_names_.push_back(name); domain_sources_.emplace_back(NodeId::INVALID()); @@ -303,6 +308,24 @@ DomainId TimingConstraints::create_clock_domain(const std::string name) { return id; } +DomainId TimingConstraints::create_clock_domain(const std::string name, bool inverted) { + DomainId id = find_clock_domain(name); + if(!id) { + //Create it + id = DomainId(domain_ids_.size()); + domain_ids_.push_back(id); + + domain_inverted_.push_back(inverted); + domain_names_.push_back(name); + domain_sources_.emplace_back(NodeId::INVALID()); + + TATUM_ASSERT(clock_domain_name(id) == name); + TATUM_ASSERT(find_clock_domain(name) == id); + } + + return id; +} + void TimingConstraints::set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const Time constraint) { set_setup_constraint(src_domain, sink_domain, NodeId::INVALID(), constraint); } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.hpp index 07288ed08ba..4db9d2f68bd 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingConstraints.hpp @@ -42,10 +42,15 @@ class TimingConstraints { ///\returns The name of a clock domain std::string clock_domain_name(const DomainId id) const; + ///Checks whether the clock domain was marked as inverted. Inverted clocks are virtual clocks created from netlist clocks with 180 degree phase shift applied + ///\param id The ID of the clock domain + ///\returns whether the clock domain with specified domain id was marked as inverted + bool clock_domain_inverted(const DomainId id) const; + ///\returns The source NodeId of the specified domain NodeId clock_domain_source_node(const DomainId id) const; - //\returns whether the specified domain id corresponds to a virtual lcock + //\returns whether the specified domain id corresponds to a virtual clock bool is_virtual_clock(const DomainId id) const; ///\returns The domain of the specified node id if it is a clock source @@ -123,6 +128,7 @@ class TimingConstraints { public: //Mutators ///\returns The DomainId of the clock with the specified name (will be created if it doesn not exist) DomainId create_clock_domain(const std::string name); + DomainId create_clock_domain(const std::string name, bool inverted); ///Sets the setup constraint between src_domain and sink_domain with value constraint void set_setup_constraint(const DomainId src_domain, const DomainId sink_domain, const Time constraint); @@ -170,6 +176,7 @@ class TimingConstraints { private: //Data tatum::util::linear_map domain_ids_; tatum::util::linear_map domain_names_; + tatum::util::linear_map domain_inverted_; tatum::util::linear_map domain_sources_; std::unordered_set constant_generators_; diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.cpp index 33392a6e8d1..435f41a2d77 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.cpp @@ -198,6 +198,32 @@ NodeId TimingGraph::add_node(const NodeType type) { return node_id; } +NodeId TimingGraph::add_node(const NodeType type, TriggeringEdge trigg_edge) { + //Invalidate the levelization + is_levelized_ = false; + + //Reserve an ID + NodeId node_id = NodeId(node_ids_.size()); + node_ids_.push_back(node_id); + + //Type + node_types_.push_back(type); + + //Triggering Edge + trigg_edges_.push_back(trigg_edge); + + //Edges + node_out_edges_.push_back(std::vector()); + node_in_edges_.push_back(std::vector()); + + //Verify sizes + TATUM_ASSERT(node_types_.size() == node_out_edges_.size()); + TATUM_ASSERT(node_types_.size() == node_in_edges_.size()); + + //Return the ID of the added node + return node_id; +} + EdgeId TimingGraph::add_edge(const EdgeType type, const NodeId src_node, const NodeId sink_node) { //We require that the source/sink node must already be in the graph, // so we can update them with thier edge references @@ -556,6 +582,7 @@ void TimingGraph::remap_nodes(const tatum::util::linear_map& node node_types_ = clean_and_reorder_values(node_types_, node_id_map); node_in_edges_ = clean_and_reorder_values(node_in_edges_, node_id_map); node_out_edges_ = clean_and_reorder_values(node_out_edges_, node_id_map); + trigg_edges_ = clean_and_reorder_values(trigg_edges_, node_id_map); //Update references edge_src_nodes_ = update_all_refs(edge_src_nodes_, node_id_map); @@ -597,7 +624,8 @@ bool TimingGraph::validate_sizes() const { if ( node_ids_.size() != node_types_.size() || node_ids_.size() != node_in_edges_.size() || node_ids_.size() != node_out_edges_.size() - || node_ids_.size() != node_levels_.size()) { + || node_ids_.size() != node_levels_.size() + || node_ids_.size() != trigg_edges_.size()) { throw tatum::Error("Inconsistent node attribute sizes"); } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.hpp index 72a05cad9da..38887be28bb 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingGraph.hpp @@ -63,6 +63,13 @@ namespace tatum { +enum TriggeringEdge { + RISING_EDGE, + FALLING_EDGE, + DONT_CARE, + ERROR_EDGE +}; + class TimingGraph { public: //Public types //Iterators @@ -84,6 +91,29 @@ class TimingGraph { ///\returns The type of the node NodeType node_type(const NodeId id) const { return node_types_[id]; } + ///\param id The TimingGraph node ID + ///\returns TriggeringEdge enum describing the triggering edge of the TimingGraph node + TriggeringEdge trigg_edge(const NodeId id) const { return trigg_edges_[id]; } + + ///\param id The TimingGraph node ID + ///\returns String describing the triggering edge of the TimingGraph node + std::string trigg_edge_str(const NodeId id) const { + switch(trigg_edges_[id]) { + case TriggeringEdge::RISING_EDGE: + return "RISING"; + break; + case TriggeringEdge::FALLING_EDGE: + return "FALLING"; + break; + case TriggeringEdge::DONT_CARE: + return "DONT_CARE"; + break; + default: + return "ERROR"; + break; + } + } + ///\param id The node id ///\returns A range of all out-going edges the node drives edge_range node_out_edges(const NodeId id) const { return tatum::util::make_range(node_out_edges_[id].begin(), node_out_edges_[id].end()); } @@ -198,6 +228,7 @@ class TimingGraph { ///\param type The type of the node to be added ///\warning Graph will likely need to be re-levelized after modification NodeId add_node(const NodeType type); + NodeId add_node(const NodeType type, TriggeringEdge trigg_edge); ///Adds an edge to the timing graph ///\param type The edge's type @@ -282,6 +313,7 @@ class TimingGraph { //Node data tatum::util::linear_map node_ids_; //The node IDs in the graph tatum::util::linear_map node_types_; //Type of node + tatum::util::linear_map trigg_edges_; //Triggering edge of the clock tatum::util::linear_map> node_in_edges_; //Incomiing edge IDs for node tatum::util::linear_map> node_out_edges_; //Out going edge IDs for node tatum::util::linear_map node_levels_; //Out going edge IDs for node @@ -334,7 +366,5 @@ struct GraphIdMaps { tatum::util::linear_map edge_id_map; }; - - } //namepsace diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/graph_visitors/CommonAnalysisVisitor.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/graph_visitors/CommonAnalysisVisitor.hpp index 6b901b21def..dbf3b6ebc5f 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/graph_visitors/CommonAnalysisVisitor.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/graph_visitors/CommonAnalysisVisitor.hpp @@ -294,6 +294,7 @@ bool CommonAnalysisVisitor::do_arrival_traverse_edge(const TimingGr //Pulling values from upstream source node NodeId src_node_id = tg.edge_src_node(edge_id); + NodeId sink_node_id = tg.edge_sink_node(edge_id); if(should_propagate_clocks(tg, tc, edge_id)) { /* @@ -311,7 +312,6 @@ bool CommonAnalysisVisitor::do_arrival_traverse_edge(const TimingGr for(const TimingTag& src_launch_clk_tag : src_launch_clk_tags) { //Standard propagation through the clock network - Time new_arr = src_launch_clk_tag.time() + clk_launch_edge_delay; timing_modified |= ops_.merge_arr_tags(node_id, new_arr, src_node_id, src_launch_clk_tag); @@ -328,6 +328,28 @@ bool CommonAnalysisVisitor::do_arrival_traverse_edge(const TimingGr for(const TimingTag& src_capture_clk_tag : src_capture_clk_tags) { //Standard propagation through the clock network + + /* + * Each source capture tag may be related to clock domain of one of the two types: + * 1. clock domain created for netlist clock, meant to be used with cells clocked at the rising edge + * 2. inverted virtual clock domain based on existing netlist clock. + * It is 180 degree phase shifted in relation to netlist clock + * and it is used in timing analysis for cells clocked at falling edge. + * + * The triggering edges of FFs should be taken into account when propagating tags + * to CPIN nodes. Tags of types which are incompatible with triggering edges + * of the FF clock inputs shouldn't be propagated because having those wouldn't allow + * timing analysis to calculate correct timings for transfers between cells + * clocked rising and falling edges of the same clock. + */ + if ( tg.node_type(sink_node_id) == NodeType::CPIN ) { + if ((tg.trigg_edge(sink_node_id) == TriggeringEdge::FALLING_EDGE && !tc.clock_domain_inverted(src_capture_clk_tag.launch_clock_domain())) || + (tg.trigg_edge(sink_node_id) == TriggeringEdge::RISING_EDGE && tc.clock_domain_inverted(src_capture_clk_tag.launch_clock_domain()))) { + //Skip propagation of timings derived from incompatible constraints + continue; + } + } + timing_modified |= ops_.merge_arr_tags(node_id, src_capture_clk_tag.time() + clk_capture_edge_delay, src_node_id, src_capture_clk_tag); } } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/report/graphviz_dot_writer.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/report/graphviz_dot_writer.cpp index 807a0eb963c..638f7590e6e 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/report/graphviz_dot_writer.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/report/graphviz_dot_writer.cpp @@ -120,7 +120,7 @@ void GraphvizDotWriter::write_dot_node(std::ostream& os, os << "\t"; os << node_name(node); os << "[label=\""; - os << "{" << node << " (" << tg_.node_type(node) << ")"; + os << "{" << node << " (" << tg_.node_type(node) << ") TEdge(" << tg_.trigg_edge_str(node) << ")"; for(const auto& tag_set : {tags, slacks}) { for(const auto& tag : tag_set) { diff --git a/libs/libarchfpga/src/arch_util.cpp b/libs/libarchfpga/src/arch_util.cpp index a8d89c91ed5..0fbe31278cd 100644 --- a/libs/libarchfpga/src/arch_util.cpp +++ b/libs/libarchfpga/src/arch_util.cpp @@ -171,8 +171,9 @@ void free_arch(t_arch* arch) { vtr::free(arch->architecture_id); + //Free internal model library if (arch->model_library) { - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < num_models_lib; ++i) { vtr::t_linked_vptr* vptr = arch->model_library[i].pb_types; while (vptr) { vtr::t_linked_vptr* vptr_prev = vptr; @@ -181,23 +182,41 @@ void free_arch(t_arch* arch) { } } - vtr::free(arch->model_library[0].name); - vtr::free(arch->model_library[0].outputs->name); - delete[] arch->model_library[0].outputs; - vtr::free(arch->model_library[1].inputs->name); - delete[] arch->model_library[1].inputs; - vtr::free(arch->model_library[1].name); - vtr::free(arch->model_library[2].name); - vtr::free(arch->model_library[2].inputs[0].name); - vtr::free(arch->model_library[2].inputs[1].name); - delete[] arch->model_library[2].inputs; - vtr::free(arch->model_library[2].outputs->name); - delete[] arch->model_library[2].outputs; - vtr::free(arch->model_library[3].name); - vtr::free(arch->model_library[3].inputs->name); - delete[] arch->model_library[3].inputs; - vtr::free(arch->model_library[3].outputs->name); - delete[] arch->model_library[3].outputs; + //Each model has different number of inputs/outputs - delete each model separately + //Free INPAD + vtr::free(arch->model_library[LIB_INPUT].name); + vtr::free(arch->model_library[LIB_INPUT].outputs->name); + delete[] arch->model_library[LIB_INPUT].outputs; + + //Free OUTPAD + vtr::free(arch->model_library[LIB_OUTPUT].name); + vtr::free(arch->model_library[LIB_OUTPUT].inputs->name); + delete[] arch->model_library[LIB_OUTPUT].inputs; + + //Free LATCH triggered at RISING EDGE + vtr::free(arch->model_library[LIB_LATCH_RE].name); + vtr::free(arch->model_library[LIB_LATCH_RE].inputs[0].name); + vtr::free(arch->model_library[LIB_LATCH_RE].inputs[1].name); + delete[] arch->model_library[LIB_LATCH_RE].inputs; + vtr::free(arch->model_library[LIB_LATCH_RE].outputs->name); + delete[] arch->model_library[LIB_LATCH_RE].outputs; + + //Free LATCH triggered at FALLING EDGE + vtr::free(arch->model_library[LIB_LATCH_FE].name); + vtr::free(arch->model_library[LIB_LATCH_FE].inputs[0].name); + vtr::free(arch->model_library[LIB_LATCH_FE].inputs[1].name); + delete[] arch->model_library[LIB_LATCH_FE].inputs; + vtr::free(arch->model_library[LIB_LATCH_FE].outputs->name); + delete[] arch->model_library[LIB_LATCH_FE].outputs; + + //Free NAMES + vtr::free(arch->model_library[LIB_NAMES].name); + vtr::free(arch->model_library[LIB_NAMES].inputs->name); + delete[] arch->model_library[LIB_NAMES].inputs; + vtr::free(arch->model_library[LIB_NAMES].outputs->name); + delete[] arch->model_library[LIB_NAMES].outputs; + + //Free the library array delete[] arch->model_library; } @@ -320,6 +339,8 @@ static void free_pb_graph(t_pb_graph_node* pb_graph_node) { delete[] pb_graph_node->input_pins[i][j].parent_pin_class; } delete[] pb_graph_node->input_pins[i]; + if (pb_graph_node->has_secondary) + delete[] pb_graph_node->input_pins_sec[i]; } for (i = 0; i < pb_graph_node->num_output_ports; i++) { for (j = 0; j < pb_graph_node->num_output_pins[i]; j++) { @@ -337,6 +358,8 @@ static void free_pb_graph(t_pb_graph_node* pb_graph_node) { delete[] pb_graph_node->output_pins[i][j].num_connectable_primitive_input_pins; } delete[] pb_graph_node->output_pins[i]; + if (pb_graph_node->has_secondary) + delete[] pb_graph_node->output_pins_sec[i]; } for (i = 0; i < pb_graph_node->num_clock_ports; i++) { for (j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { @@ -344,12 +367,20 @@ static void free_pb_graph(t_pb_graph_node* pb_graph_node) { delete[] pb_graph_node->clock_pins[i][j].parent_pin_class; } delete[] pb_graph_node->clock_pins[i]; + if (pb_graph_node->has_secondary) + delete[] pb_graph_node->clock_pins_sec[i]; } delete[] pb_graph_node->input_pins; delete[] pb_graph_node->output_pins; delete[] pb_graph_node->clock_pins; + if (pb_graph_node->has_secondary) { + delete[] pb_graph_node->input_pins_sec; + delete[] pb_graph_node->output_pins_sec; + delete[] pb_graph_node->clock_pins_sec; + } + delete[] pb_graph_node->num_input_pins; delete[] pb_graph_node->num_output_pins; delete[] pb_graph_node->num_clock_pins; @@ -466,14 +497,22 @@ static void free_pb_type(t_pb_type* pb_type) { for (int i = 0; i < pb_type->num_ports; ++i) { vtr::free(pb_type->ports[i].name); + if (pb_type->class_type == LATCH_CLASS) + vtr::free(pb_type->ports_sec[i].name); if (pb_type->ports[i].port_class) { vtr::free(pb_type->ports[i].port_class); + if (pb_type->class_type == LATCH_CLASS) + vtr::free(pb_type->ports_sec[i].port_class); } if (pb_type->ports[i].port_power) { vtr::free(pb_type->ports[i].port_power); + if (pb_type->class_type == LATCH_CLASS) + vtr::free(pb_type->ports_sec[i].port_power); } } vtr::free(pb_type->ports); + if (pb_type->class_type == LATCH_CLASS) + vtr::free(pb_type->ports_sec); } t_port* findPortByName(const char* name, t_pb_type* pb_type, int* high_index, int* low_index) { @@ -616,6 +655,35 @@ void alloc_and_load_default_child_for_pb_type(t_pb_type* pb_type, copy->ports[i].port_power->buffer_type = POWER_BUFFER_TYPE_NONE; } } + // Special case for latch - fill secondary data fields + // For more information on secondary fields please refer to t_pb_type definition in physical_types.h + if (pb_type->class_type == LATCH_CLASS) { + copy->ports_sec = (t_port*)vtr::calloc(pb_type->num_ports, sizeof(t_port)); + for (i = 0; i < pb_type->num_ports; i++) { + copy->ports_sec[i].is_clock = pb_type->ports[i].is_clock; + copy->ports_sec[i].model_port = pb_type->ports[i].model_port; + copy->ports_sec[i].type = pb_type->ports[i].type; + copy->ports_sec[i].num_pins = pb_type->ports[i].num_pins; + copy->ports_sec[i].parent_pb_type = copy; + copy->ports_sec[i].name = vtr::strdup(pb_type->ports[i].name); + copy->ports_sec[i].port_class = vtr::strdup(pb_type->ports[i].port_class); + copy->ports_sec[i].port_index_by_type = pb_type->ports[i].port_index_by_type; + copy->ports_sec[i].index = pb_type->ports[i].index; + copy->ports_sec[i].absolute_first_pin_index = pb_type->ports[i].absolute_first_pin_index; + + copy->ports_sec[i].port_power = (t_port_power*)vtr::calloc(1, + sizeof(t_port_power)); + //Defaults + if (copy->pb_type_power->estimation_method == POWER_METHOD_AUTO_SIZES) { + copy->ports_sec[i].port_power->wire_type = POWER_WIRE_TYPE_AUTO; + copy->ports_sec[i].port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; + } else if (copy->pb_type_power->estimation_method + == POWER_METHOD_SPECIFY_SIZES) { + copy->ports_sec[i].port_power->wire_type = POWER_WIRE_TYPE_IGNORED; + copy->ports_sec[i].port_power->buffer_type = POWER_BUFFER_TYPE_NONE; + } + } + } copy->annotations = (t_pin_to_pin_annotation*)vtr::calloc(pb_type->num_annotations, sizeof(t_pin_to_pin_annotation)); copy->num_annotations = pb_type->num_annotations; @@ -1002,102 +1070,238 @@ e_power_estimation_method power_method_inherited(e_power_estimation_method paren void CreateModelLibrary(t_arch* arch) { t_model* model_library; - model_library = new t_model[4]; + model_library = new t_model[num_models_lib]; //INPAD - model_library[0].name = vtr::strdup(MODEL_INPUT); - model_library[0].index = 0; - model_library[0].inputs = nullptr; - model_library[0].instances = nullptr; - model_library[0].next = &model_library[1]; - model_library[0].outputs = new t_model_ports[1]; - model_library[0].outputs->dir = OUT_PORT; - model_library[0].outputs->name = vtr::strdup("inpad"); - model_library[0].outputs->next = nullptr; - model_library[0].outputs->size = 1; - model_library[0].outputs->min_size = 1; - model_library[0].outputs->index = 0; - model_library[0].outputs->is_clock = false; + model_library[LIB_INPUT].name = vtr::strdup(MODEL_INPUT); + model_library[LIB_INPUT].index = LIB_INPUT; + model_library[LIB_INPUT].inputs = nullptr; + model_library[LIB_INPUT].instances = nullptr; + model_library[LIB_INPUT].next = &model_library[LIB_OUTPUT]; + model_library[LIB_INPUT].outputs = new t_model_ports[1]; + model_library[LIB_INPUT].outputs->dir = OUT_PORT; + model_library[LIB_INPUT].outputs->name = vtr::strdup("inpad"); + model_library[LIB_INPUT].outputs->next = nullptr; + model_library[LIB_INPUT].outputs->size = 1; + model_library[LIB_INPUT].outputs->min_size = 1; + model_library[LIB_INPUT].outputs->index = 0; + model_library[LIB_INPUT].outputs->is_clock = false; //OUTPAD - model_library[1].name = vtr::strdup(MODEL_OUTPUT); - model_library[1].index = 1; - model_library[1].inputs = new t_model_ports[1]; - model_library[1].inputs->dir = IN_PORT; - model_library[1].inputs->name = vtr::strdup("outpad"); - model_library[1].inputs->next = nullptr; - model_library[1].inputs->size = 1; - model_library[1].inputs->min_size = 1; - model_library[1].inputs->index = 0; - model_library[1].inputs->is_clock = false; - model_library[1].instances = nullptr; - model_library[1].next = &model_library[2]; - model_library[1].outputs = nullptr; - - //LATCH - model_library[2].name = vtr::strdup(MODEL_LATCH); - model_library[2].index = 2; - model_library[2].inputs = new t_model_ports[2]; - - model_library[2].inputs[0].dir = IN_PORT; - model_library[2].inputs[0].name = vtr::strdup("D"); - model_library[2].inputs[0].next = &model_library[2].inputs[1]; - model_library[2].inputs[0].size = 1; - model_library[2].inputs[0].min_size = 1; - model_library[2].inputs[0].index = 0; - model_library[2].inputs[0].is_clock = false; - model_library[2].inputs[0].clock = "clk"; - - model_library[2].inputs[1].dir = IN_PORT; - model_library[2].inputs[1].name = vtr::strdup("clk"); - model_library[2].inputs[1].next = nullptr; - model_library[2].inputs[1].size = 1; - model_library[2].inputs[1].min_size = 1; - model_library[2].inputs[1].index = 0; - model_library[2].inputs[1].is_clock = true; - - model_library[2].instances = nullptr; - model_library[2].next = &model_library[3]; - - model_library[2].outputs = new t_model_ports[1]; - model_library[2].outputs[0].dir = OUT_PORT; - model_library[2].outputs[0].name = vtr::strdup("Q"); - model_library[2].outputs[0].next = nullptr; - model_library[2].outputs[0].size = 1; - model_library[2].outputs[0].min_size = 1; - model_library[2].outputs[0].index = 0; - model_library[2].outputs[0].is_clock = false; - model_library[2].outputs[0].clock = "clk"; + model_library[LIB_OUTPUT].name = vtr::strdup(MODEL_OUTPUT); + model_library[LIB_OUTPUT].index = LIB_OUTPUT; + model_library[LIB_OUTPUT].inputs = new t_model_ports[1]; + model_library[LIB_OUTPUT].inputs->dir = IN_PORT; + model_library[LIB_OUTPUT].inputs->name = vtr::strdup("outpad"); + model_library[LIB_OUTPUT].inputs->next = nullptr; + model_library[LIB_OUTPUT].inputs->size = 1; + model_library[LIB_OUTPUT].inputs->min_size = 1; + model_library[LIB_OUTPUT].inputs->index = 0; + model_library[LIB_OUTPUT].inputs->is_clock = false; + model_library[LIB_OUTPUT].instances = nullptr; + model_library[LIB_OUTPUT].next = &model_library[LIB_LATCH_RE]; + model_library[LIB_OUTPUT].outputs = nullptr; + + //LATCH triggered at RISING EDGE + model_library[LIB_LATCH_RE].name = vtr::strdup(MODEL_LATCH); + model_library[LIB_LATCH_RE].index = LIB_LATCH_RE; + model_library[LIB_LATCH_RE].inputs = new t_model_ports[2]; + + model_library[LIB_LATCH_RE].inputs[0].dir = IN_PORT; + model_library[LIB_LATCH_RE].inputs[0].name = vtr::strdup("D"); + model_library[LIB_LATCH_RE].inputs[0].next = &model_library[LIB_LATCH_RE].inputs[1]; + model_library[LIB_LATCH_RE].inputs[0].size = 1; + model_library[LIB_LATCH_RE].inputs[0].min_size = 1; + model_library[LIB_LATCH_RE].inputs[0].index = 0; + model_library[LIB_LATCH_RE].inputs[0].is_clock = false; + model_library[LIB_LATCH_RE].inputs[0].clock = "clk"; + model_library[LIB_LATCH_RE].inputs[0].trigg_edge = TriggeringEdge::RISING_EDGE; + + model_library[LIB_LATCH_RE].inputs[1].dir = IN_PORT; + model_library[LIB_LATCH_RE].inputs[1].name = vtr::strdup("clk"); + model_library[LIB_LATCH_RE].inputs[1].next = nullptr; + model_library[LIB_LATCH_RE].inputs[1].size = 1; + model_library[LIB_LATCH_RE].inputs[1].min_size = 1; + model_library[LIB_LATCH_RE].inputs[1].index = 0; + model_library[LIB_LATCH_RE].inputs[1].is_clock = true; + model_library[LIB_LATCH_RE].inputs[1].trigg_edge = TriggeringEdge::RISING_EDGE; + + model_library[LIB_LATCH_RE].instances = nullptr; + model_library[LIB_LATCH_RE].next = &model_library[LIB_LATCH_FE]; + + model_library[LIB_LATCH_RE].outputs = new t_model_ports[1]; + model_library[LIB_LATCH_RE].outputs[0].dir = OUT_PORT; + model_library[LIB_LATCH_RE].outputs[0].name = vtr::strdup("Q"); + model_library[LIB_LATCH_RE].outputs[0].next = nullptr; + model_library[LIB_LATCH_RE].outputs[0].size = 1; + model_library[LIB_LATCH_RE].outputs[0].min_size = 1; + model_library[LIB_LATCH_RE].outputs[0].index = 0; + model_library[LIB_LATCH_RE].outputs[0].is_clock = false; + model_library[LIB_LATCH_RE].outputs[0].clock = "clk"; + model_library[LIB_LATCH_RE].outputs[0].trigg_edge = TriggeringEdge::RISING_EDGE; + + /* + * Duplicate LATCH model. + * Second separate model is required for falling edge support. + * It is a copy of LIB_LATCH_RE but with different trigg_edge configuration + * for inputs/outputs t_model_ports. It is used to represent FFs triggered at + * the falling edge of the clock, while LIB_LATCH_RE represents FFs triggered + * at the rising edge. + * + * VPR uses single model of each blif primitive type (e.g. '.latch', '.names') parsed from input blif file. + * That means models are used as configuration reference for parsed blif primitives when + * Latch instances are created in circuit evaluation, meaning: + * 1 unique blif primitive configuration (parsed from blif file) maps to + * 1 unique model (defined here, in VPR internal library) which represents configuration of + * multiple primitive instances (of given type and configuration) used in the circuit + * + * Models have to be duplicated because otherwise VPR will use the configuration + * of the last parsed '.latch' for each FF in the design + * + */ + //LATCH triggered at FALLING EDGE + model_library[LIB_LATCH_FE].name = vtr::strdup(MODEL_LATCH); + model_library[LIB_LATCH_FE].index = LIB_LATCH_FE; + model_library[LIB_LATCH_FE].inputs = new t_model_ports[2]; + + model_library[LIB_LATCH_FE].inputs[0].dir = IN_PORT; + model_library[LIB_LATCH_FE].inputs[0].name = vtr::strdup("D"); + model_library[LIB_LATCH_FE].inputs[0].next = &model_library[LIB_LATCH_FE].inputs[1]; + model_library[LIB_LATCH_FE].inputs[0].size = 1; + model_library[LIB_LATCH_FE].inputs[0].min_size = 1; + model_library[LIB_LATCH_FE].inputs[0].index = 0; + model_library[LIB_LATCH_FE].inputs[0].is_clock = false; + model_library[LIB_LATCH_FE].inputs[0].clock = "clk"; + model_library[LIB_LATCH_FE].inputs[0].trigg_edge = TriggeringEdge::FALLING_EDGE; + + model_library[LIB_LATCH_FE].inputs[1].dir = IN_PORT; + model_library[LIB_LATCH_FE].inputs[1].name = vtr::strdup("clk"); + model_library[LIB_LATCH_FE].inputs[1].next = nullptr; + model_library[LIB_LATCH_FE].inputs[1].size = 1; + model_library[LIB_LATCH_FE].inputs[1].min_size = 1; + model_library[LIB_LATCH_FE].inputs[1].index = 0; + model_library[LIB_LATCH_FE].inputs[1].is_clock = true; + model_library[LIB_LATCH_FE].inputs[1].trigg_edge = TriggeringEdge::FALLING_EDGE; + + model_library[LIB_LATCH_FE].instances = nullptr; + model_library[LIB_LATCH_FE].next = &model_library[LIB_NAMES]; + + model_library[LIB_LATCH_FE].outputs = new t_model_ports[1]; + model_library[LIB_LATCH_FE].outputs[0].dir = OUT_PORT; + model_library[LIB_LATCH_FE].outputs[0].name = vtr::strdup("Q"); + model_library[LIB_LATCH_FE].outputs[0].next = nullptr; + model_library[LIB_LATCH_FE].outputs[0].size = 1; + model_library[LIB_LATCH_FE].outputs[0].min_size = 1; + model_library[LIB_LATCH_FE].outputs[0].index = 0; + model_library[LIB_LATCH_FE].outputs[0].is_clock = false; + model_library[LIB_LATCH_FE].outputs[0].clock = "clk"; + model_library[LIB_LATCH_FE].outputs[0].trigg_edge = TriggeringEdge::FALLING_EDGE; //NAMES - model_library[3].name = vtr::strdup(MODEL_NAMES); - model_library[3].index = 3; - - model_library[3].inputs = new t_model_ports[1]; - model_library[3].inputs[0].dir = IN_PORT; - model_library[3].inputs[0].name = vtr::strdup("in"); - model_library[3].inputs[0].next = nullptr; - model_library[3].inputs[0].size = 1; - model_library[3].inputs[0].min_size = 1; - model_library[3].inputs[0].index = 0; - model_library[3].inputs[0].is_clock = false; - model_library[3].inputs[0].combinational_sink_ports = {"out"}; - - model_library[3].instances = nullptr; - model_library[3].next = nullptr; - - model_library[3].outputs = new t_model_ports[1]; - model_library[3].outputs[0].dir = OUT_PORT; - model_library[3].outputs[0].name = vtr::strdup("out"); - model_library[3].outputs[0].next = nullptr; - model_library[3].outputs[0].size = 1; - model_library[3].outputs[0].min_size = 1; - model_library[3].outputs[0].index = 0; - model_library[3].outputs[0].is_clock = false; + model_library[LIB_NAMES].name = vtr::strdup(MODEL_NAMES); + model_library[LIB_NAMES].index = LIB_NAMES; + + model_library[LIB_NAMES].inputs = new t_model_ports[1]; + model_library[LIB_NAMES].inputs[0].dir = IN_PORT; + model_library[LIB_NAMES].inputs[0].name = vtr::strdup("in"); + model_library[LIB_NAMES].inputs[0].next = nullptr; + model_library[LIB_NAMES].inputs[0].size = 1; + model_library[LIB_NAMES].inputs[0].min_size = 1; + model_library[LIB_NAMES].inputs[0].index = 0; + model_library[LIB_NAMES].inputs[0].is_clock = false; + model_library[LIB_NAMES].inputs[0].combinational_sink_ports = {"out"}; + + model_library[LIB_NAMES].instances = nullptr; + model_library[LIB_NAMES].next = nullptr; + + model_library[LIB_NAMES].outputs = new t_model_ports[1]; + model_library[LIB_NAMES].outputs[0].dir = OUT_PORT; + model_library[LIB_NAMES].outputs[0].name = vtr::strdup("out"); + model_library[LIB_NAMES].outputs[0].next = nullptr; + model_library[LIB_NAMES].outputs[0].size = 1; + model_library[LIB_NAMES].outputs[0].min_size = 1; + model_library[LIB_NAMES].outputs[0].index = 0; + model_library[LIB_NAMES].outputs[0].is_clock = false; arch->model_library = model_library; } +void SyncModel(t_pb_type* pb_type, t_model* model_match_prim, bool is_secondary_model) { + vtr::t_linked_vptr* old; + bool found; + t_model_ports* model_port; + t_port* pb_type_ports; + int p; + + if (is_secondary_model) { + pb_type->model_sec = model_match_prim; + pb_type_ports = pb_type->ports_sec; + } else { + pb_type->model = model_match_prim; + pb_type_ports = pb_type->ports; + } + + old = model_match_prim->pb_types; + model_match_prim->pb_types = (vtr::t_linked_vptr*)vtr::malloc(sizeof(vtr::t_linked_vptr)); + model_match_prim->pb_types->next = old; + model_match_prim->pb_types->data_vptr = pb_type; + for (p = 0; p < pb_type->num_ports; p++) { + found = false; + /* TODO: Parse error checking - check if INPUT matches INPUT and OUTPUT matches OUTPUT (not yet done) */ + model_port = model_match_prim->inputs; + while (model_port && !found) { + if (strcmp(model_port->name, pb_type_ports[p].name) == 0) { + if (model_port->size < pb_type_ports[p].num_pins) { + model_port->size = pb_type_ports[p].num_pins; + } + if (model_port->min_size > pb_type_ports[p].num_pins + || model_port->min_size == -1) { + model_port->min_size = pb_type_ports[p].num_pins; + } + pb_type_ports[p].model_port = model_port; + if (pb_type_ports[p].type != model_port->dir) { + archfpga_throw(get_arch_file_name(), 0, + "Direction for port '%s' on model does not match port direction in pb_type '%s', secondary: %d\n", + pb_type_ports[p].name, pb_type->name, is_secondary_model); + } + if (pb_type_ports[p].is_clock != model_port->is_clock) { + archfpga_throw(get_arch_file_name(), 0, + "Port '%s' on model does not match is_clock in pb_type '%s', secondary: %d\n", + pb_type_ports[p].name, pb_type->name, is_secondary_model); + } + found = true; + } + model_port = model_port->next; + } + model_port = model_match_prim->outputs; + while (model_port && !found) { + if (strcmp(model_port->name, pb_type_ports[p].name) == 0) { + if (model_port->size < pb_type_ports[p].num_pins) { + model_port->size = pb_type_ports[p].num_pins; + } + if (model_port->min_size > pb_type_ports[p].num_pins + || model_port->min_size == -1) { + model_port->min_size = pb_type_ports[p].num_pins; + } + + pb_type_ports[p].model_port = model_port; + if (pb_type_ports[p].type != model_port->dir) { + archfpga_throw(get_arch_file_name(), 0, + "Direction for port '%s' on model does not match port direction in pb_type '%s', secondary: %d\n", + pb_type_ports[p].name, pb_type->name, is_secondary_model); + } + found = true; + } + model_port = model_port->next; + } + if (found != true) { + archfpga_throw(get_arch_file_name(), 0, + "No matching model port for port %s in pb_type %s, secondary: %d\n", + pb_type_ports[p].name, pb_type->name, is_secondary_model); + } + } +} + void SyncModelsPbTypes(t_arch* arch, const std::vector& Types) { for (auto& Type : Types) { @@ -1109,12 +1313,10 @@ void SyncModelsPbTypes(t_arch* arch, void SyncModelsPbTypes_rec(t_arch* arch, t_pb_type* pb_type) { - int i, j, p; + int i, j; t_model *model_match_prim, *cur_model; - t_model_ports* model_port; - vtr::t_linked_vptr* old; char* blif_model_name = nullptr; - + bool sync_secondary_model = false; bool found; if (pb_type->blif_model != nullptr) { @@ -1143,6 +1345,15 @@ void SyncModelsPbTypes_rec(t_arch* arch, while (cur_model && !found) { /* blif model always starts with .subckt so need to skip first 8 characters */ if (strcmp(blif_model_name, cur_model->name) == 0) { + if (strcmp(blif_model_name, MODEL_LATCH) == 0) { + /** + * Special case for .latch: this model exists in 2 variations which are + * defined one after another in linked list, make sure the second variant match + * and mark secondary model for sync. + */ + VTR_ASSERT(strcmp(blif_model_name, cur_model->next->name) == 0); + sync_secondary_model = true; + } found = true; model_match_prim = cur_model; } @@ -1153,67 +1364,11 @@ void SyncModelsPbTypes_rec(t_arch* arch, "No matching model for pb_type %s\n", pb_type->blif_model); } - pb_type->model = model_match_prim; - old = model_match_prim->pb_types; - model_match_prim->pb_types = (vtr::t_linked_vptr*)vtr::malloc(sizeof(vtr::t_linked_vptr)); - model_match_prim->pb_types->next = old; - model_match_prim->pb_types->data_vptr = pb_type; - - for (p = 0; p < pb_type->num_ports; p++) { - found = false; - /* TODO: Parse error checking - check if INPUT matches INPUT and OUTPUT matches OUTPUT (not yet done) */ - model_port = model_match_prim->inputs; - while (model_port && !found) { - if (strcmp(model_port->name, pb_type->ports[p].name) == 0) { - if (model_port->size < pb_type->ports[p].num_pins) { - model_port->size = pb_type->ports[p].num_pins; - } - if (model_port->min_size > pb_type->ports[p].num_pins - || model_port->min_size == -1) { - model_port->min_size = pb_type->ports[p].num_pins; - } - pb_type->ports[p].model_port = model_port; - if (pb_type->ports[p].type != model_port->dir) { - archfpga_throw(get_arch_file_name(), 0, - "Direction for port '%s' on model does not match port direction in pb_type '%s'\n", - pb_type->ports[p].name, pb_type->name); - } - if (pb_type->ports[p].is_clock != model_port->is_clock) { - archfpga_throw(get_arch_file_name(), 0, - "Port '%s' on model does not match is_clock in pb_type '%s'\n", - pb_type->ports[p].name, pb_type->name); - } - found = true; - } - model_port = model_port->next; - } - model_port = model_match_prim->outputs; - while (model_port && !found) { - if (strcmp(model_port->name, pb_type->ports[p].name) == 0) { - if (model_port->size < pb_type->ports[p].num_pins) { - model_port->size = pb_type->ports[p].num_pins; - } - if (model_port->min_size > pb_type->ports[p].num_pins - || model_port->min_size == -1) { - model_port->min_size = pb_type->ports[p].num_pins; - } + SyncModel(pb_type, model_match_prim, false); + // Synchronize secondary model + if (sync_secondary_model) + SyncModel(pb_type, model_match_prim->next, true); - pb_type->ports[p].model_port = model_port; - if (pb_type->ports[p].type != model_port->dir) { - archfpga_throw(get_arch_file_name(), 0, - "Direction for port '%s' on model does not match port direction in pb_type '%s'\n", - pb_type->ports[p].name, pb_type->name); - } - found = true; - } - model_port = model_port->next; - } - if (found != true) { - archfpga_throw(get_arch_file_name(), 0, - "No matching model port for port %s in pb_type %s\n", - pb_type->ports[p].name, pb_type->name); - } - } } else { for (i = 0; i < pb_type->num_modes; i++) { for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) { diff --git a/libs/libarchfpga/src/arch_util.h b/libs/libarchfpga/src/arch_util.h index 7d882450a5a..9701adc948d 100644 --- a/libs/libarchfpga/src/arch_util.h +++ b/libs/libarchfpga/src/arch_util.h @@ -16,6 +16,16 @@ void set_arch_file_name(const char* arch); const char* get_arch_file_name(); constexpr const char* EMPTY_BLOCK_NAME = "EMPTY"; +constexpr const int num_models_lib = 5; +constexpr const int LATCH_CLOCK_INPUT_ID = 1; + +enum InternalModel { + LIB_INPUT, + LIB_OUTPUT, + LIB_LATCH_RE, + LIB_LATCH_FE, + LIB_NAMES +}; class InstPort { public: @@ -86,6 +96,10 @@ e_power_estimation_method power_method_inherited(e_power_estimation_method paren void CreateModelLibrary(t_arch* arch); +void SyncModel(t_pb_type* pb_type, + t_model* model_match_prim, + bool is_secondary_model); + void SyncModelsPbTypes(t_arch* arch, const std::vector& Types); diff --git a/libs/libarchfpga/src/logic_types.h b/libs/libarchfpga/src/logic_types.h index 4427b85016f..89a26754621 100644 --- a/libs/libarchfpga/src/logic_types.h +++ b/libs/libarchfpga/src/logic_types.h @@ -25,12 +25,20 @@ enum PORTS { ERR_PORT }; +enum TriggeringEdge { + RISING_EDGE, + FALLING_EDGE, + DONT_CARE, + ERROR_EDGE +}; + struct t_model_ports { enum PORTS dir = ERR_PORT; /* port direction */ char* name = nullptr; /* name of this port */ int size = 0; /* maximum number of pins */ int min_size = 0; /* minimum number of pins */ bool is_clock = false; /* clock? */ + enum TriggeringEdge trigg_edge = DONT_CARE; /* triggering edge of the clock port */ bool is_non_clock_global = false; /* not a clock but is a special, global, control signal (eg global asynchronous reset, etc) */ std::string clock; /* The clock associated with this pin (if the pin is sequential) */ std::vector combinational_sink_ports; /* The other ports on this model which are combinationally driven by this port */ diff --git a/libs/libarchfpga/src/physical_types.cpp b/libs/libarchfpga/src/physical_types.cpp index dfa110f393f..e0fe226f037 100644 --- a/libs/libarchfpga/src/physical_types.cpp +++ b/libs/libarchfpga/src/physical_types.cpp @@ -4,6 +4,7 @@ #include "vtr_log.h" #include "arch_util.h" +#include "arch_types.h" static bool switch_type_is_buffered(SwitchType type); static bool switch_type_is_configurable(SwitchType type); @@ -194,6 +195,131 @@ std::string t_pb_graph_node::hierarchical_type_name() const { return vtr::join(names.rbegin(), names.rend(), "/"); } +void t_pb_graph_node::update_pins() { + int i, j, i_input = 0, i_output = 0, i_clockport = 0; + t_port* pb_type_ports = pb_type->ports_sec; + + VTR_ASSERT(this->has_secondary == false); + this->has_secondary = true; + + int pin_count_in_cluster; + for (i = 0; i < this->pb_type->num_ports; i++) { + if (pb_type_ports[i].model_port) { + VTR_ASSERT(this->pb_type->num_modes == 0); + } else { + VTR_ASSERT(this->pb_type->num_modes != 0 || pb_type_ports[i].is_clock); + } + if (pb_type_ports[i].type == IN_PORT && !pb_type_ports[i].is_clock) { + this->input_pins_sec = new t_pb_graph_pin* [this->num_input_ports] { nullptr }; + this->input_pins_sec[i_input] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { + this->input_pins_sec[i_input][j].pin_number = j; + this->input_pins_sec[i_input][j].port = &pb_type_ports[i]; + this->input_pins_sec[i_input][j].parent_node = this; + this->input_pins_sec[i_input][j].pin_count_in_cluster = this->input_pins[i_input][j].pin_count_in_cluster; + this->input_pins_sec[i_input][j].parent_pin_class = this->input_pins[i_input][j].parent_pin_class; + if (this->pb_type->blif_model != nullptr) { + if (strcmp(this->pb_type->blif_model, MODEL_OUTPUT) == 0) { + this->input_pins_sec[i_input][j].type = PB_PIN_OUTPAD; + } else if (this->num_clock_ports != 0) { + this->input_pins_sec[i_input][j].type = PB_PIN_SEQUENTIAL; + } else { + this->input_pins_sec[i_input][j].type = PB_PIN_TERMINAL; + } + } + pin_count_in_cluster++; + + //Copy timings from primary pins + // sequential timing information + this->input_pins_sec[i_input][j].tsu = this->input_pins[i_input][j].tsu; + this->input_pins_sec[i_input][j].thld = this->input_pins[i_input][j].thld; + this->input_pins_sec[i_input][j].tco_min = this->input_pins[i_input][j].tco_min; + this->input_pins_sec[i_input][j].tco_max = this->input_pins[i_input][j].tco_max; + //this->input_pins_sec[i_input][j].t_pb_graph_pin* associated_clock_pin = nullptr; /* For sequentail elements, the associated clock */ + + // combinational timing information + this->input_pins_sec[i_input][j].num_pin_timing = this->input_pins[i_input][j].num_pin_timing; + this->input_pins_sec[i_input][j].pin_timing_del_max = this->input_pins[i_input][j].pin_timing_del_max; + this->input_pins_sec[i_input][j].pin_timing_del_min = this->input_pins[i_input][j].pin_timing_del_min; + this->input_pins_sec[i_input][j].num_pin_timing_del_max_annotated = this->input_pins[i_input][j].num_pin_timing_del_max_annotated; + this->input_pins_sec[i_input][j].num_pin_timing_del_min_annotated = this->input_pins[i_input][j].num_pin_timing_del_min_annotated; + //this->input_pins_sec[i_input][j].std::vector pin_timing; /* timing edge sink pins [0..num_pin_timing-1]*/ + } + i_input++; + } else if (pb_type_ports[i].type == OUT_PORT) { + this->output_pins_sec = new t_pb_graph_pin* [this->num_input_ports] { nullptr }; + this->output_pins_sec[i_output] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { + this->output_pins_sec[i_output][j].pin_number = j; + this->output_pins_sec[i_output][j].port = &pb_type_ports[i]; + this->output_pins_sec[i_output][j].parent_node = this; + this->output_pins_sec[i_output][j].pin_count_in_cluster = this->output_pins[i_output][j].pin_count_in_cluster; + this->output_pins_sec[i_output][j].parent_pin_class = this->output_pins[i_output][j].parent_pin_class; + this->output_pins_sec[i_output][j].list_of_connectable_input_pin_ptrs = this->output_pins[i_output][j].list_of_connectable_input_pin_ptrs; + this->output_pins_sec[i_output][j].num_connectable_primitive_input_pins = this->output_pins[i_output][j].num_connectable_primitive_input_pins; + if (this->pb_type->blif_model != nullptr) { + if (strcmp(this->pb_type->blif_model, MODEL_INPUT) == 0) { + this->output_pins_sec[i_output][j].type = PB_PIN_INPAD; + } else if (this->num_clock_ports != 0) { + this->output_pins_sec[i_output][j].type = PB_PIN_SEQUENTIAL; + } else { + this->output_pins_sec[i_output][j].type = PB_PIN_TERMINAL; + } + } + pin_count_in_cluster++; + + //Copy timings from primary pins + // sequential timing information + this->output_pins_sec[i_output][j].tsu = this->output_pins[i_output][j].tsu; + this->output_pins_sec[i_output][j].thld = this->output_pins[i_output][j].thld; + this->output_pins_sec[i_output][j].tco_min = this->output_pins[i_output][j].tco_min; + this->output_pins_sec[i_output][j].tco_max = this->output_pins[i_output][j].tco_max; + //this->output_pins_sec[i_input][j].t_pb_graph_pin* associated_clock_pin = nullptr; /* For sequentail elements, the associated clock */ + + // combinational timing information + this->output_pins_sec[i_output][j].num_pin_timing = this->output_pins[i_output][j].num_pin_timing; + this->output_pins_sec[i_output][j].pin_timing_del_max = this->output_pins[i_output][j].pin_timing_del_max; + this->output_pins_sec[i_output][j].pin_timing_del_min = this->output_pins[i_output][j].pin_timing_del_min; + this->output_pins_sec[i_output][j].num_pin_timing_del_max_annotated = this->output_pins[i_output][j].num_pin_timing_del_max_annotated; + this->output_pins_sec[i_output][j].num_pin_timing_del_min_annotated = this->output_pins[i_output][j].num_pin_timing_del_min_annotated; + //this->output_pins_sec[i_input][j].std::vector pin_timing; /* timing edge sink pins [0..num_pin_timing-1]*/ + } + i_output++; + } else { + VTR_ASSERT(pb_type_ports[i].is_clock && pb_type_ports[i].type == IN_PORT); + this->clock_pins_sec = new t_pb_graph_pin* [this->num_input_ports] { nullptr }; + this->clock_pins_sec[i_clockport] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { + this->clock_pins_sec[i_clockport][j].pin_number = j; + this->clock_pins_sec[i_clockport][j].port = &pb_type_ports[i]; + this->clock_pins_sec[i_clockport][j].parent_node = this; + this->clock_pins_sec[i_clockport][j].pin_count_in_cluster = this->clock_pins[i_clockport][j].pin_count_in_cluster; + this->clock_pins_sec[i_clockport][j].parent_pin_class = this->clock_pins[i_clockport][j].parent_pin_class; + if (this->pb_type->blif_model != nullptr) { + this->clock_pins_sec[i_clockport][j].type = PB_PIN_CLOCK; + } + pin_count_in_cluster++; + + //Copy timings from primary pins + // sequential timing information + this->clock_pins_sec[i_clockport][j].tsu = this->clock_pins[i_clockport][j].tsu; + this->clock_pins_sec[i_clockport][j].thld = this->clock_pins[i_clockport][j].thld; + this->clock_pins_sec[i_clockport][j].tco_min = this->clock_pins[i_clockport][j].tco_min; + this->clock_pins_sec[i_clockport][j].tco_max = this->clock_pins[i_clockport][j].tco_max; + //this->clock_pins_sec[i_clockport][j].t_pb_graph_pin* associated_clock_pin = nullptr; /* For sequentail elements, the associated clock */ + + // combinational timing information + this->clock_pins_sec[i_clockport][j].num_pin_timing = this->clock_pins[i_clockport][j].num_pin_timing; + this->clock_pins_sec[i_clockport][j].pin_timing_del_max = this->clock_pins[i_clockport][j].pin_timing_del_max; + this->clock_pins_sec[i_clockport][j].pin_timing_del_min = this->clock_pins[i_clockport][j].pin_timing_del_min; + this->clock_pins_sec[i_clockport][j].num_pin_timing_del_max_annotated = this->clock_pins[i_clockport][j].num_pin_timing_del_max_annotated; + this->clock_pins_sec[i_clockport][j].num_pin_timing_del_min_annotated = this->clock_pins[i_clockport][j].num_pin_timing_del_min_annotated; + //this->clock_pins_sec[i_clockport][j].std::vector pin_timing; /* timing edge sink pins [0..num_pin_timing-1]*/ + } + i_clockport++; + } + } +} /** * t_pb_graph_pin */ diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 55c99e23752..4aee269c679 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -787,6 +787,7 @@ struct t_physical_pin { * Data members: * name: name of the port * is_clock: whether or not this port is a clock + * trigg_edge: triggering edge of the clock port * is_non_clock_global: Applies to top level pb_type, this pin is not a clock but * is a global signal (useful for stuff like global reset signals, * perhaps useful for VCC and GND) @@ -803,6 +804,7 @@ struct t_physical_tile_port { char* name; enum PORTS type; bool is_clock; + enum TriggeringEdge trigg_edge; bool is_non_clock_global; int num_pins; PortEquivalence equivalent; @@ -908,9 +910,19 @@ struct t_logical_block_type { * name: name of the physical block type * num_pb: maximum number of instances of this physical block type sharing one parent * blif_model: the string in the blif circuit that corresponds with this pb type + * model: primary logical model - used for processing the majority of pb_types, + * including those matched to FFs triggered at the rising edge of the clock + * model_sec: secondary logical model - used for pb_types matched to .latch models + * representing FFs triggered at the falling edge of the clock. + * Required for asserting correct match between pb_type from pb_graph_node + * and netlist block in read_netlist.cpp and vpr_utils.cpp * class_type: Special library name * modes: Different modes accepted - * ports: I/O and clock ports + * ports: primary I/O and clock ports - used for processing the majority of pb_types, + * including those matched to FFs triggered at the rising edge of the clock + * ports_sec: secondary I/O and clock ports - used for pb_types matched to .latch models + * representing FFs triggered at the falling edge of the clock. + * Required for feasibility checks in clustering step. * num_clock_pins: A count of the total number of clock pins * num_input_pins: A count of the total number of input pins * num_output_pins: A count of the total number of output pins @@ -925,12 +937,14 @@ struct t_pb_type { int num_pb = 0; char* blif_model = nullptr; t_model* model = nullptr; + t_model* model_sec = nullptr; enum e_pb_type_class class_type = UNKNOWN_CLASS; t_mode* modes = nullptr; /* [0..num_modes-1] */ int num_modes = 0; t_port* ports = nullptr; /* [0..num_ports] */ int num_ports = 0; + t_port* ports_sec = nullptr; /* [0..num_ports] */ int num_clock_pins = 0; int num_input_pins = 0; /* inputs not including clock pins */ @@ -1162,6 +1176,7 @@ struct t_pin_to_pin_annotation { class t_pb_graph_node { public: t_pb_type* pb_type; + bool has_secondary; int placement_index; @@ -1185,9 +1200,12 @@ class t_pb_graph_node { * */ std::vector illegal_modes; - t_pb_graph_pin** input_pins; /* [0..num_input_ports-1] [0..num_port_pins-1]*/ - t_pb_graph_pin** output_pins; /* [0..num_output_ports-1] [0..num_port_pins-1]*/ - t_pb_graph_pin** clock_pins; /* [0..num_clock_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** input_pins; /* [0..num_input_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** output_pins; /* [0..num_output_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** clock_pins; /* [0..num_clock_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** input_pins_sec; /* [0..num_input_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** output_pins_sec; /* [0..num_output_ports-1] [0..num_port_pins-1]*/ + t_pb_graph_pin** clock_pins_sec; /* [0..num_clock_ports-1] [0..num_port_pins-1]*/ int num_input_ports; int num_output_ports; @@ -1230,6 +1248,16 @@ class t_pb_graph_node { // Returns a string containing the hierarchical type name of the pb_graph_node // Ex: clb[0][default]/lab[0][default]/fle[3][n1_lut6]/ble6[0][default]/lut6[0] std::string hierarchical_type_name() const; + + /** Fill secondary pin structs with data + * + * When pb_graph_node for falling edge clocked FF is processed, + * secondary structures for input, output and clock pins have to be + * filled with data. + * Do that by copying data from primary pin structures and replacing + * references to primary t_ports with references to secondary t_ports. + */ + void update_pins(); }; /* Identify pb pin type for timing purposes */ diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index cd1b8a0002a..585d13c0b9e 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -267,6 +267,9 @@ std::string inst_port_to_port_name(std::string inst_port); static bool attribute_to_bool(const pugi::xml_node node, const pugi::xml_attribute attr, const pugiutil::loc_data& loc_data); +static TriggeringEdge attribute_to_trigg_edge(const pugi::xml_node node, + const pugi::xml_attribute attr, + const pugiutil::loc_data& loc_data); int find_switch_by_name(const t_arch& arch, std::string switch_name); e_side string_to_side(std::string side_str); @@ -1188,6 +1191,8 @@ static void ProcessPb_Type(vtr::string_internment* strings, pugi::xml_node Paren num_clock_ports = count_children(Parent, "clock", loc_data, ReqOpt::OPTIONAL); num_ports = num_in_ports + num_out_ports + num_clock_ports; pb_type->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + if (pb_type->class_type == LATCH_CLASS) + pb_type->ports_sec = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); pb_type->num_ports = num_ports; /* Enforce VPR's definition of LUT/FF by checking number of ports */ @@ -1223,21 +1228,32 @@ static void ProcessPb_Type(vtr::string_internment* strings, pugi::xml_node Paren Cur = get_first_child(Parent, "clock", loc_data, ReqOpt::OPTIONAL); } while (Cur) { - pb_type->ports[j].parent_pb_type = pb_type; - pb_type->ports[j].index = j; - pb_type->ports[j].port_index_by_type = k; - ProcessPb_TypePort(Cur, &pb_type->ports[j], - pb_type->pb_type_power->estimation_method, is_root_pb_type, loc_data); - - pb_type->ports[j].absolute_first_pin_index = absolute_port_first_pin_index; - absolute_port_first_pin_index += pb_type->ports[j].num_pins; + t_port* pb_type_ports = pb_type->ports; + int l = 0; + do { + pb_type_ports[j].parent_pb_type = pb_type; + pb_type_ports[j].index = j; + pb_type_ports[j].port_index_by_type = k; + ProcessPb_TypePort(Cur, &pb_type_ports[j], + pb_type->pb_type_power->estimation_method, is_root_pb_type, loc_data); + + pb_type_ports[j].absolute_first_pin_index = absolute_port_first_pin_index; + absolute_port_first_pin_index += pb_type_ports[j].num_pins; + + //If the processed pb_type is of LATCH class, + //then process additional secondary set of pb_type_ports + //which is required for falling edge support in FFs due to + //2 possible internal models (t_model) for latches and their ports + pb_type_ports = pb_type->ports_sec; + l++; + } while ((pb_type->class_type == LATCH_CLASS) && (l <= 1)); //Check port name duplicates ret_pb_ports = pb_port_names.insert(std::pair(pb_type->ports[j].name, 0)); if (!ret_pb_ports.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate port names in pb_type '%s': port '%s'\n", - pb_type->name, pb_type->ports[j].name); + pb_type->name, pb_type_ports[j].name); } /* get next iteration */ @@ -1251,18 +1267,27 @@ static void ProcessPb_Type(vtr::string_internment* strings, pugi::xml_node Paren /* Count stats on the number of each type of pin */ pb_type->num_clock_pins = pb_type->num_input_pins = pb_type->num_output_pins = 0; - for (i = 0; i < pb_type->num_ports; i++) { - if (pb_type->ports[i].type == IN_PORT - && pb_type->ports[i].is_clock == false) { - pb_type->num_input_pins += pb_type->ports[i].num_pins; - } else if (pb_type->ports[i].type == OUT_PORT) { - pb_type->num_output_pins += pb_type->ports[i].num_pins; - } else { - VTR_ASSERT(pb_type->ports[i].is_clock - && pb_type->ports[i].type == IN_PORT); - pb_type->num_clock_pins += pb_type->ports[i].num_pins; + t_port* pb_type_ports = pb_type->ports; + int l = 0; + do { + for (i = 0; i < pb_type->num_ports; i++) { + if (pb_type_ports[i].type == IN_PORT + && pb_type_ports[i].is_clock == false) { + pb_type->num_input_pins += pb_type_ports[i].num_pins; + } else if (pb_type_ports[i].type == OUT_PORT) { + pb_type->num_output_pins += pb_type_ports[i].num_pins; + } else { + VTR_ASSERT(pb_type_ports[i].is_clock + && pb_type_ports[i].type == IN_PORT); + pb_type->num_clock_pins += pb_type_ports[i].num_pins; + } } - } + //If the processed pb_type is of LATCH class, then also count + //the numbers of secondary pin sets because of 2 possible + //internal models (t_model) for latches and their ports + pb_type_ports = pb_type->ports_sec; + l++; + } while ((pb_type->class_type == LATCH_CLASS) && (l <= 1)); pb_type->num_pins = pb_type->num_input_pins + pb_type->num_output_pins + pb_type->num_clock_pins; @@ -2314,6 +2339,9 @@ static void ProcessModelPorts(pugi::xml_node port_group, t_model* model, std::se } else if (attr.name() == std::string("is_clock")) { model_port->is_clock = attribute_to_bool(port, attr, loc_data); + } else if (attr.name() == std::string("edge")) { + model_port->trigg_edge = attribute_to_trigg_edge(port, attr, loc_data); + } else if (attr.name() == std::string("is_non_clock_global")) { model_port->is_non_clock_global = attribute_to_bool(port, attr, loc_data); @@ -4787,6 +4815,20 @@ static bool attribute_to_bool(const pugi::xml_node node, return false; } +static TriggeringEdge attribute_to_trigg_edge(const pugi::xml_node node, + const pugi::xml_attribute attr, + const pugiutil::loc_data& loc_data) { + if (attr.value() == std::string("rising")) { + return TriggeringEdge::RISING_EDGE; + } else if (attr.value() == std::string("falling")) { + return TriggeringEdge::FALLING_EDGE; + } else { + bad_attribute_value(attr, node, loc_data, {"rising", "falling"}); + } + + return ERROR_EDGE; +} + int find_switch_by_name(const t_arch& arch, std::string switch_name) { for (int iswitch = 0; iswitch < arch.num_switches; ++iswitch) { const t_arch_switch_inf& arch_switch = arch.Switches[iswitch]; diff --git a/vpr/src/base/atom_netlist_utils.cpp b/vpr/src/base/atom_netlist_utils.cpp index cbfed079c25..95117d24a57 100644 --- a/vpr/src/base/atom_netlist_utils.cpp +++ b/vpr/src/base/atom_netlist_utils.cpp @@ -189,8 +189,17 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { clk_net = make_unconn(unconn_count, PinType::SINK); } - //Latch type: VPR always assumes rising edge - auto type = "re"; + //Latch type + auto type = "invalid"; + if ((blk_model->name == std::string(MODEL_LATCH)) && (blk_model->inputs[LATCH_CLOCK_INPUT_ID].is_clock)) { + if (blk_model->inputs[LATCH_CLOCK_INPUT_ID].trigg_edge == TriggeringEdge::FALLING_EDGE) { + type = "fe"; + } else { + //Otherwise always assume rising edge (.latch could be also configured as: + //'active high', 'active low', 'asynchronous') + type = "re"; + } + } //Latch initial value int init_val = 3; //Unkown or unspecified diff --git a/vpr/src/base/netlist_writer.cpp b/vpr/src/base/netlist_writer.cpp index 60036425bbf..84e975ab5cd 100644 --- a/vpr/src/base/netlist_writer.cpp +++ b/vpr/src/base/netlist_writer.cpp @@ -431,6 +431,16 @@ class LatchInst : public Instance { return is; } + private: + std::string type_to_sdf_edge() { + if (type_ == Type::RISING_EDGE) + return "posedge"; + else if (type_ == Type::FALLING_EDGE) + return "negedge"; + else + VTR_ASSERT(false); + } + public: LatchInst(std::string inst_name, /// port_conns, ///inputs->next->trigg_edge == TriggeringEdge::FALLING_EDGE) { + type = LatchInst::Type::FALLING_EDGE; + } else { + type = LatchInst::Type::RISING_EDGE; + } + + //VPR currently doesn't store enough information to determine this attribute vtr::LogicValue init_value = vtr::LogicValue::FALSE; return std::make_shared(inst_name, port_conns, type, init_value, tcq, tsu); diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 2425a18d239..776a439d49f 100644 --- a/vpr/src/base/read_blif.cpp +++ b/vpr/src/base/read_blif.cpp @@ -194,15 +194,20 @@ struct BlifAllocCallback : public blifparse::Callback { void latch(std::string input, std::string output, blifparse::LatchType type, std::string control, blifparse::LogicValue init) override { if (type == blifparse::LatchType::UNSPECIFIED) { VTR_LOGF_WARN(filename_.c_str(), lineno_, "Treating latch '%s' of unspecified type as rising edge triggered\n", output.c_str()); - } else if (type != blifparse::LatchType::RISING_EDGE) { - vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Only rising edge latches supported\n"); + } else if (type != blifparse::LatchType::RISING_EDGE && type != blifparse::LatchType::FALLING_EDGE) { + vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Only rising and falling edge latches supported\n"); } if (control.empty()) { vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Latch must have a clock\n"); } - const t_model* blk_model = find_model(MODEL_LATCH); + TriggeringEdge t_edge; + if (type == blifparse::LatchType::FALLING_EDGE) + t_edge = TriggeringEdge::FALLING_EDGE; + else + t_edge = TriggeringEdge::RISING_EDGE; + const t_model* blk_model = find_latch_model(t_edge); VTR_ASSERT_MSG(blk_model->inputs, "Has one input port"); VTR_ASSERT_MSG(blk_model->inputs->next, "Has two input port"); @@ -211,7 +216,7 @@ struct BlifAllocCallback : public blifparse::Callback { VTR_ASSERT_MSG(!blk_model->outputs->next, "Has no more than one input port"); const t_model_ports* d_model_port = blk_model->inputs; - const t_model_ports* clk_model_port = blk_model->inputs->next; + t_model_ports* clk_model_port = blk_model->inputs->next; const t_model_ports* q_model_port = blk_model->outputs; VTR_ASSERT(d_model_port->name == std::string("D")); @@ -466,6 +471,31 @@ struct BlifAllocCallback : public blifparse::Callback { return arch_model; } + const t_model* find_latch_model(TriggeringEdge t_edge) { + const t_model* arch_model = nullptr; + for (const t_model* arch_models : {user_arch_models_, library_arch_models_}) { + arch_model = arch_models; + while (arch_model) { + if (strcmp(MODEL_LATCH, arch_model->name) == 0) { + if (t_edge == arch_model->inputs[LATCH_CLOCK_INPUT_ID].trigg_edge) { + //Found it + break; + } + } + arch_model = arch_model->next; + } + if (arch_model) { + //Found it + break; + } + } + if (!arch_model) { + vpr_throw(VPR_ERROR_BLIF_F, filename_.c_str(), lineno_, "Failed to find matching architecture model for '%s' with edge: %d\n", + MODEL_LATCH, t_edge); + } + return arch_model; + } + const t_model_ports* find_model_port(const t_model* blk_model, std::string port_name) { //We need to handle both single, and multi-bit port names // diff --git a/vpr/src/base/read_netlist.cpp b/vpr/src/base/read_netlist.cpp index 9195721a012..cfd148fd469 100644 --- a/vpr/src/base/read_netlist.cpp +++ b/vpr/src/base/read_netlist.cpp @@ -1171,17 +1171,31 @@ static void load_atom_pin_mapping(const ClusteredNetlist& clb_nlist) { VTR_ASSERT_MSG(pb, "Atom block must have a matching PB"); const t_pb_graph_node* gnode = pb->pb_graph_node; - VTR_ASSERT_MSG(gnode->pb_type->model == atom_ctx.nlist.block_model(blk), - "Atom block PB must match BLIF model"); + if (strcmp(atom_ctx.nlist.block_model(blk)->name, MODEL_LATCH) == 0) { + if (atom_ctx.nlist.block_model(blk)->inputs->trigg_edge == TriggeringEdge::FALLING_EDGE) { + VTR_ASSERT_MSG(gnode->pb_type->model_sec == atom_ctx.nlist.block_model(blk), + "Atom block PB must match BLIF model"); + } else { + VTR_ASSERT_MSG(gnode->pb_type->model == atom_ctx.nlist.block_model(blk), + "Atom block PB must match BLIF model"); + } + } else { + VTR_ASSERT_MSG(gnode->pb_type->model == atom_ctx.nlist.block_model(blk), + "Atom block PB must match BLIF model"); + } + + // Always assign primary pins + t_pb_graph_pin* pins; for (int iport = 0; iport < gnode->num_input_ports; ++iport) { if (gnode->num_input_pins[iport] <= 0) continue; + pins = gnode->input_pins[iport]; - const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, gnode->input_pins[iport][0].port->model_port); + const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, pins[0].port->model_port); if (!port) continue; for (int ipin = 0; ipin < gnode->num_input_pins[iport]; ++ipin) { - const t_pb_graph_pin* gpin = &gnode->input_pins[iport][ipin]; + const t_pb_graph_pin* gpin = &pins[ipin]; VTR_ASSERT(gpin); set_atom_pin_mapping(clb_nlist, blk, port, gpin); @@ -1190,12 +1204,13 @@ static void load_atom_pin_mapping(const ClusteredNetlist& clb_nlist) { for (int iport = 0; iport < gnode->num_output_ports; ++iport) { if (gnode->num_output_pins[iport] <= 0) continue; + pins = gnode->output_pins[iport]; - const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, gnode->output_pins[iport][0].port->model_port); + const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, pins[0].port->model_port); if (!port) continue; for (int ipin = 0; ipin < gnode->num_output_pins[iport]; ++ipin) { - const t_pb_graph_pin* gpin = &gnode->output_pins[iport][ipin]; + const t_pb_graph_pin* gpin = &pins[ipin]; VTR_ASSERT(gpin); set_atom_pin_mapping(clb_nlist, blk, port, gpin); @@ -1204,12 +1219,13 @@ static void load_atom_pin_mapping(const ClusteredNetlist& clb_nlist) { for (int iport = 0; iport < gnode->num_clock_ports; ++iport) { if (gnode->num_clock_pins[iport] <= 0) continue; + pins = gnode->clock_pins[iport]; - const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, gnode->clock_pins[iport][0].port->model_port); + const AtomPortId port = atom_ctx.nlist.find_atom_port(blk, pins[0].port->model_port); if (!port) continue; for (int ipin = 0; ipin < gnode->num_clock_pins[iport]; ++ipin) { - const t_pb_graph_pin* gpin = &gnode->clock_pins[iport][ipin]; + const t_pb_graph_pin* gpin = &pins[ipin]; VTR_ASSERT(gpin); set_atom_pin_mapping(clb_nlist, blk, port, gpin); diff --git a/vpr/src/pack/cluster_placement.cpp b/vpr/src/pack/cluster_placement.cpp index 602d6696909..3739da4657c 100644 --- a/vpr/src/pack/cluster_placement.cpp +++ b/vpr/src/pack/cluster_placement.cpp @@ -138,7 +138,7 @@ bool get_next_primitive_list(t_cluster_placement_stats* cluster_placement_stats, continue; /* no more primitives of this type available */ } if (primitive_type_feasible(molecule->atom_block_ids[molecule->root], - cluster_placement_stats->valid_primitives[i]->next_primitive->pb_graph_node->pb_type)) { + cluster_placement_stats->valid_primitives[i]->next_primitive->pb_graph_node)) { prev = cluster_placement_stats->valid_primitives[i]; cur = cluster_placement_stats->valid_primitives[i]->next_primitive; while (cur) { @@ -397,7 +397,7 @@ static float try_place_molecule(const t_pack_molecule* molecule, list_size = get_array_size_of_molecule(molecule); if (primitive_type_feasible(molecule->atom_block_ids[molecule->root], - root->pb_type)) { + root)) { if (root->cluster_placement_primitive->valid == true) { for (i = 0; i < list_size; i++) { primitives_list[i] = nullptr; diff --git a/vpr/src/pack/cluster_util.cpp b/vpr/src/pack/cluster_util.cpp index 3a91d13ed40..10dc988b46d 100644 --- a/vpr/src/pack/cluster_util.cpp +++ b/vpr/src/pack/cluster_util.cpp @@ -631,7 +631,7 @@ bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { } //Generic feasibility check - return primitive_type_feasible(blk_id, cur_pb_type); + return primitive_type_feasible(blk_id, cur_pb->pb_graph_node); } bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { diff --git a/vpr/src/pack/pb_type_graph.cpp b/vpr/src/pack/pb_type_graph.cpp index 983d6efed68..07d280f3a6e 100644 --- a/vpr/src/pack/pb_type_graph.cpp +++ b/vpr/src/pack/pb_type_graph.cpp @@ -257,18 +257,28 @@ static void alloc_and_load_pb_graph(t_pb_graph_node* pb_graph_node, } i_input = i_output = i_clockport = 0; + t_port* pb_type_ports; for (i = 0; i < pb_type->num_ports; i++) { - if (pb_type->ports[i].model_port) { + //Decide which ports should be referenced in pins of pb_graph_nodes + //If this pb_graph_node was marked with has_secondary, it means that + //it references FF clocked at falling edge so the secondary ports should be used. + if (pb_graph_node->has_secondary) { + pb_type_ports = pb_type->ports_sec; + } else { + pb_type_ports = pb_type->ports; + } + + if (pb_type_ports[i].model_port) { VTR_ASSERT(pb_type->num_modes == 0); } else { - VTR_ASSERT(pb_type->num_modes != 0 || pb_type->ports[i].is_clock); + VTR_ASSERT(pb_type->num_modes != 0 || pb_type_ports[i].is_clock); } - if (pb_type->ports[i].type == IN_PORT && !pb_type->ports[i].is_clock) { - pb_graph_node->input_pins[i_input] = new t_pb_graph_pin[pb_type->ports[i].num_pins]; - pb_graph_node->num_input_pins[i_input] = pb_type->ports[i].num_pins; - for (j = 0; j < pb_type->ports[i].num_pins; j++) { + if (pb_type_ports[i].type == IN_PORT && !pb_type_ports[i].is_clock) { + pb_graph_node->input_pins[i_input] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + pb_graph_node->num_input_pins[i_input] = pb_type_ports[i].num_pins; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { pb_graph_node->input_pins[i_input][j].pin_number = j; - pb_graph_node->input_pins[i_input][j].port = &pb_type->ports[i]; + pb_graph_node->input_pins[i_input][j].port = &pb_type_ports[i]; pb_graph_node->input_pins[i_input][j].parent_node = pb_graph_node; pb_graph_node->input_pins[i_input][j].pin_count_in_cluster = pin_count_in_cluster; if (pb_graph_node->pb_type->blif_model != nullptr) { @@ -283,12 +293,12 @@ static void alloc_and_load_pb_graph(t_pb_graph_node* pb_graph_node, pin_count_in_cluster++; } i_input++; - } else if (pb_type->ports[i].type == OUT_PORT) { - pb_graph_node->output_pins[i_output] = new t_pb_graph_pin[pb_type->ports[i].num_pins]; - pb_graph_node->num_output_pins[i_output] = pb_type->ports[i].num_pins; - for (j = 0; j < pb_type->ports[i].num_pins; j++) { + } else if (pb_type_ports[i].type == OUT_PORT) { + pb_graph_node->output_pins[i_output] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + pb_graph_node->num_output_pins[i_output] = pb_type_ports[i].num_pins; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { pb_graph_node->output_pins[i_output][j].pin_number = j; - pb_graph_node->output_pins[i_output][j].port = &pb_type->ports[i]; + pb_graph_node->output_pins[i_output][j].port = &pb_type_ports[i]; pb_graph_node->output_pins[i_output][j].parent_node = pb_graph_node; pb_graph_node->output_pins[i_output][j].pin_count_in_cluster = pin_count_in_cluster; if (pb_graph_node->pb_type->blif_model != nullptr) { @@ -304,12 +314,12 @@ static void alloc_and_load_pb_graph(t_pb_graph_node* pb_graph_node, } i_output++; } else { - VTR_ASSERT(pb_type->ports[i].is_clock && pb_type->ports[i].type == IN_PORT); - pb_graph_node->clock_pins[i_clockport] = new t_pb_graph_pin[pb_type->ports[i].num_pins]; - pb_graph_node->num_clock_pins[i_clockport] = pb_type->ports[i].num_pins; - for (j = 0; j < pb_type->ports[i].num_pins; j++) { + VTR_ASSERT(pb_type_ports[i].is_clock && pb_type_ports[i].type == IN_PORT); + pb_graph_node->clock_pins[i_clockport] = new t_pb_graph_pin[pb_type_ports[i].num_pins]; + pb_graph_node->num_clock_pins[i_clockport] = pb_type_ports[i].num_pins; + for (j = 0; j < pb_type_ports[i].num_pins; j++) { pb_graph_node->clock_pins[i_clockport][j].pin_number = j; - pb_graph_node->clock_pins[i_clockport][j].port = &pb_type->ports[i]; + pb_graph_node->clock_pins[i_clockport][j].port = &pb_type_ports[i]; pb_graph_node->clock_pins[i_clockport][j].parent_node = pb_graph_node; pb_graph_node->clock_pins[i_clockport][j].pin_count_in_cluster = pin_count_in_cluster; if (pb_graph_node->pb_type->blif_model != nullptr) { diff --git a/vpr/src/pack/prepack.cpp b/vpr/src/pack/prepack.cpp index 3307472ac79..fdd404bbc86 100644 --- a/vpr/src/pack/prepack.cpp +++ b/vpr/src/pack/prepack.cpp @@ -1237,7 +1237,7 @@ static t_pb_graph_node* get_expected_lowest_cost_primitive_for_atom_block_in_pb_ } if (curr_pb_graph_node->pb_type->blif_model != nullptr) { - if (primitive_type_feasible(blk_id, curr_pb_graph_node->pb_type)) { + if (primitive_type_feasible(blk_id, curr_pb_graph_node)) { cur_cost = compute_primitive_base_cost(curr_pb_graph_node); if (best_cost == UNDEFINED || best_cost > cur_cost) { best_cost = cur_cost; diff --git a/vpr/src/timing/read_sdc.cpp b/vpr/src/timing/read_sdc.cpp index 202a3cfe6e0..460867a4d10 100644 --- a/vpr/src/timing/read_sdc.cpp +++ b/vpr/src/timing/read_sdc.cpp @@ -81,7 +81,7 @@ class SdcParseCallback : public sdcparse::Callback { if (cmd.is_virtual) { //Create a virtual clock - tatum::DomainId virtual_clk = tc_.create_clock_domain(cmd.name); + tatum::DomainId virtual_clk = tc_.create_clock_domain(cmd.name, cmd.inverted); if (sdc_clocks_.count(virtual_clk)) { vpr_throw(VPR_ERROR_SDC, fname_.c_str(), lineno_, @@ -119,12 +119,6 @@ class SdcParseCallback : public sdcparse::Callback { //Create netlist clock tatum::DomainId netlist_clk = tc_.create_clock_domain(clock_name); - if (sdc_clocks_.count(netlist_clk)) { - vpr_throw(VPR_ERROR_SDC, fname_.c_str(), lineno_, - "Found duplicate netlist clock definition for clock '%s' matching target pattern '%s'", - clock_name.c_str(), clock_name_glob_pattern.c_str()); - } - //Set the clock source AtomPinId clock_driver = netlist_.net_driver(clock_net); tatum::NodeId clock_source = lookup_.atom_pin_tnode(clock_driver); @@ -747,6 +741,11 @@ class SdcParseCallback : public sdcparse::Callback { constraint = launch_clock.period; + } else if ((std::fabs(launch_clock.period - capture_clock.period) < EPSILON) && (std::fabs((launch_clock.period / 2) - std::fabs(launch_clock.rise_edge - capture_clock.rise_edge)) < EPSILON) && (std::fabs((launch_clock.period / 2) - std::fabs(launch_clock.fall_edge - capture_clock.fall_edge)) < EPSILON)) { + //The source and sink domains have the same period but are inverted, the constraint is half of the common clock period. + + constraint = (launch_clock.period / 2); + } else if (launch_clock.period < EPSILON || capture_clock.period < EPSILON) { //If either period is 0, the constraint is 0 constraint = 0.; @@ -907,48 +906,67 @@ class SdcParseCallback : public sdcparse::Callback { "Expected clock names or collection via get_clocks"); } - for (const auto& clock_glob_pattern : clock_group.strings) { - std::regex clock_regex = glob_pattern_to_regex(clock_glob_pattern); - - bool found = false; - for (tatum::DomainId domain : tc_.clock_domains()) { - const auto& clock_name = tc_.clock_domain_name(domain); - - // Clock net aliases are built when reading the input circuit file. - // These aliases represent only real clock net names. - // - // If the SDC contains virtual clocks, the name of these does not - // appear in the net aliases data structure, therefore there is no - // need to iterate through the vector and a direct regex match can - // be applied. - // - // Furthermore, a virtual clock name would cause an error as there - // is no net associated with that when getting the net aliases from - // the netlist. - if (tc_.is_virtual_clock(domain)) { - if (std::regex_match(clock_name, clock_regex)) { - found = true; - - domains.insert(domain); - } - } else { - auto net_aliases = netlist_.net_aliases(clock_name); + //There are 2 clock variants: + // * regular - derived from netlist clocks specified in SDC file + // * inverted - copy of regular clock, created as virtual clock + // with 180 degree phase shift and name suffixed with '_negedge' + // Look for both variants + for (int clk_variant = 0; clk_variant < 2; clk_variant++) { + for (const auto& clock_glob_pattern : clock_group.strings) { + std::string clk_gpattern; + switch (clk_variant) { + case 0: + clk_gpattern = clock_glob_pattern; + break; + case 1: + clk_gpattern = clock_glob_pattern + "_negedge"; + break; + default: + clk_gpattern = clock_glob_pattern; + break; + } + std::regex clock_regex = glob_pattern_to_regex(clk_gpattern); - for (const auto& alias : net_aliases) { - if (std::regex_match(alias, clock_regex)) { + bool found = false; + for (tatum::DomainId domain : tc_.clock_domains()) { + const auto& clock_name = tc_.clock_domain_name(domain); + + // Clock net aliases are built when reading the input circuit file. + // These aliases represent only real clock net names. + // + // If the SDC contains virtual clocks, the name of these does not + // appear in the net aliases data structure, therefore there is no + // need to iterate through the vector and a direct regex match can + // be applied. + // + // Furthermore, a virtual clock name would cause an error as there + // is no net associated with that when getting the net aliases from + // the netlist. + if (tc_.is_virtual_clock(domain)) { + if (std::regex_match(clock_name, clock_regex)) { found = true; domains.insert(domain); - // Exit the inner loop as the net is already being constrained - break; + } + } else { + auto net_aliases = netlist_.net_aliases(clock_name); + + for (const auto& alias : net_aliases) { + if (std::regex_match(alias, clock_regex)) { + found = true; + + domains.insert(domain); + // Exit the inner loop as the net is already being constrained + break; + } } } - } - if (!found) { - VTR_LOGF_WARN(fname_.c_str(), lineno_, - "get_clocks target name or pattern '%s' matched no clocks\n", - clock_glob_pattern.c_str()); + if (!found) { + VTR_LOGF_WARN(fname_.c_str(), lineno_, + "get_clocks target name or pattern '%s' matched no clocks\n", + clk_gpattern.c_str()); + } } } } diff --git a/vpr/src/timing/timing_graph_builder.cpp b/vpr/src/timing/timing_graph_builder.cpp index c0462429648..56c8cd798e6 100644 --- a/vpr/src/timing/timing_graph_builder.cpp +++ b/vpr/src/timing/timing_graph_builder.cpp @@ -351,7 +351,10 @@ void TimingGraphBuilder::add_io_to_timing_graph(const AtomBlockId blk) { } } - NodeId tnode = tg_->add_node(node_type); + AtomPortId port = netlist_.pin_port(pin); + const t_model_ports* model_port = netlist_.port_model(port); + + NodeId tnode = tg_->add_node(node_type, (tatum::TriggeringEdge)model_port->trigg_edge); netlist_lookup_.set_atom_pin_tnode(pin, tnode, BlockTnode::EXTERNAL); } @@ -413,21 +416,21 @@ std::set TimingGraphBuilder::create_block_timing_nodes(const Atom VTR_ASSERT(!model_port->is_clock); if (model_port->clock.empty()) { //No clock => combinational input - tnode = tg_->add_node(NodeType::IPIN); + tnode = tg_->add_node(NodeType::IPIN, (tatum::TriggeringEdge)model_port->trigg_edge); //A combinational pin is really both internal and external, mark it internal here //and external in the default case below netlist_lookup_.set_atom_pin_tnode(input_pin, tnode, BlockTnode::INTERNAL); } else { //This is a sequential data input (i.e. a sequential data capture point/timing path end-point) - tnode = tg_->add_node(NodeType::SINK); + tnode = tg_->add_node(NodeType::SINK, (tatum::TriggeringEdge)model_port->trigg_edge); if (!model_port->combinational_sink_ports.empty()) { //There is an internal combinational connection starting at this sequential input //pin. This is a new timing path and hence we must create a new SOURCE node. //Create the internal source - NodeId internal_tnode = tg_->add_node(NodeType::SOURCE); + NodeId internal_tnode = tg_->add_node(NodeType::SOURCE, (tatum::TriggeringEdge)model_port->trigg_edge); netlist_lookup_.set_atom_pin_tnode(input_pin, internal_tnode, BlockTnode::INTERNAL); } } @@ -452,7 +455,7 @@ std::set TimingGraphBuilder::create_block_timing_nodes(const Atom VTR_ASSERT(model_port->is_clock); VTR_ASSERT(model_port->clock.empty()); - NodeId tnode = tg_->add_node(NodeType::CPIN); + NodeId tnode = tg_->add_node(NodeType::CPIN, (tatum::TriggeringEdge)model_port->trigg_edge); netlist_lookup_.set_atom_pin_tnode(clock_pin, tnode, BlockTnode::EXTERNAL); } @@ -468,7 +471,7 @@ std::set TimingGraphBuilder::create_block_timing_nodes(const Atom NodeId tnode; if (is_netlist_clock_source(output_pin)) { //A generated clock source - tnode = tg_->add_node(NodeType::SOURCE); + tnode = tg_->add_node(NodeType::SOURCE, (tatum::TriggeringEdge)model_port->trigg_edge); clock_generator_tnodes.insert(tnode); @@ -488,7 +491,7 @@ std::set TimingGraphBuilder::create_block_timing_nodes(const Atom if (model_port->clock.empty()) { //No clock => combinational output - tnode = tg_->add_node(NodeType::OPIN); + tnode = tg_->add_node(NodeType::OPIN, (tatum::TriggeringEdge)model_port->trigg_edge); //A combinational pin is really both internal and external, mark it internal here //and external in the default case below @@ -497,13 +500,13 @@ std::set TimingGraphBuilder::create_block_timing_nodes(const Atom } else { VTR_ASSERT(!model_port->clock.empty()); //Has an associated clock => sequential output - tnode = tg_->add_node(NodeType::SOURCE); + tnode = tg_->add_node(NodeType::SOURCE, (tatum::TriggeringEdge)model_port->trigg_edge); if (output_ports_used_as_combinational_sinks.count(model_port->name)) { //There is a combinational path within the primitive terminating at this sequential output //Create the internal sink node - NodeId internal_tnode = tg_->add_node(NodeType::SINK); + NodeId internal_tnode = tg_->add_node(NodeType::SINK, (tatum::TriggeringEdge)model_port->trigg_edge); netlist_lookup_.set_atom_pin_tnode(output_pin, internal_tnode, BlockTnode::INTERNAL); } } diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index fde540f78c2..88e2228fef8 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -906,6 +906,93 @@ bool primitive_type_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_t return true; } +bool primitive_type_feasible(const AtomBlockId blk_id, t_pb_graph_node* curr_pb_graph_node) { + t_pb_type* cur_pb_type = curr_pb_graph_node->pb_type; + + if (cur_pb_type == nullptr) { + return false; + } + + auto& atom_ctx = g_vpr_ctx.atom(); + //Model does not match + if (cur_pb_type->model != atom_ctx.nlist.block_model(blk_id)) { + //Check if the AtomBlock is related to FF (LATCH model) + if ((strcmp(atom_ctx.nlist.block_model(blk_id)->name, MODEL_LATCH) == 0) && (strcmp(atom_ctx.nlist.block_model(blk_id)->name, cur_pb_type->model->name) == 0)) { + //Special case for .latch: this model exists in 2 variations which are + //defined one after another in linked list, check if the second variant match + if (cur_pb_type->model->next == atom_ctx.nlist.block_model(blk_id) && atom_ctx.nlist.block_model(blk_id)->inputs[LATCH_CLOCK_INPUT_ID].trigg_edge == TriggeringEdge::FALLING_EDGE) { + // Next primitive matched AtomBlock + // VPR will need data in secondary pin structs in curr_pb_graph_node + // Fill those if they are empty + if (!curr_pb_graph_node->has_secondary) + curr_pb_graph_node->update_pins(); + } else { + //Next primitive and atom do not match + return false; + } + } else { + //Primitive and atom do not match + return false; + } + } + + VTR_ASSERT_MSG(atom_ctx.nlist.is_compressed(), "This function assumes a compressed/non-dirty netlist"); + + //Keep track of how many atom ports were checked. + // + //We need to do this since we iterate over the pb's ports and + //may miss some atom ports if there is a mismatch + size_t checked_ports = 0; + + //Look at each port on the pb and find the associated port on the + //atom. To be feasible the pb must have as many pins on each port + //as the atom requires + for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { + t_port* pb_port; + if (curr_pb_graph_node->has_secondary) + pb_port = &cur_pb_type->ports_sec[iport]; + else + pb_port = &cur_pb_type->ports[iport]; + + const t_model_ports* pb_model_port = pb_port->model_port; + + //Find the matching port on the atom + auto port_id = atom_ctx.nlist.find_atom_port(blk_id, pb_model_port); + + if (port_id) { //Port is used by the atom + + //In compressed form the atom netlist stores only in-use pins, + //so we can query the number of required pins directly + int required_atom_pins = atom_ctx.nlist.port_pins(port_id).size(); + + int available_pb_pins = pb_port->num_pins; + + if (available_pb_pins < required_atom_pins) { + //Too many pins required + return false; + } + + //Note that this port was checked + ++checked_ports; + } + } + + //Similarly to pins, only in-use ports are stored in the compressed + //atom netlist, so we can figure out how many ports should have been + //checked directly + size_t atom_ports = atom_ctx.nlist.block_ports(blk_id).size(); + + //See if all the atom ports were checked + if (checked_ports != atom_ports) { + VTR_ASSERT(checked_ports < atom_ports); + //Required atom port was missing from physical primitive + return false; + } + + //Feasible + return true; +} + //Returns the sibling atom of a memory slice pb // Note that the pb must be part of a MEMORY_CLASS AtomBlockId find_memory_sibling(const t_pb* pb) { @@ -929,6 +1016,8 @@ AtomBlockId find_memory_sibling(const t_pb* pb) { /** * Return pb_graph_node pin from model port and pin + * Decide pins of which set (primary or secondary) should be used + * based on triggering edge of t_model_ports (defaults to primary pins) * NULL if not found */ t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node) { @@ -936,20 +1025,32 @@ t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* m if (model_port->dir == IN_PORT) { if (model_port->is_clock == false) { + t_pb_graph_pin** input_pins; + if (model_port->trigg_edge == TriggeringEdge::FALLING_EDGE) + input_pins = pb_graph_node->input_pins_sec; + else + input_pins = pb_graph_node->input_pins; + for (i = 0; i < pb_graph_node->num_input_ports; i++) { - if (pb_graph_node->input_pins[i][0].port->model_port == model_port) { + if (input_pins[i][0].port->model_port == model_port) { if (pb_graph_node->num_input_pins[i] > model_pin) { - return &pb_graph_node->input_pins[i][model_pin]; + return &input_pins[i][model_pin]; } else { return nullptr; } } } } else { + t_pb_graph_pin** clock_pins; + if (model_port->trigg_edge == TriggeringEdge::FALLING_EDGE) + clock_pins = pb_graph_node->clock_pins_sec; + else + clock_pins = pb_graph_node->clock_pins; + for (i = 0; i < pb_graph_node->num_clock_ports; i++) { - if (pb_graph_node->clock_pins[i][0].port->model_port == model_port) { + if (clock_pins[i][0].port->model_port == model_port) { if (pb_graph_node->num_clock_pins[i] > model_pin) { - return &pb_graph_node->clock_pins[i][model_pin]; + return &clock_pins[i][model_pin]; } else { return nullptr; } @@ -958,10 +1059,16 @@ t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* m } } else { VTR_ASSERT(model_port->dir == OUT_PORT); + t_pb_graph_pin** output_pins; + if (model_port->trigg_edge == TriggeringEdge::FALLING_EDGE) + output_pins = pb_graph_node->output_pins_sec; + else + output_pins = pb_graph_node->output_pins; + for (i = 0; i < pb_graph_node->num_output_ports; i++) { - if (pb_graph_node->output_pins[i][0].port->model_port == model_port) { + if (output_pins[i][0].port->model_port == model_port) { if (pb_graph_node->num_output_pins[i] > model_pin) { - return &pb_graph_node->output_pins[i][model_pin]; + return &output_pins[i][model_pin]; } else { return nullptr; } @@ -1011,7 +1118,14 @@ const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLo VTR_ASSERT(pb_gnode); //The graph node and pin/block should agree on the model they represent - VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); + if (strcmp(netlist.block_model(blk_id)->name, MODEL_LATCH) == 0) { + if (netlist.block_model(blk_id)->inputs->trigg_edge == TriggeringEdge::FALLING_EDGE) + VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model_sec); + else + VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); + } else { + VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); + } //Get the pin index AtomPortId port_id = netlist.pin_port(pin_id); diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index c4c61692399..7f64ffd797f 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -108,7 +108,12 @@ t_logical_block_type_ptr infer_logic_block_type(const DeviceGrid& grid); int get_max_primitives_in_pb_type(t_pb_type* pb_type); int get_max_depth_of_pb_type(t_pb_type* pb_type); int get_max_nets_in_pb_type(const t_pb_type* pb_type); + +//Check whether given pb_type is feasible for use with given atom block bool primitive_type_feasible(AtomBlockId blk_id, const t_pb_type* cur_pb_type); +//Perform the same check whether given pb_type is feasible for use with given atom block +//but also take into account the special case for atom blocks related to FFs clocked at falling edge +bool primitive_type_feasible(AtomBlockId blk_id, t_pb_graph_node* curr_pb_graph_node); t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node); const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id); t_pb_graph_pin* get_pb_graph_node_pin_from_block_pin(ClusterBlockId iblock, int ipin); diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 5a0d727558f..7a9e787e72e 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -29,13 +29,14 @@ TEST_CASE("read_interchange_models", "[vpr]") { REQUIRE(models.size() == 0); - std::unordered_set lib_models = {MODEL_INPUT, MODEL_OUTPUT, MODEL_LATCH, MODEL_NAMES}; + std::vector lib_models = {MODEL_INPUT, MODEL_OUTPUT, MODEL_LATCH, MODEL_LATCH, MODEL_NAMES}; // Check that there are exactly the expected models for (auto* model = arch.model_library; model != nullptr; model = model->next) { std::string name = model->name; - REQUIRE(lib_models.find(name) != lib_models.end()); - lib_models.erase(name); + auto it = std::find(lib_models.begin(), lib_models.end(), name); + REQUIRE(it != lib_models.end()); + lib_models.erase(it); } REQUIRE(lib_models.size() == 0); diff --git a/vtr_flow/arch/timing/EArch.xml b/vtr_flow/arch/timing/EArch.xml index 8585e14f6d4..48077ef35c5 100644 --- a/vtr_flow/arch/timing/EArch.xml +++ b/vtr_flow/arch/timing/EArch.xml @@ -104,7 +104,7 @@ - + @@ -126,7 +126,7 @@ - + diff --git a/vtr_flow/benchmarks/blif/multiclock/multiclock.blif b/vtr_flow/benchmarks/blif/multiclock/multiclock.blif index 3dc5f636365..b147b62096b 100644 --- a/vtr_flow/benchmarks/blif/multiclock/multiclock.blif +++ b/vtr_flow/benchmarks/blif/multiclock/multiclock.blif @@ -3,7 +3,7 @@ .outputs out1 out2 out3 .latch to_FFA FFA re clk 0 -.latch to_FFC FFC re clk 0 +.latch to_FFC FFC fe clk 0 .latch to_FFD FFD re clk2 0 .latch to_FFB FFB re clk2 0 @@ -27,4 +27,4 @@ .names in3 out3 1 1 -.end \ No newline at end of file +.end