diff --git a/.gitignore b/.gitignore index fb39c6bba..0fafa39af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ +## user config +/Makefile.conf + +## build artifacts +# compiler intermediate files *.o *.d *.dwo -.*.swp *.gch *.gcda *.gcno @@ -26,6 +30,11 @@ __pycache__ /Makefile.conf /Brewfile.lock.json /viz.js +*.so.dSYM/ + +# compiler output files +/kernel/version_*.cc +/share /yosys /yosys.exe /yosys.js @@ -41,14 +50,12 @@ __pycache__ /yosys-witness-script.py /yosys-filterlib /yosys-filterlib.exe -/kernel/*.pyh -/kernel/python_wrappers.cc -/kernel/version_*.cc -/share /yosys-win32-mxebin-* /yosys-win32-vcxsrc-* /yosysjs-* /libyosys.so + +# build directories /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests @@ -56,12 +63,42 @@ __pycache__ /tests/arch/quicklogic/qlf_k6n10f/run-test.mk /tests/verilog/roundtrip_proc_1.v /tests/verilog/roundtrip_proc_2.v +/build /result /dist -/*.egg-info -/build -/venv + +# pyosys +/kernel/*.pyh +/kernel/python_wrappers.cc /boost /ffi /doxygen +/venv /*.whl +/*.egg-info + +# yosysjs dependency +/viz.js + +# other +/coverage.info +/coverage_html + + +# these really belong in global gitignore since they're not specific to this project but rather to user tool choice +# but too many people don't have a global gitignore configured: +# https://docs.github.com/en/get-started/git-basics/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer +__pycache__ +*~ +.*.swp +/.cache +/.vscode +/.cproject +/.project +/.settings +/qtcreator.files +/qtcreator.includes +/qtcreator.config +/qtcreator.creator +/qtcreator.creator.user +/compile_commands.json diff --git a/Makefile b/Makefile index bc102f5e4..98add1a32 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.55+112 +YOSYS_VER := 0.55+146 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3) @@ -925,6 +925,7 @@ MK_TEST_DIRS = # MK_TEST_DIRS += tests/arch/quicklogic/pp3 # MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f # MK_TEST_DIRS += tests/arch/xilinx +MK_TEST_DIRS += tests/bugpoint MK_TEST_DIRS += tests/opt MK_TEST_DIRS += tests/sat MK_TEST_DIRS += tests/sim diff --git a/abc b/abc index fb5050530..f24c67268 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit fb5050530d8aba2a99f8e4ffb846e072017af232 +Subproject commit f24c672683db09bdf783a8a01f3dd4081dcc8f83 diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index a8c9eeee5..e49e4816f 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1180,9 +1180,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); + f << (cell->type == ID($_AOI3_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI3_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" "); dump_cell_expr_port(f, cell, "C", false); @@ -1195,13 +1195,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI4_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" ("); dump_cell_expr_port(f, cell, "C", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "D", false); f << stringf("));\n"); return true; @@ -1412,10 +1412,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int s_width = cell->getPort(ID::S).size(); std::string func_name = cellname(cell); - f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str()); - f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1); - f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1); - f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); + f << stringf("%s" "function [%d:0] %s;\n", indent, width-1, func_name); + f << stringf("%s" " input [%d:0] a;\n", indent, width-1); + f << stringf("%s" " input [%d:0] b;\n", indent, s_width*width-1); + f << stringf("%s" " input [%d:0] s;\n", indent, s_width-1); dump_attributes(f, indent + " ", cell->attributes); if (noparallelcase) @@ -1424,7 +1424,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (!noattr) f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); f << stringf("%s" " casez (s)", indent.c_str()); - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + f << (noattr ? " // synopsys parallel_case\n" : "\n"); } for (int i = 0; i < s_width; i++) @@ -2637,7 +2637,7 @@ struct VerilogBackend : public Backend { Pass::call(design, "clean_zerowidth"); log_pop(); - design->sort(); + design->sort_modules(); *f << stringf("/* Generated by %s */\n", yosys_maybe_version()); @@ -2650,6 +2650,7 @@ struct VerilogBackend : public Backend { continue; } log("Dumping module `%s'.\n", module->name.c_str()); + module->sort(); dump_module(*f, "", module); } diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 35cecd98b..69ec5e592 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -136,7 +136,7 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil msg_type == VERIFIC_IGNORE ? "IGNORE" : msg_type == VERIFIC_INFO ? "INFO" : msg_type == VERIFIC_COMMENT ? "COMMENT" : - msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id); + msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id ? message_id : ""); string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; message += vstringf(msg, args); @@ -3052,6 +3052,9 @@ std::set import_tops(const char* work, std::map args, RTLIL::Design *design) override { - static bool set_verific_global_flags = true; if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" @@ -4353,6 +4356,9 @@ struct VerificPass : public Pass { if ((unsigned long)verific_sva_fsm_limit >= sizeof(1ull)*8) log_cmd_error("-L %d: limit too large; maximum allowed value is %zu.\n", verific_sva_fsm_limit, sizeof(1ull)*8-1); + if (already_imported) + log_warning("Note that all Verific flags were reset to defaults after last -import.\n"); + std::set top_mod_names; if (mode_all) @@ -4430,6 +4436,7 @@ struct VerificPass : public Pass { } verific_cleanup(); + already_imported = true; goto check_error; } diff --git a/kernel/constids.inc b/kernel/constids.inc index c77a25c01..29872d45e 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -286,3 +286,4 @@ X(A_WIDTHS) X(B_WIDTHS) X(C_WIDTHS) X(C_SIGNED) +X(raise_error) diff --git a/kernel/io.cc b/kernel/io.cc index d7d126c4c..3deb54d86 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -384,4 +384,153 @@ std::string escape_filename_spaces(const std::string& filename) return out; } +void format_emit_unescaped(std::string &result, std::string_view fmt) +{ + result.reserve(result.size() + fmt.size()); + for (size_t i = 0; i < fmt.size(); ++i) { + char ch = fmt[i]; + result.push_back(ch); + if (ch == '%' && i + 1 < fmt.size() && fmt[i + 1] == '%') { + ++i; + } + } +} + +std::string unescape_format_string(std::string_view fmt) +{ + std::string result; + format_emit_unescaped(result, fmt); + return result; +} + +static std::string string_view_stringf(std::string_view spec, ...) +{ + std::string fmt(spec); + char format_specifier = fmt[fmt.size() - 1]; + switch (format_specifier) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': { + // Strip any length modifier off `fmt` + std::string long_fmt; + for (size_t i = 0; i + 1 < fmt.size(); ++i) { + char ch = fmt[i]; + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + break; + } + long_fmt.push_back(ch); + } + // Add `lld` or whatever + long_fmt += "ll"; + long_fmt.push_back(format_specifier); + fmt = long_fmt; + break; + } + default: + break; + } + + va_list ap; + va_start(ap, spec); + std::string result = vstringf(fmt.c_str(), ap); + va_end(ap); + return result; +} + +template +static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, Arg arg) +{ + // Delegate nontrivial formats to the C library. + switch (num_dynamic_ints) { + case DynamicIntCount::NONE: + result += string_view_stringf(spec, arg); + return; + case DynamicIntCount::ONE: + result += string_view_stringf(spec, dynamic_ints[0], arg); + return; + case DynamicIntCount::TWO: + result += string_view_stringf(spec, dynamic_ints[0], dynamic_ints[1], arg); + return; + } + YOSYS_ABORT("Internal error"); +} + +void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, long long arg) +{ + if (spec == "%d") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += std::to_string(arg); + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, unsigned long long arg) +{ + if (spec == "%u") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += std::to_string(arg); + return; + } + if (spec == "%c") { + result += static_cast(arg); + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, double arg) +{ + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const char *arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += arg; + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + +void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const std::string &arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + result += arg; + return; + } + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str()); +} + +void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, std::string_view arg) +{ + if (spec == "%s") { + // Format checking will have guaranteed num_dynamic_ints == 0. + // We can output the string without creating a temporary copy. + result += arg; + return; + } + // Delegate nontrivial formats to the C library. We need to construct + // a temporary string to ensure null termination. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, std::string(arg).c_str()); +} + +void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const void *arg) +{ + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); +} + YOSYS_NAMESPACE_END diff --git a/kernel/io.h b/kernel/io.h index 91699d775..ea2499e43 100644 --- a/kernel/io.h +++ b/kernel/io.h @@ -1,5 +1,6 @@ #include #include +#include #include "kernel/yosys_common.h" #ifndef YOSYS_IO_H @@ -50,18 +51,391 @@ inline std::string vstringf(const char *fmt, va_list ap) #endif } -std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); - -inline std::string stringf(const char *fmt, ...) +enum ConversionSpecifier : uint8_t { - std::string string; - va_list ap; + CONVSPEC_NONE, + // Specifier not understood/supported + CONVSPEC_ERROR, + // Consumes a "long long" + CONVSPEC_SIGNED_INT, + // Consumes a "unsigned long long" + CONVSPEC_UNSIGNED_INT, + // Consumes a "double" + CONVSPEC_DOUBLE, + // Consumes a "const char*" + CONVSPEC_CHAR_PTR, + // Consumes a "void*" + CONVSPEC_VOID_PTR, +}; - va_start(ap, fmt); - string = vstringf(fmt, ap); - va_end(ap); +constexpr ConversionSpecifier parse_conversion_specifier(char ch, char prev_ch) +{ + switch (ch) { + case 'd': + case 'i': + return CONVSPEC_SIGNED_INT; + case 'o': + case 'u': + case 'x': + case 'X': + case 'm': + return CONVSPEC_UNSIGNED_INT; + case 'c': + if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') { + // wchar not supported + return CONVSPEC_ERROR; + } + return CONVSPEC_UNSIGNED_INT; + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + return CONVSPEC_DOUBLE; + case 's': + if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') { + // wchar not supported + return CONVSPEC_ERROR; + } + return CONVSPEC_CHAR_PTR; + case 'p': + return CONVSPEC_VOID_PTR; + case '$': // positional parameters + case 'n': + case 'S': + return CONVSPEC_ERROR; + default: + return CONVSPEC_NONE; + } +} - return string; +enum class DynamicIntCount : uint8_t { + NONE = 0, + ONE = 1, + TWO = 2, +}; + +// Describes a printf-style format conversion specifier found in a format string. +struct FoundFormatSpec +{ + // The start offset of the conversion spec in the format string. + int start; + // The end offset of the conversion spec in the format string. + int end; + ConversionSpecifier spec; + // Number of int args consumed by '*' dynamic width/precision args. + DynamicIntCount num_dynamic_ints; +}; + +// Ensure there is no format spec. +constexpr void ensure_no_format_spec(std::string_view fmt, int index, bool *has_escapes) +{ + int fmt_size = static_cast(fmt.size()); + // A trailing '%' is not a format spec. + while (index + 1 < fmt_size) { + if (fmt[index] != '%') { + ++index; + continue; + } + if (fmt[index + 1] != '%') { + YOSYS_ABORT("More format conversion specifiers than arguments"); + } + *has_escapes = true; + index += 2; + } +} + +// Returns the next format conversion specifier (starting with '%'). +// Returns CONVSPEC_NONE if there isn't a format conversion specifier. +constexpr FoundFormatSpec find_next_format_spec(std::string_view fmt, int fmt_start, bool *has_escapes) +{ + int index = fmt_start; + int fmt_size = static_cast(fmt.size()); + while (index < fmt_size) { + if (fmt[index] != '%') { + ++index; + continue; + } + int p = index + 1; + uint8_t num_dynamic_ints = 0; + while (true) { + if (p == fmt_size) { + return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE}; + } + if (fmt[p] == '%') { + *has_escapes = true; + index = p + 1; + break; + } + if (fmt[p] == '*') { + if (num_dynamic_ints >= 2) { + return {0, 0, CONVSPEC_ERROR, DynamicIntCount::NONE}; + } + ++num_dynamic_ints; + } + ConversionSpecifier spec = parse_conversion_specifier(fmt[p], fmt[p - 1]); + if (spec != CONVSPEC_NONE) { + return {index, p + 1, spec, static_cast(num_dynamic_ints)}; + } + ++p; + } + } + return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE}; +} + +template +constexpr typename std::enable_if::type +check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec*, DynamicIntCount) +{ + ensure_no_format_spec(fmt, fmt_start, has_escapes); +} + +// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments. +// Fills `specs` with the FoundFormatSpecs found in the format string. +// `int_args_consumed` is the number of int arguments already consumed to satisfy the +// dynamic width/precision args for the next format conversion specifier. +template +constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec* specs, + DynamicIntCount int_args_consumed) +{ + FoundFormatSpec found = find_next_format_spec(fmt, fmt_start, has_escapes); + if (found.num_dynamic_ints > int_args_consumed) { + // We need to consume at least one more int for the dynamic + // width/precision of this format conversion specifier. + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected dynamic int argument"); + } + check_format(fmt, fmt_start, has_escapes, specs, + static_cast(static_cast(int_args_consumed) + 1)); + return; + } + switch (found.spec) { + case CONVSPEC_NONE: + YOSYS_ABORT("Expected format conversion specifier for argument"); + break; + case CONVSPEC_ERROR: + YOSYS_ABORT("Found unsupported format conversion specifier"); + break; + case CONVSPEC_SIGNED_INT: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to signed integer"); + } + *specs = found; + break; + case CONVSPEC_UNSIGNED_INT: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to unsigned integer"); + } + *specs = found; + break; + case CONVSPEC_DOUBLE: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to double"); + } + *specs = found; + break; + case CONVSPEC_CHAR_PTR: + if constexpr (!std::is_convertible_v && + !std::is_convertible_v && + !std::is_convertible_v) { + YOSYS_ABORT("Expected type convertible to char *"); + } + *specs = found; + break; + case CONVSPEC_VOID_PTR: + if constexpr (!std::is_convertible_v) { + YOSYS_ABORT("Expected pointer type"); + } + *specs = found; + break; + } + check_format(fmt, found.end, has_escapes, specs + 1, DynamicIntCount::NONE); +} + +// Emit the string representation of `arg` that has been converted to a `long long'. +void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, long long arg); + +// Emit the string representation of `arg` that has been converted to a `unsigned long long'. +void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, unsigned long long arg); + +// Emit the string representation of `arg` that has been converted to a `double'. +void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, double arg); + +// Emit the string representation of `arg` that has been converted to a `const char*'. +void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const char *arg); + +// Emit the string representation of `arg` that has been converted to a `std::string'. +void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const std::string &arg); + +// Emit the string representation of `arg` that has been converted to a `std::string_view'. +void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, std::string_view arg); + +// Emit the string representation of `arg` that has been converted to a `double'. +void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints, + DynamicIntCount num_dynamic_ints, const void *arg); + +// Emit the string representation of `arg` according to the given `FoundFormatSpec`, +// appending it to `result`. +template +inline void format_emit_one(std::string &result, std::string_view fmt, const FoundFormatSpec &ffspec, + int *dynamic_ints, const Arg& arg) +{ + std::string_view spec = fmt.substr(ffspec.start, ffspec.end - ffspec.start); + DynamicIntCount num_dynamic_ints = ffspec.num_dynamic_ints; + switch (ffspec.spec) { + case CONVSPEC_SIGNED_INT: + if constexpr (std::is_convertible_v) { + long long s = arg; + format_emit_long_long(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_UNSIGNED_INT: + if constexpr (std::is_convertible_v) { + unsigned long long s = arg; + format_emit_unsigned_long_long(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_DOUBLE: + if constexpr (std::is_convertible_v) { + double s = arg; + format_emit_double(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_CHAR_PTR: + if constexpr (std::is_convertible_v) { + const char *s = arg; + format_emit_char_ptr(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + if constexpr (std::is_convertible_v) { + const std::string &s = arg; + format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + if constexpr (std::is_convertible_v) { + const std::string_view &s = arg; + format_emit_string_view(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + case CONVSPEC_VOID_PTR: + if constexpr (std::is_convertible_v) { + const void *s = arg; + format_emit_void_ptr(result, spec, dynamic_ints, num_dynamic_ints, s); + return; + } + break; + default: + break; + } + YOSYS_ABORT("Internal error"); +} + +// Append the format string `fmt` to `result`, assuming there are no format conversion +// specifiers other than "%%" and therefore no arguments. Unescape "%%". +void format_emit_unescaped(std::string &result, std::string_view fmt); +std::string unescape_format_string(std::string_view fmt); + +inline void format_emit(std::string &result, std::string_view fmt, int fmt_start, + bool has_escapes, const FoundFormatSpec*, int*, DynamicIntCount) +{ + fmt = fmt.substr(fmt_start); + if (has_escapes) { + format_emit_unescaped(result, fmt); + } else { + result += fmt; + } +} +// Format `args` according to `fmt` (starting at `fmt_start`) and `specs` and append to `result`. +// `num_dynamic_ints` in `dynamic_ints[]` have already been collected to provide as +// dynamic width/precision args for the next format conversion specifier. +template +inline void format_emit(std::string &result, std::string_view fmt, int fmt_start, bool has_escapes, + const FoundFormatSpec* specs, int *dynamic_ints, DynamicIntCount num_dynamic_ints, + const Arg &arg, const Args &... args) +{ + if (specs->num_dynamic_ints > num_dynamic_ints) { + // Collect another int for the dynamic width precision/args + // for the next format conversion specifier. + if constexpr (std::is_convertible_v) { + dynamic_ints[static_cast(num_dynamic_ints)] = arg; + } else { + YOSYS_ABORT("Internal error"); + } + format_emit(result, fmt, fmt_start, has_escapes, specs, dynamic_ints, + static_cast(static_cast(num_dynamic_ints) + 1), args...); + return; + } + std::string_view str = fmt.substr(fmt_start, specs->start - fmt_start); + if (has_escapes) { + format_emit_unescaped(result, str); + } else { + result += str; + } + format_emit_one(result, fmt, *specs, dynamic_ints, arg); + format_emit(result, fmt, specs->end, has_escapes, specs + 1, dynamic_ints, DynamicIntCount::NONE, args...); +} + +template +inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec* specs, const Args &... args) +{ + std::string result; + int dynamic_ints[2] = { 0, 0 }; + format_emit(result, fmt, 0, has_escapes, specs, dynamic_ints, DynamicIntCount::NONE, args...); + return result; +} +template <> +inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec*) +{ + if (!has_escapes) { + return std::string(fmt); + } + return unescape_format_string(fmt); +} + +// This class parses format strings to build a list of `FoundFormatSpecs` in `specs`. +// When the compiler supports `consteval` (C++20), this parsing happens at compile time and +// type errors will be reported at compile time. Otherwise the parsing happens at +// runtime and type errors will trigger an `abort()` at runtime. +template +class FmtString +{ +public: + // Implicit conversion from const char * means that users can pass + // C string constants which are automatically parsed. + YOSYS_CONSTEVAL FmtString(const char *p) : fmt(p) + { + check_format(fmt, 0, &has_escapes, specs, DynamicIntCount::NONE); + } + std::string format(const Args &... args) + { + return format_emit_toplevel(fmt, has_escapes, specs, args...); + } +private: + std::string_view fmt; + bool has_escapes = false; + FoundFormatSpec specs[sizeof...(Args)] = {}; +}; + +template struct WrapType { using type = T; }; +template using TypeIdentity = typename WrapType::type; + +template +inline std::string stringf(FmtString...> fmt, Args... args) +{ + return fmt.format(args...); } int readsome(std::istream &f, char *s, int n); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index e5f3d773b..11ed09169 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -37,7 +37,7 @@ YOSYS_NAMESPACE_BEGIN bool RTLIL::IdString::destruct_guard_ok = false; RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; -dict RTLIL::IdString::global_id_index_; +std::unordered_map RTLIL::IdString::global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT std::vector RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_free_idx_list_; @@ -1149,6 +1149,12 @@ void RTLIL::Design::sort() it.second->sort(); } +void RTLIL::Design::sort_modules() +{ + scratchpad.sort(); + modules_.sort(sort_by_id_str()); +} + void RTLIL::Design::check() { #ifndef NDEBUG diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 5c8ccfac4..d78c6a4ba 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -23,6 +23,9 @@ #include "kernel/yosys_common.h" #include "kernel/yosys.h" +#include +#include + YOSYS_NAMESPACE_BEGIN namespace RTLIL @@ -122,7 +125,7 @@ struct RTLIL::IdString } destruct_guard; static std::vector global_id_storage_; - static dict global_id_index_; + static std::unordered_map global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT static std::vector global_refcount_storage_; static std::vector global_free_idx_list_; @@ -1376,6 +1379,7 @@ struct RTLIL::Design std::string scratchpad_get_string(const std::string &varname, const std::string &default_value = std::string()) const; void sort(); + void sort_modules(); void check(); void optimize(); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 094af13c4..195f14e4c 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -134,6 +134,15 @@ # define YS_COLD #endif +#ifdef __cpp_consteval +#define YOSYS_CONSTEVAL consteval +#else +// If we can't use consteval we can at least make it constexpr. +#define YOSYS_CONSTEVAL constexpr +#endif + +#define YOSYS_ABORT(s) abort() + #include "kernel/io.h" YOSYS_NAMESPACE_BEGIN diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index da06acedf..08dc76dda 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -20,6 +20,16 @@ #include "kernel/yosys.h" #include "backends/rtlil/rtlil_backend.h" +#if defined(_WIN32) +# include +# define WIFEXITED(x) 1 +# define WIFSIGNALED(x) 0 +# define WIFSTOPPED(x) 0 +# define WEXITSTATUS(x) ((x) & 0xff) +# define WTERMSIG(x) SIGTERM +# define WSTOPSIG(x) 0 +#endif + USING_YOSYS_NAMESPACE using namespace RTLIL_BACKEND; PRIVATE_NAMESPACE_BEGIN @@ -50,6 +60,10 @@ struct BugpointPass : public Pass { log(" -grep \"\"\n"); log(" only consider crashes that place this string in the log file.\n"); log("\n"); + log(" -expect-return \n"); + log(" only consider crashes that return the specified value. e.g. SEGFAULT\n"); + log(" returns a value of 139.\n"); + log("\n"); log(" -fast\n"); log(" run `proc_clean; clean -purge` after each minimization step. converges\n"); log(" faster, but produces larger testcases, and may fail to produce any\n"); @@ -60,6 +74,17 @@ struct BugpointPass : public Pass { log(" finishing. produces smaller and more useful testcases, but may fail to\n"); log(" produce any testcase at all if the crash is related to dangling wires.\n"); log("\n"); + log(" -runner \"\"\n"); + log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); + log("\n"); + log(" -err-grep \"\"\n"); + log(" only consider crashes that print this string on stderr. useful for\n"); + log(" errors outside of yosys.\n"); + log("\n"); + log(" -suffix \"\"\n"); + log(" add suffix to generated file names. useful when running more than one\n"); + log(" instance of bugpoint in the same directory. limited to 8 characters.\n"); + log("\n"); log("It is possible to constrain which parts of the design will be considered for\n"); log("removal. Unless one or more of the following options are specified, all parts\n"); log("will be considered.\n"); @@ -89,24 +114,39 @@ struct BugpointPass : public Pass { log(" -updates\n"); log(" try to remove process updates from syncs.\n"); log("\n"); - log(" -runner \"\"\n"); - log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); + log(" -wires\n"); + log(" try to remove wires. wires with a (* bugpoint_keep *) attribute will be\n"); + log(" skipped.\n"); log("\n"); } - bool run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg) + int run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg, string suffix, bool catch_err) { design->sort(); - std::ofstream f("bugpoint-case.il"); + string bugpoint_file = "bugpoint-case"; + if (suffix.size()) + bugpoint_file += stringf(".%.8s", suffix.c_str()); + + std::ofstream f(bugpoint_file + ".il"); RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false); f.close(); - string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str()); - return run_command(yosys_cmdline) == 0; + string yosys_cmdline = stringf("%s %s -qq -L %s.log %s %s.il", runner.c_str(), yosys_cmd.c_str(), bugpoint_file.c_str(), yosys_arg.c_str(), bugpoint_file.c_str()); + if (catch_err) yosys_cmdline += stringf(" 2>%s.err", bugpoint_file.c_str()); + auto status = run_command(yosys_cmdline); + // we're not processing lines, which means we're getting raw system() returns + if(WIFEXITED(status)) + return WEXITSTATUS(status); + else if(WIFSIGNALED(status)) + return WTERMSIG(status); + else if(WIFSTOPPED(status)) + return WSTOPSIG(status); + else + return 0; } - bool check_logfile(string grep) + bool check_logfile(string grep, string suffix, bool err=false) { if (grep.empty()) return true; @@ -114,7 +154,13 @@ struct BugpointPass : public Pass { if (grep.size() > 2 && grep.front() == '"' && grep.back() == '"') grep = grep.substr(1, grep.size() - 2); - std::ifstream f("bugpoint-case.log"); + string bugpoint_file = "bugpoint-case"; + if (suffix.size()) + bugpoint_file += stringf(".%.8s", suffix.c_str()); + bugpoint_file += err ? ".err" : ".log"; + + std::ifstream f(bugpoint_file); + while (!f.eof()) { string line; @@ -125,6 +171,11 @@ struct BugpointPass : public Pass { return false; } + bool check_logfiles(string grep, string err_grep, string suffix) + { + return check_logfile(grep, suffix) && check_logfile(err_grep, suffix, true); + } + RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) { if (!do_clean) @@ -399,7 +450,9 @@ struct BugpointPass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { - string yosys_cmd = "yosys", yosys_arg, grep, runner; + string yosys_cmd = "yosys", yosys_arg, grep, err_grep, runner, suffix; + bool flag_expect_return = false, has_check = false, check_err = false; + int expect_return_value = 0; bool fast = false, clean = false; bool modules = false, ports = false, cells = false, connections = false, processes = false, assigns = false, updates = false, wires = false, has_part = false; @@ -426,9 +479,25 @@ struct BugpointPass : public Pass { continue; } if (args[argidx] == "-grep" && argidx + 1 < args.size()) { + has_check = true; grep = args[++argidx]; continue; } + if (args[argidx] == "-err-grep" && argidx + 1 < args.size()) { + has_check = true; + check_err = true; + err_grep = args[++argidx]; + continue; + } + if (args[argidx] == "-expect-return") { + flag_expect_return = true; + ++argidx; + if (argidx >= args.size()) + log_cmd_error("No expected return value specified.\n"); + + expect_return_value = atoi(args[argidx].c_str()); + continue; + } if (args[argidx] == "-fast") { fast = true; continue; @@ -485,6 +554,14 @@ struct BugpointPass : public Pass { } continue; } + if (args[argidx] == "-suffix" && argidx + 1 < args.size()) { + suffix = args[++argidx]; + if (suffix.size() && suffix.at(0) == '"') { + log_assert(suffix.back() == '"'); + suffix = suffix.substr(1, suffix.size() - 2); + } + continue; + } break; } extra_args(args, argidx, design); @@ -492,6 +569,9 @@ struct BugpointPass : public Pass { if (yosys_arg.empty()) log_cmd_error("Missing -script or -command option.\n"); + if (flag_expect_return && expect_return_value == 0 && !has_check) + log_cmd_error("Nothing to match on for -expect-return 0; change value or use -grep.\n"); + if (!has_part) { modules = true; @@ -508,10 +588,15 @@ struct BugpointPass : public Pass { log_cmd_error("This command only operates on fully selected designs!\n"); RTLIL::Design *crashing_design = clean_design(design, clean); - if (run_yosys(crashing_design, runner, yosys_cmd, yosys_arg)) + int retval = run_yosys(crashing_design, runner, yosys_cmd, yosys_arg, suffix, check_err); + if (flag_expect_return && retval != expect_return_value) + log_cmd_error("The provided script file or command and Yosys binary returned value %d instead of expected %d on this design!\n", retval, expect_return_value); + if (!flag_expect_return && retval == 0) log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); - if (!check_logfile(grep)) + if (!check_logfile(grep, suffix)) log_cmd_error("The provided grep string is not found in the log file!\n"); + if (!check_logfile(err_grep, suffix, true)) + log_cmd_error("The provided grep string is not found in stderr log!\n"); int seed = 0; bool found_something = false, stage2 = false; @@ -521,21 +606,37 @@ struct BugpointPass : public Pass { { simplified = clean_design(simplified, fast, /*do_delete=*/true); - bool crashes; if (clean) { RTLIL::Design *testcase = clean_design(simplified); - crashes = !run_yosys(testcase, runner, yosys_cmd, yosys_arg); + retval = run_yosys(testcase, runner, yosys_cmd, yosys_arg, suffix, check_err); delete testcase; } else { - crashes = !run_yosys(simplified, runner, yosys_cmd, yosys_arg); + retval = run_yosys(simplified, runner, yosys_cmd, yosys_arg, suffix, check_err); } - if (crashes && check_logfile(grep)) + bool crashes = false; + if (flag_expect_return && retval == expect_return_value && check_logfiles(grep, err_grep, suffix)) + { + log("Testcase matches expected crash.\n"); + crashes = true; + } + else if (!flag_expect_return && retval == 0) + log("Testcase does not crash.\n"); + else if (!flag_expect_return && check_logfiles(grep, err_grep, suffix)) { log("Testcase crashes.\n"); + crashes = true; + } + else + // flag_expect_return && !(retval == expect_return_value && check_logfiles(grep, err_grep, suffix)) + // !flag_expect_return && !(retval == 0 && check_logfiles(grep, err_grep, suffix)) + log("Testcase does not match expected crash.\n"); + + if (crashes) + { if (crashing_design != design) delete crashing_design; crashing_design = simplified; @@ -543,7 +644,6 @@ struct BugpointPass : public Pass { } else { - log("Testcase does not crash.\n"); delete simplified; seed++; } diff --git a/passes/memory/memlib.md b/passes/memory/memlib.md index 855aa1345..f3c0dd937 100644 --- a/passes/memory/memlib.md +++ b/passes/memory/memlib.md @@ -148,7 +148,7 @@ The rules for this property are as follows: - for every available width, the width needs to be a multiple of the byte size, or the byte size needs to be larger than the width -- if the byte size is larger than the width, the byte enable signel is assumed +- if the byte size is larger than the width, the byte enable signal is assumed to be one bit wide and cover the whole port - otherwise, the byte enable signal has one bit for every `byte` bits of the data port @@ -176,7 +176,7 @@ Eg. for the following properties: The cost of a given cell will be assumed to be `(8 - 7) + 7 * (used_bits / 14)`. If `widthscale` is used, The pass will attach a `BITS_USED` parameter to mapped -calls, with a bitmask of which data bits of the memory are actually in use. +cells, with a bitmask of which data bits of the memory are actually in use. The parameter width will be the widest width in the `widths` property, and the bit correspondence is defined accordingly. @@ -193,7 +193,7 @@ one of the following values: - `zero`: the memory contents are zero, memories can be mapped to this cell iff their initialization value is entirely zero or undef - `any`: the memory contents can be arbitrarily selected, and the initialization - will be passes as the `INIT` parameter to the mapped cell + will be passed as the `INIT` parameter to the mapped cell - `no_undef`: like `any`, but only 0 and 1 bit values are supported (the pass will convert any x bits to 0) @@ -234,7 +234,7 @@ Ports come in 5 kinds: - `ar`: asynchronous read port - `sr`: synchronous read port - `sw`: synchronous write port -- `arsw`: simultanous synchronous write + asynchronous read with common address (commonly found in LUT RAMs) +- `arsw`: simultaneous synchronous write + asynchronous read with common address (commonly found in LUT RAMs) - `srsw`: synchronous write + synchronous read with common address The port properties available are: @@ -419,7 +419,7 @@ If not provided, `none` is assumed for all three properties. The `wrprio` property is only allowed on write ports and defines a priority relationship between port — when `wrprio "B";` is used in definition of port `"A"`, and both ports -simultanously write to the same memory cell, the value written by port `"A"` will have +simultaneously write to the same memory cell, the value written by port `"A"` will have precedence. This property is optional, and can be used multiple times as necessary. If no relationship @@ -493,7 +493,7 @@ will disallow combining the RAM option `ABC = 2` with port option `DEF = "GHI"`. ## Ifdefs -To allow reusing a library for multiple FPGA families with slighly differing +To allow reusing a library for multiple FPGA families with slightly differing capabilities, `ifdef` (and `ifndef`) blocks are provided: ifdef IS_FANCY_FPGA_WITH_CONFIGURABLE_ASYNC_RESET { diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index 2e9beb227..367f06ac4 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -24,11 +24,25 @@ #include #include #include +#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -IdString concat_name(RTLIL::Cell *cell, IdString object_name, const std::string &separator = ".") +template +[[nodiscard]] std::string concat_views(const Args&... views) { + static_assert((std::is_convertible_v && ...), + "All arguments must be convertible to std::string_view."); + const std::size_t total_size = (std::string_view(views).size() + ... + 0); + + std::string result; + result.reserve(total_size); + + (result.append(views), ...); + return result; +} + +IdString concat_name(RTLIL::Cell *cell, IdString const &object_name, const std::string &separator = ".") { return stringf("%s%s%s", cell->name.c_str(), separator.c_str(), object_name.c_str() + 1); } diff --git a/passes/tests/Makefile.inc b/passes/tests/Makefile.inc index 531943d78..25e11967c 100644 --- a/passes/tests/Makefile.inc +++ b/passes/tests/Makefile.inc @@ -2,4 +2,5 @@ OBJS += passes/tests/test_autotb.o OBJS += passes/tests/test_cell.o OBJS += passes/tests/test_abcloop.o +OBJS += passes/tests/raise_error.o diff --git a/passes/tests/raise_error.cc b/passes/tests/raise_error.cc new file mode 100644 index 000000000..f9055e6d2 --- /dev/null +++ b/passes/tests/raise_error.cc @@ -0,0 +1,83 @@ +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct RaiseErrorPass : public Pass { + RaiseErrorPass() : Pass("raise_error", "raise errors from attributes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" raise_error [options] [selection]\n"); + log("\n"); + log("Test error handling by raising arbitrary errors. This pass iterates over the\n"); + log("design (or selection of it) checking for objects with the 'raise_error'\n"); + log("attribute set. Assigning 'raise_error' to a string more than one character long\n"); + log("will log that string as an error message before exiting. Assigning 'raise_error'\n"); + log("to an integer (less than 256) will exit with that value as the exit code.\n"); + log("\n"); + log(" -stderr\n"); + log(" Log error messages directly to stderr instead of using 'log_error'.\n"); + log("\n"); + } + void execute(vector args, RTLIL::Design *design) override + { + log_header(design, "Executing RAISE_ERROR pass.\n"); + + bool use_stderr = false; + + int argidx; + for (argidx = 1; argidx < GetSize(args); argidx++) + { + if (args[argidx] == "-stderr") { + use_stderr = true; + continue; + } + break; + } + + extra_args(args, argidx, design, true); + + RTLIL::NamedObject *err_obj = nullptr; + + for (auto mod : design->all_selected_modules()) { + if (mod->has_attribute(ID::raise_error)) { + err_obj = mod->clone(); + break; + } + for (auto memb : mod->selected_members()) { + if (memb->has_attribute(ID::raise_error)) { + err_obj = memb; + break; + } + } + if (err_obj != nullptr) break; + } + + if (err_obj != nullptr) { + log("Raising error from '%s'.\n", log_id(err_obj)); + auto err_no = err_obj->attributes[ID::raise_error].as_int(); + if (err_no < 256) { + log_flush(); + } else { + auto err_msg = err_obj->get_string_attribute(ID::raise_error); + if (use_stderr) { + std::cerr << err_msg << std::endl; + err_no = 1; + } else { + log_error("%s\n", err_msg.c_str()); + } + } + #if defined(_MSC_VER) + _exit(err_no); + #else + _Exit(err_no); + #endif + } else { + log("'raise_error' attribute not found\n"); + } + } +} RaiseErrorPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 0d9c28fde..7cb771190 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -1,7 +1,6 @@ bram1_cmp bram1.mk bram1_[0-9]*/ -bram2.log bram2_syn.v bram2_tb dsp_work*/ diff --git a/tests/opt/.gitignore b/tests/.gitignore similarity index 60% rename from tests/opt/.gitignore rename to tests/.gitignore index 8355de9dc..d8e01e026 100644 --- a/tests/opt/.gitignore +++ b/tests/.gitignore @@ -1,2 +1,4 @@ *.log +*.out +*.err run-test.mk diff --git a/tests/aiger/.gitignore b/tests/aiger/.gitignore index 54b4a279b..4bb3e67f6 100644 --- a/tests/aiger/.gitignore +++ b/tests/aiger/.gitignore @@ -1,3 +1,2 @@ /*_ref.v -/*.log /neg.out/ diff --git a/tests/arch/anlogic/.gitignore b/tests/arch/anlogic/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/anlogic/.gitignore +++ b/tests/arch/anlogic/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/ecp5/.gitignore b/tests/arch/ecp5/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/ecp5/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/efinix/.gitignore b/tests/arch/efinix/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/arch/efinix/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/arch/fabulous/.gitignore b/tests/arch/fabulous/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/fabulous/.gitignore +++ b/tests/arch/fabulous/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/gatemate/.gitignore b/tests/arch/gatemate/.gitignore index 9a71dca69..d9f7e276c 100644 --- a/tests/arch/gatemate/.gitignore +++ b/tests/arch/gatemate/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/gowin/.gitignore b/tests/arch/gowin/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/arch/gowin/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/arch/ice40/.gitignore b/tests/arch/ice40/.gitignore index 54f908bdb..a86b36792 100644 --- a/tests/arch/ice40/.gitignore +++ b/tests/arch/ice40/.gitignore @@ -1,5 +1,3 @@ -*.log *.json -/run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/intel_alm/.gitignore b/tests/arch/intel_alm/.gitignore deleted file mode 100644 index ba42e1ee6..000000000 --- a/tests/arch/intel_alm/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.log -/run-test.mk diff --git a/tests/arch/machxo2/.gitignore b/tests/arch/machxo2/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/machxo2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/microchip/.gitignore b/tests/arch/microchip/.gitignore index 9c0a77944..aae26dc02 100644 --- a/tests/arch/microchip/.gitignore +++ b/tests/arch/microchip/.gitignore @@ -1,4 +1,2 @@ -*.log -/run-test.mk *.vm diff --git a/tests/arch/nanoxplore/.gitignore b/tests/arch/nanoxplore/.gitignore deleted file mode 100644 index 1d329c933..000000000 --- a/tests/arch/nanoxplore/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -/run-test.mk diff --git a/tests/arch/nexus/.gitignore b/tests/arch/nexus/.gitignore deleted file mode 100644 index ba42e1ee6..000000000 --- a/tests/arch/nexus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.log -/run-test.mk diff --git a/tests/arch/quicklogic/.gitignore b/tests/arch/quicklogic/.gitignore index ae20ed342..d9f7e276c 100644 --- a/tests/arch/quicklogic/.gitignore +++ b/tests/arch/quicklogic/.gitignore @@ -1,4 +1,2 @@ -*.log -run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/xilinx/.gitignore b/tests/arch/xilinx/.gitignore index c99b79371..15a12d049 100644 --- a/tests/arch/xilinx/.gitignore +++ b/tests/arch/xilinx/.gitignore @@ -1,5 +1,3 @@ -/*.log -/*.out /run-test.mk /*_uut.v /test_macc diff --git a/tests/asicworld/.gitignore b/tests/asicworld/.gitignore deleted file mode 100644 index 073f46157..000000000 --- a/tests/asicworld/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -*.out diff --git a/tests/bind/.gitignore b/tests/bind/.gitignore deleted file mode 100644 index 8355de9dc..000000000 --- a/tests/bind/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -run-test.mk diff --git a/tests/blif/.gitignore b/tests/blif/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/blif/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/bugpoint/.gitignore b/tests/bugpoint/.gitignore new file mode 100644 index 000000000..1012c66cc --- /dev/null +++ b/tests/bugpoint/.gitignore @@ -0,0 +1,5 @@ +bugpoint-case.* +*.log +*.err +*.temp +run-test.mk diff --git a/tests/bugpoint/failures.ys b/tests/bugpoint/failures.ys new file mode 100644 index 000000000..ce8daa8cc --- /dev/null +++ b/tests/bugpoint/failures.ys @@ -0,0 +1,29 @@ +write_file fail.temp << EOF +logger -expect error "Missing -script or -command option." 1 +bugpoint -suffix fail -yosys ../../yosys +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "do not crash on this design" 1 +bugpoint -suffix fail -yosys ../../yosys -command "dump" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "returned value 3 instead of expected 7" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -expect-return 7 +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "not found in the log file!" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -grep "nope" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp + +write_file fail.temp << EOF +logger -expect error "not found in stderr log!" 1 +bugpoint -suffix fail -yosys ../../yosys -command raise_error -err-grep "nope" +EOF +exec -expect-return 0 -- ../../yosys -qq mods.il -s fail.temp diff --git a/tests/bugpoint/mod_constraints.ys b/tests/bugpoint/mod_constraints.ys new file mode 100644 index 000000000..f35095510 --- /dev/null +++ b/tests/bugpoint/mod_constraints.ys @@ -0,0 +1,83 @@ +read_rtlil mods.il +select -assert-count 7 w:* +select -assert-mod-count 3 =* +select -assert-count 4 c:* +design -stash base + +# everything is removed by default +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# don't remove wires +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -modules -cells +select -assert-count 3 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# don't remove cells or their connections +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -modules +select -assert-count 5 w:* +select -assert-mod-count 1 =* +select -assert-count 4 c:* + +# don't remove cells but do remove their connections +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -modules -connections +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-count 4 c:* + +# don't remove modules +design -load base +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 -wires -cells +select -assert-count 1 w:* +select -assert-mod-count 3 =* +select -assert-none c:* + +# can keep wires +design -load base +setattr -set bugpoint_keep 1 w:w_b +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 2 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# a wire with keep won't keep the cell/module containing it +design -load base +setattr -set bugpoint_keep 1 w:w_o +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-none c:* + +# can keep cells (and do it without the associated module) +design -load base +setattr -set bugpoint_keep 1 c:c_a +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 1 =* +select -assert-count 1 c:* + +# can keep modules +design -load base +setattr -mod -set bugpoint_keep 1 m_a +bugpoint -suffix mods -yosys ../../yosys -command raise_error -expect-return 3 +select -assert-count 1 w:* +select -assert-mod-count 2 =* +select -assert-none c:* + +# minimize to just the path connecting w_a and w_c +# which happens via w_b, w_i, w_o, m_a, c_a and c_b +write_file script.temp << EOF +select -assert-none w:w_a %co* w:w_c %ci* %i +EOF +design -load base +bugpoint -suffix mods -yosys ../../yosys -script script.temp -grep "Assertion failed" +select -assert-count 5 w:* +select -assert-mod-count 2 =* +select -assert-count 2 c:* diff --git a/tests/bugpoint/mods.il b/tests/bugpoint/mods.il new file mode 100644 index 000000000..fe3ce6522 --- /dev/null +++ b/tests/bugpoint/mods.il @@ -0,0 +1,38 @@ +module \m_a + wire input 1 \w_i + wire output 2 \w_o + connect \w_o \w_i +end + +module \m_b + wire input 1 \w_i + wire output 2 \w_o +end + +attribute \top 1 +module \top + attribute \raise_error 3 + wire \w_a + wire \w_b + wire \w_c + + cell \m_a \c_a + connect \w_i \w_a + connect \w_o \w_b + end + + cell \m_a \c_b + connect \w_i \w_b + connect \w_o \w_c + end + + cell \m_b \c_c + connect \w_i \w_c + connect \w_o \w_a + end + + cell \m_b \c_d + connect \w_i 1'0 + connect \w_o 1'1 + end +end diff --git a/tests/bugpoint/proc_constraints.ys b/tests/bugpoint/proc_constraints.ys new file mode 100644 index 000000000..22b8b3c60 --- /dev/null +++ b/tests/bugpoint/proc_constraints.ys @@ -0,0 +1,49 @@ +read_rtlil procs.il +select -assert-count 2 p:* +design -stash err_q + +# processes get removed by default +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 +select -assert-none p:* + +# individual processes can be kept +design -load err_q +setattr -set bugpoint_keep 1 p:proc_a +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 +select -assert-count 1 p:* + +# all processes can be kept +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -wires +select -assert-count 2 p:* + +# d and clock are connected after proc +design -load err_q +proc +select -assert-count 3 w:d %co +select -assert-count 3 w:clock %co + +# no assigns means no d +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -assigns +proc +select -assert-count 1 w:d %co + +# no updates means no clock +design -load err_q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -updates +proc +select -assert-count 1 w:clock %co + +# can remove ports +design -load err_q +select -assert-count 5 x:* +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -ports +select -assert-none x:* + +# can keep ports +design -load err_q +setattr -set bugpoint_keep 1 i:d o:q +bugpoint -suffix procs -yosys ../../yosys -command raise_error -expect-return 4 -ports +select -assert-count 2 x:* diff --git a/tests/bugpoint/procs.il b/tests/bugpoint/procs.il new file mode 100644 index 000000000..cb9f7c8dd --- /dev/null +++ b/tests/bugpoint/procs.il @@ -0,0 +1,42 @@ +module \ff_with_en_and_sync_reset + wire $0\q[1:1] + wire $0\q[0:0] + attribute \raise_error 4 + wire width 2 output 5 \q + wire width 2 input 4 \d + wire input 3 \enable + wire input 2 \reset + wire input 1 \clock + + process \proc_a + assign $0\q[0:0] \q [0] + switch \reset + case 1'1 + assign $0\q[0:0] 1'0 + case + switch \enable + case 1'1 + assign $0\q[0:0] \d [0] + case + end + end + sync posedge \clock + update \q [0] $0\q[0:0] + end + + process \proc_b + assign $0\q[1:1] \q [1] + switch \reset + case 1'1 + assign $0\q[1:1] 1'0 + case + switch \enable + case 1'1 + assign $0\q[1:1] \d [1] + case + end + end + sync posedge \clock + update \q [1] $0\q[1:1] + end +end diff --git a/tests/bugpoint/raise_error.ys b/tests/bugpoint/raise_error.ys new file mode 100644 index 000000000..a0a03f447 --- /dev/null +++ b/tests/bugpoint/raise_error.ys @@ -0,0 +1,43 @@ +read_verilog -noblackbox << EOF +(* raise_error=7 *) +module top(); +endmodule + +(* raise_error="help me" *) +module other(); +endmodule + +(* raise_error *) +module def(); +endmodule +EOF +select -assert-mod-count 3 =* +design -stash read + +# raise_error with int exits with status +design -load read +bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 7 +select -assert-mod-count 1 =* +select -assert-mod-count 1 top + +# raise_error with string prints message and exits with 1 +design -load read +rename top abc +bugpoint -suffix error -yosys ../../yosys -command raise_error -grep "help me" -expect-return 1 +select -assert-mod-count 1 =* +select -assert-mod-count 1 other + +# raise_error with no value exits with 1 +design -load read +rename def zzy +delete other +bugpoint -suffix error -yosys ../../yosys -command raise_error -expect-return 1 +select -assert-mod-count 1 =* +select -assert-mod-count 1 zzy + +# raise_error -stderr prints to stderr and exits with 1 +design -load read +rename top abc +bugpoint -suffix error -yosys ../../yosys -command "raise_error -stderr" -err-grep "help me" -expect-return 1 +select -assert-mod-count 1 =* +select -assert-mod-count 1 other diff --git a/tests/bugpoint/run-test.sh b/tests/bugpoint/run-test.sh new file mode 100755 index 000000000..006c731e3 --- /dev/null +++ b/tests/bugpoint/run-test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu +source ../gen-tests-makefile.sh +generate_mk --yosys-scripts diff --git a/tests/fmt/.gitignore b/tests/fmt/.gitignore index a36a15ec4..e6ae0d879 100644 --- a/tests/fmt/.gitignore +++ b/tests/fmt/.gitignore @@ -1,3 +1,2 @@ -*.log iverilog-* yosys-* diff --git a/tests/hana/.gitignore b/tests/hana/.gitignore deleted file mode 100644 index 073f46157..000000000 --- a/tests/hana/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -*.out diff --git a/tests/liberty/.gitignore b/tests/liberty/.gitignore index 2ee56e9d1..b312f8c50 100644 --- a/tests/liberty/.gitignore +++ b/tests/liberty/.gitignore @@ -1,3 +1,2 @@ -*.log /*.filtered *.verilogsim diff --git a/tests/lut/.gitignore b/tests/lut/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/lut/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/memlib/.gitignore b/tests/memlib/.gitignore index 03dbe82ae..bfa1a801e 100644 --- a/tests/memlib/.gitignore +++ b/tests/memlib/.gitignore @@ -1,5 +1,2 @@ -t_*.log -t_*.out t_*.v t_*.ys -run-test.mk diff --git a/tests/memories/.gitignore b/tests/memories/.gitignore index 90a0983a6..14e98cead 100644 --- a/tests/memories/.gitignore +++ b/tests/memories/.gitignore @@ -1,3 +1 @@ -*.log -*.out *.dmp diff --git a/tests/peepopt/.gitignore b/tests/peepopt/.gitignore deleted file mode 100644 index 50e13221d..000000000 --- a/tests/peepopt/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.log diff --git a/tests/proc/.gitignore b/tests/proc/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/proc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/rpc/.gitignore b/tests/rpc/.gitignore deleted file mode 100644 index 397b4a762..000000000 --- a/tests/rpc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.log diff --git a/tests/sat/.gitignore b/tests/sat/.gitignore index 664425d73..3fa128fcc 100644 --- a/tests/sat/.gitignore +++ b/tests/sat/.gitignore @@ -1,4 +1,2 @@ -*.log -run-test.mk *.vcd *.fst diff --git a/tests/select/.gitignore b/tests/select/.gitignore deleted file mode 100644 index 50e13221d..000000000 --- a/tests/select/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.log diff --git a/tests/sim/.gitignore b/tests/sim/.gitignore index 2c96b65f8..769b314cd 100644 --- a/tests/sim/.gitignore +++ b/tests/sim/.gitignore @@ -1,6 +1,3 @@ -*.log -/run-test.mk +*_synth.v +*_testbench -*.out *.fst diff --git a/tests/simple/.gitignore b/tests/simple/.gitignore deleted file mode 100644 index 5daaadbd7..000000000 --- a/tests/simple/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.out -*.err diff --git a/tests/simple_abc9/.gitignore b/tests/simple_abc9/.gitignore index fda60e577..e31b7dc7f 100644 --- a/tests/simple_abc9/.gitignore +++ b/tests/simple_abc9/.gitignore @@ -1,5 +1,3 @@ *.v *.sv -*.log -*.out *.bak diff --git a/tests/svtypes/.gitignore b/tests/svtypes/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/svtypes/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore deleted file mode 100644 index 56c9ba8f9..000000000 --- a/tests/techmap/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.out -/*.mk diff --git a/tests/various/.gitignore b/tests/various/.gitignore index 0e79d092f..1ae3b5b30 100644 --- a/tests/various/.gitignore +++ b/tests/various/.gitignore @@ -4,7 +4,6 @@ /*.sel /write_gzip.v /write_gzip.v.gz -/run-test.mk /plugin.so /plugin.so.dSYM /temp diff --git a/tests/verific/.gitignore b/tests/verific/.gitignore deleted file mode 100644 index b48f808a1..000000000 --- a/tests/verific/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/*.log -/*.out -/run-test.mk diff --git a/tests/verilog/.gitignore b/tests/verilog/.gitignore index cfd72076e..3702f10cf 100644 --- a/tests/verilog/.gitignore +++ b/tests/verilog/.gitignore @@ -1,7 +1,3 @@ -/*.log -/*.out -/*.err -/run-test.mk /const_arst.v /const_sr.v /doubleslash.v