ecp5: Split the SLICE bel into separate LUT/FF/RAMW bels

This commit is contained in:
gatecat 2022-03-31 11:17:57 +01:00
parent c4e47ba1a8
commit efb58711b0
13 changed files with 1144 additions and 1356 deletions

View File

@ -48,7 +48,7 @@ RUN set -e -x ;\
cd /usr/local/src ;\
git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\
cd prjtrellis ;\
git reset --hard 7239331d5463321d4864164f320beef67310f1e5 ;\
git reset --hard 26f917d6052e084df30211ae3a78c8a165121e09 ;\
cd libtrellis ;\
cmake . ;\
make -j $(nproc) ;\

View File

@ -103,7 +103,19 @@ Arch::Arch(ArchArgs args) : args(args)
if (!package_info)
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
bel_to_cell.resize(chip_info->height * chip_info->width * max_loc_bels, nullptr);
tile_status.resize(chip_info->num_tiles);
for (int i = 0; i < chip_info->num_tiles; i++) {
auto &ts = tile_status.at(i);
auto &tile_data = chip_info->tile_info[i];
ts.boundcells.resize(chip_info->locations[chip_info->location_type[i]].bel_data.size(), nullptr);
for (auto &name : tile_data.tile_names) {
if (strcmp(chip_info->tiletype_names[name.type_idx].get(), "PLC2") == 0) {
// Is a logic tile
ts.lts = new LogicTileStatus();
break;
}
}
}
BaseArch::init_cell_types();
BaseArch::init_bel_buckets();
@ -545,20 +557,24 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const
{
if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB)
if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB ||
(src_pin == id_F && dst_pin == id_DI))
return 0;
auto driver_loc = getBelLocation(src_bel);
auto sink_loc = getBelLocation(dst_bel);
// Encourage use of direct interconnect
// exact LUT input doesn't matter as they can be permuted by the router...
if (driver_loc.x == sink_loc.x && driver_loc.y == sink_loc.y) {
if ((dst_pin == id_A0 || dst_pin == id_A1) && (src_pin == id_F1) && (driver_loc.z == 2 || driver_loc.z == 3))
return 0;
if ((dst_pin == id_B0 || dst_pin == id_B1) && (src_pin == id_F1) && (driver_loc.z == 0 || driver_loc.z == 1))
return 0;
if ((dst_pin == id_C0 || dst_pin == id_C1) && (src_pin == id_F0) && (driver_loc.z == 2 || driver_loc.z == 3))
return 0;
if ((dst_pin == id_D0 || dst_pin == id_D1) && (src_pin == id_F0) && (driver_loc.z == 0 || driver_loc.z == 1))
return 0;
if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_Q) {
int lut = (sink_loc.z >> lc_idx_shift), ff = (driver_loc.z >> lc_idx_shift);
if (lut == ff)
return 0;
}
if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_F) {
int l0 = (driver_loc.z >> lc_idx_shift);
if (l0 != 1 && l0 != 6)
return 0;
}
}
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
@ -597,6 +613,14 @@ bool Arch::place()
cfg.cellGroups.back().insert(id_MULT18X18D);
cfg.cellGroups.back().insert(id_ALU54B);
cfg.cellGroups.emplace_back();
cfg.cellGroups.back().insert(id_TRELLIS_COMB);
cfg.cellGroups.back().insert(id_TRELLIS_FF);
cfg.cellGroups.back().insert(id_TRELLIS_RAMW);
cfg.placeAllAtOnce = true;
cfg.beta = 0.75;
if (!placer_heap(getCtx(), cfg))
return false;
} else if (placer == "sa") {
@ -605,7 +629,6 @@ bool Arch::place()
} else {
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
}
permute_luts();
// In out-of-context mode, create a locked macro
if (bool_or_default(settings, id("arch.ooc")))
@ -710,7 +733,7 @@ DecalXY Arch::getBelDecal(BelId bel) const
decalxy.decal.type = DecalId::TYPE_BEL;
decalxy.decal.location = bel.location;
decalxy.decal.z = bel.index;
decalxy.decal.active = (bel_to_cell.at(get_bel_flat_index(bel)) != nullptr);
decalxy.decal.active = getBoundBelCell(bel) != nullptr;
return decalxy;
}
@ -791,14 +814,19 @@ void Arch::get_setuphold_from_tmg_db(IdString tctype, IdString clock, IdString p
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
{
// Data for -8 grade
if (cell->type == id_TRELLIS_SLICE) {
bool has_carry = cell->sliceInfo.is_carry;
if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_M1 ||
fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) {
return get_delay_from_tmg_db(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
}
if (cell->type == id_TRELLIS_COMB) {
bool has_carry = cell->combInfo.flags & ArchCellInfo::COMB_CARRY;
IdString tmg_type = has_carry ? (((cell->constr_z >> Arch::lc_idx_shift) % 2) ? id_TRELLIS_COMB_CARRY1
: id_TRELLIS_COMB_CARRY0)
: id_TRELLIS_COMB;
if (fromPort == id_A || fromPort == id_B || fromPort == id_C || fromPort == id_D || fromPort == id_M ||
fromPort == id_F1 || fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI)
return get_delay_from_tmg_db(tmg_type, fromPort, toPort, delay);
else
return false;
} else if (cell->type == id_TRELLIS_FF) {
return false;
} else if (cell->type == id_TRELLIS_RAMW) {
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
@ -841,45 +869,46 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
{
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
clockInfoCount = 0;
if (cell->type == id_TRELLIS_SLICE) {
int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1;
if (port == id_CLK || port == id_WCK)
if (cell->type == id_TRELLIS_COMB) {
if (port == id_WCK)
return TMG_CLOCK_INPUT;
if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 ||
port == id_D0 || port == id_D1 || port == id_FCI || port == id_FXA || port == id_FXB)
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI || port == id_FXA ||
port == id_FXB || port == id_F1)
return TMG_COMB_INPUT;
if (port == id_F0 && disconnected(id_A0) && disconnected(id_B0) && disconnected(id_C0) && disconnected(id_D0) &&
if (port == id_F && disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) &&
disconnected(id_FCI))
return TMG_IGNORE; // LUT with no inputs is a constant
if (port == id_F1 && disconnected(id_A1) && disconnected(id_B1) && disconnected(id_C1) && disconnected(id_D1) &&
disconnected(id_FCI))
return TMG_IGNORE; // LUT with no inputs is a constant
if (port == id_F0 || port == id_F1 || port == id_FCO || port == id_OFX0 || port == id_OFX1)
if (port == id_F || port == id_FCO || port == id_OFX)
return TMG_COMB_OUTPUT;
if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) ||
(sd1 == 0 && port == id_M1)) {
if (port == id_M)
return TMG_COMB_INPUT;
if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 ||
port == id_WRE) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id_M0 || port == id_M1)
return TMG_COMB_INPUT;
if (port == id_Q0 || port == id_Q1) {
return TMG_IGNORE;
} else if (cell->type == id_TRELLIS_FF) {
bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED);
if (port == id_CLK)
return TMG_CLOCK_INPUT;
if (port == id_DI || (using_m && (port == id_M)) || port == id_CE || port == id_LSR) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id_Q) {
clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
}
return TMG_IGNORE;
} else if (cell->type == id_TRELLIS_RAMW) {
if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 ||
port == id_D0 || port == id_D1)
return TMG_COMB_INPUT;
if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3 || port == id_WADO0 ||
port == id_WADO1 || port == id_WADO2 || port == id_WADO3)
return TMG_COMB_OUTPUT;
if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
port == id_WAD3 || port == id_WRE) {
clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
NPNR_ASSERT_FALSE_STR("no timing type for slice port '" + port.str(this) + "'");
return TMG_IGNORE;
} else if (cell->type == id_TRELLIS_IO) {
if (port == id_T || port == id_I)
return TMG_ENDPOINT;
@ -1024,21 +1053,29 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
info.setup = DelayPair(0);
info.hold = DelayPair(0);
info.clockToQ = DelayQuad(0);
if (cell->type == id_TRELLIS_SLICE) {
int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1;
if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
port == id_WAD3 || port == id_WRE) {
info.edge = RISING_EDGE;
if (cell->type == id_TRELLIS_COMB) {
if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 ||
port == id_WRE) {
if (port == id_WD)
port = id_WD0;
info.edge = (cell->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV) ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_WCK;
get_setuphold_from_tmg_db(id_SDPRAME, id_WCK, port, info.setup, info.hold);
} else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) ||
(sd1 == 0 && port == id_M1)) {
info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE;
}
} else if (cell->type == id_TRELLIS_FF) {
bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED);
if (port == id_DI || port == id_CE || port == id_LSR || (using_m && port == id_M)) {
if (port == id_DI)
port = id_DI0;
if (port == id_M)
port = id_M0;
info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK;
get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold);
} else {
info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE;
NPNR_ASSERT(port == id_Q);
port = id_Q0;
info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE;
info.clock_port = id_CLK;
bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ);
NPNR_ASSERT(is_path);

