ecp5: Helper functions for DQS and ECLK

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2019-02-11 17:56:19 +00:00 committed by David Shah
parent db1666fc3d
commit 63e1f02c65
5 changed files with 153 additions and 1 deletions

View File

@ -877,4 +877,41 @@ GlobalInfoPOD Arch::globalInfoAtLoc(Location loc)
return chip_info->location_glbinfo[locidx];
}
bool Arch::getPIODQSGroup(BelId pio, bool &dqsright, int &dqsrow)
{
for (int i = 0; i < chip_info->num_pios; i++) {
if (Location(chip_info->pio_info[i].abs_loc) == pio.location && chip_info->pio_info[i].bel_index == pio.index) {
int dqs = chip_info->pio_info[i].dqsgroup;
if (dqs == -1)
return false;
else {
dqsright = (dqs & 2048) != 0;
dqsrow = dqs & 0x1FF;
return true;
}
}
}
NPNR_ASSERT_FALSE("failed to find PIO");
}
BelId Arch::getDQSBUF(bool dqsright, int dqsrow)
{
BelId bel;
bel.location.y = dqsrow;
bel.location.x = (dqsright ? (chip_info->width - 1) : 0);
for (int i = 0; i < locInfo(bel)->num_bels; i++) {
auto &bd = locInfo(bel)->bel_data[i];
if (bd.type == id_DQSBUFM.index) {
bel.index = i;
return bel;
}
}
NPNR_ASSERT_FALSE("failed to find DQSBUF");
}
WireId Arch::getBankECLK(int bank, int eclk)
{
return getWireByLocAndBasename(Location(0, 0), "G_BANK" + std::to_string(bank) + "ECLK" + std::to_string(eclk));
}
NEXTPNR_NAMESPACE_END

View File

@ -1006,6 +1006,10 @@ struct Arch : BaseCtx
GlobalInfoPOD globalInfoAtLoc(Location loc);
bool getPIODQSGroup(BelId pio, bool &dqsright, int &dqsrow);
BelId getDQSBUF(bool dqsright, int dqsrow);
WireId getBankECLK(int bank, int eclk);
// Apply LPF constraints to the context
bool applyLPF(std::string filename, std::istream &in);

View File

@ -201,6 +201,9 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
}
// Just copy ports from the Bel
copy_bel_ports();
} else if (type == id_TRELLIS_ECLKBUF) {
add_port(ctx, new_cell.get(), "ECLKI", PORT_IN);
add_port(ctx, new_cell.get(), "ECLKO", PORT_OUT);
} else {
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
}

View File

@ -1280,3 +1280,5 @@ X(BURSTDET)
X(RDCFLAG)
X(WRCFLAG)
X(SCLK)
X(TRELLIS_ECLKBUF)

View File

