aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5
diff options
context:
space:
mode:
Diffstat (limited to 'ecp5')
-rw-r--r--ecp5/arch.cc111
-rw-r--r--ecp5/arch.h16
-rw-r--r--ecp5/arch_place.cc2
-rw-r--r--ecp5/arch_pybindings.cc4
-rw-r--r--ecp5/archdefs.h13
-rw-r--r--ecp5/bitstream.cc137
-rw-r--r--ecp5/constids.inc307
-rw-r--r--ecp5/dcu_bitstream.h254
-rw-r--r--ecp5/globals.cc61
-rw-r--r--ecp5/main.cc2
-rw-r--r--ecp5/pack.cc170
-rwxr-xr-xecp5/trellis_import.py11
12 files changed, 1032 insertions, 56 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 4a0b31b5..afea8d4a 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -400,7 +400,7 @@ BelId Arch::getBelByLocation(Loc loc) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
- return 100 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
+ return 170 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@@ -409,7 +409,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
- return 100 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
+ return 170 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
}
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
@@ -456,7 +456,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
el.x1 = bel.location.x + logic_cell_x1;
el.x2 = bel.location.x + logic_cell_x2;
el.y1 = bel.location.y + logic_cell_y1 + (2 * z) * logic_cell_pitch;
- el.y2 = bel.location.y + logic_cell_y2 + (2 * z + 1) * logic_cell_pitch;
+ el.y2 = bel.location.y + logic_cell_y2 + (2 * z + 0.5f) * logic_cell_pitch;
ret.push_back(el);
}
}
@@ -539,10 +539,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
return true;
}
#if 0 // FIXME
- if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
- delay.delay = 717;
- return true;
- }
+ if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
+ delay.delay = 717;
+ return true;
+ }
#endif
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) ||
@@ -576,10 +576,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
}
}
-TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const
+TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
-
+ clockInfoCount = 0;
if (cell->type == id_TRELLIS_SLICE) {
int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
if (port == id_CLK || port == id_WCK)
@@ -598,13 +598,13 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
return TMG_COMB_OUTPUT;
if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
(sd1 == 1 && port == id_M1)) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id_M0 || port == id_M1)
return TMG_COMB_INPUT;
if (port == id_Q0 || port == id_Q1) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
}
@@ -614,7 +614,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
port == id_WAD3 || port == id_WRE) {
- clockPort = id_WCK;
+ clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
@@ -638,10 +638,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
for (auto c : boost::adaptors::reverse(port_name)) {
if (std::isdigit(c))
continue;
- if (c == 'A')
- clockPort = id_CLKA;
- else if (c == 'B')
- clockPort = id_CLKB;
+ if (c == 'A' || c == 'B')
+ clockInfoCount = 1;
else
NPNR_ASSERT_FALSE_STR("bad ram port");
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
@@ -653,11 +651,92 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
return TMG_IGNORE; // FIXME
} else if (cell->type == id_EHXPLLL) {
return TMG_IGNORE;
+ } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) {
+ if (port == id_CH0_FF_TXI_CLK || port == id_CH0_FF_RXI_CLK || port == id_CH1_FF_TXI_CLK ||
+ port == id_CH1_FF_RXI_CLK)
+ return TMG_CLOCK_INPUT;
+ std::string prefix = port.str(this).substr(0, 9);
+ if (prefix == "CH0_FF_TX" || prefix == "CH0_FF_RX" || prefix == "CH1_FF_TX" || prefix == "CH1_FF_RX") {
+ clockInfoCount = 1;
+ return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
+ }
+ return TMG_IGNORE;
} else {
NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'");
}
}
+TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+{
+ TimingClockingInfo info;
+ info.setup.delay = 0;
+ info.hold.delay = 0;
+ info.clockToQ.delay = 0;
+ if (cell->type == id_TRELLIS_SLICE) {
+ int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
+
+ if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
+ port == id_WAD3 || port == id_WRE) {
+ info.edge = RISING_EDGE;
+ info.clock_port = id_WCK;
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ } else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
+ (sd1 == 1 && port == id_M1)) {
+ info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
+ info.clock_port = id_CLK;
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ } else {
+ info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
+ info.clock_port = id_CLK;
+ info.clockToQ.delay = 395;
+ }
+ } else if (cell->type == id_DP16KD) {
+ std::string port_name = port.str(this);
+ for (auto c : boost::adaptors::reverse(port_name)) {
+ if (std::isdigit(c))
+ continue;
+ if (c == 'A') {
+ info.clock_port = id_CLKA;
+ break;
+ } else if (c == 'B') {
+ info.clock_port = id_CLKB;
+ break;
+ } else
+ NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this));
+ }
+ info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") ==
+ "INV")
+ ? FALLING_EDGE
+ : RISING_EDGE;
+ if (cell->ports.at(port).type == PORT_OUT) {
+ info.clockToQ.delay = 4280;
+ } else {
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ }
+ } else if (cell->type == id_DCUA) {
+ std::string prefix = port.str(this).substr(0, 9);
+ info.edge = RISING_EDGE;
+ if (prefix == "CH0_FF_TX")
+ info.clock_port = id_CH0_FF_TXI_CLK;
+ else if (prefix == "CH0_FF_RX")
+ info.clock_port = id_CH0_FF_RXI_CLK;
+ else if (prefix == "CH1_FF_TX")
+ info.clock_port = id_CH1_FF_TXI_CLK;
+ else if (prefix == "CH1_FF_RX")
+ info.clock_port = id_CH1_FF_RXI_CLK;
+ if (cell->ports.at(port).type == PORT_OUT) {
+ info.clockToQ.delay = 660;
+ } else {
+ info.setup.delay = 1000;
+ info.hold.delay = 0;
+ }
+ }
+ return info;
+}
+
std::vector<std::pair<std::string, std::string>> Arch::getTilesAtLocation(int row, int col)
{
std::vector<std::pair<std::string, std::string>> ret;
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 583d539f..aa3c5348 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -619,6 +619,8 @@ struct Arch : BaseCtx
return wire_to_net.at(wire);
}
+ WireId getConflictingWireWire(WireId wire) const { return wire; }
+
NetInfo *getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
@@ -724,6 +726,8 @@ struct Arch : BaseCtx
return pip_to_net.at(pip);
}
+ WireId getConflictingPipWire(PipId pip) const { return WireId(); }
+
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
@@ -855,6 +859,12 @@ struct Arch : BaseCtx
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; }
+ DelayInfo getDelayFromNS(float ns) const
+ {
+ DelayInfo del;
+ del.delay = delay_t(ns * 1000);
+ return del;
+ }
uint32_t getDelayChecksum(delay_t v) const { return v; }
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
@@ -878,8 +888,10 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
- // Get the port class, also setting clockPort if applicable
- TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const;
+ // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port
+ TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
+ // Get the TimingClockingInfo of a port
+ TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc
index 6fcd8bde..41f87cb8 100644
--- a/ecp5/arch_place.cc
+++ b/ecp5/arch_place.cc
@@ -101,6 +101,8 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
bel_cells.push_back(cell);
return slicesCompatible(bel_cells);
+ } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) {
+ return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F && args.type != ArchArgs::LFE5U_85F;
} else {
// other checks
return true;
diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc
index 9312b4ad..5e73a673 100644
--- a/ecp5/arch_pybindings.cc
+++ b/ecp5/arch_pybindings.cc
@@ -130,6 +130,10 @@ void arch_wrap_python()
"cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets");
+
+ fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
+ pass_through<float>>::def_wrap(ctx_cls, "addClock");
+
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>);
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index b85852c2..b2c23134 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -75,6 +75,7 @@ struct Location
bool operator==(const Location &other) const { return x == other.x && y == other.y; }
bool operator!=(const Location &other) const { return x != other.x || y != other.y; }
+ bool operator<(const Location &other) const { return y == other.y ? x < other.x : y < other.y; }
};
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); }
@@ -86,6 +87,10 @@ struct BelId
bool operator==(const BelId &other) const { return index == other.index && location == other.location; }
bool operator!=(const BelId &other) const { return index != other.index || location != other.location; }
+ bool operator<(const BelId &other) const
+ {
+ return location == other.location ? index < other.index : location < other.location;
+ }
};
struct WireId
@@ -95,6 +100,10 @@ struct WireId
bool operator==(const WireId &other) const { return index == other.index && location == other.location; }
bool operator!=(const WireId &other) const { return index != other.index || location != other.location; }
+ bool operator<(const WireId &other) const
+ {
+ return location == other.location ? index < other.index : location < other.location;
+ }
};
struct PipId
@@ -104,6 +113,10 @@ struct PipId
bool operator==(const PipId &other) const { return index == other.index && location == other.location; }
bool operator!=(const PipId &other) const { return index != other.index || location != other.location; }
+ bool operator<(const PipId &other) const
+ {
+ return location == other.location ? index < other.index : location < other.location;
+ }
};
struct GroupId
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index 1c150ae2..961a3956 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -24,7 +24,7 @@
#include <queue>
#include <regex>
#include <streambuf>
-
+#include <boost/algorithm/string/predicate.hpp>
#include "config.h"
#include "pio.h"
#include "log.h"
@@ -379,12 +379,23 @@ std::vector<std::string> get_dsp_tiles(Context *ctx, BelId bel)
return tiles;
}
+// Get the list of tiles corresponding to a DCU
+std::vector<std::string> get_dcu_tiles(Context *ctx, BelId bel)
+{
+ std::vector<std::string> tiles;
+ Loc loc = ctx->getBelLocation(bel);
+ for (int i = 0; i < 9; i++)
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + i, "DCU" + std::to_string(i)));
+ return tiles;
+}
+
// Get the list of tiles corresponding to a PLL
std::vector<std::string> get_pll_tiles(Context *ctx, BelId bel)
{
std::string name = ctx->locInfo(bel)->bel_data[bel.index].name.get();
std::vector<std::string> tiles;
Loc loc = ctx->getBelLocation(bel);
+ static const std::set<std::string> pll1_lr = {"PLL1_LR", "BANKREF4"};
if (name == "EHXPLL_UL") {
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x - 1, "PLL0_UL"));
@@ -394,7 +405,7 @@ std::vector<std::string> get_pll_tiles(Context *ctx, BelId bel)
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x + 1, "BANKREF8"));
} else if (name == "EHXPLL_LR") {
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "PLL0_LR"));
- tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, "PLL1_LR"));
+ tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, pll1_lr));
} else if (name == "EHXPLL_UR") {
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x - 1, "PLL0_UR"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y + 1, loc.x - 1, "PLL1_UR"));
@@ -407,26 +418,23 @@ std::vector<std::string> get_pll_tiles(Context *ctx, BelId bel)
void fix_tile_names(Context *ctx, ChipConfig &cc)
{
// Remove the V prefix/suffix on certain tiles if device is a SERDES variant
- if (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM_45F ||
- ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_25F ||
- ctx->args.type == ArchArgs::LFE5UM5G_45F || ctx->args.type == ArchArgs::LFE5UM5G_85F) {
+ if (ctx->args.type == ArchArgs::LFE5U_25F || ctx->args.type == ArchArgs::LFE5U_45F ||
+ ctx->args.type == ArchArgs::LFE5U_85F) {
std::map<std::string, std::string> tiletype_xform;
for (const auto &tile : cc.tiles) {
std::string newname = tile.first;
- auto vcib = tile.first.find("VCIB");
- if (vcib != std::string::npos) {
- // Remove the V
- newname.erase(vcib, 1);
+ auto cibdcu = tile.first.find("CIB_DCU");
+ if (cibdcu != std::string::npos) {
+ // Add the V
+ if (newname.at(cibdcu - 1) != 'V')
+ newname.insert(cibdcu, 1, 'V');
+ tiletype_xform[tile.first] = newname;
+ } else if (boost::ends_with(tile.first, "BMID_0H")) {
+ newname.back() = 'V';
+ tiletype_xform[tile.first] = newname;
+ } else if (boost::ends_with(tile.first, "BMID_2")) {
+ newname.push_back('V');
tiletype_xform[tile.first] = newname;
- } else if (tile.first.back() == 'V') {
- // BMID_0V or BMID_2V
- if (tile.first.at(tile.first.size() - 2) == '0') {
- newname.at(tile.first.size() - 1) = 'H';
- tiletype_xform[tile.first] = newname;
- } else if (tile.first.at(tile.first.size() - 2) == '2') {
- newname.pop_back();
- tiletype_xform[tile.first] = newname;
- }
}
}
// Apply the name changes
@@ -455,6 +463,20 @@ void tieoff_dsp_ports(Context *ctx, ChipConfig &cc, CellInfo *ci)
}
}
+void tieoff_dcu_ports(Context *ctx, ChipConfig &cc, CellInfo *ci)
+{
+ for (auto port : ci->ports) {
+ if (port.second.net == nullptr && port.second.type == PORT_IN) {
+ if (port.first.str(ctx).find("CLK") != std::string::npos ||
+ port.first.str(ctx).find("HDIN") != std::string::npos ||
+ port.first.str(ctx).find("HDOUT") != std::string::npos)
+ continue;
+ bool value = bool_or_default(ci->params, ctx->id(port.first.str(ctx) + "MUX"), false);
+ tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), value);
+ }
+ }
+}
+
static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
{
std::string tile = ctx->getPipTilename(pip);
@@ -463,6 +485,46 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
cc.tiles[tile].add_arc(sink, source);
}
+static std::vector<bool> parse_config_str(std::string str, int length)
+{
+ // For DCU config which might be bin, hex or dec using prefices accordingly
+ std::string base = str.substr(0, 2);
+ std::vector<bool> word;
+ word.resize(length, false);
+ if (base == "0b") {
+ for (int i = 0; i < int(str.length()) - 2; i++) {
+ char c = str.at((str.size() - 1) - i);
+ NPNR_ASSERT(c == '0' || c == '1');
+ word.at(i) = (c == '1');
+ }
+ } else if (base == "0x") {
+ for (int i = 0; i < int(str.length()) - 2; i++) {
+ char c = str.at((str.size() - i) - 1);
+ int nibble = chtohex(c);
+ word.at(i * 4) = nibble & 0x1;
+ if (i * 4 + 1 < length)
+ word.at(i * 4 + 1) = nibble & 0x2;
+ if (i * 4 + 2 < length)
+ word.at(i * 4 + 2) = nibble & 0x4;
+ if (i * 4 + 3 < length)
+ word.at(i * 4 + 3) = nibble & 0x8;
+ }
+ } else if (base == "0d") {
+ NPNR_ASSERT(length < 64);
+ unsigned long long value = std::stoull(str.substr(2));
+ for (int i = 0; i < length; i++)
+ if (value & (1 << i))
+ word.at(i) = true;
+ } else {
+ NPNR_ASSERT(length < 64);
+ unsigned long long value = std::stoull(str);
+ for (int i = 0; i < length; i++)
+ if (value & (1 << i))
+ word.at(i) = true;
+ }
+ return word;
+}
+
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file)
{
ChipConfig cc;
@@ -480,6 +542,23 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// TODO: .bit metadata
}
+ // Clear out DCU tieoffs in base config if DCU used
+ for (auto &cell : ctx->cells) {
+ CellInfo *ci = cell.second.get();
+ if (ci->type == id_DCUA) {
+ Loc loc = ctx->getBelLocation(ci->bel);
+ for (int i = 0; i < 12; i++) {
+ auto tiles = ctx->getTilesAtLocation(loc.y - 1, loc.x + i);
+ for (const auto &tile : tiles) {
+ auto cc_tile = cc.tiles.find(tile.first);
+ if (cc_tile != cc.tiles.end()) {
+ cc_tile->second.cenums.clear();
+ cc_tile->second.cunknowns.clear();
+ }
+ }
+ }
+ }
+ }
// Add all set, configurable pips to the config
for (auto pip : ctx->getPips()) {
if (ctx->getBoundPipNet(pip) != nullptr) {
@@ -999,6 +1078,28 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1));
cc.tilegroups.push_back(tg);
+ } else if (ci->type == id_DCUA) {
+ TileGroup tg;
+ tg.tiles = get_dcu_tiles(ctx, ci->bel);
+ tg.config.add_enum("DCU.MODE", "DCUA");
+#include "dcu_bitstream.h"
+ cc.tilegroups.push_back(tg);
+ tieoff_dcu_ports(ctx, cc, ci);
+ } else if (ci->type == id_EXTREFB) {
+ TileGroup tg;
+ tg.tiles = get_dcu_tiles(ctx, ci->bel);
+ tg.config.add_word("EXTREF.REFCK_DCBIAS_EN",
+ parse_config_str(str_or_default(ci->params, ctx->id("REFCK_DCBIAS_EN"), "0"), 1));
+ tg.config.add_word("EXTREF.REFCK_RTERM",
+ parse_config_str(str_or_default(ci->params, ctx->id("REFCK_RTERM"), "0"), 1));
+ tg.config.add_word("EXTREF.REFCK_PWDNB",
+ parse_config_str(str_or_default(ci->params, ctx->id("REFCK_PWDNB"), "0"), 1));
+ cc.tilegroups.push_back(tg);
+ } else if (ci->type == id_PCSCLKDIV) {
+ Loc loc = ctx->getBelLocation(ci->bel);
+ std::string tname = ctx->getTileByTypeAndLocation(loc.y + 1, loc.x, "BMID_0H");
+ cc.tiles[tname].add_enum("PCSCLKDIV" + std::to_string(loc.z),
+ str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index bdcbc1ea..11ecc240 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -810,3 +810,310 @@ X(LOCK)
X(INTLOCK)
X(REFCLK)
X(CLKINTFB)
+
+
+X(EXTREFB)
+X(REFCLKP)
+X(REFCLKN)
+X(REFCLKO)
+
+X(DCUA)
+X(CH0_HDINP)
+X(CH1_HDINP)
+X(CH0_HDINN)
+X(CH1_HDINN)
+X(D_TXBIT_CLKP_FROM_ND)
+X(D_TXBIT_CLKN_FROM_ND)
+X(D_SYNC_ND)
+X(D_TXPLL_LOL_FROM_ND)
+X(CH0_RX_REFCLK)
+X(CH1_RX_REFCLK)
+X(CH0_FF_RXI_CLK)
+X(CH1_FF_RXI_CLK)
+X(CH0_FF_TXI_CLK)
+X(CH1_FF_TXI_CLK)
+X(CH0_FF_EBRD_CLK)
+X(CH1_FF_EBRD_CLK)
+X(CH0_FF_TX_D_0)
+X(CH1_FF_TX_D_0)
+X(CH0_FF_TX_D_1)
+X(CH1_FF_TX_D_1)
+X(CH0_FF_TX_D_2)
+X(CH1_FF_TX_D_2)
+X(CH0_FF_TX_D_3)
+X(CH1_FF_TX_D_3)
+X(CH0_FF_TX_D_4)
+X(CH1_FF_TX_D_4)
+X(CH0_FF_TX_D_5)
+X(CH1_FF_TX_D_5)
+X(CH0_FF_TX_D_6)
+X(CH1_FF_TX_D_6)
+X(CH0_FF_TX_D_7)
+X(CH1_FF_TX_D_7)
+X(CH0_FF_TX_D_8)
+X(CH1_FF_TX_D_8)
+X(CH0_FF_TX_D_9)
+X(CH1_FF_TX_D_9)
+X(CH0_FF_TX_D_10)
+X(CH1_FF_TX_D_10)
+X(CH0_FF_TX_D_11)
+X(CH1_FF_TX_D_11)
+X(CH0_FF_TX_D_12)
+X(CH1_FF_TX_D_12)
+X(CH0_FF_TX_D_13)
+X(CH1_FF_TX_D_13)
+X(CH0_FF_TX_D_14)
+X(CH1_FF_TX_D_14)
+X(CH0_FF_TX_D_15)
+X(CH1_FF_TX_D_15)
+X(CH0_FF_TX_D_16)
+X(CH1_FF_TX_D_16)
+X(CH0_FF_TX_D_17)
+X(CH1_FF_TX_D_17)
+X(CH0_FF_TX_D_18)
+X(CH1_FF_TX_D_18)
+X(CH0_FF_TX_D_19)
+X(CH1_FF_TX_D_19)
+X(CH0_FF_TX_D_20)
+X(CH1_FF_TX_D_20)
+X(CH0_FF_TX_D_21)
+X(CH1_FF_TX_D_21)
+X(CH0_FF_TX_D_22)
+X(CH1_FF_TX_D_22)
+X(CH0_FF_TX_D_23)
+X(CH1_FF_TX_D_23)
+X(CH0_FFC_EI_EN)
+X(CH1_FFC_EI_EN)
+X(CH0_FFC_PCIE_DET_EN)
+X(CH1_FFC_PCIE_DET_EN)
+X(CH0_FFC_PCIE_CT)
+X(CH1_FFC_PCIE_CT)
+X(CH0_FFC_SB_INV_RX)
+X(CH1_FFC_SB_INV_RX)
+X(CH0_FFC_ENABLE_CGALIGN)
+X(CH1_FFC_ENABLE_CGALIGN)
+X(CH0_FFC_SIGNAL_DETECT)
+X(CH1_FFC_SIGNAL_DETECT)
+X(CH0_FFC_FB_LOOPBACK)
+X(CH1_FFC_FB_LOOPBACK)
+X(CH0_FFC_SB_PFIFO_LP)
+X(CH1_FFC_SB_PFIFO_LP)
+X(CH0_FFC_PFIFO_CLR)
+X(CH1_FFC_PFIFO_CLR)
+X(CH0_FFC_RATE_MODE_RX)
+X(CH1_FFC_RATE_MODE_RX)
+X(CH0_FFC_RATE_MODE_TX)
+X(CH1_FFC_RATE_MODE_TX)
+X(CH0_FFC_DIV11_MODE_RX)
+X(CH1_FFC_DIV11_MODE_RX)
+X(CH0_FFC_RX_GEAR_MODE)
+X(CH1_FFC_RX_GEAR_MODE)
+X(CH0_FFC_TX_GEAR_MODE)
+X(CH1_FFC_TX_GEAR_MODE)
+X(CH0_FFC_DIV11_MODE_TX)
+X(CH1_FFC_DIV11_MODE_TX)
+X(CH0_FFC_LDR_CORE2TX_EN)
+X(CH1_FFC_LDR_CORE2TX_EN)
+X(CH0_FFC_LANE_TX_RST)
+X(CH1_FFC_LANE_TX_RST)
+X(CH0_FFC_LANE_RX_RST)
+X(CH1_FFC_LANE_RX_RST)
+X(CH0_FFC_RRST)
+X(CH1_FFC_RRST)
+X(CH0_FFC_TXPWDNB)
+X(CH1_FFC_TXPWDNB)
+X(CH0_FFC_RXPWDNB)
+X(CH1_FFC_RXPWDNB)
+X(CH0_LDR_CORE2TX)
+X(CH1_LDR_CORE2TX)
+X(D_SCIWDATA0)
+X(D_SCIWDATA1)
+X(D_SCIWDATA2)
+X(D_SCIWDATA3)
+X(D_SCIWDATA4)
+X(D_SCIWDATA5)
+X(D_SCIWDATA6)
+X(D_SCIWDATA7)
+X(D_SCIADDR0)
+X(D_SCIADDR1)
+X(D_SCIADDR2)
+X(D_SCIADDR3)
+X(D_SCIADDR4)
+X(D_SCIADDR5)
+X(D_SCIENAUX)
+X(D_SCISELAUX)
+X(CH0_SCIEN)
+X(CH1_SCIEN)
+X(CH0_SCISEL)
+X(CH1_SCISEL)
+X(D_SCIRD)
+X(D_SCIWSTN)
+X(D_CYAWSTN)
+X(D_FFC_SYNC_TOGGLE)
+X(D_FFC_DUAL_RST)
+X(D_FFC_MACRO_RST)
+X(D_FFC_MACROPDB)
+X(D_FFC_TRST)
+X(CH0_FFC_CDR_EN_BITSLIP)
+X(CH1_FFC_CDR_EN_BITSLIP)
+X(D_SCAN_ENABLE)
+X(D_SCAN_IN_0)
+X(D_SCAN_IN_1)
+X(D_SCAN_IN_2)
+X(D_SCAN_IN_3)
+X(D_SCAN_IN_4)
+X(D_SCAN_IN_5)
+X(D_SCAN_IN_6)
+X(D_SCAN_IN_7)
+X(D_SCAN_MODE)
+X(D_SCAN_RESET)
+X(D_CIN0)
+X(D_CIN1)
+X(D_CIN2)
+X(D_CIN3)
+X(D_CIN4)
+X(D_CIN5)
+X(D_CIN6)
+X(D_CIN7)
+X(D_CIN8)
+X(D_CIN9)
+X(D_CIN10)
+X(D_CIN11)
+X(CH0_HDOUTP)
+X(CH1_HDOUTP)
+X(CH0_HDOUTN)
+X(CH1_HDOUTN)
+X(D_TXBIT_CLKP_TO_ND)
+X(D_TXBIT_CLKN_TO_ND)
+X(D_SYNC_PULSE2ND)
+X(D_TXPLL_LOL_TO_ND)
+X(CH0_FF_RX_F_CLK)
+X(CH1_FF_RX_F_CLK)
+X(CH0_FF_RX_H_CLK)
+X(CH1_FF_RX_H_CLK)
+X(CH0_FF_TX_F_CLK)
+X(CH1_FF_TX_F_CLK)
+X(CH0_FF_TX_H_CLK)
+X(CH1_FF_TX_H_CLK)
+X(CH0_FF_RX_PCLK)
+X(CH1_FF_RX_PCLK)
+X(CH0_FF_TX_PCLK)
+X(CH1_FF_TX_PCLK)
+X(CH0_FF_RX_D_0)
+X(CH1_FF_RX_D_0)
+X(CH0_FF_RX_D_1)
+X(CH1_FF_RX_D_1)
+X(CH0_FF_RX_D_2)
+X(CH1_FF_RX_D_2)
+X(CH0_FF_RX_D_3)
+X(CH1_FF_RX_D_3)
+X(CH0_FF_RX_D_4)
+X(CH1_FF_RX_D_4)
+X(CH0_FF_RX_D_5)
+X(CH1_FF_RX_D_5)
+X(CH0_FF_RX_D_6)
+X(CH1_FF_RX_D_6)
+X(CH0_FF_RX_D_7)
+X(CH1_FF_RX_D_7)
+X(CH0_FF_RX_D_8)
+X(CH1_FF_RX_D_8)
+X(CH0_FF_RX_D_9)
+X(CH1_FF_RX_D_9)
+X(CH0_FF_RX_D_10)
+X(CH1_FF_RX_D_10)
+X(CH0_FF_RX_D_11)
+X(CH1_FF_RX_D_11)
+X(CH0_FF_RX_D_12)
+X(CH1_FF_RX_D_12)
+X(CH0_FF_RX_D_13)
+X(CH1_FF_RX_D_13)
+X(CH0_FF_RX_D_14)
+X(CH1_FF_RX_D_14)
+X(CH0_FF_RX_D_15)
+X(CH1_FF_RX_D_15)
+X(CH0_FF_RX_D_16)
+X(CH1_FF_RX_D_16)
+X(CH0_FF_RX_D_17)
+X(CH1_FF_RX_D_17)
+X(CH0_FF_RX_D_18)
+X(CH1_FF_RX_D_18)
+X(CH0_FF_RX_D_19)
+X(CH1_FF_RX_D_19)
+X(CH0_FF_RX_D_20)
+X(CH1_FF_RX_D_20)
+X(CH0_FF_RX_D_21)
+X(CH1_FF_RX_D_21)
+X(CH0_FF_RX_D_22)
+X(CH1_FF_RX_D_22)
+X(CH0_FF_RX_D_23)
+X(CH1_FF_RX_D_23)
+X(CH0_FFS_PCIE_DONE)
+X(CH1_FFS_PCIE_DONE)
+X(CH0_FFS_PCIE_CON)
+X(CH1_FFS_PCIE_CON)
+X(CH0_FFS_RLOS)
+X(CH1_FFS_RLOS)
+X(CH0_FFS_LS_SYNC_STATUS)
+X(CH1_FFS_LS_SYNC_STATUS)
+X(CH0_FFS_CC_UNDERRUN)
+X(CH1_FFS_CC_UNDERRUN)
+X(CH0_FFS_CC_OVERRUN)
+X(CH1_FFS_CC_OVERRUN)
+X(CH0_FFS_RXFBFIFO_ERROR)
+X(CH1_FFS_RXFBFIFO_ERROR)
+X(CH0_FFS_TXFBFIFO_ERROR)
+X(CH1_FFS_TXFBFIFO_ERROR)
+X(CH0_FFS_RLOL)
+X(CH1_FFS_RLOL)
+X(CH0_FFS_SKP_ADDED)
+X(CH1_FFS_SKP_ADDED)
+X(CH0_FFS_SKP_DELETED)
+X(CH1_FFS_SKP_DELETED)
+X(CH0_LDR_RX2CORE)
+X(CH1_LDR_RX2CORE)
+X(D_SCIRDATA0)
+X(D_SCIRDATA1)
+X(D_SCIRDATA2)
+X(D_SCIRDATA3)
+X(D_SCIRDATA4)
+X(D_SCIRDATA5)
+X(D_SCIRDATA6)
+X(D_SCIRDATA7)
+X(D_SCIINT)
+X(D_SCAN_OUT_0)
+X(D_SCAN_OUT_1)
+X(D_SCAN_OUT_2)
+X(D_SCAN_OUT_3)
+X(D_SCAN_OUT_4)
+X(D_SCAN_OUT_5)
+X(D_SCAN_OUT_6)
+X(D_SCAN_OUT_7)
+X(D_COUT0)
+X(D_COUT1)
+X(D_COUT2)
+X(D_COUT3)
+X(D_COUT4)
+X(D_COUT5)
+X(D_COUT6)
+X(D_COUT7)
+X(D_COUT8)
+X(D_COUT9)
+X(D_COUT10)
+X(D_COUT11)
+X(D_COUT12)
+X(D_COUT13)
+X(D_COUT14)
+X(D_COUT15)
+X(D_COUT16)
+X(D_COUT17)
+X(D_COUT18)
+X(D_COUT19)
+X(D_REFCLKI)
+X(D_FFS_PLOL)
+
+X(PCSCLKDIV)
+X(SEL2)
+X(SEL1)
+X(SEL0)
+X(CDIV1)
+X(CDIVX) \ No newline at end of file
diff --git a/ecp5/dcu_bitstream.h b/ecp5/dcu_bitstream.h
new file mode 100644
index 00000000..0a5028d2
--- /dev/null
+++ b/ecp5/dcu_bitstream.h
@@ -0,0 +1,254 @@
+tg.config.add_word("DCU.CH0_AUTO_CALIB_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_AUTO_CALIB_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_AUTO_FACQ_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_AUTO_FACQ_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_BAND_THRESHOLD", parse_config_str(str_or_default(ci->params, ctx->id("CH0_BAND_THRESHOLD"), "0"), 6));
+tg.config.add_word("DCU.CH0_CALIB_CK_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CALIB_CK_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_CC_MATCH_1", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CC_MATCH_1"), "0"), 10));
+tg.config.add_word("DCU.CH0_CC_MATCH_2", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CC_MATCH_2"), "0"), 10));
+tg.config.add_word("DCU.CH0_CC_MATCH_3", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CC_MATCH_3"), "0"), 10));
+tg.config.add_word("DCU.CH0_CC_MATCH_4", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CC_MATCH_4"), "0"), 10));
+tg.config.add_word("DCU.CH0_CDR_CNT4SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CDR_CNT4SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_CDR_CNT8SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CDR_CNT8SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_CTC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_CTC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_DCOATDCFG", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOATDCFG"), "0"), 2));
+tg.config.add_word("DCU.CH0_DCOATDDLY", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOATDDLY"), "0"), 2));
+tg.config.add_word("DCU.CH0_DCOBYPSATD", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOBYPSATD"), "0"), 1));
+tg.config.add_word("DCU.CH0_DCOCALDIV", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOCALDIV"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOCTLGI", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOCTLGI"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCODISBDAVOID", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCODISBDAVOID"), "0"), 1));
+tg.config.add_word("DCU.CH0_DCOFLTDAC", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOFLTDAC"), "0"), 2));
+tg.config.add_word("DCU.CH0_DCOFTNRG", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOFTNRG"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOIOSTUNE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOIOSTUNE"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOITUNE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOITUNE"), "0"), 2));
+tg.config.add_word("DCU.CH0_DCOITUNE4LSB", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOITUNE4LSB"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOIUPDNX2", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOIUPDNX2"), "0"), 1));
+tg.config.add_word("DCU.CH0_DCONUOFLSB", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCONUOFLSB"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOSCALEI", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOSCALEI"), "0"), 2));
+tg.config.add_word("DCU.CH0_DCOSTARTVAL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOSTARTVAL"), "0"), 3));
+tg.config.add_word("DCU.CH0_DCOSTEP", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DCOSTEP"), "0"), 2));
+tg.config.add_word("DCU.CH0_DEC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_DEC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_ENABLE_CG_ALIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_ENABLE_CG_ALIGN"), "0"), 1));
+tg.config.add_word("DCU.CH0_ENC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_ENC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_FF_RX_F_CLK_DIS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_FF_RX_F_CLK_DIS"), "0"), 1));
+tg.config.add_word("DCU.CH0_FF_RX_H_CLK_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_FF_RX_H_CLK_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_FF_TX_F_CLK_DIS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_FF_TX_F_CLK_DIS"), "0"), 1));
+tg.config.add_word("DCU.CH0_FF_TX_H_CLK_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_FF_TX_H_CLK_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_GE_AN_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_GE_AN_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH0_INVERT_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_INVERT_RX"), "0"), 1));
+tg.config.add_word("DCU.CH0_INVERT_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_INVERT_TX"), "0"), 1));
+tg.config.add_word("DCU.CH0_LDR_CORE2TX_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_LDR_CORE2TX_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_LDR_RX2CORE_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_LDR_RX2CORE_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_LEQ_OFFSET_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_LEQ_OFFSET_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_LEQ_OFFSET_TRIM", parse_config_str(str_or_default(ci->params, ctx->id("CH0_LEQ_OFFSET_TRIM"), "0"), 3));
+tg.config.add_word("DCU.CH0_LSM_DISABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_LSM_DISABLE"), "0"), 1));
+tg.config.add_word("DCU.CH0_MATCH_2_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_MATCH_2_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH0_MATCH_4_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_MATCH_4_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH0_MIN_IPG_CNT", parse_config_str(str_or_default(ci->params, ctx->id("CH0_MIN_IPG_CNT"), "0"), 2));
+tg.config.add_word("DCU.CH0_PCIE_EI_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PCIE_EI_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_PCIE_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PCIE_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_PCS_DET_TIME_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PCS_DET_TIME_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_PDEN_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PDEN_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_PRBS_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PRBS_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH0_PRBS_LOCK", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PRBS_LOCK"), "0"), 1));
+tg.config.add_word("DCU.CH0_PRBS_SELECTION", parse_config_str(str_or_default(ci->params, ctx->id("CH0_PRBS_SELECTION"), "0"), 1));
+tg.config.add_word("DCU.CH0_RATE_MODE_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RATE_MODE_RX"), "0"), 1));
+tg.config.add_word("DCU.CH0_RATE_MODE_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RATE_MODE_TX"), "0"), 1));
+tg.config.add_word("DCU.CH0_RCV_DCC_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RCV_DCC_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_REG_BAND_OFFSET", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REG_BAND_OFFSET"), "0"), 4));
+tg.config.add_word("DCU.CH0_REG_BAND_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REG_BAND_SEL"), "0"), 6));
+tg.config.add_word("DCU.CH0_REG_IDAC_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REG_IDAC_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_REG_IDAC_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REG_IDAC_SEL"), "0"), 10));
+tg.config.add_word("DCU.CH0_REQ_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REQ_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_REQ_LVL_SET", parse_config_str(str_or_default(ci->params, ctx->id("CH0_REQ_LVL_SET"), "0"), 2));
+tg.config.add_word("DCU.CH0_RIO_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RIO_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_RLOS_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RLOS_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_RPWDNB", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RPWDNB"), "0"), 1));
+tg.config.add_word("DCU.CH0_RTERM_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RTERM_RX"), "0"), 5));
+tg.config.add_word("DCU.CH0_RTERM_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RTERM_TX"), "0"), 5));
+tg.config.add_word("DCU.CH0_RXIN_CM", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RXIN_CM"), "0"), 2));
+tg.config.add_word("DCU.CH0_RXTERM_CM", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RXTERM_CM"), "0"), 2));
+tg.config.add_word("DCU.CH0_RX_DCO_CK_DIV", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_DCO_CK_DIV"), "0"), 3));
+tg.config.add_word("DCU.CH0_RX_DIV11_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_DIV11_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_RX_GEAR_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_GEAR_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_RX_GEAR_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_GEAR_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_RX_LOS_CEQ", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_LOS_CEQ"), "0"), 2));
+tg.config.add_word("DCU.CH0_RX_LOS_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_LOS_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_RX_LOS_HYST_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_LOS_HYST_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_RX_LOS_LVL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_LOS_LVL"), "0"), 3));
+tg.config.add_word("DCU.CH0_RX_RATE_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_RATE_SEL"), "0"), 4));
+tg.config.add_word("DCU.CH0_RX_SB_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_RX_SB_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_SB_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_SB_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_SEL_SD_RX_CLK", parse_config_str(str_or_default(ci->params, ctx->id("CH0_SEL_SD_RX_CLK"), "0"), 1));
+tg.config.add_word("DCU.CH0_TDRV_DAT_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_DAT_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_POST_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_POST_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_TDRV_PRE_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_PRE_EN"), "0"), 1));
+tg.config.add_word("DCU.CH0_TDRV_SLICE0_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE0_CUR"), "0"), 3));
+tg.config.add_word("DCU.CH0_TDRV_SLICE0_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE0_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE1_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE1_CUR"), "0"), 3));
+tg.config.add_word("DCU.CH0_TDRV_SLICE1_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE1_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE2_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE2_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE2_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE2_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE3_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE3_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE3_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE3_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE4_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE4_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE4_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE4_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE5_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE5_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH0_TDRV_SLICE5_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TDRV_SLICE5_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TPWDNB", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TPWDNB"), "0"), 1));
+tg.config.add_word("DCU.CH0_TX_CM_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_CM_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH0_TX_DIV11_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_DIV11_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH0_TX_GEAR_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_GEAR_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_TX_GEAR_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_GEAR_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_TX_POST_SIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_POST_SIGN"), "0"), 1));
+tg.config.add_word("DCU.CH0_TX_PRE_SIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH0_TX_PRE_SIGN"), "0"), 1));
+tg.config.add_word("DCU.CH0_UC_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_UC_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH0_UDF_COMMA_A", parse_config_str(str_or_default(ci->params, ctx->id("CH0_UDF_COMMA_A"), "0"), 10));
+tg.config.add_word("DCU.CH0_UDF_COMMA_B", parse_config_str(str_or_default(ci->params, ctx->id("CH0_UDF_COMMA_B"), "0"), 10));
+tg.config.add_word("DCU.CH0_UDF_COMMA_MASK", parse_config_str(str_or_default(ci->params, ctx->id("CH0_UDF_COMMA_MASK"), "0"), 10));
+tg.config.add_word("DCU.CH0_WA_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH0_WA_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH0_WA_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH0_WA_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_AUTO_CALIB_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_AUTO_CALIB_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_AUTO_FACQ_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_AUTO_FACQ_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_BAND_THRESHOLD", parse_config_str(str_or_default(ci->params, ctx->id("CH1_BAND_THRESHOLD"), "0"), 6));
+tg.config.add_word("DCU.CH1_CALIB_CK_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CALIB_CK_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_CC_MATCH_1", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CC_MATCH_1"), "0"), 10));
+tg.config.add_word("DCU.CH1_CC_MATCH_2", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CC_MATCH_2"), "0"), 10));
+tg.config.add_word("DCU.CH1_CC_MATCH_3", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CC_MATCH_3"), "0"), 10));
+tg.config.add_word("DCU.CH1_CC_MATCH_4", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CC_MATCH_4"), "0"), 10));
+tg.config.add_word("DCU.CH1_CDR_CNT4SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CDR_CNT4SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_CDR_CNT8SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CDR_CNT8SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_CTC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_CTC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_DCOATDCFG", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOATDCFG"), "0"), 2));
+tg.config.add_word("DCU.CH1_DCOATDDLY", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOATDDLY"), "0"), 2));
+tg.config.add_word("DCU.CH1_DCOBYPSATD", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOBYPSATD"), "0"), 1));
+tg.config.add_word("DCU.CH1_DCOCALDIV", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOCALDIV"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOCTLGI", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOCTLGI"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCODISBDAVOID", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCODISBDAVOID"), "0"), 1));
+tg.config.add_word("DCU.CH1_DCOFLTDAC", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOFLTDAC"), "0"), 2));
+tg.config.add_word("DCU.CH1_DCOFTNRG", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOFTNRG"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOIOSTUNE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOIOSTUNE"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOITUNE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOITUNE"), "0"), 2));
+tg.config.add_word("DCU.CH1_DCOITUNE4LSB", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOITUNE4LSB"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOIUPDNX2", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOIUPDNX2"), "0"), 1));
+tg.config.add_word("DCU.CH1_DCONUOFLSB", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCONUOFLSB"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOSCALEI", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOSCALEI"), "0"), 2));
+tg.config.add_word("DCU.CH1_DCOSTARTVAL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOSTARTVAL"), "0"), 3));
+tg.config.add_word("DCU.CH1_DCOSTEP", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DCOSTEP"), "0"), 2));
+tg.config.add_word("DCU.CH1_DEC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_DEC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_ENABLE_CG_ALIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_ENABLE_CG_ALIGN"), "0"), 1));
+tg.config.add_word("DCU.CH1_ENC_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_ENC_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_FF_RX_F_CLK_DIS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_FF_RX_F_CLK_DIS"), "0"), 1));
+tg.config.add_word("DCU.CH1_FF_RX_H_CLK_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_FF_RX_H_CLK_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_FF_TX_F_CLK_DIS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_FF_TX_F_CLK_DIS"), "0"), 1));
+tg.config.add_word("DCU.CH1_FF_TX_H_CLK_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_FF_TX_H_CLK_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_GE_AN_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_GE_AN_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH1_INVERT_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_INVERT_RX"), "0"), 1));
+tg.config.add_word("DCU.CH1_INVERT_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_INVERT_TX"), "0"), 1));
+tg.config.add_word("DCU.CH1_LDR_CORE2TX_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_LDR_CORE2TX_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_LDR_RX2CORE_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_LDR_RX2CORE_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_LEQ_OFFSET_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_LEQ_OFFSET_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_LEQ_OFFSET_TRIM", parse_config_str(str_or_default(ci->params, ctx->id("CH1_LEQ_OFFSET_TRIM"), "0"), 3));
+tg.config.add_word("DCU.CH1_LSM_DISABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_LSM_DISABLE"), "0"), 1));
+tg.config.add_word("DCU.CH1_MATCH_2_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_MATCH_2_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH1_MATCH_4_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_MATCH_4_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH1_MIN_IPG_CNT", parse_config_str(str_or_default(ci->params, ctx->id("CH1_MIN_IPG_CNT"), "0"), 2));
+tg.config.add_word("DCU.CH1_PCIE_EI_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PCIE_EI_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_PCIE_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PCIE_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_PCS_DET_TIME_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PCS_DET_TIME_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_PDEN_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PDEN_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_PRBS_ENABLE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PRBS_ENABLE"), "0"), 1));
+tg.config.add_word("DCU.CH1_PRBS_LOCK", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PRBS_LOCK"), "0"), 1));
+tg.config.add_word("DCU.CH1_PRBS_SELECTION", parse_config_str(str_or_default(ci->params, ctx->id("CH1_PRBS_SELECTION"), "0"), 1));
+tg.config.add_word("DCU.CH1_RATE_MODE_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RATE_MODE_RX"), "0"), 1));
+tg.config.add_word("DCU.CH1_RATE_MODE_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RATE_MODE_TX"), "0"), 1));
+tg.config.add_word("DCU.CH1_RCV_DCC_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RCV_DCC_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_REG_BAND_OFFSET", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REG_BAND_OFFSET"), "0"), 4));
+tg.config.add_word("DCU.CH1_REG_BAND_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REG_BAND_SEL"), "0"), 6));
+tg.config.add_word("DCU.CH1_REG_IDAC_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REG_IDAC_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_REG_IDAC_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REG_IDAC_SEL"), "0"), 10));
+tg.config.add_word("DCU.CH1_REQ_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REQ_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_REQ_LVL_SET", parse_config_str(str_or_default(ci->params, ctx->id("CH1_REQ_LVL_SET"), "0"), 2));
+tg.config.add_word("DCU.CH1_RIO_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RIO_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_RLOS_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RLOS_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_RPWDNB", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RPWDNB"), "0"), 1));
+tg.config.add_word("DCU.CH1_RTERM_RX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RTERM_RX"), "0"), 5));
+tg.config.add_word("DCU.CH1_RTERM_TX", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RTERM_TX"), "0"), 5));
+tg.config.add_word("DCU.CH1_RXIN_CM", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RXIN_CM"), "0"), 2));
+tg.config.add_word("DCU.CH1_RXTERM_CM", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RXTERM_CM"), "0"), 2));
+tg.config.add_word("DCU.CH1_RX_DCO_CK_DIV", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_DCO_CK_DIV"), "0"), 3));
+tg.config.add_word("DCU.CH1_RX_DIV11_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_DIV11_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_RX_GEAR_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_GEAR_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_RX_GEAR_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_GEAR_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_RX_LOS_CEQ", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_LOS_CEQ"), "0"), 2));
+tg.config.add_word("DCU.CH1_RX_LOS_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_LOS_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_RX_LOS_HYST_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_LOS_HYST_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_RX_LOS_LVL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_LOS_LVL"), "0"), 3));
+tg.config.add_word("DCU.CH1_RX_RATE_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_RATE_SEL"), "0"), 4));
+tg.config.add_word("DCU.CH1_RX_SB_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_RX_SB_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_SB_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_SB_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_SEL_SD_RX_CLK", parse_config_str(str_or_default(ci->params, ctx->id("CH1_SEL_SD_RX_CLK"), "0"), 1));
+tg.config.add_word("DCU.CH1_TDRV_DAT_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_DAT_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_POST_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_POST_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_TDRV_PRE_EN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_PRE_EN"), "0"), 1));
+tg.config.add_word("DCU.CH1_TDRV_SLICE0_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE0_CUR"), "0"), 3));
+tg.config.add_word("DCU.CH1_TDRV_SLICE0_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE0_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE1_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE1_CUR"), "0"), 3));
+tg.config.add_word("DCU.CH1_TDRV_SLICE1_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE1_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE2_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE2_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE2_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE2_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE3_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE3_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE3_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE3_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE4_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE4_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE4_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE4_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE5_CUR", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE5_CUR"), "0"), 2));
+tg.config.add_word("DCU.CH1_TDRV_SLICE5_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TDRV_SLICE5_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TPWDNB", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TPWDNB"), "0"), 1));
+tg.config.add_word("DCU.CH1_TX_CM_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_CM_SEL"), "0"), 2));
+tg.config.add_word("DCU.CH1_TX_DIV11_SEL", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_DIV11_SEL"), "0"), 1));
+tg.config.add_word("DCU.CH1_TX_GEAR_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_GEAR_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_TX_GEAR_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_GEAR_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_TX_POST_SIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_POST_SIGN"), "0"), 1));
+tg.config.add_word("DCU.CH1_TX_PRE_SIGN", parse_config_str(str_or_default(ci->params, ctx->id("CH1_TX_PRE_SIGN"), "0"), 1));
+tg.config.add_word("DCU.CH1_UC_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_UC_MODE"), "0"), 1));
+tg.config.add_word("DCU.CH1_UDF_COMMA_A", parse_config_str(str_or_default(ci->params, ctx->id("CH1_UDF_COMMA_A"), "0"), 10));
+tg.config.add_word("DCU.CH1_UDF_COMMA_B", parse_config_str(str_or_default(ci->params, ctx->id("CH1_UDF_COMMA_B"), "0"), 10));
+tg.config.add_word("DCU.CH1_UDF_COMMA_MASK", parse_config_str(str_or_default(ci->params, ctx->id("CH1_UDF_COMMA_MASK"), "0"), 10));
+tg.config.add_word("DCU.CH1_WA_BYPASS", parse_config_str(str_or_default(ci->params, ctx->id("CH1_WA_BYPASS"), "0"), 1));
+tg.config.add_word("DCU.CH1_WA_MODE", parse_config_str(str_or_default(ci->params, ctx->id("CH1_WA_MODE"), "0"), 1));
+tg.config.add_word("DCU.D_BITCLK_FROM_ND_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_BITCLK_FROM_ND_EN"), "0"), 1));
+tg.config.add_word("DCU.D_BITCLK_LOCAL_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_BITCLK_LOCAL_EN"), "0"), 1));
+tg.config.add_word("DCU.D_BITCLK_ND_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_BITCLK_ND_EN"), "0"), 1));
+tg.config.add_word("DCU.D_BUS8BIT_SEL", parse_config_str(str_or_default(ci->params, ctx->id("D_BUS8BIT_SEL"), "0"), 1));
+tg.config.add_word("DCU.D_CDR_LOL_SET", parse_config_str(str_or_default(ci->params, ctx->id("D_CDR_LOL_SET"), "0"), 2));
+tg.config.add_word("DCU.D_CMUSETBIASI", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETBIASI"), "0"), 2));
+tg.config.add_word("DCU.D_CMUSETI4CPP", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETI4CPP"), "0"), 4));
+tg.config.add_word("DCU.D_CMUSETI4CPZ", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETI4CPZ"), "0"), 4));
+tg.config.add_word("DCU.D_CMUSETI4VCO", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETI4VCO"), "0"), 2));
+tg.config.add_word("DCU.D_CMUSETICP4P", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETICP4P"), "0"), 2));
+tg.config.add_word("DCU.D_CMUSETICP4Z", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETICP4Z"), "0"), 3));
+tg.config.add_word("DCU.D_CMUSETINITVCT", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETINITVCT"), "0"), 2));
+tg.config.add_word("DCU.D_CMUSETISCL4VCO", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETISCL4VCO"), "0"), 3));
+tg.config.add_word("DCU.D_CMUSETP1GM", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETP1GM"), "0"), 3));
+tg.config.add_word("DCU.D_CMUSETP2AGM", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETP2AGM"), "0"), 3));
+tg.config.add_word("DCU.D_CMUSETZGM", parse_config_str(str_or_default(ci->params, ctx->id("D_CMUSETZGM"), "0"), 3));
+tg.config.add_word("DCU.D_DCO_CALIB_TIME_SEL", parse_config_str(str_or_default(ci->params, ctx->id("D_DCO_CALIB_TIME_SEL"), "0"), 2));
+tg.config.add_word("DCU.D_HIGH_MARK", parse_config_str(str_or_default(ci->params, ctx->id("D_HIGH_MARK"), "0"), 4));
+tg.config.add_word("DCU.D_IB_PWDNB", parse_config_str(str_or_default(ci->params, ctx->id("D_IB_PWDNB"), "0"), 1));
+tg.config.add_word("DCU.D_ISETLOS", parse_config_str(str_or_default(ci->params, ctx->id("D_ISETLOS"), "0"), 8));
+tg.config.add_word("DCU.D_LOW_MARK", parse_config_str(str_or_default(ci->params, ctx->id("D_LOW_MARK"), "0"), 4));
+tg.config.add_word("DCU.D_MACROPDB", parse_config_str(str_or_default(ci->params, ctx->id("D_MACROPDB"), "0"), 1));
+tg.config.add_word("DCU.D_PD_ISET", parse_config_str(str_or_default(ci->params, ctx->id("D_PD_ISET"), "0"), 2));
+tg.config.add_word("DCU.D_PLL_LOL_SET", parse_config_str(str_or_default(ci->params, ctx->id("D_PLL_LOL_SET"), "0"), 2));
+tg.config.add_word("DCU.D_REFCK_MODE", parse_config_str(str_or_default(ci->params, ctx->id("D_REFCK_MODE"), "0"), 3));
+tg.config.add_word("DCU.D_REQ_ISET", parse_config_str(str_or_default(ci->params, ctx->id("D_REQ_ISET"), "0"), 3));
+tg.config.add_word("DCU.D_RG_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_RG_EN"), "0"), 1));
+tg.config.add_word("DCU.D_RG_SET", parse_config_str(str_or_default(ci->params, ctx->id("D_RG_SET"), "0"), 2));
+tg.config.add_word("DCU.D_SETICONST_AUX", parse_config_str(str_or_default(ci->params, ctx->id("D_SETICONST_AUX"), "0"), 2));
+tg.config.add_word("DCU.D_SETICONST_CH", parse_config_str(str_or_default(ci->params, ctx->id("D_SETICONST_CH"), "0"), 2));
+tg.config.add_word("DCU.D_SETIRPOLY_AUX", parse_config_str(str_or_default(ci->params, ctx->id("D_SETIRPOLY_AUX"), "0"), 2));
+tg.config.add_word("DCU.D_SETIRPOLY_CH", parse_config_str(str_or_default(ci->params, ctx->id("D_SETIRPOLY_CH"), "0"), 2));
+tg.config.add_word("DCU.D_SETPLLRC", parse_config_str(str_or_default(ci->params, ctx->id("D_SETPLLRC"), "0"), 6));
+tg.config.add_word("DCU.D_SYNC_LOCAL_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_SYNC_LOCAL_EN"), "0"), 1));
+tg.config.add_word("DCU.D_SYNC_ND_EN", parse_config_str(str_or_default(ci->params, ctx->id("D_SYNC_ND_EN"), "0"), 1));
+tg.config.add_word("DCU.D_TXPLL_PWDNB", parse_config_str(str_or_default(ci->params, ctx->id("D_TXPLL_PWDNB"), "0"), 1));
+tg.config.add_word("DCU.D_TX_VCO_CK_DIV", parse_config_str(str_or_default(ci->params, ctx->id("D_TX_VCO_CK_DIV"), "0"), 3));
+tg.config.add_word("DCU.D_XGE_MODE", parse_config_str(str_or_default(ci->params, ctx->id("D_XGE_MODE"), "0"), 1));
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index 06412fef..66c62024 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -55,6 +55,9 @@ class Ecp5GlobalRouter
{
if (user.cell->type == id_TRELLIS_SLICE && (user.port == id_CLK || user.port == id_WCK))
return true;
+ if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK ||
+ user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK))
+ return true;
return false;
}
@@ -65,8 +68,11 @@ class Ecp5GlobalRouter
NetInfo *ni = net.second.get();
clockCount[ni->name] = 0;
for (const auto &user : ni->users) {
- if (is_clock_port(user))
+ if (is_clock_port(user)) {
clockCount[ni->name]++;
+ if (user.cell->type == id_DCUA)
+ clockCount[ni->name] += 100;
+ }
}
// log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]);
}
@@ -290,6 +296,10 @@ class Ecp5GlobalRouter
float tns;
return get_net_metric(ctx, clki, MetricType::WIRELENGTH, tns);
} else {
+ // Check for dedicated routing
+ if (has_short_route(ctx->getBelPinWire(drv_bel, drv.port), ctx->getBelPinWire(dcc->bel, id_CLKI))) {
+ return 0;
+ }
// Driver is locked
Loc dcc_loc = ctx->getBelLocation(dcc->bel);
Loc drv_loc = ctx->getBelLocation(drv_bel);
@@ -297,6 +307,46 @@ class Ecp5GlobalRouter
}
}
+ // Return true if a short (<5) route exists between two wires
+ bool has_short_route(WireId src, WireId dst, int thresh = 5)
+ {
+ std::queue<WireId> visit;
+ std::unordered_map<WireId, PipId> backtrace;
+ visit.push(src);
+ WireId cursor;
+ while (true) {
+
+ if (visit.empty() || visit.size() > 1000) {
+ // log_info ("dist %s -> %s = inf\n", ctx->getWireName(src).c_str(ctx),
+ // ctx->getWireName(dst).c_str(ctx));
+ return false;
+ }
+ cursor = visit.front();
+ visit.pop();
+
+ if (cursor == dst)
+ break;
+ for (auto dh : ctx->getPipsDownhill(cursor)) {
+ WireId pipDst = ctx->getPipDstWire(dh);
+ if (backtrace.count(pipDst))
+ continue;
+ backtrace[pipDst] = dh;
+ visit.push(pipDst);
+ }
+ }
+ int length = 0;
+ while (true) {
+ auto fnd = backtrace.find(cursor);
+ if (fnd == backtrace.end())
+ break;
+ cursor = ctx->getPipSrcWire(fnd->second);
+ length++;
+ }
+ // log_info ("dist %s -> %s = %d\n", ctx->getWireName(src).c_str(ctx), ctx->getWireName(dst).c_str(ctx),
+ // length);
+ return length < thresh;
+ }
+
// Attempt to place a DCC
void place_dcc(CellInfo *dcc)
{
@@ -335,6 +385,8 @@ class Ecp5GlobalRouter
for (auto user : net->users) {
if (user.port == id_CLKFB) {
keep_users.push_back(user);
+ } else if (net->driver.cell->type == id_EXTREFB && user.cell->type == id_DCUA) {
+ keep_users.push_back(user);
} else {
glbnet->users.push_back(user);
user.cell->ports.at(user.port).net = glbnet.get();
@@ -350,6 +402,13 @@ class Ecp5GlobalRouter
place_dcc(dcc.get());
+ if (net->clkconstr) {
+ glbnet->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
+ glbnet->clkconstr->low = net->clkconstr->low;
+ glbnet->clkconstr->high = net->clkconstr->high;
+ glbnet->clkconstr->period = net->clkconstr->period;
+ }
+
ctx->cells[dcc->name] = std::move(dcc);
NetInfo *glbptr = glbnet.get();
ctx->nets[glbnet->name] = std::move(glbnet);
diff --git a/ecp5/main.cc b/ecp5/main.cc
index c444f96f..cc004df3 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -40,7 +40,7 @@ class ECP5CommandHandler : public CommandHandler
void customBitstream(Context *ctx) override;
protected:
- po::options_description getArchOptions();
+ po::options_description getArchOptions() override;
};
ECP5CommandHandler::ECP5CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 73e15609..2d2f7578 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -235,6 +235,46 @@ class Ecp5Packer
}
}
+ // Return true if an port is a top level port that provides its own IOBUF
+ bool is_top_port(PortRef &port)
+ {
+ if (port.cell == nullptr)
+ return false;
+ if (port.cell->type == id_DCUA) {
+ return port.port == id_CH0_HDINP || port.port == id_CH0_HDINN || port.port == id_CH0_HDOUTP ||
+ port.port == id_CH0_HDOUTN || port.port == id_CH1_HDINP || port.port == id_CH1_HDINN ||
+ port.port == id_CH1_HDOUTP || port.port == id_CH1_HDOUTN;
+ } else if (port.cell->type == id_EXTREFB) {
+ return port.port == id_REFCLKP || port.port == id_REFCLKN;
+ } else {
+ return false;
+ }
+ }
+
+ // Return true if a net only drives a top port
+ bool drives_top_port(NetInfo *net, PortRef &tp)
+ {
+ if (net == nullptr)
+ return false;
+ for (auto user : net->users) {
+ if (is_top_port(user)) {
+ if (net->users.size() > 1)
+ log_error(" port %s.%s must be connected to (and only to) a top level pin\n",
+ user.cell->name.c_str(ctx), user.port.c_str(ctx));
+ tp = user;
+ return true;
+ }
+ }
+ if (net->driver.cell != nullptr && is_top_port(net->driver)) {
+ if (net->users.size() > 1)
+ log_error(" port %s.%s must be connected to (and only to) a top level pin\n",
+ net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
+ tp = net->driver;
+ return true;
+ }
+ return false;
+ }
+
// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
void pack_io()
{
@@ -244,10 +284,14 @@ class Ecp5Packer
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
CellInfo *trio = nullptr;
+ NetInfo *ionet = nullptr;
+ PortRef tp;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
- trio = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_trellis_io, ctx->id("B"), true, ci);
+ ionet = ci->ports.at(ctx->id("O")).net;
+ trio = net_only_drives(ctx, ionet, is_trellis_io, ctx->id("B"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
+ ionet = ci->ports.at(ctx->id("I")).net;
trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci);
}
if (trio != nullptr) {
@@ -266,6 +310,19 @@ class Ecp5Packer
ctx->nets.erase(net2->name);
}
}
+ } else if (drives_top_port(ionet, tp)) {
+ log_info("%s feeds %s %s.%s, removing %s %s.\n", ci->name.c_str(ctx), tp.cell->type.c_str(ctx),
+ tp.cell->name.c_str(ctx), tp.port.c_str(ctx), ci->type.c_str(ctx), ci->name.c_str(ctx));
+ if (ionet != nullptr) {
+ ctx->nets.erase(ionet->name);
+ tp.cell->ports.at(tp.port).net = nullptr;
+ }
+ if (ci->type == ctx->id("$nextpnr_iobuf")) {
+ NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
+ if (net2 != nullptr) {
+ ctx->nets.erase(net2->name);
+ }
+ }
} else {
// Create a TRELLIS_IO buffer
std::unique_ptr<CellInfo> tr_cell =
@@ -276,21 +333,23 @@ class Ecp5Packer
}
packed_cells.insert(ci->name);
- for (const auto &attr : ci->attrs)
- trio->attrs[attr.first] = attr.second;
-
- auto loc_attr = trio->attrs.find(ctx->id("LOC"));
- if (loc_attr != trio->attrs.end()) {
- std::string pin = loc_attr->second;
- BelId pinBel = ctx->getPackagePinBel(pin);
- if (pinBel == BelId()) {
- log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
- trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
- } else {
- log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
- ctx->getBelName(pinBel).c_str(ctx));
+ if (trio != nullptr) {
+ for (const auto &attr : ci->attrs)
+ trio->attrs[attr.first] = attr.second;
+
+ auto loc_attr = trio->attrs.find(ctx->id("LOC"));
+ if (loc_attr != trio->attrs.end()) {
+ std::string pin = loc_attr->second;
+ BelId pinBel = ctx->getPackagePinBel(pin);
+ if (pinBel == BelId()) {
+ log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
+ trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
+ } else {
+ log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
+ ctx->getBelName(pinBel).c_str(ctx));
+ }
+ trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
- trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
}
}
@@ -1033,12 +1092,93 @@ class Ecp5Packer
}
}
+ // "Pack" DCUs
+ void pack_dcus()
+ {
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type == id_DCUA) {
+ if (ci->attrs.count(ctx->id("LOC"))) {
+ std::string loc = ci->attrs.at(ctx->id("LOC"));
+ if (loc == "DCU0" &&
+ (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM5G_25F))
+ ci->attrs[ctx->id("BEL")] = "X42/Y50/DCU";
+ else if (loc == "DCU0" &&
+ (ctx->args.type == ArchArgs::LFE5UM_45F || ctx->args.type == ArchArgs::LFE5UM5G_45F))
+ ci->attrs[ctx->id("BEL")] = "X42/Y71/DCU";
+ else if (loc == "DCU1" &&
+ (ctx->args.type == ArchArgs::LFE5UM_45F || ctx->args.type == ArchArgs::LFE5UM5G_45F))
+ ci->attrs[ctx->id("BEL")] = "X69/Y71/DCU";
+ else if (loc == "DCU0" &&
+ (ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_85F))
+ ci->attrs[ctx->id("BEL")] = "X46/Y95/DCU";
+ else if (loc == "DCU1" &&
+ (ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_85F))
+ ci->attrs[ctx->id("BEL")] = "X71/Y95/DCU";
+ else
+ log_error("no DCU location '%s' in device '%s'\n", loc.c_str(), ctx->getChipName().c_str());
+ }
+ if (!ci->attrs.count(ctx->id("BEL")))
+ log_error("DCU must be constrained to a Bel!\n");
+ // Empty port auto-creation to generate correct tie-downs
+ BelId exemplar_bel;
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) == id_DCUA) {
+ exemplar_bel = bel;
+ break;
+ }
+ }
+ NPNR_ASSERT(exemplar_bel != BelId());
+ for (auto pin : ctx->getBelPins(exemplar_bel))
+ if (ctx->getBelPinType(exemplar_bel, pin) == PORT_IN)
+ autocreate_empty_port(ci, pin);
+ }
+ }
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type == id_EXTREFB) {
+ const NetInfo *refo = net_or_nullptr(ci, id_REFCLKO);
+ CellInfo *dcu = nullptr;
+ if (refo == nullptr)
+ log_error("EXTREFB REFCLKO must not be unconnected\n");
+ for (auto user : refo->users) {
+ if (user.cell->type != id_DCUA)
+ continue;
+ if (dcu != nullptr && dcu != user.cell)
+ log_error("EXTREFB REFCLKO must only drive a single DCUA\n");
+ dcu = user.cell;
+ }
+ if (!dcu->attrs.count(ctx->id("BEL")))
+ log_error("DCU must be constrained to a Bel!\n");
+ std::string bel = dcu->attrs.at(ctx->id("BEL"));
+ NPNR_ASSERT(bel.substr(bel.length() - 3) == "DCU");
+ bel.replace(bel.length() - 3, 3, "EXTREF");
+ ci->attrs[ctx->id("BEL")] = bel;
+ } else if (ci->type == id_PCSCLKDIV) {
+ const NetInfo *clki = net_or_nullptr(ci, id_CLKI);
+ if (clki != nullptr && clki->driver.cell != nullptr && clki->driver.cell->type == id_DCUA) {
+ CellInfo *dcu = clki->driver.cell;
+ if (!dcu->attrs.count(ctx->id("BEL")))
+ log_error("DCU must be constrained to a Bel!\n");
+ BelId bel = ctx->getBelByName(ctx->id(dcu->attrs.at(ctx->id("BEL"))));
+ if (bel == BelId())
+ log_error("Invalid DCU bel '%s'\n", dcu->attrs.at(ctx->id("BEL")).c_str());
+ Loc loc = ctx->getBelLocation(bel);
+ // DCU0 -> CLKDIV z=0; DCU1 -> CLKDIV z=1
+ ci->constr_abs_z = true;
+ ci->constr_z = (loc.x >= 69) ? 1 : 0;
+ }
+ }
+ }
+ }
+
public:
void pack()
{
pack_io();
pack_ebr();
pack_dsps();
+ pack_dcus();
pack_constants();
pack_dram();
pack_carries();
diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py
index 9a26b605..99fe7ba9 100755
--- a/ecp5/trellis_import.py
+++ b/ecp5/trellis_import.py
@@ -200,9 +200,14 @@ def write_database(dev_name, chip, ddrg, endianness):
write_loc(arc.sinkWire.rel, "dst")
bba.u32(arc.srcWire.id, "src_idx")
bba.u32(arc.sinkWire.id, "dst_idx")
- bba.u32(get_pip_delay(get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id), get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id)), "delay") # TODO:delay
+ src_name = get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id)
+ snk_name = get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id)
+ bba.u32(get_pip_delay(src_name, snk_name), "delay") # TODO:delay
bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type")
- bba.u8(int(arc.cls), "pip_type")
+ cls = arc.cls
+ if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name:
+ cls = 2
+ bba.u8(cls, "pip_type")
bba.u8(0, "padding")
if len(loctype.wires) > 0:
for wire_idx in range(len(loctype.wires)):
@@ -340,7 +345,7 @@ def write_database(dev_name, chip, ddrg, endianness):
bba.pop()
return bba
-dev_names = {"25k": "LFE5U-25F", "45k": "LFE5U-45F", "85k": "LFE5U-85F"}
+dev_names = {"25k": "LFE5UM5G-25F", "45k": "LFE5UM5G-45F", "85k": "LFE5UM5G-85F"}
def main():
global max_row, max_col