Skip to content

Commit 3fb142d

Browse files
committed
vpr: base: introduce netlist writer for merged verilog ports
Signed-off-by: Pawel Czarnecki <[email protected]>
1 parent d9b8ef5 commit 3fb142d

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed

vpr/src/base/netlist_writer.cpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <memory>
1111
#include <unordered_set>
1212
#include <cmath>
13+
#include <regex>
1314

1415
#include "vtr_assert.h"
1516
#include "vtr_util.h"
@@ -2099,6 +2100,186 @@ class NetlistWriterVisitor : public NetlistVisitor {
20992100
struct t_analysis_opts opts_;
21002101
};
21012102

2103+
/**
2104+
* @brief A class which writes post-synthesis merged netlists (Verilog)
2105+
*
2106+
* It implements the NetlistVisitor interface used by NetlistWalker (see netlist_walker.h)
2107+
*/
2108+
class MergedNetlistWriterVisitor : public NetlistWriterVisitor {
2109+
public: //Public interface
2110+
MergedNetlistWriterVisitor(std::ostream& verilog_os, ///<Output stream for verilog netlist
2111+
std::ostream& blif_os, ///<Output stream for blif netlist
2112+
std::ostream& sdf_os, ///<Output stream for SDF
2113+
std::shared_ptr<const AnalysisDelayCalculator> delay_calc)
2114+
: NetlistWriterVisitor(verilog_os, blif_os, sdf_os, delay_calc) {}
2115+
2116+
std::map<std::string, int> portmap;
2117+
2118+
void visit_atom_impl(const t_pb* atom) override {
2119+
auto& atom_ctx = g_vpr_ctx.atom();
2120+
2121+
auto atom_pb = atom_ctx.lookup.pb_atom(atom);
2122+
if (atom_pb == AtomBlockId::INVALID()) {
2123+
return;
2124+
}
2125+
const t_model* model = atom_ctx.nlist.block_model(atom_pb);
2126+
2127+
if (model->name == std::string(MODEL_INPUT)) {
2128+
auto merged_io_name = make_io(atom, PortType::INPUT);
2129+
if (merged_io_name != "")
2130+
inputs_.emplace_back(merged_io_name);
2131+
} else if (model->name == std::string(MODEL_OUTPUT)) {
2132+
auto merged_io_name = make_io(atom, PortType::OUTPUT);
2133+
if (merged_io_name != "")
2134+
outputs_.emplace_back(merged_io_name);
2135+
} else if (model->name == std::string(MODEL_NAMES)) {
2136+
cell_instances_.push_back(make_lut_instance(atom));
2137+
} else if (model->name == std::string(MODEL_LATCH)) {
2138+
cell_instances_.push_back(make_latch_instance(atom));
2139+
} else if (model->name == std::string("single_port_ram")) {
2140+
cell_instances_.push_back(make_ram_instance(atom));
2141+
} else if (model->name == std::string("dual_port_ram")) {
2142+
cell_instances_.push_back(make_ram_instance(atom));
2143+
} else if (model->name == std::string("multiply")) {
2144+
cell_instances_.push_back(make_multiply_instance(atom));
2145+
} else if (model->name == std::string("adder")) {
2146+
cell_instances_.push_back(make_adder_instance(atom));
2147+
} else {
2148+
cell_instances_.push_back(make_blackbox_instance(atom));
2149+
}
2150+
}
2151+
2152+
/**
2153+
* @brief Returns the name of circuit-level Input/Output ports with multi-bit
2154+
* ports merged into one.
2155+
*
2156+
* The I/O is recorded and instantiated by the top level output routines
2157+
* @param atom The implementation primitive representing the I/O
2158+
* @param dir The IO direction
2159+
* @param portmap Map for keeping port names and width
2160+
*/
2161+
std::string make_io(const t_pb* atom,
2162+
PortType dir) {
2163+
const t_pb_graph_node* pb_graph_node = atom->pb_graph_node;
2164+
2165+
std::string io_name;
2166+
std::string indexed_io_name;
2167+
int cluster_pin_idx = -1;
2168+
// regex for matching 3 groups:
2169+
// * 'out:' - optional
2170+
// * verilog identifier - mandatory
2171+
// * index - optional
2172+
std::string rgx = "(out:)?([a-zA-Z$_]+[a-zA-Z0-9$_]*)(\\[[0-9]+\\])?$";
2173+
std::string name(atom->name);
2174+
std::regex regex(rgx);
2175+
std::smatch matches;
2176+
2177+
if (dir == PortType::INPUT) {
2178+
VTR_ASSERT(pb_graph_node->num_output_ports == 1); //One output port
2179+
VTR_ASSERT(pb_graph_node->num_output_pins[0] == 1); //One output pin
2180+
cluster_pin_idx = pb_graph_node->output_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster
2181+
2182+
io_name = "";
2183+
indexed_io_name = atom->name;
2184+
2185+
if (std::regex_match(name, matches, regex)) {
2186+
if (std::find(inputs_.begin(), inputs_.end(), matches[2]) == inputs_.end()) { //Skip already existing multi-bit port names
2187+
io_name = matches[2];
2188+
portmap[matches[2]] = 0;
2189+
} else {
2190+
portmap[matches[2]]++;
2191+
}
2192+
}
2193+
2194+
} else {
2195+
VTR_ASSERT(pb_graph_node->num_input_ports == 1); //One input port
2196+
VTR_ASSERT(pb_graph_node->num_input_pins[0] == 1); //One input pin
2197+
cluster_pin_idx = pb_graph_node->input_pins[0][0].pin_count_in_cluster; //Unique pin index in cluster
2198+
2199+
//Strip off the starting 'out:' that vpr adds to uniqify outputs
2200+
//this makes the port names match the input blif file
2201+
2202+
io_name = "";
2203+
indexed_io_name = atom->name + 4;
2204+
2205+
if (std::regex_search(name, matches, regex)) {
2206+
if (std::find(outputs_.begin(), outputs_.end(), matches[2]) == outputs_.end()) { //Skip already existing multi-bit port names
2207+
portmap[matches[2]] = 0;
2208+
io_name = matches[2];
2209+
} else {
2210+
portmap[matches[2]]++;
2211+
}
2212+
}
2213+
}
2214+
2215+
const auto& top_pb_route = find_top_pb_route(atom);
2216+
2217+
if (top_pb_route.count(cluster_pin_idx)) {
2218+
//Net exists
2219+
auto atom_net_id = top_pb_route[cluster_pin_idx].atom_net_id; //Connected net in atom netlist
2220+
2221+
//Port direction is inverted (inputs drive internal nets, outputs sink internal nets)
2222+
PortType wire_dir = (dir == PortType::INPUT) ? PortType::OUTPUT : PortType::INPUT;
2223+
2224+
//Look up the tnode associated with this pin (used for delay calculation)
2225+
tatum::NodeId tnode_id = find_tnode(atom, cluster_pin_idx);
2226+
2227+
auto wire_name = make_inst_wire(atom_net_id, tnode_id, indexed_io_name, wire_dir, 0, 0);
2228+
2229+
//Connect the wires to to I/Os with assign statements
2230+
if (wire_dir == PortType::INPUT) {
2231+
assignments_.emplace_back(indexed_io_name, escape_verilog_identifier(wire_name));
2232+
} else {
2233+
assignments_.emplace_back(escape_verilog_identifier(wire_name), indexed_io_name);
2234+
}
2235+
}
2236+
2237+
return io_name;
2238+
}
2239+
2240+
void print_primary_io(int depth) {
2241+
//Primary Inputs
2242+
for (auto iter = inputs_.begin(); iter != inputs_.end(); ++iter) {
2243+
//verilog_os_ << indent(depth + 1) << "input " << escape_verilog_identifier(*iter);
2244+
std::string range;
2245+
if (portmap[*iter] > 0)
2246+
verilog_os_ << indent(depth + 1) << "input [" << portmap[*iter] << ":0] " << *iter;
2247+
else
2248+
verilog_os_ << indent(depth + 1) << "input " << *iter;
2249+
if (iter + 1 != inputs_.end() || outputs_.size() > 0) {
2250+
verilog_os_ << ",";
2251+
}
2252+
verilog_os_ << "\n";
2253+
}
2254+
2255+
//Primary Outputs
2256+
for (auto iter = outputs_.begin(); iter != outputs_.end(); ++iter) {
2257+
std::string range;
2258+
if (portmap[*iter] > 0)
2259+
verilog_os_ << indent(depth + 1) << "output [" << portmap[*iter] << ":0] " << *iter;
2260+
else
2261+
verilog_os_ << indent(depth + 1) << "output " << *iter;
2262+
if (iter + 1 != outputs_.end()) {
2263+
verilog_os_ << ",";
2264+
}
2265+
verilog_os_ << "\n";
2266+
}
2267+
}
2268+
2269+
void print_assignments(int depth) {
2270+
verilog_os_ << "\n";
2271+
verilog_os_ << indent(depth + 1) << "//IO assignments\n";
2272+
for (auto& assign : assignments_) {
2273+
assign.print_merged_verilog(verilog_os_, indent(depth + 1));
2274+
}
2275+
}
2276+
2277+
void finish_impl() override {
2278+
// Don't write to blif and sdf streams
2279+
print_verilog();
2280+
}
2281+
};
2282+
21022283
//
21032284
// Externally Accessible Functions
21042285
//
@@ -2124,6 +2305,23 @@ void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDe
21242305
nl_walker.walk();
21252306
}
21262307

2308+
///@brief Main routing for this file. See netlist_writer.h for details.
2309+
void merged_netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc) {
2310+
std::string verilog_filename = basename + "_merged_post_synthesis.v";
2311+
2312+
VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str());
2313+
2314+
std::ofstream verilog_os(verilog_filename);
2315+
// Don't write blif and sdf, pass dummy streams
2316+
std::ofstream blif_os;
2317+
std::ofstream sdf_os;
2318+
2319+
MergedNetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc);
2320+
2321+
NetlistWalker nl_walker(visitor);
2322+
2323+
nl_walker.walk();
2324+
}
21272325
//
21282326
// File-scope function implementations
21292327
//

vpr/src/base/netlist_writer.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,13 @@
1717
*/
1818
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, struct t_analysis_opts opts);
1919

20+
/**
21+
* @brief Writes out the post-synthesis implementation netlists in Verilog format.
22+
* It has its top module ports merged into multi-bit ones.
23+
*
24+
* Written filename ends in {basename}_merged_post_synthesis.v where {basename} is the
25+
* basename argument.
26+
*/
27+
void merged_netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc);
28+
2029
#endif

0 commit comments

Comments
 (0)