gatemate: Multi die support and primitives model improvement (#1501)

* SER_CLK support

* Update constids

* wip

* CLK_FEEDBACK

* Handle SER_CLK and SER_CLK_N

* clangformat

* Cleanup

* Use _ as separator for PLL CFGs

* Remove unused clocking cells

* Do not use same name for IO models

* Fix IDDR merge

* Cleanup

* Properly handle user global signals

* Move signal inversion in bitstream creation

* Start adding multi die support

* Display die location for pins used

* Do not use constant s as locations

* Cleanup SB_DRIVE handling

* Use DDR locations from chip database

* Place only in prefered die for now

* Set D2D

* Fixed typos
This commit is contained in:
Miodrag Milanović 2025-06-18 08:32:57 +02:00 committed by GitHub
parent 5275c14ac0
commit 7318d6a8ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 622 additions and 339 deletions

View File

@ -38,17 +38,17 @@ if (NOT HIMBAECHEL_PEPPERCORN_PATH AND NOT IMPORT_BBA_FILES)
message(FATAL_ERROR "HIMBAECHEL_PEPPERCORN_PATH must be set to a Project Peppercorn checkout")
endif()
#set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1 CCGM1A2 CCGM1A4 CCGM1A9 CCGM1A16 CCGM1A25)
set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1)
set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHE_GATEMATE_DEVICES} CACHE STRING
"Include support for these GateMate devices (available: ${ALL_HIMBAECHE_GATEMATE_DEVICES})")
#set(ALL_HIMBAECHEL_GATEMATE_DEVICES CCGM1A1 CCGM1A2 CCGM1A4 CCGM1A9 CCGM1A16 CCGM1A25)
set(ALL_HIMBAECHEL_GATEMATE_DEVICES CCGM1A1 CCGM1A2)
set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHEL_GATEMATE_DEVICES} CACHE STRING
"Include support for these GateMate devices (available: ${ALL_HIMBAECHEL_GATEMATE_DEVICES})")
if (HIMBAECHEL_GATEMATE_DEVICES STREQUAL "all")
set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHE_GATEMATE_DEVICES})
set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHEL_GATEMATE_DEVICES})
endif()
message(STATUS "Enabled Himbaechel-GateMate devices: ${HIMBAECHEL_GATEMATE_DEVICES}")
foreach (device ${HIMBAECHEL_GATEMATE_DEVICES})
if (NOT device IN_LIST ALL_HIMBAECHE_GATEMATE_DEVICES)
if (NOT device IN_LIST ALL_HIMBAECHEL_GATEMATE_DEVICES)
message(FATAL_ERROR "Device ${device} is not a supported GateMate device")
endif()

View File

@ -46,6 +46,77 @@ struct BitstreamBackend
return reinterpret_cast<const GateMateTileExtraDataPOD *>(ctx->chip_info->tile_insts[tile].extra_data.get());
}
bool need_inversion(CellInfo *cell, IdString port)
{
PortRef sink;
sink.cell = cell;
sink.port = port;
NetInfo *net_info = cell->getPort(port);
if (!net_info)
return false;
WireId src_wire = ctx->getNetinfoSourceWire(net_info);
WireId dst_wire = ctx->getNetinfoSinkWire(net_info, sink, 0);
if (src_wire == WireId())
return false;
WireId cursor = dst_wire;
bool invert = false;
while (cursor != WireId() && cursor != src_wire) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end())
break;
PipId pip = it->second.pip;
if (pip == PipId())
break;
invert ^= ctx->isPipInverting(pip);
cursor = ctx->getPipSrcWire(pip);
}
return invert;
}
void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict<IdString, Property> &params)
{
unsigned init_val = int_or_default(params, init);
bool invert = need_inversion(cell, port);
if (invert) {
if (port.in(id_IN1, id_IN3))
init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1;
else
init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2;
params[init] = Property(init_val, 4);
}
}
void update_cpe_inv(CellInfo *cell, IdString port, IdString param, dict<IdString, Property> &params)
{
unsigned init_val = int_or_default(params, param);
bool invert = need_inversion(cell, port);
if (invert) {
params[param] = Property(3 - init_val, 2);
}
}
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit, dict<IdString, Property> &params)
{
// Mux inversion data is contained in other CPE half
Loc l = ctx->getBelLocation(cell->bel);
CellInfo *cell_u = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 0)));
unsigned init_val = int_or_default(params, param);
bool invert = need_inversion(cell_u, port);
if (invert) {
int old = (init_val >> bit) & 1;
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
params[param] = Property(val, 4);
}
}
std::vector<bool> int_to_bitvector(int val, int size)
{
std::vector<bool> bv;
@ -105,36 +176,34 @@ struct BitstreamBackend
boost::replace_all(word, "TES.", stringf("TES%d.", id));
if (boost::starts_with(word, "SB_DRIVE.")) {
Loc l;
auto ti = *tile_extra_data(pip.tile);
tile_xy(ctx->chip_info, pip.tile, l.x, l.y);
l.z = 0;
BelId cpe_bel = ctx->getBelByLocation(l);
// Only if switchbox is inside core (same as sharing location with CPE)
if (cpe_bel != BelId() && ctx->getBelType(cpe_bel).in(id_CPE_HALF_L, id_CPE_HALF_U)) {
// Convert coordinates into in-tile coordinates
int xt = ((l.x - 2 - 1) + 16) % 8;
int yt = ((l.y - 2 - 1) + 16) % 8;
// Bitstream data for certain SB_DRIVES is located in other tiles
switch (word[14]) {
case '3':
if (xt >= 4) {
if (ti.tile_x >= 4) {
loc.x -= 2;
word[14] = '1';
};
break;
case '4':
if (yt >= 4) {
if (ti.tile_y >= 4) {
loc.y -= 2;
word[14] = '2';
};
break;
case '1':
if (xt <= 3) {
if (ti.tile_x <= 3) {
loc.x += 2;
word[14] = '3';
};
break;
case '2':
if (yt <= 3) {
if (ti.tile_y <= 3) {
loc.y += 2;
word[14] = '4';
};
@ -154,39 +223,75 @@ struct BitstreamBackend
{
ChipConfig cc;
cc.chip_name = device;
int bank[9] = {0};
int bank[uarch->dies][9] = {0};
for (auto &cell : ctx->cells) {
CfgLoc loc = get_config_loc(cell.second.get()->bel.tile);
auto &params = cell.second.get()->params;
switch (cell.second->type.index) {
case id_CC_IBUF.index:
case id_CC_TOBUF.index:
case id_CC_OBUF.index:
case id_CC_IOBUF.index:
case id_CC_LVDS_IBUF.index:
case id_CC_LVDS_TOBUF.index:
case id_CC_LVDS_OBUF.index:
case id_CC_LVDS_IOBUF.index:
case id_CPE_IBUF.index:
case id_CPE_TOBUF.index:
case id_CPE_OBUF.index:
case id_CPE_IOBUF.index:
case id_CPE_LVDS_IBUF.index:
case id_CPE_LVDS_TOBUF.index:
case id_CPE_LVDS_OBUF.index:
case id_CPE_LVDS_IOBUF.index:
for (auto &p : params) {
bank[ctx->get_bel_package_pin(cell.second.get()->bel)->pad_bank] = 1;
bank[loc.die][ctx->get_bel_package_pin(cell.second.get()->bel)->pad_bank] = 1;
cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits());
}
break;
case id_CPE_HALF_U.index:
case id_CPE_HALF_L.index: {
// Update configuration bits based on signal inversion
dict<IdString, Property> params = cell.second->params;
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
if (cell.second->type.in(id_CPE_HALF_U) && func != C_MX4) {
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params);
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00, params);
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params);
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01, params);
}
if (cell.second->type.in(id_CPE_HALF_L)) {
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02, params);
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02, params);
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03, params);
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03, params);
if (func == C_MX4) {
update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0, params);
update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1, params);
update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2, params);
update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3, params);
}
}
if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK, params);
update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN, params);
bool set = int_or_default(params, id_C_EN_SR, 0) == 1;
if (set)
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_SET, params);
else
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES, params);
}
int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id;
for (auto &p : params) {
cc.tiles[loc].add_word(stringf("CPE%d.%s", id, p.first.c_str(ctx)), p.second.as_bits());
}
} break;
case id_BUFG.index: {
Loc l = ctx->getBelLocation(cell.second->bel);
cc.configs[0].add_word(stringf("GLBOUT.GLB%d_EN", l.z), int_to_bitvector(1, 1));
case id_CLKIN.index: {
for (auto &p : params) {
cc.configs[loc.die].add_word(stringf("CLKIN.%s", p.first.c_str(ctx)), p.second.as_bits());
}
} break;
case id_GLBOUT.index: {
for (auto &p : params) {
cc.configs[loc.die].add_word(stringf("GLBOUT.%s", p.first.c_str(ctx)), p.second.as_bits());
}
} break;
case id_PLL.index: {
Loc l = ctx->getBelLocation(cell.second->bel);
for (auto &p : params) {
cc.configs[0].add_word(stringf("PLL%d.%s", l.z - 4, p.first.c_str(ctx)), p.second.as_bits());
cc.configs[loc.die].add_word(stringf("PLL%d.%s", l.z - 2, p.first.c_str(ctx)), p.second.as_bits());
}
} break;
case id_RAM.index: {
@ -224,15 +329,21 @@ struct BitstreamBackend
}
}
cc.configs[0].add_word("GPIO.BANK_N1", int_to_bitvector(bank[0], 1));
cc.configs[0].add_word("GPIO.BANK_N2", int_to_bitvector(bank[1], 1));
cc.configs[0].add_word("GPIO.BANK_E1", int_to_bitvector(bank[2], 1));
cc.configs[0].add_word("GPIO.BANK_E2", int_to_bitvector(bank[3], 1));
cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(bank[4], 1));
cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(bank[5], 1));
cc.configs[0].add_word("GPIO.BANK_S1", int_to_bitvector(bank[6], 1));
cc.configs[0].add_word("GPIO.BANK_S2", int_to_bitvector(bank[7], 1));
cc.configs[0].add_word("GPIO.BANK_CFG", int_to_bitvector(bank[8], 1));
for (int i = 0; i < uarch->dies; i++) {
cc.configs[i].add_word("GPIO.BANK_N1", int_to_bitvector(bank[i][0], 1));
cc.configs[i].add_word("GPIO.BANK_N2", int_to_bitvector(bank[i][1], 1));
cc.configs[i].add_word("GPIO.BANK_E1", int_to_bitvector(bank[i][2], 1));
cc.configs[i].add_word("GPIO.BANK_E2", int_to_bitvector(bank[i][3], 1));
cc.configs[i].add_word("GPIO.BANK_W1", int_to_bitvector(bank[i][4], 1));
cc.configs[i].add_word("GPIO.BANK_W2", int_to_bitvector(bank[i][5], 1));
cc.configs[i].add_word("GPIO.BANK_S1", int_to_bitvector(bank[i][6], 1));
cc.configs[i].add_word("GPIO.BANK_S2", int_to_bitvector(bank[i][7], 1));
cc.configs[i].add_word("GPIO.BANK_CFG", int_to_bitvector(bank[i][8], 1));
}
if (uarch->dies == 2) {
cc.configs[0].add_word("D2D.N", int_to_bitvector(1, 1));
cc.configs[1].add_word("D2D.S", int_to_bitvector(1, 1));
}
for (auto &net : ctx->nets) {
NetInfo *ni = net.second.get();

View File

@ -43,6 +43,7 @@ struct GateMateCCFReader
std::istream &in;
int lineno;
dict<IdString, Property> defaults;
std::vector<int> count;
GateMateCCFReader(Context *ctx, GateMateImpl *uarch, std::istream &in) : ctx(ctx), uarch(uarch), in(in){};
@ -81,11 +82,17 @@ struct GateMateCCFReader
if (name == "LOC") {
if (is_default)
log_error("Value '%s' can not be defined for default GPIO in line %d.\n", name.c_str(), lineno);
if (ctx->get_package_pin_bel(ctx->id(value)) == BelId())
if (ctx->get_package_pin_bel(ctx->id(value)) == BelId() && value != "SER_CLK" && value != "SER_CLK_N")
log_error("Unknown location '%s' used in line %d.\n", value.c_str(), lineno);
if (!uarch->available_pads.count(ctx->id(value)))
log_error("Pad '%s' used in line %d not available.\n", value.c_str(), lineno);
props->emplace(id_LOC, Property(value));
for (int i = 0; i < uarch->dies; i++) {
if (uarch->locations.count(std::make_pair(ctx->id(value), i)))
count[i]++;
else
count[i]--;
}
uarch->available_pads.erase(ctx->id(value));
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
@ -148,6 +155,7 @@ struct GateMateCCFReader
return std::all_of(str.begin(), str.end(), [](char c) { return isblank(c) || c == '\r' || c == '\n'; });
};
lineno = 0;
count = std::vector<int>(uarch->dies, 0);
while (std::getline(in, line)) {
++lineno;
// Both // and # are considered start of comment
@ -213,6 +221,14 @@ struct GateMateCCFReader
}
if (!isempty(linebuf))
log_error("unexpected end of CCF file\n");
int max_num = 0;
uarch->preferred_die = 0;
for (int i = 0; i < uarch->dies; i++) {
if (count[i] > max_num) {
max_num = count[i];
uarch->preferred_die = i;
}
}
}
};

