|
| 1 | +/* |
| 2 | + * yosys -- Yosys Open SYnthesis Suite |
| 3 | + * |
| 4 | + * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at> |
| 5 | + * |
| 6 | + * Permission to use, copy, modify, and/or distribute this software for any |
| 7 | + * purpose with or without fee is hereby granted, provided that the above |
| 8 | + * copyright notice and this permission notice appear in all copies. |
| 9 | + * |
| 10 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 11 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 12 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 13 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 14 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 15 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 16 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 17 | + * |
| 18 | + */ |
| 19 | + |
| 20 | +#include "kernel/yosys.h" |
| 21 | +#include "kernel/sigtools.h" |
| 22 | +#include "kernel/modtools.h" |
| 23 | + |
| 24 | +USING_YOSYS_NAMESPACE |
| 25 | +PRIVATE_NAMESPACE_BEGIN |
| 26 | + |
| 27 | +/* |
| 28 | +//get the list of cells hooked up to at least one bit of a given net |
| 29 | +pool<Cell*> get_other_cells(const RTLIL::SigSpec& port, ModIndex& index, Cell* src) |
| 30 | +{ |
| 31 | + pool<Cell*> rval; |
| 32 | + for(auto b : port) |
| 33 | + { |
| 34 | + pool<ModIndex::PortInfo> ports = index.query_ports(b); |
| 35 | + for(auto x : ports) |
| 36 | + { |
| 37 | + if(x.cell == src) |
| 38 | + continue; |
| 39 | + rval.insert(x.cell); |
| 40 | + } |
| 41 | + } |
| 42 | + return rval; |
| 43 | +} |
| 44 | +
|
| 45 | +//return true if there is a full-width bus connection from cell a port ap to cell b port bp |
| 46 | +//if other_conns_allowed is false, then we require a strict point to point connection (no other links) |
| 47 | +bool is_full_bus( |
| 48 | + const RTLIL::SigSpec& sig, |
| 49 | + ModIndex& index, |
| 50 | + Cell* a, |
| 51 | + RTLIL::IdString ap, |
| 52 | + Cell* b, |
| 53 | + RTLIL::IdString bp, |
| 54 | + bool other_conns_allowed = false) |
| 55 | +{ |
| 56 | + for(auto s : sig) |
| 57 | + { |
| 58 | + pool<ModIndex::PortInfo> ports = index.query_ports(s); |
| 59 | + bool found_a = false; |
| 60 | + bool found_b = false; |
| 61 | + for(auto x : ports) |
| 62 | + { |
| 63 | + if( (x.cell == a) && (x.port == ap) ) |
| 64 | + found_a = true; |
| 65 | + else if( (x.cell == b) && (x.port == bp) ) |
| 66 | + found_b = true; |
| 67 | + else if(!other_conns_allowed) |
| 68 | + return false; |
| 69 | + } |
| 70 | +
|
| 71 | + if( (!found_a) || (!found_b) ) |
| 72 | + return false; |
| 73 | + } |
| 74 | +
|
| 75 | + return true; |
| 76 | +} |
| 77 | +
|
| 78 | +//return true if the signal connects to one port only (nothing on the other end) |
| 79 | +bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index) |
| 80 | +{ |
| 81 | + for(auto b : port) |
| 82 | + { |
| 83 | + pool<ModIndex::PortInfo> ports = index.query_ports(b); |
| 84 | + if(ports.size() > 1) |
| 85 | + return false; |
| 86 | + } |
| 87 | +
|
| 88 | + return true; |
| 89 | +} |
| 90 | +
|
| 91 | +struct CounterExtraction |
| 92 | +{ |
| 93 | + int width; //counter width |
| 94 | + RTLIL::Wire* rwire; //the register output |
| 95 | + bool has_reset; //true if we have a reset |
| 96 | + RTLIL::SigSpec rst; //reset pin |
| 97 | + int count_value; //value we count from |
| 98 | + RTLIL::SigSpec clk; //clock signal |
| 99 | + RTLIL::SigSpec outsig; //counter output signal |
| 100 | + RTLIL::Cell* count_mux; //counter mux |
| 101 | + RTLIL::Cell* count_reg; //counter register |
| 102 | + RTLIL::Cell* underflow_inv; //inverter reduction for output-underflow detect |
| 103 | + pool<ModIndex::PortInfo> pouts; //Ports that take a parallel output from us |
| 104 | +}; |
| 105 | +*/ |
| 106 | +/* |
| 107 | +//attempt to extract a counter centered on the given adder cell |
| 108 | +//For now we only support DOWN counters. |
| 109 | +//TODO: up/down support |
| 110 | +int bus_tryextract( |
| 111 | + ModIndex& index, |
| 112 | + Cell *cell, |
| 113 | + CounterExtraction& extract, |
| 114 | + pool<RTLIL::IdString>& parallel_cells, |
| 115 | + int maxwidth) |
| 116 | +{ |
| 117 | + SigMap& sigmap = index.sigmap; |
| 118 | +
|
| 119 | + //A counter with less than 2 bits makes no sense |
| 120 | + //TODO: configurable min threshold |
| 121 | + int a_width = cell->getParam("\\A_WIDTH").as_int(); |
| 122 | + extract.width = a_width; |
| 123 | + if( (a_width < 2) || (a_width > maxwidth) ) |
| 124 | + return 1; |
| 125 | +
|
| 126 | + //Second input must be a single bit |
| 127 | + int b_width = cell->getParam("\\B_WIDTH").as_int(); |
| 128 | + if(b_width != 1) |
| 129 | + return 2; |
| 130 | +
|
| 131 | + //Both inputs must be unsigned, so don't extract anything with a signed input |
| 132 | + bool a_sign = cell->getParam("\\A_SIGNED").as_bool(); |
| 133 | + bool b_sign = cell->getParam("\\B_SIGNED").as_bool(); |
| 134 | + if(a_sign || b_sign) |
| 135 | + return 3; |
| 136 | +
|
| 137 | + //To be a counter, one input of the ALU must be a constant 1 |
| 138 | + //TODO: can A or B be swapped in synthesized RTL or is B always the 1? |
| 139 | + const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B")); |
| 140 | + if(!b_port.is_fully_const() || (b_port.as_int() != 1) ) |
| 141 | + return 4; |
| 142 | +
|
| 143 | + //BI and CI must be constant 1 as well |
| 144 | + const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI")); |
| 145 | + if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) ) |
| 146 | + return 5; |
| 147 | + const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI")); |
| 148 | + if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) ) |
| 149 | + return 6; |
| 150 | +
|
| 151 | + //CO and X must be unconnected (exactly one connection to each port) |
| 152 | + if(!is_unconnected(sigmap(cell->getPort("\\CO")), index)) |
| 153 | + return 7; |
| 154 | + if(!is_unconnected(sigmap(cell->getPort("\\X")), index)) |
| 155 | + return 8; |
| 156 | +
|
| 157 | + //Y must have exactly one connection, and it has to be a $mux cell. |
| 158 | + //We must have a direct bus connection from our Y to their A. |
| 159 | + const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y")); |
| 160 | + pool<Cell*> y_loads = get_other_cells(aluy, index, cell); |
| 161 | + if(y_loads.size() != 1) |
| 162 | + return 9; |
| 163 | + Cell* count_mux = *y_loads.begin(); |
| 164 | + extract.count_mux = count_mux; |
| 165 | + if(count_mux->type != "$mux") |
| 166 | + return 10; |
| 167 | + if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A")) |
| 168 | + return 11; |
| 169 | +
|
| 170 | + //B connection of the mux is our underflow value |
| 171 | + const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B")); |
| 172 | + if(!underflow.is_fully_const()) |
| 173 | + return 12; |
| 174 | + extract.count_value = underflow.as_int(); |
| 175 | +
|
| 176 | + //S connection of the mux must come from an inverter (need not be the only load) |
| 177 | + const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S")); |
| 178 | + extract.outsig = muxsel; |
| 179 | + pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux); |
| 180 | + Cell* underflow_inv = NULL; |
| 181 | + for(auto c : muxsel_conns) |
| 182 | + { |
| 183 | + if(c->type != "$logic_not") |
| 184 | + continue; |
| 185 | + if(!is_full_bus(muxsel, index, c, "\\Y", count_mux, "\\S", true)) |
| 186 | + continue; |
| 187 | +
|
| 188 | + underflow_inv = c; |
| 189 | + break; |
| 190 | + } |
| 191 | + if(underflow_inv == NULL) |
| 192 | + return 13; |
| 193 | + extract.underflow_inv = underflow_inv; |
| 194 | +
|
| 195 | + //Y connection of the mux must have exactly one load, the counter's internal register |
| 196 | + const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y")); |
| 197 | + pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux); |
| 198 | + if(muxy_loads.size() != 1) |
| 199 | + return 14; |
| 200 | + Cell* count_reg = *muxy_loads.begin(); |
| 201 | + extract.count_reg = count_reg; |
| 202 | + if(count_reg->type == "$dff") |
| 203 | + extract.has_reset = false; |
| 204 | + else if(count_reg->type == "$adff") |
| 205 | + { |
| 206 | + extract.has_reset = true; |
| 207 | +
|
| 208 | + //Verify ARST_VALUE is zero and ARST_POLARITY is 1 |
| 209 | + //TODO: infer an inverter to make it 1 if necessary, so we can support negative level resets? |
| 210 | + if(count_reg->getParam("\\ARST_POLARITY").as_int() != 1) |
| 211 | + return 22; |
| 212 | + if(count_reg->getParam("\\ARST_VALUE").as_int() != 0) |
| 213 | + return 23; |
| 214 | +
|
| 215 | + //Save the reset |
| 216 | + extract.rst = sigmap(count_reg->getPort("\\ARST")); |
| 217 | + } |
| 218 | + //TODO: support synchronous reset |
| 219 | + else |
| 220 | + return 15; |
| 221 | + if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D")) |
| 222 | + return 16; |
| 223 | +
|
| 224 | + //TODO: Verify count_reg CLK_POLARITY is 1 |
| 225 | +
|
| 226 | + //Register output must have exactly two loads, the inverter and ALU |
| 227 | + //(unless we have a parallel output!) |
| 228 | + const RTLIL::SigSpec qport = count_reg->getPort("\\Q"); |
| 229 | + const RTLIL::SigSpec cnout = sigmap(qport); |
| 230 | + pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg); |
| 231 | + if(cnout_loads.size() > 2) |
| 232 | + { |
| 233 | + //If we specified a limited set of cells for parallel output, check that we only drive them |
| 234 | + if(!parallel_cells.empty()) |
| 235 | + { |
| 236 | + for(auto c : cnout_loads) |
| 237 | + { |
| 238 | + if(c == underflow_inv) |
| 239 | + continue; |
| 240 | + if(c == cell) |
| 241 | + continue; |
| 242 | +
|
| 243 | + //Make sure we're in the whitelist |
| 244 | + if( parallel_cells.find(c->type) == parallel_cells.end()) |
| 245 | + return 17; |
| 246 | +
|
| 247 | + //Figure out what port(s) are driven by it |
| 248 | + //TODO: this can probably be done more efficiently w/o multiple iterations over our whole net? |
| 249 | + RTLIL::IdString portname; |
| 250 | + for(auto b : qport) |
| 251 | + { |
| 252 | + pool<ModIndex::PortInfo> ports = index.query_ports(b); |
| 253 | + for(auto x : ports) |
| 254 | + { |
| 255 | + if(x.cell != c) |
| 256 | + continue; |
| 257 | + if(portname == "") |
| 258 | + portname = x.port; |
| 259 | +
|
| 260 | + //somehow our counter output is going to multiple ports |
| 261 | + //this makes no sense, don't allow inference |
| 262 | + else if(portname != x.port) |
| 263 | + return 17; |
| 264 | + } |
| 265 | + } |
| 266 | +
|
| 267 | + //Save the other loads |
| 268 | + extract.pouts.insert(ModIndex::PortInfo(c, portname, 0)); |
| 269 | + } |
| 270 | + } |
| 271 | + } |
| 272 | + if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true)) |
| 273 | + return 18; |
| 274 | + if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true)) |
| 275 | + return 19; |
| 276 | +
|
| 277 | + //Look up the clock from the register |
| 278 | + extract.clk = sigmap(count_reg->getPort("\\CLK")); |
| 279 | +
|
| 280 | + //Register output net must have an INIT attribute equal to the count value |
| 281 | + extract.rwire = cnout.as_wire(); |
| 282 | + if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end()) |
| 283 | + return 20; |
| 284 | + int rinit = extract.rwire->attributes["\\init"].as_int(); |
| 285 | + if(rinit != extract.count_value) |
| 286 | + return 21; |
| 287 | +
|
| 288 | + return 0; |
| 289 | +} |
| 290 | +*/ |
| 291 | + |
| 292 | +void bus_worker( |
| 293 | + ModIndex& index, |
| 294 | + Cell *cell, |
| 295 | + unsigned int& total_buses) |
| 296 | +{ |
| 297 | + SigMap& sigmap = index.sigmap; |
| 298 | + |
| 299 | + /* |
| 300 | + Table of ports on each cell that are guaranteed to be a bus. |
| 301 | +
|
| 302 | + Note that with commutative operations such as addition, we cannot make the inputs buses |
| 303 | + because we don't know which bits belong to which input. |
| 304 | + We also cannot infer a bus from |
| 305 | + */ |
| 306 | + //TODO: better way to store this table |
| 307 | + std::set<RTLIL::IdString> busports; |
| 308 | + if(cell->type == "$add") |
| 309 | + busports.emplace("\\Y"); |
| 310 | + else if(cell->type == "$__COUNT_") |
| 311 | + busports.emplace("\\POUT"); |
| 312 | + |
| 313 | + //Unknown cell, bus ports not supported |
| 314 | + else |
| 315 | + return; |
| 316 | + |
| 317 | + //Bus-ify each port |
| 318 | + log("Inferring buses for port(s) of %s\n", log_id(cell->name)); |
| 319 | + for(auto p : busports) |
| 320 | + { |
| 321 | + //See what's on the port now |
| 322 | + //If it's a wire, stop - no action needed |
| 323 | + //If there's nothing on the port, skip it as well! |
| 324 | + if(!cell->hasPort(p)) |
| 325 | + { |
| 326 | + //log(" Cell does not have a port named %s\n", p.c_str()); |
| 327 | + continue; |
| 328 | + } |
| 329 | + auto port = sigmap(cell->getPort(p)); |
| 330 | + if(port.is_wire()) |
| 331 | + { |
| 332 | + //log(" Port %s is already a wire\n", p.c_str()); |
| 333 | + continue; |
| 334 | + } |
| 335 | + |
| 336 | + //Not a wire, create one! |
| 337 | + auto wire = cell->module->addWire(NEW_ID, port.size()); |
| 338 | + |
| 339 | + //Find downstream entities connected to the old wire and reconnect them |
| 340 | + log(" Inferring bus for port %s\n", p.c_str()); |
| 341 | + for(int i=0; i<port.size(); i++) |
| 342 | + { |
| 343 | + auto b = port[i]; |
| 344 | + pool<ModIndex::PortInfo> ports = index.query_ports(b); |
| 345 | + |
| 346 | + log(" Finding additional loads for bit %d / signal %s (found %zu)\n", |
| 347 | + i, log_id(b.wire->name), ports.size()); |
| 348 | + for(auto x : ports) |
| 349 | + { |
| 350 | + if(x.cell == cell) |
| 351 | + continue; |
| 352 | + log(" Cell %s port %s is connected to us at offset %d\n", |
| 353 | + log_id(x.cell->name), log_id(x.port), x.offset); |
| 354 | + |
| 355 | + //Patch the sigspec one bit at a time as needed |
| 356 | + auto dspec = x.cell->getPort(x.port); |
| 357 | + dspec[x.offset] = RTLIL::SigBit(wire, i); |
| 358 | + x.cell->setPort(x.port, dspec); |
| 359 | + } |
| 360 | + |
| 361 | + //See if this signal is a top-level module port |
| 362 | + //If so, add buffers to preserve the existing signal names |
| 363 | + //TODO: how to handle top level vector ports? |
| 364 | + bool found = false; |
| 365 | + for(auto p : cell->module->ports) |
| 366 | + { |
| 367 | + if(p == b.wire->name) |
| 368 | + found = true; |
| 369 | + } |
| 370 | + |
| 371 | + if(found) |
| 372 | + { |
| 373 | + log(" Cell %s port %s drives top level module port %s\n", |
| 374 | + log_id(cell->name), p.c_str(), log_id(b.wire->name)); |
| 375 | + |
| 376 | + cell->module->addBufGate(NEW_ID, RTLIL::SigSpec(wire, i, 1), b.wire); |
| 377 | + } |
| 378 | + } |
| 379 | + |
| 380 | + //Hook up the new wire |
| 381 | + cell->setPort(p, wire); |
| 382 | + |
| 383 | + //We made a match, note it |
| 384 | + total_buses ++; |
| 385 | + } |
| 386 | +} |
| 387 | + |
| 388 | +struct ExtractBusPass : public Pass { |
| 389 | + ExtractBusPass() : Pass("extract_bus", "Extract buses from split nets") { } |
| 390 | + virtual void help() |
| 391 | + { |
| 392 | + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| 393 | + log("\n"); |
| 394 | + log(" extract_bus [selection]\n"); |
| 395 | + log("\n"); |
| 396 | + log("This pass finds buses in a design consisting of single-bit nets\n"); |
| 397 | + log("\n"); |
| 398 | + } |
| 399 | + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) |
| 400 | + { |
| 401 | + log_header(design, "Executing EXTRACT_BUS pass (find buses in netlist).\n"); |
| 402 | + |
| 403 | + //int argidx = 0; |
| 404 | + //extra_args(args, argidx, design); |
| 405 | + |
| 406 | + //Extract all of the buses we could find |
| 407 | + unsigned int total_buses = 0; |
| 408 | + for (auto module : design->selected_modules()) |
| 409 | + { |
| 410 | + ModIndex index(module); |
| 411 | + for (auto cell : module->selected_cells()) |
| 412 | + bus_worker(index, cell, total_buses); |
| 413 | + } |
| 414 | + |
| 415 | + //TODO: Recurse and find additional buses by elimination |
| 416 | + |
| 417 | + if(total_buses) |
| 418 | + log("Extracted %u buses\n", total_buses); |
| 419 | + } |
| 420 | +} ExtractBusPass; |
| 421 | + |
| 422 | +PRIVATE_NAMESPACE_END |
0 commit comments