icetime progress

This commit is contained in:
Clifford Wolf 2016-01-15 18:25:02 +01:00
parent d939d1b92f
commit 0cfb6eed91
6 changed files with 319 additions and 128 deletions

View File

@ -6,7 +6,7 @@ all:
$(MAKE) -C iceprog
$(MAKE) -C icemulti
$(MAKE) -C icepll
# $(MAKE) -C icetime
$(MAKE) -C icetime
clean:
$(MAKE) -C icebox clean
@ -14,7 +14,7 @@ clean:
$(MAKE) -C iceprog clean
$(MAKE) -C icemulti clean
$(MAKE) -C icepll clean
# $(MAKE) -C icetime clean
$(MAKE) -C icetime clean
install:
$(MAKE) -C icebox install
@ -22,7 +22,7 @@ install:
$(MAKE) -C iceprog install
$(MAKE) -C icemulti install
$(MAKE) -C icepll install
# $(MAKE) -C icetime install
$(MAKE) -C icetime install
uninstall:
$(MAKE) -C icebox uninstall
@ -30,7 +30,7 @@ uninstall:
$(MAKE) -C iceprog uninstall
$(MAKE) -C icemulti uninstall
$(MAKE) -C icepll uninstall
# $(MAKE) -C icetime uninstall
$(MAKE) -C icetime uninstall
.PHONY: all clean install uninstall

1
icetime/.gitignore vendored
View File

@ -1,4 +1,5 @@
icetime
timings.inc
test[0-9]*
*.d
*.o

View File

@ -6,6 +6,12 @@ all: icetime
icetime: icetime.o
icetime.o: icetime.cc timings.inc
timings.inc: timings.py
python3 timings.py > timings.inc.new
mv timings.inc.new timings.inc
install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp icetime $(DESTDIR)$(PREFIX)/bin/icetime
@ -24,7 +30,7 @@ test0 test1 test2 test3 test4 test5 test6 test7 test8 test9: icetime
yosys $@.ys
run0 run1 run2 run3 run4 run5 run6 run7 run8 run9: icetime
./icetime -P tq144 -p $(subst run,test,$@).pcf $(subst run,test,$@).asc $(subst run,test,$@)_out.v
./icetime -t -P tq144 -p $(subst run,test,$@).pcf $(subst run,test,$@).asc $(subst run,test,$@)_out.v
show0 show1 show2 show3 show4 show5 show6 show7 show8 show9: icetime
bash show.sh $(subst show,test,$@)
@ -35,7 +41,7 @@ test: test0 test1 test2 test3 test4 test5 test6 test7 test8 test9
show: show0 show1 show2 show3 show4 show5 show6 show7 show8 show9
clean:
rm -f icetime *.o *.d
rm -f icetime timings.inc *.o *.d
rm -rf test[0-9]*
-include *.d

View File

@ -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");
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());
fprintf(fout, " wire gnd, vcc;\n");
fprintf(fout, " GND gnd_cell (.Y(gnd));\n");
fprintf(fout, " VCC vcc_cell (.Y(vcc));\n");
for (auto &str : extra_vlog)
fprintf(fout, "%s", str.c_str());
for (auto it : netlist_cell_types)
if (fout != NULL)
{
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, "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 (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());
fprintf(fout, " wire gnd, vcc;\n");
fprintf(fout, " GND gnd_cell (.Y(gnd));\n");
fprintf(fout, " VCC vcc_cell (.Y(vcc));\n");
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, "%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;
}
fprintf(fout, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
sep = ",";
}
fprintf(fout, "\n ) ");
sep = "";
}
fprintf(fout, "%s (", it.first.c_str());
std::map<std::string, std::vector<std::string>> multibit_ports;
for (auto it : multibit_ports)
{
fprintf(fout, "%s\n .%s({", sep, it.first.c_str());
sep = ",";
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;
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, "%s\n .%s(%s)", sep, port.first.c_str(), port.second.c_str());
sep = ",";
fprintf(fout, "\n );\n");
}
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 = ", ";
}
fprintf(fout, "})");
}
fprintf(fout, "\n );\n");
fprintf(fout, "endmodule\n");
}
fprintf(fout, "endmodule\n");
if (print_timing || !print_timing_nets.empty())
{
printf("\n");
printf("icetime topological timing analysis report\n");
printf("==========================================\n");
printf("\n");
TimingAnalysis ta;
ta.report();
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;
}

View File

@ -148,6 +148,7 @@ with open("%s.ys" % sys.argv[1], "w") as f:
print("equiv_status -assert", file=f)
assert os.system("bash ../icefuzz/icecube.sh %s.v" % sys.argv[1]) == 0
os.rename("%s.tmp/outputs/netlist/top_timing.rpt" % sys.argv[1], "%s.rpt" % sys.argv[1])
os.rename("%s.v" % sys.argv[1], "%s_in.v" % sys.argv[1])
if False:

49
icetime/timings.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import re
print("// auto-generated by timings.py from ../icefuzz/timings_*.txt")
def timings_to_c(chip, f):
print("")
print("double get_delay_%s(std::string cell_type, std::string in_port, std::string out_port)" % chip)
print("{")
in_cell = False
for line in f:
fields = line.split()
if len(fields) == 0:
continue
if fields[0] == "CELL":
if in_cell:
print(" }")
print(" if (cell_type == \"%s\") {" % fields[1])
in_cell = True
if fields[0] == "SETUP":
inport = fields[1].split(":")[1]
delay = max([0 if s == "*" else float(s) / 1000 for s in fields[3].split(":")])
print(" if (in_port == \"%s\" && out_port == \"*setup*\") return %.5f;" % (inport, delay))
if fields[0] == "IOPATH":
if fields[1].startswith("posedge:") or fields[1].startswith("negedge:"):
fields[1] = "*clkedge*"
delay = max([0 if s == "*" else float(s) / 1000 for s in fields[3].split(":") + fields[4].split(":")])
print(" if (in_port == \"%s\" && out_port == \"%s\") return %.5f;" % (fields[1], fields[2], delay))
if in_cell:
print(" }")
print(" if (in_port == \"*clkedge*\"|| out_port == \"*setup*\") return 0;")
print(" fprintf(stderr, \"Unable to resolve delay for path %s -> %s in cell type %s!\\n\", in_port.c_str(), out_port.c_str(), cell_type.c_str());")
print(" exit(1);")
print("}")
with open("../icefuzz/timings_1k.txt", "r") as f:
timings_to_c("1k", f);
with open("../icefuzz/timings_8k.txt", "r") as f:
timings_to_c("8k", f);