Skip to content

Commit

Permalink
Gowin. FFs placement.
Browse files Browse the repository at this point in the history
* Allow clusters to be created from FFs and LUTs;

* Immediately create pass-through LUTs from free LUTs adjacent to FF - at the same time ensure alternating use of LUT inputs;

* In case of constant networks, such pass-through LUTs are disconnected from networks altogether;

* Allow FF to be placed directly into SSRAM slides - this is useful when using synchronous reading.

Signed-off-by: YRabbit <[email protected]>
  • Loading branch information
yrabbit committed Oct 22, 2024
1 parent cf42baa commit 7ba83d3
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 62 deletions.
119 changes: 103 additions & 16 deletions himbaechel/uarch/gowin/gowin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ struct GowinImpl : HimbaechelAPI
std::vector<GowinCellInfo> fast_cell_info;
void assign_cell_info();

// If there is an unused LUT adjacent to FF, use it
void create_passthrough_luts(void);

// Remember HCLK sections that have been reserved to route HCLK signals
std::set<BelId> routing_reserved_hclk_sections;

Expand Down Expand Up @@ -531,6 +534,7 @@ void GowinImpl::postPlace()

// adjust cell pin to bel pin mapping for DSP cells (CE, CLK and RESET pins)
adjust_dsp_pin_mapping();
create_passthrough_luts();
}

void GowinImpl::preRoute() { gowin_route_globals(ctx); }
Expand Down Expand Up @@ -590,8 +594,15 @@ bool GowinImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
case ID_ALU:
return slice_valid(l.x, l.y, l.z - BelZ::ALU0_Z);
case ID_RAM16SDP4:
// only slices 4 and 5 are critical for RAM
return slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 5) && slice_valid(l.x, l.y, l.z - BelZ::RAMW_Z + 4);
return slice_valid(l.x, l.y, 0);
case ID_MUX2_LUT5:
return slice_valid(l.x, l.y, (l.z - BelZ::MUX20_Z) / 2);
case ID_MUX2_LUT6:
return slice_valid(l.x, l.y, (l.z - BelZ::MUX21_Z) / 2 + 1);
case ID_MUX2_LUT7:
return slice_valid(l.x, l.y, 3);
case ID_MUX2_LUT8:
return slice_valid(l.x, l.y, 7);
case ID_PADD9: /* fall-through */
case ID_PADD18: /* fall-through */
case ID_MULT9X9: /* fall-through */
Expand Down Expand Up @@ -725,6 +736,73 @@ void GowinImpl::assign_cell_info()
}
}

// If there is an unused LUT next to the DFF, use its inputs for the D input
void GowinImpl::create_passthrough_luts(void)
{
std::vector<std::unique_ptr<CellInfo>> new_cells;
// evenly use all 4 LUT inputs
const std::vector<std::pair<IdString, int>> lut_tmpl = {
{id_I0, 0xaaaa}, {id_I1, 0xcccc}, {id_I2, 0xf0f0}, {id_I3, 0xff00}};
int cur_lut_tmpl = 3;

for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (is_dff(ci)) {
Loc loc = ctx->getBelLocation(ci->bel);
BelId lut_bel = ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z - 1));
CellInfo *lut = ctx->getBoundBelCell(lut_bel);
CellInfo *alu = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z / 2 + BelZ::ALU0_Z)));
const CellInfo *ramw = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BelZ::RAMW_Z)));

if (!(lut || alu || ramw)) {
if (ctx->debug) {
log_info("Found an unused LUT:%s, ", ctx->nameOfBel(lut_bel));
}
// make LUT
auto lut_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_passthrough_lut$"), id_LUT4);
CellInfo *lut = lut_cell.get();
NetInfo *d_net = ci->getPort(id_D);
NPNR_ASSERT(d_net != nullptr);

if (d_net->name == ctx->id("$PACKER_GND") || d_net->name == ctx->id("$PACKER_VCC")) {
if (ctx->debug) {
log("make a constant %s.\n", d_net->name == ctx->id("$PACKER_VCC") ? "VCC" : "GND");
}
ci->disconnectPort(id_D);
if (d_net->name == ctx->id("$PACKER_GND")) {
lut->setParam(id_INIT, 0x0000);
} else {
lut->setParam(id_INIT, 0xffff);
}
} else {
if (ctx->debug) {
log("make a pass-through.\n");
}
IdString lut_input = lut_tmpl.at(cur_lut_tmpl).first;
int lut_init = lut_tmpl.at(cur_lut_tmpl).second;
cur_lut_tmpl = (cur_lut_tmpl + 1) % 4;

lut->addInput(lut_input);
lut->cell_bel_pins[lut_input].clear();
lut->cell_bel_pins.at(lut_input).push_back(lut_input);
ci->movePortTo(id_D, lut, lut_input);
lut->setParam(id_INIT, lut_init);
}
lut->addOutput(id_F);
lut->cell_bel_pins[id_F].clear();
lut->cell_bel_pins.at(id_F).push_back(id_F);
ci->connectPorts(id_D, lut, id_F);

ctx->bindBel(lut_bel, lut, PlaceStrength::STRENGTH_LOCKED);
new_cells.push_back(std::move(lut_cell));
}
}
}
for (auto &cell : new_cells) {
ctx->cells[cell->name] = std::move(cell);
}
}

