#include "log.h" #include "nextpnr.h" #include "util.h" #include "viaduct_api.h" #include "viaduct_helpers.h" #define GEN_INIT_CONSTIDS #define VIADUCT_CONSTIDS "viaduct/hercules/constids.inc" #include "viaduct_constids.h" NEXTPNR_NAMESPACE_BEGIN namespace { struct LpInfo { std::array byp; WireId c_in; std::array down_i; WireId dx_fb; std::array f2; std::array f1; std::array f0; WireId fy; std::array up_i; WireId c_out; // down_o is the same as qx; std::array dx; WireId dx40; WireId dy; // fx is the same as dx[1] std::array qx; // up_o is the same as qx; }; struct LbufInfo { WireId a_sr; WireId mclk_b; WireId sclk; std::array sh; }; struct LeInfo { LbufInfo lbuf; std::array lp; }; struct PlbInfo { LeInfo le; std::array tn0_o; std::array te0_o; std::array ts0_o; std::array tw0_o; std::array mn0_o; std::array me0_o; std::array ms0_o; std::array mw0_o; std::array on0_o; std::array oe0_o; std::array os0_o; std::array ow0_o; std::array n_xy_o; WireId ne_xy_o; std::array en_xy_o; std::array e_xy_o; WireId es_xy_o; std::array se_xy_o; std::array s_xy_o; WireId sw_xy_o; std::array ws_xy_o; std::array w_xy_o; WireId wn_xy_o; std::array nw_xy_o; std::array cclk; std::array clk_xbar; }; struct RbufInfo { std::array gclk; std::array fabric_rclk; }; struct Hercules : ViaductAPI { void init(Context *const ctx) override { init_uarch_constids(ctx); // Set up the string-interning pool. ViaductAPI::init(ctx); h.init(ctx); log_info("Setting up FPGA...\n"); create_bels(); log_info("Setting up FPGA...done\n"); } void pack() override { // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis const pool top_ports{ CellTypePort(id_M7S_DGPIO, id_pad), }; h.remove_nextpnr_iobs(top_ports); // Replace constants with LUTs const dict vcc_params = {{id_config_data, Property(0xFFFF, 16)}}; const dict gnd_params = {{id_config_data, Property(0x0000, 16)}}; h.replace_constants(CellTypePort(id_LUT4, id_f3), CellTypePort(id_LUT4, id_f3), vcc_params, gnd_params); rename_regs(); } bool isBelLocationValid(BelId bel) const override { const Loc loc = ctx->getBelLocation(bel); const bool at_x_edge = (loc.x == 0) || (loc.x == COLUMNS - 1); const bool at_y_edge = (loc.y == 0) || (loc.y == ROWS - 1); if (at_y_edge) { // TODO: I/O buffer validation. return true; } else if (!at_x_edge && !at_y_edge) { // TODO: LP validation. return lp_is_valid(loc.x, loc.y, loc.z / LP_BELS); } return true; } private: void rename_regs() { for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type != id_REG) continue; ci->type = id_REGS; } } BelId addBel(const int x, const int y, const int z, const IdString id) { return ctx->addBel(h.xy_id(x, y, IdStringList::concat(id, ctx->idf("%d", z))), id, Loc(x, y, z), /* gb = */ false, /* hidden = */ false); } WireId addWire(const int x, const int y, const IdStringList name, const IdString type) { return ctx->addWire(h.xy_id(x, y, name), type, x, y); } WireId addWireAsBelInput(const int x, const int y, const IdString id, const BelId bel) { WireId wire = ctx->addWire(IdStringList::concat(ctx->getBelName(bel), id), id, x, y); ctx->addBelInput(bel, id, wire); return wire; } WireId addWireAsBelOutput(const int x, const int y, const IdString id, const BelId bel) { WireId wire = ctx->addWire(IdStringList::concat(ctx->getBelName(bel), id), id, x, y); ctx->addBelOutput(bel, id, wire); return wire; } void add_cfgmux(const Loc loc, const WireId dst, const WireId src) { NPNR_ASSERT(src != WireId()); NPNR_ASSERT(dst != WireId()); IdStringList name = IdStringList::concat(ctx->getWireName(dst), ctx->getWireName(src)); ctx->addPip(name, id_PIP, src, dst, 0.05, loc); } template void add_cfgmux(const Loc loc, const WireId dst, const WireId src, Rest... rest) { NPNR_ASSERT(src != WireId()); NPNR_ASSERT(dst != WireId()); IdStringList name = IdStringList::concat(ctx->getWireName(dst), ctx->getWireName(src)); ctx->addPip(name, id_PIP, src, dst, 0.05, loc); add_cfgmux(loc, dst, rest...); } void create_iopad(const int x, const int y) { // Is there another IO pad combined? // note to self: xbar_tile_[ew]cap_io for (int z = 0; z < 4; z++) { const BelId bel = addBel(x, y, z, id_M7S_DGPIO); if (x == 0) { ctx->addBelInput(bel, id_od_d, plbs[x + 1][y].tw0_o[z]); ctx->addBelInput(bel, id_oen, plbs[x + 1][y].mw0_o[3]); } else { ctx->addBelInput(bel, id_od_d, plbs[x - 1][y].te0_o[z]); ctx->addBelInput(bel, id_oen, plbs[x - 1][y].me0_o[3]); } plbs[x][y].te0_o[z] = plbs[x][y].tw0_o[z] = addWireAsBelOutput(x, y, id_id_q, bel); const WireId pad = addWire(x, y, IdStringList::concat(ctx->getBelName(bel), id_pad), id_pad); ctx->addBelInout(bel, id_pad, pad); } } void create_logic_parcel(const int x, const int y, const int lp_idx, LeInfo& le) { LpInfo& lp = le.lp[lp_idx]; const BelId lut0 = addBel(x, y, LP_BELS*lp_idx + 0, id_LUT4); ctx->addBelInput(lut0, id_f0, lp.f0[0]); ctx->addBelInput(lut0, id_f1, lp.f1[0]); const WireId l0_f2 = addWireAsBelInput(x, y, id_f2, lut0); const WireId l0_f3 = addWireAsBelInput(x, y, id_f3, lut0); ctx->addBelOutput(lut0, id_dx, lp.dx[0]); const BelId lut40 = addBel(x, y, LP_BELS*lp_idx + 1, id_LUT4C); const WireId l40_f0 = addWireAsBelInput(x, y, id_f0, lut40); const WireId l40_f1 = addWireAsBelInput(x, y, id_f1, lut40); const WireId l40_f2 = addWireAsBelInput(x, y, id_f2, lut40); const WireId l40_f3 = addWireAsBelInput(x, y, id_f3, lut40); const WireId l40_ca = addWireAsBelInput(x, y, id_ca, lut40); ctx->addBelInput(lut40, id_ci, lp.c_in); ctx->addBelOutput(lut40, id_co, lp.c_out); ctx->addBelOutput(lut40, id_dx, lp.dx40); const WireId l40_s = addWireAsBelOutput(x, y, id_s, lut40); const BelId lut41 = addBel(x, y, LP_BELS*lp_idx + 2, id_LUT4); ctx->addBelInput(lut41, id_f0, lp.f0[2]); ctx->addBelInput(lut41, id_f1, lp.f1[2]); ctx->addBelInput(lut41, id_f2, lp.f2[2]); const WireId l41_f3 = addWireAsBelInput(x, y, id_f3, lut41); const WireId l41_dx = addWireAsBelOutput(x, y, id_dx, lut41); const BelId reg0 = addBel(x, y, LP_BELS*lp_idx + 3, id_REGS); ctx->addBelInput(reg0, id_a_sr, le.lbuf.a_sr); const WireId r0_di = addWireAsBelInput(x, y, id_di, reg0); ctx->addBelInput(reg0, id_down_i, lp.down_i[0]); ctx->addBelInput(reg0, id_mclk_b, le.lbuf.mclk_b); ctx->addBelInput(reg0, id_sclk, le.lbuf.sclk); ctx->addBelInput(reg0, id_shift, le.lbuf.sh[0]); ctx->addBelInput(reg0, id_up_i, lp.up_i[0]); ctx->addBelOutput(reg0, id_qx, lp.qx[0]); const BelId reg1 = addBel(x, y, LP_BELS*lp_idx + 4, id_REGS); ctx->addBelInput(reg1, id_a_sr, le.lbuf.a_sr); const WireId r1_di = addWireAsBelInput(x, y, id_di, reg1); ctx->addBelInput(reg1, id_down_i, lp.down_i[1]); ctx->addBelInput(reg1, id_mclk_b, le.lbuf.mclk_b); ctx->addBelInput(reg1, id_sclk, le.lbuf.sclk); ctx->addBelInput(reg1, id_shift, le.lbuf.sh[1]); ctx->addBelInput(reg1, id_up_i, lp.up_i[1]); ctx->addBelOutput(reg1, id_qx, lp.qx[1]); const BelId mux_dx4_0 = addBel(x, y, LP_BELS*lp_idx + 5, id_mux_dx4); ctx->addBelInput(mux_dx4_0, id_in0, lp.dx40); ctx->addBelInput(mux_dx4_0, id_in1, l41_dx); ctx->addBelInput(mux_dx4_0, id_sel, lp.byp[2]); ctx->addBelOutput(mux_dx4_0, id_out, lp.dx[1]); const BelId mux_dx4_1 = addBel(x, y, LP_BELS*lp_idx + 6, id_mux_dx4); ctx->addBelInput(mux_dx4_1, id_in0, l41_dx); ctx->addBelInput(mux_dx4_1, id_in1, lp.dx40); ctx->addBelInput(mux_dx4_1, id_sel, lp.byp[2]); const WireId dx41_out = addWireAsBelOutput(x, y, id_out, mux_dx4_1); add_cfgmux(Loc(x, y, 0), l0_f2, lp.f2[0], lp.qx[0]); add_cfgmux(Loc(x, y, 0), l0_f3, lp.byp[0], lp.dx_fb); add_cfgmux(Loc(x, y, 0), l40_f0, lp.f0[1], lp.dx[1]); add_cfgmux(Loc(x, y, 0), l40_f1, lp.f1[1], l41_dx); add_cfgmux(Loc(x, y, 0), l40_f2, lp.f2[1], lp.qx[1], lp.fy); add_cfgmux(Loc(x, y, 0), l40_f3, lp.byp[1], lp.f2[2]); add_cfgmux(Loc(x, y, 0), l40_ca, lp.byp[3], l40_f2, lp.dx[0]); // Technically input 0 should be GND, and this should go through an inverter. WireId mux_sc = addWire(x, y, IdStringList::concat(ctx->getBelName(lut40), id_mux_sc), id_mux_sc); add_cfgmux(Loc(x, y, 0), mux_sc, lp.c_out, l40_s); add_cfgmux(Loc(x, y, 0), lp.dy, dx41_out, mux_sc); add_cfgmux(Loc(x, y, 0), l41_f3, lp.byp[2], lp.f2[1], lp.qx[1]); add_cfgmux(Loc(x, y, 0), r0_di, l40_s, lp.byp[3], lp.dx[0], lp.dx[1]); add_cfgmux(Loc(x, y, 0), r1_di, l40_s, lp.byp[4], lp.dx[0], dx41_out); add_cfgmux(Loc(x, y, 0), lp.dx[1], lp.dx40, l41_dx); add_cfgmux(Loc(x, y, 0), dx41_out, l41_dx, lp.dx40); } void create_logic_element(const int x, const int y, LeInfo& info) { // Create an unconnected wire for the initial carry-in if necessary. if (y > 1) info.lp[0].c_in = plbs[x][y-1].le.lp[3].c_out; else info.lp[0].c_in = addWire(x, y, IdStringList::concat(id_DUMMY, id_C_OUT), id_C_OUT); // Same for dx_fb if (y > 1) info.lp[0].dx_fb = plbs[x][y-1].le.lp[3].dx[1]; else info.lp[0].dx_fb = addWire(x, y, IdStringList::concat(id_DUMMY, id_DX_FB), id_DX_FB); // Setup LBUF wires. info.lbuf.a_sr = addWire(x, y, IdStringList(id_a_sr), id_a_sr); info.lbuf.mclk_b = addWire(x, y, IdStringList(id_mclk_b), id_mclk_b); info.lbuf.sclk = addWire(x, y, IdStringList(id_sclk), id_sclk); info.lbuf.sh[0] = addWire(x, y, IdStringList::concat(id_shift, ctx->idf("0")), id_shift); info.lbuf.sh[1] = addWire(x, y, IdStringList::concat(id_shift, ctx->idf("1")), id_shift); /* lbuf bel I guess? */ const WireId lbuf_en{}; add_cfgmux(Loc(x, y, 0), info.lbuf.a_sr, plbs[x][y].cclk[0], plbs[x][y].cclk[1], plbs[x][y].cclk[2], plbs[x][y].cclk[3], plbs[x][y].cclk[4], plbs[x][y].cclk[5] /*, rc[1]? */); add_cfgmux(Loc(x, y, 0), info.lbuf.mclk_b, plbs[x][y].cclk[0], plbs[x][y].cclk[1], plbs[x][y].cclk[2], plbs[x][y].cclk[3], plbs[x][y].cclk[4], plbs[x][y].cclk[5]); //add_cfgmux(Loc(x, y, 0), lbuf_en, plbs[x][y].cclk[0], plbs[x][y].cclk[1], plbs[x][y].cclk[2], plbs[x][y].cclk[3], plbs[x][y].cclk[4], plbs[x][y].cclk[5] /*, rc[0]? */); // Setup LP wires. for (int lp_idx = 0; lp_idx < 4; lp_idx++) { IdString lp_id = ctx->idf("LP%d", lp_idx); LpInfo& lp = info.lp[lp_idx]; // LP inputs. for (int byp = 0; byp < 5; byp++) lp.byp[byp] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("BYP[%d]", byp)), id_BYP); for (int fx = 0; fx < 3; fx++) { lp.f0[fx] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("F0[%d]", fx)), id_f0); lp.f1[fx] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("F1[%d]", fx)), id_f1); lp.f2[fx] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("F2[%d]", fx)), id_f2); } if (x > 1) lp.fy = plbs[x-1][y].le.lp[lp_idx].dy; else lp.fy = addWire(x, y, IdStringList::concat(lp_id, id_DUMMY), id_DY); // LP outputs. info.lp[lp_idx].c_out = addWire(x, y, IdStringList::concat(lp_id, id_C_OUT), id_C_OUT); for (int dx = 0; dx < 2; dx++) { lp.dx[dx] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("DX[%d]", dx)), id_dx); lp.qx[dx] = addWire(x, y, IdStringList::concat(lp_id, ctx->idf("QX[%d]", dx)), id_qx); } info.lp[lp_idx].dx40 = addWire(x, y, IdStringList::concat(lp_id, id_DX40), id_DX40); info.lp[lp_idx].dy = addWire(x, y, IdStringList::concat(lp_id, id_DY), id_DY); } for (int lp_idx = 0; lp_idx < 4; lp_idx++) { if (lp_idx != 0) { info.lp[lp_idx].c_in = info.lp[lp_idx-1].c_out; info.lp[lp_idx].down_i[0] = info.lp[lp_idx-1].qx[0]; info.lp[lp_idx].down_i[1] = info.lp[lp_idx-1].qx[1]; info.lp[lp_idx].dx_fb = info.lp[lp_idx-1].dx[1]; } if (lp_idx != 3) { info.lp[lp_idx].up_i[0] = info.lp[lp_idx+1].qx[0]; info.lp[lp_idx].up_i[1] = info.lp[lp_idx+1].qx[1]; } create_logic_parcel(x, y, lp_idx, info); } } void create_input_crossbars(const int x_coord, const int y_coord, PlbInfo& plb) { const auto& lp = plb.le.lp; const WireId i[4][4][16] = { { { plb.te0_o[0], plb.oe0_o[0], get(x_coord+2, y_coord).te0_o[0], get(x_coord+1, y_coord).e_xy_o[0], get(x_coord+1, y_coord).te0_o[0], get(x_coord+4, y_coord).oe0_o[0], get(x_coord-1, y_coord).w_xy_o[1], plb.e_xy_o[0], get(x_coord+2, y_coord).te0_o[1], plb.te0_o[1], plb.oe0_o[1], plb.me0_o[0], get(x_coord+1, y_coord).ne_xy_o, get(x_coord+1, y_coord).te0_o[1], get(x_coord+4, y_coord).oe0_o[1], get(x_coord+1, y_coord).me0_o[0], }, { plb.ts0_o[0], plb.os0_o[0], get(x_coord+1, y_coord+1).ne_xy_o, lp[3].byp[2], get(x_coord, y_coord+1).tn0_o[0], get(x_coord, y_coord+4).on0_o[1], get(x_coord, y_coord+2).tn0_o[0], get(x_coord, y_coord+1).tn0_o[1], lp[2].byp[2], get(x_coord, y_coord+1).n_xy_o[0], plb.on0_o[0], plb.mn0_o[0], get(x_coord-1, y_coord).ws_xy_o[0], plb.s_xy_o[0], get(x_coord, y_coord-4).os0_o[1], get(x_coord, y_coord-1).ms0_o[0], }, { get(x_coord, y_coord+1).mn0_o[0], get(x_coord, y_coord+4).on0_o[0], get(x_coord-1, y_coord).nw_xy_o[0], lp[2].byp[0], plb.ms0_o[0], plb.os0_o[1], get(x_coord-1, y_coord+1).nw_xy_o[0], lp[1].byp[2], get(x_coord+1, y_coord).es_xy_o, get(x_coord, y_coord-2).ts0_o[0], get(x_coord, y_coord-4).os0_o[0], get(x_coord, y_coord-1).ts0_o[0], get(x_coord, y_coord+2).tn0_o[1], plb.tn0_o[1], plb.on0_o[1], plb.tn0_o[0], }, { get(x_coord-1, y_coord).mw0_o[0], get(x_coord-4, y_coord).ow0_o[0], lp[0].dy, lp[0].dx[0], plb.mw0_o[0], plb.ow0_o[0], lp[0].qx[0], lp[2].qx[0], lp[2].dx[0], get(x_coord+1, y_coord+1).en_xy_o[0], get(x_coord-4, y_coord).ow0_o[1], get(x_coord-1, y_coord).tw0_o[0], lp[0].byp[1], get(x_coord-2, y_coord).tw0_o[0], plb.ow0_o[1], plb.tw0_o[0], }, }, { { plb.te0_o[2], plb.oe0_o[2], get(x_coord+2, y_coord).te0_o[2], get(x_coord+1, y_coord).e_xy_o[1], get(x_coord+1, y_coord).te0_o[2], get(x_coord+4, y_coord).oe0_o[2], get(x_coord-1, y_coord+1).wn_xy_o, lp[0].byp[2], lp[2].byp[3], get(x_coord+1, y_coord+1).en_xy_o[1], plb.oe0_o[3], plb.me0_o[1], lp[2].dx[1], get(x_coord-1, y_coord+1).nw_xy_o[1], get(x_coord+4, y_coord).oe0_o[3], get(x_coord+1, y_coord).me0_o[1], }, { plb.ts0_o[1], plb.os0_o[2], plb.ts0_o[2], get(x_coord, y_coord-2).ts0_o[2], get(x_coord, y_coord+1).tn0_o[2], get(x_coord, y_coord+4).on0_o[3], get(x_coord, y_coord+2).tn0_o[2], lp[1].dy, lp[2].byp[1], plb.s_xy_o[1], plb.on0_o[2], plb.mn0_o[1], get(x_coord-1, y_coord).ws_xy_o[1], get(x_coord, y_coord+1).n_xy_o[1], get(x_coord, y_coord-4).os0_o[3], get(x_coord, y_coord-1).ms0_o[1], }, { get(x_coord, y_coord+1).mn0_o[1], get(x_coord, y_coord+4).on0_o[2], plb.n_xy_o[0], lp[0].dx[1], plb.ms0_o[1], plb.os0_o[3], lp[0].byp[4], get(x_coord, y_coord-1).s_xy_o[0], get(x_coord, y_coord-1).ts0_o[2], get(x_coord, y_coord-2).ts0_o[1], get(x_coord, y_coord-4).os0_o[2], get(x_coord, y_coord-1).ts0_o[1], lp[0].qx[1], lp[2].qx[1], plb.on0_o[3], plb.tn0_o[2], }, { get(x_coord-1, y_coord).mw0_o[1], get(x_coord-4, y_coord).ow0_o[2], get(x_coord-1, y_coord).tw0_o[2], lp[3].byp[3], plb.mw0_o[1], plb.ow0_o[2], plb.tw0_o[2], get(x_coord-2, y_coord).tw0_o[2], get(x_coord-1, y_coord).nw_xy_o[1], plb.w_xy_o[0], get(x_coord-4, y_coord).ow0_o[3], get(x_coord-1, y_coord).tw0_o[1], get(x_coord-1, y_coord).w_xy_o[0], get(x_coord-2, y_coord).tw0_o[1], plb.ow0_o[3], plb.tw0_o[1], }, }, { { plb.te0_o[3], plb.oe0_o[4], get(x_coord+2, y_coord).te0_o[3], get(x_coord+1, y_coord).e_xy_o[2], get(x_coord+1, y_coord).te0_o[3], get(x_coord+4, y_coord).oe0_o[4], get(x_coord+1, y_coord).se_xy_o[0], plb.e_xy_o[2], get(x_coord+2, y_coord).te0_o[4], plb.te0_o[4], plb.oe0_o[5], plb.me0_o[2], lp[3].dx[0], get(x_coord+1, y_coord).te0_o[4], get(x_coord+4, y_coord).oe0_o[5], get(x_coord+1, y_coord).me0_o[2], }, { plb.ts0_o[3], plb.os0_o[4], lp[1].qx[0], lp[3].qx[0], get(x_coord, y_coord+1).tn0_o[3], get(x_coord, y_coord+4).on0_o[5], get(x_coord, y_coord+2).tn0_o[3], get(x_coord, y_coord+1).tn0_o[4], lp[2].byp[4], get(x_coord, y_coord+1).n_xy_o[2], plb.on0_o[4], plb.mn0_o[2], lp[1].byp[3], plb.s_xy_o[2], get(x_coord, y_coord-4).os0_o[5], get(x_coord, y_coord-1).ms0_o[2], }, { get(x_coord, y_coord+1).mn0_o[2], get(x_coord, y_coord+4).on0_o[4], get(x_coord+1, y_coord).en_xy_o[0], get(x_coord, y_coord-1).s_xy_o[1], plb.ms0_o[2], plb.os0_o[5], plb.n_xy_o[1], lp[1].dx[0], lp[2].dy, get(x_coord, y_coord-2).ts0_o[3], get(x_coord, y_coord-4).os0_o[4], get(x_coord, y_coord-1).ts0_o[3], get(x_coord, y_coord+2).tn0_o[4], plb.tn0_o[4], plb.on0_o[5], plb.tn0_o[3], }, { get(x_coord-1, y_coord).mw0_o[2], get(x_coord-4, y_coord).ow0_o[4], get(x_coord+1, y_coord-1).es_xy_o, get(x_coord+1, y_coord-1).se_xy_o[0], plb.mw0_o[2], plb.ow0_o[4], get(x_coord-1, y_coord-1).ws_xy_o[0], lp[1].byp[0], lp[3].byp[4], plb.w_xy_o[1], get(x_coord-4, y_coord).ow0_o[5], get(x_coord-1, y_coord).tw0_o[3], lp[0].byp[3], get(x_coord-2, y_coord).tw0_o[3], plb.ow0_o[5], plb.tw0_o[3], }, }, { { plb.te0_o[5], plb.oe0_o[6], get(x_coord+2, y_coord).te0_o[5], get(x_coord+1, y_coord-1).se_xy_o[1], get(x_coord+1, y_coord).te0_o[5], get(x_coord+4, y_coord).oe0_o[6], lp[1].byp[4], get(x_coord+1, y_coord).se_xy_o[1], lp[1].qx[1], lp[3].qx[1], plb.on0_o[8], plb.me0_o[3], plb.e_xy_o[1], lp[3].dy, get(x_coord, y_coord+4).on0_o[8], get(x_coord+1, y_coord).me0_o[3], }, { plb.ts0_o[4], plb.os0_o[6], plb.ts0_o[5], get(x_coord, y_coord-2).ts0_o[5], get(x_coord, y_coord+1).tn0_o[5], get(x_coord, y_coord+4).on0_o[7], get(x_coord, y_coord+2).tn0_o[5], lp[0].byp[0], lp[3].byp[0], get(x_coord+1, y_coord).en_xy_o[1], plb.on0_o[6], plb.mn0_o[3], get(x_coord-1, y_coord-1).sw_xy_o, lp[1].byp[1], get(x_coord, y_coord-4).os0_o[7], get(x_coord, y_coord-1).ms0_o[3], }, { get(x_coord, y_coord+1).mn0_o[3], get(x_coord, y_coord+4).on0_o[6], plb.n_xy_o[2], lp[1].dx[1], plb.ms0_o[3], plb.os0_o[7], get(x_coord-1, y_coord-1).ws_xy_o[1], get(x_coord, y_coord-1).s_xy_o[2], get(x_coord, y_coord-1).ts0_o[5], get(x_coord, y_coord-2).ts0_o[4], get(x_coord, y_coord-4).os0_o[6], get(x_coord, y_coord-1).ts0_o[4], lp[3].dx[1], get(x_coord-1, y_coord).wn_xy_o, plb.on0_o[7], plb.tn0_o[5], }, { get(x_coord-1, y_coord).mw0_o[3], get(x_coord-4, y_coord).ow0_o[6], get(x_coord-1, y_coord).tw0_o[5], lp[3].byp[1], plb.mw0_o[3], plb.ow0_o[6], plb.tw0_o[5], get(x_coord-2, y_coord).tw0_o[5], get(x_coord-1, y_coord).sw_xy_o, plb.w_xy_o[2], get(x_coord, y_coord-4).os0_o[8], get(x_coord-1, y_coord).tw0_o[4], get(x_coord-1, y_coord).w_xy_o[2], get(x_coord-2, y_coord).tw0_o[4], plb.os0_o[8], plb.tw0_o[4], }, }, }; const WireId z[4][4][8] = { { { plb.on0_o[0], plb.on0_o[1], lp[3].f2[0], lp[1].f0[2], lp[0].byp[0], plb.tn0_o[1], lp[0].f2[2], lp[0].f0[0], }, { plb.mn0_o[0], plb.tn0_o[0], lp[3].byp[1], plb.te0_o[1], plb.mw0_o[0], plb.te0_o[0], plb.ow0_o[1], plb.oe0_o[1], }, { lp[2].f0[2], lp[2].byp[2], plb.ts0_o[0], plb.ms0_o[0], plb.ow0_o[0], plb.oe0_o[0], plb.tw0_o[0], plb.me0_o[0], }, { lp[3].f1[1], lp[2].f1[0], plb.os0_o[0], plb.os0_o[1], lp[2].f2[1], lp[0].f1[1], lp[0].byp[4], lp[1].byp[3], }, }, { { plb.on0_o[2], plb.on0_o[3], lp[2].f1[2], lp[2].f0[0], lp[3].byp[2], lp[3].f0[0], lp[1].f2[0], lp[0].f0[1], }, { plb.mn0_o[1], plb.tn0_o[2], plb.ts0_o[2], lp[2].byp[3], plb.mw0_o[1], plb.te0_o[2], plb.ow0_o[3], plb.oe0_o[3], }, { plb.tw0_o[2], lp[0].byp[1], plb.ts0_o[1], plb.ms0_o[1], plb.ow0_o[2], plb.oe0_o[2], plb.tw0_o[1], plb.me0_o[1], }, { lp[3].f2[1], lp[2].f1[1], plb.os0_o[2], plb.os0_o[3], lp[1].f2[1], lp[0].f1[2], lp[1].byp[0], lp[1].byp[4], }, }, { { plb.on0_o[4], plb.on0_o[5], lp[2].f2[2], lp[2].f0[1], lp[2].byp[4], plb.tn0_o[4], lp[0].f2[0], lp[0].f0[2], }, { plb.mn0_o[2], plb.tn0_o[3], lp[3].f0[1], plb.te0_o[4], plb.mw0_o[2], plb.te0_o[3], plb.ow0_o[5], plb.oe0_o[5], }, { lp[3].byp[3], lp[0].byp[2], plb.ts0_o[3], plb.ms0_o[2], plb.ow0_o[4], plb.oe0_o[4], plb.tw0_o[3], plb.me0_o[2], }, { lp[3].f1[2], lp[1].f1[1], plb.os0_o[4], plb.os0_o[5], lp[1].f2[2], lp[1].f1[0], lp[1].byp[1], lp[2].byp[0], }, }, { { plb.on0_o[6], plb.on0_o[7], lp[3].f1[0], lp[1].f0[1], lp[3].byp[0], lp[0].byp[3], lp[0].f2[1], lp[1].f0[0], }, { plb.mn0_o[3], plb.tn0_o[5], plb.ts0_o[5], lp[3].f0[2], plb.mw0_o[3], plb.te0_o[5], plb.on0_o[8], plb.os0_o[8], }, { plb.tw0_o[5], lp[3].byp[4], plb.ts0_o[4], plb.ms0_o[3], plb.ow0_o[6], plb.oe0_o[6], plb.tw0_o[4], plb.me0_o[3], }, { lp[3].f2[2], lp[1].f1[2], plb.os0_o[6], plb.os0_o[7], lp[2].f2[0], lp[0].f1[0], lp[1].byp[2], lp[2].byp[1], }, }, }; const int swizzle[8] = {0, 1, 4, 5, 2, 3, 6, 7}; // Far, får får får? Nej, får får inte får, får får lamm. for (int ixbar = 0; ixbar < 4; ixbar++) { WireId x[4][8][4]; for (int a = 0; a < 4; a++) for (int b = 0; b < 8; b++) for (int c = 0; c < 4; c++) { IdString ixbar_id = ctx->idf("IXBAR%d", ixbar); IdStringList a_id = IdStringList::concat(ixbar_id, ctx->idf("IX%d", a)); IdStringList b_id = IdStringList::concat(a_id, ctx->idf("IX%d", b)); IdStringList id = IdStringList::concat(b_id, ctx->idf("SC_MUX_%d", c)); x[a][b][c] = addWire(x_coord, y_coord, id, id_IXIX); } // stage 1: the "ixix": select 32 groups of 4 signals from the i inputs. for (int ixN = 0; ixN < 4; ixN++) for (int ixM = 0; ixM < 8; ixM++) for (int SC_mux_C = 0; SC_mux_C < 4; SC_mux_C++) for (int a = 0; a < 4; a++) add_cfgmux(Loc(x_coord, y_coord, 32*ixN+4*ixM+SC_mux_C), x[ixN][ixM][SC_mux_C], i[ixbar][a][ixN*4+SC_mux_C]); // some ixixes can select from the clocks, as well. if (ixbar < 2) { for (int ixB = 6; ixB < 8; ixB++) { add_cfgmux(Loc(x_coord, y_coord, 0), x[ixbar][ixB][0], plb.clk_xbar[2*ixbar+0]); add_cfgmux(Loc(x_coord, y_coord, 0), x[ixbar][ixB][3], plb.clk_xbar[2*ixbar+1]); } } // stage 2: the "xzzx": select 16 signals from 4 groups of 4 signals from the ixix. for (int xzN = 0; xzN < 8; xzN++) { for (int zxM = 0; zxM < 4; zxM++) for (const auto x : x) for (const auto x : x[swizzle[xzN]]) add_cfgmux(Loc(x_coord, y_coord, 4*xzN+zxM), z[ixbar][zxM][xzN], x); } } } void create_output_crossbar(const int x, const int y, PlbInfo& plb) { const WireId xy[24] = { plb.s_xy_o[0], plb.ws_xy_o[0], plb.e_xy_o[0], plb.es_xy_o, plb.w_xy_o[0], plb.se_xy_o[0], plb.s_xy_o[1], plb.ws_xy_o[1], plb.n_xy_o[0], plb.sw_xy_o, plb.s_xy_o[2], plb.se_xy_o[1], plb.w_xy_o[1], plb.nw_xy_o[0], plb.e_xy_o[2], plb.ne_xy_o, plb.n_xy_o[1], plb.en_xy_o[0], plb.e_xy_o[1], plb.nw_xy_o[1], plb.w_xy_o[2], plb.wn_xy_o, plb.n_xy_o[2], plb.en_xy_o[1], }; for (int n = 0; n < 24; n++) add_cfgmux(Loc(x, y, n), xy[n], plb.le.lp[0].dx[0], plb.le.lp[0].qx[0], plb.le.lp[0].dx[1], plb.le.lp[0].qx[1], plb.le.lp[0].dy, plb.le.lp[1].dx[0], plb.le.lp[1].qx[0], plb.le.lp[1].dx[1], plb.le.lp[1].qx[1], plb.le.lp[1].dy, plb.le.lp[2].dx[0], plb.le.lp[2].qx[0], plb.le.lp[2].dx[1], plb.le.lp[2].qx[1], plb.le.lp[2].dy, plb.le.lp[3].dx[0], plb.le.lp[3].qx[0], plb.le.lp[3].dx[1], plb.le.lp[3].qx[1], plb.le.lp[3].dy, plb.le.lp[3].byp[4], plb.le.lp[2].byp[4], plb.le.lp[1].byp[4], plb.le.lp[0].byp[4] ); } void create_programmable_logic_block(const int x, const int y) { create_logic_element(x, y, plbs[x][y].le); create_input_crossbars(x, y, plbs[x][y]); create_output_crossbar(x, y, plbs[x][y]); } void create_rbuf(const int x, const int y, const int n, const WireId clk0, const WireId clk1, const WireId clk2, const WireId clk3, const WireId en0, const WireId en1) { WireId clk_mux = addWire(x, y, h.xy_id(x, y, ctx->idf("RBUF%d", n)), id_CLK_MUX); add_cfgmux(Loc(x, y, n), clk_mux, clk0, clk1, clk2, clk3); WireId en_mux = addWire(x, y, h.xy_id(x, y, ctx->idf("RBUF%d", n)), id_EN_MUX); add_cfgmux(Loc(x, y, n), en_mux, en0, en1); // TODO: clock gating bel? WireId clk_en_mux = clk_mux; // TODO: investigate rbufx6.rclk_[td]_xbar_[12] for (int x_offset = 0; x_offset < 8; x_offset++) for (int y_offset = 0; y_offset < 2; y_offset++) get(x + x_offset, y + y_offset).cclk[n] = clk_en_mux; } void create_rbufx6(const int x, const int y) { RbufInfo& rbuf = rbufs[x][y]; create_rbuf(x, y, 0, rbuf.gclk[0], rbuf.gclk[6], rbuf.gclk[10], rbuf.gclk[12], rbuf.gclk[4], rbuf.fabric_rclk[0]); create_rbuf(x, y, 1, rbuf.gclk[1], rbuf.gclk[7], rbuf.gclk[11], rbuf.gclk[13], rbuf.gclk[5], rbuf.fabric_rclk[1]); create_rbuf(x, y, 2, rbuf.gclk[2], rbuf.gclk[8], rbuf.gclk[12], rbuf.gclk[14], rbuf.gclk[0], rbuf.gclk[6]); create_rbuf(x, y, 3, rbuf.gclk[3], rbuf.gclk[9], rbuf.gclk[13], rbuf.gclk[15], rbuf.gclk[1], rbuf.gclk[7]); create_rbuf(x, y, 4, rbuf.gclk[4], rbuf.gclk[10], rbuf.gclk[14], rbuf.fabric_rclk[0], rbuf.gclk[2], rbuf.gclk[8]); create_rbuf(x, y, 5, rbuf.gclk[5], rbuf.gclk[11], rbuf.gclk[15], rbuf.fabric_rclk[1], rbuf.gclk[3], rbuf.gclk[9]); } void create_bels() { for (int x = 0; x < COLUMNS; x++) { for (int y = 0; y < ROWS; y++) { plbs[x][y] = setup_plb(x, y); if ((x - 1) % 8 == 0 && (y - 1) % 2 == 0) create_rbufx6(x, y); } } for (int x = 1; x < COLUMNS - 1; x++) { for (int y = 0; y < ROWS; y++) { const bool at_y_edge = (y == 0) || (y == ROWS - 1); if (at_y_edge) { create_iopad(x, y); } } } for (int x = 0; x < COLUMNS; x++) { const bool at_x_edge = (x == 0) || (x == COLUMNS - 1); for (int y = 0; y < ROWS; y++) { const bool at_y_edge = (y == 0) || (y == ROWS - 1); if (!at_x_edge && !at_y_edge) { create_programmable_logic_block(x, y); } } } } PlbInfo setup_plb(const int x, const int y) { PlbInfo plb{}; for (int triple = 0; triple < 6; triple++) { IdString id = ctx->idf("%d", triple); plb.tn0_o[triple] = addWire(x, y, IdStringList::concat(id_TN, id), id_TN); plb.te0_o[triple] = addWire(x, y, IdStringList::concat(id_TE, id), id_TE); plb.ts0_o[triple] = addWire(x, y, IdStringList::concat(id_TS, id), id_TS); plb.tw0_o[triple] = addWire(x, y, IdStringList::concat(id_TW, id), id_TW); } for (int mono = 0; mono < 4; mono++) { IdString id = ctx->idf("%d", mono); plb.mn0_o[mono] = addWire(x, y, IdStringList::concat(id_MN, id), id_MN); plb.me0_o[mono] = addWire(x, y, IdStringList::concat(id_ME, id), id_ME); plb.ms0_o[mono] = addWire(x, y, IdStringList::concat(id_MS, id), id_MS); plb.mw0_o[mono] = addWire(x, y, IdStringList::concat(id_MW, id), id_MW); plb.clk_xbar[mono] = addWire(x, y, IdStringList::concat(id_CLK_XBAR, id), id_CLK_XBAR); } for (int octal = 0; octal < 9; octal++) { IdString id = ctx->idf("%d", octal); plb.on0_o[octal] = addWire(x, y, IdStringList::concat(id_ON, id), id_ON); plb.os0_o[octal] = addWire(x, y, IdStringList::concat(id_OS, id), id_OS); if (octal < 7) { plb.oe0_o[octal] = addWire(x, y, IdStringList::concat(id_OE, id), id_OE); plb.ow0_o[octal] = addWire(x, y, IdStringList::concat(id_OW, id), id_OW); } } for (int xy = 0; xy < 3; xy++) { IdString id = ctx->idf("%d", xy); plb.n_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYN, id), id_XYN); plb.e_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYE, id), id_XYE); plb.s_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYS, id), id_XYS); plb.w_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYW, id), id_XYW); } for (int xy = 0; xy < 2; xy++) { IdString id = ctx->idf("%d", xy); plb.en_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYEN, id), id_XYEN); plb.se_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYSE, id), id_XYSE); plb.ws_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYWS, id), id_XYWS); plb.nw_xy_o[xy] = addWire(x, y, IdStringList::concat(id_XYNW, id), id_XYNW); } IdString id = ctx->id("0"); plb.ne_xy_o = addWire(x, y, IdStringList::concat(id_XYNE, id), id_XYNE); plb.es_xy_o = addWire(x, y, IdStringList::concat(id_XYES, id), id_XYES); plb.sw_xy_o = addWire(x, y, IdStringList::concat(id_XYSW, id), id_XYSW); plb.wn_xy_o = addWire(x, y, IdStringList::concat(id_XYWN, id), id_XYWN); return plb; } PlbInfo& get(int x, int y) { if (x < 0) x = -x; if (x >= COLUMNS) x = COLUMNS - (x - COLUMNS); if (y < 0) y = -y; if (y >= ROWS) y = ROWS - (y - ROWS); return plbs[x][y]; } bool lp_is_valid(int x, int y, int lp_idx) const { const CellInfo *lut0 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 0))); const CellInfo *lut40 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 1))); const CellInfo *lut41 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 2))); const CellInfo *reg0 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 3))); const CellInfo *reg1 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 4))); const CellInfo *mux_dx4_0 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 5))); const CellInfo *mux_dx4_1 = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, LP_BELS*lp_idx + 6))); // TODO: mux_dx4 if (!lut0 && !lut40 && !lut41) { // assume routing via byp[34] return true; } /*if (reg0) { if () }*/ // common signals for reg0/reg1 if (reg0 && reg1) { if (reg0->getPort(id_a_sr) != reg1->getPort(id_a_sr)) return false; if (reg0->getPort(id_mclk_b) != reg1->getPort(id_mclk_b)) return false; if (reg0->getPort(id_sclk) != reg1->getPort(id_sclk)) return false; } return true; } private: ViaductHelpers h; static const int COLUMNS = 28; static const int ROWS = 50; static const int LP_BELS = 7; std::array, COLUMNS> plbs; std::array, COLUMNS> rbufs; }; struct HerculesArch : ViaductArch { HerculesArch() : ViaductArch("hercules") {} std::unique_ptr create(const dict &args) { return std::make_unique(); } } hercules; } // namespace NEXTPNR_NAMESPACE_END