mirror of https://github.com/YosysHQ/nextpnr.git
gatemate: cleanup of PLL and BUFG (#1562)
* Check SER_CLK more * Use connectPorts * move rewire code * Move data structures * move placement decision for later * cleanups * find working layout * clangformat * Inverted input on ODDR * Fix some tests * Copy clocks for multi die * cleanup * reporting * bugfix * handle PLL special inputs * Fix user globals * Proper DDR per bank and cleanup * Add extra data for die regions and create them * Better forced_die implementation * Copy region to newly generated cells, and update when constrained * Update PLL error messages * Add TODO comment
This commit is contained in:
parent
125df9952c
commit
abb52f81c2
|
|
@ -94,6 +94,11 @@ struct GateMateCCFReader
|
||||||
count[i]--;
|
count[i]--;
|
||||||
}
|
}
|
||||||
uarch->available_pads.erase(ctx->id(value));
|
uarch->available_pads.erase(ctx->id(value));
|
||||||
|
// Erase aliases as well
|
||||||
|
if (value == "SER_CLK")
|
||||||
|
uarch->available_pads.erase(ctx->id("SER_CLK_N"));
|
||||||
|
if (value == "SER_CLK_N")
|
||||||
|
uarch->available_pads.erase(ctx->id("SER_CLK"));
|
||||||
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
|
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
|
||||||
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
|
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
|
||||||
if (value == "1")
|
if (value == "1")
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,21 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
|
||||||
add_port(id_CLOCK2, PORT_IN);
|
add_port(id_CLOCK2, PORT_IN);
|
||||||
add_port(id_CLOCK3, PORT_IN);
|
add_port(id_CLOCK3, PORT_IN);
|
||||||
add_port(id_CLOCK4, PORT_IN);
|
add_port(id_CLOCK4, PORT_IN);
|
||||||
|
} else if (type.in(id_CPE_IBUF)) {
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
add_port(id_I, PORT_IN);
|
||||||
|
} else if (type.in(id_PLL)) {
|
||||||
|
add_port(id_CLK_REF, PORT_IN);
|
||||||
|
add_port(id_USR_CLK_REF, PORT_IN);
|
||||||
|
add_port(id_USR_SEL_A_B, PORT_IN);
|
||||||
|
add_port(id_CLK_FEEDBACK, PORT_IN);
|
||||||
|
add_port(id_CLK0, PORT_OUT);
|
||||||
|
add_port(id_CLK90, PORT_OUT);
|
||||||
|
add_port(id_CLK180, PORT_OUT);
|
||||||
|
add_port(id_CLK270, PORT_OUT);
|
||||||
|
add_port(id_USR_PLL_LOCKED, PORT_OUT);
|
||||||
|
add_port(id_USR_PLL_LOCKED_STDY, PORT_OUT);
|
||||||
|
add_port(id_USR_LOCKED_STDY_RST, PORT_IN);
|
||||||
} else {
|
} else {
|
||||||
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2468,6 +2468,12 @@ X(MULT_INVERT)
|
||||||
X(RAM_HALF)
|
X(RAM_HALF)
|
||||||
X(RAM_HALF_DUMMY)
|
X(RAM_HALF_DUMMY)
|
||||||
|
|
||||||
|
// Dies
|
||||||
|
X(1A)
|
||||||
|
X(1B)
|
||||||
|
X(2A)
|
||||||
|
X(2B)
|
||||||
|
|
||||||
// Timing
|
// Timing
|
||||||
X(timing_ADDF2x_IN5_8_comb2)
|
X(timing_ADDF2x_IN5_8_comb2)
|
||||||
X(timing_CIN_C_CINI)
|
X(timing_CIN_C_CINI)
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,16 @@ NPNR_PACKED_STRUCT(struct GateMateTimingExtraDataPOD {
|
||||||
|
|
||||||
NPNR_PACKED_STRUCT(struct GateMateSpeedGradeExtraDataPOD { RelSlice<GateMateTimingExtraDataPOD> timings; });
|
NPNR_PACKED_STRUCT(struct GateMateSpeedGradeExtraDataPOD { RelSlice<GateMateTimingExtraDataPOD> timings; });
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct GateMateDieRegionPOD {
|
||||||
|
int32_t name;
|
||||||
|
uint16_t x1;
|
||||||
|
uint16_t y1;
|
||||||
|
uint16_t x2;
|
||||||
|
uint16_t y2;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct GateMateChipExtraDataPOD { RelSlice<GateMateDieRegionPOD> dies; });
|
||||||
|
|
||||||
enum MuxFlags
|
enum MuxFlags
|
||||||
{
|
{
|
||||||
MUX_INVERT = 1,
|
MUX_INVERT = 1,
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,27 @@ void GateMateImpl::init(Context *ctx)
|
||||||
ram_signal_clk.emplace(ctx->idf("DOB[%d]", i + num * 20), num + 2);
|
ram_signal_clk.emplace(ctx->idf("DOB[%d]", i + num * 20), num + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GateMateChipExtraDataPOD *extra =
|
||||||
|
reinterpret_cast<const GateMateChipExtraDataPOD *>(ctx->chip_info->extra_data.get());
|
||||||
|
|
||||||
|
const ArchArgs &args = ctx->args;
|
||||||
|
std::string die_name;
|
||||||
|
if (args.options.count("force_die"))
|
||||||
|
die_name = args.options.at("force_die");
|
||||||
|
bool found = false;
|
||||||
|
int index = 0;
|
||||||
|
for (auto &die : extra->dies) {
|
||||||
|
IdString name(die.name);
|
||||||
|
die_to_index[name] = index++;
|
||||||
|
ctx->createRectangularRegion(name, die.x1, die.y1, die.x2, die.y2);
|
||||||
|
if (die_name == name.c_str(ctx)) {
|
||||||
|
found = true;
|
||||||
|
forced_die = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!die_name.empty() && !found)
|
||||||
|
log_error("Unable to select forced die '%s'.\n", die_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||||
|
|
@ -159,10 +180,6 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||||
return true;
|
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 (getBelBucketForBel(bel) == id_CPE_FF) {
|
if (getBelBucketForBel(bel) == id_CPE_FF) {
|
||||||
Loc loc = ctx->getBelLocation(bel);
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
const CellInfo *adj_half = ctx->getBoundBelCell(
|
const CellInfo *adj_half = ctx->getBoundBelCell(
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
std::vector<bool> used_cpes;
|
std::vector<bool> used_cpes;
|
||||||
int fpga_mode;
|
int fpga_mode;
|
||||||
int timing_mode;
|
int timing_mode;
|
||||||
|
std::map<const NetInfo *, int> global_signals;
|
||||||
|
std::vector<CellInfo *> clkin;
|
||||||
|
std::vector<CellInfo *> glbout;
|
||||||
|
std::vector<CellInfo *> pll;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||||
|
|
@ -122,6 +126,8 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
std::map<BelId, std::map<IdString, const GateMateBelPinConstraintPOD *>> pin_to_constr;
|
std::map<BelId, std::map<IdString, const GateMateBelPinConstraintPOD *>> pin_to_constr;
|
||||||
std::map<IdString, const GateMateTimingExtraDataPOD *> timing;
|
std::map<IdString, const GateMateTimingExtraDataPOD *> timing;
|
||||||
dict<IdString, int> ram_signal_clk;
|
dict<IdString, int> ram_signal_clk;
|
||||||
|
IdString forced_die;
|
||||||
|
dict<IdString, int> die_to_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,40 @@ class SpeedGradeExtraData(BBAStruct):
|
||||||
def serialise(self, context: str, bba: BBAWriter):
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
bba.slice(f"{context}_timings", len(self.timings))
|
bba.slice(f"{context}_timings", len(self.timings))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DieRegion(BBAStruct):
|
||||||
|
name: IdString
|
||||||
|
x1: int = 0
|
||||||
|
y1: int = 0
|
||||||
|
x2: int = 0
|
||||||
|
y2: int = 0
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.u32(self.name.index)
|
||||||
|
bba.u16(self.x1)
|
||||||
|
bba.u16(self.y1)
|
||||||
|
bba.u16(self.x2)
|
||||||
|
bba.u16(self.y2)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ChipExtraData(BBAStruct):
|
||||||
|
dies: list[DieRegion] = field(default_factory = list)
|
||||||
|
|
||||||
|
def add_die(self, name: IdString, x1: int, y1: int, x2:int, y2:int):
|
||||||
|
item = DieRegion(name,x1,y1,x2,y2)
|
||||||
|
self.dies.append(item)
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
self.dies.sort(key=lambda p: p.name.index)
|
||||||
|
bba.label(f"{context}_dies")
|
||||||
|
for i, t in enumerate(self.dies):
|
||||||
|
t.serialise(f"{context}_die{i}", bba)
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.slice(f"{context}_dies", len(self.dies))
|
||||||
|
|
||||||
def convert_timing(tim):
|
def convert_timing(tim):
|
||||||
return TimingValue(tim.rise.min, tim.rise.max, tim.fall.min, tim.fall.max)
|
return TimingValue(tim.rise.min, tim.rise.max, tim.fall.min, tim.fall.max)
|
||||||
|
|
||||||
|
|
@ -222,6 +256,10 @@ def main():
|
||||||
print("==============================================================================")
|
print("==============================================================================")
|
||||||
os._exit(-1)
|
os._exit(-1)
|
||||||
|
|
||||||
|
ch.extra_data = ChipExtraData()
|
||||||
|
for d in sorted(dev.dies):
|
||||||
|
ch.extra_data.add_die(ch.strs.id(dev.dies[d].name), dev.dies[d].offset_x, dev.dies[d].offset_y, dev.dies[d].offset_x + die.num_cols() - 1, dev.dies[d].offset_y + die.num_rows()-1)
|
||||||
|
|
||||||
new_wires = dict()
|
new_wires = dict()
|
||||||
wire_delay = dict()
|
wire_delay = dict()
|
||||||
for _,nodes in dev.get_connections():
|
for _,nodes in dev.get_connections():
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ void GateMatePacker::disconnect_not_used()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMatePacker::copy_constraint(NetInfo *in_net, NetInfo *out_net)
|
void GateMatePacker::copy_constraint(const NetInfo *in_net, NetInfo *out_net)
|
||||||
{
|
{
|
||||||
if (!in_net || !out_net)
|
if (!in_net || !out_net)
|
||||||
return;
|
return;
|
||||||
|
|
@ -385,12 +385,14 @@ void GateMateImpl::pack()
|
||||||
parse_ccf(args.options.at("ccf"));
|
parse_ccf(args.options.at("ccf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forced_die != IdString())
|
||||||
|
preferred_die = die_to_index[forced_die];
|
||||||
|
|
||||||
GateMatePacker packer(ctx, this);
|
GateMatePacker packer(ctx, this);
|
||||||
packer.pack_constants();
|
packer.pack_constants();
|
||||||
packer.cleanup();
|
packer.cleanup();
|
||||||
packer.pack_io();
|
packer.pack_io();
|
||||||
packer.insert_clocking();
|
packer.insert_clocking();
|
||||||
packer.sort_bufg();
|
|
||||||
packer.pack_pll();
|
packer.pack_pll();
|
||||||
packer.pack_bufg();
|
packer.pack_bufg();
|
||||||
packer.pack_io_sel(); // merge in FF and DDR
|
packer.pack_io_sel(); // merge in FF and DDR
|
||||||
|
|
@ -400,8 +402,15 @@ void GateMateImpl::pack()
|
||||||
packer.pack_mult();
|
packer.pack_mult();
|
||||||
packer.pack_addf();
|
packer.pack_addf();
|
||||||
packer.pack_cpe();
|
packer.pack_cpe();
|
||||||
|
packer.copy_clocks();
|
||||||
packer.remove_constants();
|
packer.remove_constants();
|
||||||
packer.remove_clocking();
|
|
||||||
|
if (forced_die != IdString()) {
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->belStrength != PlaceStrength::STRENGTH_FIXED)
|
||||||
|
ctx->constrainCellToRegion(cell.second->name, forced_die);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMateImpl::repack()
|
void GateMateImpl::repack()
|
||||||
|
|
@ -409,6 +418,8 @@ void GateMateImpl::repack()
|
||||||
GateMatePacker packer(ctx, this);
|
GateMatePacker packer(ctx, this);
|
||||||
packer.repack_ram();
|
packer.repack_ram();
|
||||||
packer.repack_cpe();
|
packer.repack_cpe();
|
||||||
|
packer.reassign_clocks();
|
||||||
|
packer.remove_clocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ struct GateMatePacker
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void repack_cpe();
|
void repack_cpe();
|
||||||
void repack_ram();
|
void repack_ram();
|
||||||
|
void reassign_clocks();
|
||||||
|
void copy_clocks();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||||
|
|
@ -75,6 +77,7 @@ struct GateMatePacker
|
||||||
void dff_update_params();
|
void dff_update_params();
|
||||||
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
||||||
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
void rewire_ram_o(CellInfo *first, IdString port, CellInfo *second);
|
||||||
|
|
||||||
void disconnect_not_used();
|
void disconnect_not_used();
|
||||||
void optimize_lut();
|
void optimize_lut();
|
||||||
|
|
@ -104,12 +107,9 @@ struct GateMatePacker
|
||||||
CellInfo *create_cell_ptr(IdString type, IdString name);
|
CellInfo *create_cell_ptr(IdString type, IdString name);
|
||||||
void flush_cells();
|
void flush_cells();
|
||||||
void pack_ram_cell(CellInfo &ci, CellInfo *cell, bool is_split);
|
void pack_ram_cell(CellInfo &ci, CellInfo *cell, bool is_split);
|
||||||
void copy_constraint(NetInfo *in_net, NetInfo *out_net);
|
void copy_constraint(const NetInfo *in_net, NetInfo *out_net);
|
||||||
|
|
||||||
pool<IdString> packed_cells;
|
pool<IdString> packed_cells;
|
||||||
std::map<NetInfo *, int> global_signals;
|
|
||||||
std::vector<CellInfo *> clkin;
|
|
||||||
std::vector<CellInfo *> glbout;
|
|
||||||
|
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
GateMateImpl *uarch;
|
GateMateImpl *uarch;
|
||||||
|
|
@ -117,6 +117,7 @@ struct GateMatePacker
|
||||||
HimbaechelHelpers h;
|
HimbaechelHelpers h;
|
||||||
NetInfo *net_PACKER_VCC;
|
NetInfo *net_PACKER_VCC;
|
||||||
NetInfo *net_PACKER_GND;
|
NetInfo *net_PACKER_GND;
|
||||||
|
NetInfo *net_SER_CLK;
|
||||||
int count;
|
int count;
|
||||||
std::map<IdString, int> count_per_type;
|
std::map<IdString, int> count_per_type;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,10 @@ uint8_t GateMatePacker::ram_ctrl_signal(CellInfo *cell, IdString port, bool alt)
|
||||||
uint8_t GateMatePacker::ram_clk_signal(CellInfo *cell, IdString port)
|
uint8_t GateMatePacker::ram_clk_signal(CellInfo *cell, IdString port)
|
||||||
{
|
{
|
||||||
NetInfo *clk_net = cell->getPort(port);
|
NetInfo *clk_net = cell->getPort(port);
|
||||||
if (!global_signals.count(clk_net)) {
|
if (!uarch->global_signals.count(clk_net)) {
|
||||||
return 0b00000000;
|
return 0b00000000;
|
||||||
} else {
|
} else {
|
||||||
int index = global_signals[clk_net];
|
int index = uarch->global_signals[clk_net];
|
||||||
uint8_t val = 0;
|
uint8_t val = 0;
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -348,6 +348,7 @@ void GateMatePacker::pack_ram()
|
||||||
cell->constr_y = +8;
|
cell->constr_y = +8;
|
||||||
cell->constr_z = RAM_HALF_L_Z;
|
cell->constr_z = RAM_HALF_L_Z;
|
||||||
cell->cluster = ci.cluster;
|
cell->cluster = ci.cluster;
|
||||||
|
cell->region = ci.region;
|
||||||
cell->params[id_RAM_cfg_ecc_enable] = Property(b_ecc_en << 1 | a_ecc_en, 2);
|
cell->params[id_RAM_cfg_ecc_enable] = Property(b_ecc_en << 1 | a_ecc_en, 2);
|
||||||
cell->params[id_RAM_cfg_sram_mode] = Property(ram_mode << 1 | split, 2);
|
cell->params[id_RAM_cfg_sram_mode] = Property(ram_mode << 1 | split, 2);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,65 +92,99 @@ static int glb_mux_mapping[] = {
|
||||||
|
|
||||||
void GateMatePacker::pack_bufg()
|
void GateMatePacker::pack_bufg()
|
||||||
{
|
{
|
||||||
log_info("Packing BUFGs..\n");
|
sort_bufg();
|
||||||
CellInfo *bufg[4] = {nullptr};
|
|
||||||
CellInfo *pll[4] = {nullptr};
|
|
||||||
|
|
||||||
auto update_bufg_port = [&](CellInfo *cell, int port_num, int pll_num) {
|
log_info("Packing BUFGs..\n");
|
||||||
|
auto update_bufg_port = [&](std::vector<CellInfo *> &bufg, 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);
|
CellInfo *b = net_only_drives(ctx, cell->getPort(ctx->idf("CLK%d", 90 * port_num)), is_bufg, id_I, false);
|
||||||
if (b) {
|
if (b) {
|
||||||
if (bufg[port_num] == nullptr) {
|
if (bufg[port_num] == nullptr) {
|
||||||
bufg[port_num] = b;
|
bufg[port_num] = b;
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (bufg[pll_num] == nullptr) {
|
if (bufg[pll_num] == nullptr) {
|
||||||
bufg[pll_num] = b;
|
bufg[pll_num] = b;
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
log_error("Unable to place BUFG for PLL.\n");
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto &cell : ctx->cells) {
|
unsigned max_plls = 4; // * uarch->dies;
|
||||||
CellInfo &ci = *cell.second;
|
// Index vector for permutation
|
||||||
if (!ci.type.in(id_PLL))
|
std::vector<unsigned> indexes(max_plls);
|
||||||
continue;
|
for (unsigned i = 0; i < max_plls; ++i)
|
||||||
int index = ci.constr_z - 2;
|
indexes[i] = i;
|
||||||
pll[index] = &ci;
|
|
||||||
|
std::vector<CellInfo *> bufg(max_plls, nullptr);
|
||||||
|
std::vector<CellInfo *> pll(max_plls, nullptr);
|
||||||
|
pool<IdString> used_bufg;
|
||||||
|
bool valid = true;
|
||||||
|
do {
|
||||||
|
valid = true;
|
||||||
|
std::vector<std::vector<CellInfo *>> tmp_bufg(uarch->dies, std::vector<CellInfo *>(4, nullptr));
|
||||||
|
for (unsigned i = 0; i < max_plls; ++i) {
|
||||||
|
if (indexes[i] < uarch->pll.size()) {
|
||||||
for (int j = 0; j < 4; j++)
|
for (int j = 0; j < 4; j++)
|
||||||
update_bufg_port(pll[index], j, index);
|
valid &= update_bufg_port(tmp_bufg[i >> 2], uarch->pll[indexes[i]], j, i & 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
for (unsigned i = 0; i < max_plls; ++i) {
|
||||||
|
bufg[i] = tmp_bufg[i >> 2][i & 3];
|
||||||
|
if (bufg[i])
|
||||||
|
used_bufg.insert(bufg[i]->name);
|
||||||
|
if (indexes[i] < uarch->pll.size())
|
||||||
|
pll[i] = uarch->pll[indexes[i]];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (std::next_permutation(indexes.begin(), indexes.end()));
|
||||||
|
if (!valid)
|
||||||
|
log_error("Unable to place PLLs and BUFGs\n");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < max_plls; ++i) {
|
||||||
|
|
||||||
|
int die = i >> 2;
|
||||||
|
if (!pll[i])
|
||||||
|
continue;
|
||||||
|
CellInfo &ci = *pll[i];
|
||||||
|
ci.cluster = ci.name;
|
||||||
|
ci.constr_abs_z = true;
|
||||||
|
ci.constr_z = 2 + (i & 3); // Position to a proper Z location
|
||||||
|
|
||||||
|
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i & 3), die)];
|
||||||
|
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
|
||||||
|
ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED);
|
||||||
|
|
||||||
|
pll_out(&ci, id_CLK0, fixed_loc);
|
||||||
|
pll_out(&ci, id_CLK90, fixed_loc);
|
||||||
|
pll_out(&ci, id_CLK180, fixed_loc);
|
||||||
|
pll_out(&ci, id_CLK270, fixed_loc);
|
||||||
|
|
||||||
|
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc);
|
||||||
|
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc);
|
||||||
|
move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc);
|
||||||
|
move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc);
|
||||||
|
move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo &ci = *cell.second;
|
CellInfo &ci = *cell.second;
|
||||||
if (!ci.type.in(id_CC_BUFG))
|
if (!ci.type.in(id_CC_BUFG))
|
||||||
continue;
|
continue;
|
||||||
|
if (used_bufg.count(cell.second->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
NetInfo *in_net = ci.getPort(id_I);
|
NetInfo *in_net = ci.getPort(id_I);
|
||||||
int die = uarch->preferred_die;
|
|
||||||
if (in_net) {
|
if (in_net) {
|
||||||
if (in_net->driver.cell) {
|
|
||||||
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) {
|
|
||||||
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_Y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SER_CLK
|
|
||||||
clkin[die]->connectPort(id_SER_CLK, in_net);
|
|
||||||
}
|
|
||||||
|
|
||||||
copy_constraint(in_net, ci.getPort(id_O));
|
|
||||||
|
|
||||||
if ((in_net->driver.cell && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) ||
|
if ((in_net->driver.cell && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) ||
|
||||||
!in_net->driver.cell) {
|
!in_net->driver.cell) {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (unsigned i = 0; i < max_plls; ++i) {
|
||||||
if (bufg[i] == nullptr) {
|
if (bufg[i] == nullptr && pll[i] == nullptr) { // PLL must not be used
|
||||||
bufg[i] = &ci;
|
bufg[i] = &ci;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -159,24 +193,29 @@ void GateMatePacker::pack_bufg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (unsigned j = 0; j < max_plls; ++j) {
|
||||||
if (bufg[i]) {
|
if (bufg[j]) {
|
||||||
CellInfo &ci = *bufg[i];
|
CellInfo &ci = *bufg[j];
|
||||||
global_signals.emplace(ci.getPort(id_O), i);
|
int i = j & 3;
|
||||||
|
int die = j >> 2;
|
||||||
|
uarch->global_signals.emplace(ci.getPort(id_O), j);
|
||||||
int glb_mux = 0;
|
int glb_mux = 0;
|
||||||
NetInfo *in_net = ci.getPort(id_I);
|
NetInfo *in_net = ci.getPort(id_I);
|
||||||
int die = uarch->preferred_die;
|
copy_constraint(in_net, ci.getPort(id_O));
|
||||||
if (in_net->driver.cell) {
|
if (in_net->driver.cell) {
|
||||||
bool user_glb = true;
|
bool user_glb = true;
|
||||||
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) {
|
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) {
|
||||||
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
|
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
|
||||||
if (pad_info->flags) {
|
if (pad_info->flags) {
|
||||||
die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die;
|
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
|
||||||
clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
|
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
||||||
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die],
|
||||||
NetInfo *conn = ctx->createNet(ci.name);
|
ctx->idf("CLK_REF_OUT%d", i));
|
||||||
clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn);
|
int index = pad_info->flags - 1;
|
||||||
glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn);
|
if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index))) {
|
||||||
|
uarch->clkin[die]->connectPort(ctx->idf("CLK%d", index),
|
||||||
|
in_net->driver.cell->getPort(id_Y));
|
||||||
|
}
|
||||||
user_glb = false;
|
user_glb = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,49 +234,71 @@ void GateMatePacker::pack_bufg()
|
||||||
else
|
else
|
||||||
log_error("Uknown connecton on BUFG to PLL.\n");
|
log_error("Uknown connecton on BUFG to PLL.\n");
|
||||||
glb_mux = glb_mux_mapping[i * 16 + pll_index * 4 + pll_out];
|
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));
|
ci.movePortTo(id_I, uarch->glbout[die],
|
||||||
|
ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index));
|
||||||
user_glb = false;
|
user_glb = false;
|
||||||
}
|
}
|
||||||
if (user_glb) {
|
if (user_glb) {
|
||||||
ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i));
|
ci.movePortTo(id_I, uarch->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));
|
move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_GLB%d", i),
|
||||||
glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1);
|
ctx->getBelLocation(uarch->glbout[die]->bel));
|
||||||
|
uarch->glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SER_CLK
|
// SER_CLK
|
||||||
clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
|
uarch->clkin[die]->connectPort(id_SER_CLK, in_net);
|
||||||
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
|
||||||
NetInfo *conn = ctx->createNet(ci.name);
|
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
||||||
clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn);
|
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die],
|
||||||
glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn);
|
ctx->idf("CLK_REF_OUT%d", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i));
|
ci.movePortTo(id_O, uarch->glbout[die], ctx->idf("GLB%d", i));
|
||||||
glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
|
uarch->glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
|
||||||
glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3);
|
uarch->glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3);
|
||||||
packed_cells.emplace(ci.name);
|
packed_cells.emplace(ci.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (auto &cell : uarch->pll) {
|
||||||
if (pll[i]) {
|
CellInfo &ci = *cell;
|
||||||
NetInfo *feedback_net = pll[i]->getPort(id_CLK_FEEDBACK);
|
int i = ci.constr_z - 2;
|
||||||
int die = uarch->tile_extra_data(pll[i]->bel.tile)->die;
|
NetInfo *clk = ci.getPort(id_CLK_REF);
|
||||||
if (feedback_net) {
|
int die = uarch->tile_extra_data(ci.bel.tile)->die;
|
||||||
if (!global_signals.count(feedback_net)) {
|
if (clk) {
|
||||||
pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i));
|
if (clk->driver.cell) {
|
||||||
move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel));
|
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
|
||||||
glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1);
|
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
|
||||||
|
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
||||||
|
int index = pad_info->flags - 1;
|
||||||
|
if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index)))
|
||||||
|
ci.movePortTo(id_CLK_REF, uarch->clkin[die], ctx->idf("CLK%d", index));
|
||||||
|
else
|
||||||
|
ci.disconnectPort(id_CLK_REF);
|
||||||
} else {
|
} else {
|
||||||
int index = global_signals[feedback_net];
|
// SER_CLK
|
||||||
glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2);
|
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
|
||||||
pll[i]->disconnectPort(id_CLK_FEEDBACK);
|
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
||||||
|
ci.movePortTo(id_CLK_REF, uarch->clkin[die], id_SER_CLK);
|
||||||
}
|
}
|
||||||
NetInfo *conn =
|
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), &ci, id_CLK_REF);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetInfo *feedback_net = ci.getPort(id_CLK_FEEDBACK);
|
||||||
|
if (feedback_net) {
|
||||||
|
if (!uarch->global_signals.count(feedback_net)) {
|
||||||
|
ci.movePortTo(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("USR_FB%d", i));
|
||||||
|
move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_FB%d", i),
|
||||||
|
ctx->getBelLocation(uarch->glbout[die]->bel));
|
||||||
|
uarch->glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1);
|
||||||
|
} else {
|
||||||
|
int index = uarch->global_signals[feedback_net];
|
||||||
|
if ((index >> 2) != die)
|
||||||
|
log_error("TODO: Feedback signal from another die.\n");
|
||||||
|
uarch->glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2);
|
||||||
|
ci.disconnectPort(id_CLK_FEEDBACK);
|
||||||
|
}
|
||||||
|
ci.connectPorts(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("CLK_FB%d", i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,13 +329,13 @@ void GateMatePacker::insert_clocking()
|
||||||
log_info("Insert clocking cells..\n");
|
log_info("Insert clocking cells..\n");
|
||||||
for (int i = 0; i < uarch->dies; i++) {
|
for (int i = 0; i < uarch->dies; i++) {
|
||||||
Loc fixed_loc = uarch->locations[std::make_pair(id_CLKIN, 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)));
|
uarch->clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i)));
|
||||||
BelId clkin_bel = ctx->getBelByLocation(fixed_loc);
|
BelId clkin_bel = ctx->getBelByLocation(fixed_loc);
|
||||||
ctx->bindBel(clkin_bel, clkin.back(), PlaceStrength::STRENGTH_FIXED);
|
ctx->bindBel(clkin_bel, uarch->clkin.back(), PlaceStrength::STRENGTH_FIXED);
|
||||||
glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i)));
|
uarch->glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i)));
|
||||||
fixed_loc = uarch->locations[std::make_pair(id_GLBOUT, i)];
|
fixed_loc = uarch->locations[std::make_pair(id_GLBOUT, i)];
|
||||||
BelId glbout_bel = ctx->getBelByLocation(fixed_loc);
|
BelId glbout_bel = ctx->getBelByLocation(fixed_loc);
|
||||||
ctx->bindBel(glbout_bel, glbout.back(), PlaceStrength::STRENGTH_FIXED);
|
ctx->bindBel(glbout_bel, uarch->glbout.back(), PlaceStrength::STRENGTH_FIXED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,8 +359,8 @@ void GateMatePacker::remove_clocking()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
remove_unused_cells(clkin);
|
remove_unused_cells(uarch->clkin);
|
||||||
remove_unused_cells(glbout);
|
remove_unused_cells(uarch->glbout);
|
||||||
flush_cells();
|
flush_cells();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,7 +378,6 @@ static const char *timing_mode_to_str(int mode)
|
||||||
|
|
||||||
void GateMatePacker::pack_pll()
|
void GateMatePacker::pack_pll()
|
||||||
{
|
{
|
||||||
std::vector<int> pll_index(uarch->dies);
|
|
||||||
log_info("Packing PLLs..\n");
|
log_info("Packing PLLs..\n");
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo &ci = *cell.second;
|
CellInfo &ci = *cell.second;
|
||||||
|
|
@ -329,39 +389,18 @@ void GateMatePacker::pack_pll()
|
||||||
disconnect_if_gnd(&ci, id_CLK_FEEDBACK);
|
disconnect_if_gnd(&ci, id_CLK_FEEDBACK);
|
||||||
disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST);
|
disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST);
|
||||||
|
|
||||||
int die = uarch->preferred_die;
|
if (uarch->pll.size() >= (uarch->dies * 4U))
|
||||||
|
|
||||||
NetInfo *clk = ci.getPort(id_CLK_REF);
|
|
||||||
if (clk && clk->driver.cell) { // Only for GPIO pins
|
|
||||||
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_IOSEL) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pll_index[die] >= 4)
|
|
||||||
log_error("Used more than available PLLs.\n");
|
log_error("Used more than available PLLs.\n");
|
||||||
|
|
||||||
if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr)
|
if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr)
|
||||||
log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set.\n");
|
log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set for cell '%s'.\n",
|
||||||
|
ci.name.c_str(ctx));
|
||||||
|
|
||||||
if (ci.getPort(id_CLK_REF) != nullptr && ci.getPort(id_USR_CLK_REF) != nullptr)
|
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");
|
log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time for cell '%s'.\n",
|
||||||
|
ci.name.c_str(ctx));
|
||||||
|
|
||||||
ci.cluster = ci.name;
|
NetInfo *clk = ci.getPort(id_CLK_REF);
|
||||||
ci.constr_abs_z = true;
|
|
||||||
ci.constr_z = 2 + pll_index[die]; // Position to a proper Z location
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
clk = ci.getPort(id_CLK_REF);
|
|
||||||
delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq"));
|
delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq"));
|
||||||
if (clk) {
|
if (clk) {
|
||||||
if (clk->driver.cell) {
|
if (clk->driver.cell) {
|
||||||
|
|
@ -372,45 +411,36 @@ void GateMatePacker::pack_pll()
|
||||||
clk = in;
|
clk = in;
|
||||||
}
|
}
|
||||||
if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_IOSEL)
|
if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_IOSEL)
|
||||||
log_error("CLK_REF must be driven with GPIO pin.\n");
|
log_error("CLK_REF must be driven with GPIO pin for cell '%s'.\n", ci.name.c_str(ctx));
|
||||||
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
|
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
|
||||||
if (pad_info->flags == 0)
|
if (pad_info->flags == 0)
|
||||||
log_error("CLK_REF must be driven with CLK dedicated pin.\n");
|
log_error("CLK_REF must be driven with CLK dedicated pin for cell '%s'.\n", ci.name.c_str(ctx));
|
||||||
clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(pad_info->flags - 1, 3);
|
|
||||||
clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0);
|
|
||||||
ci.movePortTo(id_CLK_REF, clkin[die], ctx->idf("CLK%d", pad_info->flags - 1));
|
|
||||||
} else {
|
} else {
|
||||||
// SER_CLK
|
// SER_CLK
|
||||||
clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(0b100, 3);
|
if (clk != net_SER_CLK)
|
||||||
clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0);
|
log_error("CLK_REF connected to uknown pin for cell '%s'.\n", ci.name.c_str(ctx));
|
||||||
ci.movePortTo(id_CLK_REF, clkin[die], id_SER_CLK);
|
|
||||||
}
|
}
|
||||||
if (clk->clkconstr)
|
if (clk->clkconstr)
|
||||||
period = clk->clkconstr->period.minDelay();
|
period = clk->clkconstr->period.minDelay();
|
||||||
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);
|
clk = ci.getPort(id_USR_CLK_REF);
|
||||||
if (clk) {
|
if (clk) {
|
||||||
move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc);
|
|
||||||
ci.params[ctx->id("USR_CLK_REF")] = Property(0b1, 1);
|
ci.params[ctx->id("USR_CLK_REF")] = Property(0b1, 1);
|
||||||
|
if (clk->driver.cell) {
|
||||||
|
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) {
|
||||||
|
NetInfo *in = clk->driver.cell->getPort(id_I);
|
||||||
|
ci.disconnectPort(id_USR_CLK_REF);
|
||||||
|
ci.connectPort(id_USR_CLK_REF, in);
|
||||||
|
clk = in;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (clk->clkconstr)
|
if (clk->clkconstr)
|
||||||
period = clk->clkconstr->period.minDelay();
|
period = clk->clkconstr->period.minDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ci.getPort(id_CLK_REF_OUT))
|
if (ci.getPort(id_CLK_REF_OUT))
|
||||||
log_error("Output CLK_REF_OUT cannot be used if PLL is used.\n");
|
log_error("Output CLK_REF_OUT cannot be used if PLL '%s' is used.\n", ci.name.c_str(ctx));
|
||||||
|
|
||||||
pll_out(&ci, id_CLK0, fixed_loc);
|
|
||||||
pll_out(&ci, id_CLK90, fixed_loc);
|
|
||||||
pll_out(&ci, id_CLK180, fixed_loc);
|
|
||||||
pll_out(&ci, id_CLK270, fixed_loc);
|
|
||||||
|
|
||||||
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc);
|
|
||||||
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc);
|
|
||||||
move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc);
|
|
||||||
|
|
||||||
double out_clk_max = 0;
|
double out_clk_max = 0;
|
||||||
int clk270_doub = 0;
|
int clk270_doub = 0;
|
||||||
|
|
@ -450,18 +480,18 @@ void GateMatePacker::pack_pll()
|
||||||
|
|
||||||
double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0);
|
double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0);
|
||||||
if (ref_clk <= 0 || ref_clk > 125)
|
if (ref_clk <= 0 || ref_clk > 125)
|
||||||
log_error("REF_CLK parameter is out of range (0,125.00].\n");
|
log_error("REF_CLK parameter is out of range (0,125.00] for '%s'.\n", ci.name.c_str(ctx));
|
||||||
|
|
||||||
double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0);
|
double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0);
|
||||||
if (out_clk <= 0 || out_clk > max_freq)
|
if (out_clk <= 0 || out_clk > max_freq)
|
||||||
log_error("OUT_CLK parameter is out of range (0,%.2lf].\n", max_freq);
|
log_error("OUT_CLK parameter is out of range (0,%.2lf] for '%s'.\n", max_freq, ci.name.c_str(ctx));
|
||||||
|
|
||||||
if ((ci_const < 1) || (ci_const > 31)) {
|
if ((ci_const < 1) || (ci_const > 31)) {
|
||||||
log_warning("CI const out of range. Set to default CI = 2\n");
|
log_warning("CI const out of range. Set to default CI = 2 for '%s'\n", ci.name.c_str(ctx));
|
||||||
ci_const = 2;
|
ci_const = 2;
|
||||||
}
|
}
|
||||||
if ((cp_const < 1) || (cp_const > 31)) {
|
if ((cp_const < 1) || (cp_const > 31)) {
|
||||||
log_warning("CP const out of range. Set to default CP = 4\n");
|
log_warning("CP const out of range. Set to default CP = 4 for '%s'\n", ci.name.c_str(ctx));
|
||||||
cp_const = 4;
|
cp_const = 4;
|
||||||
}
|
}
|
||||||
// PLL_cfg_val_800_1400 PLL values from 11.08.2021
|
// PLL_cfg_val_800_1400 PLL values from 11.08.2021
|
||||||
|
|
@ -578,7 +608,6 @@ void GateMatePacker::pack_pll()
|
||||||
ci.disconnectPort(id_USR_SEL_A_B);
|
ci.disconnectPort(id_USR_SEL_A_B);
|
||||||
} else {
|
} else {
|
||||||
ci.params[ctx->id("USR_SET")] = Property(0b1, 1);
|
ci.params[ctx->id("USR_SET")] = Property(0b1, 1);
|
||||||
move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc);
|
|
||||||
}
|
}
|
||||||
ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1);
|
ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1);
|
||||||
ci.unsetParam(id_PLL_CFG_A);
|
ci.unsetParam(id_PLL_CFG_A);
|
||||||
|
|
@ -615,7 +644,187 @@ void GateMatePacker::pack_pll()
|
||||||
|
|
||||||
ci.type = id_PLL;
|
ci.type = id_PLL;
|
||||||
|
|
||||||
pll_index[die]++;
|
uarch->pll.push_back(&ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMatePacker::rewire_ram_o(CellInfo *first, IdString port, CellInfo *second)
|
||||||
|
{
|
||||||
|
NetInfo *net = first->getPort(port);
|
||||||
|
if (net && net->driver.cell) {
|
||||||
|
net = net->driver.cell->getPort(id_I);
|
||||||
|
if (net && net->driver.cell) {
|
||||||
|
uint8_t val = int_or_default(net->driver.cell->params, id_INIT_L00, 0);
|
||||||
|
switch (val) {
|
||||||
|
case LUT_ZERO:
|
||||||
|
net = net_PACKER_GND;
|
||||||
|
break;
|
||||||
|
case LUT_ONE:
|
||||||
|
net = net_PACKER_VCC;
|
||||||
|
break;
|
||||||
|
case LUT_D0:
|
||||||
|
net = net->driver.cell->getPort(id_IN1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error("Unsupported config, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
second->connectPort(port, net);
|
||||||
|
} else {
|
||||||
|
log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMatePacker::copy_clocks()
|
||||||
|
{
|
||||||
|
if (uarch->dies == 1)
|
||||||
|
return;
|
||||||
|
log_info("Copy clocks..\n");
|
||||||
|
|
||||||
|
// Save first CLKIN inputs
|
||||||
|
std::vector<CellInfo *> clk_iosel(4, nullptr);
|
||||||
|
bool use_ser_clk = false;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
NetInfo *in_net = uarch->clkin[0]->getPort(ctx->idf("CLK%d", i));
|
||||||
|
if (in_net) {
|
||||||
|
if (in_net->driver.cell)
|
||||||
|
clk_iosel[i] = in_net->driver.cell;
|
||||||
|
else
|
||||||
|
use_ser_clk = true;
|
||||||
|
}
|
||||||
|
uarch->clkin[0]->disconnectPort(ctx->idf("CLK%d", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int new_die = 0; new_die < uarch->dies; new_die++) {
|
||||||
|
// Reconnect CLKIN and create appropriate GPIO and IOSEL cells
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (clk_iosel[i]) {
|
||||||
|
CellInfo *iosel = clk_iosel[i];
|
||||||
|
auto pad_info = uarch->bel_to_pad[iosel->bel];
|
||||||
|
Loc l = uarch->locations[std::make_pair(IdString(pad_info->package_pin), new_die)];
|
||||||
|
CellInfo *iosel_new = ctx->getBoundBelCell(ctx->getBelByLocation(l));
|
||||||
|
if (!iosel_new) {
|
||||||
|
iosel_new = create_cell_ptr(iosel->type, ctx->idf("%s$die%d", iosel->name.c_str(ctx), new_die));
|
||||||
|
iosel_new->params = iosel->params;
|
||||||
|
ctx->bindBel(ctx->getBelByLocation(l), iosel_new, PlaceStrength::STRENGTH_FIXED);
|
||||||
|
|
||||||
|
CellInfo *gpio = iosel->getPort(id_GPIO_IN)->driver.cell;
|
||||||
|
CellInfo *gpio_new =
|
||||||
|
create_cell_ptr(gpio->type, ctx->idf("%s$die%d", gpio->name.c_str(ctx), new_die));
|
||||||
|
gpio_new->params = gpio->params;
|
||||||
|
ctx->bindBel(ctx->getBelByLocation({l.x, l.y, 0}), gpio_new, PlaceStrength::STRENGTH_FIXED);
|
||||||
|
|
||||||
|
// Duplicate input connection
|
||||||
|
gpio_new->connectPort(id_I, gpio->getPort(id_I));
|
||||||
|
// Connect IOSEL and CPE_IBUF
|
||||||
|
gpio_new->connectPorts(id_Y, iosel_new, id_GPIO_IN);
|
||||||
|
}
|
||||||
|
if (iosel_new->getPort(id_IN1))
|
||||||
|
uarch->clkin[new_die]->connectPort(ctx->idf("CLK%d", i), iosel_new->getPort(id_IN1));
|
||||||
|
else
|
||||||
|
iosel_new->connectPorts(id_IN1, uarch->clkin[new_die], ctx->idf("CLK%d", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (use_ser_clk)
|
||||||
|
uarch->clkin[new_die]->connectPort(id_SER_CLK, net_SER_CLK);
|
||||||
|
|
||||||
|
if (new_die != 0) {
|
||||||
|
// Copy configuration from first die to other dies
|
||||||
|
uarch->clkin[new_die]->params = uarch->clkin[0]->params;
|
||||||
|
uarch->glbout[new_die]->params = uarch->glbout[0]->params;
|
||||||
|
|
||||||
|
// Copy PLLs
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), 0)];
|
||||||
|
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
|
||||||
|
CellInfo *pll = ctx->getBoundBelCell(pll_bel);
|
||||||
|
if (pll) {
|
||||||
|
// Create new PLL
|
||||||
|
CellInfo *pll_new = create_cell_ptr(pll->type, ctx->idf("%s$die%d", pll->name.c_str(ctx), new_die));
|
||||||
|
pll_new->params = pll->params;
|
||||||
|
// Bind to new location
|
||||||
|
Loc new_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), new_die)];
|
||||||
|
BelId bel = ctx->getBelByLocation(new_loc);
|
||||||
|
ctx->bindBel(bel, pll_new, PlaceStrength::STRENGTH_FIXED);
|
||||||
|
|
||||||
|
if (pll->getPort(id_CLK_REF))
|
||||||
|
uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), pll_new, id_CLK_REF);
|
||||||
|
|
||||||
|
if (pll->getPort(id_CLK0))
|
||||||
|
pll_new->connectPorts(id_CLK0, uarch->glbout[new_die], ctx->idf("CLK0_%d", i));
|
||||||
|
if (pll->getPort(id_CLK90))
|
||||||
|
pll_new->connectPorts(id_CLK90, uarch->glbout[new_die], ctx->idf("CLK90_%d", i));
|
||||||
|
if (pll->getPort(id_CLK180))
|
||||||
|
pll_new->connectPorts(id_CLK180, uarch->glbout[new_die], ctx->idf("CLK180_%d", i));
|
||||||
|
if (pll->getPort(id_CLK270))
|
||||||
|
pll_new->connectPorts(id_CLK270, uarch->glbout[new_die], ctx->idf("CLK270_%d", i));
|
||||||
|
if (pll->getPort(id_USR_LOCKED_STDY_RST))
|
||||||
|
rewire_ram_o(pll, id_USR_LOCKED_STDY_RST, pll_new);
|
||||||
|
if (pll->getPort(id_USR_CLK_REF))
|
||||||
|
rewire_ram_o(pll, id_USR_CLK_REF, pll_new);
|
||||||
|
if (pll->getPort(id_USR_SEL_A_B))
|
||||||
|
rewire_ram_o(pll, id_USR_SEL_A_B, pll_new);
|
||||||
|
move_ram_o_fixed(pll_new, id_USR_LOCKED_STDY_RST, new_loc);
|
||||||
|
move_ram_o_fixed(pll_new, id_USR_CLK_REF, new_loc);
|
||||||
|
move_ram_o_fixed(pll_new, id_USR_SEL_A_B, new_loc);
|
||||||
|
// TODO: AND outputs of all USR_LOCKED_STDY_RST and use that signal to drive logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy GLBOUT inputs
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
Loc new_loc = uarch->locations[std::make_pair(id_GLBOUT, new_die)];
|
||||||
|
// Plain copy of user signals
|
||||||
|
NetInfo *net = uarch->glbout[0]->getPort(ctx->idf("USR_GLB%d", i));
|
||||||
|
if (net)
|
||||||
|
rewire_ram_o(uarch->glbout[0], ctx->idf("USR_GLB%d", i), uarch->glbout[new_die]);
|
||||||
|
|
||||||
|
net = uarch->glbout[0]->getPort(ctx->idf("USR_FB%d", i));
|
||||||
|
if (net)
|
||||||
|
rewire_ram_o(uarch->glbout[0], ctx->idf("USR_FB%d", i), uarch->glbout[new_die]);
|
||||||
|
|
||||||
|
move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_GLB%d", i), new_loc);
|
||||||
|
move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_FB%d", i), new_loc);
|
||||||
|
|
||||||
|
if (uarch->glbout[0]->getPort(ctx->idf("CLK_REF_OUT%d", i)))
|
||||||
|
uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[new_die],
|
||||||
|
ctx->idf("CLK_REF_OUT%d", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void GateMatePacker::reassign_clocks()
|
||||||
|
{
|
||||||
|
if (uarch->dies == 1)
|
||||||
|
return;
|
||||||
|
log_info("Reassign clocks..\n");
|
||||||
|
|
||||||
|
std::vector<std::vector<NetInfo *>> new_bufg(uarch->dies, std::vector<NetInfo *>(4));
|
||||||
|
|
||||||
|
for (auto &glob : uarch->global_signals) {
|
||||||
|
const NetInfo *net = glob.first;
|
||||||
|
int index = glob.second;
|
||||||
|
int drv_die = uarch->tile_extra_data(net->driver.cell->bel.tile)->die;
|
||||||
|
auto users = net->users; // make a copy
|
||||||
|
int count = 0;
|
||||||
|
for (auto &user : users) {
|
||||||
|
int cell_die = uarch->tile_extra_data(user.cell->bel.tile)->die;
|
||||||
|
if (cell_die != drv_die) {
|
||||||
|
if (!new_bufg[cell_die][index]) {
|
||||||
|
NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), cell_die));
|
||||||
|
new_bufg[cell_die][index] = new_signal;
|
||||||
|
uarch->glbout[cell_die]->connectPort(ctx->idf("GLB%d", index), new_signal);
|
||||||
|
copy_constraint(net, new_signal);
|
||||||
|
}
|
||||||
|
user.cell->disconnectPort(user.port);
|
||||||
|
user.cell->connectPort(user.port, new_bufg[cell_die][index]);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count)
|
||||||
|
log_info(" reassign %d net '%s' users\n", count, net->name.c_str(ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,7 @@ void GateMatePacker::pack_cpe()
|
||||||
|
|
||||||
auto merge_dff = [&](CellInfo &ci, CellInfo *dff) {
|
auto merge_dff = [&](CellInfo &ci, CellInfo *dff) {
|
||||||
dff->cluster = ci.name;
|
dff->cluster = ci.name;
|
||||||
|
dff->region = ci.region;
|
||||||
dff->constr_abs_z = false;
|
dff->constr_abs_z = false;
|
||||||
dff->constr_z = +2;
|
dff->constr_z = +2;
|
||||||
ci.cluster = ci.name;
|
ci.cluster = ci.name;
|
||||||
|
|
@ -253,6 +254,7 @@ void GateMatePacker::pack_cpe()
|
||||||
CellInfo *lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lower", ci.name.c_str(ctx)));
|
CellInfo *lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lower", ci.name.c_str(ctx)));
|
||||||
ci.constr_children.push_back(lower);
|
ci.constr_children.push_back(lower);
|
||||||
lower->cluster = ci.name;
|
lower->cluster = ci.name;
|
||||||
|
lower->region = ci.region;
|
||||||
lower->constr_abs_z = true;
|
lower->constr_abs_z = true;
|
||||||
lower->constr_z = CPE_LT_L_Z;
|
lower->constr_z = CPE_LT_L_Z;
|
||||||
lower->params[id_INIT_L20] = Property(LUT_D0, 4);
|
lower->params[id_INIT_L20] = Property(LUT_D0, 4);
|
||||||
|
|
@ -280,6 +282,7 @@ void GateMatePacker::pack_cpe()
|
||||||
for (auto ci : l2t5_list) {
|
for (auto ci : l2t5_list) {
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx)));
|
||||||
upper->cluster = ci->name;
|
upper->cluster = ci->name;
|
||||||
|
upper->region = ci->region;
|
||||||
upper->constr_abs_z = true;
|
upper->constr_abs_z = true;
|
||||||
upper->constr_z = CPE_LT_U_Z;
|
upper->constr_z = CPE_LT_U_Z;
|
||||||
ci->movePortTo(id_I4, upper, id_IN1);
|
ci->movePortTo(id_I4, upper, id_IN1);
|
||||||
|
|
@ -334,6 +337,7 @@ void GateMatePacker::pack_cpe()
|
||||||
|
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
|
||||||
upper->cluster = ci.name;
|
upper->cluster = ci.name;
|
||||||
|
upper->region = ci.region;
|
||||||
upper->constr_abs_z = false;
|
upper->constr_abs_z = false;
|
||||||
upper->constr_z = -1;
|
upper->constr_z = -1;
|
||||||
upper->params[id_INIT_L10] = Property(select, 4); // Selection bits
|
upper->params[id_INIT_L10] = Property(select, 4); // Selection bits
|
||||||
|
|
@ -365,6 +369,7 @@ void GateMatePacker::pack_cpe()
|
||||||
CellInfo &ci = *cell;
|
CellInfo &ci = *cell;
|
||||||
CellInfo *lt = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lt", ci.name.c_str(ctx)));
|
CellInfo *lt = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lt", ci.name.c_str(ctx)));
|
||||||
lt->cluster = ci.name;
|
lt->cluster = ci.name;
|
||||||
|
lt->region = ci.region;
|
||||||
lt->constr_abs_z = false;
|
lt->constr_abs_z = false;
|
||||||
lt->constr_z = -2;
|
lt->constr_z = -2;
|
||||||
ci.cluster = ci.name;
|
ci.cluster = ci.name;
|
||||||
|
|
@ -515,6 +520,7 @@ void GateMatePacker::pack_addf()
|
||||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||||
if (dff && are_ffs_compatible(dff, other)) {
|
if (dff && are_ffs_compatible(dff, other)) {
|
||||||
dff->cluster = cell->cluster;
|
dff->cluster = cell->cluster;
|
||||||
|
dff->region = cell->region;
|
||||||
dff->constr_abs_z = false;
|
dff->constr_abs_z = false;
|
||||||
dff->constr_z = +2;
|
dff->constr_z = +2;
|
||||||
cell->constr_children.push_back(dff);
|
cell->constr_children.push_back(dff);
|
||||||
|
|
@ -534,6 +540,7 @@ void GateMatePacker::pack_addf()
|
||||||
CellInfo *ci_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$ci_upper", root->name.c_str(ctx)));
|
CellInfo *ci_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$ci_upper", root->name.c_str(ctx)));
|
||||||
root->constr_children.push_back(ci_upper);
|
root->constr_children.push_back(ci_upper);
|
||||||
ci_upper->cluster = root->name;
|
ci_upper->cluster = root->name;
|
||||||
|
ci_upper->region = root->region;
|
||||||
ci_upper->constr_abs_z = false;
|
ci_upper->constr_abs_z = false;
|
||||||
ci_upper->constr_z = -1;
|
ci_upper->constr_z = -1;
|
||||||
ci_upper->constr_y = -1;
|
ci_upper->constr_y = -1;
|
||||||
|
|
@ -541,6 +548,7 @@ void GateMatePacker::pack_addf()
|
||||||
CellInfo *ci_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$ci", root->name.c_str(ctx)));
|
CellInfo *ci_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$ci", root->name.c_str(ctx)));
|
||||||
root->constr_children.push_back(ci_lower);
|
root->constr_children.push_back(ci_lower);
|
||||||
ci_lower->cluster = root->name;
|
ci_lower->cluster = root->name;
|
||||||
|
ci_lower->region = root->region;
|
||||||
ci_lower->constr_abs_z = false;
|
ci_lower->constr_abs_z = false;
|
||||||
ci_lower->constr_y = -1;
|
ci_lower->constr_y = -1;
|
||||||
ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||||
|
|
@ -551,6 +559,7 @@ void GateMatePacker::pack_addf()
|
||||||
ci_cplines->params[id_C_CY1_I] = Property(1, 1);
|
ci_cplines->params[id_C_CY1_I] = Property(1, 1);
|
||||||
root->constr_children.push_back(ci_cplines);
|
root->constr_children.push_back(ci_cplines);
|
||||||
ci_cplines->cluster = root->name;
|
ci_cplines->cluster = root->name;
|
||||||
|
ci_cplines->region = root->region;
|
||||||
ci_cplines->constr_abs_z = true;
|
ci_cplines->constr_abs_z = true;
|
||||||
ci_cplines->constr_y = -1;
|
ci_cplines->constr_y = -1;
|
||||||
ci_cplines->constr_z = CPE_CPLINES_Z;
|
ci_cplines->constr_z = CPE_CPLINES_Z;
|
||||||
|
|
@ -580,6 +589,7 @@ void GateMatePacker::pack_addf()
|
||||||
CellInfo *cy = grp.at(i);
|
CellInfo *cy = grp.at(i);
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
cy->cluster = root->name;
|
cy->cluster = root->name;
|
||||||
|
cy->region = root->region;
|
||||||
root->constr_children.push_back(cy);
|
root->constr_children.push_back(cy);
|
||||||
cy->constr_abs_z = false;
|
cy->constr_abs_z = false;
|
||||||
cy->constr_y = +i;
|
cy->constr_y = +i;
|
||||||
|
|
@ -602,6 +612,7 @@ void GateMatePacker::pack_addf()
|
||||||
|
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx)));
|
||||||
upper->cluster = root->name;
|
upper->cluster = root->name;
|
||||||
|
upper->region = root->region;
|
||||||
root->constr_children.push_back(upper);
|
root->constr_children.push_back(upper);
|
||||||
upper->constr_abs_z = false;
|
upper->constr_abs_z = false;
|
||||||
upper->constr_y = +i;
|
upper->constr_y = +i;
|
||||||
|
|
@ -625,12 +636,14 @@ void GateMatePacker::pack_addf()
|
||||||
break;
|
break;
|
||||||
CellInfo *co_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$co_upper", cy->name.c_str(ctx)));
|
CellInfo *co_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$co_upper", cy->name.c_str(ctx)));
|
||||||
co_upper->cluster = root->name;
|
co_upper->cluster = root->name;
|
||||||
|
co_upper->region = root->region;
|
||||||
root->constr_children.push_back(co_upper);
|
root->constr_children.push_back(co_upper);
|
||||||
co_upper->constr_abs_z = false;
|
co_upper->constr_abs_z = false;
|
||||||
co_upper->constr_z = -1;
|
co_upper->constr_z = -1;
|
||||||
co_upper->constr_y = +i + 1;
|
co_upper->constr_y = +i + 1;
|
||||||
CellInfo *co_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$co", cy->name.c_str(ctx)));
|
CellInfo *co_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$co", cy->name.c_str(ctx)));
|
||||||
co_lower->cluster = root->name;
|
co_lower->cluster = root->name;
|
||||||
|
co_lower->region = root->region;
|
||||||
root->constr_children.push_back(co_lower);
|
root->constr_children.push_back(co_lower);
|
||||||
co_lower->constr_abs_z = false;
|
co_lower->constr_abs_z = false;
|
||||||
co_lower->constr_y = +i + 1;
|
co_lower->constr_y = +i + 1;
|
||||||
|
|
@ -678,6 +691,7 @@ void GateMatePacker::pack_constants()
|
||||||
h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params);
|
h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params);
|
||||||
net_PACKER_VCC = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
net_PACKER_VCC = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||||||
net_PACKER_GND = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
net_PACKER_GND = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||||||
|
net_SER_CLK = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMatePacker::remove_constants()
|
void GateMatePacker::remove_constants()
|
||||||
|
|
@ -719,6 +733,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdS
|
||||||
if (place) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_ramio);
|
cell->constr_children.push_back(cpe_ramio);
|
||||||
cpe_ramio->cluster = cell->cluster;
|
cpe_ramio->cluster = cell->cluster;
|
||||||
|
cpe_ramio->region = cell->region;
|
||||||
cpe_ramio->constr_abs_z = false;
|
cpe_ramio->constr_abs_z = false;
|
||||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -730,6 +745,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdS
|
||||||
if (place) {
|
if (place) {
|
||||||
cpe_ramio->constr_children.push_back(cpe_half);
|
cpe_ramio->constr_children.push_back(cpe_half);
|
||||||
cpe_half->cluster = cell->cluster;
|
cpe_half->cluster = cell->cluster;
|
||||||
|
cpe_half->region = cell->region;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = -4;
|
cpe_half->constr_z = -4;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -757,6 +773,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdS
|
||||||
if (place) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_ramio);
|
cell->constr_children.push_back(cpe_ramio);
|
||||||
cpe_ramio->cluster = cell->cluster;
|
cpe_ramio->cluster = cell->cluster;
|
||||||
|
cpe_ramio->region = cell->region;
|
||||||
cpe_ramio->constr_abs_z = false;
|
cpe_ramio->constr_abs_z = false;
|
||||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -767,6 +784,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdS
|
||||||
if (place) {
|
if (place) {
|
||||||
cpe_ramio->constr_children.push_back(cpe_half);
|
cpe_ramio->constr_children.push_back(cpe_half);
|
||||||
cpe_half->cluster = cell->cluster;
|
cpe_half->cluster = cell->cluster;
|
||||||
|
cpe_half->region = cell->region;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = -4;
|
cpe_half->constr_z = -4;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -815,6 +833,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, Id
|
||||||
if (place) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_ramio);
|
cell->constr_children.push_back(cpe_ramio);
|
||||||
cpe_ramio->cluster = cell->cluster;
|
cpe_ramio->cluster = cell->cluster;
|
||||||
|
cpe_ramio->region = cell->region;
|
||||||
cpe_ramio->constr_abs_z = false;
|
cpe_ramio->constr_abs_z = false;
|
||||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index;
|
cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -826,6 +845,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, Id
|
||||||
if (place) {
|
if (place) {
|
||||||
cpe_ramio->constr_children.push_back(cpe_half);
|
cpe_ramio->constr_children.push_back(cpe_half);
|
||||||
cpe_half->cluster = cell->cluster;
|
cpe_half->cluster = cell->cluster;
|
||||||
|
cpe_half->region = cell->region;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = -4;
|
cpe_half->constr_z = -4;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ void GateMatePacker::pack_io()
|
||||||
s.cell->disconnectPort(s.port);
|
s.cell->disconnectPort(s.port);
|
||||||
s.cell->connectPort(s.port, ser_clk);
|
s.cell->connectPort(s.port, ser_clk);
|
||||||
}
|
}
|
||||||
|
net_SER_CLK = ser_clk;
|
||||||
ci.disconnectPort(id_I);
|
ci.disconnectPort(id_I);
|
||||||
packed_cells.emplace(ci.name);
|
packed_cells.emplace(ci.name);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -302,7 +303,6 @@ void GateMatePacker::pack_io()
|
||||||
uarch->available_pads.erase(id);
|
uarch->available_pads.erase(id);
|
||||||
loc = id.c_str(ctx);
|
loc = id.c_str(ctx);
|
||||||
}
|
}
|
||||||
ci.params[id_LOC] = Property(loc);
|
|
||||||
|
|
||||||
BelId bel;
|
BelId bel;
|
||||||
if (uarch->locations.count(std::make_pair(ctx->id(loc), uarch->preferred_die)))
|
if (uarch->locations.count(std::make_pair(ctx->id(loc), uarch->preferred_die)))
|
||||||
|
|
@ -370,12 +370,12 @@ void GateMatePacker::pack_io_sel()
|
||||||
} else if (clk_net == net_PACKER_VCC) {
|
} else if (clk_net == net_PACKER_VCC) {
|
||||||
cell->disconnectPort(id_CLK);
|
cell->disconnectPort(id_CLK);
|
||||||
} else {
|
} else {
|
||||||
if (!global_signals.count(clk_net)) {
|
if (!uarch->global_signals.count(clk_net)) {
|
||||||
cell->movePortTo(id_CLK, target, id_OUT4);
|
cell->movePortTo(id_CLK, target, id_OUT4);
|
||||||
target->params[id_SEL_OUT_CLOCK] = Property(Property::State::S1);
|
target->params[id_SEL_OUT_CLOCK] = Property(Property::State::S1);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
int index = global_signals[clk_net];
|
int index = uarch->global_signals[clk_net];
|
||||||
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
|
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
|
||||||
target->params[id_OUT_CLOCK] = Property(index, 2);
|
target->params[id_OUT_CLOCK] = Property(index, 2);
|
||||||
}
|
}
|
||||||
|
|
@ -391,11 +391,11 @@ void GateMatePacker::pack_io_sel()
|
||||||
} else if (clk_net == net_PACKER_VCC) {
|
} else if (clk_net == net_PACKER_VCC) {
|
||||||
cell->disconnectPort(id_CLK);
|
cell->disconnectPort(id_CLK);
|
||||||
} else {
|
} else {
|
||||||
if (!global_signals.count(clk_net)) {
|
if (!uarch->global_signals.count(clk_net)) {
|
||||||
cell->movePortTo(id_CLK, target, id_OUT4);
|
cell->movePortTo(id_CLK, target, id_OUT4);
|
||||||
target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1);
|
target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1);
|
||||||
} else {
|
} else {
|
||||||
int index = global_signals[clk_net];
|
int index = uarch->global_signals[clk_net];
|
||||||
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
|
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
|
||||||
target->params[id_IN_CLOCK] = Property(index, 2);
|
target->params[id_IN_CLOCK] = Property(index, 2);
|
||||||
}
|
}
|
||||||
|
|
@ -406,7 +406,7 @@ void GateMatePacker::pack_io_sel()
|
||||||
auto merge_ibf = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
|
auto merge_ibf = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
|
||||||
CellInfo *dff = (*di_net->users.begin()).cell;
|
CellInfo *dff = (*di_net->users.begin()).cell;
|
||||||
if (is_gpio_valid_dff(dff)) {
|
if (is_gpio_valid_dff(dff)) {
|
||||||
if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
|
if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
|
||||||
log_warning("Found DFF %s cell, but not enough CLK signals.\n", dff->name.c_str(ctx));
|
log_warning("Found DFF %s cell, but not enough CLK signals.\n", dff->name.c_str(ctx));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +431,7 @@ void GateMatePacker::pack_io_sel()
|
||||||
|
|
||||||
auto merge_iddr = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
|
auto merge_iddr = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
|
||||||
CellInfo *iddr = (*di_net->users.begin()).cell;
|
CellInfo *iddr = (*di_net->users.begin()).cell;
|
||||||
if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
|
if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
|
||||||
log_warning("Found IDDR %s cell, but not enough CLK signals.\n", iddr->name.c_str(ctx));
|
log_warning("Found IDDR %s cell, but not enough CLK signals.\n", iddr->name.c_str(ctx));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -454,6 +454,9 @@ void GateMatePacker::pack_io_sel()
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Invert CPE out for output enable (OUT3)
|
||||||
|
bool is_inverted[4] = {false, false, true, false};
|
||||||
|
|
||||||
for (auto &cell : cells) {
|
for (auto &cell : cells) {
|
||||||
CellInfo &ci = *cell;
|
CellInfo &ci = *cell;
|
||||||
bool ff_obf = bool_or_default(ci.params, id_FF_OBF, 0);
|
bool ff_obf = bool_or_default(ci.params, id_FF_OBF, 0);
|
||||||
|
|
@ -467,8 +470,6 @@ void GateMatePacker::pack_io_sel()
|
||||||
}
|
}
|
||||||
|
|
||||||
ci.cluster = ci.name;
|
ci.cluster = ci.name;
|
||||||
std::string loc = str_or_default(ci.params, id_LOC, "UNPLACED");
|
|
||||||
ci.unsetParam(id_LOC);
|
|
||||||
|
|
||||||
NetInfo *do_net = ci.getPort(id_A);
|
NetInfo *do_net = ci.getPort(id_A);
|
||||||
bool use_custom_clock = false;
|
bool use_custom_clock = false;
|
||||||
|
|
@ -503,6 +504,16 @@ void GateMatePacker::pack_io_sel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool oddr_merged = false;
|
bool oddr_merged = false;
|
||||||
|
if (do_net->driver.cell && do_net->driver.cell->type == id_CC_LUT1 && do_net->users.entries() == 1) {
|
||||||
|
NetInfo *net = do_net->driver.cell->getPort(id_I0);
|
||||||
|
if (net->driver.cell && net->driver.cell->type == id_CC_ODDR && net->users.entries() == 1) {
|
||||||
|
do_net = net;
|
||||||
|
packed_cells.insert(net->driver.cell->name);
|
||||||
|
// Inverting both input is equal to inverter at output
|
||||||
|
is_inverted[0] = true;
|
||||||
|
is_inverted[1] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (do_net->driver.cell && do_net->driver.cell->type == id_CC_ODDR && do_net->users.entries() == 1) {
|
if (do_net->driver.cell && do_net->driver.cell->type == id_CC_ODDR && do_net->users.entries() == 1) {
|
||||||
CellInfo *oddr = do_net->driver.cell;
|
CellInfo *oddr = do_net->driver.cell;
|
||||||
ci.params[id_OUT1_FF] = Property(Property::State::S1);
|
ci.params[id_OUT1_FF] = Property(Property::State::S1);
|
||||||
|
|
@ -512,7 +523,7 @@ void GateMatePacker::pack_io_sel()
|
||||||
ci.disconnectPort(id_A);
|
ci.disconnectPort(id_A);
|
||||||
oddr->movePortTo(id_D0, &ci, id_OUT2);
|
oddr->movePortTo(id_D0, &ci, id_OUT2);
|
||||||
oddr->movePortTo(id_D1, &ci, id_OUT1);
|
oddr->movePortTo(id_D1, &ci, id_OUT1);
|
||||||
const auto &pad = ctx->get_package_pin(ctx->id(loc));
|
const auto &pad = uarch->bel_to_pad[ci.bel];
|
||||||
int die = uarch->tile_extra_data(ci.bel.tile)->die;
|
int die = uarch->tile_extra_data(ci.bel.tile)->die;
|
||||||
auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank];
|
auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank];
|
||||||
if (cpe_half) {
|
if (cpe_half) {
|
||||||
|
|
@ -558,8 +569,8 @@ void GateMatePacker::pack_io_sel()
|
||||||
Loc root_loc = ctx->getBelLocation(ci.bel);
|
Loc root_loc = ctx->getBelLocation(ci.bel);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first;
|
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first;
|
||||||
if (cpe && i == 2)
|
if (cpe && is_inverted[i])
|
||||||
cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4); // Invert CPE out for output enable (OUT3)
|
cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush_cells();
|
flush_cells();
|
||||||
|
|
|
||||||
|
|
@ -573,6 +573,7 @@ void GateMatePacker::pack_mult()
|
||||||
// we also constrain it to proper Z location
|
// we also constrain it to proper Z location
|
||||||
auto *root = m.cols[0].b_passthru.upper;
|
auto *root = m.cols[0].b_passthru.upper;
|
||||||
root->cluster = root->name;
|
root->cluster = root->name;
|
||||||
|
root->region = mult->region;
|
||||||
root->constr_abs_z = true;
|
root->constr_abs_z = true;
|
||||||
root->constr_z = CPE_LT_U_Z;
|
root->constr_z = CPE_LT_U_Z;
|
||||||
|
|
||||||
|
|
@ -581,6 +582,7 @@ void GateMatePacker::pack_mult()
|
||||||
return;
|
return;
|
||||||
root->constr_children.push_back(cell);
|
root->constr_children.push_back(cell);
|
||||||
cell->cluster = root->name;
|
cell->cluster = root->name;
|
||||||
|
cell->region = root->region;
|
||||||
cell->constr_abs_z = true;
|
cell->constr_abs_z = true;
|
||||||
cell->constr_x = x_offset;
|
cell->constr_x = x_offset;
|
||||||
cell->constr_y = y_offset;
|
cell->constr_y = y_offset;
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,11 @@ TEST_F(GateMateTest, remove_lut1_zero)
|
||||||
|
|
||||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
|
|
||||||
lut1->connectPorts(id_O, obuf, id_A);
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
ASSERT_EQ(ctx->cells.size(), 2LU);
|
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||||
ctx->uarch->pack();
|
ctx->uarch->pack();
|
||||||
ASSERT_EQ(ctx->cells.size(), 1LU);
|
ASSERT_EQ(ctx->cells.size(), 4LU);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GateMateTest, remove_lut1_one)
|
TEST_F(GateMateTest, remove_lut1_one)
|
||||||
|
|
@ -63,11 +63,11 @@ TEST_F(GateMateTest, remove_lut1_one)
|
||||||
|
|
||||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
|
|
||||||
lut1->connectPorts(id_O, obuf, id_A);
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
ASSERT_EQ(ctx->cells.size(), 2LU);
|
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||||
ctx->uarch->pack();
|
ctx->uarch->pack();
|
||||||
ASSERT_EQ(ctx->cells.size(), 1LU);
|
ASSERT_EQ(ctx->cells.size(), 4LU);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GateMateTest, remove_lut1_pass)
|
TEST_F(GateMateTest, remove_lut1_pass)
|
||||||
|
|
@ -78,14 +78,14 @@ TEST_F(GateMateTest, remove_lut1_pass)
|
||||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
||||||
|
|
||||||
ibuf->connectPorts(id_Y, lut1, id_I0);
|
direct_connect(ibuf, id_Y, lut1, id_I0);
|
||||||
lut1->connectPorts(id_O, obuf, id_A);
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||||
ctx->uarch->pack();
|
ctx->uarch->pack();
|
||||||
// Expect IBUF -> CPE -> OBUF
|
// Expect IBUF -> CPE -> OBUF
|
||||||
// LUT removed, but CPE for driving OBUF added
|
// LUT removed, but CPE for driving OBUF added
|
||||||
ASSERT_EQ(ctx->cells.size(), 4LU);
|
ASSERT_EQ(ctx->cells.size(), 8LU);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GateMateTest, remove_lut1_inv)
|
TEST_F(GateMateTest, remove_lut1_inv)
|
||||||
|
|
@ -96,14 +96,14 @@ TEST_F(GateMateTest, remove_lut1_inv)
|
||||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
||||||
|
|
||||||
ibuf->connectPorts(id_Y, lut1, id_I0);
|
direct_connect(ibuf, id_Y, lut1, id_I0);
|
||||||
lut1->connectPorts(id_O, obuf, id_A);
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||||
ctx->uarch->pack();
|
ctx->uarch->pack();
|
||||||
// Expect IBUF -> CPE -> OBUF
|
// Expect IBUF -> CPE -> OBUF
|
||||||
// LUT merged, but CPE for driving OBUF added
|
// LUT merged, but CPE for driving OBUF added
|
||||||
ASSERT_EQ(ctx->cells.size(), 5LU);
|
ASSERT_EQ(ctx->cells.size(), 9LU);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GateMateTest, remove_lut1_not_driven)
|
TEST_F(GateMateTest, remove_lut1_not_driven)
|
||||||
|
|
@ -123,5 +123,5 @@ TEST_F(GateMateTest, remove_lut1_not_driven)
|
||||||
ctx->uarch->pack();
|
ctx->uarch->pack();
|
||||||
// Expect IBUF -> CPE -> OBUF
|
// Expect IBUF -> CPE -> OBUF
|
||||||
// LUT1 removed as not used, but CPE for driving OBUF added
|
// LUT1 removed as not used, but CPE for driving OBUF added
|
||||||
ASSERT_EQ(ctx->cells.size(), 4LU);
|
ASSERT_EQ(ctx->cells.size(), 8LU);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,4 +201,11 @@ CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name)
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port)
|
||||||
|
{
|
||||||
|
NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx)));
|
||||||
|
o_cell->connectPort(o_port, net);
|
||||||
|
i_cell->connectPort(i_port, net);
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class GateMateTest : public ::testing::Test
|
||||||
virtual void TearDown() override;
|
virtual void TearDown() override;
|
||||||
|
|
||||||
CellInfo *create_cell_ptr(IdString type, std::string name);
|
CellInfo *create_cell_ptr(IdString type, std::string name);
|
||||||
|
void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port);
|
||||||
|
|
||||||
ArchArgs chipArgs;
|
ArchArgs chipArgs;
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue