Skip to content

Commit d9ec991

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

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"
@@ -2119,6 +2120,186 @@ class NetlistWriterVisitor : public NetlistVisitor {
21192120
struct t_analysis_opts opts_;
21202121
};
21212122

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

2328+
///@brief Main routing for this file. See netlist_writer.h for details.
2329+
void merged_netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc) {
2330+
std::string verilog_filename = basename + "_merged_post_synthesis.v";
2331+
2332+
VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str());
2333+
2334+
std::ofstream verilog_os(verilog_filename);
2335+
// Don't write blif and sdf, pass dummy streams
2336+
std::ofstream blif_os;
2337+
std::ofstream sdf_os;
2338+
2339+
MergedNetlistWriterVisitor visitor(verilog_os, blif_os, sdf_os, delay_calc);
2340+
2341+
NetlistWalker nl_walker(visitor);
2342+
2343+
nl_walker.walk();
2344+
}
21472345
//
21482346
// File-scope function implementations
21492347
//

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)