View File

@ -452,7 +452,6 @@ struct Arch : BaseArch<ArchRanges>
mutable dict<IdStringList, PipId> pip_by_name;
std::vector<CellInfo *> bel_to_cell;
enum class LutPermRule
{
NONE,
@ -462,6 +461,39 @@ struct Arch : BaseArch<ArchRanges>
std::vector<LutPermRule> lutperm_allowed;
bool disable_router_lutperm = false;
// For fast, incremental validity checking of split SLICE
// BEL z-position lookup, x-ored with (index in tile) << 2
enum LogicBELType
{
BEL_COMB = 0,
BEL_FF = 1,
BEL_RAMW = 2
};
static const int lc_idx_shift = 2;
struct LogicTileStatus
{
// Per-SLICE valid and dirty bits
struct SliceStatus
{
bool valid = true, dirty = true;
} slices[4];
// Per-tile legality check for control set legality
bool tile_valid = true;
bool tile_dirty = true;
// Fast index from z-pos to cell
std::array<CellInfo *, 8 * (1 << lc_idx_shift)> cells;
};
struct TileStatus
{
std::vector<CellInfo *> boundcells;
LogicTileStatus *lts = nullptr;
// TODO: use similar mechanism for DSP legality checking
~TileStatus() { delete lts; }
};
// faster replacements for base_pip2net, base_wire2net
// indexed by get_pip_vecidx()
std::vector<NetInfo *> pip2net;
@ -492,7 +524,7 @@ struct Arch : BaseArch<ArchRanges>
// -------------------------------------------------
static const int max_loc_bels = 20;
static const int max_loc_bels = 32;
int getGridDimX() const override { return chip_info->width; };
int getGridDimY() const override { return chip_info->height; };
@ -509,6 +541,11 @@ struct Arch : BaseArch<ArchRanges>
return &(chip_info->locations[chip_info->location_type[id.location.y * chip_info->width + id.location.x]]);
}
template <typename Id> inline int tile_index(Id id) const
{
return id.location.y * chip_info->width + id.location.x;
}
IdStringList getBelName(BelId bel) const override
{
NPNR_ASSERT(bel != BelId());
@ -519,41 +556,57 @@ struct Arch : BaseArch<ArchRanges>
uint32_t getBelChecksum(BelId bel) const override { return bel.index; }
int get_bel_flat_index(BelId bel) const
{
return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index;
}
int get_slice_index(int x, int y, int slice) const
{
NPNR_ASSERT(slice >= 0 && slice < 4);
return (y * chip_info->width + x) * 4 + slice;
}
void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell)
{
CellInfo *act_cell = (old_cell == nullptr) ? new_cell : old_cell;
if (act_cell->type == id_TRELLIS_FF || act_cell->type == id_TRELLIS_COMB || act_cell->type == id_TRELLIS_RAMW) {
LogicTileStatus *lts = tile_status.at(tile_index(bel)).lts;
NPNR_ASSERT(lts != nullptr);
int z = loc_info(bel)->bel_data[bel.index].z;
lts->slices[(z >> lc_idx_shift) / 2].dirty = true;
if (act_cell->type == id_TRELLIS_FF)
lts->tile_dirty = true; // because FF CLK/LSR signals are tile-wide
if (act_cell->type == id_TRELLIS_COMB && (act_cell->combInfo.flags & ArchCellInfo::COMB_LUTRAM))
lts->tile_dirty = true; // because RAM shares CLK/LSR signals with FFs
lts->cells[z] = new_cell;
}
}
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
{
NPNR_ASSERT(bel != BelId());
int idx = get_bel_flat_index(bel);
NPNR_ASSERT(bel_to_cell.at(idx) == nullptr);
bel_to_cell[idx] = cell;
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
NPNR_ASSERT(slot == nullptr);
slot = cell;
cell->bel = bel;
cell->belStrength = strength;
if (getBelType(bel) == id_TRELLIS_SLICE) {
lutperm_allowed.at(get_slice_index(bel.location.x, bel.location.y, getBelLocation(bel).z)) =
(cell->sliceInfo.is_memory ? LutPermRule::NONE
: (cell->sliceInfo.is_carry ? LutPermRule::CARRY : LutPermRule::ALL));
if (getBelType(bel) == id_TRELLIS_COMB) {
int flags = cell->combInfo.flags;
lutperm_allowed.at(
get_slice_index(bel.location.x, bel.location.y, (getBelLocation(bel).z >> lc_idx_shift) / 2)) =
(((flags & ArchCellInfo::COMB_LUTRAM) || (flags & ArchCellInfo::COMB_RAMW_BLOCK))
? LutPermRule::NONE
: ((flags & ArchCellInfo::COMB_CARRY) ? LutPermRule::CARRY : LutPermRule::ALL));
}
update_bel(bel, nullptr, cell);
refreshUiBel(bel);
}
void unbindBel(BelId bel) override
{
NPNR_ASSERT(bel != BelId());
int idx = get_bel_flat_index(bel);
NPNR_ASSERT(bel_to_cell.at(idx) != nullptr);
bel_to_cell[idx]->bel = BelId();
bel_to_cell[idx]->belStrength = STRENGTH_NONE;
bel_to_cell[idx] = nullptr;
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
NPNR_ASSERT(slot != nullptr);
update_bel(bel, slot, nullptr);
slot->bel = BelId();
slot->belStrength = STRENGTH_NONE;
slot = nullptr;
refreshUiBel(bel);
}
@ -574,19 +627,22 @@ struct Arch : BaseArch<ArchRanges>
bool checkBelAvail(BelId bel) const override
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[get_bel_flat_index(bel)] == nullptr;
const CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
return slot == nullptr;
}
CellInfo *getBoundBelCell(BelId bel) const override
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[get_bel_flat_index(bel)];
CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
return slot;
}
CellInfo *getConflictingBelCell(BelId bel) const override
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[get_bel_flat_index(bel)];
CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
return slot;
}
BelRange getBels() const override
@ -957,12 +1013,11 @@ struct Arch : BaseArch<ArchRanges>
bool isBelLocationValid(BelId bel) const override;
// Helper function for above
bool slices_compatible(const std::vector<const CellInfo *> &cells) const;
bool slices_compatible(LogicTileStatus *lts) const;
void assign_arch_info_for_cell(CellInfo *ci);
void assignArchInfo() override;
void permute_luts();
std::vector<std::pair<std::string, std::string>> get_tiles_at_loc(int row, int col);
std::string get_tile_by_type_loc(int row, int col, std::string type) const
{
@ -1028,6 +1083,8 @@ struct Arch : BaseArch<ArchRanges>
std::vector<IdString> cell_types;
std::vector<BelBucketId> buckets;
mutable std::vector<TileStatus> tile_status;
};
NEXTPNR_NAMESPACE_END

