diff options
51 files changed, 2182 insertions, 267 deletions
diff --git a/.gitignore b/.gitignore index 4576d54d6..191b95992 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ qtcreator.config qtcreator.creator qtcreator.creator.user Makefile.conf -filterlib yosys yosys-config +yosys-filterlib +yosys-svgviewer @@ -3,6 +3,10 @@ CONFIG := clang-debug # CONFIG := gcc-debug # CONFIG := release +ENABLE_TCL := 1 +ENABLE_QT4 := 1 +ENABLE_GPROF := 0 + OBJS = kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/select.o kernel/show.o OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o @@ -12,6 +16,7 @@ OBJS += libs/sha1/sha1.o OBJS += libs/subcircuit/subcircuit.o GENFILES = +EXTRA_TARGETS = TARGETS = yosys yosys-config all: top-all @@ -37,12 +42,26 @@ CXX = gcc CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG endif +ifeq ($(ENABLE_TCL),1) +CXXFLAGS += -I/usr/include/tcl8.5 -DYOSYS_ENABLE_TCL +LDLIBS += -ltcl8.5 +endif + +ifeq ($(ENABLE_GPROF),1) +CXXFLAGS += -pg +LDFLAGS += -pg +endif + +ifeq ($(ENABLE_QT4),1) +TARGETS += yosys-svgviewer +endif + include frontends/*/Makefile.inc include passes/*/Makefile.inc include backends/*/Makefile.inc include techlibs/Makefile.inc -top-all: $(TARGETS) +top-all: $(TARGETS) $(EXTRA_TARGETS) yosys: $(OBJS) $(CXX) -o yosys $(LDFLAGS) $(OBJS) $(LDLIBS) @@ -51,18 +70,22 @@ yosys-config: yosys-config.in sed 's,@CXX@,$(CXX),; s,@CXXFLAGS@,$(CXXFLAGS),; s,@LDFLAGS@,$(LDFLAGS),; s,@LDLIBS@,$(LDLIBS),;' < yosys-config.in > yosys-config chmod +x yosys-config +yosys-svgviewer: libs/svgviewer/*.h libs/svgviewer/*.cpp + cd libs/svgviewer && qmake-qt4 && make + cp libs/svgviewer/svgviewer yosys-svgviewer + test: yosys cd tests/simple && bash run-test.sh cd tests/hana && bash run-test.sh cd tests/asicworld && bash run-test.sh -install: yosys - install yosys /usr/local/bin/yosys - install yosys-config /usr/local/bin/yosys-config +install: $(TARGETS) + install $(TARGETS) /usr/local/bin/ clean: rm -f $(OBJS) $(GENFILES) $(TARGETS) rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d + test ! -f libs/svgviewer/Makefile || make -C libs/svgviewer distclean mrproper: clean git clean -xdf @@ -86,6 +109,10 @@ config-gcc-debug: clean config-release: clean echo 'CONFIG := release' > Makefile.conf +config-gprof: clean + echo 'CONFIG := release' > Makefile.conf + echo 'ENABLE_GPROF := 1' >> Makefile.conf + -include libs/*/*.d -include frontends/*/*.d -include passes/*/*.d @@ -88,9 +88,13 @@ some simple optimizations: yosys> proc; opt -display design netlist using 'gv' as postscript viewer: +display design netlist using the yosys svg viewer: - yosys> show -viewer gv + yosys> show + +the same thing using 'gv' as postscript viewer: + + yosys> show -format ps -viewer gv translating netlist to gate logic and perform some simple optimizations: @@ -205,6 +209,12 @@ Verilog Attributes and non-standard features temporary variable within an always block. This is mostly used internally by yosys to synthesize verilog functions and access arrays. +- The "placeholder" attribute on modules is used to mark empty stub modules + that have the same ports as the real thing but do not contain information + on the internal configuration. This modules are only used by the synthesis + passes to identify input and output ports of cells. The verilog backend + also does not output placeholder modules on default. + - In addition to the (* ... *) attribute syntax, yosys supports the non-standard {* ... *} attribute syntax to set default attributes for everything that comes after the {* ... *} statement. (Reset @@ -240,8 +250,8 @@ TODOs / Open Bugs - Actually use range information on parameters - Add brief source code documentation to most passes and kernel code - Implement mux-to-tribuf pass and rebalance mixed mux/tribuf trees - - Add commands 'delete' (remove objects) and 'attr' (get, set and remove attributes) - - TCL and Python interfaces to frontends, passes, backends and RTLIL + - Add 'edit' command for changing the design (delete, add, modify objects) + - Improve TCL support and add 'list' command for inspecting the design from TCL - Additional internal cell types: $pla and $lut - Support for registering designs (as collection of modules) to CellTypes - Smarter resource sharing pass (add MUXes and get rid of duplicated cells) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index a4713cb0a..04a3c7643 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -923,6 +923,11 @@ struct VerilogBackend : public Backend { log(" without this option all internal cells are converted to verilog\n"); log(" expressions.\n"); log("\n"); + log(" -placeholders\n"); + log(" usually modules with the 'placeholder' attribute are ignored. with\n"); + log(" this option set only the modules with the 'placeholder' attribute\n"); + log(" are written to the output file.\n"); + log("\n"); } virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { @@ -933,6 +938,8 @@ struct VerilogBackend : public Backend { attr2comment = false; noexpr = false; + bool placeholders = false; + reg_ct.clear(); reg_ct.setup_stdcells_mem(); reg_ct.cell_types.insert("$sr"); @@ -958,16 +965,21 @@ struct VerilogBackend : public Backend { noexpr = true; continue; } + if (arg == "-placeholders") { + placeholders = true; + continue; + } break; } extra_args(f, filename, args, argidx); - for (auto it = design->modules.begin(); it != design->modules.end(); it++) { - log("Dumping module `%s'.\n", it->first.c_str()); - if (it != design->modules.begin()) - fprintf(f, "\n"); - dump_module(f, "", it->second); - } + for (auto it = design->modules.begin(); it != design->modules.end(); it++) + if ((it->second->attributes.count("\\placeholder") > 0) == placeholders) { + if (it != design->modules.begin()) + fprintf(f, "\n"); + log("Dumping module `%s'.\n", it->first.c_str()); + dump_module(f, "", it->second); + } reg_ct.clear(); } diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index d35ea4171..2c552ea22 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -46,11 +46,12 @@ namespace AST { // instanciate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg; + bool flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; RTLIL::SigSpec *genRTLIL_subst_from = NULL; RTLIL::SigSpec *genRTLIL_subst_to = NULL; + RTLIL::SigSpec ignoreThisSignalsInInitial; AstNode *current_top_block, *current_block, *current_block_child; AstModule *current_module; } @@ -122,6 +123,7 @@ std::string AST::type2str(AstNodeType type) X(AST_CELL) X(AST_PRIMITIVE) X(AST_ALWAYS) + X(AST_INITIAL) X(AST_BLOCK) X(AST_ASSIGN_EQ) X(AST_ASSIGN_LE) @@ -417,6 +419,14 @@ void AstNode::dumpVlog(FILE *f, std::string indent) } break; + case AST_INITIAL: + fprintf(f, "%s" "initial\n", indent.c_str()); + for (auto child : children) { + if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) + child->dumpVlog(f, indent + " "); + } + break; + case AST_POSEDGE: case AST_NEGEDGE: case AST_EDGE: @@ -679,10 +689,25 @@ static AstModule* process_module(AstNode *ast) log("--- END OF AST DUMP ---\n"); } + if (flag_lib) { + std::vector<AstNode*> new_children; + for (auto child : ast->children) { + if (child->type == AST_WIRE && (child->is_input || child->is_output)) + new_children.push_back(child); + else + delete child; + } + ast->children.swap(new_children); + ast->attributes["\\placeholder"] = AstNode::mkconst_int(0, false, 0); + } + current_module = new AstModule; current_module->ast = NULL; current_module->name = ast->str; current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); + + ignoreThisSignalsInInitial = RTLIL::SigSpec(); + for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) log_error("Attribute `%s' with non-constant value at %s:%d!\n", @@ -697,19 +722,30 @@ static AstModule* process_module(AstNode *ast) } for (size_t i = 0; i < ast->children.size(); i++) { AstNode *node = ast->children[i]; - if (node->type != AST_WIRE && node->type != AST_MEMORY) + if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) node->genRTLIL(); } + ignoreThisSignalsInInitial.sort_and_unify(); + + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type == AST_INITIAL) + node->genRTLIL(); + } + + ignoreThisSignalsInInitial = RTLIL::SigSpec(); + current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; current_module->nomem2reg = flag_nomem2reg; current_module->mem2reg = flag_mem2reg; + current_module->lib = flag_lib; return current_module; } // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ast_diff, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ast_diff, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib) { current_ast = ast; flag_dump_ast = dump_ast; @@ -718,6 +754,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast, bool dump_ flag_nolatches = nolatches; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; + flag_lib = lib; assert(current_ast->type == AST_DESIGN); for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { @@ -747,8 +784,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin flag_nolatches = nolatches; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; + flag_lib = lib; use_internal_line_num(); + std::string para_info; std::vector<unsigned char> hash_data; hash_data.insert(hash_data.end(), name.begin(), name.end()); hash_data.push_back(0); @@ -762,9 +801,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin continue; para_counter++; std::string para_id = child->str; - if (parameters.count(child->str) > 0) { + if (parameters.count(para_id) > 0) { log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str]))); rewrite_parameter: + para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); child->delete_children(); child->children.push_back(AstNode::mkconst_bits(parameters[para_id].bits, false)); hash_data.insert(hash_data.end(), child->str.begin(), child->str.end()); @@ -774,10 +814,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin parameters.erase(para_id); continue; } - char buf[100]; - snprintf(buf, 100, "$%d", para_counter); - if (parameters.count(buf) > 0) { - para_id = buf; + para_id = stringf("$%d", para_counter); + if (parameters.count(para_id) > 0) { log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); goto rewrite_parameter; } @@ -785,17 +823,26 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin if (parameters.size() > 0) log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str()); - unsigned char hash[20]; - unsigned char *hash_data2 = new unsigned char[hash_data.size()]; - for (size_t i = 0; i < hash_data.size(); i++) - hash_data2[i] = hash_data[i]; - sha1::calc(hash_data2, hash_data.size(), hash); - delete[] hash_data2; + std::string modname; + + if (para_info.size() > 60) + { + unsigned char hash[20]; + unsigned char *hash_data2 = new unsigned char[hash_data.size()]; + for (size_t i = 0; i < hash_data.size(); i++) + hash_data2[i] = hash_data[i]; + sha1::calc(hash_data2, hash_data.size(), hash); + delete[] hash_data2; - char hexstring[41]; - sha1::toHexString(hash, hexstring); + char hexstring[41]; + sha1::toHexString(hash, hexstring); - std::string modname = "$paramod$" + std::string(hexstring) + "$" + name; + modname = "$paramod$" + std::string(hexstring) + name; + } + else + { + modname = "$paramod" + name + para_info; + } if (design->modules.count(modname) == 0) { new_ast->str = modname; @@ -821,6 +868,7 @@ void AstModule::update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes) flag_nolatches = nolatches; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; + flag_lib = lib; use_internal_line_num(); for (auto it = auto_sizes.begin(); it != auto_sizes.end(); it++) { diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index d65851acd..acf10f9ad 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -103,6 +103,7 @@ namespace AST AST_CELL, AST_PRIMITIVE, AST_ALWAYS, + AST_INITIAL, AST_BLOCK, AST_ASSIGN_EQ, AST_ASSIGN_LE, @@ -189,13 +190,13 @@ namespace AST }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast = false, bool dump_ast_diff = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false, bool mem2reg = false); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast = false, bool dump_ast_diff = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false, bool mem2reg = false, bool lib = false); // parametric modules are supported directly by the AST library // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomem2reg, mem2reg; + bool nolatches, nomem2reg, mem2reg, lib; virtual ~AstModule(); virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters); virtual void update_auto_wires(std::map<RTLIL::IdString, int> auto_sizes); @@ -217,10 +218,10 @@ namespace AST namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast, flag_dump_ast_diff, flag_nolatches, flag_nomem2reg, flag_mem2reg; + extern bool flag_dump_ast, flag_dump_ast_diff, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; - extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to; + extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to, ignoreThisSignalsInInitial; extern AST::AstNode *current_top_block, *current_block, *current_block_child; extern AST::AstModule *current_module; struct ProcessGenerator; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 0654db2df..cb57bbab7 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -183,7 +183,9 @@ struct AST_INTERNAL::ProcessGenerator { // input and output structures AstNode *always; + RTLIL::SigSpec skipSyncSignals; RTLIL::Process *proc; + const RTLIL::SigSpec &outputSignals; // This always points to the RTLIL::CaseRule beeing filled at the moment RTLIL::CaseRule *current_case; @@ -205,7 +207,7 @@ struct AST_INTERNAL::ProcessGenerator // map helps generating nice numbered names for all this temporary signals. std::map<RTLIL::Wire*, int> new_temp_count; - ProcessGenerator(AstNode *always) : always(always) + ProcessGenerator(AstNode *always, RTLIL::SigSpec skipSyncSignalsArg = RTLIL::SigSpec()) : always(always), skipSyncSignals(skipSyncSignalsArg), outputSignals(subst_lvalue_from) { // generate process and simple root case proc = new RTLIL::Process; @@ -310,6 +312,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_COND: case AST_ALWAYS: + case AST_INITIAL: for (auto child : ast->children) if (child->type == AST_BLOCK) collect_lvalues(reg, child, type_eq, type_le, false); @@ -334,9 +337,9 @@ struct AST_INTERNAL::ProcessGenerator reg.sort_and_unify(); } - // remove all assignments to the given signal pattern in a case and all its children - // when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this - // function is acalled to clean up the first two assignments as they are overwritten by + // remove all assignments to the given signal pattern in a case and all its children. + // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this + // function is called to clean up the first two assignments as they are overwritten by // the third assignment. void removeSignalFromCaseTree(RTLIL::SigSpec pattern, RTLIL::CaseRule *cs) { @@ -350,8 +353,10 @@ struct AST_INTERNAL::ProcessGenerator // add an assignment (aka "action") but split it up in chunks. this way huge assignments // are avoided and the generated $mux cells have a more "natural" size. - void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool noSyncToUndef = false) + void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false) { + if (inSyncRule) + lvalue.remove2(skipSyncSignals, &rvalue); assert(lvalue.width == rvalue.width); lvalue.optimize(); rvalue.optimize(); @@ -360,7 +365,7 @@ struct AST_INTERNAL::ProcessGenerator for (size_t i = 0; i < lvalue.chunks.size(); i++) { RTLIL::SigSpec lhs = lvalue.chunks[i]; RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue.chunks[i].width); - if (noSyncToUndef && lvalue.chunks[i].wire && lvalue.chunks[i].wire->attributes.count("\\nosync")) + if (inSyncRule && lvalue.chunks[i].wire && lvalue.chunks[i].wire->attributes.count("\\nosync")) rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.width); actions.push_back(RTLIL::SigSig(lhs, rhs)); offset += lhs.width; @@ -456,7 +461,7 @@ struct AST_INTERNAL::ProcessGenerator } else if (node->type == AST_BLOCK) { processAst(node); } else if (!generated_default_case) - current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width)); + current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width, &subst_rvalue_from, &subst_rvalue_to)); } sw->cases.push_back(current_case); current_case = backup_case; @@ -528,6 +533,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint) case AST_LOCALPARAM: case AST_GENVAR: case AST_GENFOR: + case AST_GENBLOCK: case AST_GENIF: break; @@ -1015,6 +1021,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint) case AST_ALWAYS: { AstNode *always = this->clone(); ProcessGenerator generator(always); + ignoreThisSignalsInInitial.append(generator.outputSignals); + delete always; + } break; + + case AST_INITIAL: { + AstNode *always = this->clone(); + ProcessGenerator generator(always, ignoreThisSignalsInInitial); delete always; } break; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 887b62f2c..bf0f9e63f 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -182,6 +182,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) break; if (type == AST_GENIF && i >= 1) break; + if (type == AST_GENBLOCK) + break; if (type == AST_PREFIX && i >= 1) break; while (did_something_here && i < children.size()) { @@ -194,7 +196,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) current_block = this; current_block_child = children[i]; } - if (type == AST_ALWAYS && children[i]->type == AST_BLOCK) + if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) current_top_block = children[i]; did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage); if (did_something_here) @@ -409,6 +411,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) did_something = true; } + // simplify unconditional generate block + if (type == AST_GENBLOCK && children.size() != 0) + { + if (!str.empty()) { + std::map<std::string, std::string> name_map; + expand_genblock(std::string(), str + ".", name_map); + } + + for (size_t i = 0; i < children.size(); i++) + current_ast_mod->children.push_back(children[i]); + + children.clear(); + did_something = true; + } + // simplify generate-if blocks if (type == AST_GENIF && children.size() != 0) { @@ -434,7 +451,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) if (!buf->str.empty()) { std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str, name_map); + buf->expand_genblock(std::string(), buf->str + ".", name_map); } for (size_t i = 0; i < buf->children.size(); i++) @@ -877,10 +894,8 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma return; } - if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0) { + if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0) str = name_map[str]; - return; - } std::map<std::string, std::string> backup_name_map; diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l index a269c072a..f899191bb 100644 --- a/frontends/verilog/lexer.l +++ b/frontends/verilog/lexer.l @@ -184,7 +184,7 @@ supply1 { return TOK_SUPPLY1; } "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } -[a-zA-Z_$][a-zA-Z0-9_$]* { +[a-zA-Z_$][a-zA-Z0-9_\.$]* { frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); return TOK_ID; } @@ -233,6 +233,8 @@ supply1 { return TOK_SUPPLY1; } "<=" { return OP_LE; } ">=" { return OP_GE; } +"===" { return OP_EQ; } + /* "~&" { return OP_NAND; } */ /* "~|" { return OP_NOR; } */ "~^" { return OP_XNOR; } diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y index 9caa236f8..22af178e8 100644 --- a/frontends/verilog/parser.y +++ b/frontends/verilog/parser.y @@ -607,7 +607,7 @@ always_stmt: ast_stack.pop_back(); } | attr TOK_INITIAL { - AstNode *node = new AstNode(AST_ALWAYS); + AstNode *node = new AstNode(AST_INITIAL); append_attr(node, $1); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index f4a8c79fa..f9731cbc2 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -89,6 +89,9 @@ struct VerilogFrontend : public Frontend { log(" -nopp\n"); log(" do not run the pre-processor\n"); log("\n"); + log(" -lib\n"); + log(" only create empty placeholder modules\n"); + log("\n"); } virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { @@ -100,6 +103,7 @@ struct VerilogFrontend : public Frontend { bool flag_mem2reg = false; bool flag_ppdump = false; bool flag_nopp = false; + bool flag_lib = false; frontend_verilog_yydebug = false; log_header("Executing Verilog-2005 frontend.\n"); @@ -144,6 +148,10 @@ struct VerilogFrontend : public Frontend { flag_nopp = true; continue; } + if (arg == "-lib") { + flag_lib = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -173,7 +181,7 @@ struct VerilogFrontend : public Frontend { frontend_verilog_yyparse(); frontend_verilog_yylex_destroy(); - AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg); + AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib); if (!flag_nopp) fclose(fp); diff --git a/kernel/driver.cc b/kernel/driver.cc index 1dddefdcb..3de16e724 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -20,13 +20,26 @@ #include <stdio.h> #include <readline/readline.h> #include <readline/history.h> +#include <string.h> +#include <unistd.h> +#include <dlfcn.h> #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/log.h" -#include <string.h> -#include <unistd.h> -#include <dlfcn.h> + +bool fgetline(FILE *f, std::string &buffer) +{ + buffer = ""; + char block[4096]; + while (1) { + if (fgets(block, 4096, f) == NULL) + return false; + buffer += block; + if (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r')) + return true; + } +} static void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command) { @@ -50,9 +63,13 @@ static void run_frontend(std::string filename, std::string command, RTLIL::Desig f = fopen(filename.c_str(), "r"); if (f == NULL) log_error("Can't open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno)); - char buffer[4096]; - while (fgets(buffer, 4096, f) != NULL) { - Pass::call(design, buffer); + std::string command; + while (fgetline(f, command)) { + Pass::call(design, command); + design->check(); + } + if (!command.empty()) { + Pass::call(design, command); design->check(); } if (filename != "-") @@ -132,10 +149,13 @@ static char **readline_completion(const char *text, int start, int) return NULL; } -static const char *create_prompt(RTLIL::Design *design) +static const char *create_prompt(RTLIL::Design *design, int recursion_counter) { static char buffer[100]; - std::string str = "\nyosys"; + std::string str = "\n"; + if (recursion_counter > 1) + str += stringf("(%d) ", recursion_counter); + str += "yosys"; if (!design->selected_active_module.empty()) str += stringf(" [%s]", design->selected_active_module.c_str()); if (!design->selection_stack.back().full_selection) { @@ -151,26 +171,29 @@ static const char *create_prompt(RTLIL::Design *design) static void shell(RTLIL::Design *design) { - static bool recursion_detect = false; + static int recursion_counter = 0; - if (recursion_detect) { - log("Already in interactive shell.\n"); - return; - } - - recursion_detect = true; + recursion_counter++; log_cmd_error_throw = true; rl_readline_name = "yosys"; rl_attempted_completion_function = readline_completion; char *command = NULL; - while ((command = readline(create_prompt(design))) != NULL) + while ((command = readline(create_prompt(design, recursion_counter))) != NULL) { if (command[strspn(command, " \t\r\n")] == 0) continue; add_history(command); + char *p = command + strspn(command, " \t\r\n"); + if (!strncmp(p, "exit", 4)) { + p += 4; + p += strspn(p, " \t\r\n"); + if (*p == 0) + break; + } + try { assert(design->selection_stack.size() == 1); Pass::call(design, command); @@ -180,8 +203,10 @@ static void shell(RTLIL::Design *design) log_reset_stack(); } } + if (command == NULL) + printf("exit\n"); - recursion_detect = false; + recursion_counter--; log_cmd_error_throw = false; } @@ -216,7 +241,7 @@ struct ShellPass : public Pass { log("This command is the default action if nothing else has been specified\n"); log("on the command line.\n"); log("\n"); - log("Press Ctrl-D to leave the interactive shell.\n"); + log("Press Ctrl-D or type 'exit' to leave the interactive shell.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -225,6 +250,82 @@ struct ShellPass : public Pass { } } ShellPass; +struct ScriptPass : public Pass { + ScriptPass() : Pass("script", "execute commands from script file") { } + virtual void help() { + log("\n"); + log(" script <filename>\n"); + log("\n"); + log("This command executes the yosys commands in the specified file.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + if (args.size() < 2) + log_cmd_error("Missing script file.\n"); + if (args.size() > 2) + extra_args(args, 1, design, false); + run_frontend(args[1], "script", design, NULL); + } +} ScriptPass; + +#ifdef YOSYS_ENABLE_TCL +struct TclPass : public Pass { + TclPass() : Pass("tcl", "execute a TCL script file") { } + virtual void help() { + log("\n"); + log(" tcl <filename>\n"); + log("\n"); + log("This command executes the tcl commands in the specified file.\n"); + log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n"); + log("\n"); + log("The tcl command 'yosys -import' can be used to import all yosys\n"); + log("commands directly as tcl commands to the tcl shell. The yosys\n"); + log("command 'proc' is wrapped using the tcl command 'procs' in order\n"); + log("to avoid a name collision with the tcl builting command 'proc'.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + if (args.size() < 2) + log_cmd_error("Missing script file.\n"); + if (args.size() > 2) + extra_args(args, 1, design, false); + if (Tcl_EvalFile(yosys_tcl, args[1].c_str()) != TCL_OK) + log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_tcl)); + } +} TclPass; + +static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + std::vector<std::string> args; + for (int i = 1; i < argc; i++) + args.push_back(argv[i]); + + if (args.size() >= 1 && args[0] == "-import") { + for (auto &it : REGISTER_INTERN::pass_register) { + std::string tcl_command_name = it.first; + if (tcl_command_name == "proc") + tcl_command_name = "procs"; + Tcl_CmdInfo info; + if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) { + log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str()); + } else { + std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str()); + Tcl_Eval(interp, tcl_script.c_str()); + } + } + return TCL_OK; + } + + if (args.size() == 1) { + Pass::call(yosys_tcl_design, args[0]); + return TCL_OK; + } + + Pass::call(yosys_tcl_design, args); + return TCL_OK; +} +#endif + int main(int argc, char **argv) { std::string frontend_command = "auto"; @@ -233,10 +334,16 @@ int main(int argc, char **argv) std::vector<void*> loaded_modules; std::string output_filename = ""; std::string scriptfile = ""; + bool scriptfile_tcl = false; bool got_output_filename = false; +#ifdef YOSYS_ENABLE_TCL + yosys_tcl = Tcl_CreateInterp(); + Tcl_CreateCommand(yosys_tcl, "yosys", tcl_yosys_cmd, NULL, NULL); +#endif + int opt; - while ((opt = getopt(argc, argv, "Sm:f:b:o:p:l:qts:")) != -1) + while ((opt = getopt(argc, argv, "Sm:f:b:o:p:l:qts:c:")) != -1) { switch (opt) { @@ -284,10 +391,15 @@ int main(int argc, char **argv) break; case 's': scriptfile = optarg; + scriptfile_tcl = false; + break; + case 'c': + scriptfile = optarg; + scriptfile_tcl = true; break; default: fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s [-S] [-q] [-t] [-l logfile] [-o <outfile>] [-f <frontend>] [-s <scriptfile>]\n", argv[0]); + fprintf(stderr, "Usage: %s [-S] [-q] [-t] [-l logfile] [-o <outfile>] [-f <frontend>] [{-s|-c} <scriptfile>]\n", argv[0]); fprintf(stderr, " %*s[-p <pass> [-p ..]] [-b <backend>] [-m <module_file>] [<infile> [..]]\n", int(strlen(argv[0])+1), ""); fprintf(stderr, "\n"); fprintf(stderr, " -q\n"); @@ -311,6 +423,9 @@ int main(int argc, char **argv) fprintf(stderr, " -s scriptfile\n"); fprintf(stderr, " execute the commands in the script file\n"); fprintf(stderr, "\n"); + fprintf(stderr, " -c tcl_scriptfile\n"); + fprintf(stderr, " execute the commands in the tcl script file (see 'help tcl' for details)\n"); + fprintf(stderr, "\n"); fprintf(stderr, " -p command\n"); fprintf(stderr, " execute the commands\n"); fprintf(stderr, "\n"); @@ -366,6 +481,10 @@ int main(int argc, char **argv) design->selection_stack.push_back(RTLIL::Selection()); log_push(); +#ifdef YOSYS_ENABLE_TCL + yosys_tcl_design = design; +#endif + if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) { if (!got_output_filename) backend_command = ""; @@ -375,8 +494,17 @@ int main(int argc, char **argv) while (optind < argc) run_frontend(argv[optind++], frontend_command, design, output_filename == "-" ? &backend_command : NULL); - if (!scriptfile.empty()) - run_frontend(scriptfile, "script", design, output_filename == "-" ? &backend_command : NULL); + if (!scriptfile.empty()) { + if (scriptfile_tcl) { +#ifdef YOSYS_ENABLE_TCL + if (Tcl_EvalFile(yosys_tcl, scriptfile.c_str()) != TCL_OK) + log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_tcl)); +#else + log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); +#endif + } else + run_frontend(scriptfile, "script", design, output_filename == "-" ? &backend_command : NULL); + } for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) run_pass(*it, design); @@ -386,6 +514,10 @@ int main(int argc, char **argv) delete design; +#ifdef YOSYS_ENABLE_TCL + yosys_tcl_design = NULL; +#endif + log("\nREADY.\n"); log_pop(); @@ -400,6 +532,10 @@ int main(int argc, char **argv) for (auto mod : loaded_modules) dlclose(mod); +#ifdef YOSYS_ENABLE_TCL + Tcl_DeleteInterp(yosys_tcl); +#endif + return 0; } diff --git a/kernel/register.cc b/kernel/register.cc index 1dd608754..bfd51feb1 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -27,6 +27,11 @@ using namespace REGISTER_INTERN; #define MAX_REG_COUNT 1000 +#ifdef YOSYS_ENABLE_TCL +Tcl_Interp *yosys_tcl = NULL; +RTLIL::Design *yosys_tcl_design = NULL; +#endif + namespace REGISTER_INTERN { int raw_register_count = 0; @@ -109,9 +114,6 @@ void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::s msg.c_str(), command_text.c_str(), error_pos, ""); } -// implemented in kernel/select.cc -extern void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, RTLIL::Design *design); - void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select) { for (; argidx < args.size(); argidx++) @@ -124,7 +126,7 @@ void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Desig if (!select) cmd_error(args, argidx, "Extra argument."); - handle_extra_select_args(this, args, argidx, design); + handle_extra_select_args(this, args, argidx, args.size(), design); break; } cmd_log_args(args); diff --git a/kernel/register.h b/kernel/register.h index a817d8c64..2f664e7c1 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -26,6 +26,12 @@ #include <vector> #include <map> +#ifdef YOSYS_ENABLE_TCL +#include <tcl.h> +extern Tcl_Interp *yosys_tcl; +extern RTLIL::Design *yosys_tcl_design; +#endif + struct Pass { std::string pass_name, short_help; @@ -77,6 +83,9 @@ struct Backend : Pass static void backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args); }; +// implemented in kernel/select.cc +extern void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, size_t args_size, RTLIL::Design *design); + namespace REGISTER_INTERN { extern int raw_register_count; extern bool raw_register_done; diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index c97e2e455..b0dcfe428 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -18,6 +18,7 @@ */ #include "kernel/rtlil.h" +#include "kernel/log.h" #include <assert.h> #include <algorithm> @@ -257,13 +258,12 @@ RTLIL::Module::~Module() RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, std::map<RTLIL::IdString, RTLIL::Const>) { - assert(!"Called derive() from module base class."); - abort(); + log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } void RTLIL::Module::update_auto_wires(std::map<RTLIL::IdString, int>) { - assert(!"Called update_auto_wires() from module base class."); + log_error("Module `%s' has automatic wires bu no HDL backend to handle it!\n", id2cstr(name)); } size_t RTLIL::Module::count_id(RTLIL::IdString id) @@ -566,7 +566,7 @@ void RTLIL::SigSpec::optimize() check(); } -static bool compare_sigchunks(const RTLIL::SigChunk &a, const RTLIL::SigChunk &b) +bool RTLIL::SigChunk::compare(const RTLIL::SigChunk &a, const RTLIL::SigChunk &b) { if (a.wire != b.wire) { if (a.wire == NULL || b.wire == NULL) @@ -583,14 +583,21 @@ static bool compare_sigchunks(const RTLIL::SigChunk &a, const RTLIL::SigChunk &b return a.data.bits < b.data.bits; } +void RTLIL::SigSpec::sort() +{ + expand(); + std::sort(chunks.begin(), chunks.end(), RTLIL::SigChunk::compare); + optimize(); +} + void RTLIL::SigSpec::sort_and_unify() { expand(); - std::sort(chunks.begin(), chunks.end(), compare_sigchunks); + std::sort(chunks.begin(), chunks.end(), RTLIL::SigChunk::compare); for (size_t i = 1; i < chunks.size(); i++) { RTLIL::SigChunk &ch1 = chunks[i-1]; RTLIL::SigChunk &ch2 = chunks[i]; - if (!compare_sigchunks(ch1, ch2) && !compare_sigchunks(ch2, ch1)) { + if (!RTLIL::SigChunk::compare(ch1, ch2) && !RTLIL::SigChunk::compare(ch2, ch1)) { chunks.erase(chunks.begin()+i); width -= chunks[i].width; i--; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index a0d7a1a37..fe88182fa 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -277,6 +277,7 @@ struct RTLIL::SigChunk { bool operator <(const RTLIL::SigChunk &other) const; bool operator ==(const RTLIL::SigChunk &other) const; bool operator !=(const RTLIL::SigChunk &other) const; + static bool compare(const RTLIL::SigChunk &a, const RTLIL::SigChunk &b); }; struct RTLIL::SigSpec { @@ -291,6 +292,7 @@ struct RTLIL::SigSpec { SigSpec(RTLIL::State bit, int width = 1); void expand(); void optimize(); + void sort(); void sort_and_unify(); void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with); void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const; diff --git a/kernel/select.cc b/kernel/select.cc index a6e675c88..fa1c3db02 100644 --- a/kernel/select.cc +++ b/kernel/select.cc @@ -346,8 +346,18 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) size_t endpos = arg.find(':', pos); if (endpos == std::string::npos) endpos = arg.size(); - if (int(endpos) > pos) - limits.insert(RTLIL::escape_id(arg.substr(pos, endpos-pos))); + if (int(endpos) > pos) { + std::string str = arg.substr(pos, endpos-pos); + if (str[0] == '@') { + str = RTLIL::escape_id(str.substr(1)); + if (design->selection_vars.count(str) > 0) { + for (auto i1 : design->selection_vars.at(str).selected_members) + for (auto i2 : i1.second) + limits.insert(i2); + } + } else + limits.insert(RTLIL::escape_id(str)); + } pos = endpos; } } @@ -471,7 +481,8 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_op_expand(design, arg, 'o'); } else log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str()); - select_filter_active_mod(design, work_stack.back()); + if (work_stack.size() >= 1) + select_filter_active_mod(design, work_stack.back()); return; } @@ -585,20 +596,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_filter_active_mod(design, work_stack.back()); } -// used in kernel/register.cc -void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, RTLIL::Design *design) +// used in kernel/register.cc and maybe other locations, extern decl. in register.h +void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, size_t args_size, RTLIL::Design *design) { work_stack.clear(); - for (; argidx < args.size(); argidx++) { - if (args[argidx].substr(0, 1) == "-") - pass->cmd_error(args, argidx, "Unexpected option in selection arguments."); + for (; argidx < args_size; argidx++) { + if (args[argidx].substr(0, 1) == "-") { + if (pass != NULL) + pass->cmd_error(args, argidx, "Unexpected option in selection arguments."); + else + log_cmd_error("Unexpected option in selection arguments."); + } select_stmt(design, args[argidx]); } while (work_stack.size() > 1) { select_op_union(design, work_stack.front(), work_stack.back()); work_stack.pop_back(); } - design->selection_stack.push_back(work_stack.back()); + if (work_stack.size() > 0) + design->selection_stack.push_back(work_stack.back()); + else + design->selection_stack.push_back(RTLIL::Selection(false)); } struct SelectPass : public Pass { diff --git a/kernel/show.cc b/kernel/show.cc index 321d9e2e0..fd703dd53 100644 --- a/kernel/show.cc +++ b/kernel/show.cc @@ -36,7 +36,7 @@ struct ShowWorker std::map<RTLIL::IdString, int> autonames; int single_idx_count; - struct net_conn { std::set<std::string> in, out; int bits; }; + struct net_conn { std::set<std::string> in, out; int bits; std::string color; }; std::map<std::string, net_conn> net_conn_map; FILE *f; @@ -47,6 +47,9 @@ struct ShowWorker bool stretchIO; int page_counter; + const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections; + const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections; + uint32_t xorshift32(uint32_t x) { x ^= x << 13; x ^= x >> 17; @@ -61,6 +64,40 @@ struct ShowWorker return stringf("colorscheme=\"dark28\", color=\"%d\", fontcolor=\"%d\"", currentColor%8+1); } + std::string nextColor(std::string presetColor) + { + if (presetColor.empty()) + return nextColor(); + return presetColor; + } + + std::string nextColor(RTLIL::SigSpec sig, std::string defaultColor) + { + sig.sort_and_unify(); + for (auto &c : sig.chunks) { + if (c.wire != NULL) + for (auto &s : color_selections) + if (s.second.selected_members.count(module->name) > 0 && s.second.selected_members.at(module->name).count(c.wire->name) > 0) + return stringf("color=\"%s\", fontcolor=\"%d\"", s.first.c_str(), s.first.c_str()); + } + return defaultColor; + } + + std::string nextColor(RTLIL::SigSig &conn, std::string defaultColor) + { + return nextColor(conn.first, nextColor(conn.second, defaultColor)); + } + + std::string nextColor(RTLIL::SigSpec &sig) + { + return nextColor(sig, nextColor()); + } + + std::string nextColor(RTLIL::SigSig &conn) + { + return nextColor(conn, nextColor()); + } + std::string widthLabel(int bits) { if (bits <= 1) @@ -145,10 +182,12 @@ struct ShowWorker label_string += stringf("<s%d> %d:%d - %d:%d |", i, pos, pos-c.width+1, c.offset+c.width-1, c.offset); net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); net_conn_map[net].bits = c.width; + net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } else { label_string += stringf("<s%d> %d:%d - %d:%d |", i, c.offset+c.width-1, c.offset, pos, pos-c.width+1); net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); net_conn_map[net].bits = c.width; + net_conn_map[net].color = nextColor(c, net_conn_map[net].color); } pos -= c.width; } @@ -158,9 +197,9 @@ struct ShowWorker if (!port.empty()) { currentColor = xorshift32(currentColor); if (driver) - code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor().c_str(), widthLabel(sig.width).c_str()); + code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.width).c_str()); else - code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor().c_str(), widthLabel(sig.width).c_str()); + code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.width).c_str()); } if (node != NULL) *node = stringf("x%d", idx); @@ -173,6 +212,7 @@ struct ShowWorker else net_conn_map[net].out.insert(port); net_conn_map[net].bits = sig.width; + net_conn_map[net].color = nextColor(sig, net_conn_map[net].color); } if (node != NULL) *node = net; @@ -202,8 +242,9 @@ struct ShowWorker if (it.second->port_input || it.second->port_output) shape = "octagon"; if (it.first[0] == '\\') { - fprintf(f, "n%d [ shape=%s, label=\"%s\" ];\n", - id2num(it.first), shape, escape(it.first)); + fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n", + id2num(it.first), shape, escape(it.first), + nextColor(RTLIL::SigSpec(it.second), "color=\"black\"").c_str()); if (it.second->port_input) all_sources.insert(stringf("n%d", id2num(it.first))); else if (it.second->port_output) @@ -294,10 +335,12 @@ struct ShowWorker if (left_node[0] == 'x' && right_node[0] == 'x') { currentColor = xorshift32(currentColor); - fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor().c_str(), widthLabel(conn.first.width).c_str()); + fprintf(f, "%s:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", left_node.c_str(), right_node.c_str(), nextColor(conn).c_str(), widthLabel(conn.first.width).c_str()); } else { net_conn_map[right_node].bits = conn.first.width; + net_conn_map[right_node].color = nextColor(conn, net_conn_map[right_node].color); net_conn_map[left_node].bits = conn.first.width; + net_conn_map[left_node].color = nextColor(conn, net_conn_map[left_node].color); if (left_node[0] == 'x') { net_conn_map[right_node].in.insert(left_node); } else if (right_node[0] == 'x') { @@ -315,7 +358,7 @@ struct ShowWorker currentColor = xorshift32(currentColor); if (wires_on_demand.count(it.first) > 0) { if (it.second.in.size() == 1 && it.second.out.size() == 1) { - fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.second.in.begin()->c_str(), it.second.out.begin()->c_str(), nextColor().c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.second.in.begin()->c_str(), it.second.out.begin()->c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); continue; } if (it.second.in.size() == 0 || it.second.out.size() == 0) @@ -324,16 +367,19 @@ struct ShowWorker fprintf(f, "%s [ shape=point ];\n", it.first.c_str()); } for (auto &it2 : it.second.in) - fprintf(f, "%s:e -> %s:w [%s, %s];\n", it2.c_str(), it.first.c_str(), nextColor().c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", it2.c_str(), it.first.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); for (auto &it2 : it.second.out) - fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.c_str(), nextColor().c_str(), widthLabel(it.second.bits).c_str()); + fprintf(f, "%s:e -> %s:w [%s, %s];\n", it.first.c_str(), it2.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str()); } fprintf(f, "};\n"); } - ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels, bool stretchIO) : - f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels), stretchIO(stretchIO) + ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels, bool stretchIO, + const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections, + const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections) : + f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels), stretchIO(stretchIO), + color_selections(color_selections), label_selections(label_selections) { ct.setup_internals(); ct.setup_internals_mem(); @@ -352,8 +398,12 @@ struct ShowWorker if (!design->selected_module(module->name)) continue; if (design->selected_whole_module(module->name)) { + if (module->attributes.count("\\placeholder") > 0) { + log("Skipping placeholder module %s.\n", id2cstr(module->name)); + continue; + } else if (module->cells.empty() && module->connections.empty()) { - log("Skipping skeletton module %s.\n", id2cstr(module->name)); + log("Skipping empty module %s.\n", id2cstr(module->name)); continue; } else log("Dumping module %s to page %d.\n", id2cstr(module->name), ++page_counter); @@ -373,10 +423,14 @@ struct ShowPass : public Pass { log(" show [options] [selection]\n"); log("\n"); log("Create a graphviz DOT file for the selected part of the design and compile it\n"); - log("to a postscript file.\n"); + log("to a graphics file (usually SVG or PostScript).\n"); + log("\n"); + log(" -viewer <viewer>\n"); + log(" Run the specified command with the graphics file as parameter.\n"); log("\n"); - log(" -viewer <command>\n"); - log(" Also run the specified command with the postscript file as parameter.\n"); + log(" -format <format>\n"); + log(" Generate a graphics file in the specified format.\n"); + log(" Usually <format> is 'svg' or 'ps'.\n"); log("\n"); log(" -lib <verilog_or_ilang_file>\n"); log(" Use the specified library file for determining whether cell ports are\n"); @@ -386,6 +440,11 @@ struct ShowPass : public Pass { log(" -prefix <prefix>\n"); log(" generate <prefix>.dot and <prefix>.ps instead of yosys-show.{dot,ps}\n"); log("\n"); + log(" -color <color> <wire>\n"); + log(" assign the specified color to the specified wire. The object can be\n"); + log(" a single selection wildcard expressions or a saved set of objects in\n"); + log(" the @<name> syntax (see \"help select\" for details).\n"); + log("\n"); log(" -colors <seed>\n"); log(" Randomly assign colors to the wires. The integer argument is the seed\n"); log(" for the random number generator. Change the seed value if the colored\n"); @@ -398,7 +457,11 @@ struct ShowPass : public Pass { log(" stretch the graph so all inputs are on the left side and all outputs\n"); log(" (including inout ports) are on the right side.\n"); log("\n"); - log("The generated output files are `yosys-show.dot' and `yosys-show.ps'.\n"); + log("When no <format> is specified, SVG is used. When no <format> and <viewer> is\n"); + log("specified, 'yosys-svgviewer' is used to display the schematic.\n"); + log("\n"); + log("The generated output files are 'yosys-show.dot' and 'yosys-show.<format>',\n"); + log("unless another prefix is specified using -prefix <prefix>.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -406,6 +469,10 @@ struct ShowPass : public Pass { log_header("Generating Graphviz representation of design.\n"); log_push(); + std::vector<std::pair<std::string, RTLIL::Selection>> color_selections; + std::vector<std::pair<std::string, RTLIL::Selection>> label_selections; + + std::string format; std::string viewer_exe; std::string prefix = "yosys-show"; std::vector<std::string> libfiles; @@ -430,10 +497,32 @@ struct ShowPass : public Pass { prefix = args[++argidx]; continue; } + if (arg == "-color" && argidx+2 < args.size()) { + std::pair<std::string, RTLIL::Selection> data; + data.first = args[++argidx], argidx++; + handle_extra_select_args(this, args, argidx, argidx+1, design); + data.second = design->selection_stack.back(); + design->selection_stack.pop_back(); + color_selections.push_back(data); + continue; + } + if (arg == "-label" && argidx+2 < args.size() && false) { + std::pair<std::string, RTLIL::Selection> data; + data.first = args[++argidx], argidx++; + handle_extra_select_args(this, args, argidx, argidx+1, design); + data.second = design->selection_stack.back(); + design->selection_stack.pop_back(); + label_selections.push_back(data); + continue; + } if (arg == "-colors" && argidx+1 < args.size()) { colorSeed = atoi(args[++argidx].c_str()); continue; } + if (arg == "-format" && argidx+1 < args.size()) { + format = args[++argidx]; + continue; + } if (arg == "-width") { flag_width= true; continue; @@ -446,6 +535,20 @@ struct ShowPass : public Pass { } extra_args(args, argidx, design); + if (format != "ps") { + int modcount = 0; + for (auto &mod_it : design->modules) { + if (mod_it.second->attributes.count("\\placeholder") > 0) + continue; + if (mod_it.second->cells.empty() && mod_it.second->connections.empty()) + continue; + if (design->selected_module(mod_it.first)) + modcount++; + } + if (modcount > 1) + log_cmd_error("For formats different than 'ps' only one module must be selected.\n"); + } + for (auto filename : libfiles) { FILE *f = fopen(filename.c_str(), "rt"); if (f == NULL) @@ -460,33 +563,42 @@ struct ShowPass : public Pass { log_header("Continuing show pass.\n"); std::string dot_file = stringf("%s.dot", prefix.c_str()); - std::string ps_file = stringf("%s.ps", prefix.c_str()); + std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str()); log("Writing dot description to `%s'.\n", dot_file.c_str()); FILE *f = fopen(dot_file.c_str(), "w"); - if (f == NULL) + if (f == NULL) { + for (auto lib : libs) + delete lib; log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str()); - ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_stretch); + } + ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_stretch, color_selections, label_selections); fclose(f); + for (auto lib : libs) + delete lib; + if (worker.page_counter == 0) log_cmd_error("Nothing there to show.\n"); - std::string cmd = stringf("dot -Tps -o '%s' '%s'", ps_file.c_str(), dot_file.c_str()); + std::string cmd = stringf("dot -T%s -o '%s' '%s'", format.empty() ? "svg" : format.c_str(), out_file.c_str(), dot_file.c_str()); log("Exec: %s\n", cmd.c_str()); if (system(cmd.c_str()) != 0) log_cmd_error("Shell command failed!\n"); if (!viewer_exe.empty()) { - cmd = stringf("%s '%s' &", viewer_exe.c_str(), ps_file.c_str()); + cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str()); + log("Exec: %s\n", cmd.c_str()); + if (system(cmd.c_str()) != 0) + log_cmd_error("Shell command failed!\n"); + } else + if (format.empty()) { + cmd = stringf("fuser -s '%s' || yosys-svgviewer '%s' &", out_file.c_str(), out_file.c_str()); log("Exec: %s\n", cmd.c_str()); if (system(cmd.c_str()) != 0) log_cmd_error("Shell command failed!\n"); } - for (auto lib : libs) - delete lib; - log_pop(); } } ShowPass; diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index 6ee2a0fcc..b4e74be6b 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -1124,6 +1124,21 @@ class SubCircuit::SolverWorker while (prunePortmapCandidates(portmapCandidates, enumerationMatrix, needle, haystack)) { } + if (verbose) { + my_printf("\nPortmapper results:\n"); + for (int j = 0; j < int(enumerationMatrix.size()); j++) { + my_printf("%5d: %s\n", j, needle.graph.nodes[j].nodeId.c_str()); + int variantCounter = 0; + for (auto &i2 : portmapCandidates.at(j)) { + my_printf("%*s variant %2d:", 6, "", variantCounter++); + int mapCounter = 0; + for (auto &i3 : i2) + my_printf("%s %s -> %s", mapCounter++ ? "," : "", i3.first.c_str(), i3.second.c_str()); + my_printf("\n"); + } + } + } + for (int j = 0; j < int(enumerationMatrix.size()); j++) { if (portmapCandidates[j].size() == 0) { if (verbose) { diff --git a/libs/svgviewer/.gitignore b/libs/svgviewer/.gitignore new file mode 100644 index 000000000..b92d91f8b --- /dev/null +++ b/libs/svgviewer/.gitignore @@ -0,0 +1,5 @@ +Makefile +moc_mainwindow.cpp +moc_svgview.cpp +qrc_svgviewer.cpp +svgviewer diff --git a/libs/svgviewer/files/bubbles.svg b/libs/svgviewer/files/bubbles.svg new file mode 100644 index 000000000..51730124a --- /dev/null +++ b/libs/svgviewer/files/bubbles.svg @@ -0,0 +1,215 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<svg width="20cm" height="15cm" viewBox="0 0 800 600" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink/" + baseProfile="tiny" version="1.2"> + <title>Spheres</title> + <desc>Semi-transparent bubbles on a colored background.</desc> + <defs> + <!-- Create radial gradients for each bubble. --> + <radialGradient id="blueBubble" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" stop-opacity="1" /> + <stop offset="25%" stop-color="#cdcdff" stop-opacity=".65" /> + <stop offset="100%" stop-color="#cdaacd" stop-opacity=".75" /> + </radialGradient> + <radialGradient id="redBubble" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" stop-opacity="1" /> + <stop offset="25%" stop-color="#ffcdcd" stop-opacity=".65" /> + <stop offset="100%" stop-color="#bbbb99" stop-opacity=".75" /> + </radialGradient> + <radialGradient id="greenBubble" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" stop-opacity="1" /> + <stop offset="25%" stop-color="#cdffcd" stop-opacity=".65" /> + <stop offset="100%" stop-color="#99aaaa" stop-opacity=".75" /> + </radialGradient> + <radialGradient id="yellowBubble" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" stop-opacity="1" /> + <stop offset="25%" stop-color="#ffffcd" stop-opacity=".65" /> + <stop offset="100%" stop-color="#bbbbaa" stop-opacity=".75" /> + </radialGradient> + <radialGradient id="background" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="400" fx="250" fy="250"> + <stop offset="0%" stop-color="#ffffee" /> + <stop offset="100%" stop-color="#ccccaa" /> + </radialGradient> + <linearGradient id="surface" gradientUnits="userSpaceOnUse" + x1="-100" y1="200" x2="400" y2="200"> + <stop offset="0%" stop-color="#ffffcc" /> + <stop offset="100%" stop-color="#bbbb88" /> + </linearGradient> + + <!-- Create radial gradients for each circle to make them look like + spheres. --> + <radialGradient id="blueSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="blue" /> + <stop offset="100%" stop-color="#222244" /> + </radialGradient> + <radialGradient id="redSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="red" /> + <stop offset="100%" stop-color="#442222" /> + </radialGradient> + <radialGradient id="greenSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="green" /> + <stop offset="100%" stop-color="#113311" /> + </radialGradient> + <radialGradient id="yellowSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="yellow" /> + <stop offset="100%" stop-color="#444422" /> + </radialGradient> + <radialGradient id="shadowGrad" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="50"> + <stop offset="0%" stop-color="black" stop-opacity="1.0" /> + <stop offset="100%" stop-color="black" stop-opacity="0.0" /> + </radialGradient> + + <!-- Define a shadow for each sphere. --> + <circle id="shadow" fill="url(#shadowGrad)" cx="0" cy="0" r="100" /> + <g id="bubble"> + <circle fill="black" cx="0" cy="0" r="50" /> + <circle fill="#a6ce39" cx="0" cy="0" r="33" /> + <path fill="black" d="M 37,50 L 50,37 L 12,-1 L 22,-11 L 10,-24 L -24,10 + L -11,22 L -1,12 Z" /> + <circle cx="0" cy="0" r="100" /> + </g> + </defs> + <g> + <rect fill="url(#background)" x="0" y="0" width="800" height="600" /> + </g> + + <g transform="translate(200,700)"> + <use xlink:href="#bubble" fill="url(#blueBubble)" /> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="1s" dur="10s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(315,700)"> + <g transform="scale(0.5,0.5)"> + <use xlink:href="#bubble" fill="url(#redBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="3s" dur="7s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(80,700)"> + <g transform="scale(0.65,0.65)"> + <use xlink:href="#bubble" fill="url(#greenBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="5s" dur="9s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(255,700)"> + <g transform="scale(0.3,0.3)"> + <use xlink:href="#bubble" fill="url(#yellowBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="2s" dur="6s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(565,700)"> + <g transform="scale(0.4,0.4)"> + <use xlink:href="#bubble" fill="url(#blueBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="4s" dur="8s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(715,700)"> + <g transform="scale(0.6,0.6)"> + <use xlink:href="#bubble" fill="url(#redBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="1s" dur="4s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(645,700)"> + <g transform="scale(0.375,0.375)"> + <use xlink:href="#bubble" fill="url(#greenBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="0s" dur="11s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(555,700)"> + <g transform="scale(0.9,0.9)"> + <use xlink:href="#bubble" fill="url(#yellowBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="3s" dur="7.5s" fill="freeze" repeatCount="indefinite" /> + </g> + + <g transform="translate(360,700)"> + <g transform="scale(0.5,0.5)"> + <use xlink:href="#bubble" fill="url(#blueBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="3s" dur="6s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(215,700)"> + <g transform="scale(0.45,0.45)"> + <use xlink:href="#bubble" fill="url(#redBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="5.5s" dur="7s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(420,700)"> + <g transform="scale(0.75,0.75)"> + <use xlink:href="#bubble" fill="url(#greenBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="1s" dur="9s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(815,700)"> + <g transform="scale(0.6,0.6)"> + <use xlink:href="#bubble" fill="url(#yellowBubble)" /> + </g> + <animateTransform attributeName="transform" type="translate" additive="sum" + values="0,0; 0,-800" begin="2s" dur="9.5s" fill="freeze" repeatCount="indefinite" /> + </g> + + <g transform="translate(225,375)" > + <g transform="scale(1.0,0.5)" > + <path d="M 0 0 L 350 0 L 450 450 L -100 450 z" + fill="url(#surface)" stroke="none" /> + </g> + </g> + <g transform="translate(200,0)" > + <g transform="translate(200,490) scale(2.0,1.0) rotate(45)" > + <rect fill="#a6ce39" x="-69" y="-69" width="138" height="138" /> + <circle fill="black" cx="0" cy="0" r="50" /> + <circle fill="#a6ce39" cx="0" cy="0" r="33" /> + <path fill="black" d="M 37,50 L 50,37 L 12,-1 L 22,-11 L 10,-24 L -24,10 + L -11,22 L -1,12 Z" /> + <animateTransform attributeName="transform" type="rotate" additive="sum" values="0; 360" + begin="0s" dur="10s" fill="freeze" repeatCount="indefinite" /> + </g> + <g transform="translate(200,375)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#blueSphere)" cx="0" cy="0" r="100" /> + </g> + <g transform="translate(315,440)"> + <g transform="scale(0.5,0.5)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#redSphere)" cx="0" cy="0" r="100" /> + </g> + </g> + <g transform="translate(80,475)"> + <g transform="scale(0.65,0.65)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#greenSphere)" cx="0" cy="0" r="100" /> + </g> + </g> + <g transform="translate(255,525)"> + <g transform="scale(0.3,0.3)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#yellowSphere)" cx="0" cy="0" r="100" /> + </g> + </g> + </g> +</svg> diff --git a/libs/svgviewer/files/cubic.svg b/libs/svgviewer/files/cubic.svg new file mode 100644 index 000000000..492bb72b8 --- /dev/null +++ b/libs/svgviewer/files/cubic.svg @@ -0,0 +1,77 @@ +<?xml version="1.0" standalone="no"?> +<svg width="10cm" height="10cm" viewBox="0 0 1000 1000" + xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"> + <title>Example cubic02 - cubic Bezier commands in path data</title> + <desc>Picture showing examples of "C" and "S" commands, + along with annotations showing the control points + and end points</desc> + + <rect fill="none" stroke="blue" stroke-width="1" x="1" y="1" width="998" height="998" /> + + <!-- Path 1 --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="100,200 100,100" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="400,100 400,200" /> + <path fill="none" stroke="red" stroke-width="5" d="M100,200 C100,100 400,100 400,200" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="200" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="200" r="10" /> + <circle class="CtlPoint" cx="100" cy="100" r="10" /> + <circle class="CtlPoint" cx="400" cy="100" r="10" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="275">M100,200 C100,100 400,100 400,200</text> + + <!-- Path 2 --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="100,500 25,400" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="475,400 400,500" /> + <path fill="none" stroke="red" stroke-width="5" d="M100,500 C25,400 475,400 400,500" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="500" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="500" r="10" /> + <circle fill="#888888" stroke="none" cx="25" cy="400" r="10" /> + <circle fill="#888888" stroke="none" cx="475" cy="400" r="10" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="575">M100,500 C25,400 475,400 400,500</text> + + <!-- Path 3 --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="100,800 175,700" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="325,700 400,800" /> + <path fill="none" stroke="red" stroke-width="5" d="M100,800 C175,700 325,700 400,800" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="100" cy="800" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="400" cy="800" r="10" /> + <circle fill="#888888" stroke="none" cx="175" cy="700" r="10" /> + <circle fill="#888888" stroke="none" cx="325" cy="700" r="10" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="250" y="875">M100,800 C175,700 325,700 400,800</text> + + <!-- Path 4 --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="600,200 675,100" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="975,100 900,200" /> + <path fill="none" stroke="red" stroke-width="5" d="M600,200 C675,100 975,100 900,200" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="200" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="200" r="10" /> + <circle fill="#888888" stroke="none" cx="675" cy="100" r="10" /> + <circle fill="#888888" stroke="none" cx="975" cy="100" r="10" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="275">M600,200 C675,100 975,100 900,200</text> + + <!-- Path 5 --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="600,500 600,350" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="900,650 900,500" /> + <path fill="none" stroke="red" stroke-width="5" d="M600,500 C600,350 900,650 900,500" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="500" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="500" r="10" /> + <circle fill="#888888" stroke="none" cx="600" cy="350" r="10" /> + <circle fill="#888888" stroke="none" cx="900" cy="650" r="10" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="575">M600,500 C600,350 900,650 900,500</text> + + <!-- Path 6 (C and S command) --> + <polyline fill="none" stroke="#888888" stroke-width="2" points="600,800 625,700" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="725,700 750,800" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="750,800 775,900" /> + <polyline fill="none" stroke="#888888" stroke-width="2" points="875,900 900,800" /> + <path fill="none" stroke="red" stroke-width="5" d="M600,800 C625,700 725,700 750,800 + S875,900 900,800" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="600" cy="800" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="750" cy="800" r="10" /> + <circle fill="none" stroke="#888888" stroke-width="2" cx="900" cy="800" r="10" /> + <circle fill="#888888" stroke="none" cx="625" cy="700" r="10" /> + <circle fill="#888888" stroke="none" cx="725" cy="700" r="10" /> + <circle fill="#888888" stroke="none" cx="875" cy="900" r="10" /> + <circle fill="none" stroke="blue" stroke-width="4" cx="775" cy="900" r="9" /> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="945">M600,800 C625,700 725,700 750,800</text> + <text text-anchor="middle" font-size="22" font-family="Verdana" x="750" y="975">S875,900 900,800</text> +</svg> diff --git a/libs/svgviewer/files/spheres.svg b/libs/svgviewer/files/spheres.svg new file mode 100644 index 000000000..b23164bce --- /dev/null +++ b/libs/svgviewer/files/spheres.svg @@ -0,0 +1,72 @@ +<?xml version="1.0" standalone="no"?> +<svg width="8cm" height="8cm" viewBox="0 0 400 400" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink/" + baseProfile="tiny" version="1.2"> + <title>Spheres</title> + <desc>Gradient filled spheres with different colors.</desc> + <defs> + <!-- Create radial gradients for each circle to make them look like + spheres. --> + <radialGradient id="blueSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="blue" /> + <stop offset="100%" stop-color="#222244" /> + </radialGradient> + <radialGradient id="redSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="red" /> + <stop offset="100%" stop-color="#442222" /> + </radialGradient> + <radialGradient id="greenSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="green" /> + <stop offset="100%" stop-color="#113311" /> + </radialGradient> + <radialGradient id="yellowSphere" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="-50"> + <stop offset="0%" stop-color="white" /> + <stop offset="75%" stop-color="yellow" /> + <stop offset="100%" stop-color="#444422" /> + </radialGradient> + <radialGradient id="shadowGrad" gradientUnits="userSpaceOnUse" + cx="0" cy="0" r="100" fx="-50" fy="50"> + <stop offset="0%" stop-color="black" stop-opacity="1.0" /> + <stop offset="100%" stop-color="white" stop-opacity="0.0" /> + </radialGradient> + + <!-- Define a shadow for each sphere. --> + <circle id="shadow" fill="url(#shadowGrad)" cx="0" cy="0" r="100" /> + </defs> + <g fill="#ffee99" stroke="none" > + <rect x="0" y="0" width="400" height="400" /> + </g> + <g fill="white" stroke="none" > + <rect x="0" y="175" width="400" height="225" /> + </g> + <g transform="translate(200,175)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#blueSphere)" cx="0" cy="0" r="100" /> + </g> + <g transform="translate(315,240)"> + <g transform="scale(0.5,0.5)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#redSphere)" cx="0" cy="0" r="100" /> + </g> + </g> + <g transform="translate(80,275)"> + <g transform="scale(0.65,0.65)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#greenSphere)" cx="0" cy="0" r="100" /> + </g> + </g> + <g transform="translate(255,325)"> + <g transform="scale(0.3,0.3)"> + <use xlink:href="#shadow" transform="translate(25,55) scale(1.0,0.5)" /> + <circle fill="url(#yellowSphere)" cx="0" cy="0" r="100" /> + </g> + </g> +</svg> diff --git a/libs/svgviewer/main.cpp b/libs/svgviewer/main.cpp new file mode 100644 index 000000000..34866f854 --- /dev/null +++ b/libs/svgviewer/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QString> +#ifndef QT_NO_OPENGL +#include <QGLFormat> +#endif + +#include "mainwindow.h" + +int main(int argc, char **argv) +{ + Q_INIT_RESOURCE(svgviewer); + + QApplication app(argc, argv); + + MainWindow window; + if (argc == 2) + window.openFile(argv[1]); + else + window.openFile(":/files/bubbles.svg"); +#if defined(Q_OS_SYMBIAN) + window.showMaximized(); +#else + window.show(); +#endif + return app.exec(); +} diff --git a/libs/svgviewer/mainwindow.cpp b/libs/svgviewer/mainwindow.cpp new file mode 100644 index 000000000..b352ff9f7 --- /dev/null +++ b/libs/svgviewer/mainwindow.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" + +#include <QtGui> +#include <QFileSystemWatcher> + +#include "svgview.h" + +MainWindow::MainWindow() + : QMainWindow() + , m_view(new SvgView) + , m_watcher(NULL) + , m_filehandle(NULL) +{ + QMenu *fileMenu = new QMenu(tr("&File"), this); + QAction *openAction = fileMenu->addAction(tr("&Open...")); + openAction->setShortcut(QKeySequence(tr("Ctrl+O"))); + QAction *quitAction = fileMenu->addAction(tr("E&xit")); + quitAction->setShortcuts(QKeySequence::Quit); + + menuBar()->addMenu(fileMenu); + + QMenu *viewMenu = new QMenu(tr("&View"), this); + m_backgroundAction = viewMenu->addAction(tr("&Background")); + m_backgroundAction->setEnabled(false); + m_backgroundAction->setCheckable(true); + m_backgroundAction->setChecked(false); + connect(m_backgroundAction, SIGNAL(toggled(bool)), m_view, SLOT(setViewBackground(bool))); + + m_outlineAction = viewMenu->addAction(tr("&Outline")); + m_outlineAction->setEnabled(false); + m_outlineAction->setCheckable(true); + m_outlineAction->setChecked(true); + connect(m_outlineAction, SIGNAL(toggled(bool)), m_view, SLOT(setViewOutline(bool))); + + menuBar()->addMenu(viewMenu); + + QMenu *rendererMenu = new QMenu(tr("&Renderer"), this); + m_nativeAction = rendererMenu->addAction(tr("&Native")); + m_nativeAction->setCheckable(true); + m_nativeAction->setChecked(true); +#ifndef QT_NO_OPENGL + m_glAction = rendererMenu->addAction(tr("&OpenGL")); + m_glAction->setCheckable(true); +#endif + m_imageAction = rendererMenu->addAction(tr("&Image")); + m_imageAction->setCheckable(true); + +#ifndef QT_NO_OPENGL + rendererMenu->addSeparator(); + m_highQualityAntialiasingAction = rendererMenu->addAction(tr("&High Quality Antialiasing")); + m_highQualityAntialiasingAction->setEnabled(false); + m_highQualityAntialiasingAction->setCheckable(true); + m_highQualityAntialiasingAction->setChecked(false); + connect(m_highQualityAntialiasingAction, SIGNAL(toggled(bool)), m_view, SLOT(setHighQualityAntialiasing(bool))); +#endif + + QActionGroup *rendererGroup = new QActionGroup(this); + rendererGroup->addAction(m_nativeAction); +#ifndef QT_NO_OPENGL + rendererGroup->addAction(m_glAction); +#endif + rendererGroup->addAction(m_imageAction); + + menuBar()->addMenu(rendererMenu); + + connect(openAction, SIGNAL(triggered()), this, SLOT(openFile())); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(rendererGroup, SIGNAL(triggered(QAction*)), + this, SLOT(setRenderer(QAction*))); + + setCentralWidget(m_view); + setWindowTitle(tr("SVG Viewer")); +} + +void MainWindow::openFile(const QString &path, bool reload) +{ + QString fileName; + if (path.isNull()) + fileName = QFileDialog::getOpenFileName(this, tr("Open SVG File"), + m_currentPath, "SVG files (*.svg *.svgz *.svg.gz)"); + else + fileName = path; + + if (m_watcher) { + delete m_watcher; + m_watcher = NULL; + } + if (m_filehandle) { + fclose(m_filehandle); + m_filehandle = NULL; + } + + if (!fileName.isEmpty()) { + QFile file(fileName); + if (!file.exists()) { + QMessageBox::critical(this, tr("Open SVG File"), + QString("Could not open file '%1'.").arg(fileName)); + + m_outlineAction->setEnabled(false); + m_backgroundAction->setEnabled(false); + return; + } + + QTransform oldTransform = m_view->transform(); + m_view->openFile(file); + + if (!fileName.startsWith(":/")) + { + m_currentPath = fileName; + setWindowTitle(tr("%1 - SVGViewer").arg(m_currentPath)); + + // just keep the file open so this process is found using 'fuser' + m_filehandle = fopen(fileName.toAscii(), "r"); + + m_watcher = new QFileSystemWatcher(this); + m_watcher->addPath(fileName); + connect(m_watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(reloadFile())); + } + + m_outlineAction->setEnabled(true); + m_backgroundAction->setEnabled(true); + + if (reload) + m_view->setTransform(oldTransform); + else + resize(m_view->sizeHint() + QSize(80, 80 + menuBar()->height())); + } +} + +void MainWindow::reloadFile() +{ + openFile(m_currentPath, true); +} + +void MainWindow::setRenderer(QAction *action) +{ +#ifndef QT_NO_OPENGL + m_highQualityAntialiasingAction->setEnabled(false); +#endif + + if (action == m_nativeAction) + m_view->setRenderer(SvgView::Native); +#ifndef QT_NO_OPENGL + else if (action == m_glAction) { + m_highQualityAntialiasingAction->setEnabled(true); + m_view->setRenderer(SvgView::OpenGL); + } +#endif + else if (action == m_imageAction) { + m_view->setRenderer(SvgView::Image); + } +} diff --git a/libs/svgviewer/mainwindow.h b/libs/svgviewer/mainwindow.h new file mode 100644 index 000000000..5b549e92a --- /dev/null +++ b/libs/svgviewer/mainwindow.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QString> +#include <stdio.h> + +class SvgView; + +QT_BEGIN_NAMESPACE +class QAction; +class QGraphicsView; +class QGraphicsScene; +class QGraphicsRectItem; +class QFileSystemWatcher; +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +public slots: + void openFile(const QString &path = QString(), bool reload = false); + void setRenderer(QAction *action); + void reloadFile(); + +private: + QAction *m_nativeAction; + QAction *m_glAction; + QAction *m_imageAction; + QAction *m_highQualityAntialiasingAction; + QAction *m_backgroundAction; + QAction *m_outlineAction; + + SvgView *m_view; + + QString m_currentPath; + QFileSystemWatcher *m_watcher; + FILE *m_filehandle; +}; + +#endif diff --git a/libs/svgviewer/svgview.cpp b/libs/svgviewer/svgview.cpp new file mode 100644 index 000000000..da9a4aee3 --- /dev/null +++ b/libs/svgviewer/svgview.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "svgview.h" + +#include <QFile> +#include <QWheelEvent> +#include <QMouseEvent> +#include <QGraphicsRectItem> +#include <QGraphicsSvgItem> +#include <QPaintEvent> +#include <qmath.h> + +#ifndef QT_NO_OPENGL +#include <QGLWidget> +#endif + +SvgView::SvgView(QWidget *parent) + : QGraphicsView(parent) + , m_renderer(Native) + , m_svgItem(0) + , m_backgroundItem(0) + , m_outlineItem(0) +{ + setScene(new QGraphicsScene(this)); + setTransformationAnchor(AnchorUnderMouse); + setDragMode(ScrollHandDrag); + setViewportUpdateMode(FullViewportUpdate); + + // Prepare background check-board pattern + QPixmap tilePixmap(64, 64); + tilePixmap.fill(Qt::white); + QPainter tilePainter(&tilePixmap); + QColor color(220, 220, 220); + tilePainter.fillRect(0, 0, 32, 32, color); + tilePainter.fillRect(32, 32, 32, 32, color); + tilePainter.end(); + + setBackgroundBrush(tilePixmap); +} + +void SvgView::drawBackground(QPainter *p, const QRectF &) +{ + p->save(); + p->resetTransform(); + p->drawTiledPixmap(viewport()->rect(), backgroundBrush().texture()); + p->restore(); +} + +void SvgView::openFile(const QFile &file) +{ + if (!file.exists()) + return; + + QGraphicsScene *s = scene(); + + bool drawBackground = (m_backgroundItem ? m_backgroundItem->isVisible() : false); + bool drawOutline = (m_outlineItem ? m_outlineItem->isVisible() : true); + + s->clear(); + resetTransform(); + + m_svgItem = new QGraphicsSvgItem(file.fileName()); + m_svgItem->setFlags(QGraphicsItem::ItemClipsToShape); + m_svgItem->setCacheMode(QGraphicsItem::NoCache); + m_svgItem->setZValue(0); + + m_backgroundItem = new QGraphicsRectItem(m_svgItem->boundingRect()); + m_backgroundItem->setBrush(Qt::white); + m_backgroundItem->setPen(Qt::NoPen); + m_backgroundItem->setVisible(drawBackground); + m_backgroundItem->setZValue(-1); + + m_outlineItem = new QGraphicsRectItem(m_svgItem->boundingRect()); + QPen outline(Qt::black, 2, Qt::DashLine); + outline.setCosmetic(true); + m_outlineItem->setPen(outline); + m_outlineItem->setBrush(Qt::NoBrush); + m_outlineItem->setVisible(drawOutline); + m_outlineItem->setZValue(1); + + s->addItem(m_backgroundItem); + s->addItem(m_svgItem); + s->addItem(m_outlineItem); + + s->setSceneRect(m_outlineItem->boundingRect().adjusted(-10, -10, 10, 10)); +} + +void SvgView::setRenderer(RendererType type) +{ + m_renderer = type; + + if (m_renderer == OpenGL) { +#ifndef QT_NO_OPENGL + setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); +#endif + } else { + setViewport(new QWidget); + } +} + +void SvgView::setHighQualityAntialiasing(bool highQualityAntialiasing) +{ +#ifndef QT_NO_OPENGL + setRenderHint(QPainter::HighQualityAntialiasing, highQualityAntialiasing); +#else + Q_UNUSED(highQualityAntialiasing); +#endif +} + +void SvgView::setViewBackground(bool enable) +{ + if (!m_backgroundItem) + return; + + m_backgroundItem->setVisible(enable); +} + +void SvgView::setViewOutline(bool enable) +{ + if (!m_outlineItem) + return; + + m_outlineItem->setVisible(enable); +} + +void SvgView::paintEvent(QPaintEvent *event) +{ + if (m_renderer == Image) { + if (m_image.size() != viewport()->size()) { + m_image = QImage(viewport()->size(), QImage::Format_ARGB32_Premultiplied); + } + + QPainter imagePainter(&m_image); + QGraphicsView::render(&imagePainter); + imagePainter.end(); + + QPainter p(viewport()); + p.drawImage(0, 0, m_image); + + } else { + QGraphicsView::paintEvent(event); + } +} + +void SvgView::wheelEvent(QWheelEvent *event) +{ + qreal factor = qPow(1.2, event->delta() / 240.0); + scale(factor, factor); + event->accept(); +} + diff --git a/libs/svgviewer/svgview.h b/libs/svgviewer/svgview.h new file mode 100644 index 000000000..944401ac9 --- /dev/null +++ b/libs/svgviewer/svgview.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SVGVIEW_H +#define SVGVIEW_H + +#include <QGraphicsView> + +QT_BEGIN_NAMESPACE +class QWheelEvent; +class QPaintEvent; +class QFile; +QT_END_NAMESPACE + +class SvgView : public QGraphicsView +{ + Q_OBJECT + +public: + enum RendererType { Native, OpenGL, Image }; + + SvgView(QWidget *parent = 0); + + void openFile(const QFile &file); + void setRenderer(RendererType type = Native); + void drawBackground(QPainter *p, const QRectF &rect); + +public slots: + void setHighQualityAntialiasing(bool highQualityAntialiasing); + void setViewBackground(bool enable); + void setViewOutline(bool enable); + +protected: + void wheelEvent(QWheelEvent *event); + void paintEvent(QPaintEvent *event); + +private: + RendererType m_renderer; + + QGraphicsItem *m_svgItem; + QGraphicsRectItem *m_backgroundItem; + QGraphicsRectItem *m_outlineItem; + + QImage m_image; +}; +#endif // SVGVIEW_H diff --git a/libs/svgviewer/svgviewer.desktop b/libs/svgviewer/svgviewer.desktop new file mode 100644 index 000000000..477ef789d --- /dev/null +++ b/libs/svgviewer/svgviewer.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Type=Application +Terminal=false +Name=SVG Viewer +Exec=/opt/usr/bin/svgviewer +Icon=svgviewer +X-Window-Icon= +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable diff --git a/libs/svgviewer/svgviewer.pro b/libs/svgviewer/svgviewer.pro new file mode 100644 index 000000000..0d938f444 --- /dev/null +++ b/libs/svgviewer/svgviewer.pro @@ -0,0 +1,33 @@ +HEADERS = mainwindow.h \ + svgview.h +RESOURCES = svgviewer.qrc +SOURCES = main.cpp \ + mainwindow.cpp \ + svgview.cpp +QT += svg xml + +contains(QT_CONFIG, opengl): QT += opengl + +CONFIG += console + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/painting/svgviewer +sources.files = $$SOURCES $$HEADERS $$RESOURCES svgviewer.pro files +sources.path = $$[QT_INSTALL_EXAMPLES]/painting/svgviewer +INSTALLS += target sources + +wince*: { + addFiles.files = files\\*.svg + addFiles.path = "\\My Documents" + DEPLOYMENT += addFiles +} + +symbian: { + TARGET.UID3 = 0xA000A64E + include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri) + addFiles.files = files\\*.svg + addFiles.path = . + DEPLOYMENT += addFiles +} +maemo5: include($$QT_SOURCE_TREE/examples/maemo5pkgrules.pri) + diff --git a/libs/svgviewer/svgviewer.qrc b/libs/svgviewer/svgviewer.qrc new file mode 100644 index 000000000..db611f51f --- /dev/null +++ b/libs/svgviewer/svgviewer.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> + <file>files/bubbles.svg</file> +</qresource> +</RCC> + diff --git a/passes/dfflibmap/Makefile.inc b/passes/dfflibmap/Makefile.inc index ed92b299c..b4d73fa12 100644 --- a/passes/dfflibmap/Makefile.inc +++ b/passes/dfflibmap/Makefile.inc @@ -2,9 +2,9 @@ OBJS += passes/dfflibmap/dfflibmap.o OBJS += passes/dfflibmap/libparse.o -TARGETS += filterlib +TARGETS += yosys-filterlib GENFILES += passes/dfflibmap/filterlib.o -filterlib: passes/dfflibmap/filterlib.o - $(CXX) -o filterlib $(LDFLAGS) $^ $(LDLIBS) +yosys-filterlib: passes/dfflibmap/filterlib.o + $(CXX) -o yosys-filterlib $(LDFLAGS) $^ $(LDLIBS) diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index 26d120a16..dc9ec2b06 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -49,7 +49,7 @@ std::string kiss_convert_signal(const RTLIL::SigSpec &sig) { * @param module pointer to module which contains the FSM cell. * @param cell pointer to the FSM cell which should be exported. */ -void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell) { +void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::string filename, bool origenc) { std::map<RTLIL::IdString, RTLIL::Const>::iterator attr_it; FsmData fsm_data; FsmData::transition_t tr; @@ -58,7 +58,9 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell) { size_t i; attr_it = cell->attributes.find("\\fsm_export"); - if (attr_it != cell->attributes.end() && attr_it->second.str != "") { + if (!filename.empty()) { + kiss_name.assign(filename); + } else if (attr_it != cell->attributes.end() && attr_it->second.str != "") { kiss_name.assign(attr_it->second.str); } else { @@ -84,15 +86,24 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell) { kiss_file << ".o " << std::dec << fsm_data.num_outputs << std::endl; kiss_file << ".p " << std::dec << fsm_data.transition_table.size() << std::endl; kiss_file << ".s " << std::dec << fsm_data.state_table.size() << std::endl; - kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl; + if (origenc) { + kiss_file << ".r " << kiss_convert_signal(fsm_data.state_table[fsm_data.reset_state]) << std::endl; + } else { + kiss_file << ".r s" << std::dec << fsm_data.reset_state << std::endl; + } for (i = 0; i < fsm_data.transition_table.size(); i++) { tr = fsm_data.transition_table[i]; try { kiss_file << kiss_convert_signal(tr.ctrl_in) << ' '; - kiss_file << 's' << tr.state_in << ' '; - kiss_file << 's' << tr.state_out << ' '; + if (origenc) { + kiss_file << kiss_convert_signal(fsm_data.state_table[tr.state_in]) << ' '; + kiss_file << kiss_convert_signal(fsm_data.state_table[tr.state_out]) << ' '; + } else { + kiss_file << 's' << tr.state_in << ' '; + kiss_file << 's' << tr.state_out << ' '; + } kiss_file << kiss_convert_signal(tr.ctrl_out) << std::endl; } catch (int) { @@ -114,21 +125,32 @@ struct FsmExportPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" fsm_export [-noauto] [selection]\n"); + log(" fsm_export [-noauto] [-o filename] [-origenc] [selection]\n"); log("\n"); log("This pass creates a KISS2 file for every selected FSM. For FSMs with the\n"); log("'fsm_export' attribute set, the attribute value is used as filename, otherwise\n"); - log("the module and cell name is used as filename.\n"); + log("the module and cell name is used as filename. If the parameter '-o' is given,\n"); + log("the first exported FSM is written to the specified filename. This overwrites\n"); + log("the setting as specified with the 'fsm_export' attribute. All other FSMs are\n"); + log("exported to the default name as mentioned above.\n"); log("\n"); log(" -noauto\n"); log(" only export FSMs that have the 'fsm_export' attribute set\n"); log("\n"); + log(" -o filename\n"); + log(" filename of the first exported FSM\n"); + log("\n"); + log(" -origenc\n"); + log(" use binary state encoding as state names instead of s0, s1, ...\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::map<RTLIL::IdString, RTLIL::Const>::iterator attr_it; std::string arg; bool flag_noauto = false; + std::string filename; + bool flag_origenc = false; size_t argidx; log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); @@ -139,6 +161,15 @@ struct FsmExportPass : public Pass { flag_noauto = true; continue; } + if (arg == "-o") { + argidx++; + filename = args[argidx]; + continue; + } + if (arg == "-origenc") { + flag_origenc = true; + continue; + } break; } extra_args(args, argidx, design); @@ -149,7 +180,8 @@ struct FsmExportPass : public Pass { if (cell_it.second->type == "$fsm" && design->selected(mod_it.second, cell_it.second)) { attr_it = cell_it.second->attributes.find("\\fsm_export"); if (!flag_noauto || (attr_it != cell_it.second->attributes.end())) { - write_kiss2(mod_it.second, cell_it.second); + write_kiss2(mod_it.second, cell_it.second, filename, flag_origenc); + filename.clear(); } } } diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 04274990d..287f93097 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -50,17 +50,21 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell for (auto &celltype : found_celltypes) { std::set<std::string> portnames; + std::set<std::string> parameters; std::map<std::string, int> portwidths; log("Generate module for cell type %s:\n", celltype.c_str()); for (auto i1 : design->modules) for (auto i2 : i1.second->cells) - if (i2.second->type == celltype) + if (i2.second->type == celltype) { for (auto &conn : i2.second->connections) { if (conn.first[0] != '$') portnames.insert(conn.first); portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.width); } + for (auto ¶ : i2.second->parameters) + parameters.insert(para.first); + } for (auto &decl : portdecls) if (decl.index > 0) @@ -109,6 +113,7 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell RTLIL::Module *mod = new RTLIL::Module; mod->name = celltype; + mod->attributes["\\placeholder"] = RTLIL::Const(0, 0); design->modules[mod->name] = mod; for (auto &decl : ports) { @@ -121,6 +126,9 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell mod->add(wire); } + for (auto ¶ : parameters) + log(" ignoring parameter %s.\n", RTLIL::id2cstr(para)); + log(" module %s created.\n", RTLIL::id2cstr(mod->name)); } } @@ -139,9 +147,12 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla } if (cell->parameters.size() == 0) continue; + if (design->modules.at(cell->type)->attributes.count("\\placeholder") > 0) + continue; RTLIL::Module *mod = design->modules[cell->type]; cell->type = mod->derive(design, cell->parameters); cell->parameters.clear(); + did_something = true; } if (did_something) @@ -200,7 +211,7 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla if (auto_sizes.size() > 0) { module->update_auto_wires(auto_sizes); - log_header("Continuing EXPAND pass.\n"); + log_header("Continuing HIERARCHY pass.\n"); did_something = true; } @@ -262,7 +273,7 @@ struct HierarchyPass : public Pass { log(" use the specified top module to built a design hierarchy. modules\n"); log(" outside this tree (unused modules) are removed.\n"); log("\n"); - log("In -generate mode this pass generates skeletton modules for the given cell\n"); + log("In -generate mode this pass generates placeholder modules for the given cell\n"); log("types (wildcards supported). For this the design is searched for cells that\n"); log("match the given types and then the given port declarations are used to\n"); log("determine the direction of the ports. The syntax for a port declaration is:\n"); diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index 0effd964b..7c82f0fcb 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -181,8 +181,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module) RTLIL::SigSpec b = assign_map(cell->connections["\\B"]); if (a.is_fully_const()) { - RTLIL::SigSpec tmp = a; - a = b, b = tmp; + RTLIL::SigSpec tmp; + tmp = a, a = b, b = tmp; + cell->connections["\\A"] = a; + cell->connections["\\B"] = b; } if (b.is_fully_const()) { diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 9f79d0603..43d1a57e1 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -65,7 +65,32 @@ struct OptShareWorker for (auto &it : cell->parameters) hash_string += "P " + it.first + "=" + it.second.as_string() + "\n"; - for (auto &it : cell->connections) { + const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections; + std::map<RTLIL::IdString, RTLIL::SigSpec> alt_conn; + + if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" || + cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") { + alt_conn = *conn; + if (assign_map(alt_conn.at("\\A")) < assign_map(alt_conn.at("\\B"))) { + alt_conn["\\A"] = conn->at("\\B"); + alt_conn["\\B"] = conn->at("\\A"); + } + conn = &alt_conn; + } else + if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + alt_conn.at("\\A").sort(); + conn = &alt_conn; + } else + if (cell->type == "$reduce_and" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + alt_conn.at("\\A").sort_and_unify(); + conn = &alt_conn; + } + + for (auto &it : *conn) { if (ct.cell_output(cell->type, it.first)) continue; RTLIL::SigSpec sig = it.second; @@ -126,6 +151,28 @@ struct OptShareWorker assign_map.apply(it.second); } + if (cell1->type == "$and" || cell1->type == "$or" || cell1->type == "$xor" || cell1->type == "$xnor" || cell1->type == "$add" || cell1->type == "$mul" || + cell1->type == "$logic_and" || cell1->type == "$logic_or" || cell1->type == "$_AND_" || cell1->type == "$_OR_" || cell1->type == "$_XOR_") { + if (conn1.at("\\A") < conn1.at("\\B")) { + RTLIL::SigSpec tmp = conn1["\\A"]; + conn1["\\A"] = conn1["\\B"]; + conn1["\\B"] = tmp; + } + if (conn2.at("\\A") < conn2.at("\\B")) { + RTLIL::SigSpec tmp = conn2["\\A"]; + conn2["\\A"] = conn2["\\B"]; + conn2["\\B"] = tmp; + } + } else + if (cell1->type == "$reduce_xor" || cell1->type == "$reduce_xnor") { + conn1["\\A"].sort(); + conn2["\\A"].sort(); + } else + if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") { + conn1["\\A"].sort_and_unify(); + conn2["\\A"].sort_and_unify(); + } + if (conn1 != conn2) { lt = conn1 < conn2; return true; diff --git a/passes/submod/submod.cc b/passes/submod/submod.cc index f91d1ec3a..98854da23 100644 --- a/passes/submod/submod.cc +++ b/passes/submod/submod.cc @@ -29,6 +29,7 @@ struct SubmodWorker CellTypes ct; RTLIL::Design *design; RTLIL::Module *module; + std::string opt_name; struct SubModule { @@ -188,9 +189,9 @@ struct SubmodWorker module->cells[new_cell->name] = new_cell; } - SubmodWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) + SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, std::string opt_name = std::string()) : design(design), module(module), opt_name(opt_name) { - if (!design->selected_whole_module(module->name)) + if (!design->selected_whole_module(module->name) && opt_name.empty()) return; if (module->processes.size() > 0) { @@ -208,29 +209,47 @@ struct SubmodWorker ct.setup_stdcells(); ct.setup_stdcells_mem(); - for (auto &it : module->wires) - it.second->attributes.erase("\\submod"); - - for (auto &it : module->cells) + if (opt_name.empty()) { - RTLIL::Cell *cell = it.second; - if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].str.size() == 0) { + for (auto &it : module->wires) + it.second->attributes.erase("\\submod"); + + for (auto &it : module->cells) + { + RTLIL::Cell *cell = it.second; + if (cell->attributes.count("\\submod") == 0 || cell->attributes["\\submod"].str.size() == 0) { + cell->attributes.erase("\\submod"); + continue; + } + + std::string submod_str = cell->attributes["\\submod"].str; cell->attributes.erase("\\submod"); - continue; - } - std::string submod_str = cell->attributes["\\submod"].str; - cell->attributes.erase("\\submod"); + if (submodules.count(submod_str) == 0) { + submodules[submod_str].name = submod_str; + submodules[submod_str].full_name = module->name + "_" + submod_str; + while (design->modules.count(submodules[submod_str].full_name) != 0 || + module->count_id(submodules[submod_str].full_name) != 0) + submodules[submod_str].full_name += "_"; + } - if (submodules.count(submod_str) == 0) { - submodules[submod_str].name = submod_str; - submodules[submod_str].full_name = module->name + "_" + submod_str; - while (design->modules.count(submodules[submod_str].full_name) != 0 || - module->count_id(submodules[submod_str].full_name) != 0) - submodules[submod_str].full_name += "_"; + submodules[submod_str].cells.insert(cell); + } + } + else + { + for (auto &it : module->cells) + { + RTLIL::Cell *cell = it.second; + if (!design->selected(module, cell)) + continue; + submodules[opt_name].name = opt_name; + submodules[opt_name].full_name = RTLIL::escape_id(opt_name); + submodules[opt_name].cells.insert(cell); } - submodules[submod_str].cells.insert(cell); + if (submodules.size() == 0) + log("Nothing selected -> do nothing.\n"); } for (auto &it : submodules) @@ -256,35 +275,72 @@ struct SubmodPass : public Pass { log("This pass only operates on completely selected modules with no processes\n"); log("or memories.\n"); log("\n"); + log("\n"); + log(" submod -name <name> [selection]\n"); + log("\n"); + log("As above, but don't use the 'submod' attribute but instead use the selection.\n"); + log("Only objects from one module might be selected. The value of the -name option\n"); + log("is used as the value of the 'submod' attribute above.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing SUBMOD pass (moving cells to submodules as requested).\n"); log_push(); - Pass::call(design, "opt_rmunused"); - log_header("Continuing SUBMOD pass.\n"); - - extra_args(args, 1, design); - - std::set<std::string> handled_modules; - - bool did_something = true; - while (did_something) { - did_something = false; - std::vector<std::string> queued_modules; - for (auto &mod_it : design->modules) - if (handled_modules.count(mod_it.first) == 0) - queued_modules.push_back(mod_it.first); - for (auto &modname : queued_modules) - if (design->modules.count(modname) != 0) { - SubmodWorker worker(design, design->modules[modname]); - handled_modules.insert(modname); - did_something = true; - } + std::string opt_name; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-name" && argidx+1 < args.size()) { + opt_name = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (opt_name.empty()) + { + Pass::call(design, "opt_rmunused"); + log_header("Continuing SUBMOD pass.\n"); + + std::set<std::string> handled_modules; + + bool did_something = true; + while (did_something) { + did_something = false; + std::vector<std::string> queued_modules; + for (auto &mod_it : design->modules) + if (handled_modules.count(mod_it.first) == 0 && design->selected_whole_module(mod_it.first)) + queued_modules.push_back(mod_it.first); + for (auto &modname : queued_modules) + if (design->modules.count(modname) != 0) { + SubmodWorker worker(design, design->modules[modname]); + handled_modules.insert(modname); + did_something = true; + } + } + + Pass::call(design, "opt_rmunused"); + } + else + { + RTLIL::Module *module = NULL; + for (auto &mod_it : design->modules) { + if (!design->selected_module(mod_it.first)) + continue; + if (module != NULL) + log_cmd_error("More than one module selected: %s %s\n", module->name.c_str(), mod_it.first.c_str()); + module = mod_it.second; + } + if (module == NULL) + log("Nothing selected -> do nothing.\n"); + else + SubmodWorker worker(design, module, opt_name); } - Pass::call(design, "opt_rmunused"); + log_pop(); } } SubmodPass; diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 5fd5858aa..732bd5cb9 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -31,7 +31,7 @@ static void apply_prefix(std::string prefix, std::string &id) if (id[0] == '\\') id = prefix + "." + id.substr(1); else - id = prefix + "." + id; + id = "$techmap" + prefix + "." + id; } static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) @@ -47,8 +47,93 @@ static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module } std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; +std::map<RTLIL::Module*, bool> techmap_fail_cache; -static bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map) +static bool techmap_fail_check(RTLIL::Module *module) +{ + if (module == NULL) + return false; + + if (techmap_fail_cache.count(module) > 0) + return techmap_fail_cache.at(module); + + for (auto &it : module->wires) { + std::string name = it.first; + if (name == "\\TECHMAP_FAIL") + return techmap_fail_cache[module] = true; + if (name.size() > 13 && name[0] == '\\' && name.substr(name.size()-13) == ".TECHMAP_FAIL") + return techmap_fail_cache[module] = true; + } + + return techmap_fail_cache[module] = false; +} + +static void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl) +{ + log("Mapping `%s.%s' using `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(tpl->name)); + + if (tpl->memories.size() != 0) + log_error("Technology map yielded memories -> this is not supported."); + + if (tpl->processes.size() != 0) + log_error("Technology map yielded processes -> this is not supported."); + + for (auto &it : tpl->wires) { + RTLIL::Wire *w = new RTLIL::Wire(*it.second); + apply_prefix(cell->name, w->name); + w->port_input = false; + w->port_output = false; + w->port_id = 0; + module->wires[w->name] = w; + design->select(module, w); + } + + for (auto &it : tpl->cells) { + RTLIL::Cell *c = new RTLIL::Cell(*it.second); + if (c->type.substr(0, 2) == "\\$") + c->type = c->type.substr(1); + apply_prefix(cell->name, c->name); + for (auto &it2 : c->connections) + apply_prefix(cell->name, it2.second, module); + module->cells[c->name] = c; + design->select(module, c); + } + + for (auto &it : tpl->connections) { + RTLIL::SigSig c = it; + apply_prefix(cell->name, c.first, module); + apply_prefix(cell->name, c.second, module); + module->connections.push_back(c); + } + + for (auto &it : cell->connections) { + if (tpl->wires.count(it.first) == 0 || tpl->wires.at(it.first)->port_id == 0) + continue; + RTLIL::Wire *w = tpl->wires[it.first]; + RTLIL::SigSig c; + if (w->port_output) { + c.first = it.second; + c.second = RTLIL::SigSpec(w); + apply_prefix(cell->name, c.second, module); + } else { + c.first = RTLIL::SigSpec(w); + c.second = it.second; + apply_prefix(cell->name, c.first, module); + } + if (c.second.width > c.first.width) + c.second.remove(c.first.width, c.second.width - c.first.width); + if (c.second.width < c.first.width) + c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width)); + assert(c.first.width == c.second.width); + module->connections.push_back(c); + } + + module->cells.erase(cell->name); + delete cell; +} + +static bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, + const std::map<RTLIL::IdString, std::set<RTLIL::IdString>> &celltypeMap) { if (!design->selected(module)) return false; @@ -67,98 +152,55 @@ static bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL:: RTLIL::Cell *cell = module->cells[cell_name]; - if (!design->selected(module, cell)) + if (!design->selected(module, cell) || handled_cells.count(cell) > 0) continue; - if (map->modules.count(cell->type) == 0) + if (celltypeMap.count(cell->type) == 0) continue; - RTLIL::Module *tpl = map->modules[cell->type]; - std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(cell->type, cell->parameters); - - if (techmap_cache.count(key) > 0) { - tpl = techmap_cache[key]; - } else { - std::string derived_name = cell->type; - if (cell->parameters.size() != 0) { - derived_name = tpl->derive(map, cell->parameters); - tpl = map->modules[derived_name]; - log_header("Continuing TECHMAP pass.\n"); + for (auto &tpl_name : celltypeMap.at(cell->type)) + { + std::string derived_name = tpl_name; + RTLIL::Module *tpl = map->modules[tpl_name]; + std::map<RTLIL::IdString, RTLIL::Const> parameters = cell->parameters; + + for (auto conn : cell->connections) { + if (tpl->wires.count(conn.first) > 0 && tpl->wires.at(conn.first)->port_id > 0) + continue; + if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0) + goto next_tpl; + parameters[conn.first] = conn.second.as_const(); } - for (auto &cit : tpl->cells) - if (cit.second->type == "\\TECHMAP_FAILED") { - log("Not using module `%s' from techmap as it contains a TECHMAP_FAILED marker cell.\n", derived_name.c_str()); - tpl = NULL; - break; - } - techmap_cache[key] = tpl; - } - - if (tpl == NULL) - goto next_cell; - - log("Mapping `%s.%s' using `%s'.\n", module->name.c_str(), cell_name.c_str(), tpl->name.c_str()); - - if (tpl->memories.size() != 0) - log_error("Technology map yielded memories -> this is not supported."); - if (tpl->processes.size() != 0) - log_error("Technology map yielded processes -> this is not supported."); - - for (auto &it : tpl->wires) { - RTLIL::Wire *w = new RTLIL::Wire(*it.second); - apply_prefix(cell_name, w->name); - w->port_input = false; - w->port_output = false; - w->port_id = 0; - module->wires[w->name] = w; - design->select(module, w); - } - - for (auto &it : tpl->cells) { - RTLIL::Cell *c = new RTLIL::Cell(*it.second); - if (c->type.substr(0, 2) == "\\$") - c->type = c->type.substr(1); - apply_prefix(cell_name, c->name); - for (auto &it2 : c->connections) - apply_prefix(cell_name, it2.second, module); - module->cells[c->name] = c; - design->select(module, c); - } - - for (auto &it : tpl->connections) { - RTLIL::SigSig c = it; - apply_prefix(cell_name, c.first, module); - apply_prefix(cell_name, c.second, module); - module->connections.push_back(c); - } + if (0) { + next_tpl: + continue; + } - for (auto &it : cell->connections) { - assert(tpl->wires.count(it.first)); - assert(tpl->wires[it.first]->port_id > 0); - RTLIL::Wire *w = tpl->wires[it.first]; - RTLIL::SigSig c; - if (w->port_output) { - c.first = it.second; - c.second = RTLIL::SigSpec(w); - apply_prefix(cell_name, c.second, module); + std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters); + if (techmap_cache.count(key) > 0) { + tpl = techmap_cache[key]; } else { - c.first = RTLIL::SigSpec(w); - c.second = it.second; - apply_prefix(cell_name, c.first, module); + if (cell->parameters.size() != 0) { + derived_name = tpl->derive(map, parameters); + tpl = map->modules[derived_name]; + log_header("Continuing TECHMAP pass.\n"); + } + techmap_cache[key] = tpl; } - if (c.second.width > c.first.width) - c.second.remove(c.first.width, c.second.width - c.first.width); - if (c.second.width < c.first.width) - c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width)); - assert(c.first.width == c.second.width); - module->connections.push_back(c); + + if (techmap_fail_check(tpl)) { + log("Not using module `%s' from techmap as it contains a TECHMAP_FAIL marker wire.\n", derived_name.c_str()); + continue; + } + + techmap_module_worker(design, module, cell, tpl); + did_something = true; + cell = NULL; + break; } - delete cell; - module->cells.erase(cell_name); - did_something = true; - next_cell:; + handled_cells.insert(cell); } return did_something; @@ -182,8 +224,23 @@ struct TechmapPass : public Pass { log(" transforms the internal RTL cells to the internal gate\n"); log(" library.\n"); log("\n"); + log("When a module in the map file has the 'celltype' attribute set, it will match\n"); + log("cells with a type that match the text value of this attribute.\n"); + log("\n"); + log("When a module in the map file contains a wire with the name 'TECHMAP_FAIL' (or\n"); + log("one matching '*.TECHMAP_FAIL') then no substitution will be performed. The\n"); + log("modules in the map file are tried in alphabetical order.\n"); + log("\n"); + log("When a module in the map file has a parameter where the according cell in the\n"); + log("design has a port, the module from the map file is only used if the port in\n"); + log("the design is connected to a constant value. The parameter is then set to the\n"); + log("constant value.\n"); + log("\n"); log("See 'help extract' for a pass that does the opposite thing.\n"); log("\n"); + log("See 'help flatten' for a pass that does flatten the design (which is\n"); + log("esentially techmap but using the design itself as map library).\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -220,18 +277,68 @@ struct TechmapPass : public Pass { } map->modules.swap(modules_new); + std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap; + for (auto &it : map->modules) { + if (it.second->attributes.count("\\celltype") && !it.second->attributes.at("\\celltype").str.empty()) { + celltypeMap[RTLIL::escape_id(it.second->attributes.at("\\celltype").str)].insert(it.first); + } else + celltypeMap[it.first].insert(it.first); + } + bool did_something = true; + std::set<RTLIL::Cell*> handled_cells; while (did_something) { did_something = false; for (auto &mod_it : design->modules) - if (techmap_module(design, mod_it.second, map)) + if (techmap_module(design, mod_it.second, map, handled_cells, celltypeMap)) did_something = true; } log("No more expansions possible.\n"); techmap_cache.clear(); + techmap_fail_cache.clear(); delete map; log_pop(); } } TechmapPass; +struct FlattenPass : public Pass { + FlattenPass() : Pass("flatten", "flatten design") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" flatten [selection]\n"); + log("\n"); + log("This pass flattens the design by replacing cells by their implementation. This\n"); + log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n"); + log("pass is using the current design as mapping library.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing FLATTEN pass (flatten design).\n"); + log_push(); + + extra_args(args, 1, design); + + std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap; + for (auto &it : design->modules) + celltypeMap[it.first].insert(it.first); + + bool did_something = true; + std::set<RTLIL::Cell*> handled_cells; + while (did_something) { + did_something = false; + for (auto &mod_it : design->modules) + if (techmap_module(design, mod_it.second, design, handled_cells, celltypeMap)) + did_something = true; + } + + log("No more expansions possible.\n"); + techmap_cache.clear(); + techmap_fail_cache.clear(); + log_pop(); + } +} FlattenPass; + diff --git a/techlibs/Makefile.inc b/techlibs/Makefile.inc index 031a4ad30..6c2a5f66a 100644 --- a/techlibs/Makefile.inc +++ b/techlibs/Makefile.inc @@ -1,5 +1,5 @@ -TARGETS += techlibs/blackbox.v +EXTRA_TARGETS += techlibs/blackbox.v techlibs/blackbox.v: techlibs/blackbox.sed techlibs/simlib.v techlibs/stdcells_sim.v cat techlibs/simlib.v techlibs/stdcells_sim.v | sed -rf techlibs/blackbox.sed > techlibs/blackbox.v.new diff --git a/techlibs/simlib.v b/techlibs/simlib.v index 29c13503b..ff988cbe5 100644 --- a/techlibs/simlib.v +++ b/techlibs/simlib.v @@ -646,7 +646,6 @@ module \$sr (S, R, Q); parameter WIDTH = 0; -input CLK; input [WIDTH-1:0] S, R; output reg [WIDTH-1:0] Q; @@ -800,8 +799,8 @@ parameter MEMID = ""; parameter ABITS = 8; parameter WIDTH = 8; -parameter RD_CLK_ENABLE = 0; -parameter RD_CLK_POLARITY = 0; +parameter CLK_ENABLE = 0; +parameter CLK_POLARITY = 0; input CLK; input [ABITS-1:0] ADDR; @@ -822,8 +821,8 @@ parameter MEMID = ""; parameter ABITS = 8; parameter WIDTH = 8; -parameter RD_CLK_ENABLE = 0; -parameter RD_CLK_POLARITY = 0; +parameter CLK_ENABLE = 0; +parameter CLK_POLARITY = 0; input CLK, EN; input [ABITS-1:0] ADDR; diff --git a/tests/k68_vltor/changes.diff b/tests/k68_vltor/changes.diff new file mode 100644 index 000000000..7b9034032 --- /dev/null +++ b/tests/k68_vltor/changes.diff @@ -0,0 +1,12 @@ +diff --git a/bench/bench.cpp b/bench/bench.cpp +index 47a50c4..de27fbb 100755 +--- a/bench/bench.cpp ++++ b/bench/bench.cpp +@@ -71,6 +71,7 @@ int main(int argc, char **argv, char **env) { + main_time++; + top->arbclk_i = !top->arbclk_i; + if (main_time%5 == 0) top->clk = !top->clk; ++ if (main_time%100000 == 0) cout<<"Partial sum = "<<hex<<top->sum<<"\n"; + } + + cout<<"Final sum = "<<hex<<top->sum<<"\n"; diff --git a/tests/k68_vltor/clone.sh b/tests/k68_vltor/clone.sh new file mode 100644 index 000000000..54bba5219 --- /dev/null +++ b/tests/k68_vltor/clone.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -ex +rm -rf verilog-sim-benchmarks +git clone http://git.veripool.org/git/verilog-sim-benchmarks +cd verilog-sim-benchmarks +patch -p1 < ../changes.diff diff --git a/tests/k68_vltor/run.sh b/tests/k68_vltor/run.sh new file mode 100644 index 000000000..97ccf2389 --- /dev/null +++ b/tests/k68_vltor/run.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +if ( + set -ex + cd verilog-sim-benchmarks + rm -rf obj_dir_* synth + + cd rtl + mkdir -p ../synth + ../../../../yosys -o ../synth/k68_soc.v -p 'hierarchy -check -top k68_soc; proc; opt; memory; opt' \ + k68_soc.v k68_arb.v k68_cpu.v k68_load.v k68_clkgen.v k68_decode.v k68_execute.v \ + k68_fetch.v k68_regbank.v k68_buni.v k68_b2d.v k68_ccc.v k68_d2b.v k68_rox.v \ + k68_calc.v k68_dpmem.v k68_sasc.v sasc_brg.v sasc_top.v sasc_fifo4.v + + cd .. + VERILATOR_OPT="-Wno-fatal -Ibench --cc bench/k68_soc_test.v --exe bench/bench.cpp -prefix m68 -x-assign 0" + verilator -Mdir obj_dir_rtl -Irtl $VERILATOR_OPT; make -C obj_dir_rtl -f m68.mk + verilator -Mdir obj_dir_synth -Isynth $VERILATOR_OPT; make -C obj_dir_synth -f m68.mk + + ./obj_dir_rtl/m68 100000 | tee output_rtl.txt + ./obj_dir_synth/m68 100000 | tee output_synth.txt + diff -u <( grep ' sum ' output_rtl.txt; ) <( grep ' sum ' output_synth.txt; ) +); then + echo OK + exit 0 +else + echo ERROR + exit 1 +fi + diff --git a/tests/simple/always01.v b/tests/simple/always01.v new file mode 100644 index 000000000..21379cb01 --- /dev/null +++ b/tests/simple/always01.v @@ -0,0 +1,10 @@ +module uut_always01(clock, reset, count); + +input clock, reset; +output [3:0] count; +reg [3:0] count; + +always @(posedge clock) + count <= reset ? 0 : count + 1; + +endmodule diff --git a/tests/simple/always02.v b/tests/simple/always02.v new file mode 100644 index 000000000..8c7ef0fb5 --- /dev/null +++ b/tests/simple/always02.v @@ -0,0 +1,13 @@ +module uut_always02(clock, reset, count); + +input clock, reset; +output [3:0] count; +reg [3:0] count; + +always @(posedge clock) begin + count <= count + 1; + if (reset) + count <= 0; +end + +endmodule diff --git a/tests/simple/always03.v b/tests/simple/always03.v new file mode 100644 index 000000000..5542175e5 --- /dev/null +++ b/tests/simple/always03.v @@ -0,0 +1,22 @@ +module uut_always03(clock, in1, in2, in3, in4, in5, in6, in7, out1, out2, out3); + +input clock, in1, in2, in3, in4, in5, in6, in7; +output out1, out2, out3; +reg out1, out2, out3; + +always @(posedge clock) begin + out1 = in1; + if (in2) + out1 = !out1; + out2 <= out1; + if (in3) + out2 <= out2; + if (in4) + if (in5) + out3 <= in6; + else + out3 <= in7; + out1 = out1 ^ out2; +end + +endmodule diff --git a/tests/simple/arrays01.v b/tests/simple/arrays01.v new file mode 100644 index 000000000..bd0eda294 --- /dev/null +++ b/tests/simple/arrays01.v @@ -0,0 +1,16 @@ +module uut_arrays01(clock, we, addr, wr_data, rd_data); + +input clock, we; +input [3:0] addr, wr_data; +output [3:0] rd_data; +reg [3:0] rd_data; + +reg [3:0] memory [15:0]; + +always @(posedge clock) begin + if (we) + memory[addr] <= wr_data; + rd_data <= memory[addr]; +end + +endmodule diff --git a/tests/simple/forgen01.v b/tests/simple/forgen01.v new file mode 100644 index 000000000..70ee7e667 --- /dev/null +++ b/tests/simple/forgen01.v @@ -0,0 +1,20 @@ +module uut_forgen01(a, y); + +input [4:0] a; +output y; + +integer i, j; +reg [31:0] lut; + +initial begin + for (i = 0; i < 32; i = i+1) begin + lut[i] = i > 1; + for (j = 2; j*j <= i; j = j+1) + if (i % j == 0) + lut[i] = 0; + end +end + +assign y = lut[a]; + +endmodule diff --git a/tests/simple/forgen02.v b/tests/simple/forgen02.v new file mode 100644 index 000000000..14af070c3 --- /dev/null +++ b/tests/simple/forgen02.v @@ -0,0 +1,30 @@ +module uut_forgen02(a, b, cin, y, cout); + +parameter WIDTH = 8; + +input [WIDTH-1:0] a, b; +input cin; + +output [WIDTH-1:0] y; +output cout; + +genvar i; +wire [WIDTH-1:0] carry; + +generate + for (i = 0; i < WIDTH; i=i+1) begin:adder + wire [2:0] D; + assign D[1:0] = { a[i], b[i] }; + if (i == 0) begin:chain + assign D[2] = cin; + end else begin:chain + assign D[2] = carry[i-1]; + end + assign y[i] = ^D; + assign carry[i] = &D[1:0] | (^D[1:0] & D[2]); + end +endgenerate + +assign cout = carry[WIDTH-1]; + +endmodule diff --git a/tests/simple/process.v b/tests/simple/process.v index 532586649..8cb4c870e 100644 --- a/tests/simple/process.v +++ b/tests/simple/process.v @@ -1,4 +1,23 @@ +module blocking_cond (in, out); + +input in; +output reg out; +reg tmp; + +always @* begin + tmp = 1; + out = 1'b0; + case (1'b1) + tmp: out = in; + endcase + tmp = 0; +end + +endmodule + +// ------------------------------------------------------------- + module uut(clk, arst, a, b, c, d, e, f, out1); input clk, arst, a, b, c, d, e, f; diff --git a/tests/tools/vcdcd.pl b/tests/tools/vcdcd.pl index 4875eeeb0..93041534b 100755 --- a/tests/tools/vcdcd.pl +++ b/tests/tools/vcdcd.pl @@ -8,14 +8,21 @@ use Verilog::VCD qw(parse_vcd list_sigs); $| = 1; +my $opt_width = 0; +if ($ARGV[0] eq '-w') { + $opt_width = +$ARGV[1]; + shift @ARGV; + shift @ARGV; +} + if ($#ARGV != 1) { print STDERR "\n"; print STDERR "VCDCD - Value Change Dump Change Dumper\n"; print STDERR "\n"; - print STDERR "Usage: $0 gold.vcd gate.vcd\n"; + print STDERR "Usage: $0 [-w N] gold.vcd gate.vcd\n"; print STDERR "\n"; print STDERR "Compare a known-good (gold) vcd file with a second (gate) vcd file.\n"; - print STDERR "This is not very efficient -- so use with care with large vcd files.\n"; + print STDERR "This is not very efficient -- so use with care on large vcd files.\n"; print STDERR "\n"; exit 1; } @@ -112,6 +119,8 @@ for my $key (keys %$vcd_gate) { } } +$signal_maxlen = $opt_width if $opt_width > 0; + my $diffcount = 0; my %state_gold; my %state_gate; @@ -161,8 +170,11 @@ sub cmp_signal($$) my @a = split //, $a; my @b = split //, $b; - unshift @a, "-" while $#a < $#b; - unshift @b, "-" while $#b < $#a; + my $trail_a = $#a < 0 ? '-' : $a[0] eq '1' ? '0' : $a[0]; + my $trail_b = $#b < 0 ? '-' : $b[0] eq '1' ? '0' : $b[0]; + + unshift @a, $trail_a while $#a < $#b; + unshift @b, $trail_b while $#b < $#a; for (my $i = 0; $i <= $#a; $i++) { return 0 if $a[$i] ne "x" && $a[$i] ne $b[$i]; |