View File

@ -47,6 +47,24 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
if (type == id_CPE_HALF_L) {
add_port(id_COUTY1, PORT_OUT);
}
} else if (type.in(id_CLKIN)) {
for (int i = 0; i < 4; i++) {
add_port(ctx->idf("CLK%d", i), PORT_IN);
add_port(ctx->idf("CLK_REF%d", i), PORT_OUT);
}
add_port(id_SER_CLK, PORT_IN);
} else if (type.in(id_GLBOUT)) {
for (int i = 0; i < 4; i++) {
add_port(ctx->idf("CLK0_%d", i), PORT_IN);
add_port(ctx->idf("CLK90_%d", i), PORT_IN);
add_port(ctx->idf("CLK180_%d", i), PORT_IN);
add_port(ctx->idf("CLK270_%d", i), PORT_IN);
add_port(ctx->idf("CLK_REF_OUT%d", i), PORT_IN);
add_port(ctx->idf("USR_GLB%d", i), PORT_IN);
add_port(ctx->idf("USR_FB%d", i), PORT_IN);
add_port(ctx->idf("CLK_FB%d", i), PORT_OUT);
add_port(ctx->idf("GLB%d", i), PORT_OUT);
}
} else if (type.in(id_CC_BUFG)) {
add_port(id_I, PORT_IN);
add_port(id_O, PORT_OUT);

View File

@ -957,12 +957,68 @@ X(CLOCK1)
X(CLOCK2)
X(CLOCK3)
X(CLOCK4)
// hardware primitive BUFG
X(BUFG)
// BUFG pins
//X(I)
//X(O)
//X(IO)
//X(I_P)
//X(I_N)
//X(O_P)
//X(O_N)
//X(IO_P)
//X(IO_N)
// hardware primitive CLKIN
X(CLKIN)
// CLKIN pins
//X(CLK0)
X(CLK1)
X(CLK2)
X(CLK3)
X(SER_CLK)
X(CLK_REF0)
X(CLK_REF1)
X(CLK_REF2)
X(CLK_REF3)
// hardware primitive GLBOUT
X(GLBOUT)
// GLBOUT pins
X(CLK0_0)
X(CLK90_0)
X(CLK180_0)
X(CLK270_0)
X(CLK_REF_OUT0)
X(USR_GLB0)
X(USR_FB0)
X(CLK_FB0)
X(GLB0)
X(CLK0_1)
X(CLK90_1)
X(CLK180_1)
X(CLK270_1)
X(CLK_REF_OUT1)
X(USR_GLB1)
X(USR_FB1)
X(CLK_FB1)
X(GLB1)
X(CLK0_2)
X(CLK90_2)
X(CLK180_2)
X(CLK270_2)
X(CLK_REF_OUT2)
X(USR_GLB2)
X(USR_FB2)
X(CLK_FB2)
X(GLB2)
X(CLK0_3)
X(CLK90_3)
X(CLK180_3)
X(CLK270_3)
X(CLK_REF_OUT3)
X(USR_GLB3)
X(USR_FB3)
X(CLK_FB3)
X(GLB3)
// hardware primitive PLL
X(PLL)
@ -1593,6 +1649,10 @@ X(F_RSTN)
//X(FRD_ADDRX[14])
//X(FRD_ADDR[15])
//X(FRD_ADDRX[15])
//X(CLOCK1)
//X(CLOCK2)
//X(CLOCK3)
//X(CLOCK4)
// hardware primitive SERDES
X(SERDES)
@ -1995,9 +2055,6 @@ X(LVDS_EN)
X(LVDS_IE)
//X(LVDS_RTERM)
// GPIO virtual pin
X(DI)
X(CPE_HALF)
X(RAM_I1)
X(RAM_I2)
@ -2104,3 +2161,12 @@ X(PLL_BISC_CO)
X(CC_ADDF2)
X(A2)
X(B2)
X(CPE_IBUF)
X(CPE_OBUF)
X(CPE_TOBUF)
X(CPE_IOBUF)
X(CPE_LVDS_IBUF)
X(CPE_LVDS_OBUF)
X(CPE_LVDS_TOBUF)
X(CPE_LVDS_IOBUF)

View File

@ -28,7 +28,10 @@ NPNR_PACKED_STRUCT(struct GateMateTileExtraDataPOD {
uint8_t die;
uint8_t bit_x;
uint8_t bit_y;
uint8_t tile_x;
uint8_t tile_y;
uint8_t prim_id;
uint16_t dummy;
});
NPNR_PACKED_STRUCT(struct GateMatePipExtraDataPOD {
@ -52,6 +55,13 @@ NPNR_PACKED_STRUCT(struct GateMateBelPinConstraintPOD {
NPNR_PACKED_STRUCT(struct GateMateBelExtraDataPOD { RelSlice<GateMateBelPinConstraintPOD> constraints; });
NPNR_PACKED_STRUCT(struct GateMatePadExtraDataPOD {
uint16_t x;
uint16_t y;
uint16_t z;
uint16_t dummy;
});
enum MuxFlags
{
MUX_INVERT = 1,

View File

@ -38,6 +38,12 @@ void GateMateImpl::init_database(Arch *arch)
arch->load_chipdb(stringf("gatemate/chipdb-%s.bin", args.device.c_str()));
arch->set_package("FBGA324");
arch->set_speed_grade("DEFAULT");
dies = std::stoi(args.device.substr(6));
}
const GateMateTileExtraDataPOD *GateMateImpl::tile_extra_data(int tile) const
{
return reinterpret_cast<const GateMateTileExtraDataPOD *>(ctx->chip_info->tile_insts[tile].extra_data.get());
}
void GateMateImpl::init(Context *ctx)
@ -45,8 +51,12 @@ void GateMateImpl::init(Context *ctx)
HimbaechelAPI::init(ctx);
for (const auto &pad : ctx->package_info->pads) {
available_pads.emplace(IdString(pad.package_pin));
available_pads.emplace(ctx->id("SER_CLK"));
available_pads.emplace(ctx->id("SER_CLK_N"));
BelId bel = ctx->getBelByName(IdStringList::concat(IdString(pad.tile), IdString(pad.bel)));
bel_to_pad.emplace(bel, &pad);
locations.emplace(std::make_pair(IdString(pad.package_pin), tile_extra_data(bel.tile)->die),
ctx->getBelLocation(bel));
}
for (auto bel : ctx->getBels()) {
auto *ptr = bel_extra_data(bel);
@ -54,6 +64,10 @@ void GateMateImpl::init(Context *ctx)
for (const auto &p : ptr->constraints)
pins.emplace(IdString(p.name), &p);
pin_to_constr.emplace(bel, pins);
if (ctx->getBelType(bel).in(id_CLKIN, id_GLBOUT, id_PLL, id_USR_RSTN, id_CFG_CTRL, id_SERDES)) {
locations.emplace(std::make_pair(ctx->getBelName(bel)[1], tile_extra_data(bel.tile)->die),
ctx->getBelLocation(bel));
}
}
}
@ -72,6 +86,11 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
if (cell == nullptr) {
return true;
}
// TODO: remove when placemente per die is better handled
if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die)
return false;
if (ctx->getBelType(bel).in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U)) {
Loc loc = ctx->getBelLocation(bel);
const CellInfo *adj_half = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == 1 ? 0 : 1)));
@ -160,77 +179,6 @@ bool GateMateImpl::getClusterPlacement(ClusterId cluster, BelId root_bel,
return getChildPlacement(root_cell, root_loc, placement);
}
bool GateMateImpl::need_inversion(CellInfo *cell, IdString port)
{
PortRef sink;
sink.cell = cell;
sink.port = port;
NetInfo *net_info = cell->getPort(port);
if (!net_info)
return false;
WireId src_wire = ctx->getNetinfoSourceWire(net_info);
WireId dst_wire = ctx->getNetinfoSinkWire(net_info, sink, 0);
if (src_wire == WireId())
return false;
WireId cursor = dst_wire;
bool invert = false;
while (cursor != WireId() && cursor != src_wire) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end())
break;
PipId pip = it->second.pip;
if (pip == PipId())
break;
invert ^= ctx->isPipInverting(pip);
cursor = ctx->getPipSrcWire(pip);
}
return invert;
}
void GateMateImpl::update_cpe_lt(CellInfo *cell, IdString port, IdString init)
{
unsigned init_val = int_or_default(cell->params, init);
bool invert = need_inversion(cell, port);
if (invert) {
if (port.in(id_IN1, id_IN3))
init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1;
else
init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2;
cell->params[init] = Property(init_val, 4);
}
}
void GateMateImpl::update_cpe_inv(CellInfo *cell, IdString port, IdString param)
{
unsigned init_val = int_or_default(cell->params, param);
bool invert = need_inversion(cell, port);
if (invert) {
cell->params[param] = Property(3 - init_val, 2);
}
}
void GateMateImpl::update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit)
{
// Mux inversion data is contained in other CPE half
Loc l = ctx->getBelLocation(cell->bel);
CellInfo *cell_l = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 1)));
unsigned init_val = int_or_default(cell_l->params, param);
bool invert = need_inversion(cell, port);
if (invert) {
int old = (init_val >> bit) & 1;
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
cell_l->params[param] = Property(val, 4);
}
}
void GateMateImpl::rename_param(CellInfo *cell, IdString name, IdString new_name, int width)
{
if (cell->params.count(name)) {
@ -274,50 +222,6 @@ void GateMateImpl::preRoute() { route_clock(); }
void GateMateImpl::postRoute()
{
ctx->assignArchInfo();
// Update configuration bits based on signal inversion
for (auto &cell : ctx->cells) {
if (cell.second->type.in(id_CPE_HALF_U)) {
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
if (func != C_MX4) {
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00);
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00);
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01);
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01);
} else {
update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0);
update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1);
update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2);
update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3);
}
}
if (cell.second->type.in(id_CPE_HALF_L)) {
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02);
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02);
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03);
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03);
}
if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK);
update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN);
bool set = int_or_default(cell.second->params, id_C_EN_SR, 0) == 1;
if (set)
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_SET);
else
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES);
}
}
// Sanity check
for (auto &c : ctx->cells) {
CellInfo *cell = c.second.get();
if (!cell->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
for (auto port : cell->ports) {
if (need_inversion(cell, port.first)) {
log_error("Unhandled cell '%s' of type '%s' port '%s'\n", cell->name.c_str(ctx),
cell->type.c_str(ctx), port.first.c_str(ctx));
}
}
}
}
print_utilisation(ctx);
const ArchArgs &args = ctx->args;
@ -367,8 +271,8 @@ void GateMateImpl::assign_cell_info()
// Bel bucket functions
IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const
{
if (cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF,
id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF))
if (cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF))
return id_GPIO;
else if (cell_type.in(id_CPE_HALF_U, id_CPE_HALF_L, id_CPE_HALF))
return id_CPE_HALF;
@ -388,8 +292,8 @@ bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
{
IdString bel_type = ctx->getBelType(bel);
if (bel_type == id_GPIO)
return cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF,
id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF);
return cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF);
else if (bel_type == id_CPE_HALF_U)
return cell_type.in(id_CPE_HALF_U, id_CPE_HALF);
else if (bel_type == id_CPE_HALF_L)

