ecp5: Split the SLICE bel into separate LUT/FF/RAMW bels
This commit is contained in:
parent
c4e47ba1a8
commit
efb58711b0
@ -48,7 +48,7 @@ RUN set -e -x ;\
|
|||||||
cd /usr/local/src ;\
|
cd /usr/local/src ;\
|
||||||
git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\
|
git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\
|
||||||
cd prjtrellis ;\
|
cd prjtrellis ;\
|
||||||
git reset --hard 7239331d5463321d4864164f320beef67310f1e5 ;\
|
git reset --hard 26f917d6052e084df30211ae3a78c8a165121e09 ;\
|
||||||
cd libtrellis ;\
|
cd libtrellis ;\
|
||||||
cmake . ;\
|
cmake . ;\
|
||||||
make -j $(nproc) ;\
|
make -j $(nproc) ;\
|
||||||
|
143
ecp5/arch.cc
143
ecp5/arch.cc
@ -103,7 +103,19 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
if (!package_info)
|
if (!package_info)
|
||||||
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
|
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_cell_types();
|
||||||
BaseArch::init_bel_buckets();
|
BaseArch::init_bel_buckets();
|
||||||
@ -545,21 +557,25 @@ 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
|
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;
|
return 0;
|
||||||
auto driver_loc = getBelLocation(src_bel);
|
auto driver_loc = getBelLocation(src_bel);
|
||||||
auto sink_loc = getBelLocation(dst_bel);
|
auto sink_loc = getBelLocation(dst_bel);
|
||||||
// Encourage use of direct interconnect
|
// 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 (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))
|
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;
|
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.in(id_A, id_B, id_C, id_D) && src_pin == id_F) {
|
||||||
if ((dst_pin == id_C0 || dst_pin == id_C1) && (src_pin == id_F0) && (driver_loc.z == 2 || driver_loc.z == 3))
|
int l0 = (driver_loc.z >> lc_idx_shift);
|
||||||
return 0;
|
if (l0 != 1 && l0 != 6)
|
||||||
if ((dst_pin == id_D0 || dst_pin == id_D1) && (src_pin == id_F0) && (driver_loc.z == 0 || driver_loc.z == 1))
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
|
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_MULT18X18D);
|
||||||
cfg.cellGroups.back().insert(id_ALU54B);
|
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))
|
if (!placer_heap(getCtx(), cfg))
|
||||||
return false;
|
return false;
|
||||||
} else if (placer == "sa") {
|
} else if (placer == "sa") {
|
||||||
@ -605,7 +629,6 @@ bool Arch::place()
|
|||||||
} else {
|
} else {
|
||||||
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
|
log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str());
|
||||||
}
|
}
|
||||||
permute_luts();
|
|
||||||
|
|
||||||
// In out-of-context mode, create a locked macro
|
// In out-of-context mode, create a locked macro
|
||||||
if (bool_or_default(settings, id("arch.ooc")))
|
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.type = DecalId::TYPE_BEL;
|
||||||
decalxy.decal.location = bel.location;
|
decalxy.decal.location = bel.location;
|
||||||
decalxy.decal.z = bel.index;
|
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;
|
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
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
|
||||||
{
|
{
|
||||||
// Data for -8 grade
|
// Data for -8 grade
|
||||||
if (cell->type == id_TRELLIS_SLICE) {
|
if (cell->type == id_TRELLIS_COMB) {
|
||||||
bool has_carry = cell->sliceInfo.is_carry;
|
bool has_carry = cell->combInfo.flags & ArchCellInfo::COMB_CARRY;
|
||||||
if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
|
IdString tmg_type = has_carry ? (((cell->constr_z >> Arch::lc_idx_shift) % 2) ? id_TRELLIS_COMB_CARRY1
|
||||||
fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_M1 ||
|
: id_TRELLIS_COMB_CARRY0)
|
||||||
fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) {
|
: id_TRELLIS_COMB;
|
||||||
return get_delay_from_tmg_db(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
|
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) ||
|
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_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
|
||||||
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
|
(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; };
|
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
|
||||||
clockInfoCount = 0;
|
clockInfoCount = 0;
|
||||||
if (cell->type == id_TRELLIS_SLICE) {
|
if (cell->type == id_TRELLIS_COMB) {
|
||||||
int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1;
|
if (port == id_WCK)
|
||||||
if (port == id_CLK || port == id_WCK)
|
|
||||||
return TMG_CLOCK_INPUT;
|
return TMG_CLOCK_INPUT;
|
||||||
if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 ||
|
if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI || port == id_FXA ||
|
||||||
port == id_D0 || port == id_D1 || port == id_FCI || port == id_FXA || port == id_FXB)
|
port == id_FXB || port == id_F1)
|
||||||
return TMG_COMB_INPUT;
|
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))
|
disconnected(id_FCI))
|
||||||
return TMG_IGNORE; // LUT with no inputs is a constant
|
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) &&
|
if (port == id_F || port == id_FCO || port == id_OFX)
|
||||||
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)
|
|
||||||
return TMG_COMB_OUTPUT;
|
return TMG_COMB_OUTPUT;
|
||||||
if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) ||
|
if (port == id_M)
|
||||||
(sd1 == 0 && port == id_M1)) {
|
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;
|
clockInfoCount = 1;
|
||||||
return TMG_REGISTER_INPUT;
|
return TMG_REGISTER_INPUT;
|
||||||
}
|
}
|
||||||
if (port == id_M0 || port == id_M1)
|
return TMG_IGNORE;
|
||||||
return TMG_COMB_INPUT;
|
} else if (cell->type == id_TRELLIS_FF) {
|
||||||
if (port == id_Q0 || port == id_Q1) {
|
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;
|
clockInfoCount = 1;
|
||||||
return TMG_REGISTER_OUTPUT;
|
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 ||
|
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)
|
port == id_WADO1 || port == id_WADO2 || port == id_WADO3)
|
||||||
return TMG_COMB_OUTPUT;
|
return TMG_COMB_OUTPUT;
|
||||||
|
return TMG_IGNORE;
|
||||||
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) + "'");
|
|
||||||
} else if (cell->type == id_TRELLIS_IO) {
|
} else if (cell->type == id_TRELLIS_IO) {
|
||||||
if (port == id_T || port == id_I)
|
if (port == id_T || port == id_I)
|
||||||
return TMG_ENDPOINT;
|
return TMG_ENDPOINT;
|
||||||
@ -1024,21 +1053,29 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
info.setup = DelayPair(0);
|
info.setup = DelayPair(0);
|
||||||
info.hold = DelayPair(0);
|
info.hold = DelayPair(0);
|
||||||
info.clockToQ = DelayQuad(0);
|
info.clockToQ = DelayQuad(0);
|
||||||
if (cell->type == id_TRELLIS_SLICE) {
|
if (cell->type == id_TRELLIS_COMB) {
|
||||||
int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1;
|
if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 ||
|
||||||
if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
|
port == id_WRE) {
|
||||||
port == id_WAD3 || port == id_WRE) {
|
if (port == id_WD)
|
||||||
info.edge = RISING_EDGE;
|
port = id_WD0;
|
||||||
|
info.edge = (cell->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV) ? FALLING_EDGE : RISING_EDGE;
|
||||||
info.clock_port = id_WCK;
|
info.clock_port = id_WCK;
|
||||||
get_setuphold_from_tmg_db(id_SDPRAME, id_WCK, port, info.setup, info.hold);
|
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)) {
|
} else if (cell->type == id_TRELLIS_FF) {
|
||||||
info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE;
|
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;
|
info.clock_port = id_CLK;
|
||||||
get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold);
|
get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold);
|
||||||
|
|
||||||
} else {
|
} 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;
|
info.clock_port = id_CLK;
|
||||||
bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ);
|
bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ);
|
||||||
NPNR_ASSERT(is_path);
|
NPNR_ASSERT(is_path);
|
||||||
|
107
ecp5/arch.h
107
ecp5/arch.h
@ -452,7 +452,6 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
|
|
||||||
mutable dict<IdStringList, PipId> pip_by_name;
|
mutable dict<IdStringList, PipId> pip_by_name;
|
||||||
|
|
||||||
std::vector<CellInfo *> bel_to_cell;
|
|
||||||
enum class LutPermRule
|
enum class LutPermRule
|
||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
@ -462,6 +461,39 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
std::vector<LutPermRule> lutperm_allowed;
|
std::vector<LutPermRule> lutperm_allowed;
|
||||||
bool disable_router_lutperm = false;
|
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
|
// faster replacements for base_pip2net, base_wire2net
|
||||||
// indexed by get_pip_vecidx()
|
// indexed by get_pip_vecidx()
|
||||||
std::vector<NetInfo *> pip2net;
|
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 getGridDimX() const override { return chip_info->width; };
|
||||||
int getGridDimY() const override { return chip_info->height; };
|
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]]);
|
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
|
IdStringList getBelName(BelId bel) const override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
@ -519,41 +556,57 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
|
|
||||||
uint32_t getBelChecksum(BelId bel) const override { return bel.index; }
|
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
|
int get_slice_index(int x, int y, int slice) const
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(slice >= 0 && slice < 4);
|
NPNR_ASSERT(slice >= 0 && slice < 4);
|
||||||
return (y * chip_info->width + x) * 4 + slice;
|
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
|
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
int idx = get_bel_flat_index(bel);
|
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
||||||
NPNR_ASSERT(bel_to_cell.at(idx) == nullptr);
|
NPNR_ASSERT(slot == nullptr);
|
||||||
bel_to_cell[idx] = cell;
|
slot = cell;
|
||||||
cell->bel = bel;
|
cell->bel = bel;
|
||||||
cell->belStrength = strength;
|
cell->belStrength = strength;
|
||||||
if (getBelType(bel) == id_TRELLIS_SLICE) {
|
if (getBelType(bel) == id_TRELLIS_COMB) {
|
||||||
lutperm_allowed.at(get_slice_index(bel.location.x, bel.location.y, getBelLocation(bel).z)) =
|
int flags = cell->combInfo.flags;
|
||||||
(cell->sliceInfo.is_memory ? LutPermRule::NONE
|
lutperm_allowed.at(
|
||||||
: (cell->sliceInfo.is_carry ? LutPermRule::CARRY : LutPermRule::ALL));
|
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);
|
refreshUiBel(bel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unbindBel(BelId bel) override
|
void unbindBel(BelId bel) override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
int idx = get_bel_flat_index(bel);
|
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
||||||
NPNR_ASSERT(bel_to_cell.at(idx) != nullptr);
|
NPNR_ASSERT(slot != nullptr);
|
||||||
bel_to_cell[idx]->bel = BelId();
|
update_bel(bel, slot, nullptr);
|
||||||
bel_to_cell[idx]->belStrength = STRENGTH_NONE;
|
slot->bel = BelId();
|
||||||
bel_to_cell[idx] = nullptr;
|
slot->belStrength = STRENGTH_NONE;
|
||||||
|
slot = nullptr;
|
||||||
refreshUiBel(bel);
|
refreshUiBel(bel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,19 +627,22 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
bool checkBelAvail(BelId bel) const override
|
bool checkBelAvail(BelId bel) const override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
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
|
CellInfo *getBoundBelCell(BelId bel) const override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
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
|
CellInfo *getConflictingBelCell(BelId bel) const override
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(bel != BelId());
|
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
|
BelRange getBels() const override
|
||||||
@ -957,12 +1013,11 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
bool isBelLocationValid(BelId bel) const override;
|
bool isBelLocationValid(BelId bel) const override;
|
||||||
|
|
||||||
// Helper function for above
|
// 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 assignArchInfo() override;
|
||||||
|
|
||||||
void permute_luts();
|
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> get_tiles_at_loc(int row, int col);
|
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
|
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<IdString> cell_types;
|
||||||
std::vector<BelBucketId> buckets;
|
std::vector<BelBucketId> buckets;
|
||||||
|
|
||||||
|
mutable std::vector<TileStatus> tile_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -33,53 +33,156 @@ inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name)
|
|||||||
return found->second.net;
|
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
|
if (lts == nullptr)
|
||||||
// routing details are worked out
|
return true;
|
||||||
IdString clk_sig, lsr_sig;
|
for (int sl = 0; sl < 4; sl++) {
|
||||||
IdString CLKMUX, LSRMUX, SRMODE;
|
if (!lts->slices[sl].dirty) {
|
||||||
bool first = true;
|
if (!lts->slices[sl].valid)
|
||||||
for (auto cell : cells) {
|
return false;
|
||||||
if (cell->sliceInfo.using_dff) {
|
continue;
|
||||||
if (first) {
|
}
|
||||||
clk_sig = cell->sliceInfo.clk_sig;
|
lts->slices[sl].dirty = false;
|
||||||
lsr_sig = cell->sliceInfo.lsr_sig;
|
lts->slices[sl].valid = false;
|
||||||
CLKMUX = cell->sliceInfo.clkmux;
|
bool found_ff = false;
|
||||||
LSRMUX = cell->sliceInfo.lsrmux;
|
uint8_t last_ff_flags = 0;
|
||||||
SRMODE = cell->sliceInfo.srmode;
|
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 {
|
} else {
|
||||||
if (cell->sliceInfo.clk_sig != clk_sig)
|
found_ff = true;
|
||||||
return false;
|
last_ff_flags = flags;
|
||||||
if (cell->sliceInfo.lsr_sig != lsr_sig)
|
last_ce_sig = ff->ffInfo.ce_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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Arch::isBelLocationValid(BelId bel) const
|
bool Arch::isBelLocationValid(BelId bel) const
|
||||||
{
|
{
|
||||||
if (getBelType(bel) == id_TRELLIS_SLICE) {
|
IdString bel_type = getBelType(bel);
|
||||||
std::vector<const CellInfo *> bel_cells;
|
if (bel_type.in(id_TRELLIS_COMB, id_TRELLIS_FF, id_TRELLIS_RAMW)) {
|
||||||
Loc bel_loc = getBelLocation(bel);
|
return slices_compatible(tile_status.at(tile_index(bel)).lts);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
CellInfo *cell = getBoundBelCell(bel);
|
CellInfo *cell = getBoundBelCell(bel);
|
||||||
if (cell == nullptr) {
|
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()
|
void Arch::setup_wire_locations()
|
||||||
{
|
{
|
||||||
wire_loc_overrides.clear();
|
wire_loc_overrides.clear();
|
||||||
|
@ -156,19 +156,46 @@ struct ArchNetInfo
|
|||||||
|
|
||||||
typedef IdString ClusterId;
|
typedef IdString ClusterId;
|
||||||
|
|
||||||
|
struct CellInfo;
|
||||||
struct NetInfo;
|
struct NetInfo;
|
||||||
|
|
||||||
struct ArchCellInfo : BaseClusterInfo
|
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
|
struct
|
||||||
{
|
{
|
||||||
bool using_dff;
|
uint8_t flags;
|
||||||
bool has_l6mux;
|
IdString ram_wck, ram_wre;
|
||||||
bool is_carry;
|
CellInfo *mux_fxad;
|
||||||
bool is_memory;
|
} combInfo;
|
||||||
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
|
struct
|
||||||
int sd0, sd1;
|
{
|
||||||
} sliceInfo;
|
uint8_t flags;
|
||||||
|
IdString clk_sig, lsr_sig, ce_sig, di_sig;
|
||||||
|
} ffInfo;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
bool is_pdp;
|
bool is_pdp;
|
||||||
|
@ -524,10 +524,10 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
|
|||||||
cc.tiles[tile].add_arc(sink, source);
|
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;
|
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++) {
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]);
|
WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]);
|
||||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
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++)
|
for (unsigned i = 0; i < 4; i++)
|
||||||
if (!phys_to_log.at(i).empty())
|
if (!phys_to_log.at(i).empty())
|
||||||
used_phys_pins.insert(ports.at(i));
|
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
|
// Insert dummy entries to ensure we keep the split between the two halves of a CCU2
|
||||||
for (unsigned i = 0; i < 4; i++) {
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
if (!phys_to_log.at(i).empty())
|
if (!phys_to_log.at(i).empty())
|
||||||
@ -840,30 +840,51 @@ 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));
|
log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx));
|
||||||
}
|
}
|
||||||
BelId bel = ci->bel;
|
BelId bel = ci->bel;
|
||||||
if (ci->type == id_TRELLIS_SLICE) {
|
if (ci->type == id_TRELLIS_COMB) {
|
||||||
pool<IdString> used_phys_pins;
|
pool<IdString> used_phys_pins;
|
||||||
std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2");
|
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 z = ctx->loc_info(bel)->bel_data[bel.index].z >> Arch::lc_idx_shift;
|
||||||
int lut0_init = int_or_default(ci->params, id_LUT0_INITVAL);
|
std::string slice = std::string("SLICE") + "ABCD"[z / 2];
|
||||||
int lut1_init = int_or_default(ci->params, id_LUT1_INITVAL);
|
std::string lc = std::to_string(z % 2);
|
||||||
cc.tiles[tname].add_word(slice + ".K0.INIT",
|
std::string mode = str_or_default(ci->params, id_MODE, "LOGIC");
|
||||||
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16));
|
if (mode == "RAMW_BLOCK")
|
||||||
cc.tiles[tname].add_word(slice + ".K1.INIT",
|
continue;
|
||||||
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16));
|
int lut_init = int_or_default(ci->params, id_INITVAL);
|
||||||
cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, id_MODE, "LOGIC"));
|
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_" + lc, "_NONE_");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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) + 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);
|
||||||
|
|
||||||
cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED"));
|
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 + ".REG" + lc + ".SD", intstr_or_default(ci->params, id_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 + ".REG" + lc + ".REGSET", str_or_default(ci->params, id_REGSET, "RESET"));
|
||||||
cc.tiles[tname].add_enum(slice + ".REG0.REGSET", str_or_default(ci->params, id_REG0_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 + ".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"));
|
cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1"));
|
||||||
|
|
||||||
if (ci->sliceInfo.using_dff) {
|
NetInfo *lsrnet = ci->getPort(id_LSR);
|
||||||
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) {
|
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.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"));
|
cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
|
||||||
@ -873,44 +894,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
|
cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetInfo *clknet = nullptr;
|
NetInfo *clknet = ci->getPort(id_CLK);
|
||||||
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) {
|
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"));
|
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) {
|
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"));
|
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");
|
||||||
if (str_or_default(ci->params, id_MODE, "LOGIC") == "CCU2") {
|
cc.tiles[tname].add_enum("SLICEC.MODE", "RAMW");
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
|
cc.tiles[tname].add_word("SLICEC.K0.INIT", std::vector<bool>(16, false));
|
||||||
str_or_default(ci->params, id_CCU2_INJECT1_0, "YES"));
|
cc.tiles[tname].add_word("SLICEC.K1.INIT", std::vector<bool>(16, false));
|
||||||
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
|
|
||||||
str_or_default(ci->params, id_CCU2_INJECT1_1, "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_");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_or_default(ci->params, id_MODE, "LOGIC") == "DPRAM" && slice == "SLICEA") {
|
|
||||||
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}) {
|
|
||||||
if (!used_phys_pins.count(input)) {
|
|
||||||
cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: CLKMUX
|
|
||||||
} else if (ci->type == id_TRELLIS_IO) {
|
} else if (ci->type == id_TRELLIS_IO) {
|
||||||
std::string pio = ctx->loc_info(bel)->bel_data[bel.index].name.get();
|
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");
|
std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33");
|
||||||
|
318
ecp5/cells.cc
318
ecp5/cells.cc
@ -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_MODE] = std::string("LOGIC");
|
||||||
new_cell->params[id_GSR] = std::string("DISABLED");
|
new_cell->params[id_INITVAL] = Property(0, 16);
|
||||||
new_cell->params[id_SRMODE] = std::string("LSR_OVER_CE");
|
new_cell->params[id_CCU2_INJECT1] = std::string("NO");
|
||||||
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_WREMUX] = std::string("WRE");
|
new_cell->params[id_WREMUX] = std::string("WRE");
|
||||||
|
|
||||||
new_cell->addInput(id_A0);
|
new_cell->addInput(id_A);
|
||||||
new_cell->addInput(id_B0);
|
new_cell->addInput(id_B);
|
||||||
new_cell->addInput(id_C0);
|
new_cell->addInput(id_C);
|
||||||
new_cell->addInput(id_D0);
|
new_cell->addInput(id_D);
|
||||||
|
|
||||||
new_cell->addInput(id_A1);
|
new_cell->addInput(id_M);
|
||||||
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_F1);
|
||||||
new_cell->addInput(id_FCI);
|
new_cell->addInput(id_FCI);
|
||||||
new_cell->addInput(id_FXA);
|
new_cell->addInput(id_FXA);
|
||||||
new_cell->addInput(id_FXB);
|
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_DI0);
|
||||||
new_cell->addInput(id_DI1);
|
new_cell->addInput(id_DI1);
|
||||||
|
|
||||||
new_cell->addInput(id_WD0);
|
new_cell->addInput(id_WD);
|
||||||
new_cell->addInput(id_WD1);
|
|
||||||
new_cell->addInput(id_WAD0);
|
new_cell->addInput(id_WAD0);
|
||||||
new_cell->addInput(id_WAD1);
|
new_cell->addInput(id_WAD1);
|
||||||
new_cell->addInput(id_WAD2);
|
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_WRE);
|
||||||
new_cell->addInput(id_WCK);
|
new_cell->addInput(id_WCK);
|
||||||
|
|
||||||
new_cell->addOutput(id_F0);
|
new_cell->addOutput(id_F);
|
||||||
new_cell->addOutput(id_Q0);
|
|
||||||
new_cell->addOutput(id_F1);
|
|
||||||
new_cell->addOutput(id_Q1);
|
|
||||||
|
|
||||||
new_cell->addOutput(id_FCO);
|
new_cell->addOutput(id_FCO);
|
||||||
new_cell->addOutput(id_OFX0);
|
new_cell->addOutput(id_OFX);
|
||||||
new_cell->addOutput(id_OFX1);
|
} 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->addOutput(id_WDO0);
|
new_cell->addInput(i);
|
||||||
new_cell->addOutput(id_WDO1);
|
for (auto o : {id_WDO0, id_WDO1, id_WDO2, id_WDO3, id_WADO0, id_WADO1, id_WADO2, id_WADO3})
|
||||||
new_cell->addOutput(id_WDO2);
|
new_cell->addOutput(o);
|
||||||
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);
|
|
||||||
} else if (type == id_TRELLIS_IO) {
|
} else if (type == id_TRELLIS_IO) {
|
||||||
new_cell->params[id_DIR] = std::string("INPUT");
|
new_cell->params[id_DIR] = std::string("INPUT");
|
||||||
new_cell->attrs[id_IO_TYPE] = std::string("LVCMOS33");
|
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;
|
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)
|
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));
|
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;
|
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())
|
lut->type = id_TRELLIS_COMB;
|
||||||
lc->hierpath = ram->hierpath;
|
lut->params[id_INITVAL] = get_or_default(lut->params, id_INIT, Property(0, 16));
|
||||||
lc->params[id_MODE] = std::string("DPRAM");
|
lut->params.erase(id_INIT);
|
||||||
lc->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE");
|
lut->renamePort(id_Z, id_F);
|
||||||
lc->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK");
|
}
|
||||||
|
|
||||||
unsigned permuted_init0 = 0, permuted_init1 = 0;
|
void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw)
|
||||||
unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1);
|
{
|
||||||
|
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++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
int permuted_addr = 0;
|
int permuted_addr = 0;
|
||||||
@ -354,58 +263,41 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw
|
|||||||
permuted_addr |= 4;
|
permuted_addr |= 4;
|
||||||
if (i & 8)
|
if (i & 8)
|
||||||
permuted_addr |= 1;
|
permuted_addr |= 1;
|
||||||
if (init0 & (1 << permuted_addr))
|
if (init & (1 << permuted_addr))
|
||||||
permuted_init0 |= (1 << i);
|
permuted_init |= (1 << i);
|
||||||
if (init1 & (1 << permuted_addr))
|
|
||||||
permuted_init1 |= (1 << i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lc->params[id_LUT0_INITVAL] = Property(permuted_init0, 16);
|
comb->params[ctx->id("INITVAL")] = Property(permuted_init, 16);
|
||||||
lc->params[id_LUT1_INITVAL] = Property(permuted_init1, 16);
|
|
||||||
|
|
||||||
if (ram->ports.count(ctx->id("RAD[0]"))) {
|
if (ram->ports.count(ctx->id("RAD[0]")))
|
||||||
lc->connectPort(id_D0, ram->ports.at(ctx->id("RAD[0]")).net);
|
comb->connectPort(id_D, 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]")))
|
||||||
if (ram->ports.count(ctx->id("RAD[1]"))) {
|
comb->connectPort(id_B, ram->ports.at(ctx->id("RAD[1]")).net);
|
||||||
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]")))
|
||||||
}
|
comb->connectPort(id_C, ram->ports.at(ctx->id("RAD[2]")).net);
|
||||||
if (ram->ports.count(ctx->id("RAD[2]"))) {
|
|
||||||
lc->connectPort(id_C0, ram->ports.at(ctx->id("RAD[2]")).net);
|
if (ram->ports.count(ctx->id("RAD[3]")))
|
||||||
lc->connectPort(id_C1, ram->ports.at(ctx->id("RAD[2]")).net);
|
comb->connectPort(id_A, ram->ports.at(ctx->id("RAD[3]")).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(id_WRE))
|
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))
|
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_WADO0, comb, id_WAD0);
|
||||||
ramw->connectPorts(id_WADO1, lc, id_WAD1);
|
ramw->connectPorts(id_WADO1, comb, id_WAD1);
|
||||||
ramw->connectPorts(id_WADO2, lc, id_WAD2);
|
ramw->connectPorts(id_WADO2, comb, id_WAD2);
|
||||||
ramw->connectPorts(id_WADO3, lc, id_WAD3);
|
ramw->connectPorts(id_WADO3, comb, id_WAD3);
|
||||||
|
|
||||||
if (index == 0) {
|
NPNR_ASSERT(index < 4);
|
||||||
ramw->connectPorts(id_WDO0, lc, id_WD0);
|
std::string ii = std::to_string(index);
|
||||||
ramw->connectPorts(id_WDO1, lc, id_WD1);
|
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);
|
for (auto &attr : ram->attrs)
|
||||||
ram->movePortTo(ctx->id("DO[1]"), lc, id_F1);
|
comb->attrs[attr.first] = attr.second;
|
||||||
|
|
||||||
} 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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||||
|
11
ecp5/cells.h
11
ecp5/cells.h
@ -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_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_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; }
|
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"));
|
(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_comb(Context *ctx, CellInfo *lut);
|
||||||
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index);
|
void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
|
||||||
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc);
|
void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i);
|
||||||
void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc);
|
void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, int index);
|
||||||
void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index);
|
|
||||||
|
|
||||||
// Convert a nextpnr IO buffer to a TRELLIS_IO
|
// 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,
|
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
||||||
|
@ -1851,3 +1851,13 @@ X(placer)
|
|||||||
X(route)
|
X(route)
|
||||||
X(router)
|
X(router)
|
||||||
X(syn_useioff)
|
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)
|
||||||
|
26
ecp5/gfx.cc
26
ecp5/gfx.cc
@ -23,11 +23,13 @@
|
|||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
const float slice_x1 = 0.92;
|
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 = 0.94;
|
||||||
const float slice_x2_wide = 0.97;
|
const float slice_x2_wide = 0.97;
|
||||||
const float slice_y1 = 0.71;
|
const float slice_y1 = 0.71;
|
||||||
const float slice_y2 = 0.745 + 0.0068;
|
const float slice_y2 = 0.7275 + 0.0068 / 2;
|
||||||
const float slice_pitch = 0.0374 + 0.0068;
|
const float slice_pitch = (0.0374 + 0.0068) / 2;
|
||||||
|
|
||||||
const float io_cell_v_x1 = 0.76;
|
const float io_cell_v_x1 = 0.76;
|
||||||
const float io_cell_v_x2 = 0.95;
|
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;
|
GraphicElement el;
|
||||||
el.type = GraphicElement::TYPE_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
if (bel_type == id_TRELLIS_SLICE) {
|
if (bel_type == id_TRELLIS_COMB) {
|
||||||
el.x1 = x + slice_x1;
|
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.x2 = x + slice_x2;
|
||||||
el.y1 = y + slice_y1 + (z)*slice_pitch;
|
el.y1 = y + slice_y1 + (z >> Arch::lc_idx_shift) * slice_pitch;
|
||||||
el.y2 = y + slice_y2 + (z)*slice_pitch;
|
el.y2 = y + slice_y2 + (z >> Arch::lc_idx_shift) * slice_pitch;
|
||||||
g.push_back(el);
|
g.push_back(el);
|
||||||
|
|
||||||
el.style = GraphicElement::STYLE_FRAME;
|
el.style = GraphicElement::STYLE_FRAME;
|
||||||
|
@ -53,7 +53,9 @@ class Ecp5GlobalRouter
|
|||||||
private:
|
private:
|
||||||
bool is_clock_port(const PortRef &user)
|
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;
|
return true;
|
||||||
if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
|
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))
|
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)
|
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 true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
1259
ecp5/pack.cc
1259
ecp5/pack.cc
File diff suppressed because it is too large
Load Diff
@ -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():
|
def process_timing_data():
|
||||||
for grade in speed_grade_names:
|
for grade in speed_grade_names:
|
||||||
with open(timing_dbs.cells_db_path("ECP5", grade)) as f:
|
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(",", "_")]
|
celltype = constids[cell.replace(":", "_").replace("=", "_").replace(",", "_")]
|
||||||
delays = []
|
delays = []
|
||||||
setupholds = []
|
setupholds = []
|
||||||
|
delay_db[cell] = {}
|
||||||
for entry in cdata:
|
for entry in cdata:
|
||||||
if entry["type"] == "Width":
|
if entry["type"] == "Width":
|
||||||
continue
|
continue
|
||||||
@ -332,6 +391,7 @@ def process_timing_data():
|
|||||||
to_pin = timing_port_xform[to_pin]
|
to_pin = timing_port_xform[to_pin]
|
||||||
min_delay = min(entry["rising"][0], entry["falling"][0])
|
min_delay = min(entry["rising"][0], entry["falling"][0])
|
||||||
max_delay = min(entry["rising"][2], entry["falling"][2])
|
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))
|
delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
|
||||||
elif entry["type"] == "SetupHold":
|
elif entry["type"] == "SetupHold":
|
||||||
if type(entry["pin"]) is list:
|
if type(entry["pin"]) is list:
|
||||||
@ -346,6 +406,7 @@ def process_timing_data():
|
|||||||
else:
|
else:
|
||||||
assert False, entry["type"]
|
assert False, entry["type"]
|
||||||
cells.append((celltype, delays, setupholds))
|
cells.append((celltype, delays, setupholds))
|
||||||
|
postprocess_timing_data(cells)
|
||||||
pip_class_delays = []
|
pip_class_delays = []
|
||||||
for i in range(len(pip_class_to_idx)):
|
for i in range(len(pip_class_to_idx)):
|
||||||
pip_class_delays.append((50, 50, 0, 0))
|
pip_class_delays.append((50, 50, 0, 0))
|
||||||
@ -625,7 +686,7 @@ def main():
|
|||||||
# print("Initialising chip...")
|
# print("Initialising chip...")
|
||||||
chip = pytrellis.Chip(dev_names[args.device])
|
chip = pytrellis.Chip(dev_names[args.device])
|
||||||
# print("Building routing graph...")
|
# 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_row = chip.get_max_row()
|
||||||
max_col = chip.get_max_col()
|
max_col = chip.get_max_col()
|
||||||
process_timing_data()
|
process_timing_data()
|
||||||
|
Loading…
Reference in New Issue
Block a user