Merge pull request #1071 from yrabbit/to-float
gowin: bugfix and improved clock router
This commit is contained in:
commit
eaf2bc8bdd
@ -698,17 +698,28 @@ template <class T, class C> const T *genericLookup(const T *first, int len, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T, class C> const T *timingLookup(const T *first, int len, const T val, C compare)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
auto res = &first[i];
|
||||||
|
if (!(compare(*res, val) || compare(val, *res))) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
DelayQuad delayLookup(const TimingPOD *first, int len, IdString name)
|
DelayQuad delayLookup(const TimingPOD *first, int len, IdString name)
|
||||||
{
|
{
|
||||||
TimingPOD needle;
|
TimingPOD needle;
|
||||||
needle.name_id = name.index;
|
needle.name_id = name.index;
|
||||||
const TimingPOD *timing = genericLookup(first, len, needle, timingCompare);
|
const TimingPOD *timing = timingLookup(first, len, needle, timingCompare);
|
||||||
DelayQuad delay;
|
DelayQuad delay;
|
||||||
if (timing != nullptr) {
|
if (timing != nullptr) {
|
||||||
delay.fall.max_delay = std::max(timing->ff, timing->rf) / 1000;
|
delay.fall.max_delay = std::max(timing->ff, timing->rf) / 1000.;
|
||||||
delay.fall.min_delay = std::min(timing->ff, timing->rf) / 1000;
|
delay.fall.min_delay = std::min(timing->ff, timing->rf) / 1000.;
|
||||||
delay.rise.max_delay = std::max(timing->rr, timing->fr) / 1000;
|
delay.rise.max_delay = std::max(timing->rr, timing->fr) / 1000.;
|
||||||
delay.rise.min_delay = std::min(timing->rr, timing->fr) / 1000;
|
delay.rise.min_delay = std::min(timing->rr, timing->fr) / 1000.;
|
||||||
} else {
|
} else {
|
||||||
delay = DelayQuad(0);
|
delay = DelayQuad(0);
|
||||||
}
|
}
|
||||||
@ -2005,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));
|
||||||
|
if (!ctx->net_info(net.name).users.empty()) {
|
||||||
for (auto const pip : used_pips) {
|
for (auto const pip : used_pips) {
|
||||||
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
ctx->bindWire(net.clock_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;
|
||||||
}
|
}
|
||||||
|
if (ctx->net_info(net.name).users.empty()) {
|
||||||
|
--cur_clock;
|
||||||
|
net.clock = -1;
|
||||||
|
} else {
|
||||||
net.clock = cur_clock;
|
net.clock = cur_clock;
|
||||||
BelInfo &bi = ctx->bel_info(net.clock_io_bel);
|
}
|
||||||
|
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")] = 0;
|
|
||||||
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0;
|
|
||||||
if (vm.count("enable-globals")) {
|
|
||||||
ctx->settings[ctx->id("arch.enable-globals")] = 1;
|
ctx->settings[ctx->id("arch.enable-globals")] = 1;
|
||||||
|
ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0;
|
||||||
|
if (vm.count("disable-globals")) {
|
||||||
|
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