From 0cfb6eed910091584a1bbd5cc5a35c6634e1c55e Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 15 Jan 2016 18:25:02 +0100 Subject: icetime progress --- icetime/icetime.cc | 372 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 253 insertions(+), 119 deletions(-) (limited to 'icetime/icetime.cc') 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> config_tile_type; @@ -460,28 +461,43 @@ void read_chipdb() std::pair(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(net, other)].first, - connection_pos[std::pair(net, other)].second, other); - for (auto other : net_rbuffers[net]) - printf("// RBUFFER %d %d %d\n", connection_pos[std::pair(net, other)].first, - connection_pos[std::pair(net, other)].second, other); - for (auto other : net_routing[net]) - printf("// ROUTE %d %d %d\n", connection_pos[std::pair(net, other)].first, - connection_pos[std::pair(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(net, other)].first, + connection_pos[std::pair(net, other)].second, other); + for (auto other : net_rbuffers[net]) + printf("// RBUFFER %d %d %d\n", connection_pos[std::pair(net, other)].first, + connection_pos[std::pair(net, other)].second, other); + for (auto other : net_routing[net]) + printf("// ROUTE %d %d %d\n", connection_pos[std::pair(net, other)].first, + connection_pos[std::pair(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 &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 &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 &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[] = { , } std::map> net_driver; + // net_max_setup[] = { , , } + std::map> net_max_setup; + // net_max_path_parent[] = { , , , , } std::map> 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 lines; std::set 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 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 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 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 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 \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 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> multibit_ports; + fprintf(fout, "%s (", it.first.c_str()); + std::map> 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; } -- cgit v1.2.3