Implementation as in D2 deliverable
This commit is contained in:
parent
c504212b59
commit
0a4cfb77db
@ -1,2 +1,33 @@
|
||||
message(STATUS "Configuring Himbaechel-NG-ULTRA uarch")
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(himbaechel-ng-ultra-chipdb NONE)
|
||||
|
||||
set(HIMBAECHEL_PRJBEYOND_DB "" CACHE STRING
|
||||
"Path to a Project Beyond database")
|
||||
|
||||
set(chipdb_binaries)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra)
|
||||
foreach(device "NG-ULTRA")
|
||||
if("${HIMBAECHEL_PRJBEYOND_DB}" STREQUAL "")
|
||||
message(SEND_ERROR "HIMBAECHEL_PRJBEYOND_DB must be set to a prjbeyond database checkout")
|
||||
endif()
|
||||
|
||||
set(device_bba ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/ng-ultra.bba)
|
||||
set(device_bin ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/ng-ultra.bin)
|
||||
add_custom_command(
|
||||
OUTPUT ${device_bin}
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py --db ${HIMBAECHEL_PRJBEYOND_DB} --device ${device} --bba ${device_bba}
|
||||
COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${device_bba} ${device_bin}.new
|
||||
# atomically update
|
||||
COMMAND ${CMAKE_COMMAND} -E rename ${device_bin}.new ${device_bin}
|
||||
DEPENDS
|
||||
bbasm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/constids.inc
|
||||
VERBATIM)
|
||||
list(APPEND chipdb_binaries ${device_bin})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(chipdb-himbaechel-ng-ultra ALL DEPENDS ${chipdb_binaries})
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/ DESTINATION share/nextpnr/himbaechel/ng-ultra
|
||||
PATTERN "*.bba" EXCLUDE)
|
||||
|
389
himbaechel/uarch/ng-ultra/bitstream.cc
Normal file
389
himbaechel/uarch/ng-ultra/bitstream.cc
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#include "extra_data.h"
|
||||
#include "himbaechel_api.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "ng_ultra.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
struct BitstreamJsonBackend
|
||||
{
|
||||
Context *ctx;
|
||||
nlohmann::json json;
|
||||
NgUltraImpl *uarch;
|
||||
std::ostream &out;
|
||||
|
||||
BitstreamJsonBackend(Context *ctx, NgUltraImpl *uarch, std::ostream &out) : ctx(ctx), uarch(uarch), out(out){};
|
||||
|
||||
std::string update_name(std::string tile, std::string name)
|
||||
{
|
||||
if (boost::starts_with(tile,"FENCE[")) {
|
||||
char last = tile[tile.size()-2];
|
||||
switch(last)
|
||||
{
|
||||
case 'T':
|
||||
case 'B':
|
||||
case 'U':
|
||||
case 'L':
|
||||
std::string loc = tile.substr(tile.find("[")+1, tile.find("x")-tile.find("["));
|
||||
boost::replace_all(name, "1x", loc);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
if (boost::starts_with(tile,"TILE[") && boost::algorithm::contains(name,".FE")) {
|
||||
std::string last = name.substr(name.rfind('.') + 1);
|
||||
if (last[0]=='D') {
|
||||
boost::replace_all(name, ".D", ".");
|
||||
boost::replace_all(name, ".FE", ".DFF");
|
||||
return name;
|
||||
}
|
||||
if (last=="L" || last=="R") {
|
||||
boost::replace_all(name, ".FE", ".DFF");
|
||||
return name;
|
||||
}
|
||||
if (last=="CK") {
|
||||
boost::replace_all(name, ".FE", ".DFF");
|
||||
return name;
|
||||
}
|
||||
if (last[0]=='L') {
|
||||
boost::replace_all(name, ".L", ".");
|
||||
boost::replace_all(name, ".FE", ".LUT");
|
||||
return name;
|
||||
}
|
||||
if (last[0]=='P') {
|
||||
boost::replace_all(name, ".PI", ".I");
|
||||
boost::replace_all(name, ".FE", ".LUT");
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void add_net(std::set<std::string> &nets, std::string src_tile, std::string src_name, std::string dst_tile, std::string dst_name, IdString src_type, IdString dst_type)
|
||||
{
|
||||
if (src_type.in(ctx->id("CROSSBAR_INPUT_WIRE"), ctx->id("LUT_PERMUTATION_WIRE"), ctx->id("MUX_WIRE"), ctx->id("INTERCONNECT_INPUT"))) return;
|
||||
if (dst_type == ctx->id("MUX_WIRE"))
|
||||
dst_name = dst_name.substr(0, dst_name.rfind('.'));
|
||||
src_name = update_name(src_tile, src_name);
|
||||
dst_name = update_name(dst_tile, dst_name);
|
||||
|
||||
nets.emplace(stringf("%s:%s->%s:%s", src_tile.c_str(), src_name.c_str(), dst_tile.c_str(), dst_name.c_str()));
|
||||
}
|
||||
|
||||
std::string cleanup_name(std::string name)
|
||||
{
|
||||
std::replace(name.begin(), name.end(), '$', '_');
|
||||
return name;
|
||||
}
|
||||
|
||||
void write_nets()
|
||||
{
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *ni = net.second.get();
|
||||
if (ni->wires.size()==0) continue;
|
||||
auto& cfg = json["nets"][cleanup_name(ni->name.c_str(ctx))];
|
||||
std::set<std::string> nets;
|
||||
for (auto &w : ni->wires) {
|
||||
if (w.second.pip != PipId()) {
|
||||
PipId pip = w.second.pip;
|
||||
auto &pd = chip_pip_info(ctx->chip_info, pip);
|
||||
const auto &extra_data = *reinterpret_cast<const NGUltraPipExtraDataPOD *>(pd.extra_data.get());
|
||||
WireId swire = ctx->getPipSrcWire(pip);
|
||||
IdString src = ctx->getWireName(swire)[1];
|
||||
IdString src_type = ctx->getWireType(swire);
|
||||
|
||||
IdString src_orig = IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].name);
|
||||
IdString src_orig_type = IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].wire_type);
|
||||
|
||||
WireId dwire = ctx->getPipDstWire(pip);
|
||||
IdString dst = ctx->getWireName(dwire)[1];
|
||||
IdString dst_type = ctx->getWireType(dwire);
|
||||
|
||||
std::string s_tile_name = uarch->tile_name(swire.tile);
|
||||
std::string tile_name = uarch->tile_name(pip.tile);
|
||||
|
||||
if (src_orig!=src)
|
||||
add_net(nets, s_tile_name, src.c_str(ctx), tile_name, src_orig.c_str(ctx), src_type, src_orig_type);
|
||||
if (!extra_data.name || (extra_data.type != PipExtra::PIP_EXTRA_BYPASS && extra_data.type != PipExtra::PIP_EXTRA_VIRTUAL && extra_data.type != PipExtra::PIP_EXTRA_MUX))
|
||||
add_net(nets, tile_name, src_orig.c_str(ctx), tile_name, dst.c_str(ctx), src_orig_type, dst_type);
|
||||
} else if (ni->wires.size()==1) {
|
||||
IdString src = ctx->getWireName(w.first)[1];
|
||||
IdString src_type = ctx->getWireType(w.first);
|
||||
std::string s_tile_name = uarch->tile_name(w.first.tile);
|
||||
for (auto &u : ni->users){
|
||||
std::string tile_name = uarch->tile_name(u.cell->bel.tile);
|
||||
IdString bel_name = ctx->getBelName(u.cell->bel)[1];
|
||||
add_net(nets, s_tile_name, src.c_str(ctx), tile_name, stringf("%s.%s", bel_name.c_str(ctx), u.port.c_str(ctx)), src_type, src_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg = nets;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename KeyType>
|
||||
std::string str_or_n_value(const dict<KeyType, Property> &ct, const KeyType &key, std::string def = "N")
|
||||
{
|
||||
auto found = ct.find(key);
|
||||
if (found == ct.end())
|
||||
return def;
|
||||
else {
|
||||
if (!found->second.is_string)
|
||||
log_error("Expecting string value but got integer %d.\n", int(found->second.intval));
|
||||
if (found->second.as_string().empty())
|
||||
return def;
|
||||
return found->second.as_string();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
nlohmann::json &get_cfg(CellInfo *cell, std::string type = "", std::string rename = "")
|
||||
{
|
||||
auto& cfg = json["instances"][cleanup_name(rename.empty() ? cell->name.c_str(ctx) : rename.c_str())];
|
||||
std::string tile_name = uarch->tile_name(cell->bel.tile);
|
||||
IdString idx = ctx->getBelName(cell->bel)[1];
|
||||
std::string belname = idx.c_str(ctx);
|
||||
cfg["location"] = tile_name + ":" + belname;
|
||||
cfg["type"] = type.empty() ? str_or_default(cell->params, ctx->id("type"), "") : type;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
nlohmann::json &get_cfg_fe(CellInfo *cell, std::string type, std::string replace, std::string postfix = "")
|
||||
{
|
||||
auto& cfg = json["instances"][cleanup_name(cell->name.c_str(ctx)) + postfix];
|
||||
std::string tile_name = uarch->tile_name(cell->bel.tile);
|
||||
IdString idx = ctx->getBelName(cell->bel)[1];
|
||||
std::string belname = idx.c_str(ctx);
|
||||
boost::replace_all(belname, ".FE", replace);
|
||||
cfg["location"] = tile_name + ":" + belname;
|
||||
cfg["type"] = type;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void write_iop(CellInfo *cell) {
|
||||
auto& cfg = get_cfg(cell, "", str_or_default(cell->params, ctx->id("iobname"), ""));
|
||||
//cfg["config"]["alias_vhdl"] = str_or_default(cell->params, ctx->id("alias_vhdl"), "");
|
||||
//cfg["config"]["alias_vlog"] = str_or_default(cell->params, ctx->id("alias_vlog"), "");
|
||||
//cfg["config"]["differential"] = str_or_n_value(cell->params, ctx->id("differential"), "N");
|
||||
cfg["config"]["drive"] = str_or_default(cell->params, ctx->id("drive"), "2mA");
|
||||
//cfg["config"]["dynDrive"] = str_or_n_value(cell->params, ctx->id("dynDrive"), "N");
|
||||
//cfg["config"]["dynInput"] = str_or_n_value(cell->params, ctx->id("dynInput"), "N");
|
||||
//cfg["config"]["dynTerm"] = str_or_n_value(cell->params, ctx->id("dynTerm"), "N");
|
||||
//cfg["config"]["extra"] = int_or_default(cell->params, ctx->id("extra"), 2);
|
||||
//cfg["config"]["inputDelayLine"] = str_or_default(cell->params, ctx->id("inputDelayLine"), "");
|
||||
//cfg["config"]["inputDelayOn"] = str_or_n_value(cell->params, ctx->id("inputDelayOn"), "N");
|
||||
//cfg["config"]["inputSignalSlope"] = str_or_default(cell->params, ctx->id("inputSignalSlope"), "");
|
||||
cfg["config"]["location"] = str_or_default(cell->params, ctx->id("location"), "");
|
||||
//cfg["config"]["locked"] = bool_or_default(cell->params, ctx->id("locked"), false);
|
||||
//cfg["config"]["outputCapacity"] = str_or_default(cell->params, ctx->id("outputCapacity"), "");
|
||||
//cfg["config"]["outputDelayLine"] = str_or_default(cell->params, ctx->id("outputDelayLine"), "");
|
||||
//cfg["config"]["outputDelayOn"] = str_or_n_value(cell->params, ctx->id("outputDelayOn"), "N");
|
||||
//cfg["config"]["slewRate"] = str_or_default(cell->params, ctx->id("slewRate"), "");
|
||||
cfg["config"]["standard"] = str_or_default(cell->params, ctx->id("standard"), "LVCMOS");
|
||||
//cfg["config"]["termination"] = str_or_n_value(cell->params, ctx->id("termination"), "N");
|
||||
//cfg["config"]["terminationReference"] = str_or_n_value(cell->params, ctx->id("terminationReference"), "N");
|
||||
//cfg["config"]["turbo"] = str_or_n_value(cell->params, ctx->id("turbo"), "N");
|
||||
//cfg["config"]["weakTermination"] = str_or_n_value(cell->params, ctx->id("weakTermination"), "N");
|
||||
|
||||
std::string tile_name = uarch->tile_name(cell->bel.tile);
|
||||
std::string bank = tile_name.substr(0, tile_name.rfind(':'));
|
||||
if (uarch->bank_voltage.count(bank)==0) {
|
||||
if (bank=="IOB0" || bank=="IOB1" || bank=="IOB6" || bank=="IOB7")
|
||||
uarch->bank_voltage[bank] = "3.3V";
|
||||
else
|
||||
uarch->bank_voltage[bank] = "1.8V";
|
||||
}
|
||||
}
|
||||
|
||||
void write_dfr(CellInfo *cell) {
|
||||
auto& cfg = get_cfg(cell, "BFR");
|
||||
//cfg["config"]["data_inv"] = bool_or_default(cell->params, ctx->id("data_inv"), false);
|
||||
cfg["config"]["dff_edge"] = bool_or_default(cell->params, ctx->id("dff_edge"), false);
|
||||
//cfg["config"]["dff_init"] = bool_or_default(cell->params, ctx->id("dff_init"), false);
|
||||
//cfg["config"]["dff_load"] = bool_or_default(cell->params, ctx->id("dff_load"), false);
|
||||
cfg["config"]["dff_sync"] = bool_or_default(cell->params, ctx->id("dff_sync"), false);
|
||||
//cfg["config"]["dff_type"] = bool_or_default(cell->params, ctx->id("dff_type"), false);
|
||||
//cfg["config"]["location"] = str_or_default(cell->params, ctx->id("location"), "");
|
||||
cfg["config"]["mode"] = int_or_default(cell->params, ctx->id("mode"), 2);
|
||||
//cfg["config"]["path"] = int_or_default(cell->params, ctx->id("path"), 0);
|
||||
cfg["config"]["iobname"] = str_or_default(cell->params, ctx->id("iobname"), "");
|
||||
}
|
||||
|
||||
void write_cy(CellInfo *cell) {
|
||||
auto& cfg = get_cfg(cell, "CY");
|
||||
cfg["config"]["add_carry"] = int_or_default(cell->params, ctx->id("add_carry"), 0);
|
||||
cfg["config"]["shifter"] = bool_or_default(cell->params, ctx->id("shifter"), false);
|
||||
}
|
||||
|
||||
void write_fe(CellInfo *cell) {
|
||||
if (bool_or_default(cell->params, id_lut_used)) {
|
||||
auto& cfg = get_cfg_fe(cell, "LUT", ".LUT");
|
||||
Property init = get_or_default(cell->params, id_lut_table, Property()).extract(0, 16);
|
||||
std::string lut = init.str;
|
||||
std::reverse(lut.begin(), lut.end());
|
||||
cfg["config"]["lut_table"] = lut;
|
||||
}
|
||||
if (bool_or_default(cell->params, id_dff_used)) {
|
||||
std::string subtype = str_or_default(cell->params, ctx->id("type"), "DFF");
|
||||
auto& cfg = get_cfg_fe(cell, subtype, ".DFF", "_D");
|
||||
if (subtype =="DFF") {
|
||||
//cfg["config"]["dff_ctxt"] = int_or_default(cell->params, ctx->id("dff_ctxt"), 0);
|
||||
cfg["config"]["dff_edge"] = bool_or_default(cell->params, ctx->id("dff_edge"), false);
|
||||
cfg["config"]["dff_init"] = bool_or_default(cell->params, ctx->id("dff_init"), false);
|
||||
cfg["config"]["dff_load"] = bool_or_default(cell->params, ctx->id("dff_load"), false);
|
||||
cfg["config"]["dff_sync"] = bool_or_default(cell->params, ctx->id("dff_sync"), false);
|
||||
cfg["config"]["dff_type"] = bool_or_default(cell->params, ctx->id("dff_type"), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_iom(CellInfo *cell) {
|
||||
auto& cfg = get_cfg(cell, "IOM");
|
||||
cfg["config"]["pads_path"] = str_or_default(cell->params, ctx->id("pads_path"), ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
|
||||
}
|
||||
|
||||
void write_gck(CellInfo *cell) {
|
||||
auto& cfg = get_cfg(cell, "GCK");
|
||||
cfg["config"]["inv_in"] = bool_or_default(cell->params, ctx->id("inv_in"), false);
|
||||
cfg["config"]["inv_out"] = bool_or_default(cell->params, ctx->id("inv_out"), false);
|
||||
cfg["config"]["std_mode"] = str_or_default(cell->params, ctx->id("std_mode"), "BYPASS");
|
||||
}
|
||||
|
||||
void write_wfg(CellInfo *cell) {
|
||||
std::string subtype = str_or_default(cell->params, ctx->id("type"), "WFB");
|
||||
get_cfg(cell, subtype);
|
||||
}
|
||||
|
||||
void write_interconnections()
|
||||
{
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *ni = net.second.get();
|
||||
if (ni->wires.size()==0) continue;
|
||||
std::vector<std::string> nets;
|
||||
for (auto &w : ni->wires) {
|
||||
if (w.second.pip != PipId()) {
|
||||
PipId pip = w.second.pip;
|
||||
const auto &pip_data = chip_pip_info(ctx->chip_info, w.second.pip);
|
||||
const auto &extra_data = *reinterpret_cast<const NGUltraPipExtraDataPOD *>(pip_data.extra_data.get());
|
||||
if (!extra_data.name or extra_data.type != PipExtra::PIP_EXTRA_INTERCONNECT) continue;
|
||||
auto &pd = chip_pip_info(ctx->chip_info, pip);
|
||||
IdString src = IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].name);
|
||||
std::string tile_name = uarch->tile_name(pip.tile);
|
||||
std::string src_name = src.c_str(ctx);
|
||||
std::string type = "OTC";
|
||||
if (src_name.find("UI1x") != std::string::npos)
|
||||
type = "ITC";
|
||||
if (boost::starts_with(src_name,"SO1.")) type = "OTS";
|
||||
if (boost::starts_with(src_name,"SI1.")) type = "ITS";
|
||||
std::string name = cleanup_name(std::string(ni->name.c_str(ctx))+ "_" + type);
|
||||
while (json["instances"].contains(name))
|
||||
name += "_2";
|
||||
auto& cfg = json["instances"][name];
|
||||
cfg["type"] = type;
|
||||
src_name = update_name(tile_name, src_name);
|
||||
src_name = src_name.substr(0, src_name.size() - 2);
|
||||
cfg["location"] = tile_name + ":" + src_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_instances()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
switch (cell.second->type.index) {
|
||||
case id_IOP.index : write_iop(cell.second.get()); break;
|
||||
case id_IOTP.index : write_iop(cell.second.get()); break;
|
||||
case id_BEYOND_FE.index : write_fe(cell.second.get()); break;
|
||||
case id_CY.index : write_cy(cell.second.get()); break;
|
||||
case id_WFG.index : write_wfg(cell.second.get()); break;
|
||||
case id_GCK.index : write_gck(cell.second.get()); break;
|
||||
case id_IOM.index : write_iom(cell.second.get()); break;
|
||||
case id_DDFR.index: write_dfr(cell.second.get()); break;
|
||||
case id_DFR.index: write_dfr(cell.second.get()); break;
|
||||
//case id_XLUT.index:
|
||||
//case id_RAM.index:
|
||||
//case id_RF.index:
|
||||
//case id_XRF.index:
|
||||
//case id_FIFO.index:
|
||||
//case id_XFIFO.index:
|
||||
//case id_CDC.index:
|
||||
//case id_XCDC.index:
|
||||
//case id_CRX.index:
|
||||
//case id_CTX.index:
|
||||
//case id_DSP.index:
|
||||
//case id_PLL.index:
|
||||
//case id_PMA.index:
|
||||
//case id_Service.index:
|
||||
//case id_SOCIF.index:
|
||||
}
|
||||
}
|
||||
write_interconnections();
|
||||
}
|
||||
|
||||
void write_setup()
|
||||
{
|
||||
json["setup"]["variant"] = "NG-ULTRA";
|
||||
auto& cfg = json["setup"]["iobanks"];
|
||||
for (auto &bank : uarch->bank_voltage)
|
||||
cfg[bank.first] = bank.second;
|
||||
}
|
||||
|
||||
void write_json()
|
||||
{
|
||||
write_nets();
|
||||
write_instances();
|
||||
write_setup();
|
||||
|
||||
out << std::setw(4) << json << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void NgUltraImpl::write_bitstream_json(const std::string &filename)
|
||||
{
|
||||
std::ofstream out(filename);
|
||||
if (!out)
|
||||
log_error("failed to open file %s for writing (%s)\n", filename.c_str(), strerror(errno));
|
||||
|
||||
BitstreamJsonBackend be(ctx, this, out);
|
||||
be.write_json();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
91
himbaechel/uarch/ng-ultra/cells.cc
Normal file
91
himbaechel/uarch/ng-ultra/cells.cc
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2023 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pack.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
std::unique_ptr<CellInfo> NgUltraPacker::create_cell(IdString type, IdString name)
|
||||
{
|
||||
auto cell = std::make_unique<CellInfo>(ctx, name, type);
|
||||
|
||||
auto add_port = [&](const std::string &name, PortType dir) {
|
||||
IdString id = ctx->id(name);
|
||||
cell->ports[id].name = id;
|
||||
cell->ports[id].type = dir;
|
||||
};
|
||||
if (type == id_BEYOND_FE) {
|
||||
add_port("I1", PORT_IN);
|
||||
add_port("I2", PORT_IN);
|
||||
add_port("I3", PORT_IN);
|
||||
add_port("I4", PORT_IN);
|
||||
add_port("LO", PORT_OUT);
|
||||
add_port("DI", PORT_IN);
|
||||
add_port("L", PORT_IN);
|
||||
add_port("CK", PORT_IN);
|
||||
add_port("R", PORT_IN);
|
||||
add_port("DO", PORT_OUT);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
CellInfo *NgUltraPacker::create_cell_ptr(IdString type, IdString name)
|
||||
{
|
||||
CellInfo *cell = ctx->createCell(name, type);
|
||||
|
||||
auto add_port = [&](const std::string &name, PortType dir) {
|
||||
IdString id = ctx->id(name);
|
||||
cell->ports[id].name = id;
|
||||
cell->ports[id].type = dir;
|
||||
};
|
||||
if (type == id_BEYOND_FE) {
|
||||
add_port("I1", PORT_IN);
|
||||
add_port("I2", PORT_IN);
|
||||
add_port("I3", PORT_IN);
|
||||
add_port("I4", PORT_IN);
|
||||
add_port("LO", PORT_OUT);
|
||||
add_port("DI", PORT_IN);
|
||||
add_port("L", PORT_IN);
|
||||
add_port("CK", PORT_IN);
|
||||
add_port("R", PORT_IN);
|
||||
add_port("DO", PORT_OUT);
|
||||
} else if (type == id_DFR) {
|
||||
add_port("I", PORT_IN);
|
||||
add_port("O", PORT_OUT);
|
||||
add_port("L", PORT_IN);
|
||||
add_port("CK", PORT_IN);
|
||||
add_port("R", PORT_IN);
|
||||
} else if (type == id_DDFR) {
|
||||
add_port("I", PORT_IN);
|
||||
add_port("O", PORT_OUT);
|
||||
add_port("L", PORT_IN);
|
||||
add_port("CK", PORT_IN);
|
||||
add_port("R", PORT_IN);
|
||||
add_port("I2", PORT_IN);
|
||||
add_port("O2", PORT_OUT);
|
||||
add_port("CKF", PORT_IN);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
File diff suppressed because it is too large
Load Diff
212
himbaechel/uarch/ng-ultra/csv.cc
Normal file
212
himbaechel/uarch/ng-ultra/csv.cc
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include "extra_data.h"
|
||||
#include "himbaechel_api.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "ng_ultra.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void NgUltraImpl::parse_csv(const std::string &filename)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if (!in)
|
||||
log_error("failed to open CSV file '%s'\n", filename.c_str());
|
||||
log_info("Parsing CSV file..\n");
|
||||
std::string line;
|
||||
std::string linebuf;
|
||||
int lineno = 0;
|
||||
enum uint8_t
|
||||
{
|
||||
IO_PADS = 0,
|
||||
IO_BANKS,
|
||||
IO_GCKS,
|
||||
IO_ERROR
|
||||
} line_type;
|
||||
|
||||
auto isempty = [](const std::string &str) {
|
||||
return std::all_of(str.begin(), str.end(), [](char c) { return std::isspace(c); });
|
||||
};
|
||||
auto is_number = [](const std::string &str) {
|
||||
return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
|
||||
};
|
||||
auto get_cells = [&](std::string str) {
|
||||
std::vector<CellInfo *> tgt_cells;
|
||||
IdString cellname = ctx->id(str);
|
||||
if (ctx->cells.count(cellname))
|
||||
tgt_cells.push_back(ctx->cells.at(cellname).get());
|
||||
return tgt_cells;
|
||||
};
|
||||
|
||||
line_type = IO_PADS;
|
||||
pool<std::string> banks_used;
|
||||
while (std::getline(in, line)) {
|
||||
++lineno;
|
||||
// Trim comments, from # until end of the line
|
||||
size_t cstart = line.find('#');
|
||||
if (cstart != std::string::npos)
|
||||
line = line.substr(0, cstart);
|
||||
if (isempty(line))
|
||||
continue;
|
||||
|
||||
std::vector<std::string> arguments;
|
||||
boost::split(arguments, line, boost::is_any_of(","));
|
||||
if (arguments.empty())
|
||||
continue;
|
||||
switch(line_type) {
|
||||
case IO_PADS:
|
||||
{
|
||||
if (arguments.size()==1 && arguments[0][0]=='!') {
|
||||
line_type = IO_BANKS;
|
||||
continue;
|
||||
}
|
||||
if (arguments.size()!=15)
|
||||
log_error("number of parameters in line %d must be 15\n", lineno);
|
||||
|
||||
if (!(boost::starts_with(arguments.at(1), "IOB") && boost::contains(arguments.at(1),"_D")))
|
||||
log_error("invalid location name '%s' must start with 'IOB' in line %d\n", arguments.at(1).c_str(), lineno);
|
||||
|
||||
const char* standard_values[] = { "LVDS", "LVCMOS", "SSTL", "HSTL" };
|
||||
auto it = std::find(std::begin(standard_values),std::end(standard_values), arguments.at(2));
|
||||
if (it == std::end(standard_values))
|
||||
log_error("unknown standard value '%s' in line %d\n", arguments.at(2).c_str(), lineno);
|
||||
|
||||
const char* drive_values[] = { "2mA", "4mA", "8mA", "16mA", "I", "II" };
|
||||
it = std::find(std::begin(drive_values),std::end(drive_values), arguments.at(3));
|
||||
if (it == std::end(drive_values))
|
||||
log_error("unknown drive value '%s' in line %d\n", arguments.at(3).c_str(), lineno);
|
||||
|
||||
const char* weak_values[] = { "None", "PullDown", "PullUp", "Keeper" };
|
||||
it = std::find(std::begin(weak_values),std::end(weak_values), arguments.at(4));
|
||||
if (it == std::end(weak_values))
|
||||
log_error("unknown weak termination value '%s' in line %d\n", arguments.at(4).c_str(), lineno);
|
||||
|
||||
const char* slew_values[] = { "Slow", "Medium", "Fast" };
|
||||
it = std::find(std::begin(slew_values),std::end(slew_values), arguments.at(5));
|
||||
if (it == std::end(slew_values))
|
||||
log_error("unknown weak termination value '%s' in line %d\n", arguments.at(5).c_str(), lineno);
|
||||
|
||||
if (!arguments.at(6).empty() && !is_number(arguments.at(6)))
|
||||
log_error("termination must be string containing int, value '%s' in line %d\n", arguments.at(6).c_str(), lineno);
|
||||
|
||||
if (!arguments.at(7).empty() && !is_number(arguments.at(7)))
|
||||
log_error("input delay must be number, value '%s' in line %d\n", arguments.at(7).c_str(), lineno);
|
||||
if (!arguments.at(8).empty() && !is_number(arguments.at(8)))
|
||||
log_error("output delay must be number, value '%s' in line %d\n", arguments.at(8).c_str(), lineno);
|
||||
|
||||
if (!arguments.at(9).empty() && arguments.at(9) != "True" && arguments.at(9) != "False")
|
||||
log_error("differential must be boolean, value '%s' in line %d\n", arguments.at(9).c_str(), lineno);
|
||||
|
||||
const char* termref_values[] = { "Floating", "VT" };
|
||||
it = std::find(std::begin(termref_values),std::end(termref_values), arguments.at(10));
|
||||
if (it == std::end(termref_values))
|
||||
log_error("unknown termination reference value '%s' in line %d\n", arguments.at(10).c_str(), lineno);
|
||||
|
||||
if (!arguments.at(11).empty() && arguments.at(11) != "True" && arguments.at(11) != "False")
|
||||
log_error("turbo must be boolean, value '%s' in line %d\n", arguments.at(11).c_str(), lineno);
|
||||
|
||||
if (!arguments.at(12).empty() && !is_number(arguments.at(12)))
|
||||
log_error("signal slope must be number, value '%s' in line %d\n", arguments.at(12).c_str(), lineno);
|
||||
if (!arguments.at(13).empty() && !is_number(arguments.at(13)))
|
||||
log_error("output capacity must be number, value '%s' in line %d\n", arguments.at(13).c_str(), lineno);
|
||||
|
||||
const char* registered_values[] = { "Auto", "I", "IC", "O", "OC", "IO", "IOC" };
|
||||
it = std::find(std::begin(registered_values),std::end(registered_values), arguments.at(14));
|
||||
if (it == std::end(registered_values))
|
||||
log_error("unknown registered value '%s' in line %d\n", arguments.at(14).c_str(), lineno);
|
||||
|
||||
std::vector<CellInfo *> dest = get_cells(arguments.at(0));
|
||||
for (auto c : dest) {
|
||||
c->attrs[id_LOC] = arguments.at(1);
|
||||
c->params[ctx->id("iobname")] = arguments.at(0);
|
||||
c->params[ctx->id("standard")] = arguments.at(2);
|
||||
c->params[ctx->id("drive")] = arguments.at(3);
|
||||
c->params[ctx->id("weakTermination")] = arguments.at(4);
|
||||
c->params[ctx->id("slewRate")] = arguments.at(5);
|
||||
c->params[ctx->id("termination")] = arguments.at(6);
|
||||
c->params[ctx->id("inputDelayLine")] = arguments.at(7);
|
||||
c->params[ctx->id("outputDelayLine")] = arguments.at(8);
|
||||
c->params[ctx->id("differential")] = arguments.at(9);
|
||||
c->params[ctx->id("terminationReference")] = arguments.at(10);
|
||||
c->params[ctx->id("turbo")] = arguments.at(11);
|
||||
c->params[ctx->id("inputSignalSlope")] = arguments.at(12);
|
||||
c->params[ctx->id("outputCapacity")] = arguments.at(13);
|
||||
//c->params[ctx->id("IO_PATH")] = arguments.at(14);
|
||||
}
|
||||
if (dest.size()==0)
|
||||
log_warning("Pad with name '%s' not found in netlist.\n", arguments.at(0).c_str());
|
||||
|
||||
std::string bank_name = arguments.at(1).substr(0,arguments.at(1).find_first_of('_'));
|
||||
banks_used.emplace(bank_name);
|
||||
}
|
||||
break;
|
||||
case IO_BANKS:
|
||||
{
|
||||
if (arguments.size()==1 && arguments[0][0]=='!') {
|
||||
line_type = IO_GCKS;
|
||||
continue;
|
||||
}
|
||||
if (arguments.size()!=3)
|
||||
log_error("number of parameters in line %d must be 3\n", lineno);
|
||||
|
||||
if (!boost::starts_with(arguments.at(0), "IOB"))
|
||||
log_error("wrong bank name '%s' in line %d\n", arguments.at(0).c_str(), lineno);
|
||||
|
||||
const char* voltages[] = { "1.5V", "1.8V", "2.5V", "3.3V" };
|
||||
auto it = std::find(std::begin(voltages),std::end(voltages), arguments.at(1));
|
||||
if (it == std::end(voltages))
|
||||
log_error("unknown voltage level '%s' in line %d\n", arguments.at(1).c_str(), lineno);
|
||||
|
||||
bank_voltage[arguments.at(0)] = arguments.at(1);
|
||||
}
|
||||
break;
|
||||
case IO_GCKS:
|
||||
{
|
||||
if (arguments.size()==1 && arguments[0][0]=='!') {
|
||||
line_type = IO_ERROR;
|
||||
continue;
|
||||
}
|
||||
if (arguments.size()!=2)
|
||||
log_error("number of parameters in line %d must be 2\n", lineno);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("switching to unknown block of data in line %d\n", lineno);
|
||||
}
|
||||
}
|
||||
for(auto& bank_name : banks_used) {
|
||||
if (bank_voltage.count(bank_name) == 0) {
|
||||
log_error("IO for bank '%s' defined, but no bank configuration.\n", bank_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
65
himbaechel/uarch/ng-ultra/extra_data.h
Normal file
65
himbaechel/uarch/ng-ultra/extra_data.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef NGULTRA_EXTRA_DATA_H
|
||||
#define NGULTRA_EXTRA_DATA_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
NPNR_PACKED_STRUCT(struct NGUltraTileInstExtraDataPOD {
|
||||
int32_t name;
|
||||
uint8_t lobe;
|
||||
uint8_t dummy1;
|
||||
uint16_t dummy2;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct NGUltraPipExtraDataPOD {
|
||||
int32_t name;
|
||||
uint16_t type;
|
||||
uint8_t input;
|
||||
uint8_t output;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct NGUltraBelExtraDataPOD { int32_t flags; });
|
||||
|
||||
enum TILETypeZ
|
||||
{
|
||||
BEL_LUT_Z = 0,
|
||||
BEL_LUT_MAX_Z = 31,
|
||||
BEL_CY_Z = 32,
|
||||
BEL_XLUT_Z = BEL_CY_Z + 4,
|
||||
BEL_RF_Z = BEL_XLUT_Z + 8,
|
||||
BEL_XRF_Z = BEL_RF_Z + 2,
|
||||
BEL_FIFO_Z = BEL_XRF_Z + 1,
|
||||
BEL_XFIFO_Z = BEL_FIFO_Z + 2,
|
||||
BEL_CDC_Z = BEL_XFIFO_Z + 1,
|
||||
BEL_XCDC_Z = BEL_CDC_Z + 2
|
||||
};
|
||||
|
||||
enum ClusterPlacement
|
||||
{
|
||||
PLACE_CY_CHAIN = 1024,
|
||||
PLACE_CY_FE1,
|
||||
PLACE_CY_FE2,
|
||||
PLACE_CY_FE3,
|
||||
PLACE_CY_FE4,
|
||||
};
|
||||
|
||||
enum PipExtra
|
||||
{
|
||||
PIP_EXTRA_CROSSBAR = 1,
|
||||
PIP_EXTRA_MUX = 2,
|
||||
PIP_EXTRA_BYPASS = 3,
|
||||
PIP_EXTRA_LUT_PERMUTATION = 4,
|
||||
PIP_EXTRA_INTERCONNECT = 5,
|
||||
PIP_EXTRA_VIRTUAL = 6,
|
||||
};
|
||||
|
||||
enum BelExtra
|
||||
{
|
||||
BEL_EXTRA_FE_CSC = 1,
|
||||
BEL_EXTRA_FE_SCC = 2,
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
675
himbaechel/uarch/ng-ultra/gen/arch_gen.py
Normal file
675
himbaechel/uarch/ng-ultra/gen/arch_gen.py
Normal file
@ -0,0 +1,675 @@
|
||||
import json
|
||||
from os import path
|
||||
import sys
|
||||
import argparse
|
||||
import gzip
|
||||
|
||||
# Configuration flags
|
||||
USE_LUT_PERMUTATION = True
|
||||
|
||||
sys.path.append(path.join(path.dirname(__file__), "../../.."))
|
||||
from himbaechel_dbgen.chip import *
|
||||
|
||||
@dataclass
|
||||
class TileExtraData(BBAStruct):
|
||||
name: IdString
|
||||
lobe: int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.name.index)
|
||||
bba.u8(self.lobe)
|
||||
bba.u8(0) # dummy
|
||||
bba.u16(0) # dummy
|
||||
|
||||
@dataclass
|
||||
class PipExtraData(BBAStruct):
|
||||
name: IdString = field(default_factory=IdString)
|
||||
data_type: int = 0
|
||||
input: int = 0
|
||||
output: int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.name.index)
|
||||
bba.u16(self.data_type)
|
||||
bba.u8(self.input)
|
||||
bba.u8(self.output)
|
||||
|
||||
@dataclass
|
||||
class BelExtraData(BBAStruct):
|
||||
flags: int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.flags)
|
||||
|
||||
PLL_Z = 0
|
||||
WFG_C_Z = 1
|
||||
WFG_R_Z = 11
|
||||
|
||||
IOM_Z = 136
|
||||
|
||||
RAM_Z = 0
|
||||
DSP1_Z = 1
|
||||
DSP2_Z = 2
|
||||
|
||||
SOCIF_Z = 0
|
||||
SERVICE_Z = 1
|
||||
|
||||
LUT_Z = 0
|
||||
CY_Z = 32
|
||||
XLUT_Z = CY_Z + 4
|
||||
RF_Z = XLUT_Z + 8
|
||||
XRF_Z = RF_Z + 2
|
||||
FIFO_Z = XRF_Z + 1
|
||||
XFIFO_Z = FIFO_Z + 2
|
||||
CDC_Z = XFIFO_Z + 1
|
||||
XCDC_Z = CDC_Z + 2
|
||||
|
||||
PIP_EXTRA_CROSSBAR = 1
|
||||
PIP_EXTRA_MUX = 2
|
||||
PIP_EXTRA_BYPASS = 3
|
||||
PIP_EXTRA_LUT_PERMUTATION = 4
|
||||
PIP_EXTRA_INTERCONNECT = 5
|
||||
PIP_EXTRA_VIRTUAL = 6
|
||||
|
||||
BEL_EXTRA_FE_CSC = 1
|
||||
BEL_EXTRA_FE_SCC = 2
|
||||
|
||||
def bel_z(tile_type, bel_name, bel_type):
|
||||
if tile_type.startswith("CKG"):
|
||||
if (bel_type=="PLL"):
|
||||
return PLL_Z
|
||||
else:
|
||||
if bel_name.startswith("WFG.WFG_C"):
|
||||
return int(bel_name.replace("WFG.WFG_C","")) + WFG_C_Z -1
|
||||
else:
|
||||
return int(bel_name.replace("WFG.WFG_R","")) + WFG_R_Z -1
|
||||
elif tile_type.startswith("HSSL"):
|
||||
if bel_type=="PMA":
|
||||
return 0
|
||||
elif bel_type=="CRX":
|
||||
return int(bel_name.split(".")[0][7:]) * 2 - 1
|
||||
else:
|
||||
return int(bel_name.split(".")[0][7:]) * 2
|
||||
elif tile_type.startswith("IOB") and tile_type in ["IOB0","IOB1","IOB6","IOB7"]: #direct
|
||||
sp = bel_name.split(".")
|
||||
if bel_type=="IOP":
|
||||
return int(sp[0][1:]) * 4
|
||||
else:
|
||||
return (int(sp[0][3:])-1) * 4 + (1 if sp[1][0]=='I' else 2 if sp[1][0]=='O' else 3)
|
||||
elif tile_type.startswith("IOB"): # complex
|
||||
sp = bel_name.split(".")
|
||||
if bel_type=="IOTP":
|
||||
return (int(sp[0][1:3]) *2 + (1 if sp[0][3]=='N' else 0)) * 4
|
||||
elif bel_type=="IOM":
|
||||
return IOM_Z
|
||||
else:
|
||||
return (int(sp[0][3:])-1) * 4 + (1 if sp[1][0]=='I' else 2 if sp[1][0]=='O' else 3)
|
||||
elif tile_type == "CGB":
|
||||
if bel_type=="RAM":
|
||||
return RAM_Z
|
||||
elif bel_name=="S2.DSP1":
|
||||
return DSP1_Z
|
||||
else:
|
||||
return DSP2_Z
|
||||
elif tile_type == "SOCBank":
|
||||
if bel_type=="SOCIF":
|
||||
return SOCIF_Z
|
||||
else:
|
||||
return SERVICE_Z
|
||||
elif tile_type == "TILE":
|
||||
sp = bel_name.split(".")
|
||||
if bel_type=="BEYOND_FE":
|
||||
return (int(sp[1][2:])-1) % 32
|
||||
elif bel_type=="CY":
|
||||
pos = int(sp[1][2:])-1
|
||||
# in S1, S5 and S9 they are ordered other way arround
|
||||
return ((pos % 4) if pos < 12 else 3 - (pos % 4)) + CY_Z
|
||||
elif bel_type=="XLUT":
|
||||
return (int(sp[1][4:])-1) % 8 + XLUT_Z
|
||||
elif bel_type=="RF":
|
||||
return (int(sp[1][2:])-1) + RF_Z
|
||||
elif bel_type=="XRF":
|
||||
return (int(sp[1][3:])-1) + XRF_Z
|
||||
elif bel_type=="FIFO":
|
||||
return (int(sp[1][4:])-1) + FIFO_Z
|
||||
elif bel_type=="XFIFO":
|
||||
return (int(sp[1][5:])-1) + XFIFO_Z
|
||||
elif bel_type=="CDC":
|
||||
return (int(sp[1][3:])-1) + CDC_Z
|
||||
elif bel_type=="XCDC":
|
||||
return (int(sp[1][4:])-1) + XCDC_Z
|
||||
else:
|
||||
raise Exception(f"Unknown bel type {bel_type}")
|
||||
elif tile_type == "TUBE":
|
||||
sp = bel_name.split(".")
|
||||
return (int(sp[0][1:])-1)*20 + (int(sp[1][1:])-1)
|
||||
else:
|
||||
raise Exception(f"Unknown type {tile_type}")
|
||||
|
||||
if USE_LUT_PERMUTATION:
|
||||
# Note PI1-PI4 are not real inputs but will appear
|
||||
# before actual BEL input to enable LUT permutation
|
||||
lut_to_beyond_fe = {
|
||||
"I1" : "PI1",
|
||||
"I2" : "PI2",
|
||||
"I3" : "PI3",
|
||||
"I4" : "PI4",
|
||||
"O" : "LO",
|
||||
"J" : "LJ",
|
||||
"K" : "LK",
|
||||
"D" : "LD",
|
||||
"X" : "LX",
|
||||
}
|
||||
else:
|
||||
lut_to_beyond_fe = {
|
||||
"I1" : "I1",
|
||||
"I2" : "I2",
|
||||
"I3" : "I3",
|
||||
"I4" : "I4",
|
||||
"O" : "LO",
|
||||
"J" : "LJ",
|
||||
"K" : "LK",
|
||||
"D" : "LD",
|
||||
"X" : "LX",
|
||||
}
|
||||
|
||||
dff_to_beyond_fe = {
|
||||
"I" : "DI",
|
||||
"O" : "DO",
|
||||
"L" : "L",
|
||||
"CK" : "CK",
|
||||
"R" : "R",
|
||||
"J" : "DJ",
|
||||
"P" : "DP",
|
||||
"C" : "DC",
|
||||
"S" : "DS",
|
||||
"K" : "DK",
|
||||
}
|
||||
|
||||
def is_complex(tile_type):
|
||||
return tile_type not in ["IOB0","IOB1","IOB6","IOB7"]
|
||||
|
||||
def map_to_beyond(tile_type,bel_name,bel_type=None):
|
||||
if not tile_type in ["TILE"]:
|
||||
return (bel_name,bel_type)
|
||||
s = bel_name.split(".")
|
||||
if tile_type in ["TILE"]:
|
||||
# BEYOND_FE
|
||||
if not (s[1].startswith("LUT") or s[1].startswith("DFF")):
|
||||
return (bel_name,bel_type)
|
||||
if len(s)==3:
|
||||
# BEL and PORT
|
||||
if s[1].startswith("LUT"):
|
||||
pin = lut_to_beyond_fe[s[2]]
|
||||
else:
|
||||
pin = dff_to_beyond_fe[s[2]]
|
||||
if pin is None:
|
||||
return (None,bel_type)
|
||||
return (s[0]+".FE"+s[1][3:]+"."+pin, bel_type if type(bel_type) is int else "BEYOND_FE")
|
||||
else:
|
||||
# just BEL
|
||||
return (s[0]+".FE"+s[1][3:], "BEYOND_FE")
|
||||
|
||||
split_map = ["TILE", "CGB"]
|
||||
|
||||
def split_tilegrid(tilegrid):
|
||||
new_tilegrid = dict()
|
||||
for k,v in tilegrid.items():
|
||||
if v["type"] not in split_map:
|
||||
new_tilegrid[k] = dict()
|
||||
data = dict(v)
|
||||
data["orig"] = data["type"]
|
||||
data["x"]=data["x"]*4
|
||||
data["y"]=data["y"]*4
|
||||
new_tilegrid[k][0] = data
|
||||
else:
|
||||
new_tilegrid[k] = dict()
|
||||
for i in range(4*4):
|
||||
data = dict(v)
|
||||
data["orig"] = data["type"]
|
||||
data["type"] = data["type"]+"_"+str(i)
|
||||
data["x"]=data["x"]*4 + i // 4
|
||||
data["y"]=data["y"]*4 + i % 4
|
||||
new_tilegrid[k][i] = data
|
||||
return new_tilegrid
|
||||
|
||||
def determine_subtile(tile,bel):
|
||||
if tile=="TILE":
|
||||
if bel.startswith("SYSTEM"):
|
||||
s = 3
|
||||
elif bel.startswith("RE") or bel.startswith("RS") or bel.startswith("RI"):
|
||||
s = int(bel.split(".")[0][2:])-1
|
||||
elif bel.startswith("S"):
|
||||
s = int(bel.split(".")[0][1:])+3
|
||||
else:
|
||||
return 0
|
||||
return s
|
||||
elif tile=="CGB":
|
||||
if bel.startswith("SYSTEM"):
|
||||
s = 6
|
||||
elif bel.startswith("RE"):
|
||||
s = 8
|
||||
elif bel.startswith("RS"):
|
||||
s = int(bel.split(".")[0][2:])
|
||||
elif bel.startswith("RI"):
|
||||
s = 5
|
||||
elif bel.startswith("S1"):
|
||||
s = 0
|
||||
elif bel.startswith("S2.DSP1"):
|
||||
s = 7
|
||||
else:
|
||||
return 15
|
||||
return s
|
||||
else:
|
||||
return 0
|
||||
|
||||
def split_per_bels(bels):
|
||||
new_bels = dict()
|
||||
for k,v in bels.items():
|
||||
if k not in split_map:
|
||||
new_bels[k] = dict()
|
||||
data = dict(v)
|
||||
new_bels[k][0] = data
|
||||
else:
|
||||
new_bels[k] = dict()
|
||||
for i in range(4*4):
|
||||
new_bels[k][i] = dict()
|
||||
for item,bel in v.items():
|
||||
(item, bel) = map_to_beyond(k,item,bel)
|
||||
if item is not None:
|
||||
num = determine_subtile(k,item)
|
||||
new_bels[k][num][item] = bel
|
||||
return new_bels
|
||||
|
||||
def lookup_port_type(t):
|
||||
if t == "Input": return PinType.INPUT
|
||||
elif t == "Output": return PinType.OUTPUT
|
||||
elif t == "Bidir": return PinType.INOUT
|
||||
else: assert False
|
||||
|
||||
def create_pips(tt, tile_type, muxes, num, args):
|
||||
file_path = path.join(args.db, args.device, tile_type + ".txt")
|
||||
if not path.isfile(file_path):
|
||||
return
|
||||
with open(file_path) as f:
|
||||
for item in f:
|
||||
line = item.strip().split(" ")
|
||||
name1,_ = map_to_beyond(tile_type,line[0])
|
||||
name2,_ = map_to_beyond(tile_type,line[1])
|
||||
if name1 is None or name2 is None:
|
||||
continue
|
||||
num1 = determine_subtile(tile_type,name1)
|
||||
num2 = determine_subtile(tile_type,name2)
|
||||
|
||||
if name2 in muxes:
|
||||
name2 = name2 + "." + line[2]
|
||||
if num1==num and not tt.has_wire(name1):
|
||||
tt.create_wire(name=name1, type=tile_type + "_WIRE")
|
||||
if num2==num and not tt.has_wire(name2):
|
||||
tt.create_wire(name=name2, type=tile_type + "_WIRE")
|
||||
timing_class = line[3]
|
||||
# Only create PIP if both ends are in same subtile
|
||||
if num1==num and num2==num:
|
||||
tt.create_pip(name1,name2,timing_class)
|
||||
|
||||
def create_tile_types(ch: Chip, bels, bel_pins, crossbars, interconnects, muxes, args):
|
||||
for tile_type,items in bels.items():
|
||||
for num in items.keys():
|
||||
if len(items)==1:
|
||||
sub_type = tile_type
|
||||
else:
|
||||
sub_type = tile_type + "_"+str(num)
|
||||
tt = ch.create_tile_type(sub_type)
|
||||
|
||||
def lookup_site_wire(canon_name):
|
||||
if not tt.has_wire(canon_name):
|
||||
tt.create_wire(name=canon_name, type="BEL_PIN_WIRE")
|
||||
return canon_name
|
||||
|
||||
# Create BELs inside tile
|
||||
for name,bel in bels[tile_type][num].items():
|
||||
nb = tt.create_bel(name, bel, z=bel_z(tile_type,name,bel))
|
||||
# Create wires for each BEL port
|
||||
for index in bel_pins[bel].keys():
|
||||
pin = bel_pins[bel][index]
|
||||
tt.add_bel_pin(nb, pin["name"], lookup_site_wire(f"{name}."+pin["name"]), lookup_port_type(pin["direction"]))
|
||||
if (tile_type.startswith("TILE") and bel=="BEYOND_FE"):
|
||||
flag = 0
|
||||
fe_nxd = int(name[name.index(".")+3:])
|
||||
if (((fe_nxd-1) & 127) < 64):
|
||||
if ((fe_nxd-1)&31) < 16:
|
||||
if (fe_nxd & 1)==1:
|
||||
flag |= BEL_EXTRA_FE_SCC
|
||||
else:
|
||||
if (fe_nxd & 1)==0:
|
||||
flag |= BEL_EXTRA_FE_SCC
|
||||
|
||||
if fe_nxd in (65, 80, 81, 96, 225,240,241,256, 321,336,337,352):
|
||||
flag |= BEL_EXTRA_FE_CSC
|
||||
nb.extra_data = BelExtraData(flag)
|
||||
|
||||
# LUT drawers, LO already connected to LJ
|
||||
vi = tt.create_pip(f"{name}.LJ",f"{name}.LK","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
vi = tt.create_pip(f"{name}.LJ",f"{name}.LD","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
vi = tt.create_pip(f"{name}.LJ",f"{name}.LX","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
# DFF drawers, DO already connected to DJ
|
||||
vi = tt.create_pip(f"{name}.DJ",f"{name}.DP","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
vi = tt.create_pip(f"{name}.DJ",f"{name}.DC","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
#vi = tt.create_pip(f"{name}.DJ",f"{name}.DS","Virtual")
|
||||
#vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
vi = tt.create_pip(f"{name}.DJ",f"{name}.DK","Virtual")
|
||||
vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0)
|
||||
# DFF bypass
|
||||
by = tt.create_pip(f"{name}.DI",f"{name}.DO","BYPASS")
|
||||
by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
elif (tile_type.startswith("TILE") and bel=="CY"):
|
||||
# matrix of each input to each output combination
|
||||
# crossbar but use mux placeholder for convenience
|
||||
for inp in ["I1","I2","I3","I4"]:
|
||||
for out in ["O1","O2","O3","O4"]:
|
||||
pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"MATRIX_PIP")
|
||||
pd.extra_data = PipExtraData(ch.strs.id(f"{name}."+inp),PIP_EXTRA_MUX,int(inp[1:])-1,int(out[1:])-1)
|
||||
|
||||
elif (tile_type.startswith("IOB") and bel=="DFR"):
|
||||
by = tt.create_pip(f"{name}.I",f"{name}.O","BYPASS")
|
||||
by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
elif (tile_type.startswith("IOB") and bel=="DDFR"):
|
||||
by = tt.create_pip(f"{name}.I",f"{name}.O","BYPASS")
|
||||
by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
elif (tile_type.startswith("CKG") and bel=="WFG"):
|
||||
by = tt.create_pip(f"{name}.ZI",f"{name}.ZO","BYPASS")
|
||||
by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
elif (tile_type.startswith("TUBE") and bel=="GCK"):
|
||||
# 20 clock signals comming to 20 GCK, SI1 is bypass
|
||||
by = tt.create_pip(f"{name}.SI1",f"{name}.SO","BYPASS")
|
||||
by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
# there are CMD signals that can be bypassed as well
|
||||
#by = tt.create_pip(f"{name}.CMD",f"{name}.SO","BYPASS")
|
||||
#by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0)
|
||||
|
||||
|
||||
# Add LUT permutation
|
||||
if USE_LUT_PERMUTATION and tile_type=="TILE":
|
||||
for name,bel in bels[tile_type][num].items():
|
||||
if bel=="BEYOND_FE":
|
||||
for inp in ["PI1","PI2","PI3","PI4"]:
|
||||
tt.create_wire(name=f"{name}."+inp, type="LUT_PERMUTATION_WIRE")
|
||||
for out in ["I1","I2","I3","I4"]:
|
||||
pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"LUT_PERMUTATION")
|
||||
pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_LUT_PERMUTATION,int(inp[2:])-1,int(out[1:])-1)
|
||||
|
||||
# Create crossbars as multiple PIPs
|
||||
for name,xb in crossbars[tile_type][num].items():
|
||||
inputs = list()
|
||||
outputs = list()
|
||||
for index in bel_pins[xb].keys():
|
||||
pin = bel_pins[xb][index]
|
||||
tt.create_wire(name=f"{name}."+pin["name"], type="CROSSBAR_INPUT_WIRE" if pin["direction"] == "Input" else "CROSSBAR_OUTPUT_WIRE")
|
||||
for index in bel_pins[xb].keys():
|
||||
pin = bel_pins[xb][index]
|
||||
if pin["direction"] == "Input":
|
||||
inputs.append(pin["name"])
|
||||
else:
|
||||
outputs.append(pin["name"])
|
||||
for inp in inputs:
|
||||
for out in outputs:
|
||||
pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"CROSSBAR_"+xb)
|
||||
pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_CROSSBAR,int(inp[1:])-1,int(out[1:])-1)
|
||||
|
||||
# Interconnects are just PIPs with one I and one O
|
||||
for name,xb in interconnects[tile_type][num].items():
|
||||
for index in bel_pins[xb].keys():
|
||||
pin = bel_pins[xb][index]
|
||||
tt.create_wire(name=f"{name}."+pin["name"], type="INTERCONNECT_INPUT" if pin["direction"] == "Input" else "INTERCONNECT_OUTPUT")
|
||||
inp = f"{name}."+bel_pins[xb]["I"]["name"]
|
||||
out = f"{name}."+bel_pins[xb]["O"]["name"]
|
||||
pd = tt.create_pip(inp,out,"INTERCONNECT")
|
||||
pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_INTERCONNECT,0,0)
|
||||
|
||||
# Create MUXes as many to one connections
|
||||
if tile_type in muxes:
|
||||
for (name, n) in muxes[tile_type][num].items():
|
||||
for i in range(0,n+1):
|
||||
new_name = name + "." + str(i)
|
||||
if not tt.has_wire(new_name):
|
||||
tt.create_wire(name=new_name, type="MUX_WIRE")
|
||||
pd = tt.create_pip(new_name,name,"MUX_PIP")
|
||||
pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_MUX,i,0)
|
||||
|
||||
m = muxes[tile_type] if tile_type in muxes else dict()
|
||||
mux = muxes[tile_type][num] if num in m else dict()
|
||||
create_pips(tt, tile_type, mux, num, args)
|
||||
|
||||
def create_null(ch: Chip):
|
||||
tt = ch.create_tile_type("NULL")
|
||||
|
||||
def set_timings(ch, pip_timings, bel_timings):
|
||||
speed = "DEFAULT"
|
||||
tmg = ch.set_speed_grades([speed])
|
||||
for k,v in pip_timings.items():
|
||||
tmg.set_pip_class(grade=speed, name=k, delay=TimingValue(v[0]))
|
||||
tmg.set_node_class(grade=speed, name=k, delay=TimingValue(v[0]))
|
||||
tmg.set_pip_class(grade=speed, name="INTERCONNECT", delay=TimingValue(0))
|
||||
tmg.set_pip_class(grade=speed, name="MUX_PIP", delay=TimingValue(0))
|
||||
tmg.set_pip_class(grade=speed, name="MATRIX_PIP", delay=TimingValue(0))
|
||||
tmg.set_pip_class(grade=speed, name="LUT_PERMUTATION", delay=TimingValue(0))
|
||||
tmg.set_pip_class(grade=speed, name="BYPASS", delay=TimingValue(142))
|
||||
for k,g in bel_timings.items():
|
||||
primitive = ch.timing.add_cell_variant(speed, k)
|
||||
for t,v in g.items():
|
||||
if t=="IOPath":
|
||||
for from_port,values in v.items():
|
||||
for to_port,data in values.items():
|
||||
primitive.add_comb_arc(from_port, to_port, TimingValue(data[0], data[1]))
|
||||
elif t=="SetupHold":
|
||||
for from_port,values in v.items():
|
||||
for to_port,data in values.items():
|
||||
primitive.add_setup_hold(from_port, to_port, ClockEdge.RISING, TimingValue(data[0]), TimingValue(data[1]))
|
||||
elif t=="ClockOut":
|
||||
for from_port,values in v.items():
|
||||
for to_port,data in values.items():
|
||||
primitive.add_clock_out(from_port, to_port, ClockEdge.RISING, TimingValue(data[0],data[1]))
|
||||
|
||||
def get_pos(tilegrid,name,bel):
|
||||
tile = tilegrid[name][0]["orig"]
|
||||
num = determine_subtile(tile,bel)
|
||||
item = tilegrid[name][num]
|
||||
x = item["x"]
|
||||
y = item["y"]
|
||||
return (tile,num,x,y)
|
||||
|
||||
|
||||
global_connections = dict()
|
||||
def load_globals(args):
|
||||
print("Load global connections...")
|
||||
with gzip.open(path.join(args.db, args.device, "GLOBAL.txt.gz"),"rt") as f:
|
||||
for item in f:
|
||||
line = item.strip().split(" ")
|
||||
tile_name = line[0].split(":")[0]
|
||||
if tile_name not in global_connections:
|
||||
global_connections[tile_name] = dict()
|
||||
if line[0] not in global_connections[tile_name]:
|
||||
global_connections[tile_name][line[0]] = list()
|
||||
global_connections[tile_name][line[0]].append(line)
|
||||
|
||||
def create_nodes(ch, tile_name, tilegrid, muxes, pip_timings):
|
||||
if tile_name not in global_connections:
|
||||
return
|
||||
connections = global_connections[tile_name]
|
||||
for key,val in connections.items():
|
||||
name1 = key.split(":")
|
||||
t1,_,x1,y1 = get_pos(tilegrid,name1[0],name1[1])
|
||||
name1[1],_ = map_to_beyond(t1,name1[1])
|
||||
if name1[1] is None:
|
||||
continue
|
||||
node = [NodeWire(x1, y1, name1[1])]
|
||||
timing = None
|
||||
timing_val = -1
|
||||
for v in val:
|
||||
name2 = v[1].split(":")
|
||||
name2[1],_ = map_to_beyond(tilegrid[name2[0]][0]["orig"],name2[1])
|
||||
t2,num,x2,y2 = get_pos(tilegrid,name2[0],name2[1])
|
||||
if name2[1] is None:
|
||||
continue
|
||||
if pip_timings[v[3]][0] > timing_val:
|
||||
timing_val = pip_timings[v[3]][0]
|
||||
timing = v[3]
|
||||
if t2 in muxes and num in muxes[t2] and name2[1] in muxes[t2][num]:
|
||||
node.append(NodeWire(x2, y2, name2[1]+"."+v[2]))
|
||||
else:
|
||||
node.append(NodeWire(x2, y2, name2[1]))
|
||||
|
||||
ch.add_node(node,timing_class=timing)
|
||||
|
||||
|
||||
subtile_connections = dict()
|
||||
def create_nodes_subtiles(ch, tilegrid, name, tile_type, muxes, pip_timings, args):
|
||||
if tile_type not in subtile_connections:
|
||||
subtile_connections[tile_type] = dict()
|
||||
|
||||
file_path = path.join(args.db, args.device, tile_type + ".txt")
|
||||
if not path.isfile(file_path):
|
||||
return
|
||||
with open(file_path) as f:
|
||||
for item in f:
|
||||
line = item.strip().split(" ")
|
||||
name1 = line[0]
|
||||
name2 = line[1]
|
||||
name1,_ = map_to_beyond(tile_type,line[0])
|
||||
name2,_ = map_to_beyond(tile_type,line[1])
|
||||
if name1 is None or name2 is None:
|
||||
continue
|
||||
num1 = determine_subtile(tile_type,name1)
|
||||
num2 = determine_subtile(tile_type,name2)
|
||||
# Only create WIRE if ends are NOT in same subtile
|
||||
if num1!=num2:
|
||||
if name1 not in subtile_connections[tile_type] :
|
||||
subtile_connections[tile_type][name1] = list()
|
||||
subtile_connections[tile_type][name1].append([name1, name2, line[2], line[3]])
|
||||
|
||||
for name1,val in subtile_connections[tile_type].items():
|
||||
_,_,x1,y1 = get_pos(tilegrid,name,name1)
|
||||
node = [NodeWire(x1, y1, name1)]
|
||||
timing_val = -1
|
||||
timing = None
|
||||
for v in val:
|
||||
name2 = v[1]
|
||||
t2,num,x2,y2 = get_pos(tilegrid,name,name2)
|
||||
if pip_timings[v[3]][0] > timing_val:
|
||||
timing_val = pip_timings[v[3]][0]
|
||||
timing = v[3]
|
||||
if t2 in muxes and num in muxes[t2] and name2 in muxes[t2][num]:
|
||||
node.append(NodeWire(x2, y2, name2+"."+v[2]))
|
||||
else:
|
||||
node.append(NodeWire(x2, y2, name2))
|
||||
|
||||
ch.add_node(node,timing_class=timing)
|
||||
|
||||
def import_package(ch, package, bels, tilegrid):
|
||||
pkg = ch.create_package(package)
|
||||
for name, data in tilegrid.items():
|
||||
for key, item in data.items():
|
||||
ty = item["orig"]
|
||||
x = item["x"]
|
||||
y = item["y"]
|
||||
if ty.startswith("IOB"):
|
||||
for bel,ty in bels[ty][key].items():
|
||||
# Support for native primitives
|
||||
if ty =="IOTP":
|
||||
pin = name+"_"+bel[:4]
|
||||
elif ty =="IOP":
|
||||
pin = name+"_"+bel[:3]
|
||||
else:
|
||||
continue
|
||||
pkg.create_pad(pin, f"X{x}Y{y}", bel, "", int(name[3:]))
|
||||
|
||||
def main():
|
||||
xlbase = path.join(path.dirname(path.realpath(__file__)), "..")
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--db", help="Project Beyond device database path (e.g. ../prjbeyond/database)", type=str, required=True)
|
||||
parser.add_argument("--device", help="name of device to export", type=str, required=True)
|
||||
parser.add_argument("--constids", help="name of nextpnr constids file to read", type=str, default=path.join(xlbase, "constids.inc"))
|
||||
parser.add_argument("--bba", help="bba file to write", type=str, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(path.join(args.db, "devices.json")) as f:
|
||||
devices = json.load(f)
|
||||
|
||||
width = (devices["families"][args.device]["max_col"] + 1) * 4
|
||||
height = (devices["families"][args.device]["max_row"] + 1) * 4
|
||||
packages = devices["families"][args.device]["packages"]
|
||||
|
||||
ch = Chip("ng-ultra",args.device, width, height)
|
||||
ch.strs.read_constids(path.join(path.dirname(__file__), "..", "constids.inc"))
|
||||
|
||||
# Data that is depending of location
|
||||
with open(path.join(args.db, args.device, "tilegrid.json")) as f:
|
||||
tilegrid = split_tilegrid(json.load(f))
|
||||
|
||||
with open(path.join(args.db, args.device, "bels.json")) as f:
|
||||
bels = split_per_bels(json.load(f))
|
||||
|
||||
with open(path.join(args.db, args.device, "crossbars.json")) as f:
|
||||
crossbars = split_per_bels(json.load(f))
|
||||
|
||||
with open(path.join(args.db, args.device, "interconnects.json")) as f:
|
||||
interconnects = split_per_bels(json.load(f))
|
||||
|
||||
with open(path.join(args.db, args.device, "muxes.json")) as f:
|
||||
muxes = split_per_bels(json.load(f))
|
||||
|
||||
# Data that is not related to position
|
||||
with open(path.join(args.db, args.device, "bel_pins.json")) as f:
|
||||
bel_pins = json.load(f)
|
||||
bel_pins["IOP"]["IO"] = { "direction": "Bidir", "name": "IO" }
|
||||
bel_pins["IOTP"]["IO"] = { "direction": "Bidir", "name": "IO" }
|
||||
|
||||
with open(path.join(args.db, args.device, "pip_timings.json")) as f:
|
||||
pip_timings = json.load(f)
|
||||
|
||||
with open(path.join(args.db, args.device, "bel_timings.json")) as f:
|
||||
bel_timings = json.load(f)
|
||||
|
||||
create_tile_types(ch, bels, bel_pins, crossbars, interconnects, muxes, args)
|
||||
create_null(ch)
|
||||
set_timings(ch, pip_timings, bel_timings)
|
||||
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
ch.set_tile_type(x,y,"NULL")
|
||||
|
||||
load_globals(args)
|
||||
for name, data in tilegrid.items():
|
||||
for item in data.values():
|
||||
ti = ch.set_tile_type(item["x"],item["y"],item["type"])
|
||||
lobe = 0
|
||||
if item["orig"] in ["TILE","CGB"]:
|
||||
tmp = name.replace("TILE[","").replace("CGB[","").replace("]","")
|
||||
x,y = tmp.split("x")
|
||||
lobe = ((int(y)-1) // 12)*2 + (1 if int(x)>46 else 0) + 1
|
||||
ti.extra_data = TileExtraData(ch.strs.id(name),lobe)
|
||||
|
||||
for name, data in tilegrid.items():
|
||||
print(f"Generate nodes for {name}...")
|
||||
create_nodes_subtiles(ch, tilegrid, name, data[0]["orig"], muxes, pip_timings, args)
|
||||
create_nodes(ch, name, tilegrid, muxes, pip_timings)
|
||||
|
||||
for package in packages:
|
||||
import_package(ch, package, bels, tilegrid)
|
||||
|
||||
ch.write_bba(args.bba)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
24766
himbaechel/uarch/ng-ultra/json.hpp
Normal file
24766
himbaechel/uarch/ng-ultra/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2023 Lofty <lofty@yosyshq.com>
|
||||
* Copyright (C) 2023 Miodrag Milanovic <micko@yosyshq.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
|
||||
@ -17,126 +18,448 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <fstream>
|
||||
|
||||
#include "himbaechel_api.h"
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
#include "extra_data.h"
|
||||
#include "placer_heap.h"
|
||||
|
||||
#include "himbaechel_helpers.h"
|
||||
|
||||
#include "ng_ultra.h"
|
||||
|
||||
#define GEN_INIT_CONSTIDS
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
struct NgUltraImpl : HimbaechelAPI
|
||||
NgUltraImpl::~NgUltraImpl(){};
|
||||
|
||||
void NgUltraImpl::init_database(Arch *arch)
|
||||
{
|
||||
init_uarch_constids(arch);
|
||||
arch->load_chipdb("ng-ultra/ng-ultra.bin");
|
||||
arch->set_package("FF-1760");
|
||||
arch->set_speed_grade("DEFAULT");
|
||||
}
|
||||
|
||||
static constexpr int K = 4;
|
||||
|
||||
~NgUltraImpl(){};
|
||||
void init_database(Arch *arch) override
|
||||
{
|
||||
init_uarch_constids(arch);
|
||||
arch->load_chipdb("ng-ultra/ng-ultra.bin");
|
||||
arch->set_speed_grade("DEFAULT");
|
||||
void NgUltraImpl::init(Context *ctx)
|
||||
{
|
||||
HimbaechelAPI::init(ctx);
|
||||
for (auto bel : ctx->getBels()) {
|
||||
if (ctx->getBelType(bel) == id_IOM) {
|
||||
std::deque<BelId> wfgs;
|
||||
IdString bank = tile_name_id(bel.tile);
|
||||
iom_bels.emplace(bank,bel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(Context *ctx) override
|
||||
{
|
||||
h.init(ctx);
|
||||
HimbaechelAPI::init(ctx);
|
||||
}
|
||||
const NGUltraTileInstExtraDataPOD *NgUltraImpl::tile_extra_data(int tile) const
|
||||
{
|
||||
return reinterpret_cast<const NGUltraTileInstExtraDataPOD *>(ctx->chip_info->tile_insts[tile].extra_data.get());
|
||||
}
|
||||
|
||||
void prePlace() override { assign_cell_info(); }
|
||||
IdString NgUltraImpl::tile_name_id(int tile) const
|
||||
{
|
||||
const auto &data = *tile_extra_data(tile);
|
||||
return IdString(data.name);
|
||||
}
|
||||
|
||||
void pack() override
|
||||
{
|
||||
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
|
||||
const pool<CellTypePort> top_ports{
|
||||
CellTypePort(id_INBUF, id_PAD),
|
||||
CellTypePort(id_OUTBUF, id_PAD),
|
||||
};
|
||||
h.remove_nextpnr_iobs(top_ports);
|
||||
// Replace constants with LUTs
|
||||
const dict<IdString, Property> vcc_params = {{id_INIT, Property(0xFFFF, 16)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_INIT, Property(0x0000, 16)}};
|
||||
h.replace_constants(CellTypePort(id_LUT4, id_F), CellTypePort(id_LUT4, id_F), vcc_params, gnd_params);
|
||||
// Constrain directly connected LUTs and FFs together to use dedicated resources
|
||||
int lutffs = h.constrain_cell_pairs(pool<CellTypePort>{{id_LUT4, id_F}}, pool<CellTypePort>{{id_DFF, id_D}}, 1);
|
||||
log_info("Constrained %d LUTFF pairs.\n", lutffs);
|
||||
}
|
||||
std::string NgUltraImpl::tile_name(int tile) const
|
||||
{
|
||||
return stringf("%s", tile_name_id(tile).c_str(ctx));
|
||||
}
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid) const override
|
||||
{
|
||||
Loc l = ctx->getBelLocation(bel);
|
||||
if (ctx->getBelType(bel).in(id_LUT4, id_DFF)) {
|
||||
return slice_valid(l.x, l.y, l.z / 2);
|
||||
} else {
|
||||
int NgUltraImpl::tile_lobe(int tile) const
|
||||
{
|
||||
const auto &data = *tile_extra_data(tile);
|
||||
return data.lobe;
|
||||
}
|
||||
|
||||
void NgUltraImpl::preRoute()
|
||||
{
|
||||
route_clocks();
|
||||
}
|
||||
|
||||
bool NgUltraImpl::get_mux_data(BelId bel, IdString port, uint8_t *value)
|
||||
{
|
||||
return get_mux_data(ctx->getBelPinWire(bel, port), value);
|
||||
}
|
||||
|
||||
bool NgUltraImpl::get_mux_data(WireId wire, uint8_t *value)
|
||||
{
|
||||
for (PipId pip : ctx->getPipsUphill(wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto &pip_data = chip_pip_info(ctx->chip_info, pip);
|
||||
const auto &extra_data = *reinterpret_cast<const NGUltraPipExtraDataPOD *>(pip_data.extra_data.get());
|
||||
if (!extra_data.name) continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX) {
|
||||
*value = extra_data.input;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bel bucket functions
|
||||
IdString getBelBucketForCellType(IdString cell_type) const override
|
||||
{
|
||||
if (cell_type.in(id_INBUF, id_OUTBUF))
|
||||
return id_IOB;
|
||||
return cell_type;
|
||||
}
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override
|
||||
{
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
if (bel_type == id_IOB)
|
||||
return cell_type.in(id_INBUF, id_OUTBUF);
|
||||
else
|
||||
return (bel_type == cell_type);
|
||||
}
|
||||
|
||||
private:
|
||||
HimbaechelHelpers h;
|
||||
|
||||
// Validity checking
|
||||
struct NgUltraCellInfo
|
||||
{
|
||||
const NetInfo *lut_f = nullptr, *ff_d = nullptr;
|
||||
bool lut_i3_used = false;
|
||||
};
|
||||
std::vector<NgUltraCellInfo> fast_cell_info;
|
||||
void assign_cell_info()
|
||||
{
|
||||
fast_cell_info.resize(ctx->cells.size());
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
auto &fc = fast_cell_info.at(ci->flat_index);
|
||||
if (ci->type == id_LUT4) {
|
||||
fc.lut_f = ci->getPort(id_F);
|
||||
fc.lut_i3_used = (ci->getPort(ctx->idf("I[%d]", K - 1)) != nullptr);
|
||||
} else if (ci->type == id_DFF) {
|
||||
fc.ff_d = ci->getPort(id_D);
|
||||
void NgUltraImpl::postRoute()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
log_break();
|
||||
log_info("Resources spent on routing:\n");
|
||||
int dff_bypass = 0, fe_new = 0, wfg_bypass = 0, gck_bypass = 0, ddfr_bypass = 0;
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *ni = net.second.get();
|
||||
for (auto &w : ni->wires) {
|
||||
if (w.second.pip != PipId()) {
|
||||
const auto &pip_data = chip_pip_info(ctx->chip_info, w.second.pip);
|
||||
const auto &extra_data = *reinterpret_cast<const NGUltraPipExtraDataPOD *>(pip_data.extra_data.get());
|
||||
if (!extra_data.name) continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_BYPASS) {
|
||||
IdStringList id = ctx->getPipName(w.second.pip);
|
||||
BelId bel = ctx->getBelByName(IdStringList::concat(id[0], IdString(extra_data.name)));
|
||||
IdString type = ctx->getBelType(bel);
|
||||
if (!ctx->getBoundBelCell(bel)) {
|
||||
CellInfo *cell = ctx->createCell(ctx->id(ctx->nameOfBel(bel)), type);
|
||||
ctx->bindBel(bel,cell,PlaceStrength::STRENGTH_FIXED);
|
||||
if (type==id_BEYOND_FE) fe_new++;
|
||||
}
|
||||
CellInfo *cell = ctx->getBoundBelCell(bel);
|
||||
std::string bel_name = ctx->getBelName(cell->bel)[1].c_str(ctx);
|
||||
switch(type.index) {
|
||||
case id_BEYOND_FE.index :
|
||||
dff_bypass++;
|
||||
// set bypass mode for DFF
|
||||
cell->setParam(ctx->id("type"), Property("BFF"));
|
||||
cell->params[id_dff_used] = Property(1,1);
|
||||
break;
|
||||
case id_WFG.index : wfg_bypass++;
|
||||
cell->setParam(ctx->id("type"), Property("WFB"));
|
||||
break;
|
||||
case id_GCK.index : gck_bypass++; break;
|
||||
case id_DDFR.index :
|
||||
case id_DFR.index : ddfr_bypass++;
|
||||
{
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
cell->setParam(ctx->id("type"), Property("BFR"));
|
||||
if (boost::ends_with(bel_name, "CD")) {
|
||||
loc.z -= 3;
|
||||
//cell->setParam(ctx->id("mode"), Property(0, 2));
|
||||
cell->setParam(ctx->id("path"), Property(2, 2));
|
||||
} else if (boost::ends_with(bel_name, "OD")) {
|
||||
loc.z -= 2;
|
||||
cell->setParam(ctx->id("mode"), Property(2, 2));
|
||||
cell->setParam(ctx->id("path"), Property(0, 2));
|
||||
} else {
|
||||
loc.z -= 1;
|
||||
cell->setParam(ctx->id("mode"), Property(2, 2));
|
||||
cell->setParam(ctx->id("path"), Property(1, 2));
|
||||
}
|
||||
CellInfo *iob = ctx->getBoundBelCell(ctx->getBelByLocation(loc));
|
||||
if (iob) {
|
||||
cell->setParam(ctx->id("iobname"), iob->params[ctx->id("iobname")]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("Unmaped bel type '%s' for routing\n",type.c_str(ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool slice_valid(int x, int y, int z) const
|
||||
{
|
||||
const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2)));
|
||||
const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1)));
|
||||
if (!lut || !ff)
|
||||
return true; // always valid if only LUT or FF used
|
||||
const auto &lut_data = fast_cell_info.at(lut->flat_index);
|
||||
const auto &ff_data = fast_cell_info.at(ff->flat_index);
|
||||
// In our example arch; the FF D can either be driven from LUT F or LUT I3
|
||||
// so either; FF D must equal LUT F or LUT I3 must be unused
|
||||
if (ff_data.ff_d == lut_data.lut_f)
|
||||
log_info(" %6d DFFs used as BFF (%d new allocated FEs)\n", dff_bypass, fe_new);
|
||||
log_info(" %6d WFGs used as WFB\n", wfg_bypass);
|
||||
log_info(" %6d GCK\n", gck_bypass);
|
||||
log_info(" %6d DFR/DDFR as BFR\n", ddfr_bypass);
|
||||
|
||||
// Handle LUT permutation
|
||||
for (auto &cell : ctx->cells) {
|
||||
if (cell.second->type == id_BEYOND_FE) {
|
||||
// if LUT part used
|
||||
if (cell.second->params.count(id_lut_table) != 0) {
|
||||
std::array<std::vector<unsigned>, 4> phys_to_log;
|
||||
unsigned orig_init = int_or_default(cell.second->params, id_lut_table);
|
||||
const std::array<IdString, 4> ports{id_I1, id_I2, id_I3, id_I4};
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
WireId pin_wire = ctx->getBelPinWire(cell.second->bel, ports[i]);
|
||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto &pip_data = chip_pip_info(ctx->chip_info, pip);
|
||||
const auto &extra_data = *reinterpret_cast<const NGUltraPipExtraDataPOD *>(pip_data.extra_data.get());
|
||||
if (!extra_data.name) continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_LUT_PERMUTATION) {
|
||||
NPNR_ASSERT(extra_data.output == i);
|
||||
phys_to_log[extra_data.input].push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned permuted_init = 0;
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
unsigned log_idx = 0;
|
||||
for (unsigned j = 0; j < 4; j++) {
|
||||
if ((i >> j) & 0x1) {
|
||||
for (auto log_pin : phys_to_log[j])
|
||||
log_idx |= (1 << log_pin);
|
||||
}
|
||||
}
|
||||
if ((orig_init >> log_idx) & 0x1)
|
||||
permuted_init |= (1 << i);
|
||||
}
|
||||
cell.second->params[id_lut_table] = Property(permuted_init, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_utilisation(ctx);
|
||||
const ArchArgs &args = ctx->args;
|
||||
if (args.options.count("bit")) {
|
||||
write_bitstream_json(args.options.at("bit"));
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
|
||||
{
|
||||
cfg.hpwl_scale_x = 2;
|
||||
cfg.hpwl_scale_y = 1;
|
||||
cfg.beta = 0.5;
|
||||
cfg.placeAllAtOnce = true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <size_t N> bool check_assign_sig(std::array<const NetInfo*, N> &sig_set, const NetInfo *sig)
|
||||
{
|
||||
if (sig == nullptr)
|
||||
return true;
|
||||
for (size_t i = 0; i < N; i++)
|
||||
if (sig_set[i] == sig) {
|
||||
return true;
|
||||
if (lut_data.lut_i3_used)
|
||||
return false;
|
||||
} else if (sig_set[i] == nullptr) {
|
||||
sig_set[i] = sig;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
struct SectionFEWorker
|
||||
{
|
||||
std::array<const NetInfo *, 2> clk{};
|
||||
std::array<const NetInfo *, 4> reset_load{};
|
||||
bool run(const NgUltraImpl *impl,const Context *ctx, BelId bel)
|
||||
{
|
||||
CellInfo *cell = ctx->getBoundBelCell(bel);
|
||||
if (cell == nullptr) {
|
||||
return true;
|
||||
}
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
for (uint8_t id = 0; id <= BEL_LUT_MAX_Z; id++) {
|
||||
const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x,loc.y,id)));
|
||||
if (ff == nullptr)
|
||||
continue;
|
||||
if (!check_assign_sig(reset_load, ff->getPort(id_R)))
|
||||
return false;
|
||||
if (!check_assign_sig(reset_load, ff->getPort(id_L)))
|
||||
return false;
|
||||
if (!check_assign_sig(clk, ff->getPort(id_CK)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
bool NgUltraImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||
{
|
||||
if (ctx->getBelType(bel) == id_BEYOND_FE) {
|
||||
SectionFEWorker worker;
|
||||
return worker.run(this, ctx, bel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bel bucket functions
|
||||
IdString NgUltraImpl::getBelBucketForCellType(IdString cell_type) const
|
||||
{
|
||||
return cell_type;
|
||||
}
|
||||
|
||||
bool NgUltraImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
{
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
if (bel_type == id_IOTP)
|
||||
return cell_type.in(id_IOP,id_IOTP);
|
||||
else if (bel_type == id_DDFR)
|
||||
return cell_type.in(id_DFR,id_DDFR);
|
||||
else
|
||||
return (bel_type == cell_type);
|
||||
}
|
||||
|
||||
Loc getNextLocInCYChain(Loc loc)
|
||||
{
|
||||
static const std::vector<Loc> map =
|
||||
{
|
||||
Loc(0, 1, 0), // S1 0 -> S2 0 CY24->CY1
|
||||
Loc(0, 0, -1), // S1 1 -> S1 0 CY23->CY24
|
||||
Loc(0, 0, -1), // S1 2 -> S1 1 CY22->CY23
|
||||
Loc(0, 0, -1), // S1 3 -> S1 2 CY21->CY22
|
||||
|
||||
Loc(-1, 0,+3), // S5 0 -> S1 1 CY20->CY21
|
||||
Loc(0, 0, -1), // S5 1 -> S5 0 CY19->CY20
|
||||
Loc(0, 0, -1), // S5 2 -> S5 1 CY18->CY19
|
||||
Loc(0, 0, -1), // S5 3 -> S5 2 CY17->CY18
|
||||
|
||||
Loc(-1, 0,+3), // S9 0 -> S5 1 CY16->CY17
|
||||
Loc(0, 0, -1), // S9 1 -> S9 0 CY15->CY16
|
||||
Loc(0, 0, -1), // S9 2 -> S9 1 CY14->CY15
|
||||
Loc(0, 0, -1), // S9 3 -> S9 2 CY13->CY14
|
||||
|
||||
Loc(0, 0, +1), // S2 0 -> S2 1 CY1->CY2
|
||||
Loc(0, 0, +1), // S2 1 -> S2 2 CY2->CY3
|
||||
Loc(0, 0, +1), // S2 2 -> S2 3 CY3->CY4
|
||||
Loc(1, 0, -3), // S2 3 -> S6 0 CY4->CY5
|
||||
|
||||
Loc(0, 0, +1), // S6 0 -> S6 1 CY5->CY6
|
||||
Loc(0, 0, +1), // S6 1 -> S6 2 CY6->CY7
|
||||
Loc(0, 0, +1), // S6 2 -> S6 3 CY7->CY8
|
||||
Loc(1, 0, -3), // S6 3 -> S10 0 CY8->CY9
|
||||
|
||||
Loc(0, 0, +1), // S10 0 -> S10 1 CY9->CY10
|
||||
Loc(0, 0, +1), // S10 1 -> S10 2 CY10->CY11
|
||||
Loc(0, 0, +1), // S10 2 -> S10 3 CY11->CY12
|
||||
Loc(0,-1, 0), // S10 3 -> S9 3 CY12->CY13
|
||||
};
|
||||
int section = (loc.x % 4 - 1 + 3 * (loc.y % 4)) * 4 + loc.z - BEL_CY_Z;
|
||||
Loc result = map.at(section);
|
||||
result.x += loc.x;
|
||||
result.y += loc.y;
|
||||
result.z += loc.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
Loc getCYFE(Loc root, int pos)
|
||||
{
|
||||
int p[] = { 2-1, 25-1, 10-1, 17-1 };
|
||||
int cy = root.z - BEL_CY_Z;
|
||||
Loc result;
|
||||
result.x = root.x;
|
||||
result.y = root.y;
|
||||
result.z = p[pos] + cy * 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NgUltraImpl::getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const
|
||||
{
|
||||
Loc prev = root_loc;
|
||||
for (auto child : cluster->constr_children) {
|
||||
Loc child_loc = if_using_basecluster<Loc>(child, [&](const BaseClusterInfo *child) {
|
||||
switch(child->constr_z) {
|
||||
case PLACE_CY_CHAIN : { Loc l = getNextLocInCYChain(prev); prev = l; return l; }
|
||||
case PLACE_CY_FE1: return getCYFE(root_loc,0);
|
||||
case PLACE_CY_FE2: return getCYFE(root_loc,1);
|
||||
case PLACE_CY_FE3: return getCYFE(root_loc,2);
|
||||
case PLACE_CY_FE4: return getCYFE(root_loc,3);
|
||||
default:
|
||||
Loc result;
|
||||
result.x = root_loc.x + child->constr_x;
|
||||
result.y = root_loc.y + child->constr_y;
|
||||
result.z = child->constr_abs_z ? child->constr_z : (root_loc.z + child->constr_z);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
BelId child_bel = ctx->getBelByLocation(child_loc);
|
||||
if (child_bel == BelId() || !this->isValidBelForCellType(child->type, child_bel))
|
||||
return false;
|
||||
placement.emplace_back(child, child_bel);
|
||||
bool val = if_using_basecluster<bool>(child, [&](const BaseClusterInfo *child_cluster) -> bool {
|
||||
return getChildPlacement(child_cluster, child_loc, placement);
|
||||
});
|
||||
if (!val) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NgUltraImpl::getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const
|
||||
{
|
||||
CellInfo *root_cell = get_cluster_root(ctx, cluster);
|
||||
return if_using_basecluster<bool>(root_cell, [&](const BaseClusterInfo *cluster) -> bool {
|
||||
placement.clear();
|
||||
NPNR_ASSERT(root_bel != BelId());
|
||||
Loc root_loc = ctx->getBelLocation(root_bel);
|
||||
if (cluster->constr_abs_z) {
|
||||
// Coerce root to absolute z constraint
|
||||
root_loc.z = cluster->constr_z;
|
||||
root_bel = ctx->getBelByLocation(root_loc);
|
||||
if (root_bel == BelId() || !this->isValidBelForCellType(root_cell->type, root_bel))
|
||||
return false;
|
||||
}
|
||||
placement.emplace_back(root_cell, root_bel);
|
||||
return getChildPlacement(cluster, root_loc, placement);
|
||||
});
|
||||
}
|
||||
|
||||
BoundingBox NgUltraImpl::getRouteBoundingBox(WireId src, WireId dst) const
|
||||
{
|
||||
int x0, y0, x1, y1;
|
||||
auto expand = [&](int x, int y) {
|
||||
x0 = std::min(x0, x);
|
||||
x1 = std::max(x1, x);
|
||||
y0 = std::min(y0, y);
|
||||
y1 = std::max(y1, y);
|
||||
};
|
||||
tile_xy(ctx->chip_info, src.tile, x0, y0);
|
||||
x1 = x0;
|
||||
y1 = y0;
|
||||
int dx, dy;
|
||||
tile_xy(ctx->chip_info, dst.tile, dx, dy);
|
||||
expand(dx, dy);
|
||||
// Two TILEs left and up, and one tile right and down
|
||||
return {(x0 & 0xfffc) - 8,
|
||||
(y0 & 0xfffc) - 8,
|
||||
(x1 & 0xfffc) + 8,
|
||||
(y1 & 0xfffc) + 8};
|
||||
}
|
||||
|
||||
delay_t NgUltraImpl::estimateDelay(WireId src, WireId dst) const
|
||||
{
|
||||
int sx, sy, dx, dy;
|
||||
tile_xy(ctx->chip_info, src.tile, sx, sy);
|
||||
tile_xy(ctx->chip_info, dst.tile, dx, dy);
|
||||
if (sx==dx && sy==dy) {
|
||||
// Same sub tile
|
||||
return 50;
|
||||
} else if (((sx & 0xfffc) == (dx & 0xfffc)) && ((sy & 0xfffc) == (dy & 0xfffc))) {
|
||||
// Same "TILE"
|
||||
return 200;
|
||||
}
|
||||
return 500 + 100 * (std::abs(dy - sy)/4 + std::abs(dx - sx)/4);
|
||||
}
|
||||
|
||||
|
||||
delay_t NgUltraImpl::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const
|
||||
{
|
||||
Loc src_loc = ctx->getBelLocation(src_bel), dst_loc = ctx->getBelLocation(dst_bel);
|
||||
if (src_loc.x==dst_loc.x && src_loc.y==dst_loc.y) {
|
||||
// Same sub tile
|
||||
return 50;
|
||||
} else if (((src_loc.x & 0xfffc) == (dst_loc.x & 0xfffc)) && ((src_loc.y & 0xfffc) == (dst_loc.y & 0xfffc))) {
|
||||
// Same "TILE"
|
||||
return 200;
|
||||
}
|
||||
return 500 + 100 * (std::abs(dst_loc.y - src_loc.y)/4 + std::abs(dst_loc.x - src_loc.x)/4);
|
||||
}
|
||||
|
||||
struct NgUltraArch : HimbaechelArch
|
||||
{
|
||||
NgUltraArch() : HimbaechelArch("ng-ultra"){};
|
||||
@ -146,6 +469,5 @@ struct NgUltraArch : HimbaechelArch
|
||||
return std::make_unique<NgUltraImpl>();
|
||||
}
|
||||
} ngUltraArch;
|
||||
} // namespace
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
84
himbaechel/uarch/ng-ultra/ng_ultra.h
Normal file
84
himbaechel/uarch/ng-ultra/ng_ultra.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2023 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2023 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HIMBAECHEL_NG_ULTRA_H
|
||||
#define HIMBAECHEL_NG_ULTRA_H
|
||||
#include <deque>
|
||||
|
||||
#include "extra_data.h"
|
||||
#include "himbaechel_api.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "himbaechel_helpers.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct NgUltraImpl : HimbaechelAPI
|
||||
{
|
||||
~NgUltraImpl();
|
||||
void init_database(Arch *arch) override;
|
||||
|
||||
void init(Context *ctx) override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override;
|
||||
IdString getBelBucketForCellType(IdString cell_type) const override;
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
||||
|
||||
// Flow management
|
||||
void pack() override;
|
||||
void preRoute() override;
|
||||
void postRoute() override;
|
||||
|
||||
void configurePlacerHeap(PlacerHeapCfg &cfg) override;
|
||||
|
||||
bool getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const override;
|
||||
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||
std::vector<std::pair<CellInfo *, BelId>> &placement) const;
|
||||
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
delay_t estimateDelay(WireId src, WireId dst) const override;
|
||||
delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override;
|
||||
|
||||
public:
|
||||
IdString tile_name_id(int tile) const;
|
||||
std::string tile_name(int tile) const;
|
||||
|
||||
dict<IdString,BelId> iom_bels;
|
||||
dict<std::string, std::string> bank_voltage;
|
||||
|
||||
private:
|
||||
void write_bitstream_json(const std::string &filename);
|
||||
void route_clocks();
|
||||
void parse_csv(const std::string &filename);
|
||||
|
||||
// Misc utility functions
|
||||
bool get_mux_data(BelId bel, IdString port, uint8_t *value);
|
||||
bool get_mux_data(WireId wire, uint8_t *value);
|
||||
|
||||
const NGUltraTileInstExtraDataPOD *tile_extra_data(int tile) const;
|
||||
int tile_lobe(int tile) const;
|
||||
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
@ -1,33 +0,0 @@
|
||||
import json
|
||||
from os import path
|
||||
import sys
|
||||
sys.path.append(path.join(path.dirname(__file__), "../.."))
|
||||
from himbaechel_dbgen.chip import *
|
||||
|
||||
def create_tile(ch: Chip):
|
||||
tt = ch.create_tile_type("TILE")
|
||||
|
||||
def create_mesh(ch: Chip):
|
||||
tt = ch.create_tile_type("MESH")
|
||||
|
||||
def create_cgb(ch: Chip):
|
||||
tt = ch.create_tile_type("CGB")
|
||||
|
||||
def main():
|
||||
ch = Chip("ng-ultra", "NG-ULTRA", 93, 50)
|
||||
ch.strs.read_constids(path.join(path.dirname(__file__), "constids.inc"))
|
||||
pkg = ch.create_package("FF-1760")
|
||||
|
||||
create_tile(ch)
|
||||
create_mesh(ch)
|
||||
create_cgb(ch)
|
||||
|
||||
with open("/home/lofty/prjbeyond/database/NG-ULTRA/tilegrid.json") as f:
|
||||
tilegrid = json.load(f)
|
||||
for name, data in tilegrid.items():
|
||||
ch.set_tile_type(data["x"], data["y"], data["type"])
|
||||
|
||||
ch.write_bba(sys.argv[1])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
691
himbaechel/uarch/ng-ultra/pack.cc
Normal file
691
himbaechel/uarch/ng-ultra/pack.cc
Normal file
@ -0,0 +1,691 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2023 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2023 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "pack.h"
|
||||
#include <algorithm>
|
||||
#include <boost/optional.hpp>
|
||||
#include <iterator>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include "chain_utils.h"
|
||||
#include "design_utils.h"
|
||||
#include "extra_data.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// Return true if a cell is a LUT
|
||||
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_NX_LUT; }
|
||||
|
||||
// Return true if a cell is a flipflop
|
||||
inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_NX_DFF; }
|
||||
|
||||
// Return true if a cell is a FE
|
||||
inline bool is_fe(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_BEYOND_FE; }
|
||||
|
||||
// Process the contents of packed_cells
|
||||
void NgUltraPacker::flush_cells()
|
||||
{
|
||||
for (auto pcell : packed_cells) {
|
||||
for (auto &port : ctx->cells[pcell]->ports) {
|
||||
ctx->cells[pcell]->disconnectPort(port.first);
|
||||
}
|
||||
ctx->cells.erase(pcell);
|
||||
}
|
||||
for (auto &ncell : new_cells) {
|
||||
ctx->cells[ncell->name] = std::move(ncell);
|
||||
}
|
||||
packed_cells.clear();
|
||||
new_cells.clear();
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_constants(void)
|
||||
{
|
||||
log_info("Packing constants..\n");
|
||||
// Replace constants with LUTs
|
||||
const dict<IdString, Property> vcc_params = {{id_lut_table, Property(0xFFFF, 16)}, {id_lut_used, Property(1,1)}, {id_dff_used, Property(1,1)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_lut_table, Property(0x0000, 16)}, {id_lut_used, Property(1,1)}, {id_dff_used, Property(1,1)}};
|
||||
|
||||
h.replace_constants(CellTypePort(id_BEYOND_FE, id_LO), CellTypePort(id_BEYOND_FE, id_LO), vcc_params, gnd_params);
|
||||
}
|
||||
|
||||
void NgUltraPacker::remove_constants()
|
||||
{
|
||||
log_info("Removing constants..\n");
|
||||
auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV"));
|
||||
if (fnd_cell != ctx->cells.end()) {
|
||||
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC"));
|
||||
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries()==0) {
|
||||
ctx->cells.erase(fnd_cell);
|
||||
ctx->nets.erase(fnd_net);
|
||||
log_info(" Removed unused VCC cell\n");
|
||||
}
|
||||
}
|
||||
fnd_cell = ctx->cells.find(ctx->id("$PACKER_GND_DRV"));
|
||||
if (fnd_cell != ctx->cells.end()) {
|
||||
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND"));
|
||||
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries()==0) {
|
||||
ctx->cells.erase(fnd_cell);
|
||||
ctx->nets.erase(fnd_net);
|
||||
log_info(" Removed unused GND cell\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraPacker::update_lut_init()
|
||||
{
|
||||
log_info("Update LUT init...\n");
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_LUT))
|
||||
continue;
|
||||
set_lut_input_if_constant(&ci, id_I1);
|
||||
set_lut_input_if_constant(&ci, id_I2);
|
||||
set_lut_input_if_constant(&ci, id_I3);
|
||||
set_lut_input_if_constant(&ci, id_I4);
|
||||
NetInfo *o = ci.getPort(id_O);
|
||||
if (!o) {
|
||||
// Remove those that do not output any value
|
||||
log_warning("Removing LUT '%s' since output is not connected.\n", ci.name.c_str(ctx));
|
||||
packed_cells.insert(ci.name);
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void NgUltraPacker::update_dffs()
|
||||
{
|
||||
log_info("Update DFFs...\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_DFF))
|
||||
continue;
|
||||
|
||||
if (int_or_default(ci.params, ctx->id("dff_init"), 0)==0) {
|
||||
// Reset not used
|
||||
ci.disconnectPort(id_R);
|
||||
} else {
|
||||
// Reset used
|
||||
NetInfo *net = ci.getPort(id_R);
|
||||
if (net) {
|
||||
if (net->name == ctx->id("$PACKER_GND")) {
|
||||
log_warning("Removing reset on '%s' since it is always 0.\n", ci.name.c_str(ctx));
|
||||
ci.disconnectPort(id_R);
|
||||
} else if (net->name == ctx->id("$PACKER_VCC")) {
|
||||
log_error("Invalid DFF configuration, reset on '%s' is always 1.\n", ci.name.c_str(ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (int_or_default(ci.params, ctx->id("dff_load"), 0)==0) {
|
||||
// Load not used
|
||||
ci.disconnectPort(id_L);
|
||||
} else {
|
||||
// Load used
|
||||
NetInfo *net = ci.getPort(id_L);
|
||||
if (net) {
|
||||
if (net->name == ctx->id("$PACKER_VCC")) {
|
||||
log_warning("Removing load enable on '%s' since it is always 1.\n", ci.name.c_str(ctx));
|
||||
ci.disconnectPort(id_L);
|
||||
} else if (net->name == ctx->id("$PACKER_GND")) {
|
||||
log_warning("Converting to self loop, since load enable on '%s' is always 0.\n", ci.name.c_str(ctx));
|
||||
ci.disconnectPort(id_L);
|
||||
ci.disconnectPort(id_I);
|
||||
NetInfo *out = ci.getPort(id_O);
|
||||
ci.connectPort(id_I, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int make_init_with_const_input(int init, int input, bool value)
|
||||
{
|
||||
int new_init = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (((i >> input) & 0x1) != value) {
|
||||
int other_i = (i & (~(1 << input))) | (value << input);
|
||||
if ((init >> other_i) & 0x1)
|
||||
new_init |= (1 << i);
|
||||
} else {
|
||||
if ((init >> i) & 0x1)
|
||||
new_init |= (1 << i);
|
||||
}
|
||||
}
|
||||
return new_init;
|
||||
}
|
||||
|
||||
void NgUltraPacker::set_lut_input_if_constant(CellInfo *cell, IdString input)
|
||||
{
|
||||
NetInfo *net = cell->getPort(input);
|
||||
if (!net)
|
||||
return;
|
||||
if (!net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC")))
|
||||
return;
|
||||
bool value = net->name == ctx->id("$PACKER_VCC");
|
||||
int index = std::string("1234").find(input.str(ctx));
|
||||
int init = int_or_default(cell->params, id_lut_table);
|
||||
int new_init = make_init_with_const_input(init, index, value);
|
||||
cell->params[id_lut_table] = Property(new_init, 16);
|
||||
cell->disconnectPort(input);
|
||||
}
|
||||
|
||||
void NgUltraPacker::lut_to_fe(CellInfo *lut, CellInfo *fe, bool no_dff, Property lut_table)
|
||||
{
|
||||
fe->params[id_lut_table] = lut_table;
|
||||
fe->params[id_lut_used] = Property(1,1);
|
||||
lut->movePortTo(id_I1, fe, id_I1);
|
||||
lut->movePortTo(id_I2, fe, id_I2);
|
||||
lut->movePortTo(id_I3, fe, id_I3);
|
||||
lut->movePortTo(id_I4, fe, id_I4);
|
||||
lut->movePortTo(id_O, fe, id_LO);
|
||||
if (no_dff) {
|
||||
fe->timing_index = ctx->get_cell_timing_idx(ctx->id("BEYOND_FE_LUT"));
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraPacker::dff_to_fe(CellInfo *dff, CellInfo *fe, bool pass_thru_lut)
|
||||
{
|
||||
if (pass_thru_lut) {
|
||||
dff->movePortTo(id_I, fe, id_I1);
|
||||
fe->params[id_lut_table] = Property(0xaaaa, 16);
|
||||
fe->params[id_lut_used] = Property(1,1);
|
||||
}
|
||||
else
|
||||
dff->movePortTo(id_I, fe, id_DI);
|
||||
fe->params[id_dff_used] = Property(1,1);
|
||||
fe->setParam(ctx->id("type"), Property("DFF"));
|
||||
dff->movePortTo(id_R, fe, id_R);
|
||||
dff->movePortTo(id_CK, fe, id_CK);
|
||||
dff->movePortTo(id_L, fe, id_L);
|
||||
dff->movePortTo(id_O, fe, id_DO);
|
||||
|
||||
if (dff->params.count(ctx->id("dff_ctxt"))) fe->setParam(ctx->id("dff_ctxt"),dff->params[ctx->id("dff_ctxt")]);
|
||||
if (dff->params.count(ctx->id("dff_edge"))) fe->setParam(ctx->id("dff_edge"),dff->params[ctx->id("dff_edge")]);
|
||||
if (dff->params.count(ctx->id("dff_init"))) fe->setParam(ctx->id("dff_init"),dff->params[ctx->id("dff_init")]);
|
||||
if (dff->params.count(ctx->id("dff_load"))) fe->setParam(ctx->id("dff_load"),dff->params[ctx->id("dff_load")]);
|
||||
if (dff->params.count(ctx->id("dff_sync"))) fe->setParam(ctx->id("dff_sync"),dff->params[ctx->id("dff_sync")]);
|
||||
if (dff->params.count(ctx->id("dff_type"))) fe->setParam(ctx->id("dff_type"),dff->params[ctx->id("dff_type")]);
|
||||
|
||||
if (pass_thru_lut) {
|
||||
NetInfo *new_out = ctx->createNet(ctx->id(dff->name.str(ctx) + "$LO"));
|
||||
fe->connectPort(id_LO, new_out);
|
||||
fe->connectPort(id_DI, new_out);
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_lut_dffs(void)
|
||||
{
|
||||
log_info("Pack LUT-DFFs...\n");
|
||||
|
||||
int lut_only = 0, lut_and_ff = 0;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_LUT))
|
||||
continue;
|
||||
if (!ci.params.count(id_lut_table))
|
||||
log_error("Cell '%s' missing lut_table\n", ci.name.c_str(ctx));
|
||||
|
||||
std::unique_ptr<CellInfo> packed = create_cell(id_BEYOND_FE, ctx->id(ci.name.str(ctx) + "$fe"));
|
||||
packed_cells.insert(ci.name);
|
||||
|
||||
bool packed_dff = false;
|
||||
NetInfo *o = ci.getPort(id_O);
|
||||
if (o) {
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_I, true);
|
||||
if (dff) {
|
||||
if (ctx->verbose)
|
||||
log_info("found attached dff %s\n", dff->name.c_str(ctx));
|
||||
lut_to_fe(&ci, packed.get(), false, ci.params[id_lut_table]);
|
||||
dff_to_fe(dff, packed.get(), false);
|
||||
++lut_and_ff;
|
||||
packed_cells.insert(dff->name);
|
||||
if (ctx->verbose)
|
||||
log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx));
|
||||
packed_dff = true;
|
||||
}
|
||||
}
|
||||
if (!packed_dff) {
|
||||
lut_to_fe(&ci, packed.get(), true, ci.params[id_lut_table]);
|
||||
++lut_only;
|
||||
}
|
||||
new_cells.push_back(std::move(packed));
|
||||
}
|
||||
if (lut_only)
|
||||
log_info(" %6d FEs used as LUT only\n", lut_only);
|
||||
if (lut_and_ff)
|
||||
log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff);
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_dffs(void)
|
||||
{
|
||||
int dff_only = 0;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_DFF))
|
||||
continue;
|
||||
std::unique_ptr<CellInfo> packed = create_cell(id_BEYOND_FE, ctx->id(ci.name.str(ctx) + "$fe"));
|
||||
packed_cells.insert(ci.name);
|
||||
dff_to_fe(&ci, packed.get(), true);
|
||||
++dff_only;
|
||||
new_cells.push_back(std::move(packed));
|
||||
}
|
||||
if (dff_only)
|
||||
log_info(" %6d FEs used as DFF only\n", dff_only);
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_iobs(void)
|
||||
{
|
||||
log_info("Pack IOBs...\n");
|
||||
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
|
||||
const pool<CellTypePort> top_ports{
|
||||
CellTypePort(id_NX_IOB_I, id_IO),
|
||||
CellTypePort(id_NX_IOB_O, id_IO),
|
||||
};
|
||||
std::vector<IdString> to_remove;
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto &ci = *cell.second;
|
||||
if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf")))
|
||||
continue;
|
||||
NetInfo *i = ci.getPort(id_I);
|
||||
if (i && i->driver.cell) {
|
||||
if (!top_ports.count(CellTypePort(i->driver)))
|
||||
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
|
||||
ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port));
|
||||
for (auto &attrs : ci.attrs)
|
||||
i->driver.cell->attrs[attrs.first] = attrs.second;
|
||||
for (auto ¶ms : ci.params)
|
||||
i->driver.cell->params[params.first] = params.second;
|
||||
}
|
||||
NetInfo *o = ci.getPort(id_O);
|
||||
if (o) {
|
||||
for (auto &usr : o->users) {
|
||||
if (!top_ports.count(CellTypePort(usr)))
|
||||
log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci),
|
||||
ctx->nameOf(usr.cell), ctx->nameOf(usr.port));
|
||||
for (auto &attrs : ci.attrs)
|
||||
usr.cell->attrs[attrs.first] = attrs.second;
|
||||
for (auto ¶ms : ci.params)
|
||||
usr.cell->params[params.first] = params.second;
|
||||
}
|
||||
}
|
||||
ci.disconnectPort(id_I);
|
||||
ci.disconnectPort(id_O);
|
||||
to_remove.push_back(ci.name);
|
||||
}
|
||||
for (IdString cell_name : to_remove)
|
||||
ctx->cells.erase(cell_name);
|
||||
|
||||
std::vector<CellInfo*> to_update;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_IOB_I, id_NX_IOB_O))
|
||||
continue;
|
||||
if (ci.params.count(id_location) && ci.attrs.count(id_LOC)) {
|
||||
if (ci.params[id_location].as_string() != ci.attrs[id_LOC].as_string())
|
||||
log_error("Found conflicting LOC and location for IO:%s\n", ctx->nameOf(&ci));
|
||||
}
|
||||
if (ci.params.count(id_location) != 0)
|
||||
ci.setAttr(id_LOC, ci.params[id_location]);
|
||||
if (ci.attrs.count(id_LOC) == 0) {
|
||||
log_error("Unconstrained IO:%s\n", ctx->nameOf(&ci));
|
||||
}
|
||||
std::string loc = ci.attrs.at(id_LOC).to_string();
|
||||
ci.setParam(id_location, Property(loc));
|
||||
BelId bel = ctx->get_package_pin_bel(ctx->id(loc));
|
||||
if (bel == BelId())
|
||||
log_error("Unable to constrain IO '%s', device does not have a pin named '%s'\n", ci.name.c_str(ctx),
|
||||
loc.c_str());
|
||||
log_info(" Constraining '%s' to pad '%s'\n", ci.name.c_str(ctx), loc.c_str());
|
||||
if (!ctx->checkBelAvail(bel)) {
|
||||
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel),
|
||||
ctx->nameOf(ctx->getBoundBelCell(bel)));
|
||||
}
|
||||
|
||||
char last = loc.back();
|
||||
IdString new_type = (last == 'N' or last == 'P') ? id_IOTP : id_IOP;
|
||||
|
||||
std::string subtype = "IOP";
|
||||
if (ci.type==id_NX_IOB_O) subtype = "OP";
|
||||
if (ci.type==id_NX_IOB_I) subtype = "IP";
|
||||
|
||||
ci.unsetAttr(id_LOC);
|
||||
ci.setParam(ctx->id("type"), Property(subtype));
|
||||
ci.setParam(ctx->id("alias_vhdl"), Property(ci.type.c_str(ctx)));
|
||||
|
||||
ci.type = new_type;
|
||||
|
||||
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||
|
||||
NetInfo *net = ci.getPort(id_C);
|
||||
if (net && net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC")))
|
||||
{
|
||||
to_update.push_back(&ci);
|
||||
}
|
||||
}
|
||||
for (auto cell : to_update) {
|
||||
CellInfo *iod_cd = create_cell_ptr((cell->type==id_IOTP) ? id_DDFR : id_DFR, ctx->id(cell->name.str(ctx) + "$iod_cd"));
|
||||
cell->disconnectPort(id_C);
|
||||
NetInfo *new_out = ctx->createNet(ctx->id(iod_cd->name.str(ctx) + "$O"));
|
||||
iod_cd->connectPort(id_O, new_out);
|
||||
iod_cd->setParam(ctx->id("iobname"),str_or_default(cell->params, ctx->id("iobname"), ""));
|
||||
iod_cd->setParam(ctx->id("type"), Property("BFR"));
|
||||
if (cell->params[ctx->id("type")].as_string()=="IP")
|
||||
iod_cd->setParam(ctx->id("mode"), Property(0, 2));
|
||||
else
|
||||
iod_cd->setParam(ctx->id("mode"), Property(1, 2));
|
||||
cell->connectPort(id_C,new_out);
|
||||
Loc loc = cell->getLocation();
|
||||
loc.z += 3;
|
||||
BelId bel = ctx->getBelByLocation(loc);
|
||||
ctx->bindBel(bel, iod_cd, PlaceStrength::STRENGTH_LOCKED);
|
||||
}
|
||||
}
|
||||
void NgUltraPacker::pack_ioms(void)
|
||||
{
|
||||
log_info("Pack IOMs...\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_NX_IOM_U))
|
||||
continue;
|
||||
ci.type = id_IOM;
|
||||
IdString iob;
|
||||
for (auto &port : ci.ports) {
|
||||
if (port.second.type == PORT_IN && port.second.net != nullptr) {
|
||||
if (port.second.net->name == ctx->id("$PACKER_GND"))
|
||||
ci.disconnectPort(port.first);
|
||||
else if (port.second.net->driver.cell != nullptr &&
|
||||
port.second.net->driver.cell->type == id_IOTP) {
|
||||
IdString loc = uarch->tile_name_id(port.second.net->driver.cell->bel.tile);
|
||||
if (iob != IdString() && loc != iob) {
|
||||
log_error("Unable to constrain IOM '%s', connection to multiple IO banks exist.\n",
|
||||
ci.name.c_str(ctx));
|
||||
}
|
||||
iob = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iob == IdString())
|
||||
log_error("Unable to constrain IOM '%s', no connection to nearby IO banks found.\n", ci.name.c_str(ctx));
|
||||
log_info(" Constraining '%s' to bank '%s'\n", ci.name.c_str(ctx), iob.c_str(ctx));
|
||||
BelId bel = uarch->iom_bels[iob];
|
||||
if (!ctx->checkBelAvail(bel)) {
|
||||
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel),
|
||||
ctx->nameOf(ctx->getBoundBelCell(bel)));
|
||||
}
|
||||
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_cy_input_and_output(CellInfo *cy, IdString cluster, IdString in_port, IdString out_port, int placer, int &lut_only, int &lut_and_ff, int &dff_only)
|
||||
{
|
||||
CellInfo *fe = create_cell_ptr(id_BEYOND_FE, ctx->id(cy->name.str(ctx) + "$" + in_port.c_str(ctx)));
|
||||
NetInfo *net = cy->getPort(in_port);
|
||||
if (net) {
|
||||
if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
fe->params[id_lut_table] = Property((net->name ==ctx->id("$PACKER_GND")) ? 0x0000 : 0xffff, 16);
|
||||
fe->params[id_lut_used] = Property(1,1);
|
||||
cy->disconnectPort(in_port);
|
||||
NetInfo *new_out = ctx->createNet(ctx->id(fe->name.str(ctx) + "$o"));
|
||||
fe->connectPort(id_LO, new_out);
|
||||
cy->connectPort(in_port, new_out);
|
||||
} else {
|
||||
CellInfo *lut = net_driven_by(ctx, net, is_lut, id_O);
|
||||
if (lut && net->users.entries()==1) {
|
||||
if (!lut->params.count(id_lut_table))
|
||||
log_error("Cell '%s' missing lut_table\n", lut->name.c_str(ctx));
|
||||
lut_to_fe(lut, fe, false, lut->params[id_lut_table]);
|
||||
packed_cells.insert(lut->name);
|
||||
} else {
|
||||
fe->params[id_lut_table] = Property(0xaaaa, 16);
|
||||
fe->params[id_lut_used] = Property(1,1);
|
||||
cy->disconnectPort(in_port);
|
||||
NetInfo *new_out = ctx->createNet(ctx->id(fe->name.str(ctx) + "$o"));
|
||||
fe->connectPort(id_I1, net);
|
||||
fe->connectPort(id_LO, new_out);
|
||||
cy->connectPort(in_port, new_out);
|
||||
}
|
||||
}
|
||||
lut_only++;
|
||||
}
|
||||
net = cy->getPort(out_port);
|
||||
CellInfo *dff = net_only_drives(ctx, net, is_dff, id_I, true);
|
||||
if (dff) {
|
||||
if (ctx->verbose)
|
||||
log_info("found attached dff %s\n", dff->name.c_str(ctx));
|
||||
dff_to_fe(dff, fe, false);
|
||||
packed_cells.insert(dff->name);
|
||||
if (net) {
|
||||
lut_only--;
|
||||
lut_and_ff++;
|
||||
} else
|
||||
dff_only++;
|
||||
}
|
||||
fe->cluster = cluster;
|
||||
fe->constr_z = placer;
|
||||
cy->constr_children.push_back(fe);
|
||||
}
|
||||
|
||||
void NgUltraPacker::exchange_if_constant(CellInfo *cell, IdString input1, IdString input2)
|
||||
{
|
||||
NetInfo *net1 = cell->getPort(input1);
|
||||
NetInfo *net2 = cell->getPort(input2);
|
||||
if (!net1 || !net2)
|
||||
return;
|
||||
if (net1->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
cell->disconnectPort(input1);
|
||||
cell->disconnectPort(input2);
|
||||
cell->connectPort(input1, net2);
|
||||
cell->connectPort(input2, net1);
|
||||
}
|
||||
}
|
||||
|
||||
void NgUltraPacker::pack_cys(void)
|
||||
{
|
||||
log_info("Packing carries..\n");
|
||||
std::vector<CellInfo *> root_cys;
|
||||
int lut_only = 0, lut_and_ff = 0, dff_only = 0;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_NX_CY)
|
||||
continue;
|
||||
NetInfo *ci_net = ci->getPort(id_CI);
|
||||
if (!ci_net || !ci_net->driver.cell || ci_net->driver.cell->type != id_NX_CY) {
|
||||
root_cys.push_back(ci);
|
||||
}
|
||||
exchange_if_constant(ci, id_A1, id_B1);
|
||||
exchange_if_constant(ci, id_A2, id_B2);
|
||||
exchange_if_constant(ci, id_A3, id_B3);
|
||||
exchange_if_constant(ci, id_A4, id_B4);
|
||||
}
|
||||
|
||||
std::vector<std::vector<CellInfo *>> groups;
|
||||
for (auto root : root_cys) {
|
||||
std::vector<CellInfo *> group;
|
||||
CellInfo *cy = root;
|
||||
group.push_back(cy);
|
||||
while (true) {
|
||||
NetInfo *co_net = cy->getPort(id_CO);
|
||||
if (co_net && co_net->users.entries() > 0) {
|
||||
cy = (*co_net->users.begin()).cell;
|
||||
if (cy->type != id_NX_CY || co_net->users.entries() != 1) {
|
||||
log_warning("Cells %s CO output connected to:\n",group.back()->name.c_str(ctx));
|
||||
for(auto user : co_net->users)
|
||||
log_warning("\t%s of type %s\n",user.cell->name.c_str(ctx), user.cell->type.c_str(ctx));
|
||||
log_error("NX_CY can only be chained with one other NX_CY cell\n");
|
||||
}
|
||||
group.push_back(cy);
|
||||
} else {
|
||||
// Disconnect unused ports on last CY in chain
|
||||
// at least id_A1 and id_B1 will be connected
|
||||
// Reverse direction, must stop if used, then
|
||||
// rest is used as well
|
||||
if (!cy->getPort(id_S4) || cy->getPort(id_S4)->users.entries()==0) {
|
||||
cy->disconnectPort(id_A4);
|
||||
cy->disconnectPort(id_B4);
|
||||
} else break;
|
||||
if (!cy->getPort(id_S3) || cy->getPort(id_S3)->users.entries()==0) {
|
||||
cy->disconnectPort(id_A3);
|
||||
cy->disconnectPort(id_B3);
|
||||
} else break;
|
||||
if (!cy->getPort(id_S2) || cy->getPort(id_S2)->users.entries()==0) {
|
||||
cy->disconnectPort(id_A2);
|
||||
cy->disconnectPort(id_B2);
|
||||
} else break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
groups.push_back(group);
|
||||
}
|
||||
|
||||
for (auto &grp : groups) {
|
||||
CellInfo *root = grp.front();
|
||||
root->type = id_CY;
|
||||
root->cluster = root->name;
|
||||
if (grp.size() > 24)
|
||||
log_error("NX_CY chains are limited to contain 24 elements maximum.\n");
|
||||
|
||||
for (int i = 0; i < int(grp.size()); i++) {
|
||||
CellInfo *cy = grp.at(i);
|
||||
cy->type = id_CY;
|
||||
if (i != 0) {
|
||||
cy->cluster = root->name;
|
||||
root->constr_children.push_back(cy);
|
||||
cy->constr_z = PLACE_CY_CHAIN;
|
||||
}
|
||||
pack_cy_input_and_output(cy, root->name, id_B1, id_S1, PLACE_CY_FE1, lut_only, lut_and_ff, dff_only);
|
||||
pack_cy_input_and_output(cy, root->name, id_B2, id_S2, PLACE_CY_FE2, lut_only, lut_and_ff, dff_only);
|
||||
pack_cy_input_and_output(cy, root->name, id_B3, id_S3, PLACE_CY_FE3, lut_only, lut_and_ff, dff_only);
|
||||
pack_cy_input_and_output(cy, root->name, id_B4, id_S4, PLACE_CY_FE4, lut_only, lut_and_ff, dff_only);
|
||||
NetInfo *net = cy->getPort(id_CI);
|
||||
if (net) {
|
||||
if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
// Constant driver for CI is configuration
|
||||
cy->disconnectPort(id_CI);
|
||||
} else {
|
||||
if (net->driver.cell && net->driver.cell->type != id_CY)
|
||||
log_error("CI must be constant or driven by CO in cell '%s'\n", cy->name.c_str(ctx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lut_only)
|
||||
log_info(" %6d FEs used as LUT only\n", lut_only);
|
||||
if (lut_and_ff)
|
||||
log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff);
|
||||
if (dff_only)
|
||||
log_info(" %6d FEs used as DFF only\n", dff_only);
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void NgUltraImpl::pack()
|
||||
{
|
||||
const ArchArgs &args = ctx->args;
|
||||
if (args.options.count("csv")) {
|
||||
parse_csv(args.options.at("csv"));
|
||||
}
|
||||
NgUltraPacker packer(ctx, this);
|
||||
packer.pack_constants();
|
||||
packer.update_lut_init();
|
||||
packer.update_dffs();
|
||||
packer.pack_iobs();
|
||||
packer.pack_ioms();
|
||||
packer.pack_cys();
|
||||
packer.pack_lut_dffs();
|
||||
packer.pack_dffs();
|
||||
packer.remove_constants();
|
||||
}
|
||||
|
||||
void NgUltraImpl::route_clocks()
|
||||
{
|
||||
log_info("Routing global clocks...\n");
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *clk_net = net.second.get();
|
||||
if (!clk_net->driver.cell)
|
||||
continue;
|
||||
|
||||
// check if we have a global clock net, skip otherwise
|
||||
bool is_global = false;
|
||||
if (clk_net->driver.cell->type.in(id_IOM) && clk_net->driver.port.in(id_CKO1, id_CKO2))
|
||||
is_global = true;
|
||||
if (!is_global)
|
||||
continue;
|
||||
|
||||
log_info(" routing clock '%s'\n", clk_net->name.c_str(ctx));
|
||||
ctx->bindWire(ctx->getNetinfoSourceWire(clk_net), clk_net, STRENGTH_LOCKED);
|
||||
|
||||
for (auto &usr : clk_net->users) {
|
||||
std::queue<WireId> visit;
|
||||
dict<WireId, PipId> backtrace;
|
||||
WireId dest = WireId();
|
||||
|
||||
auto sink_wire = ctx->getNetinfoSinkWire(clk_net, usr, 0);
|
||||
if (ctx->debug) {
|
||||
auto sink_wire_name = "(uninitialized)";
|
||||
if (sink_wire != WireId())
|
||||
sink_wire_name = ctx->nameOfWire(sink_wire);
|
||||
log_info(" routing arc to %s.%s (wire %s):\n", usr.cell->name.c_str(ctx), usr.port.c_str(ctx),
|
||||
sink_wire_name);
|
||||
}
|
||||
visit.push(sink_wire);
|
||||
while (!visit.empty()) {
|
||||
WireId curr = visit.front();
|
||||
visit.pop();
|
||||
if (ctx->getBoundWireNet(curr) == clk_net) {
|
||||
dest = curr;
|
||||
break;
|
||||
}
|
||||
for (auto uh : ctx->getPipsUphill(curr)) {
|
||||
if (!ctx->checkPipAvail(uh))
|
||||
continue;
|
||||
WireId src = ctx->getPipSrcWire(uh);
|
||||
if (backtrace.count(src))
|
||||
continue;
|
||||
if (!ctx->checkWireAvail(src) && ctx->getBoundWireNet(src) != clk_net)
|
||||
continue;
|
||||
backtrace[src] = uh;
|
||||
visit.push(src);
|
||||
}
|
||||
}
|
||||
if (dest == WireId()) {
|
||||
log_info(" failed to find a route using dedicated resources.\n");
|
||||
}
|
||||
while (backtrace.count(dest)) {
|
||||
auto uh = backtrace[dest];
|
||||
dest = ctx->getPipDstWire(uh);
|
||||
if (ctx->getBoundWireNet(dest) == clk_net) {
|
||||
NPNR_ASSERT(clk_net->wires.at(dest).pip == uh);
|
||||
break;
|
||||
}
|
||||
if (ctx->debug)
|
||||
log_info(" bind pip %s --> %s\n", ctx->nameOfPip(uh), ctx->nameOfWire(dest));
|
||||
ctx->bindPip(uh, clk_net, STRENGTH_LOCKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NEXTPNR_NAMESPACE_END
|
81
himbaechel/uarch/ng-ultra/pack.h
Normal file
81
himbaechel/uarch/ng-ultra/pack.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2023 gatecat <gatecat@ds0.me>
|
||||
* Copyright (C) 2023 Miodrag Milanovic <micko@yosyshq.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/optional.hpp>
|
||||
#include <iterator>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include "chain_utils.h"
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "ng_ultra.h"
|
||||
|
||||
#ifndef NG_ULTRA_PACK_H
|
||||
#define NG_ULTRA_PACK_H
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct NgUltraPacker
|
||||
{
|
||||
NgUltraPacker(Context *ctx, NgUltraImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
|
||||
|
||||
// Constants
|
||||
void pack_constants();
|
||||
void remove_constants();
|
||||
|
||||
// LUTs & FFs
|
||||
void update_lut_init();
|
||||
void update_dffs();
|
||||
void pack_lut_dffs();
|
||||
void pack_dffs();
|
||||
void pack_cys();
|
||||
|
||||
// IO
|
||||
void pack_iobs();
|
||||
void pack_ioms();
|
||||
|
||||
private:
|
||||
void set_lut_input_if_constant(CellInfo *cell, IdString input);
|
||||
void lut_to_fe(CellInfo *lut, CellInfo *fe, bool no_dff, Property lut_table);
|
||||
void dff_to_fe(CellInfo *dff, CellInfo *fe, bool pass_thru_lut);
|
||||
|
||||
void exchange_if_constant(CellInfo *cell, IdString input1, IdString input2);
|
||||
void pack_cy_input_and_output(CellInfo *cy, IdString cluster, IdString in_port, IdString out_port, int placer, int &lut_only, int &lut_and_ff, int &dff_only);
|
||||
|
||||
// General helper functions
|
||||
void flush_cells();
|
||||
|
||||
// Cell creating
|
||||
std::unique_ptr<CellInfo> create_cell(IdString type, IdString name);
|
||||
CellInfo *create_cell_ptr(IdString type, IdString name);
|
||||
|
||||
Context *ctx;
|
||||
NgUltraImpl *uarch;
|
||||
|
||||
pool<IdString> packed_cells;
|
||||
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||
|
||||
HimbaechelHelpers h;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user