Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gowin. Add Input Edge Monitor #1396

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions himbaechel/uarch/gowin/constids.inc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ X(W800)
X(W810)
X(W820)
X(W830)
X(MCLK)
X(CLK0)
X(CLK1)
X(CLK2)
Expand Down Expand Up @@ -761,6 +762,16 @@ X(LWO6)
X(LWO7)

// IOLOGIC
X(IEM)
X(WINSIZE)
X(WINSIZE0)
X(WINSIZE1)
X(SMALL)
X(MIDSMALL)
X(MIDLARGE)
X(LARGE)
X(LAG)
X(LEAD)
X(TX)
X(TX0)
X(TX1)
Expand Down Expand Up @@ -797,6 +808,7 @@ X(IDES8)
X(IDES10)
X(IVIDEO)
X(IDES16)
X(IOLOGICI_EMPTY)
X(IOLOGIC)
X(IOLOGICI)
X(IOLOGICO)
Expand Down Expand Up @@ -1289,6 +1301,7 @@ X(RESETN)
//HCLK Parameters
X(DIV_MODE)
X(GSREN)
X(LSREN)

// EMCU
X(EMCU)
3 changes: 2 additions & 1 deletion himbaechel/uarch/gowin/gowin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ void GowinImpl::place_constrained_hclk_cells()
if ((seen_hclk_users.find(ci->name) != seen_hclk_users.end()))
continue;

if (((is_iologici(ci) || is_iologico(ci)) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC))) {
if (((is_iologici(ci) || is_iologico(ci)) &&
!ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC, id_IOLOGICI_EMPTY))) {
NetInfo *hclk_net = ci->getPort(id_FCLK);
if (hclk_net)
continue;
Expand Down
6 changes: 2 additions & 4 deletions himbaechel/uarch/gowin/gowin.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ inline bool is_iologico(const CellInfo *cell) { return type_is_iologico(cell->ty

inline bool type_is_iologici(IdString cell_type)
{
return cell_type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO);
return cell_type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO, id_IOLOGICI_EMPTY);
}
inline bool is_iologici(const CellInfo *cell) { return type_is_iologici(cell->type); }

Expand Down Expand Up @@ -138,9 +138,7 @@ NPNR_PACKED_STRUCT(struct Constraint_POD {
int32_t iostd;
});

NPNR_PACKED_STRUCT(struct Extra_package_data_POD {
RelSlice<Constraint_POD> cst;
});
NPNR_PACKED_STRUCT(struct Extra_package_data_POD { RelSlice<Constraint_POD> cst; });

NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
int32_t chip_flags;
Expand Down
2 changes: 1 addition & 1 deletion himbaechel/uarch/gowin/gowin_arch_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc:
if port == 'FCLK': # XXX compatibility
wire = f'FCLK{name[-1]}'
if not tt.has_wire(wire):
if port in {'CLK', 'PCLK'}:
if port in {'CLK', 'PCLK', 'MCLK'}:
tt.create_wire(wire, "TILE_CLK")
else:
tt.create_wire(wire, "IOL_PORT")
Expand Down
149 changes: 146 additions & 3 deletions himbaechel/uarch/gowin/pack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ struct GowinPacker

void check_iologic_placement(CellInfo &ci, Loc iob_loc, int diff /* 1 - diff */)
{
if (ci.type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC, id_OSER4) || diff) {
if (ci.type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC, id_OSER4, id_IOLOGICI_EMPTY) || diff) {
return;
}
BelId l_bel = ctx->getBelByLocation(Loc(iob_loc.x, iob_loc.y, BelZ::IOBA_Z + 1 - (iob_loc.z - BelZ::IOBA_Z)));
Expand Down Expand Up @@ -517,7 +517,7 @@ struct GowinPacker

CellInfo *create_aux_iologic_cell(CellInfo &ci, IdString mode, bool io16 = false, int idx = 0)
{
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_IDDR, id_IDDRC, id_IDES4)) {
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_IDDR, id_IDDRC, id_IDES4, id_IOLOGICI_EMPTY)) {
return nullptr;
}
IdString aux_name = gwu.create_aux_name(ci.name, idx);
Expand Down Expand Up @@ -597,6 +597,9 @@ struct GowinPacker
ctx->bindBel(l_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
std::string in_mode;
switch (ci.type.hash()) {
case ID_IOLOGICI_EMPTY:
in_mode = "EMPTY";
break;
case ID_IDDR:
case ID_IDDRC:
in_mode = "IDDRX1";
Expand All @@ -616,6 +619,10 @@ struct GowinPacker
}
ci.setParam(ctx->id("INMODE"), in_mode);

if (ci.type == id_IOLOGICI_EMPTY) {
return;
}

// disconnect D input: it is wired internally
nets_to_remove.push_back(ci.getPort(in_port)->name);
in_iob->disconnectPort(id_O);
Expand All @@ -626,6 +633,139 @@ struct GowinPacker
make_iob_nets(*in_iob);
}

