mistral: Adding support for MLABs as memory
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
fe31fba623
commit
f5f7ef6864
@ -152,11 +152,12 @@ IdStringList Arch::getBelName(BelId bel) const
|
||||
bool Arch::isBelLocationValid(BelId bel) const
|
||||
{
|
||||
auto &data = bel_data(bel);
|
||||
if (data.type == id_MISTRAL_COMB) {
|
||||
return is_alm_legal(data.lab_data.lab, data.lab_data.alm) && check_lab_input_count(data.lab_data.lab);
|
||||
if (data.type.in(id_MISTRAL_COMB, id_MISTRAL_MCOMB)) {
|
||||
return is_alm_legal(data.lab_data.lab, data.lab_data.alm) && check_lab_input_count(data.lab_data.lab) &&
|
||||
check_mlab_groups(data.lab_data.lab);
|
||||
} else if (data.type == id_MISTRAL_FF) {
|
||||
return is_alm_legal(data.lab_data.lab, data.lab_data.alm) && check_lab_input_count(data.lab_data.lab) &&
|
||||
is_lab_ctrlset_legal(data.lab_data.lab);
|
||||
is_lab_ctrlset_legal(data.lab_data.lab) && check_mlab_groups(data.lab_data.lab);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -164,7 +165,7 @@ bool Arch::isBelLocationValid(BelId bel) const
|
||||
void Arch::update_bel(BelId bel)
|
||||
{
|
||||
auto &data = bel_data(bel);
|
||||
if (data.type == id_MISTRAL_COMB || data.type == id_MISTRAL_FF) {
|
||||
if (data.type.in(id_MISTRAL_COMB, id_MISTRAL_MCOMB, id_MISTRAL_FF)) {
|
||||
update_alm_input_count(data.lab_data.lab, data.lab_data.alm);
|
||||
}
|
||||
}
|
||||
@ -249,6 +250,8 @@ bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
IdString bel_type = getBelType(bel);
|
||||
if (bel_type == id_MISTRAL_COMB)
|
||||
return is_comb_cell(cell_type);
|
||||
else if (bel_type == id_MISTRAL_MCOMB)
|
||||
return is_comb_cell(cell_type) || (cell_type == id_MISTRAL_MLAB);
|
||||
else if (bel_type == id_MISTRAL_IO)
|
||||
return is_io_cell(cell_type);
|
||||
else if (bel_type == id_MISTRAL_CLKENA)
|
||||
@ -259,7 +262,7 @@ bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
|
||||
BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const
|
||||
{
|
||||
if (is_comb_cell(cell_type))
|
||||
if (is_comb_cell(cell_type) || cell_type == id_MISTRAL_MLAB)
|
||||
return id_MISTRAL_COMB;
|
||||
else if (is_io_cell(cell_type))
|
||||
return id_MISTRAL_IO;
|
||||
@ -269,6 +272,15 @@ BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const
|
||||
return cell_type;
|
||||
}
|
||||
|
||||
BelBucketId Arch::getBelBucketForBel(BelId bel) const
|
||||
{
|
||||
IdString bel_type = getBelType(bel);
|
||||
if (bel_type == id_MISTRAL_MCOMB)
|
||||
return id_MISTRAL_COMB;
|
||||
else
|
||||
return bel_type;
|
||||
}
|
||||
|
||||
BelId Arch::bel_by_block_idx(int x, int y, IdString type, int block_index) const
|
||||
{
|
||||
auto &bels = bels_by_tile.at(pos2idx(x, y));
|
||||
@ -380,7 +392,7 @@ void Arch::assignArchInfo()
|
||||
{
|
||||
for (auto &cell : cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (is_comb_cell(ci->type))
|
||||
if (is_comb_cell(ci->type) || ci->type == id_MISTRAL_MLAB)
|
||||
assign_comb_info(ci);
|
||||
else if (ci->type == id_MISTRAL_FF)
|
||||
assign_ff_info(ci);
|
||||
|
@ -437,6 +437,7 @@ struct Arch : BaseArch<ArchRanges>
|
||||
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
||||
BelBucketId getBelBucketForCellType(IdString cell_type) const override;
|
||||
BelBucketId getBelBucketForBel(BelId bel) const override;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
@ -469,6 +470,7 @@ struct Arch : BaseArch<ArchRanges>
|
||||
bool is_alm_legal(uint32_t lab, uint8_t alm) const; // lab.cc
|
||||
bool is_lab_ctrlset_legal(uint32_t lab) const; // lab.cc
|
||||
bool check_lab_input_count(uint32_t lab) const; // lab.cc
|
||||
bool check_mlab_groups(uint32_t lab) const; // lab.cc
|
||||
|
||||
void assign_comb_info(CellInfo *cell) const; // lab.cc
|
||||
void assign_ff_info(CellInfo *cell) const; // lab.cc
|
||||
@ -480,6 +482,10 @@ struct Arch : BaseArch<ArchRanges>
|
||||
|
||||
uint64_t compute_lut_mask(uint32_t lab, uint8_t alm); // lab.cc
|
||||
|
||||
// Keeping track of unique MLAB write ports to assign them indices
|
||||
dict<IdString, IdString> get_mlab_key(const CellInfo *cell, bool include_raddr = false) const; // lab.cc
|
||||
mutable idict<dict<IdString, IdString>> mlab_groups;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
bool is_io_cell(IdString cell_type) const; // io.cc
|
||||
|
@ -195,6 +195,11 @@ struct ArchCellInfo : BaseClusterInfo
|
||||
|
||||
bool is_carry, is_shared, is_extended;
|
||||
bool carry_start, carry_end;
|
||||
|
||||
// MLABs with compatible write ports have this set to the same non-negative integer. -1 means this isn't a
|
||||
// MLAB
|
||||
int mlab_group;
|
||||
ControlSig wclk, we;
|
||||
} combInfo;
|
||||
struct
|
||||
{
|
||||
|
@ -226,11 +226,33 @@ struct MistralBitgen
|
||||
std::all_of(ffs.begin(), ffs.end(), [](CellInfo *c) { return !c; }))
|
||||
return false;
|
||||
|
||||
bool is_lutram =
|
||||
(luts[0] && luts[0]->combInfo.mlab_group != -1) || (luts[1] && luts[1]->combInfo.mlab_group != -1);
|
||||
|
||||
auto pos = alm_data.lut_bels[0].pos;
|
||||
// Combinational mode - TODO: flop feedback
|
||||
cv->bmux_m_set(block_type, pos, CycloneV::MODE, alm, alm_data.l6_mode ? CycloneV::L6 : CycloneV::L5);
|
||||
// LUT function
|
||||
cv->bmux_r_set(block_type, pos, CycloneV::LUT_MASK, alm, ctx->compute_lut_mask(lab, alm));
|
||||
if (is_lutram) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Many MLAB settings apply to the whole LAB, not just the ALM
|
||||
cv->bmux_m_set(block_type, pos, CycloneV::MODE, i, CycloneV::RAM);
|
||||
cv->bmux_n_set(block_type, pos, CycloneV::T_FEEDBACK_SEL, i, 1);
|
||||
}
|
||||
cv->bmux_r_set(block_type, pos, CycloneV::LUT_MASK, alm, 0xFFFFFFFFFFFFFFFFULL); // TODO: LUTRAM init
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::BPKREG1, alm, true);
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::TPKREG0, alm, true);
|
||||
cv->bmux_m_set(block_type, pos, CycloneV::MCRG_VOLTAGE, 0, CycloneV::VCCL);
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::RAM_DIS, 0, false);
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::WRITE_EN, 0, true);
|
||||
cv->bmux_n_set(block_type, pos, CycloneV::WRITE_PULSE_LENGTH, 0, 650); // picoseconds, presumably
|
||||
// TODO: understand how these enables really work
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::EN2_EN, 0, false);
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::EN_SCLK_LOAD_WHAT, 0, true);
|
||||
cv->bmux_m_set(block_type, pos, CycloneV::SCLR_MUX, 0, CycloneV::GIN2);
|
||||
} else {
|
||||
// Combinational mode - TODO: flop feedback
|
||||
cv->bmux_m_set(block_type, pos, CycloneV::MODE, alm, alm_data.l6_mode ? CycloneV::L6 : CycloneV::L5);
|
||||
// LUT function
|
||||
cv->bmux_r_set(block_type, pos, CycloneV::LUT_MASK, alm, ctx->compute_lut_mask(lab, alm));
|
||||
}
|
||||
// DFF/LUT output selection
|
||||
const std::array<CycloneV::bmux_type_t, 6> mux_settings{CycloneV::TDFF0, CycloneV::TDFF1, CycloneV::TDFF1L,
|
||||
CycloneV::BDFF0, CycloneV::BDFF1, CycloneV::BDFF1L};
|
||||
@ -310,6 +332,26 @@ struct MistralBitgen
|
||||
cv->bmux_b_set(block_type, pos, CycloneV::SLOAD_INV, 0, ff->ffInfo.ctrlset.sload.inverted);
|
||||
}
|
||||
}
|
||||
if (is_lutram) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
CellInfo *lut = luts[i];
|
||||
if (!lut || lut->combInfo.mlab_group == -1)
|
||||
continue;
|
||||
int ce_idx = alm_data.clk_ena_idx[1];
|
||||
cv->bmux_m_set(block_type, pos, clk_sel[1], alm, clk_choice[ce_idx]);
|
||||
if (lut->combInfo.wclk.inverted)
|
||||
cv->bmux_b_set(block_type, pos, clk_inv[ce_idx], 0, true);
|
||||
if (get_net_or_empty(lut, id_A1EN) != nullptr) {
|
||||
cv->bmux_b_set(block_type, pos, en_en[ce_idx], 0, true);
|
||||
cv->bmux_b_set(block_type, pos, en_ninv[ce_idx], 0, lut->combInfo.we.inverted);
|
||||
} else {
|
||||
cv->bmux_b_set(block_type, pos, en_en[ce_idx], 0, false);
|
||||
}
|
||||
// TODO: understand what these are doing
|
||||
cv->bmux_b_set(block_type, pos, sclr_dis[0], alm, true);
|
||||
cv->bmux_b_set(block_type, pos, sclr_dis[1], alm, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
X(MISTRAL_COMB)
|
||||
X(MISTRAL_MCOMB)
|
||||
|
||||
X(MISTRAL_FF)
|
||||
X(LAB)
|
||||
X(MLAB)
|
||||
@ -84,3 +86,11 @@ X(WA3)
|
||||
X(WA4)
|
||||
X(WCLK)
|
||||
X(WE)
|
||||
|
||||
X(MISTRAL_MLAB)
|
||||
X(CLK1)
|
||||
X(A1EN)
|
||||
X(A1DATA)
|
||||
X(B1DATA)
|
||||
X(WCLK_INV)
|
||||
X(WE_INV)
|
||||
|
115
mistral/lab.cc
115
mistral/lab.cc
@ -84,7 +84,8 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx)
|
||||
share_out = arch->add_wire(x, y, arch->id(stringf("SHARE[%d]", z * 2 + i)));
|
||||
}
|
||||
|
||||
BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_COMB%d", z, i)), id_MISTRAL_COMB);
|
||||
BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_COMB%d", z, i)),
|
||||
lab.is_mlab ? id_MISTRAL_MCOMB : id_MISTRAL_COMB);
|
||||
// LUT/MUX inputs
|
||||
arch->add_bel_pin(bel, id_A, PORT_IN, arch->get_port(block_type, x, y, z, CycloneV::A));
|
||||
arch->add_bel_pin(bel, id_B, PORT_IN, arch->get_port(block_type, x, y, z, CycloneV::B));
|
||||
@ -244,6 +245,23 @@ bool Arch::is_comb_cell(IdString cell_type) const
|
||||
}
|
||||
}
|
||||
|
||||
dict<IdString, IdString> Arch::get_mlab_key(const CellInfo *cell, bool include_raddr) const
|
||||
{
|
||||
dict<IdString, IdString> key;
|
||||
for (auto &port : cell->ports) {
|
||||
if (port.first.in(id_A1DATA, id_B1DATA))
|
||||
continue;
|
||||
if (!include_raddr && port.first.str(this).find("B1ADDR") == 0)
|
||||
continue;
|
||||
key[port.first] = port.second.net ? port.second.net->name : IdString();
|
||||
}
|
||||
if (cell->pin_data.count(id_CLK1) && cell->pin_data.at(id_CLK1).state == PIN_INV)
|
||||
key[id_WCLK_INV] = id_Y;
|
||||
if (cell->pin_data.count(id_A1EN) && cell->pin_data.at(id_A1EN).state == PIN_INV)
|
||||
key[id_WE_INV] = id_Y;
|
||||
return key;
|
||||
}
|
||||
|
||||
void Arch::assign_comb_info(CellInfo *cell) const
|
||||
{
|
||||
cell->combInfo.is_carry = false;
|
||||
@ -252,8 +270,19 @@ void Arch::assign_comb_info(CellInfo *cell) const
|
||||
cell->combInfo.carry_start = false;
|
||||
cell->combInfo.carry_end = false;
|
||||
cell->combInfo.chain_shared_input_count = 0;
|
||||
cell->combInfo.mlab_group = -1;
|
||||
|
||||
if (cell->type == id_MISTRAL_ALUT_ARITH) {
|
||||
if (cell->type == id_MISTRAL_MLAB) {
|
||||
cell->combInfo.wclk = get_ctrlsig(getCtx(), cell, id_CLK1);
|
||||
cell->combInfo.we = get_ctrlsig(getCtx(), cell, id_A1EN, true);
|
||||
cell->combInfo.lut_input_count = 5;
|
||||
cell->combInfo.lut_bits_count = 32;
|
||||
for (int i = 0; i < 5; i++)
|
||||
cell->combInfo.lut_in[i] = get_net_or_empty(cell, id(stringf("B1ADDR[%d]", i)));
|
||||
auto key = get_mlab_key(cell);
|
||||
cell->combInfo.mlab_group = mlab_groups(key);
|
||||
cell->combInfo.comb_out = get_net_or_empty(cell, id_B1DATA);
|
||||
} else if (cell->type == id_MISTRAL_ALUT_ARITH) {
|
||||
cell->combInfo.is_carry = true;
|
||||
cell->combInfo.lut_input_count = 5;
|
||||
cell->combInfo.lut_bits_count = 32;
|
||||
@ -477,8 +506,9 @@ void Arch::update_alm_input_count(uint32_t lab, uint8_t alm)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shared_lut_inputs >= 2) {
|
||||
// only 2 inputs have guaranteed sharing, without routeability based LUT permutation at least
|
||||
if (shared_lut_inputs >= 2 && luts[0]->combInfo.mlab_group == -1) {
|
||||
// only 2 inputs have guaranteed sharing in non-MLAB mode, without routeability based LUT permutation at
|
||||
// least
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -513,6 +543,38 @@ bool Arch::check_lab_input_count(uint32_t lab) const
|
||||
return (count <= 42);
|
||||
}
|
||||
|
||||
bool Arch::check_mlab_groups(uint32_t lab) const
|
||||
{
|
||||
auto &lab_data = labs.at(lab);
|
||||
if (!lab_data.is_mlab)
|
||||
return true;
|
||||
int found_group = -2;
|
||||
for (const auto &alm_data : lab_data.alms) {
|
||||
std::array<const CellInfo *, 2> luts{getBoundBelCell(alm_data.lut_bels[0]),
|
||||
getBoundBelCell(alm_data.lut_bels[1])};
|
||||
for (const CellInfo *lut : luts) {
|
||||
if (!lut)
|
||||
continue;
|
||||
if (found_group == -2)
|
||||
found_group = lut->combInfo.mlab_group;
|
||||
else if (found_group != lut->combInfo.mlab_group)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (found_group >= 0) {
|
||||
for (const auto &alm_data : lab_data.alms) {
|
||||
std::array<const CellInfo *, 4> ffs{
|
||||
getBoundBelCell(alm_data.ff_bels[0]), getBoundBelCell(alm_data.ff_bels[1]),
|
||||
getBoundBelCell(alm_data.ff_bels[2]), getBoundBelCell(alm_data.ff_bels[3])};
|
||||
for (const CellInfo *ff : ffs) {
|
||||
if (ff)
|
||||
return false; // be conservative and don't allow LUTRAMs and FFs together
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool check_assign_sig(ControlSig &sig_set, const ControlSig &sig)
|
||||
{
|
||||
@ -564,7 +626,6 @@ struct LabCtrlSetWorker
|
||||
const CellInfo *ff = arch->getBoundBelCell(arch->labs.at(lab).alms.at(alm).ff_bels.at(i));
|
||||
if (ff == nullptr)
|
||||
continue;
|
||||
|
||||
if (!check_assign_sig(clk, ff->ffInfo.ctrlset.clk))
|
||||
return false;
|
||||
if (!check_assign_sig(sload, ff->ffInfo.ctrlset.sload))
|
||||
@ -647,6 +708,19 @@ void Arch::assign_control_sets(uint32_t lab)
|
||||
|
||||
for (uint8_t alm = 0; alm < 10; alm++) {
|
||||
auto &alm_data = lab_data.alms.at(alm);
|
||||
if (lab_data.is_mlab) {
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
BelId lut_bel = alm_data.lut_bels.at(i);
|
||||
const CellInfo *lut = getBoundBelCell(lut_bel);
|
||||
if (!lut || lut->combInfo.mlab_group == -1)
|
||||
continue;
|
||||
WireId wclk_wire = getBelPinWire(lut_bel, id_WCLK);
|
||||
WireId we_wire = getBelPinWire(lut_bel, id_WE);
|
||||
// Force use of CLK0/ENA0 for LUTRAMs. Might have to revisit if we ever support packing LUTRAMs and FFs
|
||||
reserve_route(lab_data.clk_wires[0], wclk_wire);
|
||||
reserve_route(lab_data.ena_wires[0], we_wire);
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
BelId ff_bel = alm_data.ff_bels.at(i);
|
||||
const CellInfo *ff = getBoundBelCell(ff_bel);
|
||||
@ -658,7 +732,7 @@ void Arch::assign_control_sets(uint32_t lab)
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (ena_sig == worker.datain[ena_datain[j]]) {
|
||||
if (getCtx()->debug) {
|
||||
log_info("Assigned CLK/ENA set %d to FF %s (%s)\n", i, nameOf(ff), getCtx()->nameOfBel(ff_bel));
|
||||
log_info("Assigned CLK/ENA set %d to FF %s (%s)\n", j, nameOf(ff), getCtx()->nameOfBel(ff_bel));
|
||||
}
|
||||
// TODO: lock clock according to ENA choice, too, when we support two clocks per ALM
|
||||
reserve_route(lab_data.clk_wires[0], clk_wire);
|
||||
@ -667,7 +741,6 @@ void Arch::assign_control_sets(uint32_t lab)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ControlSig aclr_sig = ff->ffInfo.ctrlset.aclr;
|
||||
WireId aclr_wire = getBelPinWire(ff_bel, id_ACLR);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
@ -707,6 +780,22 @@ static void assign_lut6_inputs(CellInfo *cell, int lut)
|
||||
cell->pin_data[log].bel_pins.push_back(phys_pins.at(phys_idx++));
|
||||
}
|
||||
}
|
||||
|
||||
static void assign_mlab_inputs(Context *ctx, CellInfo *cell, int lut)
|
||||
{
|
||||
cell->pin_data[id_CLK1].bel_pins = {id_WCLK};
|
||||
cell->pin_data[id_A1EN].bel_pins = {id_WE};
|
||||
cell->pin_data[id_A1DATA].bel_pins = {(lut == 1) ? id_E1 : id_E0};
|
||||
cell->pin_data[id_B1DATA].bel_pins = {id_COMBOUT};
|
||||
cell->pin_data[id_A1EN].bel_pins = {id_WE};
|
||||
|
||||
std::array<IdString, 6> raddr_pins{id_A, id_B, id_C, id_D, id_F0};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cell->pin_data[ctx->id(stringf("A1ADDR[%d]", i))].bel_pins = {ctx->id(stringf("WA%d", i))};
|
||||
cell->pin_data[ctx->id(stringf("B1ADDR[%d]", i))].bel_pins = {raddr_pins.at(i)};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm)
|
||||
@ -720,16 +809,22 @@ void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm)
|
||||
std::array<CellInfo *, 4> ffs{getBoundBelCell(alm_data.ff_bels[0]), getBoundBelCell(alm_data.ff_bels[1]),
|
||||
getBoundBelCell(alm_data.ff_bels[2]), getBoundBelCell(alm_data.ff_bels[3])};
|
||||
|
||||
bool found_mlab = false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Currently we treat LUT6s as a special case, as they never share inputs
|
||||
if (luts[i] != nullptr && luts[i]->type == id_MISTRAL_ALUT6) {
|
||||
// Currently we treat LUT6s and MLABs as a special case, as they never share inputs or have fixed mappings
|
||||
if (!luts[i])
|
||||
continue;
|
||||
if (luts[i]->type == id_MISTRAL_ALUT6) {
|
||||
alm_data.l6_mode = true;
|
||||
NPNR_ASSERT(luts[1 - i] == nullptr); // only allow one LUT6 per ALM and no other LUTs
|
||||
assign_lut6_inputs(luts[i], i);
|
||||
} else if (luts[i]->type == id_MISTRAL_MLAB) {
|
||||
found_mlab = true;
|
||||
assign_mlab_inputs(getCtx(), luts[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!alm_data.l6_mode) {
|
||||
if (!alm_data.l6_mode && !found_mlab) {
|
||||
// In L5 mode; which is what we use in this case
|
||||
// - A and B are shared
|
||||
// - C, E0, and F0 are exclusive to the top LUT5 secion
|
||||
|
@ -342,12 +342,52 @@ struct MistralPacker
|
||||
}
|
||||
}
|
||||
|
||||
void constrain_lutram()
|
||||
{
|
||||
// We form clusters based on both read and write address; as both being the same makes it more likely these
|
||||
// cells should be packed together, too.
|
||||
// This makes things easier for the placement legaliser to deal with RAM in LAB-compatible blocks without
|
||||
// over-constraining things
|
||||
idict<dict<IdString, IdString>> mlab_keys;
|
||||
std::vector<std::vector<CellInfo *>> mlab_groups;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_MISTRAL_MLAB)
|
||||
continue;
|
||||
auto key = ctx->get_mlab_key(ci, true);
|
||||
int key_idx = mlab_keys(key);
|
||||
if (key_idx >= int(mlab_groups.size()))
|
||||
mlab_groups.resize(key_idx + 1);
|
||||
mlab_groups.at(key_idx).push_back(ci);
|
||||
}
|
||||
// Combine into clusters
|
||||
size_t cluster_size = 20;
|
||||
for (auto &group : mlab_groups) {
|
||||
for (size_t i = 0; i < group.size(); i++) {
|
||||
CellInfo *ci = group.at(i);
|
||||
CellInfo *base = group.at((i / cluster_size) * cluster_size);
|
||||
int cell_index = int(i) % cluster_size;
|
||||
int alm = i / 2;
|
||||
int alm_cell = i % 2;
|
||||
ci->constr_abs_z = true;
|
||||
ci->constr_z = alm * 6 + alm_cell;
|
||||
if (cell_index != 0) {
|
||||
// Not the root of a cluster
|
||||
base->constr_children.push_back(ci);
|
||||
ci->constr_x = 0;
|
||||
ci->constr_y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
init_constant_nets();
|
||||
pack_constants();
|
||||
pack_io();
|
||||
constrain_carries();
|
||||
constrain_lutram();
|
||||
}
|
||||
};
|
||||
}; // namespace
|
||||
|
@ -44,7 +44,11 @@ const dict<IdString, Arch::CellPinsData> Arch::cell_pins_db = {
|
||||
{id_SDATA, PINSTYLE_DEDI},
|
||||
{id_DATAIN, PINSTYLE_INP},
|
||||
}},
|
||||
};
|
||||
{id_MISTRAL_MLAB,
|
||||
{
|
||||
{id_CLK1, PINSTYLE_CLK},
|
||||
{id_A1EN, PINSTYLE_CE},
|
||||
}}};
|
||||
|
||||
CellPinStyle Arch::get_cell_pin_style(const CellInfo *cell, IdString port) const
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user