10
10
#include < memory>
11
11
#include < unordered_set>
12
12
#include < cmath>
13
+ #include < regex>
13
14
14
15
#include " vtr_assert.h"
15
16
#include " vtr_util.h"
@@ -2119,6 +2120,186 @@ class NetlistWriterVisitor : public NetlistVisitor {
2119
2120
struct t_analysis_opts opts_;
2120
2121
};
2121
2122
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
+
2122
2303
//
2123
2304
// Externally Accessible Functions
2124
2305
//
@@ -2144,6 +2325,23 @@ void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDe
2144
2325
nl_walker.walk ();
2145
2326
}
2146
2327
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
+ }
2147
2345
//
2148
2346
// File-scope function implementations
2149
2347
//
0 commit comments