aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gowin/arch.cc64
-rw-r--r--gowin/arch.h3
-rw-r--r--gowin/cells.cc4
-rw-r--r--gowin/constids.inc12
-rw-r--r--gowin/pack.cc22
-rw-r--r--ice40/pack.cc39
6 files changed, 127 insertions, 17 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 6213124f..2c1e50b7 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -440,7 +440,9 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString
{
const std::string &wirename = wire.str(this);
char buf[32];
- if (wirename == "VCC" || wirename == "GND") {
+ if (wirename == "VCC" || wirename == "VSS") {
+ row = 0;
+ col = 0;
return wire;
}
if (!isdigit(wirename[1]) || !isdigit(wirename[2]) || !isdigit(wirename[3])) {
@@ -949,6 +951,13 @@ Arch::Arch(ArchArgs args) : args(args)
package_name.c_str(this), speed_id.c_str(this));
// setup db
+ // add global VCC and GND bels
+ addBel(id_GND, id_GND, Loc(0, 0, BelZ::gnd_0_z), true);
+ addWire(id_VSS, id_VSS, 0, 0);
+ addBelOutput(id_GND, id_G, id_VSS);
+ addBel(id_VCC, id_VCC, Loc(0, 0, BelZ::vcc_0_z), true);
+ addWire(id_VCC, id_VCC, 0, 0);
+ addBelOutput(id_VCC, id_V, id_VCC);
char buf[32];
// The reverse order of the enumeration simplifies the creation
// of MUX2_LUT8s: they need the existence of the wire on the right.
@@ -1000,6 +1009,44 @@ Arch::Arch(ArchArgs args) : args(args)
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_GSRI, id(buf));
break;
+ case ID_OSC:
+ snprintf(buf, 32, "R%dC%d_OSC", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_OSC, Loc(col, row, 0), false);
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, id_OSCOUT, id(buf));
+ break;
+ case ID_OSCH:
+ snprintf(buf, 32, "R%dC%d_OSCH", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_OSCH, Loc(col, row, 0), false);
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, id_OSCOUT, id(buf));
+ break;
+ case ID_OSCF:
+ snprintf(buf, 32, "R%dC%d_OSCF", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_OSCF, Loc(col, row, 0), false);
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, id_OSCOUT, id(buf));
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCEN)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_OSCEN, id(buf));
+ break;
+ case ID_OSCZ:
+ snprintf(buf, 32, "R%dC%d_OSCZ", row + 1, col + 1);
+ belname = id(buf);
+ addBel(belname, id_OSCZ, Loc(col, row, 0), false);
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, id_OSCOUT, id(buf));
+ portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCEN)->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_OSCEN, id(buf));
+ break;
// fall through the ++
case ID_LUT7:
z++;
@@ -1149,6 +1196,21 @@ Arch::Arch(ArchArgs args) : args(args)
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
addBelInput(belname, id_CLK, id(buf));
+ const PairPOD *xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VSS);
+ if (xxx_port != nullptr) {
+ ddr_has_extra_inputs = true;
+ portname = IdString(xxx_port->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_XXX_VSS, id(buf));
+ }
+ xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VCC);
+ if (xxx_port != nullptr) {
+ ddr_has_extra_inputs = true;
+ portname = IdString(xxx_port->src_id);
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_XXX_VCC, id(buf));
+ }
+
if (oddrc) {
portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CE)->src_id);
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
diff --git a/gowin/arch.h b/gowin/arch.h
index c8392e7e..bc29a59b 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -470,6 +470,9 @@ struct Arch : BaseArch<ArchRanges>
void updateClockSpinesCache(IdString spine_id, IdString wire_id);
void fixClockSpineDecals(void);
+ // XXX GW1N-9C DDR quirk
+ bool ddr_has_extra_inputs = false;
+
// Permissible combinations of modes in a single slice
std::map<const IdString, IdString> dff_comp_mode;
};
diff --git a/gowin/cells.cc b/gowin/cells.cc
index 8e450b51..c3b21782 100644
--- a/gowin/cells.cc
+++ b/gowin/cells.cc
@@ -65,6 +65,10 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
new_cell->addOutput(id_O);
} else if (type == id_GSR) {
new_cell->addInput(id_GSRI);
+ } else if (type == id_GND) {
+ new_cell->addOutput(id_G);
+ } else if (type == id_VCC) {
+ new_cell->addOutput(id_V);
} else {
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
}
diff --git a/gowin/constids.inc b/gowin/constids.inc
index 125fdc74..d2a6b171 100644
--- a/gowin/constids.inc
+++ b/gowin/constids.inc
@@ -681,6 +681,8 @@ X(IOBJS)
// IOLOGIC
X(TX)
+X(XXX_VSS)
+X(XXX_VCC)
X(OBUF_TYPE)
X(SBUF)
X(DBUF)
@@ -756,6 +758,12 @@ X(GSR)
X(GSR0)
X(GSRI)
+// Oscillators
+X(OSC)
+X(OSCZ)
+X(OSCH)
+X(OSCF)
+
// primitive attributes
X(INIT)
X(FF_USED)
@@ -787,6 +795,10 @@ X(SUM)
X(CIN)
X(COUT)
X(OF)
+X(V)
+X(G)
+X(OSCOUT)
+X(OSCEN)
// timing
X(X0)
diff --git a/gowin/pack.cc b/gowin/pack.cc
index 9f0a2478..28370a75 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -611,20 +611,17 @@ static void pack_constants(Context *ctx)
{
log_info("Packing constants..\n");
- std::unique_ptr<CellInfo> gnd_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_GND");
- gnd_cell->params[id_INIT] = Property(0, 1 << 4);
+ std::unique_ptr<CellInfo> gnd_cell = create_generic_cell(ctx, id_GND, "$PACKER_GND");
auto gnd_net = std::make_unique<NetInfo>(ctx->id("$PACKER_GND_NET"));
gnd_net->driver.cell = gnd_cell.get();
- gnd_net->driver.port = id_F;
- gnd_cell->ports.at(id_F).net = gnd_net.get();
+ gnd_net->driver.port = id_G;
+ gnd_cell->ports.at(id_G).net = gnd_net.get();
- std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_VCC");
- // Fill with 1s
- vcc_cell->params[id_INIT] = Property(Property::S1).extract(0, (1 << 4), Property::S1);
+ std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, id_VCC, "$PACKER_VCC");
auto vcc_net = std::make_unique<NetInfo>(ctx->id("$PACKER_VCC_NET"));
vcc_net->driver.cell = vcc_cell.get();
- vcc_net->driver.port = id_F;
- vcc_cell->ports.at(id_F).net = vcc_net.get();
+ vcc_net->driver.port = id_V;
+ vcc_cell->ports.at(id_V).net = vcc_net.get();
std::vector<IdString> dead_nets;
@@ -801,6 +798,13 @@ static void pack_iologic(Context *ctx)
ci->attrs[id_IOBUF] = 1;
}
}
+ // if have XXX_ inputs connect them
+ if (ctx->ddr_has_extra_inputs) {
+ ci->addInput(id_XXX_VSS);
+ ci->connectPort(id_XXX_VSS, ctx->nets[ctx->id("$PACKER_GND_NET")].get());
+ ci->addInput(id_XXX_VCC);
+ ci->connectPort(id_XXX_VCC, ctx->nets[ctx->id("$PACKER_VCC_NET")].get());
+ }
} break;
default:
break;
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 2b5def46..263903b0 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -962,31 +962,56 @@ static void place_plls(Context *ctx)
// Find a BEL for it
BelId found_bel;
+ std::string conflict_str = "";
for (auto bel_pll : pll_all_bels) {
- if (pll_used_bels.count(bel_pll.first))
+ if (pll_used_bels.count(bel_pll.first)) {
+ conflict_str +=
+ stringf(" PLL bel '%s' is already used by '%s'.\n", ctx->nameOfBel(bel_pll.first),
+ pll_used_bels.at(bel_pll.first)->name.c_str(ctx));
continue;
+ }
BelPin pll_io_a, pll_io_b;
BelId gb_a, gb_b;
std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second;
if (bel2io.count(pll_io_a.bel)) {
if (pll_io_a.bel == pad_bel)
could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci);
+ auto conflict_pin = ctx->get_bel_package_pin(pll_io_a.bel);
+ conflict_str +=
+ stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n",
+ ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_a.bel)->name.c_str(ctx),
+ conflict_pin.c_str());
continue;
}
- if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
+ if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci)) {
+ auto conflict_pin = ctx->get_bel_package_pin(pll_io_b.bel);
+ conflict_str +=
+ stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n",
+ ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_b.bel)->name.c_str(ctx),
+ conflict_pin.c_str());
continue;
- if (gb_a_used && bel2gb.count(gb_a))
+ }
+ if (gb_a_used && bel2gb.count(gb_a)) {
+ conflict_str += stringf(
+ " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n",
+ ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_a)->name.c_str(ctx), ctx->nameOfBel(gb_a));
continue;
- if (gb_b_used && bel2gb.count(gb_b))
+ }
+ if (gb_b_used && bel2gb.count(gb_b)) {
+ conflict_str += stringf(
+ " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n",
+ ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_b)->name.c_str(ctx), ctx->nameOfBel(gb_b));
continue;
+ }
found_bel = bel_pll.first;
break;
}
// Apply constrain & Inform user of result
- if (found_bel == BelId())
- log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n", ci->name.c_str(ctx),
- could_be_pad ? " Did you mean to use a PAD PLL ?" : "");
+ if (found_bel == BelId()) {
+ log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n%s\n", ci->name.c_str(ctx),
+ could_be_pad ? " Did you mean to use a PAD PLL ?" : "", conflict_str.c_str());
+ }
log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->nameOfBel(found_bel));
if (could_be_pad)