View File

@ -33,53 +33,156 @@ inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name)
return found->second.net;
}
bool Arch::slices_compatible(const std::vector<const CellInfo *> &cells) const
bool Arch::slices_compatible(LogicTileStatus *lts) const
{
// TODO: allow different LSR/CLK and MUX/SRMODE settings once
// routing details are worked out
IdString clk_sig, lsr_sig;
IdString CLKMUX, LSRMUX, SRMODE;
bool first = true;
for (auto cell : cells) {
if (cell->sliceInfo.using_dff) {
if (first) {
clk_sig = cell->sliceInfo.clk_sig;
lsr_sig = cell->sliceInfo.lsr_sig;
CLKMUX = cell->sliceInfo.clkmux;
LSRMUX = cell->sliceInfo.lsrmux;
SRMODE = cell->sliceInfo.srmode;
} else {
if (cell->sliceInfo.clk_sig != clk_sig)
return false;
if (cell->sliceInfo.lsr_sig != lsr_sig)
return false;
if (cell->sliceInfo.clkmux != CLKMUX)
return false;
if (cell->sliceInfo.lsrmux != LSRMUX)
return false;
if (cell->sliceInfo.srmode != SRMODE)
return false;
}
first = false;
if (lts == nullptr)
return true;
for (int sl = 0; sl < 4; sl++) {
if (!lts->slices[sl].dirty) {
if (!lts->slices[sl].valid)
return false;
continue;
}
lts->slices[sl].dirty = false;
lts->slices[sl].valid = false;
bool found_ff = false;
uint8_t last_ff_flags = 0;
IdString last_ce_sig;
bool ramw_used = false;
if (sl == 2 && lts->cells[((sl * 2) << lc_idx_shift) | BEL_RAMW] != nullptr)
ramw_used = true;
for (int l = 0; l < 2; l++) {
bool comb_m_used = false;
CellInfo *comb = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_COMB];
if (comb != nullptr) {
uint8_t flags = comb->combInfo.flags;
if (ramw_used && !(flags & ArchCellInfo::COMB_RAMW_BLOCK))
return false;
if (flags & ArchCellInfo::COMB_MUX5) {
// MUX5 uses M signal and must be in LC 0
comb_m_used = true;
if (l != 0)
return false;
}
if (flags & ArchCellInfo::COMB_MUX6) {
// MUX6+ uses M signal and must be in LC 1
comb_m_used = true;
if (l != 1)
return false;
if (comb->combInfo.mux_fxad != nullptr &&
(comb->combInfo.mux_fxad->combInfo.flags & ArchCellInfo::COMB_MUX5)) {
// LUT6 structure must be rooted at SLICE 0 or 2
if (sl != 0 && sl != 2)
return false;
}
}
// LUTRAM must be in bottom two SLICEs only
if ((flags & ArchCellInfo::COMB_LUTRAM) && (sl > 1))
return false;
if (l == 1) {
// Carry usage must be the same for LCs 0 and 1 in a SLICE
CellInfo *comb0 = lts->cells[((sl * 2 + 0) << lc_idx_shift) | BEL_COMB];
if (comb0 &&
((comb0->combInfo.flags & ArchCellInfo::COMB_CARRY) != (flags & ArchCellInfo::COMB_CARRY)))
return false;
}
}
CellInfo *ff = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_FF];
if (ff != nullptr) {
uint8_t flags = ff->ffInfo.flags;
if (comb_m_used && (flags & ArchCellInfo::FF_M_USED))
return false;
if (found_ff) {
if ((flags & ArchCellInfo::FF_GSREN) != (last_ff_flags & ArchCellInfo::FF_GSREN))
return false;
if ((flags & ArchCellInfo::FF_CECONST) != (last_ff_flags & ArchCellInfo::FF_CECONST))
return false;
if ((flags & ArchCellInfo::FF_CEINV) != (last_ff_flags & ArchCellInfo::FF_CEINV))
return false;
if (ff->ffInfo.ce_sig != last_ce_sig)
return false;
} else {
found_ff = true;
last_ff_flags = flags;
last_ce_sig = ff->ffInfo.ce_sig;
}
}
}
lts->slices[sl].valid = true;
}
if (lts->tile_dirty) {
bool found_global_ff = false;
bool found_global_dpram = false;
bool global_lsrinv = false;
bool global_clkinv = false;
bool global_async = false;
IdString clk_sig, lsr_sig;
lts->tile_dirty = false;
lts->tile_valid = false;
#define CHECK_EQUAL(x, y) \
do { \
if ((x) != (y)) \
return false; \
} while (0)
for (int i = 0; i < 8; i++) {
if (i < 4) {
// DPRAM
CellInfo *comb = lts->cells[(i << lc_idx_shift) | BEL_COMB];
if (comb != nullptr && (comb->combInfo.flags & ArchCellInfo::COMB_LUTRAM)) {
if (found_global_dpram) {
CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV), global_clkinv);
CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV), global_lsrinv);
} else {
global_clkinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV);
global_lsrinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV);
found_global_dpram = true;
}
}
}
// FF
CellInfo *ff = lts->cells[(i << lc_idx_shift) | BEL_FF];
if (ff != nullptr) {
if (found_global_dpram) {
CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv);
CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv);
}
if (found_global_ff) {
CHECK_EQUAL(ff->ffInfo.clk_sig, clk_sig);
CHECK_EQUAL(ff->ffInfo.lsr_sig, lsr_sig);
CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv);
CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv);
CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC), global_async);
} else {
clk_sig = ff->ffInfo.clk_sig;
lsr_sig = ff->ffInfo.lsr_sig;
global_clkinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV);
global_lsrinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV);
global_async = bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC);
found_global_ff = true;
}
}
}
#undef CHECK_EQUAL
lts->tile_valid = true;
} else {
if (!lts->tile_valid)
return false;
}
return true;
}
bool Arch::isBelLocationValid(BelId bel) const
{
if (getBelType(bel) == id_TRELLIS_SLICE) {
std::vector<const CellInfo *> bel_cells;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *cell_other = getBoundBelCell(bel_other);
if (cell_other != nullptr) {
bel_cells.push_back(cell_other);
}
}
if (getBoundBelCell(bel) != nullptr && getBoundBelCell(bel)->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
return false;
return slices_compatible(bel_cells);
IdString bel_type = getBelType(bel);
if (bel_type.in(id_TRELLIS_COMB, id_TRELLIS_FF, id_TRELLIS_RAMW)) {
return slices_compatible(tile_status.at(tile_index(bel)).lts);
} else {
CellInfo *cell = getBoundBelCell(bel);
if (cell == nullptr) {
@ -93,70 +196,6 @@ bool Arch::isBelLocationValid(BelId bel) const
}
}
void Arch::permute_luts()
{
TimingAnalyser tmg(getCtx());
tmg.setup();
auto proc_lut = [&](CellInfo *ci, int lut) {
std::vector<IdString> port_names;
for (int i = 0; i < 4; i++)
port_names.push_back(id(std::string("ABCD").substr(i, 1) + std::to_string(lut)));
std::vector<std::pair<float, int>> inputs;
std::vector<NetInfo *> orig_nets;
for (int i = 0; i < 4; i++) {
if (!ci->ports.count(port_names.at(i))) {
ci->ports[port_names.at(i)].name = port_names.at(i);
ci->ports[port_names.at(i)].type = PORT_IN;
}
auto &port = ci->ports.at(port_names.at(i));
float crit = (port.net == nullptr) ? 0 : tmg.get_criticality(CellPortKey(ci->name, port_names.at(i)));
orig_nets.push_back(port.net);
inputs.emplace_back(crit, i);
}
// Least critical first (A input is slowest)
// Avoid permuting locked LUTs (e.g. from an OOC submodule)
if (ci->belStrength <= STRENGTH_STRONG)
std::sort(inputs.begin(), inputs.end());
for (int i = 0; i < 4; i++) {
IdString p = port_names.at(i);
// log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first);
ci->disconnectPort(p);
ci->ports.at(p).net = nullptr;
if (orig_nets.at(inputs.at(i).second) != nullptr) {
ci->connectPort(p, orig_nets.at(inputs.at(i).second));
ci->params[id(p.str(this) + "MUX")] = p.str(this);
} else {
ci->params[id(p.str(this) + "MUX")] = std::string("1");
}
}
// Rewrite function
int old_init = int_or_default(ci->params, id("LUT" + std::to_string(lut) + "_INITVAL"), 0);
int new_init = 0;
for (int i = 0; i < 16; i++) {
int old_index = 0;
for (int k = 0; k < 4; k++) {
if (i & (1 << k))
old_index |= (1 << inputs.at(k).second);
}
if (old_init & (1 << old_index))
new_init |= (1 << i);
}
ci->params[id("LUT" + std::to_string(lut) + "_INITVAL")] = Property(new_init, 16);
};
for (auto &cell : cells) {
CellInfo *ci = cell.second.get();
if (ci->type == id_TRELLIS_SLICE && str_or_default(ci->params, id_MODE, "LOGIC") == "LOGIC") {
proc_lut(ci, 0);
proc_lut(ci, 1);
}
}
}
void Arch::setup_wire_locations()
{
wire_loc_overrides.clear();

View File

@ -156,19 +156,46 @@ struct ArchNetInfo
typedef IdString ClusterId;
struct CellInfo;
struct NetInfo;
struct ArchCellInfo : BaseClusterInfo
{
enum CombFlags : uint8_t
{
COMB_NONE = 0x00,
COMB_CARRY = 0x01,
COMB_LUTRAM = 0x02,
COMB_MUX5 = 0x04,
COMB_MUX6 = 0x08,
COMB_RAM_WCKINV = 0x10,
COMB_RAM_WREINV = 0x20,
COMB_RAMW_BLOCK = 0x40,
};
enum FFFlags : uint8_t
{
FF_NONE = 0x00,
FF_CLKINV = 0x01,
FF_CEINV = 0x02,
FF_CECONST = 0x04,
FF_LSRINV = 0x08,
FF_GSREN = 0x10,
FF_ASYNC = 0x20,
FF_M_USED = 0x40,
};
struct
{
bool using_dff;
bool has_l6mux;
bool is_carry;
bool is_memory;
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
int sd0, sd1;
} sliceInfo;
uint8_t flags;
IdString ram_wck, ram_wre;
CellInfo *mux_fxad;
} combInfo;
struct
{
uint8_t flags;
IdString clk_sig, lsr_sig, ce_sig, di_sig;
} ffInfo;
struct
{
bool is_pdp;

View File

@ -524,10 +524,10 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
cc.tiles[tile].add_arc(sink, source);
}
static unsigned permute_lut(Context *ctx, CellInfo *cell, pool<IdString> &used_phys_pins, int k, unsigned orig_init)
static unsigned permute_lut(Context *ctx, CellInfo *cell, pool<IdString> &used_phys_pins, unsigned orig_init)
{
std::array<std::vector<unsigned>, 4> phys_to_log;
const std::array<IdString, 4> ports{k ? id_A1 : id_A0, k ? id_B1 : id_B0, k ? id_C1 : id_C0, k ? id_D1 : id_D0};
const std::array<IdString, 4> ports{id_A, id_B, id_C, id_D};
for (unsigned i = 0; i < 4; i++) {
WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]);
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
@ -547,7 +547,7 @@ static unsigned permute_lut(Context *ctx, CellInfo *cell, pool<IdString> &used_p
for (unsigned i = 0; i < 4; i++)
if (!phys_to_log.at(i).empty())
used_phys_pins.insert(ports.at(i));
if (cell->sliceInfo.is_carry) {
if (cell->combInfo.flags & ArchCellInfo::COMB_CARRY) {
// Insert dummy entries to ensure we keep the split between the two halves of a CCU2
for (unsigned i = 0; i < 4; i++) {
if (!phys_to_log.at(i).empty())
@ -840,77 +840,72 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx));
}
BelId bel = ci->bel;
if (ci->type == id_TRELLIS_SLICE) {
if (ci->type == id_TRELLIS_COMB) {
pool<IdString> used_phys_pins;
std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2");
std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get();
int lut0_init = int_or_default(ci->params, id_LUT0_INITVAL);
int lut1_init = int_or_default(ci->params, id_LUT1_INITVAL);
cc.tiles[tname].add_word(slice + ".K0.INIT",
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16));
cc.tiles[tname].add_word(slice + ".K1.INIT",
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16));
cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, id_MODE, "LOGIC"));
cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED"));
cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, id_REG0_SD, "0"));
cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, id_REG1_SD, "0"));
cc.tiles[tname].add_enum(slice + ".REG0.REGSET", str_or_default(ci->params, id_REG0_REGSET, "RESET"));
cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, id_REG1_REGSET, "RESET"));
cc.tiles[tname].add_enum(slice + ".REG0.LSRMODE", str_or_default(ci->params, id_REG0_LSRMODE, "LSR"));
cc.tiles[tname].add_enum(slice + ".REG1.LSRMODE", str_or_default(ci->params, id_REG1_LSRMODE, "LSR"));
cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1"));
if (ci->sliceInfo.using_dff) {
NetInfo *lsrnet = nullptr;
if (ci->ports.find(id_LSR) != ci->ports.end() && ci->ports.at(id_LSR).net != nullptr)
lsrnet = ci->ports.at(id_LSR).net;
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR0")) == lsrnet) {
cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
}
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR1")) == lsrnet) {
cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
}
NetInfo *clknet = nullptr;
if (ci->ports.find(id_CLK) != ci->ports.end() && ci->ports.at(id_CLK).net != nullptr)
clknet = ci->ports.at(id_CLK).net;
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK0")) == clknet) {
cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK"));
}
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK1")) == clknet) {
cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK"));
}
}
if (str_or_default(ci->params, id_MODE, "LOGIC") == "CCU2") {
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
str_or_default(ci->params, id_CCU2_INJECT1_0, "YES"));
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
str_or_default(ci->params, id_CCU2_INJECT1_1, "YES"));
int z = ctx->loc_info(bel)->bel_data[bel.index].z >> Arch::lc_idx_shift;
std::string slice = std::string("SLICE") + "ABCD"[z / 2];
std::string lc = std::to_string(z % 2);
std::string mode = str_or_default(ci->params, id_MODE, "LOGIC");
if (mode == "RAMW_BLOCK")
continue;
int lut_init = int_or_default(ci->params, id_INITVAL);
cc.tiles[tname].add_enum(slice + ".MODE", mode);
cc.tiles[tname].add_word(slice + ".K" + lc + ".INIT",
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, lut_init), 16));
if (mode == "CCU2") {
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_" + lc,
str_or_default(ci->params, id_CCU2_INJECT1, "YES"));
} else {
// Don't interfere with cascade mux wiring
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", "_NONE_");
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", "_NONE_");
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_" + lc, "_NONE_");
}
if (str_or_default(ci->params, id_MODE, "LOGIC") == "DPRAM" && slice == "SLICEA") {
if (mode == "DPRAM" && slice == "SLICEA" && lc == "0") {
cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, id_WREMUX, "WRE"));
std::string wckmux = str_or_default(ci->params, id_WCKMUX, "WCK");
wckmux = (wckmux == "WCK") ? "CLK" : wckmux;
cc.tiles[tname].add_enum("CLK1.CLKMUX", wckmux);
}
// Tie unused inputs high
for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) {
for (auto input : {id_A, id_B, id_C, id_D}) {
if (!used_phys_pins.count(input)) {
cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1");
cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + lc + "MUX", "1");
}
}
} else if (ci->type == id_TRELLIS_FF) {
std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2");
int z = ctx->loc_info(bel)->bel_data[bel.index].z >> Arch::lc_idx_shift;
std::string slice = std::string("SLICE") + "ABCD"[z / 2];
std::string lc = std::to_string(z % 2);
// TODO: CLKMUX
cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED"));
cc.tiles[tname].add_enum(slice + ".REG" + lc + ".SD", intstr_or_default(ci->params, id_SD, "0"));
cc.tiles[tname].add_enum(slice + ".REG" + lc + ".REGSET", str_or_default(ci->params, id_REGSET, "RESET"));
cc.tiles[tname].add_enum(slice + ".REG" + lc + ".LSRMODE", str_or_default(ci->params, id_LSRMODE, "LSR"));
cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1"));
NetInfo *lsrnet = ci->getPort(id_LSR);
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR0")) == lsrnet) {
cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
}
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR1")) == lsrnet) {
cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
}
NetInfo *clknet = ci->getPort(id_CLK);
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK0")) == clknet) {
cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK"));
}
if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK1")) == clknet) {
cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK"));
}
} else if (ci->type == id_TRELLIS_RAMW) {
std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2");
cc.tiles[tname].add_enum("SLICEC.MODE", "RAMW");
cc.tiles[tname].add_word("SLICEC.K0.INIT", std::vector<bool>(16, false));
cc.tiles[tname].add_word("SLICEC.K1.INIT", std::vector<bool>(16, false));
} else if (ci->type == id_TRELLIS_IO) {
std::string pio = ctx->loc_info(bel)->bel_data[bel.index].name.get();
std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33");

View File

@ -47,49 +47,28 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
}
};
if (type == id_TRELLIS_SLICE) {
if (type == id_TRELLIS_COMB) {
new_cell->params[id_MODE] = std::string("LOGIC");
new_cell->params[id_GSR] = std::string("DISABLED");
new_cell->params[id_SRMODE] = std::string("LSR_OVER_CE");
new_cell->params[id_CEMUX] = std::string("1");
new_cell->params[id_CLKMUX] = std::string("CLK");
new_cell->params[id_LSRMUX] = std::string("LSR");
new_cell->params[id_LUT0_INITVAL] = Property(0, 16);
new_cell->params[id_LUT1_INITVAL] = Property(0, 16);
new_cell->params[id_REG0_SD] = std::string("0");
new_cell->params[id_REG1_SD] = std::string("0");
new_cell->params[id_REG0_REGSET] = std::string("RESET");
new_cell->params[id_REG1_REGSET] = std::string("RESET");
new_cell->params[id_CCU2_INJECT1_0] = std::string("NO");
new_cell->params[id_CCU2_INJECT1_1] = std::string("NO");
new_cell->params[id_INITVAL] = Property(0, 16);
new_cell->params[id_CCU2_INJECT1] = std::string("NO");
new_cell->params[id_WREMUX] = std::string("WRE");
new_cell->addInput(id_A0);
new_cell->addInput(id_B0);
new_cell->addInput(id_C0);
new_cell->addInput(id_D0);
new_cell->addInput(id_A);
new_cell->addInput(id_B);
new_cell->addInput(id_C);
new_cell->addInput(id_D);
new_cell->addInput(id_A1);
new_cell->addInput(id_B1);
new_cell->addInput(id_C1);
new_cell->addInput(id_D1);
new_cell->addInput(id_M0);
new_cell->addInput(id_M1);
new_cell->addInput(id_M);
new_cell->addInput(id_F1);
new_cell->addInput(id_FCI);
new_cell->addInput(id_FXA);
new_cell->addInput(id_FXB);
new_cell->addInput(id_CLK);
new_cell->addInput(id_LSR);
new_cell->addInput(id_CE);
new_cell->addInput(id_DI0);
new_cell->addInput(id_DI1);
new_cell->addInput(id_WD0);
new_cell->addInput(id_WD1);
new_cell->addInput(id_WD);
new_cell->addInput(id_WAD0);
new_cell->addInput(id_WAD1);
new_cell->addInput(id_WAD2);
@ -97,23 +76,15 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
new_cell->addInput(id_WRE);
new_cell->addInput(id_WCK);
new_cell->addOutput(id_F0);
new_cell->addOutput(id_Q0);
new_cell->addOutput(id_F1);
new_cell->addOutput(id_Q1);
new_cell->addOutput(id_F);
new_cell->addOutput(id_FCO);
new_cell->addOutput(id_OFX0);
new_cell->addOutput(id_OFX1);
new_cell->addOutput(id_WDO0);
new_cell->addOutput(id_WDO1);
new_cell->addOutput(id_WDO2);
new_cell->addOutput(id_WDO3);
new_cell->addOutput(id_WADO0);
new_cell->addOutput(id_WADO1);
new_cell->addOutput(id_WADO2);
new_cell->addOutput(id_WADO3);
new_cell->addOutput(id_OFX);
} else if (type == id_TRELLIS_RAMW) {
for (auto i : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1})
new_cell->addInput(i);
for (auto o : {id_WDO0, id_WDO1, id_WDO2, id_WDO3, id_WADO0, id_WADO1, id_WADO2, id_WADO3})
new_cell->addOutput(o);
} else if (type == id_TRELLIS_IO) {
new_cell->params[id_DIR] = std::string("INPUT");
new_cell->attrs[id_IO_TYPE] = std::string("LVCMOS33");
@ -200,122 +171,6 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
return new_cell;
}
static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value)
{
NPNR_ASSERT(!has_ff || lc->params.at(name) == value);
lc->params[name] = value;
}
static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port)
{
if (has_ff) {
NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net);
NetInfo *ffnet = ff->ports.at(ff_port).net;
if (ffnet != nullptr)
ffnet->users.remove(ff->ports.at(ff_port).user_idx);
} else {
ff->movePortTo(ff_port, lc, lc_port);
}
}
void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut)
{
if (lc->hierpath == IdString())
lc->hierpath = ff->hierpath;
bool has_ff = lc->ports.at(id_Q0).net != nullptr || lc->ports.at(id_Q1).net != nullptr;
std::string reg = "REG" + std::to_string(index);
set_param_safe(has_ff, lc, id_SRMODE, str_or_default(ff->params, id_SRMODE, "LSR_OVER_CE"));
set_param_safe(has_ff, lc, id_GSR, str_or_default(ff->params, id_GSR, "DISABLED"));
set_param_safe(has_ff, lc, id_CEMUX, str_or_default(ff->params, id_CEMUX, "1"));
set_param_safe(has_ff, lc, id_LSRMUX, str_or_default(ff->params, id_LSRMUX, "LSR"));
set_param_safe(has_ff, lc, id_CLKMUX, str_or_default(ff->params, id_CLKMUX, "CLK"));
lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0");
lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, id_REGSET, "RESET");
lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, id_LSRMODE, "LSR");
replace_port_safe(has_ff, ff, id_CLK, lc, id_CLK);
if (ff->ports.find(id_LSR) != ff->ports.end())
replace_port_safe(has_ff, ff, id_LSR, lc, id_LSR);
if (ff->ports.find(id_CE) != ff->ports.end())
replace_port_safe(has_ff, ff, id_CE, lc, id_CE);
ff->movePortTo(id_Q, lc, ctx->id("Q" + std::to_string(index)));
if (ff->getPort(id_M) != nullptr) {
// PRLD FFs that use both M and DI
NPNR_ASSERT(!driven_by_lut);
// As M is used; must route DI through a new LUT
lc->params[ctx->id(reg + "_SD")] = std::string("1");
lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16);
ff->movePortTo(id_DI, lc, ctx->id("D" + std::to_string(index)));
ff->movePortTo(id_M, lc, ctx->id("M" + std::to_string(index)));
lc->connectPorts(ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index)));
} else {
if (driven_by_lut) {
ff->movePortTo(id_DI, lc, ctx->id("DI" + std::to_string(index)));
} else {
ff->movePortTo(id_DI, lc, ctx->id("M" + std::to_string(index)));
}
}
}
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
{
if (lc->hierpath == IdString())
lc->hierpath = lut->hierpath;
lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] =
get_or_default(lut->params, id_INIT, Property(0, 16));
lut->movePortTo(id_A, lc, ctx->id("A" + std::to_string(index)));
lut->movePortTo(id_B, lc, ctx->id("B" + std::to_string(index)));
lut->movePortTo(id_C, lc, ctx->id("C" + std::to_string(index)));
lut->movePortTo(id_D, lc, ctx->id("D" + std::to_string(index)));
lut->movePortTo(id_Z, lc, ctx->id("F" + std::to_string(index)));
}
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
{
if (lc->hierpath == IdString())
lc->hierpath = ccu->hierpath;
lc->params[id_MODE] = std::string("CCU2");
lc->params[id_LUT0_INITVAL] = get_or_default(ccu->params, id_INIT0, Property(0, 16));
lc->params[id_LUT1_INITVAL] = get_or_default(ccu->params, id_INIT1, Property(0, 16));
lc->params[id_CCU2_INJECT1_0] = str_or_default(ccu->params, id_INJECT1_0, "YES");
lc->params[id_CCU2_INJECT1_1] = str_or_default(ccu->params, id_INJECT1_1, "YES");
ccu->movePortTo(id_CIN, lc, id_FCI);
ccu->movePortTo(id_A0, lc, id_A0);
ccu->movePortTo(id_B0, lc, id_B0);
ccu->movePortTo(id_C0, lc, id_C0);
ccu->movePortTo(id_D0, lc, id_D0);
ccu->movePortTo(id_A1, lc, id_A1);
ccu->movePortTo(id_B1, lc, id_B1);
ccu->movePortTo(id_C1, lc, id_C1);
ccu->movePortTo(id_D1, lc, id_D1);
ccu->movePortTo(id_S0, lc, id_F0);
ccu->movePortTo(id_S1, lc, id_F1);
ccu->movePortTo(id_COUT, lc, id_FCO);
}
void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc)
{
if (lc->hierpath == IdString())
lc->hierpath = ram->hierpath;
lc->params[id_MODE] = std::string("RAMW");
ram->movePortTo(ctx->id("WAD[0]"), lc, id_D0);
ram->movePortTo(ctx->id("WAD[1]"), lc, id_B0);
ram->movePortTo(ctx->id("WAD[2]"), lc, id_C0);
ram->movePortTo(ctx->id("WAD[3]"), lc, id_A0);
ram->movePortTo(ctx->id("DI[0]"), lc, id_C1);
ram->movePortTo(ctx->id("DI[1]"), lc, id_A1);
ram->movePortTo(ctx->id("DI[2]"), lc, id_D1);
ram->movePortTo(ctx->id("DI[3]"), lc, id_B1);
}
static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
{
auto init_prop = get_or_default(ram->params, id_INITVAL, Property(0, 64));
@ -333,16 +188,70 @@ static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
return value;
}
void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index)
void lut_to_comb(Context *ctx, CellInfo *lut)
{
if (lc->hierpath == IdString())
lc->hierpath = ram->hierpath;
lc->params[id_MODE] = std::string("DPRAM");
lc->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE");
lc->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK");
lut->type = id_TRELLIS_COMB;
lut->params[id_INITVAL] = get_or_default(lut->params, id_INIT, Property(0, 16));
lut->params.erase(id_INIT);
lut->renamePort(id_Z, id_F);
}
unsigned permuted_init0 = 0, permuted_init1 = 0;
unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1);
void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
{
if (ramw->hierpath == IdString())
ramw->hierpath = ramw->hierpath;
ram->movePortTo(ctx->id("WAD[0]"), ramw, id_D0);
ram->movePortTo(ctx->id("WAD[1]"), ramw, id_B0);
ram->movePortTo(ctx->id("WAD[2]"), ramw, id_C0);
ram->movePortTo(ctx->id("WAD[3]"), ramw, id_A0);
ram->movePortTo(ctx->id("DI[0]"), ramw, id_C1);
ram->movePortTo(ctx->id("DI[1]"), ramw, id_A1);
ram->movePortTo(ctx->id("DI[2]"), ramw, id_D1);
ram->movePortTo(ctx->id("DI[3]"), ramw, id_B1);
}
void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i)
{
std::string ii = std::to_string(i);
if (comb->hierpath == IdString())
comb->hierpath = ccu->hierpath;
comb->params[id_MODE] = std::string("CCU2");
comb->params[id_INITVAL] = get_or_default(ccu->params, ctx->id("INIT" + ii), Property(0, 16));
comb->params[id_CCU2_INJECT1] = str_or_default(ccu->params, ctx->id("INJECT1_" + ii), "YES");
ccu->movePortTo(ctx->id("A" + ii), comb, id_A);
ccu->movePortTo(ctx->id("B" + ii), comb, id_B);
ccu->movePortTo(ctx->id("C" + ii), comb, id_C);
ccu->movePortTo(ctx->id("D" + ii), comb, id_D);
ccu->movePortTo(ctx->id("S" + ii), comb, id_F);
if (i == 0) {
ccu->movePortTo(id_CIN, comb, id_FCI);
comb->connectPort(id_FCO, internal_carry);
} else if (i == 1) {
comb->connectPort(id_FCI, internal_carry);
ccu->movePortTo(id_COUT, comb, id_FCO);
} else {
NPNR_ASSERT_FALSE("bad carry index!");
}
for (auto &attr : ccu->attrs)
comb->attrs[attr.first] = attr.second;
}
void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, int index)
{
if (comb->hierpath == IdString())
comb->hierpath = ram->hierpath;
comb->params[id_MODE] = std::string("DPRAM");
comb->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE");
comb->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK");
unsigned permuted_init = 0;
unsigned init = get_dram_init(ctx, ram, index);
for (int i = 0; i < 16; i++) {
int permuted_addr = 0;
@ -354,58 +263,41 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw
permuted_addr |= 4;
if (i & 8)
permuted_addr |= 1;
if (init0 & (1 << permuted_addr))
permuted_init0 |= (1 << i);
if (init1 & (1 << permuted_addr))
permuted_init1 |= (1 << i);
if (init & (1 << permuted_addr))
permuted_init |= (1 << i);
}
lc->params[id_LUT0_INITVAL] = Property(permuted_init0, 16);
lc->params[id_LUT1_INITVAL] = Property(permuted_init1, 16);
comb->params[ctx->id("INITVAL")] = Property(permuted_init, 16);
if (ram->ports.count(ctx->id("RAD[0]"))) {
lc->connectPort(id_D0, ram->ports.at(ctx->id("RAD[0]")).net);
lc->connectPort(id_D1, ram->ports.at(ctx->id("RAD[0]")).net);
}
if (ram->ports.count(ctx->id("RAD[1]"))) {
lc->connectPort(id_B0, ram->ports.at(ctx->id("RAD[1]")).net);
lc->connectPort(id_B1, ram->ports.at(ctx->id("RAD[1]")).net);
}
if (ram->ports.count(ctx->id("RAD[2]"))) {
lc->connectPort(id_C0, ram->ports.at(ctx->id("RAD[2]")).net);
lc->connectPort(id_C1, ram->ports.at(ctx->id("RAD[2]")).net);
}
if (ram->ports.count(ctx->id("RAD[3]"))) {
lc->connectPort(id_A0, ram->ports.at(ctx->id("RAD[3]")).net);
lc->connectPort(id_A1, ram->ports.at(ctx->id("RAD[3]")).net);
}
if (ram->ports.count(ctx->id("RAD[0]")))
comb->connectPort(id_D, ram->ports.at(ctx->id("RAD[0]")).net);
if (ram->ports.count(ctx->id("RAD[1]")))
comb->connectPort(id_B, ram->ports.at(ctx->id("RAD[1]")).net);
if (ram->ports.count(ctx->id("RAD[2]")))
comb->connectPort(id_C, ram->ports.at(ctx->id("RAD[2]")).net);
if (ram->ports.count(ctx->id("RAD[3]")))
comb->connectPort(id_A, ram->ports.at(ctx->id("RAD[3]")).net);
if (ram->ports.count(id_WRE))
lc->connectPort(id_WRE, ram->ports.at(id_WRE).net);
comb->connectPort(id_WRE, ram->ports.at(id_WRE).net);
if (ram->ports.count(id_WCK))
lc->connectPort(id_WCK, ram->ports.at(id_WCK).net);
comb->connectPort(id_WCK, ram->ports.at(id_WCK).net);
ramw->connectPorts(id_WADO0, lc, id_WAD0);
ramw->connectPorts(id_WADO1, lc, id_WAD1);
ramw->connectPorts(id_WADO2, lc, id_WAD2);
ramw->connectPorts(id_WADO3, lc, id_WAD3);
ramw->connectPorts(id_WADO0, comb, id_WAD0);
ramw->connectPorts(id_WADO1, comb, id_WAD1);
ramw->connectPorts(id_WADO2, comb, id_WAD2);
ramw->connectPorts(id_WADO3, comb, id_WAD3);
if (index == 0) {
ramw->connectPorts(id_WDO0, lc, id_WD0);
ramw->connectPorts(id_WDO1, lc, id_WD1);
NPNR_ASSERT(index < 4);
std::string ii = std::to_string(index);
ramw->connectPorts(ctx->id("WDO" + ii), comb, id_WD);
ram->movePortTo(ctx->id("DO[" + ii + "]"), comb, id_F);
ram->movePortTo(ctx->id("DO[0]"), lc, id_F0);
ram->movePortTo(ctx->id("DO[1]"), lc, id_F1);
} else if (index == 1) {
ramw->connectPorts(id_WDO2, lc, id_WD0);
ramw->connectPorts(id_WDO3, lc, id_WD1);
ram->movePortTo(ctx->id("DO[2]"), lc, id_F0);
ram->movePortTo(ctx->id("DO[3]"), lc, id_F1);
} else {
NPNR_ASSERT_FALSE("bad DPRAM index");
}
for (auto &attr : ram->attrs)
comb->attrs[attr.first] = attr.second;
}
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,