// DFFs must be same type or compatible
inline bool incompatible_ffs(const CellInfo *ff, const CellInfo *adj_ff)
{
Expand Down Expand Up @@ -798,22 +876,30 @@ bool GowinImpl::dsp_valid(Loc l, IdString bel_type, bool explain_invalid) const
bool GowinImpl::slice_valid(int x, int y, int z) const
{
const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2)));
const bool lut_in_4_5 = lut && (z == 4 || z == 5);
const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1)));
// There are only 6 ALUs
const CellInfo *alu = (z < 6) ? ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z + BelZ::ALU0_Z))) : nullptr;
const CellInfo *ramw =
(z == 4 || z == 5) ? ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, BelZ::RAMW_Z))) : nullptr;
const CellInfo *ramw = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, BelZ::RAMW_Z)));

if (alu && lut) {
return false;
}

if (ramw) {
if (alu || ff || lut_in_4_5) {
// FFs in slices 4 and 5 are not allowed
if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 4 * 2 + 1))) ||
ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 5 * 2 + 1)))) {
return false;
}
return true;
// ALU/LUTs in slices 4, 5, 6, 7 are not allowed
for (int i = 4; i < 8; ++i) {
if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, i * 2)))) {
return false;
}
if (i < 6 && ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, i + BelZ::ALU0_Z)))) {
return false;
}
}
}

// check for ALU/LUT in the adjacent cell
Expand All @@ -829,18 +915,19 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
return false;
}

// if there is DFF it must be connected to this LUT or ALU
if (ff) {
static std::vector<int> mux_z = {BelZ::MUX20_Z, BelZ::MUX21_Z, BelZ::MUX20_Z + 4, BelZ::MUX23_Z,
BelZ::MUX20_Z + 8, BelZ::MUX21_Z + 8, BelZ::MUX20_Z + 12, BelZ::MUX27_Z};
const auto &ff_data = fast_cell_info.at(ff->flat_index);
if (lut) {
const auto &lut_data = fast_cell_info.at(lut->flat_index);
if (ff_data.ff_d != lut_data.lut_f) {
return false;
const NetInfo *src;
// check implcit LUT(ALU) -> FF connection
if (lut || alu) {
if (lut) {
src = fast_cell_info.at(lut->flat_index).lut_f;
} else {
src = fast_cell_info.at(alu->flat_index).alu_sum;
}
}
if (alu) {
const auto &alu_data = fast_cell_info.at(alu->flat_index);
if (ff_data.ff_d != alu_data.alu_sum) {
if (ff_data.ff_d != src) {
return false;
}
}
Expand Down
9 changes: 5 additions & 4 deletions himbaechel/uarch/gowin/gowin_arch_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,11 +817,12 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tde
tt.add_bel_pin(lut, f"I{j}", f"{inp_name}{i}", PinType.INPUT)
tt.add_bel_pin(lut, "F", f"F{i}", PinType.OUTPUT)
if i < 6:
# FF data can come from LUT output, but we pretend that we can use
# any LUT input
tt.create_pip(f"F{i}", f"XD{i}", get_tm_class(db, f"F{i}"))
for inp_name in lut_inputs:
tt.create_pip(f"{inp_name}{i}", f"XD{i}", get_tm_class(db, f"{inp_name}{i}"))
# also experimental input for FF using SEL wire - this theory will
# allow to place unrelated LUT and FF next to each other
# don't create for now
#tt.create_pip(f"SEL{i}", f"XD{i}", get_tm_class(db, f"SEL{i}"))

# FF
ff = tt.create_bel(f"DFF{i}", "DFF", z =(i * 2 + 1))
tt.add_bel_pin(ff, "D", f"XD{i}", PinType.INPUT)
Expand Down
9 changes: 9 additions & 0 deletions himbaechel/uarch/gowin/gowin_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ bool GowinUtils::need_BLKSEL_fix(void)
return extra->chip_flags & Extra_chip_data_POD::NEED_BLKSEL_FIX;
}

IdString GowinUtils::create_aux_name(IdString main_name, int idx, const char *str_suffix)
{
std::string sfx("");
if (idx) {
sfx = std::to_string(idx);
}
return ctx->id(main_name.str(ctx) + std::string(str_suffix) + sfx);
}

std::unique_ptr<CellInfo> GowinUtils::create_cell(IdString name, IdString type)
{
NPNR_ASSERT(!ctx->cells.count(name));
Expand Down
3 changes: 3 additions & 0 deletions himbaechel/uarch/gowin/gowin_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ struct GowinUtils
return is_global_wire(ctx->getPipSrcWire(pip)) || is_global_wire(ctx->getPipDstWire(pip));
}

// construct name
IdString create_aux_name(IdString main_name, int idx = 0, const char *str_suffix = "_aux$");

// make cell but do not include it in the list of chip cells.
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);

Expand Down
Loading

0 comments on commit 7ba83d3

Please sign in to comment.