@ -20,6 +20,7 @@
#include <algorithm>
#include <boost/optional.hpp>
#include <iterator>
#include <queue>
#include <unordered_set>
#include "cells.h"
#include "chain_utils.h"
@ -1396,6 +1397,106 @@ class Ecp5Packer
return a->driver.cell->type == b->driver.cell->type;
}
struct EdgeClockInfo
{
CellInfo *buffer = nullptr;
NetInfo *unbuf = nullptr;
NetInfo *buf = nullptr;
};
std::map<std::pair<int, int>, EdgeClockInfo> eclks;
void make_eclk(PortInfo &usr_port, CellInfo *usr_cell, BelId usr_bel, int bank)
{
NetInfo *ecknet = usr_port.net;
if (ecknet == nullptr)
log_error("Input '%s' of cell '%s' cannot be disconnected\n", usr_port.name.c_str(ctx),
usr_cell->name.c_str(ctx));
int found_eclk = -1, free_eclk = -1;
for (int i = 0; i < 2; i++) {
if (eclks.count(std::make_pair(bank, i))) {
if (eclks.at(std::make_pair(bank, i)).unbuf == ecknet) {
found_eclk = i;
break;
}
} else if (free_eclk == -1) {
free_eclk = i;
}
}
if (found_eclk == -1) {
if (free_eclk == -1) {
log_error("Unable to promote edge clock '%s' for bank %d. 2/2 edge clocks already used by '%s' and "
"'%s'.\n",
ecknet->name.c_str(ctx), bank, eclks.at(std::make_pair(bank, 0)).unbuf->name.c_str(ctx),
eclks.at(std::make_pair(bank, 1)).unbuf->name.c_str(ctx));
} else {
log_info("Promoted '%s' to bank %d ECLK%d.\n", ecknet->name.c_str(ctx), bank, free_eclk);
auto &eclk = eclks[std::make_pair(bank, free_eclk)];
eclk.unbuf = ecknet;
IdString eckname = ctx->id(ecknet->name.str(ctx) + "$eclk" + std::to_string(bank) + "_" +
std::to_string(free_eclk));
std::unique_ptr<NetInfo> promoted_ecknet(new NetInfo);
promoted_ecknet->name = eckname;
promoted_ecknet->is_global = true; // Prevents router etc touching this special net
eclk.buf = promoted_ecknet.get();
NPNR_ASSERT(!ctx->nets.count(eckname));
ctx->nets[eckname] = std::move(promoted_ecknet);
// Insert TRELLIS_ECLKBUF to isolate edge clock from general routing
std::unique_ptr<CellInfo> eclkbuf =
create_ecp5_cell(ctx, ctx->id("TRELLIS_ECLKBUF"), eckname.str(ctx) + "$buffer");
connect_port(ctx, ecknet, eclkbuf.get(), id_ECLKI);
connect_port(ctx, eclk.buf, eclkbuf.get(), id_ECLKO);
found_eclk = free_eclk;
eclk.buffer = eclkbuf.get();
new_cells.push_back(std::move(eclkbuf));
}
}
auto &eclk = eclks[std::make_pair(bank, found_eclk)];
disconnect_port(ctx, usr_cell, usr_port.name);
connect_port(ctx, eclk.buf, usr_cell, usr_port.name);
// Simple ECLK router
WireId userWire = ctx->getBelPinWire(usr_bel, usr_port.name);
IdString bnke_name = ctx->id("BNK_ECLK" + std::to_string(found_eclk));
IdString global_name = ctx->id("G_BANK" + std::to_string(bank) + "ECLK" + std::to_string(found_eclk));
std::queue<WireId> upstream;
std::unordered_map<WireId, PipId> backtrace;
upstream.push(userWire);
WireId next;
while (true) {
next = upstream.front();
upstream.pop();
IdString basename = ctx->getWireBasename(next);
if (basename == bnke_name || basename == global_name) {
break;
}
if (ctx->checkWireAvail(next)) {
for (auto pip : ctx->getPipsUphill(next)) {
WireId src = ctx->getPipSrcWire(pip);
backtrace[src] = pip;
upstream.push(src);
}
}
if (upstream.size() > 30000) {
log_error("failed to route bank %d ECLK%d to %s.%s\n", bank, found_eclk,
ctx->getBelName(usr_bel).c_str(ctx), usr_port.name.c_str(ctx));
}
}
// Set all the pips we found along the way
WireId cursor = next;
while (true) {
auto fnd = backtrace.find(cursor);
if (fnd == backtrace.end())
break;
ctx->bindPip(fnd->second, eclk.buf, STRENGTH_LOCKED);
cursor = ctx->getPipDstWire(fnd->second);
}
}
// Pack IOLOGIC
void pack_iologic()
{
@ -1449,13 +1550,18 @@ class Ecp5Packer
curr_mode = mode;
};
auto create_pio_iologic = [&](CellInfo *pio, CellInfo *curr) {
auto get_pio_bel = [&](CellInfo *pio, CellInfo *curr) {
if (!pio->attrs.count(ctx->id("BEL")))
log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO "
"(while processing '%s').\n",
curr->name.c_str(ctx));
BelId bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL"))));
NPNR_ASSERT(bel != BelId());
return bel;
};
auto create_pio_iologic = [&](CellInfo *pio, CellInfo *curr) {
BelId bel = get_pio_bel(pio, curr);
log_info("IOLOGIC component %s connected to PIO Bel %s\n", curr->name.c_str(ctx),
ctx->getBelName(bel).c_str(ctx));
Loc loc = ctx->getBelLocation(bel);