From cc9fb1497d070eafff678a092efc649c508b3b90 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 11:00:57 +0000 Subject: [PATCH 01/16] ecp5: Groundwork for DCU support Signed-off-by: David Shah --- ecp5/bitstream.cc | 23 ++-- ecp5/constids.inc | 300 +++++++++++++++++++++++++++++++++++++++++ ecp5/trellis_import.py | 11 +- 3 files changed, 318 insertions(+), 16 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 6d43b369..a48b7793 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -414,20 +414,17 @@ void fix_tile_names(Context *ctx, ChipConfig &cc) std::map 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 + newname.insert(cibdcu, 1, 'V'); + tiletype_xform[tile.first] = newname; + } else if (tile.first.substr(tile.first.size() - 7) == "BMID_0H") { + newname.back() = 'V'; + tiletype_xform[tile.first] = newname; + } else if (tile.first.substr(tile.first.size() - 6) == "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 diff --git a/ecp5/constids.inc b/ecp5/constids.inc index bdcbc1ea..4f5c3ef3 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -810,3 +810,303 @@ 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) 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 From 983903887d1f6b3be2060fdfdd680b67006b8e08 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 11:12:21 +0000 Subject: [PATCH 02/16] ecp5: DCU bitstream gen handling Signed-off-by: David Shah --- ecp5/bitstream.cc | 45 ++++++++ ecp5/dcu_bitstream.h | 254 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 ecp5/dcu_bitstream.h diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index a48b7793..53d4eb4f 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -379,6 +379,16 @@ std::vector get_dsp_tiles(Context *ctx, BelId bel) return tiles; } +// Get the list of tiles corresponding to a DCU +std::vector get_dcu_tiles(Context *ctx, BelId bel) +{ + std::vector 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 get_pll_tiles(Context *ctx, BelId bel) { @@ -453,6 +463,19 @@ 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); + 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); @@ -478,6 +501,21 @@ 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(); + } + } + } + } // Add all set, configurable pips to the config for (auto pip : ctx->getPips()) { if (ctx->getBoundPipNet(pip) != nullptr) { @@ -997,6 +1035,13 @@ 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 { NPNR_ASSERT_FALSE("unsupported cell type"); } 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)); From c5a3571a061b1340355bb758be71a86922ee863f Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 14:24:58 +0000 Subject: [PATCH 03/16] ecp5: Working on DCU Signed-off-by: David Shah --- ecp5/arch.cc | 2 ++ ecp5/bitstream.cc | 44 +++++++++++++++++++++++++++++++++++++++----- ecp5/pack.cc | 22 ++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index fe6a9545..93ed5788 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -651,6 +651,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; // FIXME } else if (cell->type == id_EHXPLLL) { return TMG_IGNORE; + } else if (cell->type == id_DCUA) { + return TMG_IGNORE; // FIXME } else { NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'"); } diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 53d4eb4f..4fee6802 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -91,6 +91,7 @@ static void tie_cib_signal(Context *ctx, ChipConfig &cc, WireId wire, bool value NPNR_ASSERT(signals.size() < 100); cibsig = signals.front(); basename = ctx->getWireBasename(cibsig).str(ctx); + log_info("%s\n", basename.c_str()); signals.pop(); if (std::regex_match(basename, cib_re)) break; @@ -418,9 +419,8 @@ std::vector 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 tiletype_xform; for (const auto &tile : cc.tiles) { std::string newname = tile.first; @@ -467,7 +467,7 @@ 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); + 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); @@ -484,6 +484,38 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) cc.tiles[tile].add_arc(sink, source); } +static std::vector 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 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'); + } + } 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; + } + return word; +} + void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file) { ChipConfig cc; @@ -510,8 +542,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex 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()) + if (cc_tile != cc.tiles.end()) { cc_tile->second.cenums.clear(); + cc_tile->second.cunknowns.clear(); + } } } } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 73e15609..4f1c7f79 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1033,12 +1033,34 @@ class Ecp5Packer } } + // "Pack" DCUs + void pack_dcus() { + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (ci->type == id_DCUA) { + // 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); + } + } + } + public: void pack() { pack_io(); pack_ebr(); pack_dsps(); + pack_dcus(); pack_constants(); pack_dram(); pack_carries(); From 4f8dfd8e1b5276337f46a1e21bc9b0075450d10b Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 14:36:42 +0000 Subject: [PATCH 04/16] ecp5: Prefer DCCs with dedicated routing when placing DCCs Signed-off-by: David Shah --- ecp5/globals.cc | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 9b0928a4..f2a556fa 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -290,6 +290,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 +301,43 @@ 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 visit; + std::unordered_map 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) { @@ -319,6 +360,8 @@ class Ecp5GlobalRouter ctx->bindBel(best_bel, dcc, STRENGTH_LOCKED); } + + // Insert a DCC into a net to promote it to a global NetInfo *insert_dcc(NetInfo *net) { From c9d83ec08b753cc8e110bb36f2f529bfdafa293f Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 14:55:35 +0000 Subject: [PATCH 05/16] dcu: Fix bitstream param handling Signed-off-by: David Shah --- ecp5/bitstream.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 4fee6802..c5eca14f 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -493,6 +493,7 @@ static std::vector parse_config_str(std::string str, int length) { 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++) { From 37cbabecfbd22119ad5ba0adfc4d7011831a9af4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 15:08:47 +0000 Subject: [PATCH 06/16] ecp5: remove debug and clangformat Signed-off-by: David Shah --- ecp5/bitstream.cc | 9 +++++---- ecp5/globals.cc | 11 ++++++----- ecp5/pack.cc | 3 ++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index c5eca14f..00486e39 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -91,7 +91,6 @@ static void tie_cib_signal(Context *ctx, ChipConfig &cc, WireId wire, bool value NPNR_ASSERT(signals.size() < 100); cibsig = signals.front(); basename = ctx->getWireBasename(cibsig).str(ctx); - log_info("%s\n", basename.c_str()); signals.pop(); if (std::regex_match(basename, cib_re)) break; @@ -467,7 +466,9 @@ 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) + 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); @@ -475,7 +476,6 @@ void tieoff_dcu_ports(Context *ctx, ChipConfig &cc, CellInfo *ci) } } - static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) { std::string tile = ctx->getPipTilename(pip); @@ -484,7 +484,8 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) cc.tiles[tile].add_arc(sink, source); } -static std::vector parse_config_str(std::string str, int length) { +static std::vector 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 word; diff --git a/ecp5/globals.cc b/ecp5/globals.cc index f2a556fa..75535dfe 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -302,7 +302,8 @@ class Ecp5GlobalRouter } // Return true if a short (<5) route exists between two wires - bool has_short_route(WireId src, WireId dst, int thresh = 5) { + bool has_short_route(WireId src, WireId dst, int thresh = 5) + { std::queue visit; std::unordered_map backtrace; visit.push(src); @@ -310,7 +311,8 @@ class Ecp5GlobalRouter 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)); + // log_info ("dist %s -> %s = inf\n", ctx->getWireName(src).c_str(ctx), + // ctx->getWireName(dst).c_str(ctx)); return false; } cursor = visit.front(); @@ -334,7 +336,8 @@ class Ecp5GlobalRouter 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); + // log_info ("dist %s -> %s = %d\n", ctx->getWireName(src).c_str(ctx), ctx->getWireName(dst).c_str(ctx), + // length); return length < thresh; } @@ -360,8 +363,6 @@ class Ecp5GlobalRouter ctx->bindBel(best_bel, dcc, STRENGTH_LOCKED); } - - // Insert a DCC into a net to promote it to a global NetInfo *insert_dcc(NetInfo *net) { diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 4f1c7f79..ae416a7b 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1034,7 +1034,8 @@ class Ecp5Packer } // "Pack" DCUs - void pack_dcus() { + void pack_dcus() + { for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (ci->type == id_DCUA) { From e9fe444dc7dd5bf09f50ef6c742a637f6bc24f41 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 7 Nov 2018 20:44:43 +0000 Subject: [PATCH 07/16] ecp5: Adding ancillary DCU bels Signed-off-by: David Shah --- ecp5/arch.cc | 2 +- ecp5/bitstream.cc | 17 +++++++++++++++++ ecp5/constids.inc | 7 +++++++ ecp5/pack.cc | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 93ed5788..a0d8e8ae 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -651,7 +651,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; // FIXME } else if (cell->type == id_EHXPLLL) { return TMG_IGNORE; - } else if (cell->type == id_DCUA) { + } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) { return TMG_IGNORE; // FIXME } else { NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'"); diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 00486e39..2df0ed0b 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -514,6 +514,12 @@ static std::vector parse_config_str(std::string str, int length) 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; } @@ -1078,6 +1084,17 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex #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 4f5c3ef3..11ecc240 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1110,3 +1110,10 @@ 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/pack.cc b/ecp5/pack.cc index ae416a7b..66428e95 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1039,6 +1039,8 @@ class Ecp5Packer for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (ci->type == id_DCUA) { + 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()) { @@ -1051,6 +1053,36 @@ class Ecp5Packer for (auto pin : ctx->getBelPins(exemplar_bel)) if (ctx->getBelPinType(exemplar_bel, pin) == PORT_IN) autocreate_empty_port(ci, pin); + } else 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 || (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; + } } } } From 36178a571354302cfb3166f7bf83f91cf754ef70 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 9 Nov 2018 15:17:26 +0000 Subject: [PATCH 08/16] ecp5: Trim IO connected to top level ports Signed-off-by: David Shah --- ecp5/pack.cc | 86 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 66428e95..ab31a8f3 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -235,6 +235,43 @@ 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 +281,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 +307,20 @@ 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 tr_cell = @@ -276,22 +331,25 @@ class Ecp5Packer } packed_cells.insert(ci->name); - for (const auto &attr : ci->attrs) - trio->attrs[attr.first] = attr.second; + 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)); + 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); } + } } flush_cells(); From bc022173f01794ea67809f2d7cdc54c3c3d6696f Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 9 Nov 2018 15:17:37 +0000 Subject: [PATCH 09/16] ecp5: clangformat Signed-off-by: David Shah --- ecp5/bitstream.cc | 14 +++++++++----- ecp5/pack.cc | 27 ++++++++++++++------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 2df0ed0b..5af051c7 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -1087,14 +1087,18 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } 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)); + 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")); + 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/pack.cc b/ecp5/pack.cc index ab31a8f3..fdf04d4e 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -236,14 +236,14 @@ class Ecp5Packer } // Return true if an port is a top level port that provides its own IOBUF - bool is_top_port(PortRef &port) { + 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; + 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 { @@ -252,20 +252,23 @@ class Ecp5Packer } // Return true if a net only drives a top port - bool drives_top_port(NetInfo *net, PortRef &tp) { + 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)); + 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)); + 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; } @@ -308,9 +311,8 @@ class Ecp5Packer } } } 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)); + 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; @@ -349,7 +351,6 @@ class Ecp5Packer trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx); } } - } } flush_cells(); @@ -1123,7 +1124,7 @@ class Ecp5Packer } 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")); + 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; @@ -1139,7 +1140,7 @@ class Ecp5Packer 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; + ci->constr_z = (loc.x >= 69) ? 1 : 0; } } } From 0eba7d9789b0e9fa10c33349928608f59dbf9488 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 9 Nov 2018 18:18:50 +0000 Subject: [PATCH 10/16] ecp5: EXTREFB fixes Signed-off-by: David Shah --- ecp5/globals.cc | 2 ++ ecp5/pack.cc | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 75535dfe..c6cc7e57 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -379,6 +379,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_DCCA) { + keep_users.push_back(user); } else { glbnet->users.push_back(user); user.cell->ports.at(user.port).net = glbnet.get(); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index fdf04d4e..82e2888a 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1118,7 +1118,9 @@ class Ecp5Packer if (refo == nullptr) log_error("EXTREFB REFCLKO must not be unconnected\n"); for (auto user : refo->users) { - if (user.cell->type != id_DCUA || (dcu != nullptr && dcu != user.cell)) + 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; } From 084f9cf63f148a8af991c5827a25d239709ff0e6 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 11 Nov 2018 10:22:57 +0000 Subject: [PATCH 11/16] ecp5: DCU clocking fixes Signed-off-by: David Shah --- ecp5/globals.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index c6cc7e57..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]); } @@ -379,7 +385,7 @@ 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_DCCA) { + } else if (net->driver.cell->type == id_EXTREFB && user.cell->type == id_DCUA) { keep_users.push_back(user); } else { glbnet->users.push_back(user); From 02736d06800dbbab6eef9d2660e9d481874dd7ae Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 11 Nov 2018 10:46:07 +0000 Subject: [PATCH 12/16] ecp5: Add timing info for SERDES Signed-off-by: David Shah --- ecp5/arch.cc | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index a0d8e8ae..afea8d4a 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -652,7 +652,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (cell->type == id_EHXPLLL) { return TMG_IGNORE; } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) { - return TMG_IGNORE; // FIXME + 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) + "'"); } @@ -708,6 +716,23 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port 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; } From 01e0da16f0cb8fffb78b476b912edbd676106b04 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 11 Nov 2018 15:36:00 +0000 Subject: [PATCH 13/16] ecp5: Add DCU availability check Signed-off-by: David Shah --- ecp5/arch_place.cc | 2 ++ 1 file changed, 2 insertions(+) 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; From 91a09271965cf9652fd823d6df59051f35e0213d Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 11 Nov 2018 15:50:56 +0000 Subject: [PATCH 14/16] ecp5: Support LOC attribute on DCUs Signed-off-by: David Shah --- ecp5/pack.cc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 82e2888a..2d2f7578 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1098,6 +1098,26 @@ class Ecp5Packer 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 @@ -1112,7 +1132,11 @@ class Ecp5Packer for (auto pin : ctx->getBelPins(exemplar_bel)) if (ctx->getBelPinType(exemplar_bel, pin) == PORT_IN) autocreate_empty_port(ci, pin); - } else if (ci->type == id_EXTREFB) { + } + } + 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) From 7e1df8246241148acbe8a5bd74d588d9b493807a Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 15 Nov 2018 11:54:28 +0000 Subject: [PATCH 15/16] ecp5: Regression fix & format Signed-off-by: David Shah --- ecp5/archdefs.h | 15 ++++++++++++--- ecp5/bitstream.cc | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 01cbad46..b2c23134 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -87,7 +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; } + bool operator<(const BelId &other) const + { + return location == other.location ? index < other.index : location < other.location; + } }; struct WireId @@ -97,7 +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; } + bool operator<(const WireId &other) const + { + return location == other.location ? index < other.index : location < other.location; + } }; struct PipId @@ -107,7 +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; } + 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 5af051c7..b19bd1c6 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -426,7 +426,8 @@ void fix_tile_names(Context *ctx, ChipConfig &cc) auto cibdcu = tile.first.find("CIB_DCU"); if (cibdcu != std::string::npos) { // Add the V - newname.insert(cibdcu, 1, 'V'); + if (newname.at(cibdcu - 1) != 'V') + newname.insert(cibdcu, 1, 'V'); tiletype_xform[tile.first] = newname; } else if (tile.first.substr(tile.first.size() - 7) == "BMID_0H") { newname.back() = 'V'; From f07bd98d59765f2c565815d1a6483db04c57a9d1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 16 Nov 2018 09:58:18 +0000 Subject: [PATCH 16/16] ecp5: Better use of Boost Signed-off-by: David Shah --- ecp5/bitstream.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index b19bd1c6..5e3ff694 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -24,7 +24,7 @@ #include #include #include - +#include #include "config.h" #include "io.h" #include "log.h" @@ -429,10 +429,10 @@ void fix_tile_names(Context *ctx, ChipConfig &cc) if (newname.at(cibdcu - 1) != 'V') newname.insert(cibdcu, 1, 'V'); tiletype_xform[tile.first] = newname; - } else if (tile.first.substr(tile.first.size() - 7) == "BMID_0H") { + } else if (boost::ends_with(tile.first, "BMID_0H")) { newname.back() = 'V'; tiletype_xform[tile.first] = newname; - } else if (tile.first.substr(tile.first.size() - 6) == "BMID_2") { + } else if (boost::ends_with(tile.first, "BMID_2")) { newname.push_back('V'); tiletype_xform[tile.first] = newname; }