nextpnr/fpga_interchange/fpga_interchange.cpp
Keith Rothman c21e23b3eb Fix sign mismatch.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
2021-02-18 14:08:22 -08:00

879 lines
32 KiB
C++

/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2021 Symbiflow Authors
*
*
* 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.
*
*/
#include "fpga_interchange.h"
#include <capnp/message.h>
#include <sstream>
#include <capnp/serialize.h>
#include <kj/std/iostream.h>
#include "PhysicalNetlist.capnp.h"
#include "LogicalNetlist.capnp.h"
#include "zlib.h"
#include "frontend_base.h"
NEXTPNR_NAMESPACE_BEGIN
static void write_message(::capnp::MallocMessageBuilder & message, const std::string &filename) {
kj::Array<capnp::word> words = messageToFlatArray(message);
kj::ArrayPtr<kj::byte> bytes = words.asBytes();
gzFile file = gzopen(filename.c_str(), "w");
NPNR_ASSERT(file != Z_NULL);
NPNR_ASSERT(gzwrite(file, &bytes[0], bytes.size()) == (int)bytes.size());
NPNR_ASSERT(gzclose(file) == Z_OK);
}
struct StringEnumerator {
std::vector<std::string> strings;
std::unordered_map<std::string, size_t> string_to_index;
size_t get_index(const std::string &s) {
auto result = string_to_index.emplace(s, strings.size());
if(result.second) {
// This string was inserted, append.
strings.push_back(s);
}
return result.first->second;
}
};
static PhysicalNetlist::PhysNetlist::RouteBranch::Builder emit_branch(
const Context * ctx,
StringEnumerator * strings,
const std::unordered_map<PipId, PlaceStrength> &pip_place_strength,
PipId pip,
PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) {
const PipInfoPOD & pip_data = pip_info(ctx->chip_info, pip);
const TileTypeInfoPOD & tile_type = loc_info(ctx->chip_info, pip);
const TileInstInfoPOD & tile = ctx->chip_info->tiles[pip.tile];
if(pip_data.site == -1) {
// This is a PIP
auto pip_obj = branch.getRouteSegment().initPip();
pip_obj.setTile(strings->get_index(tile.name.get()));
// FIXME: This might be broken for reverse bi-pips. Re-visit this one.
//
// pip_data might need to mark that it is a reversed bi-pip, so the
// pip emission for the physical netlist would be:
//
// wire0: dst_wire
// wire1: src_wire
// forward: false
//
IdString src_wire_name = IdString(tile_type.wire_data[pip_data.src_index].name);
IdString dst_wire_name = IdString(tile_type.wire_data[pip_data.dst_index].name);
pip_obj.setWire0(strings->get_index(src_wire_name.str(ctx)));
pip_obj.setWire1(strings->get_index(dst_wire_name.str(ctx)));
pip_obj.setForward(true);
pip_obj.setIsFixed(pip_place_strength.at(pip) >= STRENGTH_FIXED);
return branch;
} else {
BelId bel;
bel.tile = pip.tile;
bel.index = pip_data.bel;
const BelInfoPOD & bel_data = bel_info(ctx->chip_info, bel);
IdStringList bel_name = ctx->getBelName(bel);
NPNR_ASSERT(bel_name.size() == 2);
std::string site_and_type = bel_name[0].str(ctx);
auto pos = site_and_type.find_first_of('.');
NPNR_ASSERT(pos != std::string::npos);
std::string site_name = site_and_type.substr(0, pos);
int site_idx = strings->get_index(site_name);
if(bel_data.category == BEL_CATEGORY_LOGIC) {
// This is a psuedo site-pip.
auto in_bel_pin = branch.getRouteSegment().initBelPin();
IdString src_wire_name = IdString(tile_type.wire_data[pip_data.src_index].name);
IdString dst_wire_name = IdString(tile_type.wire_data[pip_data.dst_index].name);
int bel_idx = strings->get_index(bel_name[1].str(ctx));
in_bel_pin.setSite(site_idx);
in_bel_pin.setBel(bel_idx);
in_bel_pin.setPin(strings->get_index(src_wire_name.str(ctx)));
auto subbranch = branch.initBranches(1);
auto bel_pin_branch = subbranch[0];
auto out_bel_pin = bel_pin_branch.getRouteSegment().initBelPin();
out_bel_pin.setSite(site_idx);
out_bel_pin.setBel(bel_idx);
out_bel_pin.setPin(strings->get_index(dst_wire_name.str(ctx)));
return bel_pin_branch;
} else if(bel_data.category == BEL_CATEGORY_ROUTING) {
// This is a site-pip.
IdStringList pip_name = ctx->getPipName(pip);
auto site_pip = branch.getRouteSegment().initSitePIP();
site_pip.setSite(site_idx);
site_pip.setBel(strings->get_index(pip_name[1].str(ctx)));
site_pip.setPin(strings->get_index(pip_name[2].str(ctx)));
site_pip.setIsFixed(pip_place_strength.at(pip) >= STRENGTH_FIXED);
// FIXME: Mark inverter state.
// This is required for US/US+ inverters, because those inverters
// only have 1 input.
return branch;
} else {
NPNR_ASSERT(bel_data.category == BEL_CATEGORY_SITE_PORT);
// This is a site port.
const TileWireInfoPOD &tile_wire = tile_type.wire_data[pip_data.src_index];
int site_pin_idx = strings->get_index(bel_name[1].str(ctx));
if(tile_wire.site == -1) {
// This site port is routing -> site.
auto site_pin = branch.getRouteSegment().initSitePin();
site_pin.setSite(site_idx);
site_pin.setPin(site_pin_idx);
auto subbranch = branch.initBranches(1);
auto bel_pin_branch = subbranch[0];
auto bel_pin = bel_pin_branch.getRouteSegment().initBelPin();
bel_pin.setSite(site_idx);
bel_pin.setBel(site_pin_idx);
bel_pin.setPin(site_pin_idx);
return bel_pin_branch;
} else {
// This site port is site -> routing.
auto bel_pin = branch.getRouteSegment().initBelPin();
bel_pin.setSite(site_idx);
bel_pin.setBel(site_pin_idx);
bel_pin.setPin(site_pin_idx);
auto subbranch = branch.initBranches(1);
auto site_pin_branch = subbranch[0];
auto site_pin = site_pin_branch.getRouteSegment().initSitePin();
site_pin.setSite(site_idx);
site_pin.setPin(site_pin_idx);
return site_pin_branch;
}
}
}
}
static void init_bel_pin(
const Context * ctx,
StringEnumerator * strings,
const BelPin &bel_pin,
PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) {
BelId bel = bel_pin.bel;
IdString pin_name = bel_pin.pin;
IdStringList bel_name = ctx->getBelName(bel);
NPNR_ASSERT(bel_name.size() == 2);
std::string site_and_type = bel_name[0].str(ctx);
auto pos = site_and_type.find_first_of('.');
NPNR_ASSERT(pos != std::string::npos);
std::string site_name = site_and_type.substr(0, pos);
auto out_bel_pin = branch.getRouteSegment().initBelPin();
out_bel_pin.setSite(strings->get_index(site_name));
out_bel_pin.setBel(strings->get_index(bel_name[1].str(ctx)));
out_bel_pin.setPin(strings->get_index(pin_name.str(ctx)));
}
static void emit_net(
const Context * ctx,
StringEnumerator * strings,
const std::unordered_map<WireId, std::vector<PipId>> &pip_downhill,
const std::unordered_map<WireId, std::vector<BelPin>> &sinks,
std::unordered_set<PipId> *pips,
const std::unordered_map<PipId, PlaceStrength> &pip_place_strength,
WireId wire, PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) {
size_t number_branches = 0;
auto downhill_iter = pip_downhill.find(wire);
if(downhill_iter != pip_downhill.end()) {
number_branches += downhill_iter->second.size();
}
auto sink_iter = sinks.find(wire);
if(sink_iter != sinks.end()) {
number_branches += sink_iter->second.size();
}
size_t branch_index = 0;
auto branches = branch.initBranches(number_branches);
if(downhill_iter != pip_downhill.end()) {
const std::vector<PipId> & wire_pips = downhill_iter->second;
for(size_t i = 0; i < wire_pips.size(); ++i) {
PipId pip = wire_pips.at(i);
NPNR_ASSERT(pips->erase(pip) == 1);
PhysicalNetlist::PhysNetlist::RouteBranch::Builder leaf_branch = emit_branch(
ctx, strings, pip_place_strength, pip, branches[branch_index++]);
emit_net(ctx, strings, pip_downhill, sinks, pips,
pip_place_strength,
ctx->getPipDstWire(pip), leaf_branch);
}
}
if(sink_iter != sinks.end()) {
for(const auto bel_pin : sink_iter->second) {
auto leaf_branch = branches[branch_index++];
init_bel_pin(ctx, strings, bel_pin, leaf_branch);
}
}
}
void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::string &filename) {
::capnp::MallocMessageBuilder message;
PhysicalNetlist::PhysNetlist::Builder phys_netlist = message.initRoot<PhysicalNetlist::PhysNetlist>();
phys_netlist.setPart(ctx->get_part());
std::unordered_set<IdString> placed_cells;
for(const auto & cell_pair : ctx->cells) {
const CellInfo & cell = *cell_pair.second;
if(cell.bel == BelId()) {
// This cell was not placed!
continue;
}
NPNR_ASSERT(cell_pair.first == cell.name);
NPNR_ASSERT(placed_cells.emplace(cell.name).second);
}
StringEnumerator strings;
size_t number_placements = 0;
for(auto & cell_name : placed_cells) {
const CellInfo & cell = *ctx->cells.at(cell_name);
if(!ctx->io_port_types.count(cell.type)) {
number_placements += 1;
}
}
std::unordered_map<std::string, std::string> sites;
auto placements = phys_netlist.initPlacements(number_placements);
auto placement_iter = placements.begin();
for(auto & cell_name : placed_cells) {
const CellInfo & cell = *ctx->cells.at(cell_name);
if(ctx->io_port_types.count(cell.type)) {
continue;
}
IdStringList bel_name = ctx->getBelName(cell.bel);
NPNR_ASSERT(bel_name.size() == 2);
std::string site_and_type = bel_name[0].str(ctx);
auto pos = site_and_type.find_first_of('.');
NPNR_ASSERT(pos != std::string::npos);
std::string site_name = site_and_type.substr(0, pos);
std::string site_type = site_and_type.substr(pos + 1);
auto result = sites.emplace(site_name, site_type);
if(!result.second) {
NPNR_ASSERT(result.first->second == site_type);
}
auto placement = *placement_iter++;
placement.setCellName(strings.get_index(cell.name.str(ctx)));
placement.setType(strings.get_index(cell.type.str(ctx)));
placement.setSite(strings.get_index(site_name));
size_t bel_index = strings.get_index(bel_name[1].str(ctx));
placement.setBel(bel_index);
placement.setIsBelFixed(cell.belStrength >= STRENGTH_FIXED);
placement.setIsSiteFixed(cell.belStrength >= STRENGTH_FIXED);
size_t pin_count = 0;
for(const auto & pin : cell.cell_bel_pins) {
pin_count += pin.second.size();
}
auto pins = placement.initPinMap(pin_count);
auto pin_iter = pins.begin();
for(const auto & cell_to_bel_pins : cell.cell_bel_pins) {
std::string cell_pin = cell_to_bel_pins.first.str(ctx);
size_t cell_pin_index = strings.get_index(cell_pin);
for(const auto & bel_pin : cell_to_bel_pins.second) {
auto pin_output = *pin_iter++;
pin_output.setCellPin(cell_pin_index);
pin_output.setBel(bel_index);
pin_output.setBelPin(strings.get_index(bel_pin.str(ctx)));
}
}
}
auto nets = phys_netlist.initPhysNets(ctx->nets.size());
auto net_iter = nets.begin();
for(auto & net_pair : ctx->nets) {
auto &net = *net_pair.second;
auto net_out = *net_iter++;
net_out.setName(strings.get_index(net.name.str(ctx)));
// FIXME: Mark net as signal/vcc/gnd.
//
// Also vcc/gnd nets needs to get special handling through inverters.
std::unordered_map<WireId, BelPin> root_wires;
std::unordered_map<WireId, std::vector<PipId>> pip_downhill;
std::unordered_set<PipId> pips;
const CellInfo *driver_cell = net.driver.cell;
if (driver_cell != nullptr && driver_cell->bel != BelId()) {
for(IdString bel_pin_name : driver_cell->cell_bel_pins.at(net.driver.port)) {
BelPin driver_bel_pin;
driver_bel_pin.bel = driver_cell->bel;
driver_bel_pin.pin = bel_pin_name;
WireId driver_wire = ctx->getBelPinWire(driver_bel_pin.bel, bel_pin_name);
if(driver_wire != WireId()) {
root_wires[driver_wire] = driver_bel_pin;
}
}
}
std::unordered_map<WireId, std::vector<BelPin>> sinks;
for(const auto &port_ref : net.users) {
if(port_ref.cell != nullptr && port_ref.cell->bel != BelId()) {
for(IdString bel_pin_name : port_ref.cell->cell_bel_pins.at(port_ref.port)) {
BelPin sink_bel_pin;
sink_bel_pin.bel = port_ref.cell->bel;
sink_bel_pin.pin = bel_pin_name;
WireId sink_wire = ctx->getBelPinWire(sink_bel_pin.bel, bel_pin_name);
if(sink_wire != WireId()) {
sinks[sink_wire].push_back(sink_bel_pin);
}
}
}
}
std::unordered_map<PipId, PlaceStrength> pip_place_strength;
for(auto &wire_pair : net.wires) {
WireId downhill_wire = wire_pair.first;
PipId pip = wire_pair.second.pip;
PlaceStrength strength = wire_pair.second.strength;
pip_place_strength[pip] = strength;
if(pip != PipId()) {
pips.emplace(pip);
WireId uphill_wire = ctx->getPipSrcWire(pip);
NPNR_ASSERT(downhill_wire != uphill_wire);
pip_downhill[uphill_wire].push_back(pip);
} else {
// This is a root wire.
NPNR_ASSERT(root_wires.count(downhill_wire));
}
}
auto sources = net_out.initSources(root_wires.size());
auto source_iter = sources.begin();
for(const auto & root_pair : root_wires) {
WireId root_wire = root_pair.first;
BelPin src_bel_pin = root_pair.second;
PhysicalNetlist::PhysNetlist::RouteBranch::Builder source_branch = *source_iter++;
init_bel_pin(ctx, &strings, src_bel_pin, source_branch);
emit_net(ctx, &strings, pip_downhill, sinks, &pips, pip_place_strength, root_wire, source_branch);
}
// Any pips that were not part of a tree starting from the source are
// stubs.
auto stubs = net_out.initStubs(pips.size());
auto stub_iter = stubs.begin();
for(PipId pip : pips) {
emit_branch(ctx, &strings, pip_place_strength, pip, *stub_iter++);
}
}
auto site_instances = phys_netlist.initSiteInsts(sites.size());
auto site_inst_iter = site_instances.begin();
auto site_iter = sites.begin();
while(site_iter != sites.end()) {
NPNR_ASSERT(site_inst_iter != site_instances.end());
auto site_instance = *site_inst_iter;
site_instance.setSite(strings.get_index(site_iter->first));
site_instance.setType(strings.get_index(site_iter->second));
++site_inst_iter;
++site_iter;
}
auto str_list = phys_netlist.initStrList(strings.strings.size());
for(size_t i = 0; i < strings.strings.size(); ++i) {
str_list.set(i, strings.strings[i]);
}
write_message(message, filename);
}
struct LogicalNetlistImpl;
size_t get_port_width(LogicalNetlist::Netlist::Port::Reader port) {
if(port.isBit()) {
return 1;
} else {
auto bus = port.getBus();
if(bus.getBusStart() < bus.getBusEnd()) {
return bus.getBusEnd() - bus.getBusStart() + 1;
} else {
return bus.getBusStart() - bus.getBusEnd() + 1;
}
}
}
struct PortKey {
PortKey(int32_t inst_idx, uint32_t port_idx) : inst_idx(inst_idx), port_idx(port_idx) {}
int32_t inst_idx;
uint32_t port_idx;
bool operator == (const PortKey &other) const {
return inst_idx == other.inst_idx && port_idx == other.port_idx;
}
};
NEXTPNR_NAMESPACE_END
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX PortKey>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PortKey &key) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, std::hash<int32_t>()(key.inst_idx));
boost::hash_combine(seed, std::hash<uint32_t>()(key.port_idx));
return seed;
}
};
NEXTPNR_NAMESPACE_BEGIN
struct ModuleReader {
const LogicalNetlistImpl *root;
bool is_top;
LogicalNetlist::Netlist::CellInstance::Reader cell_inst;
LogicalNetlist::Netlist::Cell::Reader cell;
LogicalNetlist::Netlist::CellDeclaration::Reader cell_decl;
std::unordered_map<int32_t, LogicalNetlist::Netlist::Net::Reader> net_indicies;
std::unordered_map<int32_t, std::string> disconnected_nets;
std::unordered_map<PortKey, std::vector<int32_t>> connections;
ModuleReader(const LogicalNetlistImpl *root,
LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top);
};
struct PortReader {
const ModuleReader * module;
size_t port_idx;
};
struct CellReader {
const ModuleReader * module;
size_t inst_idx;
};
struct NetReader {
NetReader(const ModuleReader * module, size_t net_idx) : module(module), net_idx(net_idx) {
scratch.resize(1);
scratch[0] = net_idx;
}
const ModuleReader * module;
size_t net_idx;
LogicalNetlist::Netlist::PropertyMap::Reader property_map;
std::vector<int32_t> scratch;
};
struct LogicalNetlistImpl
{
LogicalNetlist::Netlist::Reader root;
std::vector<std::string> strings;
typedef const ModuleReader ModuleDataType;
typedef const PortReader& ModulePortDataType;
typedef const CellReader& CellDataType;
typedef const NetReader& NetnameDataType;
typedef const std::vector<int32_t>& BitVectorDataType;
LogicalNetlistImpl(LogicalNetlist::Netlist::Reader root) : root(root) {
strings.reserve(root.getStrList().size());
for(auto s : root.getStrList()) {
strings.push_back(s);
}
}
template <typename TFunc> void foreach_module(TFunc Func) const
{
for (const auto &cell_inst : root.getInstList()) {
ModuleReader module(this, cell_inst, /*is_top=*/false);
Func(strings.at(cell_inst.getName()), module);
}
auto top = root.getTopInst();
ModuleReader top_module(this, top, /*is_top=*/true);
Func(strings.at(top.getName()), top_module);
}
template <typename TFunc> void foreach_port(const ModuleReader &mod, TFunc Func) const
{
for(auto port_idx : mod.cell_decl.getPorts()) {
PortReader port_reader;
port_reader.module = &mod;
port_reader.port_idx = port_idx;
auto port = root.getPortList()[port_idx];
Func(strings.at(port.getName()), port_reader);
}
}
template <typename TFunc> void foreach_cell(const ModuleReader &mod, TFunc Func) const
{
for (auto cell_inst_idx : mod.cell.getInsts()) {
auto cell_inst = root.getInstList()[cell_inst_idx];
CellReader cell_reader;
cell_reader.module = &mod;
cell_reader.inst_idx = cell_inst_idx;
Func(strings.at(cell_inst.getName()), cell_reader);
}
}
template <typename TFunc> void foreach_netname(const ModuleReader &mod, TFunc Func) const
{
// std::unordered_map<int32_t, LogicalNetlist::Netlist::Net::Reader> net_indicies;
for(auto net_pair : mod.net_indicies) {
NetReader net_reader(&mod, net_pair.first);
auto net = net_pair.second;
net_reader.property_map = net.getPropMap();
Func(strings.at(net.getName()), net_reader);
}
// std::unordered_map<int32_t, IdString> disconnected_nets;
for(auto net_pair : mod.disconnected_nets) {
NetReader net_reader(&mod, net_pair.first);
Func(net_pair.second, net_reader);
}
}
PortType get_port_type_for_direction(LogicalNetlist::Netlist::Direction dir) const {
if(dir == LogicalNetlist::Netlist::Direction::INPUT) {
return PORT_IN;
} else if (dir == LogicalNetlist::Netlist::Direction::INOUT) {
return PORT_INOUT;
} else if (dir == LogicalNetlist::Netlist::Direction::OUTPUT) {
return PORT_OUT;
} else {
NPNR_ASSERT_FALSE("invalid logical netlist port direction");
}
}
PortType get_port_dir(const PortReader &port_reader) const {
auto port = root.getPortList()[port_reader.port_idx];
auto dir = port.getDir();
return get_port_type_for_direction(dir);
}
const std::string &get_cell_type(const CellReader &cell) const {
auto cell_inst = root.getInstList()[cell.inst_idx];
auto cell_def = root.getCellList()[cell_inst.getCell()];
auto cell_decl = root.getCellDecls()[cell_def.getIndex()];
return strings.at(cell_decl.getName());
}
template <typename TFunc> void foreach_port_dir(const CellReader &cell, TFunc Func) const {
auto cell_inst = root.getInstList()[cell.inst_idx];
auto cell_def = root.getCellList()[cell_inst.getCell()];
auto cell_decl = root.getCellDecls()[cell_def.getIndex()];
for(auto port_idx : cell_decl.getPorts()) {
auto port = root.getPortList()[port_idx];
Func(strings.at(port.getName()), get_port_type_for_direction(port.getDir()));
}
}
template <typename TFunc> void foreach_prop_map(LogicalNetlist::Netlist::PropertyMap::Reader prop_map, TFunc Func) const {
for(auto prop : prop_map.getEntries()) {
if(prop.isTextValue()) {
Func(strings.at(prop.getKey()), Property(strings.at(prop.getTextValue())));
} else if(prop.isIntValue()) {
Func(strings.at(prop.getKey()), Property(prop.getIntValue()));
} else {
NPNR_ASSERT(prop.isBoolValue());
Func(strings.at(prop.getKey()), Property(prop.getBoolValue()));
}
}
}
template <typename TFunc> void foreach_attr(const ModuleReader &mod, TFunc Func) const {
if(mod.is_top) {
// Emit attribute "top" for top instance.
Func("top", Property(1));
}
auto cell_def = root.getCellList()[mod.cell_inst.getCell()];
auto cell_decl = root.getCellDecls()[cell_def.getIndex()];
foreach_prop_map(cell_decl.getPropMap(), Func);
}
template <typename TFunc> void foreach_attr(const CellReader &cell, TFunc Func) const {
auto cell_inst = root.getInstList()[cell.inst_idx];
foreach_prop_map(cell_inst.getPropMap(), Func);
}
template <typename TFunc> void foreach_attr(const PortReader &port_reader, TFunc Func) const {
auto port = root.getPortList()[port_reader.port_idx];
foreach_prop_map(port.getPropMap(), Func);
}
template <typename TFunc> void foreach_attr(const NetReader &net_reader, TFunc Func) const {
foreach_prop_map(net_reader.property_map, Func);
}
template <typename TFunc> void foreach_param(const CellReader &cell_reader, TFunc Func) const
{
auto cell_inst = root.getInstList()[cell_reader.inst_idx];
foreach_prop_map(cell_inst.getPropMap(), Func);
}
template <typename TFunc> void foreach_setting(const ModuleReader &obj, TFunc Func) const
{
foreach_prop_map(root.getPropMap(), Func);
}
template <typename TFunc> void foreach_port_conn(const CellReader &cell, TFunc Func) const
{
auto cell_inst = root.getInstList()[cell.inst_idx];
auto cell_def = root.getCellList()[cell_inst.getCell()];
auto cell_decl = root.getCellDecls()[cell_def.getIndex()];
for(auto port_idx : cell_decl.getPorts()) {
auto port = root.getPortList()[port_idx];
PortKey port_key(cell.inst_idx, port_idx);
const std::vector<int32_t> &connections = cell.module->connections.at(port_key);
Func(strings.at(port.getName()), connections);
}
}
int get_array_offset(const NetReader &port_reader) const
{
return 0;
}
bool is_array_upto(const NetReader &port_reader) const {
return false;
}
int get_array_offset(const PortReader &port_reader) const
{
auto port = root.getPortList()[port_reader.port_idx];
if(port.isBus()) {
auto bus = port.getBus();
return std::min(bus.getBusStart(), bus.getBusEnd());
} else {
return 0;
}
}
bool is_array_upto(const PortReader &port_reader) const {
auto port = root.getPortList()[port_reader.port_idx];
if(port.isBus()) {
auto bus = port.getBus();
return bus.getBusStart() < bus.getBusEnd();
} else {
return false;
}
}
const std::vector<int32_t> &get_port_bits(const PortReader &port_reader) const {
PortKey port_key(-1, port_reader.port_idx);
return port_reader.module->connections.at(port_key);
}
const std::vector<int32_t> &get_net_bits(const NetReader &net) const {
return net.scratch;
}
int get_vector_length(const std::vector<int32_t> &bits) const { return int(bits.size()); }
bool is_vector_bit_constant(const std::vector<int32_t> &bits, int i) const
{
// FIXME: Check if this is right. Assumption is that cells have been
// emitted for GND and VCC, e.g. VCC vcc(.P(vcc_net)).
return false;
}
char get_vector_bit_constval(const std::vector<int32_t>&bits, int i) const
{
// Unreachable!
NPNR_ASSERT(false);
}
int get_vector_bit_signal(const std::vector<int32_t>&bits, int i) const
{
return bits.at(i);
}
};
ModuleReader::ModuleReader(const LogicalNetlistImpl *root,
LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top) :
root(root), is_top(is_top), cell_inst(cell_inst) {
cell = root->root.getCellList()[cell_inst.getCell()];
cell_decl = root->root.getCellDecls()[cell.getIndex()];
// Auto-assign all ports to a net index, and then re-assign based on the
// nets.
int net_idx = 2;
auto ports = root->root.getPortList();
for(auto port_idx : cell_decl.getPorts()) {
auto port = ports[port_idx];
size_t port_width = get_port_width(port);
PortKey port_key(-1, port_idx);
auto result = connections.emplace(port_key, std::vector<int32_t>());
NPNR_ASSERT(result.second);
std::vector<int32_t> & port_connections = result.first->second;
port_connections.resize(port_width);
for(size_t i = 0; i < port_width; ++i) {
port_connections[i] = net_idx++;
}
}
for(auto inst_idx : cell.getInsts()) {
auto inst = root->root.getInstList()[inst_idx];
auto inst_cell = root->root.getCellList()[inst.getCell()];
auto inst_cell_decl = root->root.getCellDecls()[inst_cell.getIndex()];
auto inst_ports = inst_cell_decl.getPorts();
for(auto inst_port_idx : inst_ports) {
PortKey port_key(inst_idx, inst_port_idx);
auto result = connections.emplace(port_key, std::vector<int32_t>());
NPNR_ASSERT(result.second);
auto inst_port = ports[inst_port_idx];
size_t port_width = get_port_width(inst_port);
std::vector<int32_t> & port_connections = result.first->second;
port_connections.resize(port_width);
for(size_t i = 0; i < port_width; ++i) {
port_connections[i] = net_idx++;
}
}
}
auto nets = cell.getNets();
for(size_t i = 0; i < nets.size(); ++i, ++net_idx) {
auto net = nets[i];
net_indicies[net_idx] = net;
for(auto port_inst : net.getPortInsts()) {
int32_t inst_idx = -1;
if(port_inst.isInst()) {
inst_idx = port_inst.getInst();
}
PortKey port_key(inst_idx, port_inst.getPort());
std::vector<int32_t> & port_connections = connections.at(port_key);
if(port_inst.getBusIdx().isSingleBit()) {
port_connections[0] = net_idx;
} else {
port_connections.at(port_inst.getBusIdx().getIdx()) = net_idx;
}
}
}
for(const auto & port_connections : connections) {
for(size_t i = 0; i < port_connections.second.size(); ++i) {
int32_t net_idx = port_connections.second[i];
auto iter = net_indicies.find(net_idx);
if(iter == net_indicies.end()) {
PortKey port_key = port_connections.first;
auto port = ports[port_key.port_idx];
if(port_key.inst_idx != -1 && port.getDir() != LogicalNetlist::Netlist::Direction::OUTPUT) {
log_error("Cell instance %s port %s is disconnected!\n",
root->strings.at(root->root.getInstList()[port_key.inst_idx].getName()).c_str(),
root->strings.at(ports[port_key.port_idx].getName()).c_str()
);
}
disconnected_nets[net_idx] = stringf("%s.%d", root->strings.at(port.getName()).c_str(), i);
}
}
}
}
void FpgaInterchange::read_logical_netlist(Context * ctx, const std::string &filename) {
gzFile file = gzopen(filename.c_str(), "r");
NPNR_ASSERT(file != Z_NULL);
std::vector<uint8_t> output_data;
output_data.resize(4096);
std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
while(true) {
int ret = gzread(file, output_data.data(), output_data.size());
NPNR_ASSERT(ret >= 0);
if(ret > 0) {
sstream.write((const char*)output_data.data(), ret);
NPNR_ASSERT(sstream);
} else {
NPNR_ASSERT(ret == 0);
int error;
gzerror(file, &error);
NPNR_ASSERT(error == Z_OK);
break;
}
}
NPNR_ASSERT(gzclose(file) == Z_OK);
sstream.seekg(0);
kj::std::StdInputStream istream(sstream);
capnp::ReaderOptions reader_options;
reader_options.traversalLimitInWords = 32llu*1024llu*1024llu*1024llu;
capnp::InputStreamMessageReader message_reader(istream, reader_options);
LogicalNetlist::Netlist::Reader netlist = message_reader.getRoot<LogicalNetlist::Netlist>();
LogicalNetlistImpl netlist_reader(netlist);
GenericFrontend<LogicalNetlistImpl>(ctx, netlist_reader, /*split_io=*/false)();
}
NEXTPNR_NAMESPACE_END