Merge pull request #700 from antmicro/vpr-merged-netlist-writer

VPR: add second netlist writer for merged multi-bits ports
This commit is contained in:
tangxifan 2022-06-30 11:04:58 -07:00 committed by GitHub
commit 26fce2511c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 555 additions and 116 deletions

View File

@ -524,10 +524,14 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi
}
analysis_opts.gen_post_synthesis_netlist = Options.Generate_Post_Synthesis_Netlist;
analysis_opts.gen_post_implementation_merged_netlist = Options.Generate_Post_Implementation_Merged_Netlist;
analysis_opts.timing_report_npaths = Options.timing_report_npaths;
analysis_opts.timing_report_detail = Options.timing_report_detail;
analysis_opts.timing_report_skew = Options.timing_report_skew;
analysis_opts.post_synth_netlist_unconn_input_handling = Options.post_synth_netlist_unconn_input_handling;
analysis_opts.post_synth_netlist_unconn_output_handling = Options.post_synth_netlist_unconn_output_handling;
}
static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) {

View File

@ -384,6 +384,31 @@ static void ShowNetlistOpts(const t_netlist_opts& NetlistOpts) {
static void ShowAnalysisOpts(const t_analysis_opts& AnalysisOpts) {
VTR_LOG("AnalysisOpts.gen_post_synthesis_netlist: %s\n", (AnalysisOpts.gen_post_synthesis_netlist) ? "true" : "false");
const auto opts = {
std::make_tuple(&AnalysisOpts.post_synth_netlist_unconn_input_handling, "post_synth_netlist_unconn_input_handling"),
std::make_tuple(&AnalysisOpts.post_synth_netlist_unconn_output_handling, "post_synth_netlist_unconn_output_handling"),
};
for (const auto& opt : opts) {
auto value = *std::get<0>(opt);
VTR_LOG("AnalysisOpts.%s: ", std::get<1>(opt));
switch (value) {
case e_post_synth_netlist_unconn_handling::UNCONNECTED:
VTR_LOG("UNCONNECTED\n");
break;
case e_post_synth_netlist_unconn_handling::NETS:
VTR_LOG("NETS\n");
break;
case e_post_synth_netlist_unconn_handling::GND:
VTR_LOG("GND\n");
break;
case e_post_synth_netlist_unconn_handling::VCC:
VTR_LOG("VCC\n");
break;
default:
VPR_FATAL_ERROR(VPR_ERROR_UNKNOWN, "Unknown post_synth_netlist_unconn_handling\n");
}
}
VTR_LOG("\n");
}

View File

@ -10,6 +10,7 @@
#include <memory>
#include <unordered_set>
#include <cmath>
#include <regex>
#include "vtr_assert.h"
#include "vtr_util.h"
@ -111,7 +112,7 @@ 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<std::string>& nets, int depth);
void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector<std::string>& nets, PortType type, int depth);
void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector<std::string>& nets, PortType type, int depth, struct t_analysis_opts& opts);
std::string create_unconn_net(size_t& unconn_count);
std::string escape_verilog_identifier(const std::string id);
@ -125,6 +126,9 @@ std::string join_identifier(std::string lhs, std::string rhs);
//
//
// Unconnected net prefix
const std::string unconn_prefix = "__vpr__unconn";
//A combinational timing arc
class Arc {
public:
@ -201,13 +205,15 @@ class LutInst : public Instance {
LogicVec lut_mask, ///<The LUT mask representing the logic function
std::string inst_name, ///<The name of this instance
std::map<std::string, std::vector<std::string>> port_conns, ///<The port connections of this instance. Key: port name, Value: connected nets
std::vector<Arc> timing_arc_values) ///<The timing arcs of this instance
std::vector<Arc> timing_arc_values, ///<The timing arcs of this instance
struct t_analysis_opts opts)
: type_("LUT_K")
, lut_size_(lut_size)
, lut_mask_(lut_mask)
, inst_name_(inst_name)
, port_conns_(port_conns)
, timing_arcs_(timing_arc_values) {
, timing_arcs_(timing_arc_values)
, opts_(opts) {
}
//Accessors
@ -232,10 +238,10 @@ class LutInst : public Instance {
VTR_ASSERT(port_conns_.count("out"));
VTR_ASSERT(port_conns_.size() == 2);
print_verilog_port(os, unconn_count, "in", port_conns_["in"], PortType::INPUT, depth + 1);
print_verilog_port(os, unconn_count, "in", port_conns_["in"], PortType::INPUT, depth + 1, opts_);
os << ","
<< "\n";
print_verilog_port(os, unconn_count, "out", port_conns_["out"], PortType::OUTPUT, depth + 1);
print_verilog_port(os, unconn_count, "out", port_conns_["out"], PortType::OUTPUT, depth + 1, opts_);
os << "\n";
os << indent(depth) << ");\n\n";
@ -377,6 +383,7 @@ class LutInst : public Instance {
std::string inst_name_;
std::map<std::string, std::vector<std::string>> port_conns_;
std::vector<Arc> timing_arcs_;
struct t_analysis_opts opts_;
};
class LatchInst : public Instance {
@ -561,7 +568,8 @@ class BlackBoxInst : public Instance {
std::vector<Arc> timing_arcs, ///<Combinational timing arcs
std::map<std::string, sequential_port_delay_pair> ports_tsu, ///<Port setup checks
std::map<std::string, sequential_port_delay_pair> ports_thld, ///<Port hold checks
std::map<std::string, sequential_port_delay_pair> ports_tcq) ///<Port clock-to-q delays
std::map<std::string, sequential_port_delay_pair> ports_tcq, ///<Port clock-to-q delays
struct t_analysis_opts opts)
: type_name_(type_name)
, inst_name_(inst_name)
, params_(params)
@ -571,7 +579,8 @@ class BlackBoxInst : public Instance {
, timing_arcs_(timing_arcs)
, ports_tsu_(ports_tsu)
, ports_thld_(ports_thld)
, ports_tcq_(ports_tcq) {}
, ports_tcq_(ports_tcq)
, opts_(opts) {}
void print_blif(std::ostream& os, size_t& unconn_count, int depth = 0) override {
os << indent(depth) << ".subckt " << type_name_ << " \\"
@ -634,7 +643,7 @@ class BlackBoxInst : public Instance {
for (auto iter = input_port_conns_.begin(); iter != input_port_conns_.end(); ++iter) {
auto& port_name = iter->first;
auto& nets = iter->second;
print_verilog_port(os, unconn_count, port_name, nets, PortType::INPUT, depth + 1);
print_verilog_port(os, unconn_count, port_name, nets, PortType::INPUT, depth + 1, opts_);
if (!(iter == --input_port_conns_.end() && output_port_conns_.empty())) {
os << ",";
}
@ -645,7 +654,7 @@ class BlackBoxInst : public Instance {
for (auto iter = output_port_conns_.begin(); iter != output_port_conns_.end(); ++iter) {
auto& port_name = iter->first;
auto& nets = iter->second;
print_verilog_port(os, unconn_count, port_name, nets, PortType::OUTPUT, depth + 1);
print_verilog_port(os, unconn_count, port_name, nets, PortType::OUTPUT, depth + 1, opts_);
if (!(iter == --output_port_conns_.end())) {
os << ",";
}
@ -679,12 +688,12 @@ class BlackBoxInst : public Instance {
os << indent(depth + 3) << "(IOPATH ";
os << escape_sdf_identifier(arc.source_name());
if (find_port_size(arc.source_name()) > 1) {
os << "[" << arc.source_ipin() << "]";
os << "\\[" << arc.source_ipin() << "\\]";
}
os << " ";
os << escape_sdf_identifier(arc.sink_name());
if (find_port_size(arc.sink_name()) > 1) {
os << "[" << arc.sink_ipin() << "]";
os << "\\[" << arc.sink_ipin() << "\\]";
}
os << " ";
os << delay_triple.str();
@ -756,6 +765,7 @@ class BlackBoxInst : public Instance {
std::map<std::string, sequential_port_delay_pair> ports_tsu_;
std::map<std::string, sequential_port_delay_pair> ports_thld_;
std::map<std::string, sequential_port_delay_pair> ports_tcq_;
struct t_analysis_opts opts_;
};
/**
@ -774,6 +784,11 @@ class Assignment {
void print_verilog(std::ostream& os, std::string indent) {
os << indent << "assign " << escape_verilog_identifier(lval_) << " = " << escape_verilog_identifier(rval_) << ";\n";
}
void print_merged_verilog(std::ostream& os, std::string indent) {
os << indent << "assign " << lval_ << " = " << rval_ << ";\n";
}
void print_blif(std::ostream& os, std::string indent) {
os << indent << ".names " << rval_ << " " << lval_ << "\n";
os << indent << "1 1\n";
@ -794,11 +809,13 @@ class NetlistWriterVisitor : public NetlistVisitor {
NetlistWriterVisitor(std::ostream& verilog_os, ///<Output stream for verilog netlist
std::ostream& blif_os, ///<Output stream for blif netlist
std::ostream& sdf_os, ///<Output stream for SDF
std::shared_ptr<const AnalysisDelayCalculator> delay_calc)
std::shared_ptr<const AnalysisDelayCalculator> delay_calc,
struct t_analysis_opts opts)
: verilog_os_(verilog_os)
, blif_os_(blif_os)
, sdf_os_(sdf_os)
, delay_calc_(delay_calc) {
, delay_calc_(delay_calc)
, opts_(opts) {
auto& atom_ctx = g_vpr_ctx.atom();
//Initialize the pin to tnode look-up
@ -867,12 +884,8 @@ class NetlistWriterVisitor : public NetlistVisitor {
print_sdf();
}
private: //Internal Helper functions
///@brief Writes out the verilog netlist
void print_verilog(int depth = 0) {
verilog_os_ << indent(depth) << "//Verilog generated by VPR " << vtr::VERSION << " from post-place-and-route implementation\n";
verilog_os_ << indent(depth) << "module " << top_module_name_ << " (\n";
protected:
virtual void print_primary_io(int depth) {
//Primary Inputs
for (auto iter = inputs_.begin(); iter != inputs_.end(); ++iter) {
verilog_os_ << indent(depth + 1) << "input " << escape_verilog_identifier(*iter);
@ -881,7 +894,6 @@ class NetlistWriterVisitor : public NetlistVisitor {
}
verilog_os_ << "\n";
}
//Primary Outputs
for (auto iter = outputs_.begin(); iter != outputs_.end(); ++iter) {
verilog_os_ << indent(depth + 1) << "output " << escape_verilog_identifier(*iter);
@ -890,6 +902,22 @@ class NetlistWriterVisitor : public NetlistVisitor {
}
verilog_os_ << "\n";
}
}
virtual void print_assignments(int depth) {
verilog_os_ << "\n";
verilog_os_ << indent(depth + 1) << "//IO assignments\n";
for (auto& assign : assignments_) {
assign.print_verilog(verilog_os_, indent(depth + 1));
}
}
///@brief Writes out the verilog netlist
void print_verilog(int depth = 0) {
verilog_os_ << indent(depth) << "//Verilog generated by VPR " << vtr::VERSION << " from post-place-and-route implementation\n";
verilog_os_ << indent(depth) << "module " << top_module_name_ << " (\n";
print_primary_io(depth);
verilog_os_ << indent(depth) << ");\n";
//Wire declarations
@ -905,11 +933,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
}
//connections between primary I/Os and their internal wires
verilog_os_ << "\n";
verilog_os_ << indent(depth + 1) << "//IO assignments\n";
for (auto& assign : assignments_) {
assign.print_verilog(verilog_os_, indent(depth + 1));
}
print_assignments(depth);
//Interconnect between cell instances
verilog_os_ << "\n";
@ -937,10 +961,21 @@ class NetlistWriterVisitor : public NetlistVisitor {
inst->print_verilog(verilog_os_, unconn_count, depth + 1);
}
//Unconnected wires
if (unconn_count) {
verilog_os_ << "\n";
verilog_os_ << indent(depth + 1) << "//Unconnected wires\n";
for (size_t i = 0; i < unconn_count; ++i) {
auto name = unconn_prefix + std::to_string(i);
verilog_os_ << indent(depth + 1) << "wire " << escape_verilog_identifier(name) << ";\n";
}
}
verilog_os_ << "\n";
verilog_os_ << indent(depth) << "endmodule\n";
}
private: //Internal Helper functions
///@brief Writes out the blif netlist
void print_blif(int depth = 0) {
blif_os_ << indent(depth) << "#BLIF generated by VPR " << vtr::VERSION << " from post-place-and-route implementation\n";
@ -1045,47 +1080,6 @@ class NetlistWriterVisitor : public NetlistVisitor {
sdf_os_ << indent(depth) << ")\n";
}
/**
* @brief Returns the name of a wire connecting a primitive and global net.
*
* The wire is recorded and instantiated by the top level output routines.
*/
std::string make_inst_wire(AtomNetId atom_net_id, ///<The id of the net in the atom netlist
tatum::NodeId tnode_id, ///<The tnode associated with the primitive pin
std::string inst_name, ///<The name of the instance associated with the pin
PortType port_type, ///<The port direction
int port_idx, ///<The instance port index
int pin_idx) { ///<The instance pin index
std::string wire_name = inst_name;
if (port_type == PortType::INPUT) {
wire_name = join_identifier(wire_name, "input");
} else if (port_type == PortType::CLOCK) {
wire_name = join_identifier(wire_name, "clock");
} else {
VTR_ASSERT(port_type == PortType::OUTPUT);
wire_name = join_identifier(wire_name, "output");
}
wire_name = join_identifier(wire_name, std::to_string(port_idx));
wire_name = join_identifier(wire_name, std::to_string(pin_idx));
auto value = std::make_pair(wire_name, tnode_id);
if (port_type == PortType::INPUT || port_type == PortType::CLOCK) {
//Add the sink
logical_net_sinks_[atom_net_id].push_back(value);
} else {
//Add the driver
VTR_ASSERT(port_type == PortType::OUTPUT);
auto ret = logical_net_drivers_.insert(std::make_pair(atom_net_id, value));
VTR_ASSERT(ret.second); //Was inserted, drivers are unique
}
return wire_name;
}
/**
* @brief Returns the name of a circuit-level Input/Output
*
@ -1141,6 +1135,48 @@ class NetlistWriterVisitor : public NetlistVisitor {
return io_name;
}
protected:
/**
* @brief Returns the name of a wire connecting a primitive and global net.
*
* The wire is recorded and instantiated by the top level output routines.
*/
std::string make_inst_wire(AtomNetId atom_net_id, ///<The id of the net in the atom netlist
tatum::NodeId tnode_id, ///<The tnode associated with the primitive pin
std::string inst_name, ///<The name of the instance associated with the pin
PortType port_type, ///<The port direction
int port_idx, ///<The instance port index
int pin_idx) { ///<The instance pin index
std::string wire_name = inst_name;
if (port_type == PortType::INPUT) {
wire_name = join_identifier(wire_name, "input");
} else if (port_type == PortType::CLOCK) {
wire_name = join_identifier(wire_name, "clock");
} else {
VTR_ASSERT(port_type == PortType::OUTPUT);
wire_name = join_identifier(wire_name, "output");
}
wire_name = join_identifier(wire_name, std::to_string(port_idx));
wire_name = join_identifier(wire_name, std::to_string(pin_idx));
auto value = std::make_pair(wire_name, tnode_id);
if (port_type == PortType::INPUT || port_type == PortType::CLOCK) {
//Add the sink
logical_net_sinks_[atom_net_id].push_back(value);
} else {
//Add the driver
VTR_ASSERT(port_type == PortType::OUTPUT);
auto ret = logical_net_drivers_.insert(std::make_pair(atom_net_id, value));
VTR_ASSERT(ret.second); //Was inserted, drivers are unique
}
return wire_name;
}
///@brief Returns an Instance object representing the LUT
std::shared_ptr<Instance> make_lut_instance(const t_pb* atom) {
//Determine what size LUT
@ -1213,7 +1249,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
port_conns["out"].push_back(net);
}
auto inst = std::make_shared<LutInst>(lut_size, lut_mask, inst_name, port_conns, timing_arcs);
auto inst = std::make_shared<LutInst>(lut_size, lut_mask, inst_name, port_conns, timing_arcs, opts_);
return inst;
}
@ -1413,7 +1449,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
}
}
return std::make_shared<BlackBoxInst>(type, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq);
return std::make_shared<BlackBoxInst>(type, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq, opts_);
}
///@brief Returns an Instance object representing a Multiplier
@ -1509,7 +1545,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
VTR_ASSERT(pb_graph_node->num_clock_ports == 0); //No clocks
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq);
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq, opts_);
}
///@brief Returns an Instance object representing an Adder
@ -1609,7 +1645,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
}
}
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq);
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq, opts_);
}
std::shared_ptr<Instance> make_blackbox_instance(const t_pb* atom) {
@ -1747,7 +1783,7 @@ class NetlistWriterVisitor : public NetlistVisitor {
attrs[attr.first] = attr.second;
}
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq);
return std::make_shared<BlackBoxInst>(type_name, inst_name, params, attrs, input_port_conns, output_port_conns, timing_arcs, ports_tsu, ports_thld, ports_tcq, opts_);
}
///@brief Returns the top level pb_route associated with the given pb
@ -1756,18 +1792,6 @@ class NetlistWriterVisitor : public NetlistVisitor {
return top_pb->pb_route;
}
///@brief Returns the top complex block which contains the given pb
const t_pb* find_top_cb(const t_pb* curr) {
//Walk up through the pb graph until curr
//has no parent, at which point it will be the top pb
const t_pb* parent = curr->parent_pb;
while (parent != nullptr) {
curr = parent;
parent = curr->parent_pb;
}
return curr;
}
///@brief Returns the tnode ID of the given atom's connected cluster pin
tatum::NodeId find_tnode(const t_pb* atom, int cluster_pin_idx) {
auto& atom_ctx = g_vpr_ctx.atom();
@ -1785,6 +1809,19 @@ class NetlistWriterVisitor : public NetlistVisitor {
return tnode_id;
}
private:
///@brief Returns the top complex block which contains the given pb
const t_pb* find_top_cb(const t_pb* curr) {
//Walk up through the pb graph until curr
//has no parent, at which point it will be the top pb
const t_pb* parent = curr->parent_pb;
while (parent != nullptr) {
curr = parent;
parent = curr->parent_pb;
}
return curr;
}
///@brief Returns a LogicVec representing the LUT mask of the given LUT atom
LogicVec load_lut_mask(size_t num_inputs, //LUT size
const t_pb* atom) { //LUT primitive
@ -2042,13 +2079,15 @@ class NetlistWriterVisitor : public NetlistVisitor {
return ::get_delay_ps(delay_sec); //Class overload hides file-scope by default
}
private: //Data
std::string top_module_name_; ///<Name of the top level module (i.e. the circuit)
private: //Data
std::string top_module_name_; ///<Name of the top level module (i.e. the circuit)
protected:
std::vector<std::string> inputs_; ///<Name of circuit inputs
std::vector<std::string> outputs_; ///<Name of circuit outputs
std::vector<Assignment> assignments_; ///<Set of assignments (i.e. net-to-net connections)
std::vector<std::shared_ptr<Instance>> cell_instances_; ///<Set of cell instances
private:
//Drivers of logical nets.
// Key: logic net id, Value: pair of wire_name and tnode_id
std::map<AtomNetId, std::pair<std::string, tatum::NodeId>> logical_net_drivers_;
@ -2059,7 +2098,10 @@ class NetlistWriterVisitor : public NetlistVisitor {
std::map<std::string, float> logical_net_sink_delays_;
//Output streams
protected:
std::ostream& verilog_os_;
private:
std::ostream& blif_os_;
std::ostream& sdf_os_;
@ -2067,14 +2109,196 @@ class NetlistWriterVisitor : public NetlistVisitor {
std::map<std::pair<ClusterBlockId, int>, tatum::NodeId> pin_id_to_tnode_lookup_;
std::shared_ptr<const AnalysisDelayCalculator> delay_calc_;
struct t_analysis_opts opts_;
};
/**
* @brief A class which writes post-implementation merged netlists (Verilog)
*
* It implements the NetlistVisitor interface used by NetlistWalker (see netlist_walker.h)
*/
class MergedNetlistWriterVisitor : public NetlistWriterVisitor {
public: //Public interface
MergedNetlistWriterVisitor(std::ostream& verilog_os, ///<Output stream for verilog netlist
std::ostream& blif_os, ///<Output stream for blif netlist
std::ostream& sdf_os, ///<Output stream for SDF
std::shared_ptr<const AnalysisDelayCalculator> delay_calc,
struct t_analysis_opts opts)
: NetlistWriterVisitor(verilog_os, blif_os, sdf_os, delay_calc, opts) {}
std::map<std::string, int> portmap;
void visit_atom_impl(const t_pb* atom) override {
auto& atom_ctx = g_vpr_ctx.atom();
auto atom_pb = atom_ctx.lookup.pb_atom(atom);
if (atom_pb == AtomBlockId::INVALID()) {
return;
}
const t_model* model = atom_ctx.nlist.block_model(atom_pb);
if (model->name == std::string(MODEL_INPUT)) {
auto merged_io_name = make_io(atom, PortType::INPUT);
if (merged_io_name != "")
inputs_.emplace_back(merged_io_name);
} else if (model->name == std::string(MODEL_OUTPUT)) {
auto merged_io_name = make_io(atom, PortType::OUTPUT);
if (merged_io_name != "")
outputs_.emplace_back(merged_io_name);
} else if (model->name == std::string(MODEL_NAMES)) {
cell_instances_.push_back(make_lut_instance(atom));
} else if (model->name == std::string(MODEL_LATCH)) {
cell_instances_.push_back(make_latch_instance(atom));
} else if (model->name == std::string("single_port_ram")) {
cell_instances_.push_back(make_ram_instance(atom));
} else if (model->name == std::string("dual_port_ram")) {
cell_instances_.push_back(make_ram_instance(atom));
} else if (model->name == std::string("multiply")) {
cell_instances_.push_back(make_multiply_instance(atom));
} else if (model->name == std::string("adder")) {
cell_instances_.push_back(make_adder_instance(atom));
} else {
cell_instances_.push_back(make_blackbox_instance(atom));
}
}
/**
* @brief Returns the name of circuit-level Input/Output ports with multi-bit
* ports merged into one.
*
* The I/O is recorded and instantiated by the top level output routines
* @param atom The implementation primitive representing the I/O
* @param dir The IO direction
* @param portmap Map for keeping port names and width
*/
std::string make_io(const t_pb* atom,
PortType dir) {
const t_pb_graph_node* pb_graph_node = atom->pb_graph_node;
std::string io_name;
std::string indexed_io_name;
int cluster_pin_idx = -1;
// regex for matching 3 groups:
// * 'out:' - optional
// * verilog identifier - mandatory
// * index - optional
std::string rgx = "(out:)?([a-zA-Z$_]+[a-zA-Z0-9$_]*)(\\[[0-9]+\\])?$";
std::string name(atom->name);
std::regex regex(rgx);
std::smatch matches;
if (dir == PortType::INPUT) {
VTR_ASSERT(pb_graph_node->num_output_ports == 1); //One output port
VTR_ASSERT(pb_graph_node->num_output_pins[0] == 1); //One output pin
cluster_pin_idx = pb_graph_node->output_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster
io_name = "";
indexed_io_name = atom->name;
if (std::regex_match(name, matches, regex)) {
if (std::find(inputs_.begin(), inputs_.end(), matches[2]) == inputs_.end()) { //Skip already existing multi-bit port names
io_name = matches[2];
portmap[matches[2]] = 0;
} else {
portmap[matches[2]]++;
}
}
} else {
VTR_ASSERT(pb_graph_node->num_input_ports == 1); //One input port
VTR_ASSERT(pb_graph_node->num_input_pins[0] == 1); //One input pin
cluster_pin_idx = pb_graph_node->input_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster
//Strip off the starting 'out:' that vpr adds to uniqify outputs
//this makes the port names match the input blif file
io_name = "";
indexed_io_name = atom->name + 4;
if (std::regex_search(name, matches, regex)) {
if (std::find(outputs_.begin(), outputs_.end(), matches[2]) == outputs_.end()) { //Skip already existing multi-bit port names
portmap[matches[2]] = 0;
io_name = matches[2];
} else {
portmap[matches[2]]++;
}
}
}
const auto& top_pb_route = find_top_pb_route(atom);
if (top_pb_route.count(cluster_pin_idx)) {
//Net exists
auto atom_net_id = top_pb_route[cluster_pin_idx].atom_net_id; //Connected net in atom netlist
//Port direction is inverted (inputs drive internal nets, outputs sink internal nets)
PortType wire_dir = (dir == PortType::INPUT) ? PortType::OUTPUT : PortType::INPUT;
//Look up the tnode associated with this pin (used for delay calculation)
tatum::NodeId tnode_id = find_tnode(atom, cluster_pin_idx);
auto wire_name = make_inst_wire(atom_net_id, tnode_id, indexed_io_name, wire_dir, 0, 0);
//Connect the wires to to I/Os with assign statements
if (wire_dir == PortType::INPUT) {
assignments_.emplace_back(indexed_io_name, escape_verilog_identifier(wire_name));
} else {
assignments_.emplace_back(escape_verilog_identifier(wire_name), indexed_io_name);
}
}
return io_name;
}
void print_primary_io(int depth) {
//Primary Inputs
for (auto iter = inputs_.begin(); iter != inputs_.end(); ++iter) {
//verilog_os_ << indent(depth + 1) << "input " << escape_verilog_identifier(*iter);
std::string range;
if (portmap[*iter] > 0)
verilog_os_ << indent(depth + 1) << "input [" << portmap[*iter] << ":0] " << *iter;
else
verilog_os_ << indent(depth + 1) << "input " << *iter;
if (iter + 1 != inputs_.end() || outputs_.size() > 0) {
verilog_os_ << ",";
}
verilog_os_ << "\n";
}
//Primary Outputs
for (auto iter = outputs_.begin(); iter != outputs_.end(); ++iter) {
std::string range;
if (portmap[*iter] > 0)
verilog_os_ << indent(depth + 1) << "output [" << portmap[*iter] << ":0] " << *iter;
else
verilog_os_ << indent(depth + 1) << "output " << *iter;
if (iter + 1 != outputs_.end()) {
verilog_os_ << ",";
}
verilog_os_ << "\n";
}
}
void print_assignments(int depth) {
verilog_os_ << "\n";
verilog_os_ << indent(depth + 1) << "//IO assignments\n";
for (auto& assign : assignments_) {
assign.print_merged_verilog(verilog_os_, indent(depth + 1));
}
}
void finish_impl() override {
// Don't write to blif and sdf streams
print_verilog();
}
};
//
// Externally Accessible Functions
//
///@brief Main routing for this file. See netlist_writer.h for details.
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc) {
///@brief Main routine for this file. See netlist_writer.h for details.
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> 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";
@ -2087,13 +2311,30 @@ void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDe
std::ofstream blif_os(blif_filename);
std::ofstream sdf_os(sdf_filename);
NetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc);
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<const AnalysisDelayCalculator> 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();
}
//
// File-scope function implementations
//
@ -2117,7 +2358,7 @@ double get_delay_ps(double delay_sec) {
std::string create_unconn_net(size_t& unconn_count) {
//We increment unconn_count by reference so each
//call generates a unique name
return "__vpr__unconn" + std::to_string(unconn_count++);
return unconn_prefix + std::to_string(unconn_count++);
}
/**
@ -2159,7 +2400,31 @@ void print_blif_port(std::ostream& os, size_t& unconn_count, const std::string&
*
* Handles special cases like multi-bit and disconnected ports
*/
void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector<std::string>& nets, PortType type, int depth) {
void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::string& port_name, const std::vector<std::string>& nets, PortType type, int depth, struct t_analysis_opts& opts) {
auto unconn_inp_name = [&]() {
switch (opts.post_synth_netlist_unconn_input_handling) {
case e_post_synth_netlist_unconn_handling::GND:
return std::string("1'b0");
case e_post_synth_netlist_unconn_handling::VCC:
return std::string("1'b1");
case e_post_synth_netlist_unconn_handling::NETS:
return create_unconn_net(unconn_count);
case e_post_synth_netlist_unconn_handling::UNCONNECTED:
default:
return std::string("1'bX");
}
};
auto unconn_out_name = [&]() {
switch (opts.post_synth_netlist_unconn_output_handling) {
case e_post_synth_netlist_unconn_handling::NETS:
return create_unconn_net(unconn_count);
case e_post_synth_netlist_unconn_handling::UNCONNECTED:
default:
return std::string();
}
};
//Port name
os << indent(depth) << "." << port_name << "(";
@ -2169,40 +2434,58 @@ void print_verilog_port(std::ostream& os, size_t& unconn_count, const std::strin
if (nets[0].empty()) {
//Disconnected
if (type == PortType::INPUT || type == PortType::CLOCK) {
os << "1'b0";
os << unconn_inp_name();
} else {
VTR_ASSERT(type == PortType::OUTPUT);
os << create_unconn_net(unconn_count);
os << unconn_out_name();
}
} else {
//Connected
os << escape_verilog_identifier(nets[0]);
}
} else {
//A multi-bit port, we explicitly concat the single-bit nets to build the port,
//taking care to print MSB on left and LSB on right
os << "{"
<< "\n";
for (int ipin = (int)nets.size() - 1; ipin >= 0; --ipin) { //Reverse order to match endianess
os << indent(depth + 1);
if (nets[ipin].empty()) {
//Disconnected
if (type == PortType::INPUT || type == PortType::CLOCK) {
os << "1'b0";
} else {
VTR_ASSERT(type == PortType::OUTPUT);
os << create_unconn_net(unconn_count);
}
} else {
//Connected
os << escape_verilog_identifier(nets[ipin]);
}
if (ipin != 0) {
os << ",";
os << "\n";
// Check if all pins are unconnected
bool all_unconnected = true;
for (size_t i = 0; i < nets.size(); ++i) {
if (!nets[i].empty()) {
all_unconnected = false;
break;
}
}
os << "}";
//A multi-bit port, we explicitly concat the single-bit nets to build the port,
//taking care to print MSB on left and LSB on right
if (all_unconnected && type == PortType::OUTPUT && opts.post_synth_netlist_unconn_output_handling == e_post_synth_netlist_unconn_handling::UNCONNECTED) {
// Empty connection
} else {
// Individual bits
os << "{"
<< "\n";
for (int ipin = (int)nets.size() - 1; ipin >= 0; --ipin) { //Reverse order to match endianess
os << indent(depth + 1);
if (nets[ipin].empty()) {
//Disconnected
if (type == PortType::INPUT || type == PortType::CLOCK) {
os << unconn_inp_name();
} else {
VTR_ASSERT(type == PortType::OUTPUT);
// When concatenating output connection there cannot
// be an empty placeholder so we have to create a
// dummy net.
os << create_unconn_net(unconn_count);
}
} else {
//Connected
os << escape_verilog_identifier(nets[ipin]);
}
if (ipin != 0) {
os << ",";
}
os << "\n";
}
os << indent(depth) + " }";
}
}
os << ")";
}

View File

@ -15,6 +15,16 @@
* 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<const AnalysisDelayCalculator> delay_calc);
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, struct 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.
*
* 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<const AnalysisDelayCalculator> delay_calc, struct t_analysis_opts opts);
#endif

View File

@ -755,6 +755,75 @@ struct ParseReducer {
return {"min", "max", "median", "arithmean", "geomean"};
}
};
struct ParsePostSynthNetlistUnconnInputHandling {
ConvertedValue<e_post_synth_netlist_unconn_handling> from_str(std::string str) {
ConvertedValue<e_post_synth_netlist_unconn_handling> conv_value;
if (str == "unconnected")
conv_value.set_value(e_post_synth_netlist_unconn_handling::UNCONNECTED);
else if (str == "nets")
conv_value.set_value(e_post_synth_netlist_unconn_handling::NETS);
else if (str == "gnd")
conv_value.set_value(e_post_synth_netlist_unconn_handling::GND);
else if (str == "vcc")
conv_value.set_value(e_post_synth_netlist_unconn_handling::VCC);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_post_synth_netlist_unconn_handling (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_post_synth_netlist_unconn_handling val) {
ConvertedValue<std::string> conv_value;
if (val == e_post_synth_netlist_unconn_handling::NETS)
conv_value.set_value("nets");
else if (val == e_post_synth_netlist_unconn_handling::GND)
conv_value.set_value("gnd");
else if (val == e_post_synth_netlist_unconn_handling::VCC)
conv_value.set_value("vcc");
else {
VTR_ASSERT(val == e_post_synth_netlist_unconn_handling::UNCONNECTED);
conv_value.set_value("unconnected");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"unconnected", "nets", "gnd", "vcc"};
}
};
struct ParsePostSynthNetlistUnconnOutputHandling {
ConvertedValue<e_post_synth_netlist_unconn_handling> from_str(std::string str) {
ConvertedValue<e_post_synth_netlist_unconn_handling> conv_value;
if (str == "unconnected")
conv_value.set_value(e_post_synth_netlist_unconn_handling::UNCONNECTED);
else if (str == "nets")
conv_value.set_value(e_post_synth_netlist_unconn_handling::NETS);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_post_synth_netlist_unconn_handling (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_post_synth_netlist_unconn_handling val) {
ConvertedValue<std::string> conv_value;
if (val == e_post_synth_netlist_unconn_handling::NETS)
conv_value.set_value("nets");
else {
VTR_ASSERT(val == e_post_synth_netlist_unconn_handling::UNCONNECTED);
conv_value.set_value("unconnected");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"unconnected", "nets"};
}
};
argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args) {
std::string description =
@ -1656,6 +1725,13 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<bool, ParseOnOff>(args.Generate_Post_Implementation_Merged_Netlist, "--gen_post_implementation_merged_netlist")
.help(
"Generates the post-implementation netlist with merged top module ports"
" Used for post-implementation simulation and verification")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument(args.timing_report_npaths, "--timing_report_npaths")
.help("Controls how many timing paths are reported.")
.default_value("100")
@ -1675,6 +1751,28 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<e_post_synth_netlist_unconn_handling, ParsePostSynthNetlistUnconnInputHandling>(args.post_synth_netlist_unconn_input_handling, "--post_synth_netlist_unconn_inputs")
.help(
"Controls how unconnected input cell ports are handled in the post-synthesis netlist\n"
" * unconnected: leave unconnected\n"
" * nets: connect each unconnected input pin to its own separate\n"
" undriven net named: __vpr__unconn<ID>, where <ID> is index\n"
" assigned to this occurrence of unconnected port in design\n"
" * gnd: tie all to ground (1'b0)\n"
" * vcc: tie all to VCC (1'b1)\n")
.default_value("unconnected")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<e_post_synth_netlist_unconn_handling, ParsePostSynthNetlistUnconnOutputHandling>(args.post_synth_netlist_unconn_output_handling, "--post_synth_netlist_unconn_outputs")
.help(
"Controls how unconnected output cell ports are handled in the post-synthesis netlist\n"
" * unconnected: leave unconnected\n"
" * nets: connect each unconnected input pin to its own separate\n"
" undriven net named: __vpr__unconn<ID>, where <ID> is index\n"
" assigned to this occurrence of unconnected port in design\n")
.default_value("unconnected")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& power_grp = parser.add_argument_group("power analysis options");
power_grp.add_argument<bool, ParseOnOff>(args.do_power, "--power")

View File

@ -156,9 +156,12 @@ struct t_options {
/* Analysis options */
argparse::ArgValue<bool> full_stats;
argparse::ArgValue<bool> Generate_Post_Synthesis_Netlist;
argparse::ArgValue<bool> Generate_Post_Implementation_Merged_Netlist;
argparse::ArgValue<int> timing_report_npaths;
argparse::ArgValue<e_timing_report_detail> timing_report_detail;
argparse::ArgValue<bool> timing_report_skew;
argparse::ArgValue<e_post_synth_netlist_unconn_handling> post_synth_netlist_unconn_input_handling;
argparse::ArgValue<e_post_synth_netlist_unconn_handling> post_synth_netlist_unconn_output_handling;
};
argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args);

View File

@ -1195,7 +1195,13 @@ void vpr_analysis(t_vpr_setup& vpr_setup, const t_arch& Arch, const RouteStatus&
//Write the post-syntesis netlist
if (vpr_setup.AnalysisOpts.gen_post_synthesis_netlist) {
netlist_writer(atom_ctx.nlist.netlist_name().c_str(), analysis_delay_calc);
netlist_writer(atom_ctx.nlist.netlist_name().c_str(), analysis_delay_calc,
vpr_setup.AnalysisOpts);
}
//Write the post-implementation merged netlist
if (vpr_setup.AnalysisOpts.gen_post_implementation_merged_netlist) {
merged_netlist_writer(atom_ctx.nlist.netlist_name().c_str(), analysis_delay_calc, vpr_setup.AnalysisOpts);
}
//Do power analysis

View File

@ -900,6 +900,13 @@ enum class e_timing_report_detail {
DETAILED_ROUTING, //Show inter-block routing resources used
};
enum class e_post_synth_netlist_unconn_handling {
UNCONNECTED, // Leave unrouted ports unconnected
NETS, // Leave unrouted ports unconnected but add new named nets to each of them
GND, // Tie unrouted ports to ground (only for input ports)
VCC // Tie unrouted ports to VCC (only for input ports)
};
enum class e_incr_reroute_delay_ripup {
ON,
OFF,
@ -959,6 +966,9 @@ struct t_analysis_opts {
e_stage_action doAnalysis;
bool gen_post_synthesis_netlist;
bool gen_post_implementation_merged_netlist;
e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_input_handling;
e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_output_handling;
int timing_report_npaths;
e_timing_report_detail timing_report_detail;