More of making it inline
This commit is contained in:
parent
ca3d32e5ac
commit
153144022f
@ -395,21 +395,33 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr
|
|||||||
bool Arch::place()
|
bool Arch::place()
|
||||||
{
|
{
|
||||||
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
||||||
if (placer == "sa") {
|
|
||||||
bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
if (placer == "heap") {
|
||||||
getCtx()->settings[id_place] = 1;
|
|
||||||
archInfoToAttributes();
|
|
||||||
return retVal;
|
|
||||||
} else if (placer == "heap") {
|
|
||||||
PlacerHeapCfg cfg(getCtx());
|
PlacerHeapCfg cfg(getCtx());
|
||||||
|
cfg.criticalityExponent = 4;
|
||||||
cfg.ioBufTypes.insert(id_TRELLIS_IO);
|
cfg.ioBufTypes.insert(id_TRELLIS_IO);
|
||||||
bool retVal = placer_heap(getCtx(), cfg);
|
|
||||||
getCtx()->settings[id_place] = 1;
|
cfg.cellGroups.emplace_back();
|
||||||
archInfoToAttributes();
|
cfg.cellGroups.back().insert(id_TRELLIS_COMB);
|
||||||
return retVal;
|
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") {
|
||||||
|
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
log_error("MachXO2 architecture does not support placer '%s'\n", placer.c_str());
|
log_error("MachXO2 architecture does not support placer '%s'\n", placer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCtx()->settings[id_place] = 1;
|
||||||
|
|
||||||
|
archInfoToAttributes();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Arch::route()
|
bool Arch::route()
|
||||||
@ -418,6 +430,8 @@ bool Arch::route()
|
|||||||
|
|
||||||
disable_router_lutperm = getCtx()->setting<bool>("arch.disable_router_lutperm", false);
|
disable_router_lutperm = getCtx()->setting<bool>("arch.disable_router_lutperm", false);
|
||||||
|
|
||||||
|
assignArchInfo();
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
if (router == "router1") {
|
if (router == "router1") {
|
||||||
result = router1(getCtx(), Router1Cfg(getCtx()));
|
result = router1(getCtx(), Router1Cfg(getCtx()));
|
||||||
|
190
machxo2/arch_place.cc
Normal file
190
machxo2/arch_place.cc
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cells.h"
|
||||||
|
#include "design_utils.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "timing.h"
|
||||||
|
#include "util.h"
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name)
|
||||||
|
{
|
||||||
|
auto found = cell->ports.find(name);
|
||||||
|
if (found == cell->ports.end())
|
||||||
|
return nullptr;
|
||||||
|
return found->second.net;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arch::slices_compatible(LogicTileStatus *lts) const
|
||||||
|
{
|
||||||
|
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, bool explain_invalid) const
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
@ -249,47 +249,6 @@ void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, i
|
|||||||
comb->attrs[attr.first] = attr.second;
|
comb->attrs[attr.first] = attr.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
|
||||||
{
|
|
||||||
lc->params[id_LUT0_INITVAL] = lut->params[id_INIT];
|
|
||||||
|
|
||||||
for (std::string i : {"A", "B", "C", "D"}) {
|
|
||||||
IdString lut_port = ctx->id(i);
|
|
||||||
IdString lc_port = ctx->id(i + "0");
|
|
||||||
lut->movePortTo(lut_port, lc, lc_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
lut->movePortTo(id_Z, lc, id_F0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type)
|
|
||||||
{
|
|
||||||
// FIXME: This will have to change once we support FFs with reset value of 1.
|
|
||||||
lc->params[id_REG0_REGSET] = std::string("RESET");
|
|
||||||
|
|
||||||
dff->movePortTo(id_CLK, lc, id_CLK);
|
|
||||||
dff->movePortTo(id_LSR, lc, id_LSR);
|
|
||||||
dff->movePortTo(id_Q, lc, id_Q0);
|
|
||||||
|
|
||||||
if (lut_type == LutType::PassThru) {
|
|
||||||
// If a register's DI port is fed by a constant, options for placing are
|
|
||||||
// limited. Use the LUT to get around this.
|
|
||||||
// LUT output will go to F0, which will feed back to DI0 input.
|
|
||||||
lc->params[id_LUT0_INITVAL] = Property(0xAAAA, 16);
|
|
||||||
dff->movePortTo(id_DI, lc, id_A0);
|
|
||||||
lc->connectPorts(id_F0, lc, id_DI0);
|
|
||||||
} else if (lut_type == LutType::None) {
|
|
||||||
// If there is no LUT, use the M0 input because DI0 requires
|
|
||||||
// going through the LUTs.
|
|
||||||
lc->params[id_REG0_SD] = std::string("0");
|
|
||||||
dff->movePortTo(id_DI, lc, id_M0);
|
|
||||||
} else {
|
|
||||||
// Otherwise, there's a LUT being used in the slice and mapping DI to
|
|
||||||
// DI0 input is fine.
|
|
||||||
dff->movePortTo(id_DI, lc, id_DI0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
||||||
pool<IdString> &todelete_cells)
|
pool<IdString> &todelete_cells)
|
||||||
{
|
{
|
||||||
|
@ -25,49 +25,36 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// When packing DFFs, we need context of how it's connected to a LUT to
|
|
||||||
// properly map DFF ports to TRELLIS_SLICEs; DI0 input muxes F0 and OFX0,
|
|
||||||
// and a DFF inside a slice can use either DI0 or M0 as an input.
|
|
||||||
enum class LutType
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Normal,
|
|
||||||
PassThru,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a MachXO2 arch cell and return it
|
// Create a MachXO2 arch cell and return it
|
||||||
// Name will be automatically assigned if not specified
|
// Name will be automatically assigned if not specified
|
||||||
std::unique_ptr<CellInfo> create_machxo2_cell(Context *ctx, IdString type, std::string name = "");
|
std::unique_ptr<CellInfo> create_machxo2_cell(Context *ctx, IdString type, std::string name = "");
|
||||||
|
|
||||||
// Return true if a cell is a LUT
|
// Return true if a cell is a LUT
|
||||||
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_LUT4; }
|
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_LUT4; }
|
||||||
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; }
|
|
||||||
inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; }
|
|
||||||
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; }
|
|
||||||
|
|
||||||
// Return true if a cell is a flipflop
|
// Return true if a cell is a flipflop
|
||||||
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_FF; }
|
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_FF; }
|
||||||
|
|
||||||
// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports
|
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; }
|
||||||
// as needed. Set no_dff if a DFF is not being used, so that the output
|
|
||||||
// can be reconnected
|
|
||||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
|
|
||||||
|
|
||||||
// Convert a DFF primitive to (part of) an GENERIC_SLICE, setting parameters
|
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; }
|
||||||
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
|
|
||||||
// be configured as pass through and D connected to I0, otherwise D will be
|
|
||||||
// ignored
|
|
||||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type = LutType::Normal);
|
|
||||||
|
|
||||||
// Convert a nextpnr IO buffer to a TRELLIS_IO
|
inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; }
|
||||||
void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells,
|
|
||||||
pool<IdString> &todelete_cells);
|
inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_PFUMX; }
|
||||||
|
|
||||||
|
inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_L6MUX21; }
|
||||||
|
|
||||||
void lut_to_comb(Context *ctx, CellInfo *lut);
|
void lut_to_comb(Context *ctx, CellInfo *lut);
|
||||||
void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw);
|
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 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);
|
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,
|
||||||
|
pool<IdString> &todelete_cells);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
349
machxo2/pack.cc
349
machxo2/pack.cc
@ -303,6 +303,192 @@ class Ecp5Packer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass to pack LUT5s into a newly created slice
|
||||||
|
void pack_lut5xs()
|
||||||
|
{
|
||||||
|
log_info("Packing LUT5-7s...\n");
|
||||||
|
|
||||||
|
// Gets the "COMB1" side of a LUT5, where we pack a LUT[67] into
|
||||||
|
auto get_comb1_from_lut5 = [&](CellInfo *lut5) {
|
||||||
|
NetInfo *f1 = lut5->getPort(id_F1);
|
||||||
|
NPNR_ASSERT(f1 != nullptr);
|
||||||
|
NPNR_ASSERT(f1->driver.cell != nullptr);
|
||||||
|
return f1->driver.cell;
|
||||||
|
};
|
||||||
|
|
||||||
|
dict<IdString, std::pair<CellInfo *, CellInfo *>> lut5_roots, lut6_roots, lut7_roots;
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (is_pfumx(ctx, ci)) {
|
||||||
|
NetInfo *f0 = ci->ports.at(id_BLUT).net;
|
||||||
|
|
||||||
|
if (f0 == nullptr)
|
||||||
|
log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx));
|
||||||
|
NetInfo *f1 = ci->ports.at(id_ALUT).net;
|
||||||
|
if (f1 == nullptr)
|
||||||
|
log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx));
|
||||||
|
|
||||||
|
CellInfo *lut0 =
|
||||||
|
(f0->driver.cell && f0->driver.cell->type == id_TRELLIS_COMB && f0->driver.port == id_F)
|
||||||
|
? f0->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
CellInfo *lut1 =
|
||||||
|
(f1->driver.cell && f1->driver.cell->type == id_TRELLIS_COMB && f1->driver.port == id_F)
|
||||||
|
? f1->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
if (lut0 == nullptr || lut0->cluster != ClusterId())
|
||||||
|
log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
||||||
|
if (lut1 == nullptr || lut1->cluster != ClusterId())
|
||||||
|
log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
||||||
|
lut0->addInput(id_F1);
|
||||||
|
lut0->addInput(id_M);
|
||||||
|
lut0->addOutput(id_OFX);
|
||||||
|
|
||||||
|
ci->movePortTo(id_Z, lut0, id_OFX);
|
||||||
|
ci->movePortTo(id_ALUT, lut0, id_F1);
|
||||||
|
ci->movePortTo(id_C0, lut0, id_M);
|
||||||
|
ci->disconnectPort(id_BLUT);
|
||||||
|
|
||||||
|
lut5_roots[lut0->name] = {lut0, lut1};
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
// Pack LUT6s
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (is_l6mux(ctx, ci)) {
|
||||||
|
NetInfo *ofx0_0 = ci->ports.at(id_D0).net;
|
||||||
|
if (ofx0_0 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
|
||||||
|
NetInfo *ofx0_1 = ci->ports.at(id_D1).net;
|
||||||
|
if (ofx0_1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
|
||||||
|
CellInfo *comb0 = (ofx0_0->driver.cell && ofx0_0->driver.cell->type == id_TRELLIS_COMB &&
|
||||||
|
ofx0_0->driver.port == id_OFX)
|
||||||
|
? ofx0_0->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
CellInfo *comb1 = (ofx0_1->driver.cell && ofx0_1->driver.cell->type == id_TRELLIS_COMB &&
|
||||||
|
ofx0_1->driver.port == id_OFX)
|
||||||
|
? ofx0_1->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
if (comb0 == nullptr) {
|
||||||
|
if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z))
|
||||||
|
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
|
||||||
|
"('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx0_0->driver.port.c_str(ctx));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (lut6_roots.count(comb0->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (comb1 == nullptr) {
|
||||||
|
if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z))
|
||||||
|
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
|
||||||
|
"('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx0_0->driver.port.c_str(ctx));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (lut6_roots.count(comb1->name))
|
||||||
|
continue;
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx));
|
||||||
|
comb0 = get_comb1_from_lut5(comb0);
|
||||||
|
comb1 = get_comb1_from_lut5(comb1);
|
||||||
|
|
||||||
|
comb1->addInput(id_FXA);
|
||||||
|
comb1->addInput(id_FXB);
|
||||||
|
comb1->addInput(id_M);
|
||||||
|
comb1->addOutput(id_OFX);
|
||||||
|
ci->movePortTo(id_D0, comb1, id_FXA);
|
||||||
|
ci->movePortTo(id_D1, comb1, id_FXB);
|
||||||
|
ci->movePortTo(id_SD, comb1, id_M);
|
||||||
|
ci->movePortTo(id_Z, comb1, id_OFX);
|
||||||
|
lut6_roots[comb1->name] = {comb0, comb1};
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
// Pack LUT7s
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (is_l6mux(ctx, ci)) {
|
||||||
|
NetInfo *ofx1_0 = ci->ports.at(id_D0).net;
|
||||||
|
if (ofx1_0 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
|
||||||
|
NetInfo *ofx1_1 = ci->ports.at(id_D1).net;
|
||||||
|
if (ofx1_1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
|
||||||
|
CellInfo *comb1 = (ofx1_0->driver.cell && ofx1_0->driver.cell->type == id_TRELLIS_COMB &&
|
||||||
|
ofx1_0->driver.port == id_OFX)
|
||||||
|
? ofx1_0->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
CellInfo *comb3 = (ofx1_1->driver.cell && ofx1_1->driver.cell->type == id_TRELLIS_COMB &&
|
||||||
|
ofx1_1->driver.port == id_OFX)
|
||||||
|
? ofx1_1->driver.cell
|
||||||
|
: nullptr;
|
||||||
|
if (comb1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx1_0->driver.port.c_str(ctx));
|
||||||
|
if (comb3 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx),
|
||||||
|
ofx1_1->driver.port.c_str(ctx));
|
||||||
|
|
||||||
|
NetInfo *fxa_0 = comb1->ports.at(id_FXA).net;
|
||||||
|
if (fxa_0 == nullptr)
|
||||||
|
log_error("SLICE '%s' has disconnected port 'FXA'\n", comb1->name.c_str(ctx));
|
||||||
|
NetInfo *fxa_1 = comb3->ports.at(id_FXA).net;
|
||||||
|
if (fxa_1 == nullptr)
|
||||||
|
log_error("SLICE '%s' has disconnected port 'FXA'\n", comb3->name.c_str(ctx));
|
||||||
|
|
||||||
|
CellInfo *comb2 = net_driven_by(
|
||||||
|
ctx, fxa_1,
|
||||||
|
[](const Context *ctx, const CellInfo *ci) {
|
||||||
|
(void)ctx;
|
||||||
|
return ci->type == id_TRELLIS_COMB;
|
||||||
|
},
|
||||||
|
id_OFX);
|
||||||
|
if (comb2 == nullptr)
|
||||||
|
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
|
||||||
|
comb3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx),
|
||||||
|
fxa_1->driver.port.c_str(ctx));
|
||||||
|
comb2 = get_comb1_from_lut5(comb2);
|
||||||
|
comb2->addInput(id_FXA);
|
||||||
|
comb2->addInput(id_FXB);
|
||||||
|
comb2->addInput(id_M);
|
||||||
|
comb2->addOutput(id_OFX);
|
||||||
|
ci->movePortTo(id_D0, comb2, id_FXA);
|
||||||
|
ci->movePortTo(id_D1, comb2, id_FXB);
|
||||||
|
ci->movePortTo(id_SD, comb2, id_M);
|
||||||
|
ci->movePortTo(id_Z, comb2, id_OFX);
|
||||||
|
|
||||||
|
lut7_roots[comb2->name] = {comb1, comb3};
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &root : lut7_roots) {
|
||||||
|
auto &cells = root.second;
|
||||||
|
cells.second->cluster = cells.second->name;
|
||||||
|
cells.second->constr_abs_z = true;
|
||||||
|
cells.second->constr_z = (1 << Arch::lc_idx_shift) | Arch::BEL_COMB;
|
||||||
|
rel_constr_cells(cells.second, cells.first, (4 << Arch::lc_idx_shift));
|
||||||
|
}
|
||||||
|
for (auto &root : lut6_roots) {
|
||||||
|
auto &cells = root.second;
|
||||||
|
rel_constr_cells(cells.second, cells.first, (2 << Arch::lc_idx_shift));
|
||||||
|
}
|
||||||
|
for (auto &root : lut5_roots) {
|
||||||
|
auto &cells = root.second;
|
||||||
|
rel_constr_cells(cells.first, cells.second, (1 << Arch::lc_idx_shift));
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
}
|
||||||
|
|
||||||
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
|
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
|
||||||
void pack_io()
|
void pack_io()
|
||||||
{
|
{
|
||||||
@ -914,6 +1100,7 @@ class Ecp5Packer
|
|||||||
pack_dram();
|
pack_dram();
|
||||||
pack_carries();
|
pack_carries();
|
||||||
pack_luts();
|
pack_luts();
|
||||||
|
pack_lut5xs();
|
||||||
pack_ffs();
|
pack_ffs();
|
||||||
ctx->fixupHierarchy();
|
ctx->fixupHierarchy();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
@ -1021,166 +1208,4 @@ void Arch::assignArchInfo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name)
|
|
||||||
{
|
|
||||||
auto found = cell->ports.find(name);
|
|
||||||
if (found == cell->ports.end())
|
|
||||||
return nullptr;
|
|
||||||
return found->second.net;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Arch::slices_compatible(LogicTileStatus *lts) const
|
|
||||||
{
|
|
||||||
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, bool explain_invalid) const
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
Loading…
Reference in New Issue
Block a user