yowasp-yosys 0.38.0.92.post687.dev0__py3-none-any.whl → 0.39.0.165.post702.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yowasp_yosys/sby.py +50 -0
- yowasp_yosys/share/include/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +107 -37
- yowasp_yosys/share/include/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h +107 -21
- yowasp_yosys/share/include/kernel/fmt.h +17 -8
- yowasp_yosys/share/include/kernel/utils.h +3 -3
- yowasp_yosys/share/python3/sby_autotune.py +1 -0
- yowasp_yosys/share/python3/sby_cmdline.py +5 -0
- yowasp_yosys/share/python3/sby_core.py +55 -5
- yowasp_yosys/share/python3/sby_design.py +7 -0
- yowasp_yosys/share/python3/sby_engine_abc.py +156 -14
- yowasp_yosys/share/python3/sby_engine_aiger.py +100 -62
- yowasp_yosys/share/python3/sby_engine_smtbmc.py +9 -3
- yowasp_yosys/share/python3/sby_status.py +344 -0
- yowasp_yosys/share/python3/smtio.py +96 -8
- yowasp_yosys/share/quicklogic/qlf_k6n10f/bram_types_sim.v +1 -1
- yowasp_yosys/smtbmc.py +85 -10
- yowasp_yosys/yosys.wasm +0 -0
- {yowasp_yosys-0.38.0.92.post687.dev0.dist-info → yowasp_yosys-0.39.0.165.post702.dev0.dist-info}/METADATA +1 -1
- {yowasp_yosys-0.38.0.92.post687.dev0.dist-info → yowasp_yosys-0.39.0.165.post702.dev0.dist-info}/RECORD +22 -21
- {yowasp_yosys-0.38.0.92.post687.dev0.dist-info → yowasp_yosys-0.39.0.165.post702.dev0.dist-info}/WHEEL +1 -1
- {yowasp_yosys-0.38.0.92.post687.dev0.dist-info → yowasp_yosys-0.39.0.165.post702.dev0.dist-info}/entry_points.txt +0 -0
- {yowasp_yosys-0.38.0.92.post687.dev0.dist-info → yowasp_yosys-0.39.0.165.post702.dev0.dist-info}/top_level.txt +0 -0
yowasp_yosys/sby.py
CHANGED
|
@@ -22,6 +22,7 @@ import json, os, sys, shutil, tempfile, re
|
|
|
22
22
|
from sby_cmdline import parser_func
|
|
23
23
|
from sby_core import SbyConfig, SbyTask, SbyAbort, SbyTaskloop, process_filename, dress_message
|
|
24
24
|
from sby_jobserver import SbyJobClient, process_jobserver_environment
|
|
25
|
+
from sby_status import SbyStatusDb
|
|
25
26
|
import time, platform, click
|
|
26
27
|
|
|
27
28
|
process_jobserver_environment() # needs to be called early
|
|
@@ -55,6 +56,42 @@ autotune_config = args.autotune_config
|
|
|
55
56
|
sequential = args.sequential
|
|
56
57
|
jobcount = args.jobcount
|
|
57
58
|
init_config_file = args.init_config_file
|
|
59
|
+
status_show = args.status
|
|
60
|
+
status_reset = args.status_reset
|
|
61
|
+
|
|
62
|
+
if status_show or status_reset:
|
|
63
|
+
target = workdir_prefix or workdir or sbyfile
|
|
64
|
+
if target is None:
|
|
65
|
+
print("ERROR: Specify a .sby config file or working directory to use --status.")
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
if not os.path.isdir(target) and target.endswith('.sby'):
|
|
68
|
+
target = target[:-4]
|
|
69
|
+
if not os.path.isdir(target):
|
|
70
|
+
print(f"ERROR: No directory found at {target!r}.", file=sys.stderr)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
with open(f"{target}/status.path", "r") as status_path_file:
|
|
75
|
+
status_path = f"{target}/{status_path_file.read().rstrip()}"
|
|
76
|
+
except FileNotFoundError:
|
|
77
|
+
status_path = f"{target}/status.sqlite"
|
|
78
|
+
|
|
79
|
+
if not os.path.exists(status_path):
|
|
80
|
+
print(f"ERROR: No status database found at {status_path!r}.", file=sys.stderr)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
|
|
83
|
+
status_db = SbyStatusDb(status_path, task=None)
|
|
84
|
+
|
|
85
|
+
if status_show:
|
|
86
|
+
status_db.print_status_summary()
|
|
87
|
+
sys.exit(0)
|
|
88
|
+
|
|
89
|
+
if status_reset:
|
|
90
|
+
status_db.reset()
|
|
91
|
+
|
|
92
|
+
status_db.db.close()
|
|
93
|
+
sys.exit(0)
|
|
94
|
+
|
|
58
95
|
|
|
59
96
|
if sbyfile is not None:
|
|
60
97
|
if os.path.isdir(sbyfile):
|
|
@@ -260,6 +297,8 @@ def read_sbyconfig(sbydata, taskname):
|
|
|
260
297
|
|
|
261
298
|
|
|
262
299
|
sbydata = list()
|
|
300
|
+
if sbyfile is None:
|
|
301
|
+
print("Reading .sby configuration from stdin:")
|
|
263
302
|
with (open(sbyfile, "r") if sbyfile is not None else sys.stdin) as f:
|
|
264
303
|
for line in f:
|
|
265
304
|
sbydata.append(line)
|
|
@@ -356,6 +395,7 @@ def start_task(taskloop, taskname):
|
|
|
356
395
|
|
|
357
396
|
my_opt_tmpdir = opt_tmpdir
|
|
358
397
|
my_workdir = None
|
|
398
|
+
my_status_db = None
|
|
359
399
|
|
|
360
400
|
if workdir is not None:
|
|
361
401
|
my_workdir = workdir
|
|
@@ -364,10 +404,12 @@ def start_task(taskloop, taskname):
|
|
|
364
404
|
my_workdir = workdir_prefix
|
|
365
405
|
else:
|
|
366
406
|
my_workdir = workdir_prefix + "_" + taskname
|
|
407
|
+
my_status_db = f"../{os.path.basename(workdir_prefix)}/status.sqlite"
|
|
367
408
|
|
|
368
409
|
if my_workdir is None and sbyfile is not None and not my_opt_tmpdir:
|
|
369
410
|
my_workdir = sbyfile[:-4]
|
|
370
411
|
if taskname is not None:
|
|
412
|
+
my_status_db = f"../{os.path.basename(my_workdir)}/status.sqlite"
|
|
371
413
|
my_workdir += "_" + taskname
|
|
372
414
|
|
|
373
415
|
if my_workdir is not None:
|
|
@@ -399,6 +441,14 @@ def start_task(taskloop, taskname):
|
|
|
399
441
|
with open(f"{my_workdir}/.gitignore", "w") as gitignore:
|
|
400
442
|
print("*", file=gitignore)
|
|
401
443
|
|
|
444
|
+
if my_status_db is not None:
|
|
445
|
+
os.makedirs(f"{my_workdir}/{os.path.dirname(my_status_db)}", exist_ok=True)
|
|
446
|
+
if os.getenv("SBY_WORKDIR_GITIGNORE"):
|
|
447
|
+
with open(f"{my_workdir}/{os.path.dirname(my_status_db)}/.gitignore", "w") as gitignore:
|
|
448
|
+
print("*", file=gitignore)
|
|
449
|
+
with open(f"{my_workdir}/status.path", "w") as status_path:
|
|
450
|
+
print(my_status_db, file=status_path)
|
|
451
|
+
|
|
402
452
|
junit_ts_name = os.path.basename(sbyfile[:-4]) if sbyfile is not None else workdir if workdir is not None else "stdin"
|
|
403
453
|
junit_tc_name = taskname if taskname is not None else "default"
|
|
404
454
|
|
|
@@ -625,11 +625,11 @@ struct value : public expr_base<value<Bits>> {
|
|
|
625
625
|
value<Bits + 1> remainder;
|
|
626
626
|
value<Bits + 1> dividend = sext<Bits + 1>();
|
|
627
627
|
value<Bits + 1> divisor = other.template sext<Bits + 1>();
|
|
628
|
-
if (
|
|
629
|
-
if (
|
|
628
|
+
if (is_neg()) dividend = dividend.neg();
|
|
629
|
+
if (other.is_neg()) divisor = divisor.neg();
|
|
630
630
|
std::tie(quotient, remainder) = dividend.udivmod(divisor);
|
|
631
|
-
if (
|
|
632
|
-
if (
|
|
631
|
+
if (is_neg() != other.is_neg()) quotient = quotient.neg();
|
|
632
|
+
if (is_neg()) remainder = remainder.neg();
|
|
633
633
|
return {quotient.template trunc<Bits>(), remainder.template trunc<Bits>()};
|
|
634
634
|
}
|
|
635
635
|
};
|
|
@@ -1010,22 +1010,24 @@ struct observer {
|
|
|
1010
1010
|
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
|
|
1011
1011
|
struct fmt_part {
|
|
1012
1012
|
enum {
|
|
1013
|
-
|
|
1013
|
+
LITERAL = 0,
|
|
1014
1014
|
INTEGER = 1,
|
|
1015
|
-
|
|
1016
|
-
|
|
1015
|
+
STRING = 2,
|
|
1016
|
+
UNICHAR = 3,
|
|
1017
|
+
VLOG_TIME = 4,
|
|
1017
1018
|
} type;
|
|
1018
1019
|
|
|
1019
|
-
//
|
|
1020
|
+
// LITERAL type
|
|
1020
1021
|
std::string str;
|
|
1021
1022
|
|
|
1022
|
-
// INTEGER/
|
|
1023
|
+
// INTEGER/STRING/UNICHAR types
|
|
1023
1024
|
// + value<Bits> val;
|
|
1024
1025
|
|
|
1025
|
-
// INTEGER/
|
|
1026
|
+
// INTEGER/STRING/VLOG_TIME types
|
|
1026
1027
|
enum {
|
|
1027
1028
|
RIGHT = 0,
|
|
1028
1029
|
LEFT = 1,
|
|
1030
|
+
NUMERIC = 2,
|
|
1029
1031
|
} justify; // = RIGHT;
|
|
1030
1032
|
char padding; // = '\0';
|
|
1031
1033
|
size_t width; // = 0;
|
|
@@ -1033,7 +1035,14 @@ struct fmt_part {
|
|
|
1033
1035
|
// INTEGER type
|
|
1034
1036
|
unsigned base; // = 10;
|
|
1035
1037
|
bool signed_; // = false;
|
|
1036
|
-
|
|
1038
|
+
enum {
|
|
1039
|
+
MINUS = 0,
|
|
1040
|
+
PLUS_MINUS = 1,
|
|
1041
|
+
SPACE_MINUS = 2,
|
|
1042
|
+
} sign; // = MINUS;
|
|
1043
|
+
bool hex_upper; // = false;
|
|
1044
|
+
bool show_base; // = false;
|
|
1045
|
+
bool group; // = false;
|
|
1037
1046
|
|
|
1038
1047
|
// VLOG_TIME type
|
|
1039
1048
|
bool realtime; // = false;
|
|
@@ -1049,11 +1058,12 @@ struct fmt_part {
|
|
|
1049
1058
|
// We might want to replace some of these bit() calls with direct
|
|
1050
1059
|
// chunk access if it turns out to be slow enough to matter.
|
|
1051
1060
|
std::string buf;
|
|
1061
|
+
std::string prefix;
|
|
1052
1062
|
switch (type) {
|
|
1053
|
-
case
|
|
1063
|
+
case LITERAL:
|
|
1054
1064
|
return str;
|
|
1055
1065
|
|
|
1056
|
-
case
|
|
1066
|
+
case STRING: {
|
|
1057
1067
|
buf.reserve(Bits/8);
|
|
1058
1068
|
for (int i = 0; i < Bits; i += 8) {
|
|
1059
1069
|
char ch = 0;
|
|
@@ -1067,35 +1077,76 @@ struct fmt_part {
|
|
|
1067
1077
|
break;
|
|
1068
1078
|
}
|
|
1069
1079
|
|
|
1080
|
+
case UNICHAR: {
|
|
1081
|
+
uint32_t codepoint = val.template get<uint32_t>();
|
|
1082
|
+
if (codepoint >= 0x10000)
|
|
1083
|
+
buf += (char)(0xf0 | (codepoint >> 18));
|
|
1084
|
+
else if (codepoint >= 0x800)
|
|
1085
|
+
buf += (char)(0xe0 | (codepoint >> 12));
|
|
1086
|
+
else if (codepoint >= 0x80)
|
|
1087
|
+
buf += (char)(0xc0 | (codepoint >> 6));
|
|
1088
|
+
else
|
|
1089
|
+
buf += (char)codepoint;
|
|
1090
|
+
if (codepoint >= 0x10000)
|
|
1091
|
+
buf += (char)(0x80 | ((codepoint >> 12) & 0x3f));
|
|
1092
|
+
if (codepoint >= 0x800)
|
|
1093
|
+
buf += (char)(0x80 | ((codepoint >> 6) & 0x3f));
|
|
1094
|
+
if (codepoint >= 0x80)
|
|
1095
|
+
buf += (char)(0x80 | ((codepoint >> 0) & 0x3f));
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1070
1099
|
case INTEGER: {
|
|
1071
|
-
|
|
1100
|
+
bool negative = signed_ && val.is_neg();
|
|
1101
|
+
if (negative) {
|
|
1102
|
+
prefix = "-";
|
|
1103
|
+
val = val.neg();
|
|
1104
|
+
} else {
|
|
1105
|
+
switch (sign) {
|
|
1106
|
+
case MINUS: break;
|
|
1107
|
+
case PLUS_MINUS: prefix = "+"; break;
|
|
1108
|
+
case SPACE_MINUS: prefix = " "; break;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
size_t val_width = Bits;
|
|
1072
1113
|
if (base != 10) {
|
|
1073
|
-
|
|
1114
|
+
val_width = 1;
|
|
1074
1115
|
for (size_t index = 0; index < Bits; index++)
|
|
1075
1116
|
if (val.bit(index))
|
|
1076
|
-
|
|
1117
|
+
val_width = index + 1;
|
|
1077
1118
|
}
|
|
1078
1119
|
|
|
1079
1120
|
if (base == 2) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1121
|
+
if (show_base)
|
|
1122
|
+
prefix += "0b";
|
|
1123
|
+
for (size_t index = 0; index < val_width; index++) {
|
|
1124
|
+
if (group && index > 0 && index % 4 == 0)
|
|
1125
|
+
buf += '_';
|
|
1126
|
+
buf += (val.bit(index) ? '1' : '0');
|
|
1127
|
+
}
|
|
1082
1128
|
} else if (base == 8 || base == 16) {
|
|
1129
|
+
if (show_base)
|
|
1130
|
+
prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o";
|
|
1083
1131
|
size_t step = (base == 16) ? 4 : 3;
|
|
1084
|
-
for (size_t index = 0; index <
|
|
1132
|
+
for (size_t index = 0; index < val_width; index += step) {
|
|
1133
|
+
if (group && index > 0 && index % (4 * step) == 0)
|
|
1134
|
+
buf += '_';
|
|
1085
1135
|
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
|
|
1086
1136
|
if (step == 4)
|
|
1087
1137
|
value |= val.bit(index + 3) << 3;
|
|
1088
|
-
buf += "0123456789abcdef"[value];
|
|
1138
|
+
buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value];
|
|
1089
1139
|
}
|
|
1090
|
-
std::reverse(buf.begin(), buf.end());
|
|
1091
1140
|
} else if (base == 10) {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
val = val.neg();
|
|
1141
|
+
if (show_base)
|
|
1142
|
+
prefix += "0d";
|
|
1095
1143
|
if (val.is_zero())
|
|
1096
1144
|
buf += '0';
|
|
1097
1145
|
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
|
|
1146
|
+
size_t index = 0;
|
|
1098
1147
|
while (!xval.is_zero()) {
|
|
1148
|
+
if (group && index > 0 && index % 3 == 0)
|
|
1149
|
+
buf += '_';
|
|
1099
1150
|
value<(Bits > 4 ? Bits : 4)> quotient, remainder;
|
|
1100
1151
|
if (Bits >= 4)
|
|
1101
1152
|
std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u});
|
|
@@ -1103,11 +1154,18 @@ struct fmt_part {
|
|
|
1103
1154
|
std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval);
|
|
1104
1155
|
buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
|
|
1105
1156
|
xval = quotient;
|
|
1157
|
+
index++;
|
|
1106
1158
|
}
|
|
1107
|
-
if (negative || plus)
|
|
1108
|
-
buf += negative ? '-' : '+';
|
|
1109
|
-
std::reverse(buf.begin(), buf.end());
|
|
1110
1159
|
} else assert(false && "Unsupported base for fmt_part");
|
|
1160
|
+
if (justify == NUMERIC && group && padding == '0') {
|
|
1161
|
+
int group_size = base == 10 ? 3 : 4;
|
|
1162
|
+
while (prefix.size() + buf.size() < width) {
|
|
1163
|
+
if (buf.size() % (group_size + 1) == group_size)
|
|
1164
|
+
buf += '_';
|
|
1165
|
+
buf += '0';
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
std::reverse(buf.begin(), buf.end());
|
|
1111
1169
|
break;
|
|
1112
1170
|
}
|
|
1113
1171
|
|
|
@@ -1123,17 +1181,29 @@ struct fmt_part {
|
|
|
1123
1181
|
|
|
1124
1182
|
std::string str;
|
|
1125
1183
|
assert(width == 0 || padding != '\0');
|
|
1126
|
-
if (
|
|
1127
|
-
size_t pad_width = width - buf.size();
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1184
|
+
if (prefix.size() + buf.size() < width) {
|
|
1185
|
+
size_t pad_width = width - prefix.size() - buf.size();
|
|
1186
|
+
switch (justify) {
|
|
1187
|
+
case LEFT:
|
|
1188
|
+
str += prefix;
|
|
1189
|
+
str += buf;
|
|
1190
|
+
str += std::string(pad_width, padding);
|
|
1191
|
+
break;
|
|
1192
|
+
case RIGHT:
|
|
1193
|
+
str += std::string(pad_width, padding);
|
|
1194
|
+
str += prefix;
|
|
1195
|
+
str += buf;
|
|
1196
|
+
break;
|
|
1197
|
+
case NUMERIC:
|
|
1198
|
+
str += prefix;
|
|
1199
|
+
str += std::string(pad_width, padding);
|
|
1200
|
+
str += buf;
|
|
1201
|
+
break;
|
|
1202
|
+
}
|
|
1203
|
+
} else {
|
|
1204
|
+
str += prefix;
|
|
1205
|
+
str += buf;
|
|
1133
1206
|
}
|
|
1134
|
-
str += buf;
|
|
1135
|
-
if (justify == LEFT && buf.size() < width)
|
|
1136
|
-
str += std::string(width - buf.size(), padding);
|
|
1137
1207
|
return str;
|
|
1138
1208
|
}
|
|
1139
1209
|
};
|
|
@@ -49,11 +49,16 @@
|
|
|
49
49
|
// <file> ::= <file-header> <definitions> <sample>+
|
|
50
50
|
// <file-header> ::= 0x52585843 0x00004c54
|
|
51
51
|
// <definitions> ::= <packet-define>* <packet-end>
|
|
52
|
-
// <sample> ::= <packet-sample> <packet-change
|
|
52
|
+
// <sample> ::= <packet-sample> (<packet-change> | <packet-diag>)* <packet-end>
|
|
53
53
|
// <packet-define> ::= 0xc0000000 ...
|
|
54
54
|
// <packet-sample> ::= 0xc0000001 ...
|
|
55
55
|
// <packet-change> ::= 0x0??????? <chunk>+ | 0x1??????? <index> <chunk>+ | 0x2??????? | 0x3???????
|
|
56
56
|
// <chunk>, <index> ::= 0x????????
|
|
57
|
+
// <packet-diag> ::= <packet-break> | <packet-print> | <packet-assert> | <packet-assume>
|
|
58
|
+
// <packet-break> ::= 0xc0000010 <message> <source-location>
|
|
59
|
+
// <packet-print> ::= 0xc0000011 <message> <source-location>
|
|
60
|
+
// <packet-assert> ::= 0xc0000012 <message> <source-location>
|
|
61
|
+
// <packet-assume> ::= 0xc0000013 <message> <source-location>
|
|
57
62
|
// <packet-end> ::= 0xFFFFFFFF
|
|
58
63
|
//
|
|
59
64
|
// The replay log contains sample data, however, it does not cover the entire design. Rather, it only contains sample
|
|
@@ -61,6 +66,10 @@
|
|
|
61
66
|
// a minimum, and recording speed to a maximum. The player samples any missing data by setting the design state items
|
|
62
67
|
// to the same values they had during recording, and re-evaluating the design.
|
|
63
68
|
//
|
|
69
|
+
// Packets for diagnostics (prints, breakpoints, assertions, and assumptions) are used solely for diagnostics emitted
|
|
70
|
+
// by the C++ testbench driving the simulation, and are not recorded while evaluating the design. (Diagnostics emitted
|
|
71
|
+
// by the RTL can be reconstructed at replay time, so recording them would be a waste of space.)
|
|
72
|
+
//
|
|
64
73
|
// Limits
|
|
65
74
|
// ------
|
|
66
75
|
//
|
|
@@ -109,6 +118,33 @@
|
|
|
109
118
|
|
|
110
119
|
namespace cxxrtl {
|
|
111
120
|
|
|
121
|
+
// A single diagnostic that can be manipulated as an object (including being written to and read from a file).
|
|
122
|
+
// This differs from the base CXXRTL interface, where diagnostics can only be emitted via a procedure call, and are
|
|
123
|
+
// not materialized as objects.
|
|
124
|
+
struct diagnostic {
|
|
125
|
+
// The `BREAK` flavor corresponds to a breakpoint, which is a diagnostic type that can currently only be emitted
|
|
126
|
+
// by the C++ testbench code.
|
|
127
|
+
enum flavor {
|
|
128
|
+
BREAK = 0,
|
|
129
|
+
PRINT = 1,
|
|
130
|
+
ASSERT = 2,
|
|
131
|
+
ASSUME = 3,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
flavor type;
|
|
135
|
+
std::string message;
|
|
136
|
+
std::string location; // same format as the `src` attribute of `$print` or `$check` cell
|
|
137
|
+
|
|
138
|
+
diagnostic()
|
|
139
|
+
: type(BREAK) {}
|
|
140
|
+
|
|
141
|
+
diagnostic(flavor type, const std::string &message, const std::string &location)
|
|
142
|
+
: type(type), message(message), location(location) {}
|
|
143
|
+
|
|
144
|
+
diagnostic(flavor type, const std::string &message, const char *file, unsigned line)
|
|
145
|
+
: type(type), message(message), location(std::string(file) + ':' + std::to_string(line)) {}
|
|
146
|
+
};
|
|
147
|
+
|
|
112
148
|
// A spool stores CXXRTL design state changes in a file.
|
|
113
149
|
class spool {
|
|
114
150
|
public:
|
|
@@ -127,7 +163,7 @@ public:
|
|
|
127
163
|
|
|
128
164
|
static constexpr uint32_t PACKET_SAMPLE = 0xc0000001;
|
|
129
165
|
enum sample_flag : uint32_t {
|
|
130
|
-
EMPTY
|
|
166
|
+
EMPTY = 0,
|
|
131
167
|
INCREMENTAL = 1,
|
|
132
168
|
};
|
|
133
169
|
|
|
@@ -139,6 +175,9 @@ public:
|
|
|
139
175
|
static constexpr uint32_t PACKET_CHANGEL = 0x20000000/* | ident */;
|
|
140
176
|
static constexpr uint32_t PACKET_CHANGEH = 0x30000000/* | ident */;
|
|
141
177
|
|
|
178
|
+
static constexpr uint32_t PACKET_DIAGNOSTIC = 0xc0000010/* | diagnostic::flavor */;
|
|
179
|
+
static constexpr uint32_t DIAGNOSTIC_MASK = 0x0000000f;
|
|
180
|
+
|
|
142
181
|
static constexpr uint32_t PACKET_END = 0xffffffff;
|
|
143
182
|
|
|
144
183
|
// Writing spools.
|
|
@@ -281,6 +320,12 @@ public:
|
|
|
281
320
|
emit_word(data[offset]);
|
|
282
321
|
}
|
|
283
322
|
|
|
323
|
+
void write_diagnostic(const diagnostic &diagnostic) {
|
|
324
|
+
emit_word(PACKET_DIAGNOSTIC | diagnostic.type);
|
|
325
|
+
emit_string(diagnostic.message);
|
|
326
|
+
emit_string(diagnostic.location);
|
|
327
|
+
}
|
|
328
|
+
|
|
284
329
|
void write_end() {
|
|
285
330
|
emit_word(PACKET_END);
|
|
286
331
|
}
|
|
@@ -397,11 +442,16 @@ public:
|
|
|
397
442
|
return true;
|
|
398
443
|
}
|
|
399
444
|
|
|
400
|
-
bool
|
|
445
|
+
bool read_header(uint32_t &header) {
|
|
401
446
|
header = absorb_word();
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
447
|
+
return header != PACKET_END;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// This method must be separate from `read_change_data` because `chunks` and `depth` can only be looked up
|
|
451
|
+
// if `ident` is known.
|
|
452
|
+
bool read_change_ident(uint32_t header, ident_t &ident) {
|
|
453
|
+
if ((header & ~(CHANGE_MASK | MAXIMUM_IDENT)) != 0)
|
|
454
|
+
return false; // some other packet
|
|
405
455
|
ident = header & MAXIMUM_IDENT;
|
|
406
456
|
return true;
|
|
407
457
|
}
|
|
@@ -427,6 +477,18 @@ public:
|
|
|
427
477
|
for (size_t offset = 0; offset < chunks; offset++)
|
|
428
478
|
data[chunks * index + offset] = absorb_word();
|
|
429
479
|
}
|
|
480
|
+
|
|
481
|
+
bool read_diagnostic(uint32_t header, diagnostic &diagnostic) {
|
|
482
|
+
if ((header & ~DIAGNOSTIC_MASK) != PACKET_DIAGNOSTIC)
|
|
483
|
+
return false; // some other packet
|
|
484
|
+
uint32_t type = header & DIAGNOSTIC_MASK;
|
|
485
|
+
assert(type == diagnostic::BREAK || type == diagnostic::PRINT ||
|
|
486
|
+
type == diagnostic::ASSERT || type == diagnostic::ASSUME);
|
|
487
|
+
diagnostic.type = (diagnostic::flavor)type;
|
|
488
|
+
diagnostic.message = absorb_string();
|
|
489
|
+
diagnostic.location = absorb_string();
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
430
492
|
};
|
|
431
493
|
|
|
432
494
|
// Opening spools. For certain uses of the record/replay mechanism, two distinct open files (two open files, i.e.
|
|
@@ -584,6 +646,18 @@ public:
|
|
|
584
646
|
return changed;
|
|
585
647
|
}
|
|
586
648
|
|
|
649
|
+
void record_diagnostic(const diagnostic &diagnostic) {
|
|
650
|
+
assert(streaming);
|
|
651
|
+
|
|
652
|
+
// Emit an incremental delta cycle per diagnostic to simplify the logic of the recorder. This is inefficient, but
|
|
653
|
+
// diagnostics should be rare enough that this inefficiency does not matter. If it turns out to be an issue, this
|
|
654
|
+
// code should be changed to accumulate diagnostics to a buffer that is flushed in `record_{complete,incremental}`
|
|
655
|
+
// and also in `advance_time` before the timestamp is changed. (Right now `advance_time` never writes to the spool.)
|
|
656
|
+
writer.write_sample(/*incremental=*/true, pointer++, timestamp);
|
|
657
|
+
writer.write_diagnostic(diagnostic);
|
|
658
|
+
writer.write_end();
|
|
659
|
+
}
|
|
660
|
+
|
|
587
661
|
void flush() {
|
|
588
662
|
writer.flush();
|
|
589
663
|
}
|
|
@@ -657,8 +731,9 @@ public:
|
|
|
657
731
|
streaming = true;
|
|
658
732
|
|
|
659
733
|
// Establish the initial state of the design.
|
|
660
|
-
|
|
661
|
-
|
|
734
|
+
std::vector<diagnostic> diagnostics;
|
|
735
|
+
initialized = replay(&diagnostics);
|
|
736
|
+
assert(initialized && diagnostics.empty());
|
|
662
737
|
}
|
|
663
738
|
|
|
664
739
|
// Returns the pointer of the current sample.
|
|
@@ -690,8 +765,8 @@ public:
|
|
|
690
765
|
// If this function returns `true`, then `current_pointer() == at_pointer`, and the module contains values that
|
|
691
766
|
// correspond to this pointer in the replay log. To obtain a valid pointer, call `current_pointer()`; while pointers
|
|
692
767
|
// are monotonically increasing for each consecutive sample, using arithmetic operations to create a new pointer is
|
|
693
|
-
// not allowed.
|
|
694
|
-
bool rewind_to(spool::pointer_t at_pointer) {
|
|
768
|
+
// not allowed. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in this sample.
|
|
769
|
+
bool rewind_to(spool::pointer_t at_pointer, std::vector<diagnostic> *diagnostics) {
|
|
695
770
|
assert(initialized);
|
|
696
771
|
|
|
697
772
|
// The pointers in the replay log start from one that is greater than `at_pointer`. In this case the pointer will
|
|
@@ -707,9 +782,12 @@ public:
|
|
|
707
782
|
reader.rewind(position_it->second);
|
|
708
783
|
|
|
709
784
|
// Replay samples until eventually arriving to `at_pointer` or encountering end of file.
|
|
710
|
-
while(replay()) {
|
|
785
|
+
while(replay(diagnostics)) {
|
|
711
786
|
if (pointer == at_pointer)
|
|
712
787
|
return true;
|
|
788
|
+
|
|
789
|
+
if (diagnostics)
|
|
790
|
+
diagnostics->clear();
|
|
713
791
|
}
|
|
714
792
|
return false;
|
|
715
793
|
}
|
|
@@ -717,8 +795,8 @@ public:
|
|
|
717
795
|
// If this function returns `true`, then `current_time() <= at_or_before_timestamp`, and the module contains values
|
|
718
796
|
// that correspond to `current_time()` in the replay log. If `current_time() == at_or_before_timestamp` and there
|
|
719
797
|
// are several consecutive samples with the same time, the module contains values that correspond to the first of
|
|
720
|
-
// these samples.
|
|
721
|
-
bool rewind_to_or_before(const time &at_or_before_timestamp) {
|
|
798
|
+
// these samples. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in this sample.
|
|
799
|
+
bool rewind_to_or_before(const time &at_or_before_timestamp, std::vector<diagnostic> *diagnostics) {
|
|
722
800
|
assert(initialized);
|
|
723
801
|
|
|
724
802
|
// The timestamps in the replay log start from one that is greater than `at_or_before_timestamp`. In this case
|
|
@@ -734,7 +812,7 @@ public:
|
|
|
734
812
|
reader.rewind(position_it->second);
|
|
735
813
|
|
|
736
814
|
// Replay samples until eventually arriving to or past `at_or_before_timestamp` or encountering end of file.
|
|
737
|
-
while (replay()) {
|
|
815
|
+
while (replay(diagnostics)) {
|
|
738
816
|
if (timestamp == at_or_before_timestamp)
|
|
739
817
|
break;
|
|
740
818
|
|
|
@@ -743,14 +821,17 @@ public:
|
|
|
743
821
|
break;
|
|
744
822
|
if (next_timestamp > at_or_before_timestamp)
|
|
745
823
|
break;
|
|
824
|
+
|
|
825
|
+
if (diagnostics)
|
|
826
|
+
diagnostics->clear();
|
|
746
827
|
}
|
|
747
828
|
return true;
|
|
748
829
|
}
|
|
749
830
|
|
|
750
831
|
// If this function returns `true`, then `current_pointer()` and `current_time()` are updated for the next sample
|
|
751
832
|
// and the module now contains values that correspond to that sample. If it returns `false`, there was no next sample
|
|
752
|
-
// to read.
|
|
753
|
-
bool replay() {
|
|
833
|
+
// to read. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in the next sample.
|
|
834
|
+
bool replay(std::vector<diagnostic> *diagnostics) {
|
|
754
835
|
assert(streaming);
|
|
755
836
|
|
|
756
837
|
bool incremental;
|
|
@@ -771,11 +852,16 @@ public:
|
|
|
771
852
|
}
|
|
772
853
|
|
|
773
854
|
uint32_t header;
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
855
|
+
while (reader.read_header(header)) {
|
|
856
|
+
spool::ident_t ident;
|
|
857
|
+
diagnostic diag;
|
|
858
|
+
if (reader.read_change_ident(header, ident)) {
|
|
859
|
+
variable &var = variables.at(ident);
|
|
860
|
+
reader.read_change_data(header, var.chunks, var.depth, var.curr);
|
|
861
|
+
} else if (reader.read_diagnostic(header, diag)) {
|
|
862
|
+
if (diagnostics)
|
|
863
|
+
diagnostics->push_back(diag);
|
|
864
|
+
} else assert(false && "Unrecognized packet header");
|
|
779
865
|
}
|
|
780
866
|
return true;
|
|
781
867
|
}
|
|
@@ -53,22 +53,24 @@ struct VerilogFmtArg {
|
|
|
53
53
|
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
|
|
54
54
|
struct FmtPart {
|
|
55
55
|
enum {
|
|
56
|
-
|
|
56
|
+
LITERAL = 0,
|
|
57
57
|
INTEGER = 1,
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
STRING = 2,
|
|
59
|
+
UNICHAR = 3,
|
|
60
|
+
VLOG_TIME = 4,
|
|
60
61
|
} type;
|
|
61
62
|
|
|
62
|
-
//
|
|
63
|
+
// LITERAL type
|
|
63
64
|
std::string str;
|
|
64
65
|
|
|
65
|
-
// INTEGER/
|
|
66
|
+
// INTEGER/STRING/UNICHAR types
|
|
66
67
|
RTLIL::SigSpec sig;
|
|
67
68
|
|
|
68
|
-
// INTEGER/
|
|
69
|
+
// INTEGER/STRING/VLOG_TIME types
|
|
69
70
|
enum {
|
|
70
71
|
RIGHT = 0,
|
|
71
72
|
LEFT = 1,
|
|
73
|
+
NUMERIC = 2,
|
|
72
74
|
} justify = RIGHT;
|
|
73
75
|
char padding = '\0';
|
|
74
76
|
size_t width = 0;
|
|
@@ -76,7 +78,14 @@ struct FmtPart {
|
|
|
76
78
|
// INTEGER type
|
|
77
79
|
unsigned base = 10;
|
|
78
80
|
bool signed_ = false;
|
|
79
|
-
|
|
81
|
+
enum {
|
|
82
|
+
MINUS = 0,
|
|
83
|
+
PLUS_MINUS = 1,
|
|
84
|
+
SPACE_MINUS = 2,
|
|
85
|
+
} sign = MINUS;
|
|
86
|
+
bool hex_upper = false;
|
|
87
|
+
bool show_base = false;
|
|
88
|
+
bool group = false;
|
|
80
89
|
|
|
81
90
|
// VLOG_TIME type
|
|
82
91
|
bool realtime = false;
|
|
@@ -86,7 +95,7 @@ struct Fmt {
|
|
|
86
95
|
public:
|
|
87
96
|
std::vector<FmtPart> parts;
|
|
88
97
|
|
|
89
|
-
void
|
|
98
|
+
void append_literal(const std::string &str);
|
|
90
99
|
|
|
91
100
|
void parse_rtlil(const RTLIL::Cell *cell);
|
|
92
101
|
void emit_rtlil(RTLIL::Cell *cell) const;
|
|
@@ -149,7 +149,7 @@ template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> cla
|
|
|
149
149
|
std::map<T, int, C> node_to_index;
|
|
150
150
|
std::vector<std::set<int, IndirectCmp>> edges;
|
|
151
151
|
std::vector<T> sorted;
|
|
152
|
-
std::set<std::
|
|
152
|
+
std::set<std::vector<T>> loops;
|
|
153
153
|
|
|
154
154
|
TopoSort() : indirect_cmp(nodes)
|
|
155
155
|
{
|
|
@@ -220,10 +220,10 @@ template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> cla
|
|
|
220
220
|
if (active_cells[root_index]) {
|
|
221
221
|
found_loops = true;
|
|
222
222
|
if (analyze_loops) {
|
|
223
|
-
std::
|
|
223
|
+
std::vector<T> loop;
|
|
224
224
|
for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
|
|
225
225
|
const int index = active_stack[i];
|
|
226
|
-
loop.
|
|
226
|
+
loop.push_back(nodes[index]);
|
|
227
227
|
if (index == root_index)
|
|
228
228
|
break;
|
|
229
229
|
}
|
|
@@ -69,6 +69,11 @@ def parser_func():
|
|
|
69
69
|
parser.add_argument("--setup", action="store_true", dest="setupmode",
|
|
70
70
|
help="set up the working directory and exit")
|
|
71
71
|
|
|
72
|
+
parser.add_argument("--status", action="store_true", dest="status",
|
|
73
|
+
help="summarize the contents of the status database")
|
|
74
|
+
parser.add_argument("--statusreset", action="store_true", dest="status_reset",
|
|
75
|
+
help="reset the contents of the status database")
|
|
76
|
+
|
|
72
77
|
parser.add_argument("--init-config-file", dest="init_config_file",
|
|
73
78
|
help="create a default .sby config file")
|
|
74
79
|
parser.add_argument("sbyfile", metavar="<jobname>.sby | <dirname>", nargs="?",
|