diff --git a/docs/source/manual/file_formats/repack_design_constraints.rst b/docs/source/manual/file_formats/repack_design_constraints.rst index 5771123c2..56f40c310 100644 --- a/docs/source/manual/file_formats/repack_design_constraints.rst +++ b/docs/source/manual/file_formats/repack_design_constraints.rst @@ -3,6 +3,11 @@ Repack Design Constraints (.xml) -------------------------------- +.. warning:: For the best practice, current repack design constraints only support the net remapping between pins in the same port. Pin constraints are **NOT** allowed for two separated ports. + + - A legal pin constraint example: when there are two clock nets, ``clk0`` and ``clk1``, pin constraints are forced on two pins in a clock port ``clk[0:2]`` (e.g., ``clk[0] = clk0`` and ``clk[1] == clk1``). + - An **illegal** pin constraint example: when there are two clock nets, ``clk0`` and ``clk1``, pin constraints are forced on two clock ports ``clkA[0]`` and ``clkB[0]`` (e.g., ``clkA[0] = clk0`` and ``clkB[0] == clk1``). + An example of design constraints is shown as follows. .. code-block:: xml @@ -25,5 +30,5 @@ An example of design constraints is shown as follows. .. option:: net="" The net name of the pin to be mapped, which should be consistent with net definition in your ``.blif`` file. The reserved word ``OPEN`` means that no net should be mapped to a given pin. Please ensure that it is not conflicted with any net names in your ``.blif`` file. - + .. warning:: Design constraints is a feature for power-users. It may cause repack to fail. It is users's responsibility to ensure proper design constraints diff --git a/libopenfpga/librepackdc/src/repack_design_constraints.cpp b/libopenfpga/librepackdc/src/repack_design_constraints.cpp index d19c1ba71..c32ad6bb3 100644 --- a/libopenfpga/librepackdc/src/repack_design_constraints.cpp +++ b/libopenfpga/librepackdc/src/repack_design_constraints.cpp @@ -50,6 +50,20 @@ std::string RepackDesignConstraints::net(const RepackDesignConstraintId& repack_ return repack_design_constraint_nets_[repack_design_constraint_id]; } +std::string RepackDesignConstraints::find_constrained_pin_net(const std::string& pb_type, + const openfpga::BasicPort& pin) const { + std::string constrained_net_name; + for (const RepackDesignConstraintId& design_constraint : design_constraints()) { + /* If found a constraint, record the net name */ + if ( (pb_type == repack_design_constraint_pb_types_[design_constraint]) + && (pin == repack_design_constraint_pins_[design_constraint])) { + constrained_net_name = repack_design_constraint_nets_[design_constraint]; + break; + } + } + return constrained_net_name; +} + bool RepackDesignConstraints::empty() const { return 0 == repack_design_constraint_ids_.size(); } @@ -106,3 +120,11 @@ void RepackDesignConstraints::set_net(const RepackDesignConstraintId& repack_des bool RepackDesignConstraints::valid_design_constraint_id(const RepackDesignConstraintId& design_constraint_id) const { return ( size_t(design_constraint_id) < repack_design_constraint_ids_.size() ) && ( design_constraint_id == repack_design_constraint_ids_[design_constraint_id] ); } + +bool RepackDesignConstraints::unconstrained_net(const std::string& net) const { + return net.empty(); +} + +bool RepackDesignConstraints::unmapped_net(const std::string& net) const { + return std::string(REPACK_DESIGN_CONSTRAINT_OPEN_NET) == net; +} diff --git a/libopenfpga/librepackdc/src/repack_design_constraints.h b/libopenfpga/librepackdc/src/repack_design_constraints.h index c32a0aca8..8c1ca6415 100644 --- a/libopenfpga/librepackdc/src/repack_design_constraints.h +++ b/libopenfpga/librepackdc/src/repack_design_constraints.h @@ -61,6 +61,10 @@ class RepackDesignConstraints { /* Get the net to be constrained */ std::string net(const RepackDesignConstraintId& repack_design_constraint_id) const; + /* Find a constrained net */ + std::string find_constrained_pin_net(const std::string& pb_type, + const openfpga::BasicPort& pin) const; + /* Check if there are any design constraints */ bool empty() const; @@ -86,6 +90,20 @@ class RepackDesignConstraints { public: /* Public invalidators/validators */ bool valid_design_constraint_id(const RepackDesignConstraintId& repack_design_constraint_id) const; + /* Show if the net has no constraints (free to map to any pin) + * This function is used to identify the net name returned by APIs: + * - find_constrained_pin_net() + * - net() + */ + bool unconstrained_net(const std::string& net) const; + + /* Show if the net is defined specifically not to map to any pin + * This function is used to identify the net name returned by APIs: + * - find_constrained_pin_net() + * - net() + */ + bool unmapped_net(const std::string& net) const; + private: /* Internal data */ /* Unique ids for each design constraint */ vtr::vector repack_design_constraint_ids_; diff --git a/openfpga/src/repack/repack.cpp b/openfpga/src/repack/repack.cpp index 3611909bb..5e25179a5 100644 --- a/openfpga/src/repack/repack.cpp +++ b/openfpga/src/repack/repack.cpp @@ -230,6 +230,38 @@ std::vector find_routed_pb_graph_pins_atom_net(const t_pb* pb, return sink_pb_pins; } +/*************************************************************************************** + * This function will find the actual routing traces of the demanded net + * There is a specific search space applied when searching the routing traces: + * - ONLY applicable to the pb_pin of top-level pb_graph_node + * - candidate can be limited to a set of pb pins + ***************************************************************************************/ +static +std::vector find_pb_route_by_atom_net(const t_pb* pb, + const t_pb_graph_pin* source_pb_pin, + const AtomNetId& atom_net_id) { + VTR_ASSERT(true == source_pb_pin->parent_node->is_root()); + + std::vector pb_route_indices; + + for (int pin = 0; pin < pb->pb_graph_node->total_pb_pins; ++pin) { + /* Bypass unused pins */ + if ((0 == pb->pb_route.count(pin)) || (AtomNetId::INVALID() == pb->pb_route.at(pin).atom_net_id)) { + continue; + } + /* Get the driver pb pin id, it must be valid */ + if (atom_net_id != pb->pb_route.at(pin).atom_net_id) { + continue; + } + + if (source_pb_pin->port == pb->pb_route.at(pin).pb_graph_pin->port) { + pb_route_indices.push_back(pin); + } + } + + return pb_route_indices; +} + /*************************************************************************************** * This function will find the actual source_pb_pin that is mapped by packed in the pb route * As the inputs of clustered block may be renamed during routing, @@ -422,18 +454,7 @@ void add_lb_router_nets(LbRouter& lb_router, AtomNetId atom_net_id = pb_pin_mapped_nets[source_pb_pin]; /* Check if the net information is constrained or not */ - std::string constrained_net_name; - for (const RepackDesignConstraintId& design_constraint : design_constraints.design_constraints()) { - /* All the pin must have only 1 bit */ - VTR_ASSERT_SAFE(1 == design_constraints.pin(design_constraint).get_width()); - /* If found a constraint, record the net name */ - if ( (std::string(lb_type->pb_type->name) == design_constraints.pb_type(design_constraint)) - && (std::string(source_pb_pin->port->name) == design_constraints.pin(design_constraint).get_name()) - && (size_t(source_pb_pin->pin_number) == design_constraints.pin(design_constraint).get_lsb())) { - constrained_net_name = design_constraints.net(design_constraint); - break; - } - } + std::string constrained_net_name = design_constraints.find_constrained_pin_net(std::string(lb_type->pb_type->name), BasicPort(std::string(source_pb_pin->port->name), source_pb_pin->pin_number, source_pb_pin->pin_number)); /* Find the constrained net mapped to this pin in clustering results */ AtomNetId constrained_atom_net_id = AtomNetId::INVALID(); @@ -443,16 +464,21 @@ void add_lb_router_nets(LbRouter& lb_router, * - if this is valid net name, find the net id from atom_netlist * and overwrite the atom net id to mapped */ - if (!constrained_net_name.empty()) { - if (std::string(REPACK_DESIGN_CONSTRAINT_OPEN_NET) != constrained_net_name) { - constrained_atom_net_id = atom_ctx.nlist.find_net(constrained_net_name); - if (false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)) { - VTR_LOG_WARN("Invalid net '%s' to be constrained! Will drop the constraint in repacking\n", - constrained_net_name.c_str()); - } + if ( (!design_constraints.unconstrained_net(constrained_net_name)) + && (!design_constraints.unmapped_net(constrained_net_name))) { + constrained_atom_net_id = atom_ctx.nlist.find_net(constrained_net_name); + if (false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)) { + VTR_LOG_WARN("Invalid net '%s' to be constrained! Will drop the constraint in repacking\n", + constrained_net_name.c_str()); + } else { + VTR_ASSERT_SAFE(false == atom_ctx.nlist.valid_net_id(constrained_atom_net_id)); + VTR_LOGV(verbose, + "Accept net '%s' to be constrained on pin '%s[%d]' during repacking\n", + constrained_net_name.c_str(), + source_pb_pin->port->name, + source_pb_pin->pin_number); } - } else { - VTR_ASSERT_SAFE(constrained_net_name.empty()); + } else if (design_constraints.unconstrained_net(constrained_net_name)) { constrained_atom_net_id = atom_net_id; } @@ -486,18 +512,35 @@ void add_lb_router_nets(LbRouter& lb_router, LbRRNodeId source_lb_rr_node = lb_rr_graph.find_node(LB_INTERMEDIATE, source_pb_pin); VTR_ASSERT(true == lb_rr_graph.valid_node_id(source_lb_rr_node)); + /* Output verbose messages for debugging only */ + VTR_LOGV(verbose, + "Pb route for Net %s:\n", + atom_ctx.nlist.net_name(atom_net_id_to_route).c_str()); + /* As the pin remapping is allowed during routing, we should * - Find the routing traces from packing results which is mapped to the net * from the same port (as remapping is allowed for pins in the same port only) * - Find the source pb_graph_pin that drives the routing traces during packing * - Then we can find the sink nodes + * + * When there is a pin constraint applied. The routing trace + * - Find the routing traces from packing results which is mapped to the net + * with the same port constraints */ - std::vector pb_route_indices = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id_to_route); + std::vector pb_route_indices; + if (design_constraints.unconstrained_net(constrained_net_name)) { + pb_route_indices = find_pb_route_remapped_source_pb_pin(pb, source_pb_pin, atom_net_id_to_route); + } else { + VTR_ASSERT_SAFE(!design_constraints.unconstrained_net(constrained_net_name)); + pb_route_indices = find_pb_route_by_atom_net(pb, source_pb_pin, atom_net_id_to_route); + } /* It could happen that the constrained net is NOT used in this clb, we just skip it for routing * For example, a clkB net is never mapped to any ports in the pb that is clocked by clkA net * */ int pb_route_index; if (0 == pb_route_indices.size()) { + VTR_LOGV(verbose, + "Bypass routing due to no routing traces found\n"); continue; } else { VTR_ASSERT(1 == pb_route_indices.size()); @@ -512,9 +555,6 @@ void add_lb_router_nets(LbRouter& lb_router, VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); /* Output verbose messages for debugging only */ - VTR_LOGV(verbose, - "Pb route for Net %s:\n", - atom_ctx.nlist.net_name(atom_net_id_to_route).c_str()); VTR_LOGV(verbose, "Source node:\n\t%s -> %s\n", source_pb_pin->to_string().c_str(), diff --git a/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v b/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v new file mode 100644 index 000000000..a34cfdc91 --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v @@ -0,0 +1,49 @@ +///////////////////////////////////////// +// Functionality: Two 2-input AND with clocked +// and combinational outputs +// Each of which are controlled by different clocks +// Author: Xifan Tang +//////////////////////////////////////// + +`timescale 1ns / 1ps + +module and2_latch_2clock( + a0, + b0, + clk0, + a1, + b1, + clk1, + c0, + d0, + c1, + d1); + +input wire clk0; + +input wire a0; +input wire b0; +output wire c0; +output reg d0; + +input wire clk1; + +input wire a1; +input wire b1; +output wire c1; +output reg d1; + + +assign c0 = a0 & b0; + +always @(posedge clk0) begin + d0 <= c0; +end + +assign c1 = a1 & b1; + +always @(posedge clk1) begin + d1 <= c1; +end + +endmodule diff --git a/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf index 275a32b57..e7cbcb5ab 100644 --- a/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_4clock/config/task.conf @@ -9,17 +9,11 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 -# Due to the limitation in ACE2 which cannot output .blif files -# with correct multi-clock assignments to .latch lines -# We have to use the vpr_blif flow where the .blif is modified -# based on yosys outputs with correct clock assignment! -# TODO: This limitation should be removed and we should use yosys_vpr flow!!! -fpga_flow=vpr_blif -#fpga_flow=yosys_vpr +fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/global_tile_multiclock_example_script.openfpga @@ -32,14 +26,12 @@ openfpga_pin_constraints_file=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/basic_te arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTile4Clk_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.blif -#bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v [SYNTHESIS_PARAM] bench0_top = counter4bit_2clock -bench0_act=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock.act -bench0_verilog=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counter4bit_2clock/counter4bit_2clock_post_yosys.v -bench0_chan_width = 300 +bench1_top = and2_latch_2clock [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test=