View File

@ -62,9 +62,14 @@ struct GateMateImpl : HimbaechelAPI
bool isPipInverting(PipId pip) const override;
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
std::set<IdString> available_pads;
std::map<BelId, const PadInfoPOD *> bel_to_pad;
pool<IdString> ddr_nets;
dict<std::pair<IdString, int>, Loc> locations;
int dies;
int preferred_die;
private:
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
@ -75,10 +80,6 @@ struct GateMateImpl : HimbaechelAPI
void parse_ccf(const std::string &filename);
void assign_cell_info();
bool need_inversion(CellInfo *cell, IdString port);
void update_cpe_lt(CellInfo *cell, IdString port, IdString init);
void update_cpe_inv(CellInfo *cell, IdString port, IdString param);
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit);
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
void route_clock();

View File

@ -46,6 +46,8 @@ class TileExtraData(BBAStruct):
die : int = 0
bit_x: int = 0
bit_y: int = 0
tile_x: int = 0
tile_y: int = 0
prim_id : int = 0
def serialise_lists(self, context: str, bba: BBAWriter):
@ -54,7 +56,10 @@ class TileExtraData(BBAStruct):
bba.u8(self.die)
bba.u8(self.bit_x)
bba.u8(self.bit_y)
bba.u8(self.tile_x)
bba.u8(self.tile_y)
bba.u8(self.prim_id)
bba.u16(0)
@dataclass
class PipExtraData(BBAStruct):
@ -111,6 +116,20 @@ class BelExtraData(BBAStruct):
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_constraints", len(self.constraints))
@dataclass
class PadExtraData(BBAStruct):
x: int = 0
y: int = 0
z: int = 0
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u16(self.x)
bba.u16(self.y)
bba.u16(self.z)
bba.u16(0)
def set_timings(ch):
speed = "DEFAULT"
tmg = ch.set_speed_grades([speed])
@ -140,7 +159,7 @@ def set_timings(ch):
dff.add_setup_hold("CLK", "IN4", ClockEdge.RISING, TimingValue(60), TimingValue(50))
dff.add_clock_out("CLK", "OUT", ClockEdge.RISING, TimingValue(60))
EXPECTED_VERSION = 1.1
EXPECTED_VERSION = 1.2
def main():
# Range needs to be +1, but we are adding +2 more to coordinates, since
@ -178,16 +197,12 @@ def main():
tt.create_group(group.name, group.type)
for wire in sorted(die.get_endpoints_for_type(type_name)):
tt.create_wire(wire.name, wire.type)
if "GPIO" in type_name:
tt.create_wire("GPIO.DI", "CPE_VIRTUAL_WIRE")
for prim in sorted(die.get_primitives_for_type(type_name)):
bel = tt.create_bel(prim.name, prim.type, prim.z)
extra = BelExtraData()
for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)):
extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,0 if constr.pin_num==2 else 1)
bel.extra_data = extra
if prim.name == "GPIO":
tt.add_bel_pin(bel, "DI", "GPIO.DI", PinType.INPUT)
for pin in sorted(die.get_primitive_pins(prim.type)):
tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir)
for mux in sorted(die.get_mux_connections_for_type(type_name)):
@ -204,16 +219,13 @@ def main():
if mux.name.startswith("SB_DRIVE"):
plane = int(mux.name[10:12])
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(mux.name), mux.bits, mux.value, mux_flags, plane)
if "GPIO" in type_name:
tt.create_pip("GPIO.DI", "GPIO.IN1")
tt.create_pip("GPIO.DI", "GPIO.IN2")
# Setup tile grid
for x in range(dev.max_col() + 3):
for y in range(dev.max_row() + 3):
ti = ch.set_tile_type(x, y, dev.get_tile_type(x - 2,y - 2))
tileinfo = dev.get_tile_info(x - 2,y - 2)
ti.extra_data = TileExtraData(tileinfo.die, tileinfo.bit_x, tileinfo.bit_y, tileinfo.prim_index)
ti.extra_data = TileExtraData(tileinfo.die, tileinfo.bit_x, tileinfo.bit_y, tileinfo.tile_x, tileinfo.tile_y, tileinfo.prim_index)
# Create nodes between tiles
for _,nodes in dev.get_connections():
@ -226,7 +238,8 @@ def main():
for package in dev.get_packages():
pkg = ch.create_package(package)
for pad in sorted(dev.get_package_pads(package)):
pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags)
pp = pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags)
pp.extra_data = PadExtraData(pad.ddr.x+2, pad.ddr.y+2, pad.ddr.z)
ch.write_bba(args.bba)

