diff --git a/nexus/constids.inc b/nexus/constids.inc index 380d2112..8b30137e 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -560,6 +560,27 @@ X(RSTSMCLK) X(CONFIG_CLKRST_CORE) +X(DELAYA) +X(DELAYB) +X(COARSE0) +X(COARSE1) +X(CIBCRS0) +X(CIBCRS1) +X(DIRECTION) +X(DIR) +X(DEL_MODE) +X(DEL_VALUE) +X(COARSE_DELAY) +X(COARSE_DELAY_MODE) +X(EDGE_MONITOR) +X(WAIT_FOR_EDGE) +X(DELAYMUX) +X(INMUX) +X(OUTMUX) +X(DELAY) +X(OUT_REG) +X(CFLAG) + X(BEL_TYPE) X(BEL_Z) X(CEOUTMUX) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 02813aa9..b789e6a2 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -660,6 +660,17 @@ struct NexusFasmWriter write_enum(cell, "IDDRX1_ODDRX1.TRISTATE"); write_enum(cell, "GSR", "DISABLED"); write_enum(cell, "TSREG.REGSET", "RESET"); + if (cell->params.count(ctx->id("DELAY.DEL_VALUE"))) { + write_int_vector(stringf("DELAY.DEL_VALUE[6:0]"), + int_or_default(cell->params, ctx->id("DELAY.DEL_VALUE"), 0), 7); + } + write_enum(cell, "DELAY.COARSE_DELAY"); + write_enum(cell, "DELAY.EDGE_MONITOR"); + write_enum(cell, "DELAY.WAIT_FOR_EDGE"); + write_enum(cell, "DELAYMUX"); + write_enum(cell, "INMUX"); + write_enum(cell, "OUTMUX"); + write_cell_muxes(cell); pop(); } diff --git a/nexus/pack.cc b/nexus/pack.cc index 58999965..d81fdf64 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1220,6 +1220,58 @@ struct NexusPacker } } + void pack_iodelay() + { + dict base_iodelay_rules; + base_iodelay_rules[id_DELAYB].new_type = id_IOLOGIC; + + base_iodelay_rules[id_DELAYB].param_xform[id_DEL_VALUE] = ctx->id("DELAY.DEL_VALUE"); + base_iodelay_rules[id_DELAYB].param_xform[id_COARSE_DELAY] = ctx->id("DELAY.COARSE_DELAY"); + + base_iodelay_rules[id_DELAYA] = base_iodelay_rules[id_DELAYB]; + + base_iodelay_rules[id_DELAYA].param_xform[id_COARSE_DELAY_MODE] = ctx->id("DELAY.COARSE_DELAY_MODE"); + base_iodelay_rules[id_DELAYA].param_xform[id_EDGE_MONITOR] = ctx->id("DELAY.EDGE_MONITOR"); + base_iodelay_rules[id_DELAYA].param_xform[id_WAIT_FOR_EDGE] = ctx->id("DELAY.WAIT_FOR_EDGE"); + + base_iodelay_rules[id_DELAYA].port_xform[id_COARSE0] = id_CIBCRS0; + base_iodelay_rules[id_DELAYA].port_xform[id_COARSE1] = id_CIBCRS1; + base_iodelay_rules[id_DELAYA].port_xform[id_CFLAG] = id_COUT; + + // Find IO delays, and convert them to IOLOGIC + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (!ci->type.in(id_DELAYA, id_DELAYB)) + continue; + if (str_or_default(ci->params, id_DEL_MODE, "USER_DEFINED") != "USER_DEFINED") { + // TODO: DEL_MODE (we need to work out how to convert these to a DEL_VALUE) + log_error("DEL_MODE other than \"USER_DEFINED\" is not supported on DELAY '%s'\n", ci->name.c_str(ctx)); + } + xform_cell(base_iodelay_rules, ci); + + NetInfo *a = ci->getPort(id_A); + if (a != nullptr && a->driver.cell != nullptr && a->driver.cell->type.in(id_SEIO18_CORE, id_SEIO33_CORE, id_DIFFIO18_CORE)) { + // It's an input delay + log_info(" processing input delay cell '%s'\n", ci->name.c_str(ctx)); + ci->params[id_INMUX] = std::string("DELAY"); + ci->renamePort(id_A, id_DI); + ci->renamePort(id_Z, id_INDD); + continue; + } + NetInfo *z = ci->getPort(id_Z); + if (z != nullptr && z->users.entries() == 1 && (*z->users.begin()).cell->type.in(id_SEIO18_CORE, id_SEIO33_CORE, id_DIFFIO18_CORE)) { + // It's an output delay + log_info(" processing output delay cell '%s'\n", ci->name.c_str(ctx)); + ci->params[id_OUTMUX] = std::string("DELAY"); + ci->renamePort(id_A, id_TXDATA0); + ci->renamePort(id_Z, id_DOUT); + continue; + } + log_error("Failed to determine if delay cell '%s' was an input or output delay.\n", ci->name.c_str(ctx)); + } + } + + void transform_iologic() { dict iol_rules; @@ -1303,6 +1355,7 @@ struct NexusPacker void pack_iologic() { log_info("Packing IOLOGIC...\n"); + pack_iodelay(); transform_iologic(); constrain_merge_iol(); }