View File

@ -37,8 +37,6 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; }
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_SLICE; }
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; }
inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; }
@ -62,11 +60,10 @@ inline bool is_iologic_output_cell(const BaseCtx *ctx, const CellInfo *cell)
(str_or_default(cell->attrs, id_ioff_dir, "") != "input"));
}
void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut);
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index);
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc);
void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc);
void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index);
void lut_to_comb(Context *ctx, CellInfo *lut);
void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i);
void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, int index);
// Convert a nextpnr IO buffer to a TRELLIS_IO
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,

View File

@ -1851,3 +1851,13 @@ X(placer)
X(route)
X(router)
X(syn_useioff)
X(TRELLIS_COMB)
X(TRELLIS_RAMW)
X(TRELLIS_COMB_CARRY0)
X(TRELLIS_COMB_CARRY1)
X(WD)
X(OFX)
X(F)
X(CCU2_INJECT1)

View File

@ -23,11 +23,13 @@
NEXTPNR_NAMESPACE_BEGIN
const float slice_x1 = 0.92;
const float slice_x2_comb = 0.927;
const float slice_x1_ff = 0.933;
const float slice_x2 = 0.94;
const float slice_x2_wide = 0.97;
const float slice_y1 = 0.71;
const float slice_y2 = 0.745 + 0.0068;
const float slice_pitch = 0.0374 + 0.0068;
const float slice_y2 = 0.7275 + 0.0068 / 2;
const float slice_pitch = (0.0374 + 0.0068) / 2;
const float io_cell_v_x1 = 0.76;
const float io_cell_v_x2 = 0.95;
@ -54,11 +56,25 @@ void gfxTileBel(std::vector<GraphicElement> &g, int x, int y, int z, int w, int
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = style;
if (bel_type == id_TRELLIS_SLICE) {
if (bel_type == id_TRELLIS_COMB) {
el.x1 = x + slice_x1;
el.x2 = x + slice_x2_comb;
el.y1 = y + slice_y1 + (z >> Arch::lc_idx_shift) * slice_pitch;
el.y2 = y + slice_y2 + (z >> Arch::lc_idx_shift) * slice_pitch;
g.push_back(el);
el.style = GraphicElement::STYLE_FRAME;
el.x1 = x + slice_x2_comb + 15 * wire_distance;
el.x2 = el.x1 + wire_distance;
el.y1 = y + slice_y2 - wire_distance * (TILE_WIRE_CLK3_SLICE - TILE_WIRE_DUMMY_D2 + 5 + (3 - z) * 26) +
3 * slice_pitch - 0.0007f;
el.y2 = el.y1 + wire_distance * 5;
g.push_back(el);
} else if (bel_type == id_TRELLIS_FF) {
el.x1 = x + slice_x1_ff;
el.x2 = x + slice_x2;
el.y1 = y + slice_y1 + (z)*slice_pitch;
el.y2 = y + slice_y2 + (z)*slice_pitch;
el.y1 = y + slice_y1 + (z >> Arch::lc_idx_shift) * slice_pitch;
el.y2 = y + slice_y2 + (z >> Arch::lc_idx_shift) * slice_pitch;
g.push_back(el);
el.style = GraphicElement::STYLE_FRAME;

View File

@ -53,7 +53,9 @@ class Ecp5GlobalRouter
private:
bool is_clock_port(const PortRef &user)
{
if (user.cell->type == id_TRELLIS_SLICE && (user.port == id_CLK || user.port == id_WCK))
if (user.cell->type == id_TRELLIS_FF && user.port == id_CLK)
return true;
if (user.cell->type == id_TRELLIS_COMB && user.port == id_WCK)
return true;
if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK))
@ -65,7 +67,9 @@ class Ecp5GlobalRouter
bool is_logic_port(const PortRef &user)
{
if (user.cell->type == id_TRELLIS_SLICE && user.port != id_CLK && user.port != id_WCK)
if (user.cell->type == id_TRELLIS_FF && user.port != id_CLK)
return true;
if (user.cell->type == id_TRELLIS_COMB && user.port != id_WCK)
return true;
return false;
}

File diff suppressed because it is too large Load Diff

View File

@ -311,6 +311,64 @@ timing_port_xform = {
}
delay_db = {}
# Convert from Lattice-style grouped SLICE to new nextpnr split style SLICE
def postprocess_timing_data(cells):
def delay_diff(x, y):
return (x[0] - y[0], x[1] - y[1])
split_cells = {}
comb_delays = {}
comb_delays[("A", "F")] = delay_db["SLOGICB"][("A0", "F0")]
comb_delays[("B", "F")] = delay_db["SLOGICB"][("B0", "F0")]
comb_delays[("C", "F")] = delay_db["SLOGICB"][("C0", "F0")]
comb_delays[("D", "F")] = delay_db["SLOGICB"][("D0", "F0")]
comb_delays[("A", "OFX")] = delay_db["SLOGICB"][("A0", "OFX0")]
comb_delays[("B", "OFX")] = delay_db["SLOGICB"][("B0", "OFX0")]
comb_delays[("C", "OFX")] = delay_db["SLOGICB"][("C0", "OFX0")]
comb_delays[("D", "OFX")] = delay_db["SLOGICB"][("D0", "OFX0")]
comb_delays[("M", "OFX")] = delay_db["SLOGICB"][("M0", "OFX0")] # worst case
comb_delays[("F1", "OFX")] = delay_diff(delay_db["SLOGICB"][("A1", "OFX0")],
delay_db["SLOGICB"][("A1", "F1")])
comb_delays[("FXA", "OFX")] = delay_db["SLOGICB"][("FXA", "OFX1")]
comb_delays[("FXB", "OFX")] = delay_db["SLOGICB"][("FXB", "OFX1")]
split_cells["TRELLIS_COMB"] = comb_delays
carry0_delays = {}
carry0_delays[("A", "F")] = delay_db["SCCU2C"][("A0", "F0")]
carry0_delays[("B", "F")] = delay_db["SCCU2C"][("B0", "F0")]
carry0_delays[("C", "F")] = delay_db["SCCU2C"][("C0", "F0")]
carry0_delays[("D", "F")] = delay_db["SCCU2C"][("D0", "F0")]
carry0_delays[("A", "FCO")] = delay_db["SCCU2C"][("A0", "FCO")]
carry0_delays[("B", "FCO")] = delay_db["SCCU2C"][("B0", "FCO")]
carry0_delays[("C", "FCO")] = delay_db["SCCU2C"][("C0", "FCO")]
carry0_delays[("D", "FCO")] = delay_db["SCCU2C"][("D0", "FCO")]
carry0_delays[("FCI", "F")] = delay_db["SCCU2C"][("FCI", "F0")]
carry0_delays[("FCI", "FCO")] = delay_db["SCCU2C"][("FCI", "FCO")]
split_cells["TRELLIS_COMB_CARRY0"] = carry0_delays
carry1_delays = {}
carry1_delays[("A", "F")] = delay_db["SCCU2C"][("A1", "F1")]
carry1_delays[("B", "F")] = delay_db["SCCU2C"][("B1", "F1")]
carry1_delays[("C", "F")] = delay_db["SCCU2C"][("C1", "F1")]
carry1_delays[("D", "F")] = delay_db["SCCU2C"][("D1", "F1")]
carry1_delays[("A", "FCO")] = delay_db["SCCU2C"][("A1", "FCO")]
carry1_delays[("B", "FCO")] = delay_db["SCCU2C"][("B1", "FCO")]
carry1_delays[("C", "FCO")] = delay_db["SCCU2C"][("C1", "FCO")]
carry1_delays[("D", "FCO")] = delay_db["SCCU2C"][("D1", "FCO")]
carry1_delays[("FCI", "F")] = delay_diff(delay_db["SCCU2C"][("FCI", "F1")], delay_db["SCCU2C"][("FCI", "FCO")])
carry1_delays[("FCI", "FCO")] = (0, 0)
split_cells["TRELLIS_COMB_CARRY1"] = carry1_delays
for celltype, celldelays in sorted(split_cells.items()):
delays = []
setupholds = []
for (from_pin, to_pin), (min_delay, max_delay) in sorted(celldelays.items()):
delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
cells.append((constids[celltype], delays, setupholds))
def process_timing_data():
for grade in speed_grade_names:
with open(timing_dbs.cells_db_path("ECP5", grade)) as f:
@ -320,6 +378,7 @@ def process_timing_data():
celltype = constids[cell.replace(":", "_").replace("=", "_").replace(",", "_")]
delays = []
setupholds = []
delay_db[cell] = {}
for entry in cdata:
if entry["type"] == "Width":
continue
@ -332,6 +391,7 @@ def process_timing_data():
to_pin = timing_port_xform[to_pin]
min_delay = min(entry["rising"][0], entry["falling"][0])
max_delay = min(entry["rising"][2], entry["falling"][2])
delay_db[cell][(from_pin, to_pin)] = (min_delay, max_delay)
delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
elif entry["type"] == "SetupHold":
if type(entry["pin"]) is list:
@ -346,6 +406,7 @@ def process_timing_data():
else:
assert False, entry["type"]
cells.append((celltype, delays, setupholds))
postprocess_timing_data(cells)
pip_class_delays = []
for i in range(len(pip_class_to_idx)):
pip_class_delays.append((50, 50, 0, 0))
@ -625,7 +686,7 @@ def main():
# print("Initialising chip...")
chip = pytrellis.Chip(dev_names[args.device])
# print("Building routing graph...")
ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True)
ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True, split_slice_mode=True)
max_row = chip.get_max_row()
max_col = chip.get_max_col()
process_timing_data()