blob: fc2bc6240481101bc01477324cba9007cc792162 [file]
#include <fstream>
#include <cmath>
#include <vector>
#include <algorithm>
#include "echo_writer.hpp"
#include "tatum/util/tatum_assert.hpp"
#include "tatum/TimingGraph.hpp"
#include "tatum/TimingConstraints.hpp"
#include "tatum/tags/TimingTags.hpp"
#include "tatum/timing_analyzers.hpp"
namespace tatum {
void write_tags(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const NodeId node_id);
void write_slacks(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const EdgeId edge);
void write_slacks(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const NodeId edge);
void write_echo(std::string filename, const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const std::shared_ptr<const TimingAnalyzer> analyzer) {
std::ofstream os(filename);
write_echo(os, tg, tc, dc, analyzer);
}
void write_echo(std::ostream& os, const TimingGraph& tg, const TimingConstraints& tc, const DelayCalculator& dc, const std::shared_ptr<const TimingAnalyzer> analyzer) {
write_timing_graph(os, tg);
write_timing_constraints(os, tc);
write_delay_model(os, tg, dc);
write_analysis_result(os, tg, analyzer);
}
void write_delay_model(std::ostream& os, const TimingGraph& tg, const DelayCalculator& dc) {
os << "delay_model:\n";
for(auto edge_id : tg.edges()) {
NodeId src_node = tg.edge_src_node(edge_id);
NodeId sink_node = tg.edge_sink_node(edge_id);
os << " edge: " << size_t(edge_id);
if(tg.node_type(src_node) == NodeType::CPIN && tg.node_type(sink_node) == NodeType::SINK) {
os << " setup_time: " << dc.setup_time(tg, edge_id).value();
os << " hold_time: " << dc.hold_time(tg, edge_id).value();
} else {
os << " min_delay: " << dc.min_edge_delay(tg, edge_id).value();
os << " max_delay: " << dc.max_edge_delay(tg, edge_id).value();
}
os << "\n";
}
os << "\n";
}
void write_timing_graph(std::ostream& os, const TimingGraph& tg) {
os << "timing_graph:" << "\n";
//We manually iterate to write the nodes in ascending order
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
os << " node: " << size_t(node_id) << " \\\n";
os << " type: " << tg.node_type(node_id) << " \\\n";
os << " in_edges:";
auto in_edges = tg.node_in_edges(node_id);
std::vector<EdgeId> edges(in_edges.begin(), in_edges.end());
std::sort(edges.begin(), edges.end()); //sort the edges for consitent output
for(EdgeId edge_id : edges) {
os << " " << size_t(edge_id) ;
}
os << " \\\n";
os << " out_edges:";
auto out_edges = tg.node_out_edges(node_id);
edges = std::vector<EdgeId>(out_edges.begin(), out_edges.end());
std::sort(edges.begin(), edges.end()); //sort the edges for consitent output
for(EdgeId edge_id : edges) {
os << " " << size_t(edge_id);
}
os << "\n";
}
//We manually iterate to write the edges in ascending order
for(size_t edge_idx = 0; edge_idx < tg.edges().size(); ++edge_idx) {
EdgeId edge_id(edge_idx);
os << " edge: " << size_t(edge_id) << " \\\n";
os << " type: " << tg.edge_type(edge_id) << " \\\n";
os << " src_node: " << size_t(tg.edge_src_node(edge_id)) << " \\\n";
os << " sink_node: " << size_t(tg.edge_sink_node(edge_id)) << " \\\n";
os << " disabled: ";
if(tg.edge_disabled(edge_id)) {
os << "true";
} else {
os << "false";
}
os << "\n";
}
os << "\n";
}
void write_timing_constraints(std::ostream& os, const TimingConstraints& tc) {
os << "timing_constraints:\n";
for(auto domain_id : tc.clock_domains()) {
os << " type: CLOCK domain: " << size_t(domain_id) << " name: \"" << tc.clock_domain_name(domain_id) << "\"\n";
}
for(auto domain_id : tc.clock_domains()) {
NodeId source_node_id = tc.clock_domain_source_node(domain_id);
if(source_node_id) {
os << " type: CLOCK_SOURCE node: " << size_t(source_node_id) << " domain: " << size_t(domain_id) << "\n";
}
}
for(auto node_id : tc.constant_generators()) {
os << " type: CONSTANT_GENERATOR node: " << size_t(node_id) << "\n";
}
for(auto kv : tc.input_constraints(DelayType::MAX)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
auto constraint = kv.second.constraint;
if(constraint.valid()) {
os << " type: MAX_INPUT_CONSTRAINT node: " << size_t(node_id) << " domain: " << size_t(domain_id) << " constraint: " << constraint << "\n";
}
}
for(auto kv : tc.input_constraints(DelayType::MIN)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
auto constraint = kv.second.constraint;
if(constraint.valid()) {
os << " type: MIN_INPUT_CONSTRAINT node: " << size_t(node_id) << " domain: " << size_t(domain_id) << " constraint: " << constraint << "\n";
}
}
for(auto kv : tc.output_constraints(DelayType::MAX)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
auto constraint = kv.second.constraint;
if(constraint.valid()) {
os << " type: MAX_OUTPUT_CONSTRAINT node: " << size_t(node_id) << " domain: " << size_t(domain_id) << " constraint: " << constraint << "\n";
}
}
for(auto kv : tc.output_constraints(DelayType::MIN)) {
auto node_id = kv.first;
auto domain_id = kv.second.domain;
auto constraint = kv.second.constraint;
if(constraint.valid()) {
os << " type: MIN_OUTPUT_CONSTRAINT node: " << size_t(node_id) << " domain: " << size_t(domain_id) << " constraint: " << constraint << "\n";
}
}
for(auto kv : tc.setup_constraints()) {
auto key = kv.first;
auto constraint = kv.second;
if(constraint.valid()) {
os << " type: SETUP_CONSTRAINT";
os << " launch_domain: " << size_t(key.domain_pair.src_domain_id);
os << " capture_domain: " << size_t(key.domain_pair.sink_domain_id);
if (key.capture_node) {
os << " capture_node: " << size_t(key.capture_node);
} else {
os << " capture_node: -1";
}
os << " constraint: " << constraint;
os << "\n";
}
}
for(auto kv : tc.hold_constraints()) {
auto key = kv.first;
auto constraint = kv.second;
if(constraint.valid()) {
os << " type: HOLD_CONSTRAINT";
os << " launch_domain: " << size_t(key.domain_pair.src_domain_id);
os << " capture_domain: " << size_t(key.domain_pair.sink_domain_id);
if (key.capture_node) {
os << " capture_node: " << size_t(key.capture_node);
} else {
os << " capture_node: -1";
}
os << " constraint: " << constraint;
os << "\n";
}
}
for(auto kv : tc.setup_clock_uncertainties()) {
auto key = kv.first;
auto uncertainty = kv.second;
os << " type: SETUP_UNCERTAINTY";
os << " launch_domain: " << size_t(key.src_domain_id);
os << " capture_domain: " << size_t(key.sink_domain_id);
os << " uncertainty: " << uncertainty;
os << "\n";
}
for(auto kv : tc.hold_clock_uncertainties()) {
auto key = kv.first;
auto uncertainty = kv.second;
os << " type: HOLD_UNCERTAINTY";
os << " launch_domain: " << size_t(key.src_domain_id);
os << " capture_domain: " << size_t(key.sink_domain_id);
os << " uncertainty: " << uncertainty;
os << "\n";
}
for(auto kv : tc.source_latencies(ArrivalType::EARLY)) {
auto domain = kv.first;
auto latency = kv.second;
os << " type: EARLY_SOURCE_LATENCY";
os << " domain: " << size_t(domain);
os << " latency: " << latency;
os << "\n";
}
for(auto kv : tc.source_latencies(ArrivalType::LATE)) {
auto domain = kv.first;
auto latency = kv.second;
os << " type: LATE_SOURCE_LATENCY";
os << " domain: " << size_t(domain);
os << " latency: " << latency;
os << "\n";
}
os << "\n";
}
void write_analysis_result(std::ostream& os, const TimingGraph& tg, const std::shared_ptr<const TimingAnalyzer> analyzer) {
os << "analysis_result:\n";
auto setup_analyzer = std::dynamic_pointer_cast<const SetupTimingAnalyzer>(analyzer);
if(setup_analyzer) {
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "SETUP_DATA_ARRIVAL", setup_analyzer->setup_tags(node_id, TagType::DATA_ARRIVAL), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "SETUP_DATA_REQUIRED", setup_analyzer->setup_tags(node_id, TagType::DATA_REQUIRED), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "SETUP_LAUNCH_CLOCK", setup_analyzer->setup_tags(node_id, TagType::CLOCK_LAUNCH), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "SETUP_CAPTURE_CLOCK", setup_analyzer->setup_tags(node_id, TagType::CLOCK_CAPTURE), node_id);
}
for(size_t edge_idx = 0; edge_idx < tg.edges().size(); ++edge_idx) {
EdgeId edge_id(edge_idx);
write_slacks(os, "SETUP_SLACK", setup_analyzer->setup_slacks(edge_id), edge_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_slacks(os, "SETUP_SLACK", setup_analyzer->setup_slacks(node_id), node_id);
}
}
auto hold_analyzer = std::dynamic_pointer_cast<const HoldTimingAnalyzer>(analyzer);
if(hold_analyzer) {
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "HOLD_DATA_ARRIVAL", hold_analyzer->hold_tags(node_id, TagType::DATA_ARRIVAL), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "HOLD_DATA_REQUIRED", hold_analyzer->hold_tags(node_id, TagType::DATA_REQUIRED), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "HOLD_LAUNCH_CLOCK", hold_analyzer->hold_tags(node_id, TagType::CLOCK_LAUNCH), node_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_tags(os, "HOLD_CAPTURE_CLOCK", hold_analyzer->hold_tags(node_id, TagType::CLOCK_CAPTURE), node_id);
}
for(size_t edge_idx = 0; edge_idx < tg.edges().size(); ++edge_idx) {
EdgeId edge_id(edge_idx);
write_slacks(os, "HOLD_SLACK", hold_analyzer->hold_slacks(edge_id), edge_id);
}
for(size_t node_idx = 0; node_idx < tg.nodes().size(); ++node_idx) {
NodeId node_id(node_idx);
write_slacks(os, "HOLD_SLACK", hold_analyzer->hold_slacks(node_id), node_id);
}
}
os << "\n";
}
void write_tags(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const NodeId node_id) {
for(const auto& tag : tags) {
TATUM_ASSERT(tag.type() != TagType::SLACK);
float time = tag.time().value();
if(!std::isnan(time)) {
os << " type: " << type;
os << " node: " << size_t(node_id);
os << " launch_domain: ";
if(tag.launch_clock_domain()) {
os << size_t(tag.launch_clock_domain());
} else {
os << "-1";
}
os << " capture_domain: ";
if(tag.capture_clock_domain()) {
os << size_t(tag.capture_clock_domain());
} else {
os << "-1";
}
os << " time: " << time;
os << "\n";
}
}
}
void write_slacks(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const EdgeId edge) {
for(const auto& tag : tags) {
TATUM_ASSERT(tag.type() == TagType::SLACK);
float time = tag.time().value();
if(!std::isnan(time)) {
os << " type: " << type;
os << " edge: " << size_t(edge);
os << " launch_domain: ";
if(tag.launch_clock_domain()) {
os << size_t(tag.launch_clock_domain());
} else {
os << "-1";
}
os << " capture_domain: ";
if(tag.capture_clock_domain()) {
os << size_t(tag.capture_clock_domain());
} else {
os << "-1";
}
os << " slack: " << time;
os << "\n";
}
}
}
void write_slacks(std::ostream& os, const std::string& type, const TimingTags::tag_range tags, const NodeId node) {
for(const auto& tag : tags) {
TATUM_ASSERT(tag.type() == TagType::SLACK);
float time = tag.time().value();
if(!std::isnan(time)) {
os << " type: " << type;
os << " node: " << size_t(node);
os << " launch_domain: ";
if(tag.launch_clock_domain()) {
os << size_t(tag.launch_clock_domain());
} else {
os << "-1";
}
os << " capture_domain: ";
if(tag.capture_clock_domain()) {
os << size_t(tag.capture_clock_domain());
} else {
os << "-1";
}
os << " slack: " << time;
os << "\n";
}
}
}
}