gowin: improve clock wire routing
The dedicated router for clock wires now understands not only the IO pins but also the rPLL outputs as clock sources. This simple router sets an optimal route, so it is now the default router. It can be disabled with the --disable-globals command line flag if desired, but this is not recommended due to possible clock skew. Still for GW1N-4C there is no good router for clock wires as there external quartz resonator is connected via PLL. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
8424dc79d2
commit
b8ab3116b2
@ -2016,6 +2016,16 @@ static bool is_PLL_T_FB_iob(const Context *ctx, const CellInfo *cell)
|
|||||||
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_FB"));
|
return is_spec_iob(ctx, cell, ctx->id("RPLL_T_FB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Arch::is_GCLKT_iob(const CellInfo *cell)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
if (is_spec_iob(getCtx(), cell, idf("GCLKT_%d", i))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If the PLL input can be connected using a direct wire, then do so,
|
// If the PLL input can be connected using a direct wire, then do so,
|
||||||
// bypassing conventional routing.
|
// bypassing conventional routing.
|
||||||
void Arch::fix_pll_nets(Context *ctx)
|
void Arch::fix_pll_nets(Context *ctx)
|
||||||
|
@ -480,6 +480,7 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
void auto_longwires();
|
void auto_longwires();
|
||||||
void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col);
|
void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col);
|
||||||
void fix_pll_nets(Context *ctx);
|
void fix_pll_nets(Context *ctx);
|
||||||
|
bool is_GCLKT_iob(const CellInfo *cell);
|
||||||
|
|
||||||
GowinGlobalRouter globals_router;
|
GowinGlobalRouter globals_router;
|
||||||
void mark_gowin_globals(Context *ctx);
|
void mark_gowin_globals(Context *ctx);
|
||||||
|
@ -38,22 +38,28 @@ bool GowinGlobalRouter::is_clock_port(PortRef const &user)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<WireId, BelId> GowinGlobalRouter::clock_io(Context *ctx, PortRef const &driver)
|
std::pair<WireId, BelId> GowinGlobalRouter::clock_src(Context *ctx, PortRef const &driver)
|
||||||
{
|
{
|
||||||
// XXX normally all alternative functions of the pins should be passed
|
if (driver.cell == nullptr) {
|
||||||
// in the chip database, but at the moment we find them from aliases/pips
|
|
||||||
// XXX check diff inputs too
|
|
||||||
if (driver.cell == nullptr || driver.cell->type != id_IOB || !driver.cell->attrs.count(id_BEL)) {
|
|
||||||
return std::make_pair(WireId(), BelId());
|
return std::make_pair(WireId(), BelId());
|
||||||
}
|
}
|
||||||
// clock IOs have pips output->SPINExx
|
|
||||||
|
|
||||||
BelInfo &bel = ctx->bel_info(ctx->id(driver.cell->attrs[id_BEL].as_string()));
|
BelInfo &bel = ctx->bel_info(driver.cell->bel);
|
||||||
WireId wire = bel.pins[id_O].wire;
|
WireId wire;
|
||||||
for (auto const pip : ctx->getPipsDownhill(wire)) {
|
if (driver.cell->type == id_IOB) {
|
||||||
if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) {
|
if (ctx->is_GCLKT_iob(driver.cell)) {
|
||||||
|
wire = bel.pins[id_O].wire;
|
||||||
return std::make_pair(wire, bel.name);
|
return std::make_pair(wire, bel.name);
|
||||||
}
|
}
|
||||||
|
return std::make_pair(WireId(), BelId());
|
||||||
|
}
|
||||||
|
if (driver.cell->type == id_RPLLA) {
|
||||||
|
if (driver.port == id_CLKOUT || driver.port == id_CLKOUTP || driver.port == id_CLKOUTD ||
|
||||||
|
driver.port == id_CLKOUTD3) {
|
||||||
|
wire = bel.pins[driver.port].wire;
|
||||||
|
return std::make_pair(wire, bel.name);
|
||||||
|
}
|
||||||
|
return std::make_pair(WireId(), BelId());
|
||||||
}
|
}
|
||||||
return std::make_pair(WireId(), BelId());
|
return std::make_pair(WireId(), BelId());
|
||||||
}
|
}
|
||||||
@ -64,12 +70,12 @@ void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t>
|
|||||||
for (auto const &net : ctx->nets) {
|
for (auto const &net : ctx->nets) {
|
||||||
NetInfo const *ni = net.second.get();
|
NetInfo const *ni = net.second.get();
|
||||||
auto new_clock = clock_nets.end();
|
auto new_clock = clock_nets.end();
|
||||||
auto clock_wire_bel = clock_io(ctx, ni->driver);
|
auto clock_wire_bel = clock_src(ctx, ni->driver);
|
||||||
if (clock_wire_bel.first != WireId()) {
|
if (clock_wire_bel.first != WireId()) {
|
||||||
clock_nets.emplace_back(net.first);
|
clock_nets.emplace_back(net.first);
|
||||||
new_clock = --clock_nets.end();
|
new_clock = --clock_nets.end();
|
||||||
new_clock->clock_io_wire = clock_wire_bel.first;
|
new_clock->clock_wire = clock_wire_bel.first;
|
||||||
new_clock->clock_io_bel = clock_wire_bel.second;
|
new_clock->clock_bel = clock_wire_bel.second;
|
||||||
}
|
}
|
||||||
for (auto const &user : ni->users) {
|
for (auto const &user : ni->users) {
|
||||||
if (is_clock_port(user)) {
|
if (is_clock_port(user)) {
|
||||||
@ -86,8 +92,8 @@ void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t>
|
|||||||
|
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
for (auto const &net : clock_nets) {
|
for (auto const &net : clock_nets) {
|
||||||
log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports,
|
log_info(" Net:%s, ports:%d, clock source:%s\n", net.name.c_str(ctx), net.clock_ports,
|
||||||
net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx));
|
net.clock_wire == WireId() ? "No" : net.clock_wire.c_str(ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,33 +244,35 @@ void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
|
|||||||
}
|
}
|
||||||
used_pips.insert(spine_pip_id);
|
used_pips.insert(spine_pip_id);
|
||||||
|
|
||||||
// >>> SPINExx <- IO
|
// >>> SPINExx <- Src
|
||||||
dstWire = ctx->getPipSrcWire(spine_pip_id);
|
dstWire = ctx->getPipSrcWire(spine_pip_id);
|
||||||
dstWireInfo = ctx->wire_info(dstWire);
|
dstWireInfo = ctx->wire_info(dstWire);
|
||||||
PipId io_pip_id = PipId();
|
PipId src_pip_id = PipId();
|
||||||
for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
|
for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
|
||||||
if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) {
|
if (ctx->getPipSrcWire(uphill_pip) == net.clock_wire) {
|
||||||
io_pip_id = uphill_pip;
|
src_pip_id = uphill_pip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NPNR_ASSERT(io_pip_id != PipId());
|
NPNR_ASSERT(src_pip_id != PipId());
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx));
|
log_info(" Src Pip:%s\n", src_pip_id.c_str(ctx));
|
||||||
}
|
}
|
||||||
// if already routed
|
// if already routed
|
||||||
if (used_pips.count(io_pip_id)) {
|
if (used_pips.count(src_pip_id)) {
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
log_info(" ^routed already^\n");
|
log_info(" ^routed already^\n");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
used_pips.insert(io_pip_id);
|
used_pips.insert(src_pip_id);
|
||||||
}
|
}
|
||||||
log_info(" Net %s is routed.\n", net.name.c_str(ctx));
|
log_info(" Net %s is routed.\n", net.name.c_str(ctx));
|
||||||
for (auto const pip : used_pips) {
|
if (!ctx->net_info(net.name).users.empty()) {
|
||||||
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
for (auto const pip : used_pips) {
|
||||||
|
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||||
|
}
|
||||||
|
ctx->bindWire(net.clock_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GowinGlobalRouter::route_globals(Context *ctx)
|
void GowinGlobalRouter::route_globals(Context *ctx)
|
||||||
@ -289,16 +297,21 @@ void GowinGlobalRouter::mark_globals(Context *ctx)
|
|||||||
int max_clock = 3, cur_clock = -1;
|
int max_clock = 3, cur_clock = -1;
|
||||||
for (auto &net : clock_nets) {
|
for (auto &net : clock_nets) {
|
||||||
// XXX only IO clock for now
|
// XXX only IO clock for now
|
||||||
if (net.clock_io_wire == WireId()) {
|
if (net.clock_wire == WireId()) {
|
||||||
log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx));
|
log_info(" Non clock source, skip %s.\n", net.name.c_str(ctx));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (++cur_clock >= max_clock) {
|
if (++cur_clock >= max_clock) {
|
||||||
log_info(" No more clock wires left, skip the remaining nets.\n");
|
log_info(" No more clock wires left, skip the remaining nets.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
net.clock = cur_clock;
|
if (ctx->net_info(net.name).users.empty()) {
|
||||||
BelInfo &bi = ctx->bel_info(net.clock_io_bel);
|
--cur_clock;
|
||||||
|
net.clock = -1;
|
||||||
|
} else {
|
||||||
|
net.clock = cur_clock;
|
||||||
|
}
|
||||||
|
BelInfo &bi = ctx->bel_info(net.clock_bel);
|
||||||
bi.gb = true;
|
bi.gb = true;
|
||||||
nets.emplace_back(net);
|
nets.emplace_back(net);
|
||||||
}
|
}
|
||||||
|
@ -39,32 +39,32 @@ class GowinGlobalRouter
|
|||||||
{
|
{
|
||||||
IdString name;
|
IdString name;
|
||||||
int clock_ports;
|
int clock_ports;
|
||||||
BelId clock_io_bel;
|
BelId clock_bel;
|
||||||
WireId clock_io_wire; // IO wire if there is one
|
WireId clock_wire; // clock wire if there is one
|
||||||
int clock; // clock #
|
int clock; // clock #
|
||||||
|
|
||||||
globalnet_t()
|
globalnet_t()
|
||||||
{
|
{
|
||||||
name = IdString();
|
name = IdString();
|
||||||
clock_ports = 0;
|
clock_ports = 0;
|
||||||
clock_io_bel = BelId();
|
clock_bel = BelId();
|
||||||
clock_io_wire = WireId();
|
clock_wire = WireId();
|
||||||
clock = -1;
|
clock = -1;
|
||||||
}
|
}
|
||||||
globalnet_t(IdString _name)
|
globalnet_t(IdString _name)
|
||||||
{
|
{
|
||||||
name = _name;
|
name = _name;
|
||||||
clock_ports = 0;
|
clock_ports = 0;
|
||||||
clock_io_bel = BelId();
|
clock_bel = BelId();
|
||||||
clock_io_wire = WireId();
|
clock_wire = WireId();
|
||||||
clock = -1;
|
clock = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort
|
// sort
|
||||||
bool operator<(const globalnet_t &other) const
|
bool operator<(const globalnet_t &other) const
|
||||||
{
|
{
|
||||||
if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) {
|
if ((clock_wire != WireId()) ^ (other.clock_wire != WireId())) {
|
||||||
return !(clock_io_wire != WireId());
|
return !(clock_wire != WireId());
|
||||||
}
|
}
|
||||||
return clock_ports < other.clock_ports;
|
return clock_ports < other.clock_ports;
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ class GowinGlobalRouter
|
|||||||
std::vector<globalnet_t> nets;
|
std::vector<globalnet_t> nets;
|
||||||
|
|
||||||
bool is_clock_port(PortRef const &user);
|
bool is_clock_port(PortRef const &user);
|
||||||
std::pair<WireId, BelId> clock_io(Context *ctx, PortRef const &driver);
|
std::pair<WireId, BelId> clock_src(Context *ctx, PortRef const &driver);
|
||||||
void gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets);
|
void gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets);
|
||||||
IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool<IdString> &used_pips,
|
IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool<IdString> &used_pips,
|
||||||
pool<IdString> &undo_wires);
|
pool<IdString> &undo_wires);
|
||||||
|
@ -51,7 +51,8 @@ po::options_description GowinCommandHandler::getArchOptions()
|
|||||||
specific.add_options()("device", po::value<std::string>(), "device name");
|
specific.add_options()("device", po::value<std::string>(), "device name");
|
||||||
specific.add_options()("family", po::value<std::string>(), "family name");
|
specific.add_options()("family", po::value<std::string>(), "family name");
|
||||||
specific.add_options()("cst", po::value<std::string>(), "physical constraints file");
|
specific.add_options()("cst", po::value<std::string>(), "physical constraints file");
|
||||||
specific.add_options()("enable-globals", "separate routing of the clocks");
|
specific.add_options()("enable-globals", "enable separate routing of the clocks");
|
||||||
|
specific.add_options()("disable-globals", "disable separate routing of the clocks");
|
||||||
specific.add_options()("enable-auto-longwires", "automatic detection and routing of long wires");
|
specific.add_options()("enable-auto-longwires", "automatic detection and routing of long wires");
|
||||||
return specific;
|
return specific;
|
||||||
}
|
}
|
||||||
@ -84,11 +85,10 @@ std::unique_ptr<Context> GowinCommandHandler::createContext(dict<std::string, Pr
|
|||||||
|
|
||||||
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||||
// routing options
|
// routing options
|
||||||
// the default values will change in the future
|
ctx->settings[ctx->id("arch.enable-globals")] = 1;
|
||||||
ctx->settings[ctx->id("arch.enable-globals")] = 0;
|
|
||||||
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0;
|
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0;
|
||||||
if (vm.count("enable-globals")) {
|
if (vm.count("disable-globals")) {
|
||||||
ctx->settings[ctx->id("arch.enable-globals")] = 1;
|
ctx->settings[ctx->id("arch.enable-globals")] = 0;
|
||||||
}
|
}
|
||||||
if (vm.count("enable-auto-longwires")) {
|
if (vm.count("enable-auto-longwires")) {
|
||||||
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 1;
|
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user