diff --git a/vpr/src/base/netlist_writer.cpp b/vpr/src/base/netlist_writer.cpp index 2cc43788007..53e79a871d9 100644 --- a/vpr/src/base/netlist_writer.cpp +++ b/vpr/src/base/netlist_writer.cpp @@ -1,36 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vtr_assert.h" -#include "vtr_util.h" -#include "vtr_log.h" -#include "vtr_logic.h" -#include "vtr_version.h" - -#include "vpr_error.h" -#include "vpr_types.h" - -#include "read_blif.h" - -#include "netlist_walker.h" -#include "netlist_writer.h" - -#include "globals.h" -#include "atom_netlist.h" -#include "atom_netlist_utils.h" -#include "logic_vec.h" - /** * @file * @@ -89,17 +56,103 @@ * simulation. */ +#include "netlist_writer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atom_netlist.h" +#include "atom_netlist_utils.h" +#include "globals.h" +#include "logic_vec.h" +#include "netlist_walker.h" +#include "read_blif.h" +#include "tatum/TimingGraph.hpp" +#include "tatum/TimingGraphFwd.hpp" +#include "vpr_error.h" +#include "vpr_types.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_logic.h" +#include "vtr_version.h" + /* Enable for extra output while calculating LUT masks */ //#define DEBUG_LUT_MASK +namespace { + // //File local type declarations // +/** + * @brief A triple of delay values (all delays should be in seconds). + * + * For delay values in SDF files, three numbers are specified to describe the + * minimum, typical, and maximum delays along a timing edge. + */ +struct DelayTriple { + DelayTriple() = default; + constexpr DelayTriple(double minimum_sec, double typical_sec, double maximum_sec) + : minimum(minimum_sec) + , typical(typical_sec) + , maximum(maximum_sec) {} + + /// @brief The minimum delay along a timing edge. + double minimum = std::numeric_limits::quiet_NaN(); + /// @brief The typical delay along a timing edge. + double typical = std::numeric_limits::quiet_NaN(); + /// @brief The maximum delay along a timing edge. + double maximum = std::numeric_limits::quiet_NaN(); + + /** + * @brief Returns true if the minimum, typical, and maximum delay values have + * been assigned a number. + * + * These values are defaulted to NaN, so this checks if the values have changed. + */ + inline bool has_value() const { + return !std::isnan(minimum) && !std::isnan(typical) && !std::isnan(maximum); + } + + /** + * @brief Convert the triple into a string. This string will be of the form: + * (minimum:typical:maximum) + * + * This string is expected to be written directly into an SDF file. + * + * Since the delays stored in this struct are implied to be in seconds, this + * print method converts the output into picoseconds. + */ + inline std::string str() const { + VTR_ASSERT_MSG(has_value(), + "Cannot create a non-initialized delay triple string"); + + // Convert the delays to picoseconds for printing. + double minimum_ps = minimum * 1e12; + double typical_ps = typical * 1e12; + double maximum_ps = maximum * 1e12; + + // Create the string. + std::stringstream delay_ss; + delay_ss << '(' << minimum_ps << ':' << typical_ps << ':' << maximum_ps << ')'; + return delay_ss.str(); + } +}; + // This pair cointains the following values: // - double: hold, setup or clock-to-q delays of the port // - string: port name of the associated source clock pin of the sequential port -typedef std::pair sequential_port_delay_pair; +typedef std::pair sequential_port_delay_pair; /*enum class PortType { * IN, @@ -110,8 +163,21 @@ typedef std::pair sequential_port_delay_pair; // // File local function declarations // + +/** + * @brief Get the tco delay triple for the given pb_graph pin. + */ +DelayTriple get_pin_tco_delay_triple(const t_pb_graph_pin& pin); + +/** + * @brief Get the edge delay triple for the given edge, as found in the given + * timing graph. + */ +DelayTriple get_edge_delay_triple(tatum::EdgeId edge_id, + const AnalysisDelayCalculator& delay_calc, + const tatum::TimingGraph& timing_graph); + std::string indent(size_t depth); -double get_delay_ps(double delay_sec); void print_blif_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector& nets, int depth); void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector& nets, PortType type, int depth, struct t_analysis_opts& opts); @@ -138,7 +204,7 @@ class Arc { int src_ipin, /// port_conns, ///::quiet_NaN(), ///::quiet_NaN(), ///::quiet_NaN()) /// port_conns, /// port_connections_; Type type_; vtr::LogicValue initial_value_; - double tcq_; ///input_pins[0][0].tsu; + DelayTriple tsu_triple(tsu, tsu, tsu); //Output (Q) int output_cluster_pin_idx = pb_graph_node->output_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster @@ -1301,7 +1332,7 @@ class NetlistWriterVisitor : public NetlistVisitor { std::string output_net = make_inst_wire(output_atom_net_id, find_tnode(atom, output_cluster_pin_idx), inst_name, PortType::OUTPUT, 0, 0); port_conns["Q"] = output_net; - double tcq = pb_graph_node->output_pins[0][0].tco_max; + DelayTriple tcq_triple = get_pin_tco_delay_triple(pb_graph_node->output_pins[0][0]); //Clock (control) int control_cluster_pin_idx = pb_graph_node->clock_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster @@ -1315,7 +1346,7 @@ class NetlistWriterVisitor : public NetlistVisitor { LatchInst::Type type = LatchInst::Type::RISING_EDGE; vtr::LogicValue init_value = vtr::LogicValue::FALSE; - return std::make_shared(inst_name, port_conns, type, init_value, tcq, tsu); + return std::make_shared(inst_name, port_conns, type, init_value, tcq_triple, tsu_triple); } /** @@ -1395,7 +1426,8 @@ class NetlistWriterVisitor : public NetlistVisitor { } input_port_conns[port_name].push_back(net); - ports_tsu[port_name] = std::make_pair(pin->tsu, pin->associated_clock_pin->port->name); + DelayTriple delay_triple(pin->tsu, pin->tsu, pin->tsu); + ports_tsu[port_name] = std::make_pair(delay_triple, pin->associated_clock_pin->port->name); } } @@ -1431,7 +1463,8 @@ class NetlistWriterVisitor : public NetlistVisitor { "Unrecognized input port class '%s' for primitive '%s' (%s)\n", port_class.c_str(), atom->name, pb_type->name); } output_port_conns[port_name].push_back(net); - ports_tcq[port_name] = std::make_pair(pin->tco_max, pin->associated_clock_pin->port->name); + DelayTriple delay_triple = get_pin_tco_delay_triple(*pin); + ports_tcq[port_name] = std::make_pair(delay_triple, pin->associated_clock_pin->port->name); } } @@ -1487,7 +1520,7 @@ class NetlistWriterVisitor : public NetlistVisitor { params["WIDTH"] = "0"; //Delay matrix[sink_tnode] -> tuple of source_port_name, pin index, delay - std::map>> tnode_delay_matrix; + std::map>> tnode_delay_matrix; //Process the input ports for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) { @@ -1511,12 +1544,11 @@ class NetlistWriterVisitor : public NetlistVisitor { //Delays // - //We record the souce sink tnodes and thier delays here + //We record the source sink tnodes and their delays here for (tatum::EdgeId edge : timing_ctx.graph->node_out_edges(src_tnode)) { - double delay = delay_calc_->max_edge_delay(*timing_ctx.graph, edge); - + DelayTriple delay_triple = get_edge_delay_triple(edge, *delay_calc_, *timing_ctx.graph); auto sink_tnode = timing_ctx.graph->edge_sink_node(edge); - tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay); + tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay_triple); } } @@ -1547,8 +1579,8 @@ class NetlistWriterVisitor : public NetlistVisitor { for (auto& data_tuple : tnode_delay_matrix[inode]) { auto src_name = std::get<0>(data_tuple); auto src_ipin = std::get<1>(data_tuple); - auto delay = std::get<2>(data_tuple); - timing_arcs.emplace_back(src_name, src_ipin, port->name, ipin, delay); + auto delay_triple = std::get<2>(data_tuple); + timing_arcs.emplace_back(src_name, src_ipin, port->name, ipin, delay_triple); } } @@ -1583,7 +1615,7 @@ class NetlistWriterVisitor : public NetlistVisitor { params["WIDTH"] = "0"; //Delay matrix[sink_tnode] -> tuple of source_port_name, pin index, delay - std::map>> tnode_delay_matrix; + std::map>> tnode_delay_matrix; //Process the input ports for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) { @@ -1614,10 +1646,9 @@ class NetlistWriterVisitor : public NetlistVisitor { // //We record the souce sink tnodes and thier delays here for (tatum::EdgeId edge : timing_ctx.graph->node_out_edges(src_tnode)) { - double delay = delay_calc_->max_edge_delay(*timing_ctx.graph, edge); - + DelayTriple delay_triple = get_edge_delay_triple(edge, *delay_calc_, *timing_ctx.graph); auto sink_tnode = timing_ctx.graph->edge_sink_node(edge); - tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay); + tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay_triple); } } @@ -1687,7 +1718,7 @@ class NetlistWriterVisitor : public NetlistVisitor { std::map ports_tcq; //Delay matrix[sink_tnode] -> tuple of source_port_name, pin index, delay - std::map>> tnode_delay_matrix; + std::map>> tnode_delay_matrix; //Process the input ports for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) { @@ -1713,17 +1744,22 @@ class NetlistWriterVisitor : public NetlistVisitor { // //We record the source's sink tnodes and their delays here for (tatum::EdgeId edge : timing_ctx.graph->node_out_edges(src_tnode)) { - double delay = delay_calc_->max_edge_delay(*timing_ctx.graph, edge); - + DelayTriple delay_triple = get_edge_delay_triple(edge, *delay_calc_, *timing_ctx.graph); auto sink_tnode = timing_ctx.graph->edge_sink_node(edge); - tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay); + tnode_delay_matrix[sink_tnode].emplace_back(port->name, ipin, delay_triple); } } input_port_conns[port->name].push_back(net); if (pin->type == PB_PIN_SEQUENTIAL) { - if (!std::isnan(pin->tsu)) ports_tsu[port->name] = std::make_pair(pin->tsu, pin->associated_clock_pin->port->name); - if (!std::isnan(pin->thld)) ports_thld[port->name] = std::make_pair(pin->thld, pin->associated_clock_pin->port->name); + if (!std::isnan(pin->tsu)) { + DelayTriple delay_triple(pin->tsu, pin->tsu, pin->tsu); + ports_tsu[port->name] = std::make_pair(delay_triple, pin->associated_clock_pin->port->name); + } + if (!std::isnan(pin->thld)) { + DelayTriple delay_triple(pin->thld, pin->thld, pin->thld); + ports_thld[port->name] = std::make_pair(delay_triple, pin->associated_clock_pin->port->name); + } } } } @@ -1757,7 +1793,10 @@ class NetlistWriterVisitor : public NetlistVisitor { } output_port_conns[port->name].push_back(net); - if (pin->type == PB_PIN_SEQUENTIAL && !std::isnan(pin->tco_max)) ports_tcq[port->name] = std::make_pair(pin->tco_max, pin->associated_clock_pin->port->name); + if (pin->type == PB_PIN_SEQUENTIAL && !std::isnan(pin->tco_max)) { + DelayTriple delay_triple = get_pin_tco_delay_triple(*pin); + ports_tcq[port->name] = std::make_pair(delay_triple, pin->associated_clock_pin->port->name); + } } } @@ -2080,16 +2119,14 @@ class NetlistWriterVisitor : public NetlistVisitor { return name; } - ///@brief Returns the delay in pico-seconds from source_tnode to sink_tnode - double get_delay_ps(tatum::NodeId source_tnode, tatum::NodeId sink_tnode) { + ///@brief Returns the delay triple from source_tnode to sink_tnode + DelayTriple get_src_to_sink_delay_triple(tatum::NodeId source_tnode, tatum::NodeId sink_tnode) { auto& timing_ctx = g_vpr_ctx.timing(); tatum::EdgeId edge = timing_ctx.graph->find_edge(source_tnode, sink_tnode); VTR_ASSERT(edge); - double delay_sec = delay_calc_->max_edge_delay(*timing_ctx.graph, edge); - - return ::get_delay_ps(delay_sec); //Class overload hides file-scope by default + return get_edge_delay_triple(edge, *delay_calc_, *timing_ctx.graph); } private: //Data @@ -2307,50 +2344,33 @@ class MergedNetlistWriterVisitor : public NetlistWriterVisitor { }; // -// Externally Accessible Functions +// File-scope function implementations // -///@brief Main routine for this file. See netlist_writer.h for details. -void netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts) { - std::string verilog_filename = basename + "_post_synthesis.v"; - std::string blif_filename = basename + "_post_synthesis.blif"; - std::string sdf_filename = basename + "_post_synthesis.sdf"; - - VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str()); - VTR_LOG("Writing Implementation Netlist: %s\n", blif_filename.c_str()); - VTR_LOG("Writing Implementation SDF : %s\n", sdf_filename.c_str()); - - std::ofstream verilog_os(verilog_filename); - std::ofstream blif_os(blif_filename); - std::ofstream sdf_os(sdf_filename); - - NetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc, opts); - - NetlistWalker nl_walker(visitor); - - nl_walker.walk(); +DelayTriple get_pin_tco_delay_triple(const t_pb_graph_pin& pin) { + DelayTriple delay_triple; + delay_triple.minimum = pin.tco_min; + delay_triple.maximum = pin.tco_max; + // Since Tatum does not provide typical delays, set it to be the average + // of min and max. + delay_triple.typical = (pin.tco_min + pin.tco_max) / 2.0; + return delay_triple; } -///@brief Main routine for this file. See netlist_writer.h for details. -void merged_netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts) { - std::string verilog_filename = basename + "_merged_post_implementation.v"; - - VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str()); - - std::ofstream verilog_os(verilog_filename); - // Don't write blif and sdf, pass dummy streams - std::ofstream blif_os; - std::ofstream sdf_os; - - MergedNetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc, opts); - - NetlistWalker nl_walker(visitor); - - nl_walker.walk(); +DelayTriple get_edge_delay_triple(tatum::EdgeId edge_id, + const AnalysisDelayCalculator& delay_calc, + const tatum::TimingGraph& timing_graph) { + double min_edge_delay = delay_calc.min_edge_delay(timing_graph, edge_id); + double max_edge_delay = delay_calc.max_edge_delay(timing_graph, edge_id); + + DelayTriple delay_triple; + delay_triple.minimum = min_edge_delay; + delay_triple.maximum = max_edge_delay; + // Since Tatum does not provide typical delays, set it to be the average + // of min and max. + delay_triple.typical = (min_edge_delay + max_edge_delay) / 2.0; + return delay_triple; } -// -// File-scope function implementations -// ///@brief Returns a blank string for indenting the given depth std::string indent(size_t depth) { @@ -2362,11 +2382,6 @@ std::string indent(size_t depth) { return new_indent; } -///@brief Returns the delay in pico-seconds from a floating point delay -double get_delay_ps(double delay_sec) { - return delay_sec * 1e12; //Scale to picoseconds -} - ///@brief Returns the name of a unique unconnected net std::string create_unconn_net(size_t& unconn_count) { //We increment unconn_count by reference so each @@ -2572,3 +2587,48 @@ std::string escape_sdf_identifier(const std::string identifier) { std::string join_identifier(std::string lhs, std::string rhs) { return lhs + '_' + rhs; } + +} // namespace + +// +// Externally Accessible Functions +// + +///@brief Main routine for this file. See netlist_writer.h for details. +void netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts) { + std::string verilog_filename = basename + "_post_synthesis.v"; + std::string blif_filename = basename + "_post_synthesis.blif"; + std::string sdf_filename = basename + "_post_synthesis.sdf"; + + VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str()); + VTR_LOG("Writing Implementation Netlist: %s\n", blif_filename.c_str()); + VTR_LOG("Writing Implementation SDF : %s\n", sdf_filename.c_str()); + + std::ofstream verilog_os(verilog_filename); + std::ofstream blif_os(blif_filename); + std::ofstream sdf_os(sdf_filename); + + NetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc, opts); + + NetlistWalker nl_walker(visitor); + + nl_walker.walk(); +} + +///@brief Main routine for this file. See netlist_writer.h for details. +void merged_netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts) { + std::string verilog_filename = basename + "_merged_post_implementation.v"; + + VTR_LOG("Writing Merged Implementation Netlist: %s\n", verilog_filename.c_str()); + + std::ofstream verilog_os(verilog_filename); + // Don't write blif and sdf, pass dummy streams + std::ofstream blif_os; + std::ofstream sdf_os; + + MergedNetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc, opts); + + NetlistWalker nl_walker(visitor); + + nl_walker.walk(); +} diff --git a/vpr/src/base/netlist_writer.h b/vpr/src/base/netlist_writer.h index 8a8a19976e1..bb1c0fba202 100644 --- a/vpr/src/base/netlist_writer.h +++ b/vpr/src/base/netlist_writer.h @@ -1,29 +1,29 @@ -#ifndef NETLIST_WRITER_H -#define NETLIST_WRITER_H +#pragma once + #include #include -#include - -#include "vtr_logic.h" - #include "AnalysisDelayCalculator.h" /** * @brief Writes out the post-synthesis implementation netlists in BLIF and Verilog formats, * along with an SDF for delay annotations. * + * Here, post-synthesis implementation netlist is the netlist as it appears after + * routing (i.e. implementation is complete). + * * All written filenames end in {basename}_post_synthesis.{fmt} where {basename} is the * basename argument and {fmt} is the file format (e.g. v, blif, sdf) */ -void netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts); +void netlist_writer(const std::string basename, std::shared_ptr delay_calc, t_analysis_opts opts); /** * @brief Writes out the post implementation netlist in Verilog format. * It has its top module ports merged into multi-bit ones. * + * Here, post-synthesis implementation netlist is the netlist as it appears after + * routing (i.e. implementation is complete). + * * Written filename ends in {basename}_merged_post_implementation.v where {basename} is the * basename argument. */ -void merged_netlist_writer(const std::string basename, std::shared_ptr delay_calc, struct t_analysis_opts opts); - -#endif +void merged_netlist_writer(const std::string basename, std::shared_ptr delay_calc, t_analysis_opts opts);