aboutsummaryrefslogtreecommitdiffstats
path: root/icetime/icetime.cc
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2016-01-15 18:25:02 +0100
committerClifford Wolf <clifford@clifford.at>2016-01-15 18:25:02 +0100
commit0cfb6eed910091584a1bbd5cc5a35c6634e1c55e (patch)
tree9112042a77c3977fba84d492821969847616ba2d /icetime/icetime.cc
parentd939d1b92f6e1a46aa8a573d3f7d19cc8e342950 (diff)
downloadicestorm-0cfb6eed910091584a1bbd5cc5a35c6634e1c55e.tar.gz
icestorm-0cfb6eed910091584a1bbd5cc5a35c6634e1c55e.tar.bz2
icestorm-0cfb6eed910091584a1bbd5cc5a35c6634e1c55e.zip
icetime progress
Diffstat (limited to 'icetime/icetime.cc')
-rw-r--r--icetime/icetime.cc372
1 files changed, 253 insertions, 119 deletions
diff --git a/icetime/icetime.cc b/icetime/icetime.cc
index 09a4ca4..ad11831 100644
--- a/icetime/icetime.cc
+++ b/icetime/icetime.cc
@@ -31,6 +31,7 @@
#define MAX_SPAN_HACK 1
FILE *fin, *fout;
+bool verbose = false;
std::string config_device, selected_package;
std::vector<std::vector<std::string>> config_tile_type;
@@ -460,28 +461,43 @@ void read_chipdb()
std::pair<int, int>(x, y);
}
-
-#if 1
- for (int net : used_nets)
+ if (verbose)
{
- printf("// NET %d:\n", net);
- for (auto seg : net_to_segments[net])
- printf("// SEG %d %d %s\n", seg.x, seg.y, seg.name.c_str());
- for (auto other : net_buffers[net])
- printf("// BUFFER %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
- connection_pos[std::pair<int, int>(net, other)].second, other);
- for (auto other : net_rbuffers[net])
- printf("// RBUFFER %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
- connection_pos[std::pair<int, int>(net, other)].second, other);
- for (auto other : net_routing[net])
- printf("// ROUTE %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
- connection_pos[std::pair<int, int>(net, other)].second, other);
+ for (int net : used_nets)
+ {
+ printf("// NET %d:\n", net);
+ for (auto seg : net_to_segments[net])
+ printf("// SEG %d %d %s\n", seg.x, seg.y, seg.name.c_str());
+ for (auto other : net_buffers[net])
+ printf("// BUFFER %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
+ connection_pos[std::pair<int, int>(net, other)].second, other);
+ for (auto other : net_rbuffers[net])
+ printf("// RBUFFER %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
+ connection_pos[std::pair<int, int>(net, other)].second, other);
+ for (auto other : net_routing[net])
+ printf("// ROUTE %d %d %d\n", connection_pos[std::pair<int, int>(net, other)].first,
+ connection_pos[std::pair<int, int>(net, other)].second, other);
+ }
}
-#endif
}
bool is_primary(std::string cell_name, std::string out_port)
{
+ auto cell_type = netlist_cell_types[cell_name];
+
+ if (cell_type == "SB_RAM40_4K")
+ return true;
+
+ if (cell_type == "LogicCell40" && out_port == "lcout")
+ {
+ // SEQ_MODE = "4'bX...";
+ bool dff_enable = netlist_cell_params[cell_name]["SEQ_MODE"][3] == '1';
+ return dff_enable;
+ }
+
+ if (cell_type == "PRE_IO")
+ return true;
+
return false;
}
@@ -556,7 +572,7 @@ const std::set<std::string> &get_inports(std::string cell_type)
inports_map["ICE_CARRY_IN_MUX"] = { "carryinitin" };
inports_map["LogicCell40"] = { "clk", "carryin", "in0", "in1", "in2", "in3", "sr", "ce" };
- inports_map["PRE_IO"] = { "INPUTCLK", "OUTPUTCLK", "LATCHINPUTVALUE", "CLOCKENABLE", "OUTPUTENABLE", "DOUT1", "DOUT0" };
+ inports_map["PRE_IO"] = { "INPUTCLK", "OUTPUTCLK", "LATCHINPUTVALUE", "CLOCKENABLE", "OUTPUTENABLE", "DOUT1", "DOUT0", "PADIN" };
inports_map["SB_RAM40_4K"] = { "RCLK", "RCLKE", "RE", "WCLK", "WCLKE", "WE" };
@@ -569,6 +585,8 @@ const std::set<std::string> &get_inports(std::string cell_type)
inports_map["SB_RAM40_4K"].insert(stringf("RADDR[%d]", i));
inports_map["SB_RAM40_4K"].insert(stringf("WADDR[%d]", i));
}
+
+ inports_map["INTERCONN"] = { "I" };
}
if (inports_map.count(cell_type) == 0) {
@@ -579,9 +597,21 @@ const std::set<std::string> &get_inports(std::string cell_type)
return inports_map.at(cell_type);
}
+#include "timings.inc"
+
double get_delay(std::string cell_type, std::string in_port, std::string out_port)
{
- return 1.0;
+ if (cell_type == "INTERCONN")
+ return 0;
+
+ if (config_device == "1k")
+ return get_delay_1k(cell_type, in_port, out_port);
+
+ if (config_device == "8k")
+ return get_delay_8k(cell_type, in_port, out_port);
+
+ fprintf(stderr, "No built-in timing database for '%s' devices!\n", config_device.c_str());
+ exit(1);
}
struct TimingAnalysis
@@ -589,6 +619,9 @@ struct TimingAnalysis
// net_driver[<net_name>] = { <cell_name>, <cell_port> }
std::map<std::string, std::pair<std::string, std::string>> net_driver;
+ // net_max_setup[<net_name>] = { <setup_time>, <cell_name>, <cell_port> }
+ std::map<std::string, std::tuple<double, std::string, std::string>> net_max_setup;
+
// net_max_path_parent[<net_name>] = { <parent_net>, <cell_name>, <inport>, <outport>, <delay> }
std::map<std::string, std::tuple<std::string, std::string, std::string, std::string, double>> net_max_path_parent;
@@ -609,20 +642,28 @@ struct TimingAnalysis
auto &driver_cell = net_driver.at(net).first;
auto &driver_port = net_driver.at(net).second;
+ auto &driver_type = netlist_cell_types.at(driver_cell);
if (is_primary(driver_cell, driver_port)) {
- net_max_path_delay[net] = 0;
+ net_max_path_delay[net] = get_delay(driver_type, "*clkedge*", driver_port);
return 0;
}
- auto &driver_type = netlist_cell_types.at(driver_cell);
- auto &driver_inputs = get_inports(driver_type);
-
- for (auto &inport : driver_inputs)
+ for (auto &inport : get_inports(driver_type))
{
- if (inport == "clk" || inport == "INPUTCLK" || inport == "OUTPUTCLK")
+ if (inport == "clk" || inport == "INPUTCLK" || inport == "OUTPUTCLK" || inport == "PADIN")
continue;
+ if (driver_type == "LogicCell40" && driver_port == "carryout") {
+ if (inport == "in0" || inport == "in3")
+ continue;
+ }
+
+ if (driver_type == "LogicCell40" && (driver_port == "ltout" || driver_port == "lcout")) {
+ if (inport == "carryin")
+ continue;
+ }
+
std::string *in_net = &netlist_cell_ports.at(driver_cell).at(inport);
while (net_assignments.count(*in_net))
in_net = &net_assignments.at(*in_net);
@@ -657,8 +698,20 @@ struct TimingAnalysis
if (net_name == "")
continue;
- if (get_inports(netlist_cell_types.at(cell_name)).count(port_name))
+ auto &cell_type = netlist_cell_types.at(cell_name);
+
+ if (get_inports(cell_type).count(port_name)) {
+ std::string n = net_name;
+ while (1) {
+ double setup_time = get_delay(cell_type, port_name, "*setup*");
+ if (setup_time >= std::get<0>(net_max_setup[n]))
+ net_max_setup[n] = std::make_tuple(setup_time, cell_name, port_name);
+ if (net_assignments.count(n) == 0)
+ break;
+ n = net_assignments.at(n);
+ }
continue;
+ }
net_driver[net_name] = { cell_name, port_name };
all_nets.insert(net_name);
@@ -667,7 +720,7 @@ struct TimingAnalysis
global_max_path_delay = 0;
for (auto &net : all_nets) {
- double d = calc_net_max_path_delay(net);
+ double d = calc_net_max_path_delay(net) + std::get<0>(net_max_setup[net]);
if (d > global_max_path_delay) {
global_max_path_delay = d;
global_max_path_net = net;
@@ -675,18 +728,47 @@ struct TimingAnalysis
}
}
- void report()
+ void report(std::string n = std::string())
{
std::vector<std::string> lines;
std::set<std::string> visited_nets;
- std::string n = global_max_path_net;
+ if (n.empty()) {
+ n = global_max_path_net;
+ printf("Report for longest path:\n\n");
+ } else {
+ printf("Requested report for net %s:\n\n", n.c_str());
+ }
+
+ if (net_max_path_delay.count(n) == 0) {
+ printf("Net not found: %s\n", n.c_str());
+ return;
+ }
+
+ double delay = net_max_path_delay.at(n);
+
+ int logic_levels = 0;
bool last_line = true;
+ auto &user = net_max_setup[n];
+
+ if (!std::get<1>(user).empty()) {
+ lines.push_back(stringf(" %s (%s) %s [setup]: %.3f ns", std::get<1>(user).c_str(),
+ netlist_cell_types.at(std::get<1>(user)).c_str(), std::get<2>(user).c_str(), std::get<0>(user)));
+ delay += std::get<0>(user);
+ }
+
while (1)
{
- if (net_max_path_parent.count(n) == 0) {
+ if (net_max_path_parent.count(n) == 0)
+ {
lines.push_back(stringf("%10.3f ns %s", calc_net_max_path_delay(n), n.c_str()));
+
+ auto &driver_cell = net_driver.at(n).first;
+ auto &driver_port = net_driver.at(n).second;
+ auto &driver_type = netlist_cell_types.at(driver_cell);
+ lines.push_back(stringf(" %s (%s) [clkedge] -> %s: %.3f ns", driver_cell.c_str(),
+ driver_type.c_str(), driver_port.c_str(), calc_net_max_path_delay(n)));
break;
}
@@ -697,8 +779,10 @@ struct TimingAnalysis
auto &entry = net_max_path_parent.at(n);
- if (last_line || netlist_cell_types.at(std::get<1>(entry)) == "LogicCell40")
+ if (last_line || netlist_cell_types.at(std::get<1>(entry)) == "LogicCell40") {
lines.push_back(stringf("%10.3f ns %s", calc_net_max_path_delay(n), n.c_str()));
+ logic_levels++;
+ }
lines.push_back(stringf(" %s (%s) %s -> %s: %.3f ns", std::get<1>(entry).c_str(),
netlist_cell_types.at(std::get<1>(entry)).c_str(), std::get<2>(entry).c_str(),
@@ -711,6 +795,11 @@ struct TimingAnalysis
for (int i = int(lines.size())-1; i >= 0; i--)
printf("%s\n", lines[i].c_str());
+
+ printf("\n");
+ printf("Total number of logic levels: %d\n", logic_levels);
+ printf("Total path delay: %.2f ns (%.2f MHz)\n", delay, 1000.0 / delay);
+ printf("\n");
}
};
@@ -1517,27 +1606,28 @@ void make_interconn(const net_segment_t &src, FILE *graph_f)
worker.build_net_tree(src.net);
worker.build_seg_tree(src);
-#if 1
- printf("// INTERCONN %d %d %s %d\n", src.x, src.y, src.name.c_str(), src.net);
- std::function<void(int,int)> print_net_tree = [&] (int net, int indent) {
- printf("// %*sNET_TREE %d\n", indent, "", net);
- for (int child : worker.net_tree.at(net))
- print_net_tree(child, indent+2);
- };
- std::function<void(const net_segment_t&,int,bool)> print_seg_tree = [&] (const net_segment_t &seg, int indent, bool chain) {
- printf("// %*sSEG_TREE %d %d %s %d\n", indent, chain ? "`" : "", seg.x, seg.y, seg.name.c_str(), seg.net);
- if (worker.seg_tree.count(seg)) {
- auto &children = worker.seg_tree.at(seg);
- bool child_chain = children.size() == 1;
- for (auto &child : children)
- print_seg_tree(child, child_chain ? (chain ? indent : indent+1) : indent+2, child_chain);
- } else {
- printf("// %*s DEAD_END (!)\n", indent, "");
- }
- };
- print_net_tree(src.net, 2);
- print_seg_tree(src, 2, false);
-#endif
+ if (verbose)
+ {
+ printf("// INTERCONN %d %d %s %d\n", src.x, src.y, src.name.c_str(), src.net);
+ std::function<void(int,int)> print_net_tree = [&] (int net, int indent) {
+ printf("// %*sNET_TREE %d\n", indent, "", net);
+ for (int child : worker.net_tree.at(net))
+ print_net_tree(child, indent+2);
+ };
+ std::function<void(const net_segment_t&,int,bool)> print_seg_tree = [&] (const net_segment_t &seg, int indent, bool chain) {
+ printf("// %*sSEG_TREE %d %d %s %d\n", indent, chain ? "`" : "", seg.x, seg.y, seg.name.c_str(), seg.net);
+ if (worker.seg_tree.count(seg)) {
+ auto &children = worker.seg_tree.at(seg);
+ bool child_chain = children.size() == 1;
+ for (auto &child : children)
+ print_seg_tree(child, child_chain ? (chain ? indent : indent+1) : indent+2, child_chain);
+ } else {
+ printf("// %*s DEAD_END (!)\n", indent, "");
+ }
+ };
+ print_net_tree(src.net, 2);
+ print_seg_tree(src, 2, false);
+ }
for (auto &seg : worker.target_segs) {
net_assignments[net_name(seg.net)] = seg_name(seg);
@@ -1564,13 +1654,26 @@ void help(const char *cmd)
printf(" write a graphviz description of the interconnect tree\n");
printf(" that includes the given net to 'icetime_graph.dot'.\n");
printf("\n");
+ printf(" -t\n");
+ printf(" print a timing estimate (based on topological timing\n");
+ printf(" analysis)\n");
+ printf("\n");
+ printf(" -T <net_name>\n");
+ printf(" print a timing estimate for the specified net\n");
+ printf("\n");
+ printf(" -v\n");
+ printf(" verbose mode (print all interconnect trees)\n");
+ printf("\n");
exit(1);
}
int main(int argc, char **argv)
{
+ bool print_timing = false;
+ std::vector<std::string> print_timing_nets;
+
int opt;
- while ((opt = getopt(argc, argv, "p:P:g:")) != -1)
+ while ((opt = getopt(argc, argv, "p:P:g:tT:v")) != -1)
{
switch (opt)
{
@@ -1584,6 +1687,15 @@ int main(int argc, char **argv)
case 'g':
graph_nets.insert(atoi(optarg));
break;
+ case 't':
+ print_timing = true;
+ break;
+ case 'T':
+ print_timing_nets.push_back(optarg);
+ break;
+ case 'v':
+ verbose = true;
+ break;
default:
help(argv[0]);
}
@@ -1595,7 +1707,7 @@ int main(int argc, char **argv)
perror("Can't open input file");
exit(1);
}
- fout = stdout;
+ fout = nullptr;
} else
if (optind+2 == argc) {
fin = fopen(argv[optind], "r");
@@ -1603,10 +1715,14 @@ int main(int argc, char **argv)
perror("Can't open input file");
exit(1);
}
- fout = fopen(argv[optind+1], "w");
- if (fout == nullptr) {
- perror("Can't open output file");
- exit(1);
+ if (std::string(argv[optind+1]) == "-") {
+ fout = stdout;
+ } else {
+ fout = fopen(argv[optind+1], "w");
+ if (fout == nullptr) {
+ perror("Can't open output file");
+ exit(1);
+ }
}
} else
help(argv[0]);
@@ -1731,84 +1847,102 @@ int main(int argc, char **argv)
extra_wires.insert(port.second);
}
- fprintf(fout, "module chip (");
- const char *io_sep = "";
- for (auto io : io_names) {
- fprintf(fout, "%s%s", io_sep, io.c_str());
- io_sep = ", ";
- }
- fprintf(fout, ");\n");
+ if (fout != NULL)
+ {
+ fprintf(fout, "module chip (");
+ const char *io_sep = "";
+ for (auto io : io_names) {
+ fprintf(fout, "%s%s", io_sep, io.c_str());
+ io_sep = ", ";
+ }
+ fprintf(fout, ");\n");
- for (int net : declared_nets)
- fprintf(fout, " wire net_%d;\n", net);
+ for (int net : declared_nets)
+ fprintf(fout, " wire net_%d;\n", net);
- for (auto net : extra_wires)
- fprintf(fout, " wire %s;\n", net.c_str());
-
- for (auto &it : net_assignments)
- fprintf(fout, " assign %s = %s;\n", it.first.c_str(), it.second.c_str());
+ for (auto net : extra_wires)
+ fprintf(fout, " wire %s;\n", net.c_str());
- fprintf(fout, " wire gnd, vcc;\n");
- fprintf(fout, " GND gnd_cell (.Y(gnd));\n");
- fprintf(fout, " VCC vcc_cell (.Y(vcc));\n");
+ for (auto &it : net_assignments)
+ fprintf(fout, " assign %s = %s;\n", it.first.c_str(), it.second.c_str());
- for (auto &str : extra_vlog)
- fprintf(fout, "%s", str.c_str());
+ fprintf(fout, " wire gnd, vcc;\n");
+ fprintf(fout, " GND gnd_cell (.Y(gnd));\n");
+ fprintf(fout, " VCC vcc_cell (.Y(vcc));\n");
- for (auto it : netlist_cell_types)
- {
- const char *sep = "";
- fprintf(fout, " %s ", it.second.c_str());
- if (netlist_cell_params.count(it.first)) {
- fprintf(fout, "#(");
- for (auto port : netlist_cell_params[it.first]) {
- fprintf(fout, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
- sep = ",";
+ for (auto &str : extra_vlog)
+ fprintf(fout, "%s", str.c_str());
+
+ for (auto it : netlist_cell_types)
+ {
+ const char *sep = "";
+ fprintf(fout, " %s ", it.second.c_str());
+ if (netlist_cell_params.count(it.first)) {
+ fprintf(fout, "#(");
+ for (auto port : netlist_cell_params[it.first]) {
+ fprintf(fout, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
+ sep = ",";
+ }
+ fprintf(fout, "\n ) ");
+ sep = "";
}
- fprintf(fout, "\n ) ");
- sep = "";
- }
- fprintf(fout, "%s (", it.first.c_str());
- std::map<std::string, std::vector<std::string>> multibit_ports;
+ fprintf(fout, "%s (", it.first.c_str());
+ std::map<std::string, std::vector<std::string>> multibit_ports;
- for (auto port : netlist_cell_ports[it.first])
- {
- size_t open_bracket_pos = port.first.find('[');
- if (open_bracket_pos != std::string::npos) {
- std::string base_name = port.first.substr(0, open_bracket_pos);
- int bit_index = atoi(port.first.substr(open_bracket_pos+1).c_str());
- if (multibit_ports[base_name].size() <= bit_index)
- multibit_ports[base_name].resize(bit_index+1);
- multibit_ports[base_name][bit_index] = port.second;
- continue;
+ for (auto port : netlist_cell_ports[it.first])
+ {
+ size_t open_bracket_pos = port.first.find('[');
+ if (open_bracket_pos != std::string::npos) {
+ std::string base_name = port.first.substr(0, open_bracket_pos);
+ int bit_index = atoi(port.first.substr(open_bracket_pos+1).c_str());
+ if (multibit_ports[base_name].size() <= bit_index)
+ multibit_ports[base_name].resize(bit_index+1);
+ multibit_ports[base_name][bit_index] = port.second;
+ continue;
+ }
+
+ fprintf(fout, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
+ sep = ",";
}
- fprintf(fout, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
- sep = ",";
- }
+ for (auto it : multibit_ports)
+ {
+ fprintf(fout, "%s\n .%s({", sep, it.first.c_str());
+ sep = ",";
- for (auto it : multibit_ports)
- {
- fprintf(fout, "%s\n .%s({", sep, it.first.c_str());
- sep = ",";
-
- const char *sepsep = "";
- for (int i = int(it.second.size())-1; i >= 0; i--) {
- std::string wire_name = it.second[i];
- fprintf(fout, "%s%s", sepsep, wire_name.c_str());
- sepsep = ", ";
+ const char *sepsep = "";
+ for (int i = int(it.second.size())-1; i >= 0; i--) {
+ std::string wire_name = it.second[i];
+ fprintf(fout, "%s%s", sepsep, wire_name.c_str());
+ sepsep = ", ";
+ }
+ fprintf(fout, "})");
}
- fprintf(fout, "})");
+
+ fprintf(fout, "\n );\n");
}
- fprintf(fout, "\n );\n");
+ fprintf(fout, "endmodule\n");
}
- fprintf(fout, "endmodule\n");
-
- TimingAnalysis ta;
- ta.report();
+ if (print_timing || !print_timing_nets.empty())
+ {
+ printf("\n");
+ printf("icetime topological timing analysis report\n");
+ printf("==========================================\n");
+ printf("\n");
+
+ TimingAnalysis ta;
+ for (auto &n : print_timing_nets)
+ ta.report(n);
+ ta.report();
+ }
+ else
+ {
+ TimingAnalysis ta;
+ printf("// Timing estimate: %.2f ns (%.2f MHz)\n", ta.global_max_path_delay, 1000.0 / ta.global_max_path_delay);
+ }
return 0;
}