aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5
diff options
context:
space:
mode:
Diffstat (limited to 'ecp5')
-rw-r--r--ecp5/arch.cc12
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/arch_place.cc24
-rw-r--r--ecp5/archdefs.h2
-rw-r--r--ecp5/bitstream.cc10
-rw-r--r--ecp5/constids.inc8
-rw-r--r--ecp5/family.cmake4
-rw-r--r--ecp5/pack.cc57
-rwxr-xr-xecp5/trellis_import.py8
9 files changed, 114 insertions, 17 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 8ce0653c..d931c5b2 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -117,6 +117,9 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported ECP5 chip type.\n");
}
#endif
+ if (chip_info->const_id_count != DB_CONST_ID_COUNT)
+ log_error("Chip database 'bba' and nextpnr code are out of sync; please rebuild (or contact distribution "
+ "maintainer)!\n");
package_info = nullptr;
for (int i = 0; i < chip_info->num_packages; i++) {
if (args.package == chip_info->package_info[i].name.get()) {
@@ -477,7 +480,13 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
}
};
- auto src_loc = est_location(src), dst_loc = est_location(dst);
+ auto src_loc = est_location(src);
+ std::pair<int, int> dst_loc;
+ if (wire_loc_overrides.count(dst)) {
+ dst_loc = wire_loc_overrides.at(dst);
+ } else {
+ dst_loc = est_location(dst);
+ }
int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
@@ -562,6 +571,7 @@ bool Arch::place()
bool Arch::route()
{
+ setupWireLocations();
route_ecp5_globals(getCtx());
assignArchInfo();
assign_budget(getCtx(), true);
diff --git a/ecp5/arch.h b/ecp5/arch.h
index a479abb6..3df2d84f 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -194,6 +194,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t num_tiles;
int32_t num_location_types;
int32_t num_packages, num_pios;
+ int32_t const_id_count;
RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type;
RelPtr<GlobalInfoPOD> location_glbinfo;
@@ -1048,6 +1049,11 @@ struct Arch : BaseCtx
// Special case for delay estimates due to its physical location
// being far from the logical location of its primitive
WireId gsrclk_wire;
+ // Improves directivity of routing to DSP inputs, avoids issues
+ // with different routes to the same physical reset wire causing
+ // conflicts and slow routing
+ std::unordered_map<WireId, std::pair<int, int>> wire_loc_overrides;
+ void setupWireLocations();
mutable std::unordered_map<DelayKey, std::pair<bool, DelayInfo>> celldelay_cache;
diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc
index d5c345af..6057605b 100644
--- a/ecp5/arch_place.cc
+++ b/ecp5/arch_place.cc
@@ -196,4 +196,28 @@ void Arch::permute_luts()
}
}
+void Arch::setupWireLocations()
+{
+ wire_loc_overrides.clear();
+ for (auto cell : sorted(cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->bel == BelId())
+ continue;
+ if (ci->type == id_MULT18X18D || ci->type == id_DCUA) {
+ for (auto &port : ci->ports) {
+ if (port.second.type != PORT_IN || port.second.net == nullptr)
+ continue;
+ WireId pw = getBelPinWire(ci->bel, port.first);
+ if (pw == WireId())
+ continue;
+ for (auto uh : getPipsUphill(pw)) {
+ WireId pip_src = getPipSrcWire(uh);
+ wire_loc_overrides[pw] = std::make_pair(pip_src.location.x, pip_src.location.y);
+ break;
+ }
+ }
+ }
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index da12eeaa..3cd92119 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -58,6 +58,8 @@ enum ConstIds
#define X(t) , ID_##t
#include "constids.inc"
#undef X
+ ,
+ DB_CONST_ID_COUNT
};
#define X(t) static constexpr auto id_##t = IdString(ID_##t);
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index acab95dd..9732cd52 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -865,6 +865,16 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
if (ci->attrs.count(ctx->id("DIFFRESISTOR")))
cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR",
str_or_default(ci->attrs, ctx->id("DIFFRESISTOR"), "OFF"));
+ if (ci->attrs.count(ctx->id("DRIVE"))) {
+ static bool drive_3v3_warning_done = false;
+ if (iotype == "LVCMOS33") {
+ cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "8"));
+ } else {
+ if (!drive_3v3_warning_done)
+ log_warning("Trellis limitation: DRIVE can only be set on 3V3 IO pins.\n");
+ drive_3v3_warning_done = true;
+ }
+ }
if (ci->attrs.count(ctx->id("TERMINATION"))) {
auto vccio = get_vccio(ioType_from_str(iotype));
switch (vccio) {
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index 5e8fc7da..4b5e3a3a 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -1294,3 +1294,11 @@ X(P)
X(ECLKBRIDGECS)
X(SEL)
X(ECSOUT)
+
+X(IOLOGIC_MODE_IDDRX1F)
+X(IOLOGIC_MODE_IDDRX2F)
+X(IOLOGIC_MODE_IREG)
+X(IOLOGIC_MODE_ODDRX1F)
+X(IOLOGIC_MODE_ODDRX2F)
+X(IOLOGIC_MODE_OREG)
+X(IOLOGIC_MODE_TSREG)
diff --git a/ecp5/family.cmake b/ecp5/family.cmake
index 5512e7dd..2d8dcfcc 100644
--- a/ecp5/family.cmake
+++ b/ecp5/family.cmake
@@ -48,7 +48,7 @@ if (NOT EXTERNAL_CHIPDB)
else()
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} ${dev} > ${DEV_CC_BBA_DB}
- DEPENDS ${DB_PY} ${PREV_DEV_CC_BBA_DB}
+ DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${PREV_DEV_CC_BBA_DB}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}
@@ -80,7 +80,7 @@ if (NOT EXTERNAL_CHIPDB)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} ${dev} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
- DEPENDS ${DB_PY} ${PREV_DEV_CC_BBA_DB}
+ DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${PREV_DEV_CC_BBA_DB}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 00ed891a..c498d20a 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -2345,6 +2345,7 @@ class Ecp5Packer
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_ECLKBRIDGECS) {
+ Loc loc;
NetInfo *i0 = get_net_or_empty(ci, id_CLK0), *i1 = get_net_or_empty(ci, id_CLK1),
*o = get_net_or_empty(ci, id_ECSOUT);
for (NetInfo *input : {i0, i1}) {
@@ -2358,24 +2359,53 @@ class Ecp5Packer
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
continue;
- Loc loc = ctx->getBelLocation(bel);
+ loc = ctx->getBelLocation(bel);
if (loc.x == user_loc.x) {
ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
- if (o != nullptr)
- for (auto user2 : o->users) {
- // Set side hint to ensure edge clock choice is routeable
- if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) {
- NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO);
- if (synco != nullptr)
- bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1;
- }
- }
goto eclkbridge_done;
}
}
}
+ if (input->driver.cell != nullptr) {
+ CellInfo *drv = input->driver.cell;
+ if (!drv->attrs.count(ctx->id("BEL")))
+ continue;
+ Loc drv_loc = ctx->getBelLocation(
+ ctx->getBelByName(ctx->id(drv->attrs.at(ctx->id("BEL")).as_string())));
+ BelId closest;
+ int closest_x = -1; // aim for same side of chip
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
+ continue;
+ loc = ctx->getBelLocation(bel);
+ if (closest_x == -1 || std::abs(loc.x - drv_loc.x) < std::abs(closest_x - drv_loc.x)) {
+ closest_x = loc.x;
+ closest = bel;
+ }
+ }
+ NPNR_ASSERT(closest != BelId());
+ loc = ctx->getBelLocation(closest);
+ ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest).str(ctx);
+ goto eclkbridge_done;
+ }
+ }
+ // If all else fails, place randomly
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
+ continue;
+ loc = ctx->getBelLocation(bel);
+ ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
}
eclkbridge_done:
+ if (o != nullptr)
+ for (auto user2 : o->users) {
+ // Set side hint to ensure edge clock choice is routeable
+ if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) {
+ NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO);
+ if (synco != nullptr)
+ bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1;
+ }
+ }
continue;
}
}
@@ -2575,7 +2605,7 @@ class Ecp5Packer
std::unordered_set<IdString> changed_cells;
for (auto net : changed_nets)
for (auto &user : ctx->nets.at(net)->users)
- if (user.port == id_CLKI || user.port == id_ECLKI)
+ if (user.port == id_CLKI || user.port == id_ECLKI || user.port == id_CLK0 || user.port == id_CLK1)
changed_cells.insert(user.cell->name);
changed_nets.clear();
for (auto cell : sorted(changed_cells)) {
@@ -2592,6 +2622,9 @@ class Ecp5Packer
copy_constraint(ci, id_CLKI, id_CDIVX, ratio);
} else if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF) {
copy_constraint(ci, id_ECLKI, id_ECLKO, 1);
+ } else if (ci->type == id_ECLKBRIDGECS) {
+ copy_constraint(ci, id_CLK0, id_ECSOUT, 1);
+ copy_constraint(ci, id_CLK1, id_ECSOUT, 1);
} else if (ci->type == id_DCCA) {
copy_constraint(ci, id_CLKI, id_CLKO, 1);
} else if (ci->type == id_EHXPLLL) {
@@ -2651,12 +2684,12 @@ class Ecp5Packer
prepack_checks();
pack_io();
pack_dqsbuf();
+ preplace_plls();
pack_iologic();
pack_ebr();
pack_dsps();
pack_dcus();
pack_misc();
- preplace_plls();
pack_constants();
pack_dram();
pack_carries();
diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py
index 610bd331..ee960fd3 100755
--- a/ecp5/trellis_import.py
+++ b/ecp5/trellis_import.py
@@ -186,6 +186,8 @@ def process_timing_data():
max_delay = min(entry["rising"][2], entry["falling"][2])
delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay))
elif entry["type"] == "SetupHold":
+ if type(entry["pin"]) is list:
+ continue
pin = constids[entry["pin"]]
clock = constids[entry["clock"][1]]
min_setup = entry["setup"][0]
@@ -441,6 +443,7 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.u32(len(location_types), "num_location_types")
bba.u32(len(packages), "num_packages")
bba.u32(len(pindata), "num_pios")
+ bba.u32(const_id_count, "const_id_count")
bba.r("locations", "locations")
bba.r("location_types", "location_type")
@@ -457,11 +460,12 @@ def write_database(dev_name, chip, ddrg, endianness):
dev_names = {"25k": "LFE5UM5G-25F", "45k": "LFE5UM5G-45F", "85k": "LFE5UM5G-85F"}
def main():
- global max_row, max_col
+ global max_row, max_col, const_id_count
pytrellis.load_database(database.get_db_root())
args = parser.parse_args()
# Read port pin file
+ const_id_count = 1 # count ID_NONE
with open(args.constids) as f:
for line in f:
line = line.replace("(", " ")
@@ -473,7 +477,7 @@ def main():
assert line[0] == "X"
idx = len(constids) + 1
constids[line[1]] = idx
-
+ const_id_count += 1
constids["SLICE"] = constids["TRELLIS_SLICE"]
constids["PIO"] = constids["TRELLIS_IO"]