| /* | |
| * yosys -- Yosys Open SYnthesis Suite | |
| * | |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> | |
| * 2019 Eddie Hung <eddie@fpgeh.com> | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| * | |
| */ | |
| // https://stackoverflow.com/a/46137633 | |
| #ifdef _MSC_VER | |
| #include <stdlib.h> | |
| #define bswap32 _byteswap_ulong | |
| #elif defined(__APPLE__) | |
| #include <libkern/OSByteOrder.h> | |
| #define bswap32 OSSwapInt32 | |
| #elif defined(__GNUC__) | |
| #define bswap32 __builtin_bswap32 | |
| #else | |
| #include <cstdint> | |
| inline static uint32_t bswap32(uint32_t x) | |
| { | |
| // https://stackoverflow.com/a/27796212 | |
| register uint32_t value = number_to_be_reversed; | |
| uint8_t lolo = (value >> 0) & 0xFF; | |
| uint8_t lohi = (value >> 8) & 0xFF; | |
| uint8_t hilo = (value >> 16) & 0xFF; | |
| uint8_t hihi = (value >> 24) & 0xFF; | |
| return (hihi << 24) | |
| | (hilo << 16) | |
| | (lohi << 8) | |
| | (lolo << 0); | |
| } | |
| #endif | |
| #include "kernel/yosys.h" | |
| #include "kernel/sigtools.h" | |
| #include "kernel/utils.h" | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| inline int32_t to_big_endian(int32_t i32) { | |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
| return bswap32(i32); | |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
| return i32; | |
| #else | |
| #error "Unknown endianness" | |
| #endif | |
| } | |
| void aiger_encode(std::ostream &f, int x) | |
| { | |
| log_assert(x >= 0); | |
| while (x & ~0x7f) { | |
| f.put((x & 0x7f) | 0x80); | |
| x = x >> 7; | |
| } | |
| f.put(x); | |
| } | |
| struct XAigerWriter | |
| { | |
| Module *module; | |
| SigMap sigmap; | |
| pool<SigBit> input_bits, output_bits; | |
| dict<SigBit, SigBit> not_map, alias_map; | |
| dict<SigBit, pair<SigBit, SigBit>> and_map; | |
| vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; | |
| vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; | |
| dict<SigBit, float> arrival_times; | |
| vector<pair<int, int>> aig_gates; | |
| vector<int> aig_outputs; | |
| int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; | |
| dict<SigBit, int> aig_map; | |
| dict<SigBit, int> ordered_outputs; | |
| vector<Cell*> box_list; | |
| bool omode = false; | |
| int mkgate(int a0, int a1) | |
| { | |
| aig_m++, aig_a++; | |
| aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); | |
| return 2*aig_m; | |
| } | |
| int bit2aig(SigBit bit) | |
| { | |
| auto it = aig_map.find(bit); | |
| if (it != aig_map.end()) { | |
| log_assert(it->second >= 0); | |
| return it->second; | |
| } | |
| // NB: Cannot use iterator returned from aig_map.insert() | |
| // since this function is called recursively | |
| int a = -1; | |
| if (not_map.count(bit)) { | |
| a = bit2aig(not_map.at(bit)) ^ 1; | |
| } else | |
| if (and_map.count(bit)) { | |
| auto args = and_map.at(bit); | |
| int a0 = bit2aig(args.first); | |
| int a1 = bit2aig(args.second); | |
| a = mkgate(a0, a1); | |
| } else | |
| if (alias_map.count(bit)) { | |
| a = bit2aig(alias_map.at(bit)); | |
| } | |
| if (bit == State::Sx || bit == State::Sz) { | |
| log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); | |
| a = aig_map.at(State::S0); | |
| } | |
| log_assert(a >= 0); | |
| aig_map[bit] = a; | |
| return a; | |
| } | |
| XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module) | |
| { | |
| pool<SigBit> undriven_bits; | |
| pool<SigBit> unused_bits; | |
| pool<SigBit> keep_bits; | |
| // promote public wires | |
| for (auto wire : module->wires()) | |
| if (wire->name[0] == '\\') | |
| sigmap.add(wire); | |
| // promote input wires | |
| for (auto wire : module->wires()) | |
| if (wire->port_input) | |
| sigmap.add(wire); | |
| // promote output wires | |
| for (auto wire : module->wires()) | |
| if (wire->port_output) | |
| sigmap.add(wire); | |
| for (auto wire : module->wires()) | |
| { | |
| bool keep = wire->attributes.count("\\keep"); | |
| for (int i = 0; i < GetSize(wire); i++) | |
| { | |
| SigBit wirebit(wire, i); | |
| SigBit bit = sigmap(wirebit); | |
| if (bit.wire) { | |
| undriven_bits.insert(bit); | |
| unused_bits.insert(bit); | |
| } | |
| if (keep) | |
| keep_bits.insert(bit); | |
| if (wire->port_input || keep) { | |
| if (bit != wirebit) | |
| alias_map[bit] = wirebit; | |
| input_bits.insert(wirebit); | |
| } | |
| if (wire->port_output || keep) { | |
| if (bit != RTLIL::Sx) { | |
| if (bit != wirebit) | |
| alias_map[wirebit] = bit; | |
| output_bits.insert(wirebit); | |
| } | |
| else | |
| log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit)); | |
| } | |
| } | |
| } | |
| for (auto bit : input_bits) | |
| undriven_bits.erase(sigmap(bit)); | |
| for (auto bit : output_bits) | |
| if (!bit.wire->port_input) | |
| unused_bits.erase(bit); | |
| // TODO: Speed up toposort -- ultimately we care about | |
| // box ordering, but not individual AIG cells | |
| dict<SigBit, pool<IdString>> bit_drivers, bit_users; | |
| TopoSort<IdString, RTLIL::sort_by_id_str> toposort; | |
| bool abc9_box_seen = false; | |
| for (auto cell : module->selected_cells()) { | |
| if (cell->type == "$_NOT_") | |
| { | |
| SigBit A = sigmap(cell->getPort("\\A").as_bit()); | |
| SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); | |
| unused_bits.erase(A); | |
| undriven_bits.erase(Y); | |
| not_map[Y] = A; | |
| if (!holes_mode) { | |
| toposort.node(cell->name); | |
| bit_users[A].insert(cell->name); | |
| bit_drivers[Y].insert(cell->name); | |
| } | |
| continue; | |
| } | |
| if (cell->type == "$_AND_") | |
| { | |
| SigBit A = sigmap(cell->getPort("\\A").as_bit()); | |
| SigBit B = sigmap(cell->getPort("\\B").as_bit()); | |
| SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); | |
| unused_bits.erase(A); | |
| unused_bits.erase(B); | |
| undriven_bits.erase(Y); | |
| and_map[Y] = make_pair(A, B); | |
| if (!holes_mode) { | |
| toposort.node(cell->name); | |
| bit_users[A].insert(cell->name); | |
| bit_users[B].insert(cell->name); | |
| bit_drivers[Y].insert(cell->name); | |
| } | |
| continue; | |
| } | |
| log_assert(!holes_mode); | |
| RTLIL::Module* inst_module = module->design->module(cell->type); | |
| if (inst_module && inst_module->attributes.count("\\abc9_box_id")) { | |
| abc9_box_seen = true; | |
| if (!holes_mode) { | |
| toposort.node(cell->name); | |
| for (const auto &conn : cell->connections()) { | |
| auto port_wire = inst_module->wire(conn.first); | |
| if (port_wire->port_input) { | |
| // Ignore inout for the sake of topographical ordering | |
| if (port_wire->port_output) continue; | |
| for (auto bit : sigmap(conn.second)) | |
| bit_users[bit].insert(cell->name); | |
| } | |
| if (port_wire->port_output) | |
| for (auto bit : sigmap(conn.second)) | |
| bit_drivers[bit].insert(cell->name); | |
| } | |
| } | |
| } | |
| else { | |
| bool cell_known = inst_module || cell->known(); | |
| for (const auto &c : cell->connections()) { | |
| if (c.second.is_fully_const()) continue; | |
| auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr; | |
| auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first); | |
| auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first); | |
| if (!is_input && !is_output) | |
| log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); | |
| if (is_input) { | |
| for (auto b : c.second) { | |
| Wire *w = b.wire; | |
| if (!w) continue; | |
| if (!w->port_output || !cell_known) { | |
| SigBit I = sigmap(b); | |
| if (I != b) | |
| alias_map[b] = I; | |
| output_bits.insert(b); | |
| unused_bits.erase(b); | |
| if (!cell_known) | |
| keep_bits.insert(b); | |
| } | |
| } | |
| } | |
| if (is_output) { | |
| int arrival = 0; | |
| if (port_wire) { | |
| auto it = port_wire->attributes.find("\\abc9_arrival"); | |
| if (it != port_wire->attributes.end()) { | |
| if (it->second.flags != 0) | |
| log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); | |
| arrival = it->second.as_int(); | |
| } | |
| } | |
| for (auto b : c.second) { | |
| Wire *w = b.wire; | |
| if (!w) continue; | |
| input_bits.insert(b); | |
| SigBit O = sigmap(b); | |
| if (O != b) | |
| alias_map[O] = b; | |
| undriven_bits.erase(O); | |
| if (arrival) | |
| arrival_times[b] = arrival; | |
| } | |
| } | |
| } | |
| } | |
| //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); | |
| } | |
| if (abc9_box_seen) { | |
| for (auto &it : bit_users) | |
| if (bit_drivers.count(it.first)) | |
| for (auto driver_cell : bit_drivers.at(it.first)) | |
| for (auto user_cell : it.second) | |
| toposort.edge(driver_cell, user_cell); | |
| #if 0 | |
| toposort.analyze_loops = true; | |
| #endif | |
| bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); | |
| #if 0 | |
| unsigned i = 0; | |
| for (auto &it : toposort.loops) { | |
| log(" loop %d\n", i++); | |
| for (auto cell_name : it) { | |
| auto cell = module->cell(cell_name); | |
| log_assert(cell); | |
| log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); | |
| } | |
| } | |
| #endif | |
| log_assert(no_loops); | |
| for (auto cell_name : toposort.sorted) { | |
| RTLIL::Cell *cell = module->cell(cell_name); | |
| log_assert(cell); | |
| RTLIL::Module* box_module = module->design->module(cell->type); | |
| if (!box_module || !box_module->attributes.count("\\abc9_box_id")) | |
| continue; | |
| bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); | |
| // Fully pad all unused input connections of this box cell with S0 | |
| // Fully pad all undriven output connections of this box cell with anonymous wires | |
| // NB: Assume box_module->ports are sorted alphabetically | |
| // (as RTLIL::Module::fixup_ports() would do) | |
| for (const auto &port_name : box_module->ports) { | |
| RTLIL::Wire* w = box_module->wire(port_name); | |
| log_assert(w); | |
| auto it = cell->connections_.find(port_name); | |
| if (w->port_input) { | |
| RTLIL::SigSpec rhs; | |
| if (it != cell->connections_.end()) { | |
| if (GetSize(it->second) < GetSize(w)) | |
| it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second))); | |
| rhs = it->second; | |
| } | |
| else { | |
| rhs = RTLIL::SigSpec(State::S0, GetSize(w)); | |
| cell->setPort(port_name, rhs); | |
| } | |
| int offset = 0; | |
| for (auto b : rhs.bits()) { | |
| SigBit I = sigmap(b); | |
| if (b == RTLIL::Sx) | |
| b = State::S0; | |
| else if (I != b) { | |
| if (I == RTLIL::Sx) | |
| alias_map[b] = State::S0; | |
| else | |
| alias_map[b] = I; | |
| } | |
| co_bits.emplace_back(b, cell, port_name, offset++, 0); | |
| unused_bits.erase(b); | |
| } | |
| } | |
| if (w->port_output) { | |
| RTLIL::SigSpec rhs; | |
| auto it = cell->connections_.find(w->name); | |
| if (it != cell->connections_.end()) { | |
| if (GetSize(it->second) < GetSize(w)) | |
| it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second))); | |
| rhs = it->second; | |
| } | |
| else { | |
| Wire *wire = module->addWire(NEW_ID, GetSize(w)); | |
| if (blackbox) | |
| wire->set_bool_attribute(ID(abc9_padding)); | |
| rhs = wire; | |
| cell->setPort(port_name, rhs); | |
| } | |
| int offset = 0; | |
| for (const auto &b : rhs.bits()) { | |
| ci_bits.emplace_back(b, cell, port_name, offset++); | |
| SigBit O = sigmap(b); | |
| if (O != b) | |
| alias_map[O] = b; | |
| undriven_bits.erase(O); | |
| input_bits.erase(b); | |
| } | |
| } | |
| } | |
| box_list.emplace_back(cell); | |
| } | |
| // TODO: Free memory from toposort, bit_drivers, bit_users | |
| } | |
| for (auto bit : input_bits) { | |
| if (!output_bits.count(bit)) | |
| continue; | |
| RTLIL::Wire *wire = bit.wire; | |
| // If encountering an inout port, or a keep-ed wire, then create a new wire | |
| // with $inout.out suffix, make it a PO driven by the existing inout, and | |
| // inherit existing inout's drivers | |
| if ((wire->port_input && wire->port_output && !undriven_bits.count(bit)) | |
| || keep_bits.count(bit)) { | |
| RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str()); | |
| RTLIL::Wire *new_wire = module->wire(wire_name); | |
| if (!new_wire) | |
| new_wire = module->addWire(wire_name, GetSize(wire)); | |
| SigBit new_bit(new_wire, bit.offset); | |
| module->connect(new_bit, bit); | |
| if (not_map.count(bit)) { | |
| auto a = not_map.at(bit); | |
| not_map[new_bit] = a; | |
| } | |
| else if (and_map.count(bit)) { | |
| auto a = and_map.at(bit); | |
| and_map[new_bit] = a; | |
| } | |
| else if (alias_map.count(bit)) { | |
| auto a = alias_map.at(bit); | |
| alias_map[new_bit] = a; | |
| } | |
| else | |
| alias_map[new_bit] = bit; | |
| output_bits.erase(bit); | |
| output_bits.insert(new_bit); | |
| } | |
| } | |
| for (auto bit : unused_bits) | |
| undriven_bits.erase(bit); | |
| if (!undriven_bits.empty() && !holes_mode) { | |
| undriven_bits.sort(); | |
| for (auto bit : undriven_bits) { | |
| log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); | |
| input_bits.insert(bit); | |
| } | |
| log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); | |
| } | |
| if (holes_mode) { | |
| struct sort_by_port_id { | |
| bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { | |
| return a.wire->port_id < b.wire->port_id; | |
| } | |
| }; | |
| input_bits.sort(sort_by_port_id()); | |
| output_bits.sort(sort_by_port_id()); | |
| } | |
| else { | |
| input_bits.sort(); | |
| output_bits.sort(); | |
| } | |
| not_map.sort(); | |
| and_map.sort(); | |
| aig_map[State::S0] = 0; | |
| aig_map[State::S1] = 1; | |
| for (auto bit : input_bits) { | |
| aig_m++, aig_i++; | |
| log_assert(!aig_map.count(bit)); | |
| aig_map[bit] = 2*aig_m; | |
| } | |
| for (auto &c : ci_bits) { | |
| RTLIL::SigBit bit = std::get<0>(c); | |
| aig_m++, aig_i++; | |
| aig_map[bit] = 2*aig_m; | |
| } | |
| for (auto &c : co_bits) { | |
| RTLIL::SigBit bit = std::get<0>(c); | |
| std::get<4>(c) = ordered_outputs[bit] = aig_o++; | |
| aig_outputs.push_back(bit2aig(bit)); | |
| } | |
| if (output_bits.empty()) { | |
| output_bits.insert(State::S0); | |
| omode = true; | |
| } | |
| for (auto bit : output_bits) { | |
| ordered_outputs[bit] = aig_o++; | |
| aig_outputs.push_back(bit2aig(bit)); | |
| } | |
| } | |
| void write_aiger(std::ostream &f, bool ascii_mode) | |
| { | |
| int aig_obc = aig_o; | |
| int aig_obcj = aig_obc; | |
| int aig_obcjf = aig_obcj; | |
| log_assert(aig_m == aig_i + aig_l + aig_a); | |
| log_assert(aig_obcjf == GetSize(aig_outputs)); | |
| f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); | |
| f << stringf("\n"); | |
| if (ascii_mode) | |
| { | |
| for (int i = 0; i < aig_i; i++) | |
| f << stringf("%d\n", 2*i+2); | |
| for (int i = 0; i < aig_obc; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = aig_obc; i < aig_obcj; i++) | |
| f << stringf("1\n"); | |
| for (int i = aig_obc; i < aig_obcj; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = aig_obcj; i < aig_obcjf; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = 0; i < aig_a; i++) | |
| f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); | |
| } | |
| else | |
| { | |
| for (int i = 0; i < aig_obc; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = aig_obc; i < aig_obcj; i++) | |
| f << stringf("1\n"); | |
| for (int i = aig_obc; i < aig_obcj; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = aig_obcj; i < aig_obcjf; i++) | |
| f << stringf("%d\n", aig_outputs.at(i)); | |
| for (int i = 0; i < aig_a; i++) { | |
| int lhs = 2*(aig_i+aig_l+i)+2; | |
| int rhs0 = aig_gates.at(i).first; | |
| int rhs1 = aig_gates.at(i).second; | |
| int delta0 = lhs - rhs0; | |
| int delta1 = rhs0 - rhs1; | |
| aiger_encode(f, delta0); | |
| aiger_encode(f, delta1); | |
| } | |
| } | |
| f << "c"; | |
| log_assert(!output_bits.empty()); | |
| auto write_buffer = [](std::stringstream &buffer, int i32) { | |
| int32_t i32_be = to_big_endian(i32); | |
| buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); | |
| }; | |
| std::stringstream h_buffer; | |
| auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); | |
| write_h_buffer(1); | |
| log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); | |
| write_h_buffer(input_bits.size() + ci_bits.size()); | |
| log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); | |
| write_h_buffer(output_bits.size() + GetSize(co_bits)); | |
| log_debug("piNum = %d\n", GetSize(input_bits)); | |
| write_h_buffer(input_bits.size()); | |
| log_debug("poNum = %d\n", GetSize(output_bits)); | |
| write_h_buffer(output_bits.size()); | |
| log_debug("boxNum = %d\n", GetSize(box_list)); | |
| write_h_buffer(box_list.size()); | |
| auto write_buffer_float = [](std::stringstream &buffer, float f32) { | |
| buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32)); | |
| }; | |
| std::stringstream i_buffer; | |
| auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); | |
| for (auto bit : input_bits) | |
| write_i_buffer(arrival_times.at(bit, 0)); | |
| //std::stringstream o_buffer; | |
| //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1); | |
| //for (auto bit : output_bits) | |
| // write_o_buffer(0); | |
| if (!box_list.empty()) { | |
| RTLIL::Module *holes_module = module->design->addModule("$__holes__"); | |
| log_assert(holes_module); | |
| int port_id = 1; | |
| int box_count = 0; | |
| for (auto cell : box_list) { | |
| RTLIL::Module* box_module = module->design->module(cell->type); | |
| int box_inputs = 0, box_outputs = 0; | |
| Cell *holes_cell = nullptr; | |
| if (box_module->get_bool_attribute("\\whitebox")) { | |
| holes_cell = holes_module->addCell(cell->name, cell->type); | |
| holes_cell->parameters = cell->parameters; | |
| } | |
| // NB: Assume box_module->ports are sorted alphabetically | |
| // (as RTLIL::Module::fixup_ports() would do) | |
| for (const auto &port_name : box_module->ports) { | |
| RTLIL::Wire *w = box_module->wire(port_name); | |
| log_assert(w); | |
| RTLIL::Wire *holes_wire; | |
| RTLIL::SigSpec port_wire; | |
| if (w->port_input) { | |
| for (int i = 0; i < GetSize(w); i++) { | |
| box_inputs++; | |
| holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); | |
| if (!holes_wire) { | |
| holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); | |
| holes_wire->port_input = true; | |
| holes_wire->port_id = port_id++; | |
| holes_module->ports.push_back(holes_wire->name); | |
| } | |
| if (holes_cell) | |
| port_wire.append(holes_wire); | |
| } | |
| if (!port_wire.empty()) | |
| holes_cell->setPort(w->name, port_wire); | |
| } | |
| if (w->port_output) { | |
| box_outputs += GetSize(w); | |
| for (int i = 0; i < GetSize(w); i++) { | |
| if (GetSize(w) == 1) | |
| holes_wire = holes_module->addWire(stringf("%s.%s", cell->name.c_str(), w->name.c_str())); | |
| else | |
| holes_wire = holes_module->addWire(stringf("%s.%s[%d]", cell->name.c_str(), w->name.c_str(), i)); | |
| holes_wire->port_output = true; | |
| holes_wire->port_id = port_id++; | |
| holes_module->ports.push_back(holes_wire->name); | |
| if (holes_cell) | |
| port_wire.append(holes_wire); | |
| else | |
| holes_module->connect(holes_wire, State::S0); | |
| } | |
| if (!port_wire.empty()) | |
| holes_cell->setPort(w->name, port_wire); | |
| } | |
| } | |
| write_h_buffer(box_inputs); | |
| write_h_buffer(box_outputs); | |
| write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int()); | |
| write_h_buffer(box_count++); | |
| } | |
| std::stringstream r_buffer; | |
| auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); | |
| write_r_buffer(0); | |
| f << "r"; | |
| std::string buffer_str = r_buffer.str(); | |
| int32_t buffer_size_be = to_big_endian(buffer_str.size()); | |
| f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | |
| f.write(buffer_str.data(), buffer_str.size()); | |
| if (holes_module) { | |
| log_push(); | |
| // NB: fixup_ports() will sort ports by name | |
| //holes_module->fixup_ports(); | |
| holes_module->check(); | |
| holes_module->design->selection_stack.emplace_back(false); | |
| RTLIL::Selection& sel = holes_module->design->selection_stack.back(); | |
| sel.select(holes_module); | |
| // TODO: Should not need to opt_merge if we only instantiate | |
| // each box type once... | |
| Pass::call(holes_module->design, "opt_merge -share_all"); | |
| Pass::call(holes_module->design, "flatten -wb"); | |
| // TODO: Should techmap/aigmap/check all lib_whitebox-es just once, | |
| // instead of per write_xaiger call | |
| Pass::call(holes_module->design, "techmap"); | |
| Pass::call(holes_module->design, "aigmap"); | |
| for (auto cell : holes_module->cells()) | |
| if (!cell->type.in("$_NOT_", "$_AND_")) | |
| log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n"); | |
| holes_module->design->selection_stack.pop_back(); | |
| // Move into a new (temporary) design so that "clean" will only | |
| // operate (and run checks on) this one module | |
| RTLIL::Design *holes_design = new RTLIL::Design; | |
| holes_module->design->modules_.erase(holes_module->name); | |
| holes_design->add(holes_module); | |
| Pass::call(holes_design, "clean -purge"); | |
| std::stringstream a_buffer; | |
| XAigerWriter writer(holes_module, true /* holes_mode */); | |
| writer.write_aiger(a_buffer, false /*ascii_mode*/); | |
| delete holes_design; | |
| f << "a"; | |
| std::string buffer_str = a_buffer.str(); | |
| int32_t buffer_size_be = to_big_endian(buffer_str.size()); | |
| f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | |
| f.write(buffer_str.data(), buffer_str.size()); | |
| log_pop(); | |
| } | |
| } | |
| f << "h"; | |
| std::string buffer_str = h_buffer.str(); | |
| int32_t buffer_size_be = to_big_endian(buffer_str.size()); | |
| f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | |
| f.write(buffer_str.data(), buffer_str.size()); | |
| f << "i"; | |
| buffer_str = i_buffer.str(); | |
| buffer_size_be = to_big_endian(buffer_str.size()); | |
| f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | |
| f.write(buffer_str.data(), buffer_str.size()); | |
| //f << "o"; | |
| //buffer_str = o_buffer.str(); | |
| //buffer_size_be = to_big_endian(buffer_str.size()); | |
| //f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); | |
| //f.write(buffer_str.data(), buffer_str.size()); | |
| f << stringf("Generated by %s\n", yosys_version_str); | |
| } | |
| void write_map(std::ostream &f, bool verbose_map) | |
| { | |
| dict<int, string> input_lines; | |
| dict<int, string> output_lines; | |
| dict<int, string> wire_lines; | |
| for (auto wire : module->wires()) | |
| { | |
| //if (!verbose_map && wire->name[0] == '$') | |
| // continue; | |
| SigSpec sig = sigmap(wire); | |
| for (int i = 0; i < GetSize(wire); i++) | |
| { | |
| RTLIL::SigBit b(wire, i); | |
| if (input_bits.count(b)) { | |
| int a = aig_map.at(b); | |
| log_assert((a & 1) == 0); | |
| input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); | |
| } | |
| if (output_bits.count(b)) { | |
| int o = ordered_outputs.at(b); | |
| output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), i, log_id(wire)); | |
| continue; | |
| } | |
| if (verbose_map) { | |
| if (aig_map.count(sig[i]) == 0) | |
| continue; | |
| int a = aig_map.at(sig[i]); | |
| wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire)); | |
| } | |
| } | |
| } | |
| input_lines.sort(); | |
| for (auto &it : input_lines) | |
| f << it.second; | |
| log_assert(input_lines.size() == input_bits.size()); | |
| int box_count = 0; | |
| for (auto cell : box_list) | |
| f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); | |
| output_lines.sort(); | |
| if (omode) | |
| output_lines[State::S0] = "output 0 0 $__dummy__\n"; | |
| for (auto &it : output_lines) | |
| f << it.second; | |
| log_assert(output_lines.size() == output_bits.size()); | |
| wire_lines.sort(); | |
| for (auto &it : wire_lines) | |
| f << it.second; | |
| } | |
| }; | |
| struct XAigerBackend : public Backend { | |
| XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" write_xaiger [options] [filename]\n"); | |
| log("\n"); | |
| log("Write the current design to an XAIGER file. The design must be flattened and\n"); | |
| log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\n"); | |
| log("\n"); | |
| log(" -ascii\n"); | |
| log(" write ASCII version of AIGER format\n"); | |
| log("\n"); | |
| log(" -map <filename>\n"); | |
| log(" write an extra file with port and latch symbols\n"); | |
| log("\n"); | |
| log(" -vmap <filename>\n"); | |
| log(" like -map, but more verbose\n"); | |
| log("\n"); | |
| } | |
| void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| bool ascii_mode = false; | |
| bool verbose_map = false; | |
| std::string map_filename; | |
| log_header(design, "Executing XAIGER backend.\n"); | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-ascii") { | |
| ascii_mode = true; | |
| continue; | |
| } | |
| if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { | |
| map_filename = args[++argidx]; | |
| continue; | |
| } | |
| if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { | |
| map_filename = args[++argidx]; | |
| verbose_map = true; | |
| continue; | |
| } | |
| break; | |
| } | |
| extra_args(f, filename, args, argidx, !ascii_mode); | |
| Module *top_module = design->top_module(); | |
| if (top_module == nullptr) | |
| log_error("Can't find top module in current design!\n"); | |
| XAigerWriter writer(top_module); | |
| writer.write_aiger(*f, ascii_mode); | |
| if (!map_filename.empty()) { | |
| std::ofstream mapf; | |
| mapf.open(map_filename.c_str(), std::ofstream::trunc); | |
| if (mapf.fail()) | |
| log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); | |
| writer.write_map(mapf, verbose_map); | |
| } | |
| } | |
| } XAigerBackend; | |
| PRIVATE_NAMESPACE_END |