View File

@ -62,7 +62,8 @@ void GateMateImpl::drawBel(std::vector<GraphicElement> &g, GraphicElement::style
el.y2 = el.y1 + 0.60;
g.push_back(el);
break;
case id_BUFG.index:
case id_CLKIN.index:
case id_GLBOUT.index:
el.x1 = loc.x + 0.15 + loc.z * 0.20;
el.x2 = el.x1 + 0.15;
el.y1 = loc.y + 0.10;
@ -70,7 +71,7 @@ void GateMateImpl::drawBel(std::vector<GraphicElement> &g, GraphicElement::style
g.push_back(el);
break;
case id_PLL.index:
el.x1 = loc.x + 0.15 + (loc.z - 4) * 0.20;
el.x1 = loc.x + 0.15 + (loc.z - 2) * 0.20;
el.x2 = el.x1 + 0.15;
el.y1 = loc.y + 0.60;
el.y2 = el.y1 + 0.30;

View File

@ -178,7 +178,7 @@ void GateMatePacker::pack_misc()
continue;
ci.type = id_USR_RSTN;
ci.cluster = ci.name;
Loc fixed_loc(0, 0, 3); // USR_RSTN
Loc fixed_loc = uarch->locations[std::make_pair(id_USR_RSTN, uarch->preferred_die)];
ctx->bindBel(ctx->getBelByLocation(fixed_loc), &ci, PlaceStrength::STRENGTH_FIXED);
move_ram_i_fixed(&ci, id_USR_RSTN, fixed_loc);
@ -189,7 +189,7 @@ void GateMatePacker::pack_misc()
continue;
ci.type = id_CFG_CTRL;
ci.cluster = ci.name;
Loc fixed_loc(0, 0, 2); // CFG_CTRL
Loc fixed_loc = uarch->locations[std::make_pair(id_CFG_CTRL, uarch->preferred_die)];
ctx->bindBel(ctx->getBelByLocation(fixed_loc), &ci, PlaceStrength::STRENGTH_FIXED);
move_ram_o_fixed(&ci, id_CLK, fixed_loc);
@ -261,6 +261,7 @@ void GateMateImpl::pack()
packer.pack_addf();
packer.pack_cpe();
packer.remove_constants();
packer.remove_clocking();
}
NEXTPNR_NAMESPACE_END

View File

@ -42,6 +42,7 @@ struct GateMatePacker
void pack_serdes();
void remove_constants();
void remove_clocking();
void remove_not_used();
private:
@ -60,7 +61,6 @@ struct GateMatePacker
uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt);
uint8_t ram_clk_signal(CellInfo *cell, IdString port);
bool is_gpio_valid_dff(CellInfo *dff);
BelId get_bank_cpe(int bank);
// Cell creating
CellInfo *create_cell_ptr(IdString type, IdString name);
void flush_cells();
@ -69,6 +69,8 @@ struct GateMatePacker
pool<IdString> packed_cells;
std::map<NetInfo *, int> global_signals;
std::vector<CellInfo *> clkin;
std::vector<CellInfo *> glbout;
Context *ctx;
GateMateImpl *uarch;

View File