void pack_iem()
{
log_info("Pack Input Edge Monitors...\n");
std::vector<IdString> cells_to_remove;
std::vector<std::unique_ptr<CellInfo>> new_cells;

for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (ci.type != id_IEM) {
continue;
}
if (ctx->debug) {
log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
}
// IEM is part of IOLOGIC but functions independently of the
// presence/absence of other IOLOGIC components. Therefore, we use
// the existing cell whenever possible.
const NetInfo *d_net = ci.ports.at(id_D).net;
CellInfo *in_iob = net_driven_by(ctx, d_net, is_iob, id_O);
NPNR_ASSERT(in_iob != nullptr && in_iob->bel != BelId());
BelId iob_bel = in_iob->bel;

BelId l_bel = get_iologici_bel(in_iob);
if (l_bel == BelId()) {
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
}
CellInfo *iologic = nullptr;
for (auto &usr : d_net->users) {
if (is_iologici(usr.cell)) {
if (ctx->debug) {
log_info(" found IOLOGIC cell %s of type %s, use it.\n", ctx->nameOf(usr.cell),
usr.cell->type.c_str(ctx));
}
iologic = usr.cell;
if (iologic->ports.count(id_CLK)) {
NPNR_ASSERT(iologic->ports.at(id_CLK).net == ci.ports.at(id_CLK).net);
} else {
if (iologic->ports.count(id_PCLK)) {
NPNR_ASSERT(iologic->ports.at(id_PCLK).net == ci.ports.at(id_CLK).net);
}
iologic->addInput(ctx->id("CLK"));
}
if (iologic->ports.count(id_RESET)) {
NPNR_ASSERT(iologic->ports.at(id_RESET).net == ci.ports.at(id_RESET).net);
} else {
iologic->addInput(ctx->id("RESET"));
}
break;
}
}
if (iologic == nullptr) {
IdString iologic_name = gwu.create_aux_name(ci.name);
if (ctx->debug) {
log_info(" create IOLOGIC cell %s.\n", iologic_name.c_str(ctx));
}
auto iologic_cell = gwu.create_cell(iologic_name, id_IOLOGICI_EMPTY);
new_cells.push_back(std::move(iologic_cell));
iologic = new_cells.back().get();
ci.copyPortTo(id_D, iologic, id_D);
ci.copyPortTo(id_CLK, iologic, id_CLK);
ci.copyPortTo(id_RESET, iologic, id_RESET);
}
ci.movePortTo(id_MCLK, iologic, id_MCLK);
ci.movePortTo(id_LAG, iologic, id_LAG);
ci.movePortTo(id_LEAD, iologic, id_LEAD);

ci.disconnectPort(id_D);
ci.disconnectPort(id_CLK);
ci.disconnectPort(id_RESET);

// WINSIZE attribute defines routing to ports WINSIZE0/1
iologic->addInput(id_WINSIZE0);
iologic->addInput(id_WINSIZE1);
if (ci.params.count(id_WINSIZE) == 0) {
ci.setParam(id_WINSIZE, Property("SMALL"));
}

NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
IdString winsize = ctx->id(ci.params.at(id_WINSIZE).as_string());
switch (winsize.hash()) {
case ID_SMALL:
iologic->connectPort(id_WINSIZE0, vss_net);
iologic->connectPort(id_WINSIZE1, vss_net);
break;
case ID_MIDSMALL:
iologic->connectPort(id_WINSIZE0, vcc_net);
iologic->connectPort(id_WINSIZE1, vss_net);
break;
case ID_MIDLARGE:
iologic->connectPort(id_WINSIZE0, vss_net);
iologic->connectPort(id_WINSIZE1, vcc_net);
break;
case ID_LARGE:
iologic->connectPort(id_WINSIZE0, vcc_net);
iologic->connectPort(id_WINSIZE1, vcc_net);
break;
default:
log_error("%s has incorrect WINSIZE:%s\n", ctx->nameOf(&ci), ci.params.at(id_WINSIZE).c_str());
}

if (ci.params.count(id_GSREN) != 0) {
if (iologic->params.count(id_GSREN) == 0) {
iologic->setParam(id_GSREN, ci.params.at(id_GSREN));
} else {
if (ci.params.at(id_GSREN) != iologic->params.at(id_GSREN)) {
log_error("GSREN parameter values of %s and %s do not match.\n", ctx->nameOf(&ci),
ctx->nameOf(iologic));
}
}
}
if (ci.params.count(id_LSREN) != 0) {
if (iologic->params.count(id_LSREN) == 0) {
iologic->setParam(id_LSREN, ci.params.at(id_LSREN));
} else {
if (ci.params.at(id_LSREN) != iologic->params.at(id_LSREN)) {
log_error("LSREN parameter values of %s and %s do not match.\n", ctx->nameOf(&ci),
ctx->nameOf(iologic));
}
}
}
cells_to_remove.push_back(ci.name);
}

for (auto cell : cells_to_remove) {
ctx->cells.erase(cell);
}

for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}

void pack_iologic()
{
log_info("Pack IO logic...\n");
Expand All @@ -649,7 +789,7 @@ struct GowinPacker
create_aux_iologic_cell(ci, ctx->id("OUTMODE"));
continue;
}
if (ci.type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO)) {
if (ci.type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO, id_IOLOGICI_EMPTY)) {
pack_ides_iol(ci, nets_to_remove);
create_aux_iologic_cell(ci, ctx->id("INMODE"));
continue;
Expand Down Expand Up @@ -3310,6 +3450,9 @@ struct GowinPacker
pack_diff_iobs();
ctx->check();

pack_iem();
ctx->check();

pack_iologic();
ctx->check();

Expand Down