Skip to content

Commit

Permalink
Himbaechel Gowin: Add support for CLKDIV and CLKDIV2
Browse files Browse the repository at this point in the history
  • Loading branch information
Seyviour committed Jul 11, 2024
1 parent b90cecd commit bdd69b7
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 59 deletions.
2 changes: 1 addition & 1 deletion himbaechel/uarch/gowin/cst.cc
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ struct GowinCstReader
} break;
case hclk: {
IdString cell_type = it->second->type;
if (cell_type != IdString(ID_CLKDIV)) {
if (cell_type != id_CLKDIV) {
log_error("Unsupported or invalid cell type %s for hclk\n", cell_type.c_str(ctx));
}
BelId hclk_bel = getConstrainedHCLKBel(match, ctx->getGridDimX(), ctx->getGridDimY());
Expand Down
5 changes: 3 additions & 2 deletions himbaechel/uarch/gowin/gowin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,11 @@ void GowinImpl::place_constrained_hclk_cells()

if (((is_iologici(ci) || is_iologico(ci)) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC))) {
NetInfo *hclk_net = ci->getPort(id_FCLK);
if (hclk_net == nullptr)
if (hclk_net)
continue;
CellInfo *hclk_driver = hclk_net->driver.cell;

if (!hclk_driver)
continue;
if (chip.str(ctx) == "GW1N-9C" && hclk_driver->type != id_CLKDIV2) {
// CLKDIV doesn't seem to connect directly to FCLK on this device, and routing is guaranteed to succeed.
continue;
Expand Down
74 changes: 18 additions & 56 deletions himbaechel/uarch/gowin/pack.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "design_utils.h"
#include "log.h"
#include "nextpnr.h"
#include "util.h"

#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc"
#include "himbaechel_constids.h"
Expand Down Expand Up @@ -1325,7 +1324,7 @@ struct GowinPacker
{
int num = (bit_width == 9 || bit_width == 18 || bit_width == 36) ? 36 : 32;
for (int i = 0, j = offset; i < num; ++i, ++j) {
if (((j + 1) % 9) == 0 && (bit_width == 16 || bit_width == 32)) {
if (((i + 1) % 9) == 0 && (bit_width == 16 || bit_width == 32)) {
++j;
}
ci->renamePort(ctx->idf(from, i), ctx->idf(to, offset ? j % 36 : j));
Expand Down Expand Up @@ -1391,15 +1390,6 @@ struct GowinPacker
}
++idx;
}
switch (idx) {
case 1:
init |= init << 2; /* fallthrough */
case 2:
init |= init << 4;
break;
default:
break;
}
lut->setParam(id_INIT, init);

new_cells.push_back(std::move(lut_cell));
Expand All @@ -1416,7 +1406,7 @@ struct GowinPacker
if (bit_width == 32 || bit_width == 36) {
return;
}
int read_mode = int_or_default(ci->params, id_READ_MODE, 0);
int read_mode = ci->params.at(id_READ_MODE).as_int64();
if (read_mode == 0) {
return;
}
Expand Down Expand Up @@ -1521,7 +1511,7 @@ struct GowinPacker
CellInfo *new_ce_net_src = ce_pre_dff;

// add delay register in pipeline mode
int read_mode = int_or_default(ci->params, id_READ_MODE, 0);
int read_mode = ci->params.at(id_READ_MODE).as_int64();
if (read_mode) {
auto ce_pipe_dff_cell = gwu.create_cell(create_aux_name(ci->name, 0, "_ce_pipe_dff$"), id_DFF);
new_cells.push_back(std::move(ce_pipe_dff_cell));
Expand Down Expand Up @@ -1597,7 +1587,10 @@ struct GowinPacker
ci->connectPort(port, vcc_net);
}

ci->addInput(id_WRE);
ci->connectPort(id_WRE, vss_net);
ci->addInput(id_WREB);
ci->connectPort(id_WREB, vss_net);

if (!ci->params.count(id_BIT_WIDTH)) {
ci->setParam(id_BIT_WIDTH, Property(default_bw, 32));
Expand All @@ -1609,19 +1602,10 @@ struct GowinPacker
ci->copyPortTo(id_CE, ci, id_CEB);
ci->copyPortTo(id_OCE, ci, id_OCEB);
ci->copyPortTo(id_RESET, ci, id_RESETB);
ci->connectPort(id_WREB, vcc_net);

// disconnect lower address bits for ROM
std::array rom_ignore_bits = {2, 4, 8, 16, 32};
std::array romx9_ignore_bits = {9, 9, 9, 18, 36};
for (unsigned int i = 0; i < 14; ++i) {
if (i < size(rom_ignore_bits) && ((ci->type == id_pROM && bit_width >= rom_ignore_bits[i]) ||
(ci->type == id_pROMX9 && bit_width >= romx9_ignore_bits[i]))) {
ci->disconnectPort(ctx->idf("AD[%d]", i));
} else {
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("ADA%d", i));
ci->copyPortTo(ctx->idf("ADA%d", i), ci, ctx->idf("ADB%d", i));
}
for (int i = 0; i < 14; ++i) {
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("ADA%d", i));
ci->copyPortTo(ctx->idf("ADA%d", i), ci, ctx->idf("ADB%d", i));
}
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
} else {
Expand All @@ -1630,18 +1614,9 @@ struct GowinPacker
ci->renamePort(id_OCE, id_OCEB);
ci->renamePort(id_CE, id_CEB);
ci->renamePort(id_RESET, id_RESETB);
ci->connectPort(id_WREB, vss_net);

ci->addInput(id_CE);
ci->connectPort(id_CE, vcc_net);
ci->disconnectPort(id_OCEB);

int read_mode = int_or_default(ci->params, id_READ_MODE, 0);
if (read_mode) {
ci->connectPort(id_OCEB, vcc_net);
} else {
ci->copyPortTo(id_CEB, ci, id_OCEB);
}
ci->addInput(id_CEA);
ci->connectPort(id_CEA, vss_net);
for (int i = 0; i < 14; ++i) {
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("ADB%d", i));
}
Expand Down Expand Up @@ -1823,21 +1798,6 @@ struct GowinPacker
bsram_fix_blksel(ci, new_cells);
}

// The statement in the Gowin documentation that in the reading mode
// "READ_MODE=0" the output register is not used and the OCE signal is
// ignored is not confirmed by practice - if the OCE was left
// unconnected or connected to the constant network, then a change in
// output data was observed even with CE=0, as well as the absence of
// such at CE=1.
// Synchronizing CE and OCE helps but it's definitely a hack.
NetInfo *oce_net = ci->getPort(id_OCE);
if (oce_net == nullptr || oce_net->name == ctx->id("$PACKER_VCC") || oce_net->name == ctx->id("$PACKER_GND")) {
if (oce_net != nullptr) {
ci->disconnectPort(id_OCE);
}
ci->copyPortTo(id_CE, ci, id_OCE);
}

// XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide:
// For GW1N-9/GW1NR-9/GW1NS-4 series, 32/36-bit SP/SPX9 is divided into two
// SP/SPX9s, which occupy two BSRAMs.
Expand Down Expand Up @@ -2990,15 +2950,17 @@ struct GowinPacker

for (auto &cell : ctx->cells) {
auto ci = cell.second.get();
if (ci->type == id_CLKDIV) {
NetInfo *in = ci->getPort(id_HCLKIN);
CellInfo *this_driver = in->driver.cell;
if (this_driver->type == id_CLKDIV2) {
if (ci->type != id_CLKDIV)
continue;
NetInfo *hclk_in = ci->getPort(id_HCLKIN);
if (hclk_in) {
CellInfo *this_driver = hclk_in->driver.cell;
if (this_driver && this_driver->type == id_CLKDIV2) {
NetInfo *out = this_driver->getPort(id_CLKOUT);
if (out->users.entries() > 1) {
// We could do as the IDE does sometimes and replicate the CLKDIV2 cell
// as many times as we need. For now, we keep things simple
log_error("CLKDIV2 that drives CLKDIV should drive no other cells");
log_error("CLKDIV2 that drives CLKDIV should drive no other cells\n");
}
ci->cluster = ci->name;
this_driver->cluster = ci->name;
Expand Down

0 comments on commit bdd69b7

Please sign in to comment.