Compare commits
9 Commits
master
...
timing_viz
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0c891bf90c | ||
![]() |
b01f012e86 | ||
![]() |
f1a735cf3e | ||
![]() |
02985b014a | ||
![]() |
5afa31d031 | ||
![]() |
70e37f6d27 | ||
![]() |
e615627766 | ||
![]() |
5f2bf8d6df | ||
![]() |
4657fab2d5 |
142
common/timing.cc
142
common/timing.cc
@ -27,6 +27,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -636,6 +637,147 @@ struct Timing
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->debug) {
|
||||||
|
log_info("Writing timing.dot\n");
|
||||||
|
std::ofstream f("timing.dot");
|
||||||
|
f << "digraph T {" << std::endl;
|
||||||
|
f << "\tlabel=<<font point-size=\"32\">clk_period=" << clk_period << "</font>"
|
||||||
|
<< "<font point-size=\"24\"><i>"
|
||||||
|
<< "<br/><b>Nodes</b> represent ports, clustered by their cells, and annotated with max arrival times."
|
||||||
|
<< "<br/><b>Filled nodes</b> represent timing start points (i.e. flip-flops)."
|
||||||
|
<< "<br/><b>Solid edges</b> represent inter-cell delays (i.e. nets) and are annotated with net name at its center, as well as net delay/budget at its head."
|
||||||
|
<< "<br/><b>Dotted edges</b> represent intra-cell delays and are annotated with this value."
|
||||||
|
<< "</i></font>>;"
|
||||||
|
<< std::endl;
|
||||||
|
f << "\tlabelloc=t;" << std::endl;
|
||||||
|
// Use the new ranking algorithm in dot to allow ranking of nodes across clusters
|
||||||
|
f << "\tnewrank=true;" << std::endl;
|
||||||
|
|
||||||
|
// For each net, draw an edge from driver -> user and label middle of edge with net name, and head of edge with delay/budget
|
||||||
|
for (auto &net : ctx->nets) {
|
||||||
|
// For rendering speed, ignore global nets (e.g. clocks)
|
||||||
|
if (ctx->isGlobalNet(net.second.get()))
|
||||||
|
continue;
|
||||||
|
for (auto &usr : net.second->users) {
|
||||||
|
f << "\t\"" << net.second->driver.cell->name.str(ctx) << "." << net.second->driver.port.str(ctx) << "\" -> \"" << usr.cell->name.str(ctx) << "." << usr.port.str(ctx) << "\" [label=\"" << net.second->name.c_str(ctx) << "\"; headlabel=\"" << ctx->getNetinfoRouteDelay(net.second.get(), usr) << "/" << usr.budget << "\"];" << std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of IOBs, so they can be placed at top/bottom of graph
|
||||||
|
std::vector<std::string> startpoints, endpoints;
|
||||||
|
// Temporary vector for each cell
|
||||||
|
std::vector<IdString> input_ports, output_ports;
|
||||||
|
|
||||||
|
// Place all ports of cell in a subgraph/cluster with label
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
f << "\tsubgraph \"cluster_" << cell.second->name.str(ctx) << "\" {" << std::endl;
|
||||||
|
f << "\t\tlabel = \"" << cell.second->name.str(ctx) << "\";" << std::endl;
|
||||||
|
|
||||||
|
// Collect all input/output ports
|
||||||
|
input_ports.clear();
|
||||||
|
output_ports.clear();
|
||||||
|
for (auto &port : cell.second->ports) {
|
||||||
|
if (!port.second.net) continue;
|
||||||
|
int port_clocks;
|
||||||
|
auto portClass = ctx->getPortTimingClass(cell.second.get(), port.first, port_clocks);
|
||||||
|
// For rendering speed, ignore clock inputs (since global nets are not drawn)
|
||||||
|
if (portClass == TMG_CLOCK_INPUT) continue;
|
||||||
|
if (port.second.type == PORT_IN) {
|
||||||
|
input_ports.push_back(port.first);
|
||||||
|
// IOB inputs are endpoints
|
||||||
|
if (ctx->getBelIOB(cell.second->bel))
|
||||||
|
endpoints.emplace_back(cell.second->name.str(ctx) + "." + port.first.str(ctx));
|
||||||
|
// Label port
|
||||||
|
f << "\t\t" << "\"" << cell.second->name.str(ctx) << "." << port.first.str(ctx) << "\" [label = \"" << port.first.str(ctx);
|
||||||
|
// Recover the PortRef from its net
|
||||||
|
for (const auto &usr : port.second.net->users) {
|
||||||
|
if (usr.cell != cell.second.get() || usr.port != port.first)
|
||||||
|
continue;
|
||||||
|
// And for each clock event, label node with event as well as max arrival time
|
||||||
|
auto it = net_data.find(port.second.net);
|
||||||
|
if (it != net_data.end()) {
|
||||||
|
for (const auto &i : it->second)
|
||||||
|
f << "\\n" << (i.first.edge == RISING_EDGE ? "posedge" : "negedge") << " " << i.first.clock.str(ctx) << " @ " << i.second.max_arrival + ctx->getNetinfoRouteDelay(port.second.net, usr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
f << "\"]" << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output_ports.push_back(port.first);
|
||||||
|
// Label port
|
||||||
|
f << "\t\t" << "\"" << cell.second->name.str(ctx) << "." << port.first.str(ctx) << "\" [label = \"" << port.first.str(ctx);
|
||||||
|
// And for each clock event, label node with event as well as max arrival time
|
||||||
|
auto it = net_data.find(port.second.net);
|
||||||
|
if (it != net_data.end()) {
|
||||||
|
for (const auto &i : it->second)
|
||||||
|
f << "\\n" << (i.first.edge == RISING_EDGE ? "posedge" : "negedge") << " " << i.first.clock.str(ctx) << " @ " << i.second.max_arrival;
|
||||||
|
}
|
||||||
|
f << "\"";
|
||||||
|
// IOB outputs are startpoints
|
||||||
|
if (ctx->getBelIOB(cell.second->bel))
|
||||||
|
startpoints.emplace_back(cell.second->name.str(ctx) + "." + port.first.str(ctx));
|
||||||
|
else if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_STARTPOINT) {
|
||||||
|
// Otherwise, draw other timing points differently
|
||||||
|
f << "; shape=parallelogram; style=filled";
|
||||||
|
}
|
||||||
|
f << "];" << std::endl;
|
||||||
|
}
|
||||||
|
// Place port inside subgraph
|
||||||
|
f << "\t\t" << "\"" << cell.second->name.str(ctx) << "." << port.first.str(ctx) << "\";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark all input ports of cells with minimum rank, to appear above all output ports
|
||||||
|
f << "\t\t{rank=min";
|
||||||
|
for (auto i : input_ports)
|
||||||
|
f << "; \"" << cell.second->name.str(ctx) << "." << i.str(ctx) << "\"";
|
||||||
|
f << ";}" << std::endl;
|
||||||
|
f << "\t\t{rank=max";
|
||||||
|
for (auto o : output_ports)
|
||||||
|
f << "; \"" << cell.second->name.str(ctx) << "." << o.str(ctx) << "\"";
|
||||||
|
f << ";}" << std::endl;
|
||||||
|
|
||||||
|
// Now enumerate all input -> output ports
|
||||||
|
for (auto i : input_ports) {
|
||||||
|
for (auto o : output_ports) {
|
||||||
|
// Look for combinatorial delay paths
|
||||||
|
DelayInfo comb_delay;
|
||||||
|
bool is_path = ctx->getCellDelay(cell.second.get(), i, o, comb_delay);
|
||||||
|
if (is_path) {
|
||||||
|
// And add an edge to reflect that, annotated with its delay
|
||||||
|
f << "\t\t" << "\"" << cell.second->name.str(ctx) << "." << i.str(ctx) << "\" -> \"" << cell.second->name.str(ctx) << "." << o.str(ctx) << "\" [style=dotted; label=" << comb_delay.maxDelay() << "];" << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, look for setup time
|
||||||
|
int port_clocks;
|
||||||
|
auto portClass = ctx->getPortTimingClass(cell.second.get(), i, port_clocks);
|
||||||
|
if (portClass == TMG_REGISTER_INPUT) {
|
||||||
|
for (int j = 0; j < port_clocks; j++) {
|
||||||
|
TimingClockingInfo clkInfo = ctx->getPortClockingInfo(cell.second.get(), i, j);
|
||||||
|
f << "\t\t" << "\"" << cell.second->name.str(ctx) << "." << i.str(ctx) << "\" -> \"" << cell.second->name.str(ctx) << "." << o.str(ctx) << "\" [style=dotted; label=" << clkInfo.setup.maxDelay() << "];" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f << "\t}" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now force all start points to have minimum rank (i.e. appear at top of graph), and end points to have maximum rank
|
||||||
|
f << "\t{rank=min";
|
||||||
|
for (auto i : startpoints)
|
||||||
|
f << "; \"" << i << "\"";
|
||||||
|
f << ";}" << std::endl;
|
||||||
|
f << "\t{rank=max";
|
||||||
|
for (auto o : endpoints)
|
||||||
|
f << "; \"" << o << "\"";
|
||||||
|
f << ";}" << std::endl;
|
||||||
|
|
||||||
|
f << "}" << std::endl;
|
||||||
|
}
|
||||||
return min_slack;
|
return min_slack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +115,10 @@ Return a list of all bels at the give X/Y location.
|
|||||||
|
|
||||||
Returns true if the given bel is a global buffer. A global buffer does not "pull in" other cells it drives to be close to the location of the global buffer.
|
Returns true if the given bel is a global buffer. A global buffer does not "pull in" other cells it drives to be close to the location of the global buffer.
|
||||||
|
|
||||||
|
### bool getBelIOB(BelId bel) const
|
||||||
|
|
||||||
|
Returns true if the given bel is a IO block.
|
||||||
|
|
||||||
### uint32\_t getBelChecksum(BelId bel) const
|
### uint32\_t getBelChecksum(BelId bel) const
|
||||||
|
|
||||||
Return a (preferably unique) number that represents this bel. This is used in design state checksum calculations.
|
Return a (preferably unique) number that represents this bel. This is used in design state checksum calculations.
|
||||||
@ -429,7 +433,7 @@ Run the placer.
|
|||||||
|
|
||||||
### bool route()
|
### bool route()
|
||||||
|
|
||||||
run the router.
|
Run the router.
|
||||||
|
|
||||||
Graphics Methods
|
Graphics Methods
|
||||||
----------------
|
----------------
|
||||||
@ -477,6 +481,13 @@ Return the _clocking info_ (including port name of clock, clock polarity and set
|
|||||||
port. Where ports have more than one clock edge associated with them (such as DDR outputs), `index` can be used to obtain
|
port. Where ports have more than one clock edge associated with them (such as DDR outputs), `index` can be used to obtain
|
||||||
information for all edges. `index` must be in [0, clockInfoCount), behaviour is undefined otherwise.
|
information for all edges. `index` must be in [0, clockInfoCount), behaviour is undefined otherwise.
|
||||||
|
|
||||||
|
Net Methods
|
||||||
|
------------------
|
||||||
|
|
||||||
|
### bool isGlobalNet(const NetInfo *net) const
|
||||||
|
|
||||||
|
Returns true if the given net is driven by a global buffer.
|
||||||
|
|
||||||
Placer Methods
|
Placer Methods
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@ -488,5 +499,5 @@ a certain number of different clock signals allowed for a group of bels.
|
|||||||
|
|
||||||
### bool isBelLocationValid(BelId bel) const
|
### bool isBelLocationValid(BelId bel) const
|
||||||
|
|
||||||
Returns true if a bell in the current configuration is valid, i.e. if
|
Returns true if a bel in the current configuration is valid, i.e. if
|
||||||
`isValidBelForCell()` would return true for the current mapping.
|
`isValidBelForCell()` would return true for the current mapping.
|
||||||
|
@ -768,6 +768,13 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Arch::isGlobalNet(const NetInfo *net) const
|
||||||
|
{
|
||||||
|
if (net == nullptr)
|
||||||
|
return false;
|
||||||
|
return net->driver.cell != nullptr && net->driver.port == id_CLKO;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> Arch::getTilesAtLocation(int row, int col)
|
std::vector<std::pair<std::string, std::string>> Arch::getTilesAtLocation(int row, int col)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, std::string>> ret;
|
std::vector<std::pair<std::string, std::string>> ret;
|
||||||
|
@ -539,6 +539,7 @@ struct Arch : BaseCtx
|
|||||||
BelRange getBelsByTile(int x, int y) const;
|
BelRange getBelsByTile(int x, int y) const;
|
||||||
|
|
||||||
bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_DCCA; }
|
bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_DCCA; }
|
||||||
|
bool getBelIOB(BelId bel) const { return getBelType(bel) == ID_TRELLIS_IO; }
|
||||||
|
|
||||||
bool checkBelAvail(BelId bel) const
|
bool checkBelAvail(BelId bel) const
|
||||||
{
|
{
|
||||||
@ -954,7 +955,7 @@ struct Arch : BaseCtx
|
|||||||
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
||||||
// Get the TimingClockingInfo of a port
|
// Get the TimingClockingInfo of a port
|
||||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
||||||
// Return true if a port is a net
|
// Return true if net is driven from global buffer
|
||||||
bool isGlobalNet(const NetInfo *net) const;
|
bool isGlobalNet(const NetInfo *net) const;
|
||||||
|
|
||||||
bool getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const;
|
bool getDelayFromTimingDatabase(IdString tctype, IdString from, IdString to, DelayInfo &delay) const;
|
||||||
|
@ -224,6 +224,7 @@ BelId Arch::getBelByLocation(Loc loc) const
|
|||||||
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); }
|
const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); }
|
||||||
|
|
||||||
bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; }
|
bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; }
|
||||||
|
bool Arch::getBelIOB(BelId bel) const { return /* TODO */ false; }
|
||||||
|
|
||||||
uint32_t Arch::getBelChecksum(BelId bel) const
|
uint32_t Arch::getBelChecksum(BelId bel) const
|
||||||
{
|
{
|
||||||
@ -473,6 +474,11 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
NPNR_ASSERT_FALSE("no clocking info for generic");
|
NPNR_ASSERT_FALSE("no clocking info for generic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Arch::isGlobalNet(const NetInfo *net) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; }
|
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; }
|
||||||
bool Arch::isBelLocationValid(BelId bel) const { return true; }
|
bool Arch::isBelLocationValid(BelId bel) const { return true; }
|
||||||
|
|
||||||
|
@ -150,6 +150,7 @@ struct Arch : BaseCtx
|
|||||||
BelId getBelByLocation(Loc loc) const;
|
BelId getBelByLocation(Loc loc) const;
|
||||||
const std::vector<BelId> &getBelsByTile(int x, int y) const;
|
const std::vector<BelId> &getBelsByTile(int x, int y) const;
|
||||||
bool getBelGlobalBuf(BelId bel) const;
|
bool getBelGlobalBuf(BelId bel) const;
|
||||||
|
bool getBelIOB(BelId bel) const;
|
||||||
uint32_t getBelChecksum(BelId bel) const;
|
uint32_t getBelChecksum(BelId bel) const;
|
||||||
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength);
|
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength);
|
||||||
void unbindBel(BelId bel);
|
void unbindBel(BelId bel);
|
||||||
@ -237,6 +238,8 @@ struct Arch : BaseCtx
|
|||||||
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
||||||
// Get the TimingClockingInfo of a port
|
// Get the TimingClockingInfo of a port
|
||||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
||||||
|
// Return true if net is driven from global buffer
|
||||||
|
bool isGlobalNet(const NetInfo *net) const;
|
||||||
|
|
||||||
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
|
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
|
||||||
bool isBelLocationValid(BelId bel) const;
|
bool isBelLocationValid(BelId bel) const;
|
||||||
|
@ -513,6 +513,7 @@ struct Arch : BaseCtx
|
|||||||
BelRange getBelsByTile(int x, int y) const;
|
BelRange getBelsByTile(int x, int y) const;
|
||||||
|
|
||||||
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == ID_SB_GB; }
|
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == ID_SB_GB; }
|
||||||
|
bool getBelIOB(BelId bel) const { return chip_info->bel_data[bel.index].type == ID_SB_IO; }
|
||||||
|
|
||||||
IdString getBelType(BelId bel) const
|
IdString getBelType(BelId bel) const
|
||||||
{
|
{
|
||||||
@ -850,7 +851,7 @@ struct Arch : BaseCtx
|
|||||||
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
||||||
// Get the TimingClockingInfo of a port
|
// Get the TimingClockingInfo of a port
|
||||||
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
||||||
// Return true if a port is a net
|
// Return true if net is driven from global buffer
|
||||||
bool isGlobalNet(const NetInfo *net) const;
|
bool isGlobalNet(const NetInfo *net) const;
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user