@ -78,17 +78,22 @@ void GateMatePacker::sort_bufg()
flush_cells();
}
static int glb_mux_mapping[] = {
// CLK0_0 CLK90_0 CLK180_0 CLK270_0 CLK0_1 CLK0_2 CLK0_3
4, 5, 6, 7, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, // GLBOUT 0
// CLK90_0 CLK0_1 CLK90_1 CLK180_1 CLK270_1 CLK90_2 CLK90_3
0, 1, 0, 0, 4, 5, 6, 7, 0, 2, 0, 0, 0, 3, 0, 0, // GLBOUT 1
// CLK180_0 CLK180_1 CLK0_2 CLK90_2 CLK180_2 CLK270_2 CLK180_3
0, 0, 1, 0, 0, 0, 2, 0, 4, 5, 6, 7, 0, 0, 3, 0, // GLBOUT 2
// CLK270_0 CLK270_1 CLK270_2 CLK0_3 CLK90_3 CLK180_3 CLK270_3
0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 4, 5, 6, 7, // GLBOUT 3
};
void GateMatePacker::pack_bufg()
{
log_info("Packing BUFGs..\n");
CellInfo *bufg[4] = {nullptr};
CellInfo *pll[4] = {nullptr};
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_PLL))
continue;
pll[ci.constr_z - 4] = &ci;
}
auto update_bufg_port = [&](CellInfo *cell, int port_num, int pll_num) {
CellInfo *b = net_only_drives(ctx, cell->getPort(ctx->idf("CLK%d", 90 * port_num)), is_bufg, id_I, false);
@ -105,11 +110,14 @@ void GateMatePacker::pack_bufg()
}
};
for (int i = 0; i < 4; i++) {
if (pll[i]) {
for (int j = 0; j < 4; j++)
update_bufg_port(pll[i], j, i);
}
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_PLL))
continue;
int index = ci.constr_z - 2;
pll[index] = &ci;
for (int j = 0; j < 4; j++)
update_bufg_port(pll[index], j, index);
}
for (auto &cell : ctx->cells) {
@ -118,34 +126,32 @@ void GateMatePacker::pack_bufg()
continue;
NetInfo *in_net = ci.getPort(id_I);
int die = uarch->preferred_die;
if (in_net) {
bool is_cpe_source = true;
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_GPIO) {
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
if (pad_info->flags)
is_cpe_source = false;
if (in_net->driver.cell) {
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_GPIO) {
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
if (pad_info->flags) {
int index = pad_info->flags - 1;
die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die;
if (!clkin[die]->getPort(ctx->idf("CLK%d", index)))
clkin[die]->connectPort(ctx->idf("CLK%d", index), in_net->driver.cell->getPort(id_I));
}
}
} else {
// SER_CLK
clkin[die]->connectPort(id_SER_CLK, in_net);
}
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_PLL) {
is_cpe_source = false;
}
if (is_cpe_source) {
ci.cluster = ci.name;
}
copy_constraint(in_net, ci.getPort(id_O));
}
ci.type = id_BUFG;
}
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_BUFG))
continue;
NetInfo *in_net = ci.getPort(id_I);
if (in_net && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) {
for (int i = 0; i < 4; i++) {
if (bufg[i] == nullptr) {
bufg[i] = &ci;
break;
copy_constraint(in_net, ci.getPort(id_O));
if ((in_net->driver.cell && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) ||
!in_net->driver.cell) {
for (int i = 0; i < 4; i++) {
if (bufg[i] == nullptr) {
bufg[i] = &ci;
break;
}
}
}
}
@ -155,13 +161,82 @@ void GateMatePacker::pack_bufg()
if (bufg[i]) {
CellInfo &ci = *bufg[i];
global_signals.emplace(ci.getPort(id_O), i);
Loc fixed_loc(33 + 2, 131 + 2, i); // BUFG
BelId bufg_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(bufg_bel, &ci, PlaceStrength::STRENGTH_FIXED);
if (ci.cluster != ClusterId())
move_ram_o_fixed(&ci, id_I, fixed_loc);
int glb_mux = 0;
NetInfo *in_net = ci.getPort(id_I);
int die = uarch->preferred_die;
if (in_net->driver.cell) {
bool user_glb = true;
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_GPIO) {
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
if (pad_info->flags) {
die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die;
clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
NetInfo *conn = ctx->createNet(ci.name);
clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn);
glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn);
user_glb = false;
}
}
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_PLL) {
CellInfo *pll = in_net->driver.cell;
int pll_index = ctx->getBelLocation(pll->bel).z - 2;
int pll_out = 0;
if (pll->getPort(id_CLK0) == in_net)
pll_out = 0;
else if (pll->getPort(id_CLK90) == in_net)
pll_out = 1;
else if (pll->getPort(id_CLK180) == in_net)
pll_out = 2;
else if (pll->getPort(id_CLK270) == in_net)
pll_out = 3;
else
log_error("Uknown connecton on BUFG to PLL.\n");
glb_mux = glb_mux_mapping[i * 16 + pll_index * 4 + pll_out];
ci.movePortTo(id_I, glbout[die], ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index));
user_glb = false;
}
if (user_glb) {
ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i));
move_ram_o_fixed(glbout[die], ctx->idf("USR_GLB%d", i), ctx->getBelLocation(glbout[die]->bel));
glbout[die]->params[ctx->idf("USR_GLB%d", i)] = Property(Property::State::S1);
}
} else {
// SER_CLK
clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
}
ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i));
glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
glbout[die]->params[ctx->idf("GLB%d", i)] = Property(glb_mux, 3);
packed_cells.emplace(ci.name);
}
}
for (int i = 0; i < 4; i++) {
if (pll[i]) {
NetInfo *feedback_net = pll[i]->getPort(id_CLK_FEEDBACK);
int die = uarch->tile_extra_data(pll[i]->bel.tile)->die;
if (feedback_net) {
if (!global_signals.count(feedback_net)) {
pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i));
move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel));
glbout[die]->params[ctx->idf("USR_FB%d", i)] = Property(Property::State::S1);
} else {
int index = global_signals[feedback_net];
glbout[die]->params[ctx->idf("FB%d", i)] = Property(index, 2);
pll[i]->disconnectPort(id_CLK_FEEDBACK);
}
NetInfo *conn =
ctx->createNet(ctx->idf("%s_%s", glbout[die]->name.c_str(ctx), feedback_net->name.c_str(ctx)));
pll[i]->connectPort(id_CLK_FEEDBACK, conn);
glbout[die]->connectPort(ctx->idf("CLK_FB%d", i), conn);
}
}
}
flush_cells();
}
void GateMatePacker::pll_out(CellInfo *cell, IdString origPort, Loc fixed)
@ -203,6 +278,16 @@ void GateMatePacker::insert_bufg(CellInfo *cell, IdString port)
void GateMatePacker::insert_pll_bufg()
{
for (int i = 0; i < uarch->dies; i++) {
Loc fixed_loc = uarch->locations[std::make_pair(id_CLKIN, i)];
clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i)));
BelId clkin_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(clkin_bel, clkin.back(), PlaceStrength::STRENGTH_FIXED);
glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i)));
fixed_loc = uarch->locations[std::make_pair(id_GLBOUT, i)];
BelId glbout_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(glbout_bel, glbout.back(), PlaceStrength::STRENGTH_FIXED);
}
std::vector<CellInfo *> cells;
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
@ -218,9 +303,33 @@ void GateMatePacker::insert_pll_bufg()
}
}
void GateMatePacker::remove_clocking()
{
auto remove_unused_cells = [&](std::vector<CellInfo *> &cells, const char *type) {
for (auto cell : cells) {
bool used = false;
for (auto port : cell->ports) {
if (cell->getPort(port.first)) {
used = true;
break;
}
}
if (!used) {
BelId bel = cell->bel;
if (bel != BelId())
ctx->unbindBel(bel);
packed_cells.emplace(cell->name);
}
}
};
remove_unused_cells(clkin, "CLKIN");
remove_unused_cells(glbout, "GLBOUT");
flush_cells();
}
void GateMatePacker::pack_pll()
{
int pll_index = 0;
int pll_index[uarch->dies] = {0};
log_info("Packing PLLss..\n");
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
@ -232,15 +341,30 @@ void GateMatePacker::pack_pll()
disconnect_if_gnd(&ci, id_CLK_FEEDBACK);
disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST);
int die = uarch->preferred_die;
NetInfo *clk = ci.getPort(id_CLK_REF);
if (clk) {
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) {
clk = clk->driver.cell->getPort(id_I);
}
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_GPIO) {
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
if (pad_info->flags != 0) {
die = uarch->tile_extra_data(clk->driver.cell->bel.tile)->die;
}
}
}
ci.cluster = ci.name;
ci.constr_abs_z = true;
ci.constr_z = 4 + pll_index; // Position to a proper Z location
ci.constr_z = 2 + pll_index[die]; // Position to a proper Z location
Loc fixed_loc(33 + 2, 131 + 2, 4 + pll_index); // PLL
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", pll_index[die]), die)];
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED);
if (pll_index > 4)
if (pll_index[die] > 4)
log_error("Used more than available PLLs.\n");
if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr)
@ -249,7 +373,7 @@ void GateMatePacker::pack_pll()
if (ci.getPort(id_CLK_REF) != nullptr && ci.getPort(id_USR_CLK_REF) != nullptr)
log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time.\n");
NetInfo *clk = ci.getPort(id_CLK_REF);
clk = ci.getPort(id_CLK_REF);
delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq"));
if (clk) {
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) {
@ -261,10 +385,16 @@ void GateMatePacker::pack_pll()
if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_GPIO)
log_error("CLK_REF must be driven with GPIO pin.\n");
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
if (!(pad_info->flags & 1))
if (pad_info->flags == 0)
log_error("CLK_REF must be driven with CLK dedicated pin.\n");
if (clk->clkconstr)
period = clk->clkconstr->period.minDelay();
ci.movePortTo(id_CLK_REF, clkin[die], ctx->idf("CLK%d", pad_info->flags - 1));
NetInfo *conn = ctx->createNet(ctx->idf("%s_CLK_REF", ci.name.c_str(ctx)));
clkin[die]->connectPort(ctx->idf("CLK_REF%d", pll_index[die]), conn);
ci.connectPort(id_CLK_REF, conn);
}
clk = ci.getPort(id_USR_CLK_REF);
@ -275,10 +405,6 @@ void GateMatePacker::pack_pll()
period = clk->clkconstr->period.minDelay();
}
NetInfo *fbk = ci.getPort(id_CLK_FEEDBACK);
if (fbk && !fbk->driver.cell->type.in(id_CC_BUFG))
move_ram_o_fixed(&ci, id_CLK_FEEDBACK, fixed_loc);
if (ci.getPort(id_CLK_REF_OUT))
log_error("Output CLK_REF_OUT cannot be used if PLL is used.\n");
@ -342,28 +468,28 @@ void GateMatePacker::pack_pll()
// PLL_cfg_val_800_1400 PLL values from 11.08.2021
bool feedback = false;
if (ci.getPort(id_CLK_FEEDBACK)) {
ci.params[ctx->id("CFG_A.FB_PATH")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A_FB_PATH")] = Property(0b1, 1);
feedback = true;
}
ci.params[ctx->id("CFG_A.FINE_TUNE")] = Property(0b00011001000, 11);
ci.params[ctx->id("CFG_A.COARSE_TUNE")] = Property(0b100, 3);
ci.params[ctx->id("CFG_A.AO_SW")] = Property(0b01000, 5);
ci.params[ctx->id("CFG_A.OPEN_LOOP")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.ENFORCE_LOCK")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.PFD_SEL")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.LOCK_DETECT_WIN")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.SYNC_BYPASS")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.FILTER_SHIFT")] = Property(0b10, 2);
ci.params[ctx->id("CFG_A.FAST_LOCK")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A.SAR_LIMIT")] = Property(0b010, 3);
ci.params[ctx->id("CFG_A.OP_LOCK")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.PDIV0_MUX")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A.EN_COARSE_TUNE")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A.EN_USR_CFG")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.PLL_EN_SEL")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_FINE_TUNE")] = Property(0b00011001000, 11);
ci.params[ctx->id("CFG_A_COARSE_TUNE")] = Property(0b100, 3);
ci.params[ctx->id("CFG_A_AO_SW")] = Property(0b01000, 5);
ci.params[ctx->id("CFG_A_OPEN_LOOP")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_ENFORCE_LOCK")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_PFD_SEL")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_LOCK_DETECT_WIN")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_SYNC_BYPASS")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_FILTER_SHIFT")] = Property(0b10, 2);
ci.params[ctx->id("CFG_A_FAST_LOCK")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A_SAR_LIMIT")] = Property(0b010, 3);
ci.params[ctx->id("CFG_A_OP_LOCK")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_PDIV0_MUX")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A_EN_COARSE_TUNE")] = Property(0b1, 1);
ci.params[ctx->id("CFG_A_EN_USR_CFG")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A_PLL_EN_SEL")] = Property(0b0, 1);
ci.params[ctx->id("CFG_A.CI_FILTER_CONST")] = Property(ci_const, 5);
ci.params[ctx->id("CFG_A.CP_FILTER_CONST")] = Property(cp_const, 5);
ci.params[ctx->id("CFG_A_CI_FILTER_CONST")] = Property(ci_const, 5);
ci.params[ctx->id("CFG_A_CP_FILTER_CONST")] = Property(cp_const, 5);
/*
clock path selection
0-0 PDIV0_MUX = 0, FB_PATH = 0 // DCO clock with intern feedback
@ -375,12 +501,12 @@ void GateMatePacker::pack_pll()
bool pdiv0_mux = true;
PllCfgRecord val = get_pll_settings(ref_clk, out_clk, perf_md, low_jitter, pdiv0_mux, feedback);
if (val.f_core > 0) { // cfg exists
ci.params[ctx->id("CFG_A.K")] = Property(val.K, 12);
ci.params[ctx->id("CFG_A.N1")] = Property(val.N1, 6);
ci.params[ctx->id("CFG_A.N2")] = Property(val.N2, 10);
ci.params[ctx->id("CFG_A.M1")] = Property(val.M1, 6);
ci.params[ctx->id("CFG_A.M2")] = Property(val.M2, 10);
ci.params[ctx->id("CFG_A.PDIV1_SEL")] = Property(val.PDIV1 == 2 ? 1 : 0, 1);
ci.params[ctx->id("CFG_A_K")] = Property(val.K, 12);
ci.params[ctx->id("CFG_A_N1")] = Property(val.N1, 6);
ci.params[ctx->id("CFG_A_N2")] = Property(val.N2, 10);
ci.params[ctx->id("CFG_A_M1")] = Property(val.M1, 6);
ci.params[ctx->id("CFG_A_M2")] = Property(val.M2, 10);
ci.params[ctx->id("CFG_A_PDIV1_SEL")] = Property(val.PDIV1 == 2 ? 1 : 0, 1);
} else {
log_error("Unable to configure PLL %s\n", ci.name.c_str(ctx));
}
@ -397,41 +523,41 @@ void GateMatePacker::pack_pll()
for (int i = 0; i < 2; i++) {
char cfg = 'A' + i;
IdString id = i == 0 ? id_PLL_CFG_A : id_PLL_CFG_B;
ci.params[ctx->idf("CFG_%c.CI_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 0, 5), 5);
ci.params[ctx->idf("CFG_%c.CP_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 5, 5), 5);
ci.params[ctx->idf("CFG_%c.N1", cfg)] = Property(extract_bits(ci.params, id, 10, 6), 6);
ci.params[ctx->idf("CFG_%c.N2", cfg)] = Property(extract_bits(ci.params, id, 16, 10), 10);
ci.params[ctx->idf("CFG_%c.M1", cfg)] = Property(extract_bits(ci.params, id, 26, 6), 6);
ci.params[ctx->idf("CFG_%c.M2", cfg)] = Property(extract_bits(ci.params, id, 32, 10), 10);
ci.params[ctx->idf("CFG_%c.K", cfg)] = Property(extract_bits(ci.params, id, 42, 12), 12);
ci.params[ctx->idf("CFG_%c.FB_PATH", cfg)] = Property(extract_bits(ci.params, id, 54, 1), 1);
ci.params[ctx->idf("CFG_%c.FINE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 55, 11), 11);
ci.params[ctx->idf("CFG_%c.COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 66, 3), 3);
ci.params[ctx->idf("CFG_%c.AO_SW", cfg)] = Property(extract_bits(ci.params, id, 69, 5), 5);
ci.params[ctx->idf("CFG_%c.OPEN_LOOP", cfg)] = Property(extract_bits(ci.params, id, 74, 1), 1);
ci.params[ctx->idf("CFG_%c.ENFORCE_LOCK", cfg)] = Property(extract_bits(ci.params, id, 75, 1), 1);
ci.params[ctx->idf("CFG_%c.PFD_SEL", cfg)] = Property(extract_bits(ci.params, id, 76, 1), 1);
ci.params[ctx->idf("CFG_%c.LOCK_DETECT_WIN", cfg)] = Property(extract_bits(ci.params, id, 77, 1), 1);
ci.params[ctx->idf("CFG_%c.SYNC_BYPASS", cfg)] = Property(extract_bits(ci.params, id, 78, 1), 1);
ci.params[ctx->idf("CFG_%c.FILTER_SHIFT", cfg)] = Property(extract_bits(ci.params, id, 79, 2), 2);
ci.params[ctx->idf("CFG_%c.FAST_LOCK", cfg)] = Property(extract_bits(ci.params, id, 81, 1), 1);
ci.params[ctx->idf("CFG_%c.SAR_LIMIT", cfg)] = Property(extract_bits(ci.params, id, 82, 3), 3);
ci.params[ctx->idf("CFG_%c.OP_LOCK", cfg)] = Property(extract_bits(ci.params, id, 85, 1), 1);
ci.params[ctx->idf("CFG_%c.PDIV1_SEL", cfg)] = Property(extract_bits(ci.params, id, 86, 1), 1);
ci.params[ctx->idf("CFG_%c.PDIV0_MUX", cfg)] = Property(extract_bits(ci.params, id, 87, 1), 1);
ci.params[ctx->idf("CFG_%c.EN_COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 88, 1), 1);
ci.params[ctx->idf("CFG_%c.EN_USR_CFG", cfg)] = Property(extract_bits(ci.params, id, 89, 1), 1);
ci.params[ctx->idf("CFG_%c.PLL_EN_SEL", cfg)] = Property(extract_bits(ci.params, id, 90, 1), 1);
int N1 = int_or_default(ci.params, ctx->idf("CFG_%c.N1", cfg));
int N2 = int_or_default(ci.params, ctx->idf("CFG_%c.N2", cfg));
int M1 = int_or_default(ci.params, ctx->idf("CFG_%c.M1", cfg));
int M2 = int_or_default(ci.params, ctx->idf("CFG_%c.M2", cfg));
int K = int_or_default(ci.params, ctx->idf("CFG_%c.K", cfg));
int PDIV1 = bool_or_default(ci.params, ctx->idf("CFG_%c.PDIV1_SEL", cfg)) ? 2 : 0;
ci.params[ctx->idf("CFG_%c_CI_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 0, 5), 5);
ci.params[ctx->idf("CFG_%c_CP_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 5, 5), 5);
ci.params[ctx->idf("CFG_%c_N1", cfg)] = Property(extract_bits(ci.params, id, 10, 6), 6);
ci.params[ctx->idf("CFG_%c_N2", cfg)] = Property(extract_bits(ci.params, id, 16, 10), 10);
ci.params[ctx->idf("CFG_%c_M1", cfg)] = Property(extract_bits(ci.params, id, 26, 6), 6);
ci.params[ctx->idf("CFG_%c_M2", cfg)] = Property(extract_bits(ci.params, id, 32, 10), 10);
ci.params[ctx->idf("CFG_%c_K", cfg)] = Property(extract_bits(ci.params, id, 42, 12), 12);
ci.params[ctx->idf("CFG_%c_FB_PATH", cfg)] = Property(extract_bits(ci.params, id, 54, 1), 1);
ci.params[ctx->idf("CFG_%c_FINE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 55, 11), 11);
ci.params[ctx->idf("CFG_%c_COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 66, 3), 3);
ci.params[ctx->idf("CFG_%c_AO_SW", cfg)] = Property(extract_bits(ci.params, id, 69, 5), 5);
ci.params[ctx->idf("CFG_%c_OPEN_LOOP", cfg)] = Property(extract_bits(ci.params, id, 74, 1), 1);
ci.params[ctx->idf("CFG_%c_ENFORCE_LOCK", cfg)] = Property(extract_bits(ci.params, id, 75, 1), 1);
ci.params[ctx->idf("CFG_%c_PFD_SEL", cfg)] = Property(extract_bits(ci.params, id, 76, 1), 1);
ci.params[ctx->idf("CFG_%c_LOCK_DETECT_WIN", cfg)] = Property(extract_bits(ci.params, id, 77, 1), 1);
ci.params[ctx->idf("CFG_%c_SYNC_BYPASS", cfg)] = Property(extract_bits(ci.params, id, 78, 1), 1);
ci.params[ctx->idf("CFG_%c_FILTER_SHIFT", cfg)] = Property(extract_bits(ci.params, id, 79, 2), 2);
ci.params[ctx->idf("CFG_%c_FAST_LOCK", cfg)] = Property(extract_bits(ci.params, id, 81, 1), 1);
ci.params[ctx->idf("CFG_%c_SAR_LIMIT", cfg)] = Property(extract_bits(ci.params, id, 82, 3), 3);
ci.params[ctx->idf("CFG_%c_OP_LOCK", cfg)] = Property(extract_bits(ci.params, id, 85, 1), 1);
ci.params[ctx->idf("CFG_%c_PDIV1_SEL", cfg)] = Property(extract_bits(ci.params, id, 86, 1), 1);
ci.params[ctx->idf("CFG_%c_PDIV0_MUX", cfg)] = Property(extract_bits(ci.params, id, 87, 1), 1);
ci.params[ctx->idf("CFG_%c_EN_COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 88, 1), 1);
ci.params[ctx->idf("CFG_%c_EN_USR_CFG", cfg)] = Property(extract_bits(ci.params, id, 89, 1), 1);
ci.params[ctx->idf("CFG_%c_PLL_EN_SEL", cfg)] = Property(extract_bits(ci.params, id, 90, 1), 1);
int N1 = int_or_default(ci.params, ctx->idf("CFG_%c_N1", cfg));
int N2 = int_or_default(ci.params, ctx->idf("CFG_%c_N2", cfg));
int M1 = int_or_default(ci.params, ctx->idf("CFG_%c_M1", cfg));
int M2 = int_or_default(ci.params, ctx->idf("CFG_%c_M2", cfg));
int K = int_or_default(ci.params, ctx->idf("CFG_%c_K", cfg));
int PDIV1 = bool_or_default(ci.params, ctx->idf("CFG_%c_PDIV1_SEL", cfg)) ? 2 : 0;
double out_clk;
double ref_clk = 1000.0f / ctx->getDelayNS(period);
if (!bool_or_default(ci.params, ctx->idf("CFG_%c.FB_PATH", cfg))) {
if (bool_or_default(ci.params, ctx->idf("CFG_%c.PDIV0_MUX", cfg))) {
if (!bool_or_default(ci.params, ctx->idf("CFG_%c_FB_PATH", cfg))) {
if (bool_or_default(ci.params, ctx->idf("CFG_%c_PDIV0_MUX", cfg))) {
out_clk = (ref_clk * N1 * N2) / (K * 2 * M1 * M2);
} else {
out_clk = (ref_clk / K) * N1 * N2 * PDIV1;
@ -490,7 +616,7 @@ void GateMatePacker::pack_pll()
ci.type = id_PLL;
pll_index++;
pll_index[die]++;
}
}

View File

@ -24,30 +24,11 @@
NEXTPNR_NAMESPACE_BEGIN
BelId GateMatePacker::get_bank_cpe(int bank)
std::string get_die_name(int total_dies, int die)
{
switch (bank) {
case 0:
return ctx->getBelByLocation(Loc(97 + 2, 128 + 2, 1)); // N1, RAM_O1
case 1:
return ctx->getBelByLocation(Loc(97 + 2, 128 + 2, 0)); // N2, RAM_O2
case 2:
return ctx->getBelByLocation(Loc(160 + 2, 65 + 2, 1)); // E1, RAM_O1
case 3:
return ctx->getBelByLocation(Loc(160 + 2, 65 + 2, 0)); // E2, RAM_O2
case 4:
return ctx->getBelByLocation(Loc(1 + 2, 65 + 2, 1)); // W1, RAM_O1
case 5:
return ctx->getBelByLocation(Loc(1 + 2, 65 + 2, 0)); // W2, RAM_O2
case 6:
return ctx->getBelByLocation(Loc(97 + 2, 1 + 2, 1)); // S1, RAM_O1
case 7:
return ctx->getBelByLocation(Loc(97 + 2, 1 + 2, 0)); // S2, RAM_O2
case 8:
return ctx->getBelByLocation(Loc(49 + 2, 1 + 2, 1)); // S3, RAM_O1
default:
log_error("Unkown bank\n");
}
if (total_dies == 1)
return "";
return stringf("on die '%d%c'", int(die / total_dies) + 1, 'A' + int(die % total_dies));
}
void GateMatePacker::pack_io()
@ -152,6 +133,21 @@ void GateMatePacker::pack_io()
loc = new_loc;
}
if (loc == "SER_CLK" || loc == "SER_CLK_N") {
if (ci.type.in(id_CC_IBUF)) {
log_info(" Constraining '%s' to pad '%s'\n", ci.name.c_str(ctx), loc.c_str());
NetInfo *ser_clk = ci.getPort(id_I);
for (auto s : ci.getPort(id_Y)->users) {
s.cell->disconnectPort(s.port);
s.cell->connectPort(s.port, ser_clk);
}
ci.disconnectPort(id_I);
packed_cells.emplace(ci.name);
continue;
} else {
log_error("SER_CLK and SER_CLK_N pins can only be used on input port.\n");
}
}
if (loc == "UNPLACED") {
const ArchArgs &args = ctx->args;
if (args.options.count("allow-unconstrained"))
@ -271,16 +267,13 @@ void GateMatePacker::pack_io()
}
}
// Disconnect PADs
ci.disconnectPort(id_IO);
ci.disconnectPort(id_I);
ci.disconnectPort(id_O);
ci.disconnectPort(id_IO_P);
ci.disconnectPort(id_IO_N);
ci.disconnectPort(id_I_P);
ci.disconnectPort(id_I_N);
ci.disconnectPort(id_O_P);
ci.disconnectPort(id_O_N);
static dict<IdString, IdString> map_types = {
{id_CC_IBUF, id_CPE_IBUF}, {id_CC_OBUF, id_CPE_OBUF},
{id_CC_TOBUF, id_CPE_TOBUF}, {id_CC_IOBUF, id_CPE_IOBUF},
{id_CC_LVDS_IBUF, id_CPE_LVDS_IBUF}, {id_CC_LVDS_TOBUF, id_CPE_LVDS_TOBUF},
{id_CC_LVDS_OBUF, id_CPE_LVDS_OBUF}, {id_CC_LVDS_IOBUF, id_CPE_LVDS_IOBUF},
};
ci.type = map_types[ci.type];
if (loc.empty() || loc == "UNPLACED") {
if (uarch->available_pads.empty())
@ -291,17 +284,23 @@ void GateMatePacker::pack_io()
}
ci.params[id_LOC] = Property(loc);
BelId bel = ctx->get_package_pin_bel(ctx->id(loc));
BelId bel;
if (uarch->locations.count(std::make_pair(ctx->id(loc), uarch->preferred_die)))
bel = ctx->getBelByLocation(uarch->locations[std::make_pair(ctx->id(loc), uarch->preferred_die)]);
else
bel = ctx->get_package_pin_bel(ctx->id(loc));
if (bel == BelId())
log_error("Unable to constrain IO '%s', device does not have a pin named '%s'\n", ci.name.c_str(ctx),
loc.c_str());
log_info(" Constraining '%s' to pad '%s'\n", ci.name.c_str(ctx), loc.c_str());
log_info(" Constraining '%s' to pad '%s'%s.\n", ci.name.c_str(ctx), loc.c_str(),
get_die_name(uarch->dies, uarch->tile_extra_data(bel.tile)->die).c_str());
if (!ctx->checkBelAvail(bel)) {
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel),
ctx->nameOf(ctx->getBoundBelCell(bel)));
}
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED);
}
flush_cells();
}
void GateMatePacker::pack_io_sel()
@ -309,14 +308,13 @@ void GateMatePacker::pack_io_sel()
std::vector<CellInfo *> cells;
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_OBUF,
id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF))
if (!uarch->getBelBucketForCellType(ci.type).in(id_GPIO))
continue;
cells.push_back(&ci);
}
CellInfo *ddr[9] = {nullptr}; // for each bank
CellInfo *ddr[uarch->dies][9] = {nullptr}; // for each bank
auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool {
NetInfo *clk_net = cell->getPort(id_CLK);
@ -371,7 +369,7 @@ void GateMatePacker::pack_io_sel()
ci.params[id_IN2_FF] = Property(Property::State::S1);
packed_cells.emplace(dff->name);
ci.disconnectPort(id_Y);
dff->movePortTo(id_Q, &ci, id_DI);
dff->movePortTo(id_Q, &ci, id_IN1);
set_in_clk(dff, &ci);
bool invert = bool_or_default(dff->params, id_CLK_INV, 0);
if (invert) {
@ -407,7 +405,7 @@ void GateMatePacker::pack_io_sel()
} else {
ci.params[id_INV_IN2_CLOCK] = Property(Property::State::S1);
}
return false;
return true;
};
for (auto &cell : cells) {
@ -466,7 +464,8 @@ void GateMatePacker::pack_io_sel()
oddr->movePortTo(id_D0, &ci, id_OUT2);
oddr->movePortTo(id_D1, &ci, id_OUT1);
const auto &pad = ctx->get_package_pin(ctx->id(loc));
CellInfo *cpe_half = ddr[pad->pad_bank];
int die = uarch->tile_extra_data(ci.bel.tile)->die;
CellInfo *cpe_half = ddr[die][pad->pad_bank];
if (cpe_half) {
if (cpe_half->getPort(id_IN1) != oddr->getPort(id_DDR))
log_error("DDR port use signal different than already occupied DDR source.\n");
@ -477,8 +476,10 @@ void GateMatePacker::pack_io_sel()
oddr->movePortTo(id_DDR, &ci, id_DDR);
cpe_half = move_ram_o(&ci, id_DDR, false);
uarch->ddr_nets.insert(cpe_half->getPort(id_IN1)->name);
ctx->bindBel(get_bank_cpe(pad->pad_bank), cpe_half, PlaceStrength::STRENGTH_FIXED);
ddr[pad->pad_bank] = cpe_half;
auto l = reinterpret_cast<const GateMatePadExtraDataPOD *>(pad->extra_data.get());
ctx->bindBel(ctx->getBelByLocation(Loc(l->x, l->y, l->z)), cpe_half,
PlaceStrength::STRENGTH_FIXED);
ddr[die][pad->pad_bank] = cpe_half;
}
use_custom_clock = set_out_clk(oddr, &ci);
bool invert = bool_or_default(oddr->params, id_CLK_INV, 0);
@ -505,7 +506,7 @@ void GateMatePacker::pack_io_sel()
}
if (!ff_ibf_merged && !iddr_merged)
ci.renamePort(id_Y, id_DI);
ci.renamePort(id_Y, id_IN1);
}
Loc root_loc = ctx->getBelLocation(ci.bel);

View File

@ -111,8 +111,21 @@ void GateMateImpl::route_clock()
log_info(" routing net '%s'\n", clk_net->name.c_str(ctx));
ctx->bindWire(ctx->getNetinfoSourceWire(clk_net), clk_net, STRENGTH_LOCKED);
auto bufg_idx = ctx->getBelLocation(clk_net->driver.cell->bel).z;
auto clk_plane = 9 + bufg_idx;
auto clk_plane = 0;
switch (clk_net->driver.port.index) {
case id_GLB0.index:
clk_plane = 9;
break;
case id_GLB1.index:
clk_plane = 10;
break;
case id_GLB2.index:
clk_plane = 11;
break;
case id_GLB3.index:
clk_plane = 12;
break;
}
for (auto &usr : clk_net->users) {
std::priority_queue<QueuedWire, std::vector<QueuedWire>, std::greater<QueuedWire>> visit;
@ -123,7 +136,7 @@ void GateMateImpl::route_clock()
continue;
auto cpe_loc = ctx->getBelLocation(usr.cell->bel);
auto is_glb_clk = clk_net->driver.cell->type == id_BUFG;
auto is_glb_clk = clk_net->driver.cell->type == id_GLBOUT;
auto sink_wire = ctx->getNetinfoSinkWire(clk_net, usr, 0);
if (ctx->debug) {