diff options
-rw-r--r-- | README | 9 | ||||
-rw-r--r-- | backends/blif/blif.cc | 2 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 32 | ||||
-rw-r--r-- | frontends/ast/ast.h | 6 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 80 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 49 | ||||
-rw-r--r-- | libs/ezsat/ezminisat.cc | 5 | ||||
-rw-r--r-- | passes/cmds/show.cc | 4 | ||||
-rw-r--r-- | passes/techmap/dfflibmap.cc | 2 | ||||
-rw-r--r-- | techlibs/cmos/cmos_cells.lib | 14 | ||||
-rw-r--r-- | techlibs/cmos/cmos_cells.v | 12 | ||||
-rw-r--r-- | tests/simple/vloghammer.v | 7 |
12 files changed, 189 insertions, 33 deletions
@@ -280,6 +280,15 @@ and after each occurrence of PRIi64 in the header file: sudo sed -i 's/PRIi64/ & /' /usr/include/minisat/utils/Options.h +Roadmap / Large-scale TODOs +=========================== + +- Technology mapping for real-world applications (specific FPGAs and ASIC processes) +- Improve standard complience of const folding and parameters (mostly expression widths) +- Implement SAT-based formal equivialence checker based on existing SAT framework +- Re-implement Verilog frontend (cleaner AST format, pipeline of well structured AST transformations) + + TODOs / Open Bugs ================= diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 747ba17d3..e8909b91a 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -298,7 +298,7 @@ struct BlifBackend : public Backend { for (auto module_it : design->modules) { RTLIL::Module *module = module_it.second; - if ((module->get_bool_attribute("\\placeholder")) > 0) + if (module->get_bool_attribute("\\placeholder")) continue; if (module->processes.size() != 0) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index df30c6d95..51dcd34ac 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -646,12 +646,37 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe return node; } +RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) +{ + std::vector<RTLIL::State> bits = this->bits; + if (width >= 0 && width < int(bits.size())) + bits.resize(width); + if (width >= 0 && width > int(bits.size())) { + RTLIL::State extbit = RTLIL::State::S0; + if (is_signed && !bits.empty()) + extbit = bits.back(); + while (width > int(bits.size())) + bits.push_back(extbit); + } + return RTLIL::Const(bits); +} + +RTLIL::Const AstNode::bitsAsConst(int width) +{ + return bitsAsConst(width, is_signed); +} + // create a new AstModule from an AST_MODULE AST node static AstModule* process_module(AstNode *ast) { assert(ast->type == AST_MODULE); log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); + 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); + current_ast_mod = ast; AstNode *ast_before_simplify = ast->clone(); @@ -661,7 +686,7 @@ static AstModule* process_module(AstNode *ast) log("--- END OF AST DUMP ---\n"); } - while (ast->simplify(!flag_noopt, false, false, 0)) { } + while (ast->simplify(!flag_noopt, false, false, 0, -1, false)) { } if (flag_dump_ast2) { log("Dumping verilog AST after simplification:\n"); @@ -687,11 +712,6 @@ static AstModule* process_module(AstNode *ast) ast->attributes["\\placeholder"] = AstNode::mkconst_int(1, false); } - 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) { diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 504ec5f2d..37f75454c 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -164,7 +164,7 @@ namespace AST // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() - bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage); + bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); void replace_ids(std::map<std::string, std::string> &rules); void mem2reg_as_needed_pass1(std::set<AstNode*> &mem2reg_set, std::set<AstNode*> &mem2reg_candidates, bool sync_proc, bool async_proc, bool force_mem2reg); @@ -193,6 +193,10 @@ namespace AST // helper functions for creating AST nodes for constants static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32); static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed); + + // helper function for creating sign-extended const objects + RTLIL::Const bitsAsConst(int width, bool is_signed); + RTLIL::Const bitsAsConst(int width = -1); }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 8b122a7a5..ab22b8657 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -508,8 +508,10 @@ struct AST_INTERNAL::ProcessGenerator void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) { std::string type_name; - bool dummy_sign_hint = true; - // int dummy_width_hint = -1; + bool sub_sign_hint = true; + int sub_width_hint = -1; + int this_width = 0; + AstNode *range = NULL; switch (type) { @@ -520,23 +522,83 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) break; case AST_IDENTIFIER: - if ((id2ast && !id2ast->is_signed) || children.size() > 0) + if (!id2ast) + log_error("Failed to resolve identifier %s for width detection at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + if ((id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) && id2ast->children[0]->type == AST_CONSTANT) { + this_width = id2ast->children[0]->bits.size(); + if (children.size() != 0) + range = children[0]; + } else if (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE) { + if (!id2ast->range_valid) { + if (id2ast->type == AST_AUTOWIRE) + this_width = 1; + else { + current_ast_mod->dumpAst(stdout, ""); + printf("---\n"); + dumpAst(stdout, ""); + fflush(stdout); + log_error("Failed to detect with of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + } + } else { + this_width = id2ast->range_left - id2ast->range_right + 1; + if (children.size() != 0) + range = children[0]; + } + } else if (id2ast->type == AST_GENVAR) { + this_width = 32; + } else if (id2ast->type == AST_MEMORY) { + if (!id2ast->children[0]->range_valid) + log_error("Failed to detect with of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; + } else + log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + if (range) { + if (range->children.size() == 1) + this_width = 1; + else if (!range->range_valid) { + AstNode *left_at_zero_ast = children[0]->children[0]->clone(); + AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); + while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } + while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } + if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) + log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", + str.c_str(), filename.c_str(), linenum); + this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + delete left_at_zero_ast; + delete right_at_zero_ast; + } else + this_width = range->range_left - range->range_right + 1; + } else + width_hint = std::max(width_hint, this_width); + if (!id2ast->is_signed) sign_hint = false; - width_hint = std::max(width_hint, genRTLIL().width); break; case AST_TO_SIGNED: - children.at(0)->detectSignWidthWorker(width_hint, dummy_sign_hint); + children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint); break; case AST_TO_UNSIGNED: - children.at(0)->detectSignWidthWorker(width_hint, dummy_sign_hint); + children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint); sign_hint = false; break; case AST_CONCAT: + for (auto child : children) { + sub_width_hint = 0; + sub_sign_hint = true; + child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); + this_width += sub_width_hint; + } + width_hint = std::max(width_hint, this_width); + sign_hint = false; + break; + case AST_REPLICATE: - width_hint = std::max(width_hint, genRTLIL().width); + if (children[0]->type != AST_CONSTANT) + log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); + children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); + width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; break; @@ -797,8 +859,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (!children[0]->range_valid) { AstNode *left_at_zero_ast = children[0]->children[0]->clone(); AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, true, false, 1)) { } - while (right_at_zero_ast->simplify(true, true, false, 1)) { } + while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } + while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index a8c4d01ce..960f12075 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -43,7 +43,7 @@ using namespace AST_INTERNAL; // // this function also does all name resolving and sets the id2ast member of all // nodes that link to a different node using names and lexical scoping. -bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) +bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint) { AstNode *newNode = NULL; bool did_something = false; @@ -52,7 +52,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) { assert(type == AST_MODULE); - while (simplify(const_fold, at_zero, in_lvalue, 1)) { } + while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint)) { } if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) { @@ -71,6 +71,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) reg->is_reg = true; reg->is_signed = node->is_signed; children.push_back(reg); + while (reg->simplify(true, false, false, 1, -1, false)) { } } } @@ -84,7 +85,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) } } - while (simplify(const_fold, at_zero, in_lvalue, 2)) { } + while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint)) { } return false; } @@ -174,6 +175,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) auto backup_current_block_child = current_block_child; auto backup_current_top_block = current_top_block; + // calculate width and sign hints + if (type == AST_RANGE) { + width_hint = -1; + sign_hint = false; + } + if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE || type == AST_ASSIGN) { + while (children[0]->simplify(false, at_zero, true, stage, -1, false) == true) { } + children[0]->detectSignWidth(width_hint, sign_hint); + } + // simplify all children first // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { @@ -188,6 +199,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) break; while (did_something_here && i < children.size()) { bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; + int width_hint_here = width_hint; + bool sign_hint_here = sign_hint; if (i == 0 && type == AST_REPLICATE) const_fold_here = true; if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) @@ -198,13 +211,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) } 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 (i == 1 && (type == AST_SHIFT_LEFT || type == AST_SHIFT_RIGHT || type == AST_SHIFT_SLEFT || type == AST_SHIFT_SRIGHT)) { + while (children[i]->simplify(false, at_zero, in_lvalue_here, stage, -1, false) == true) { } + children[i]->detectSignWidth(width_hint_here, sign_hint_here); + } + did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here); if (did_something_here) did_something = true; } } for (auto &attr : attributes) { - while (attr.second->simplify(true, false, false, stage)) { } + while (attr.second->simplify(true, false, false, stage, -1, false)) { } } current_block = backup_current_block; @@ -351,7 +368,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); - while (varbuf->simplify(true, false, false, stage)) { } + while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint)) { } if (varbuf->type != AST_CONSTANT) log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -373,7 +390,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) { // eval 2nd expression AstNode *buf = while_ast->clone(); - while (buf->simplify(true, false, false, stage)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -412,7 +429,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) // eval 3rd expression buf = next_ast->children[1]->clone(); - while (buf->simplify(true, false, false, stage)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -446,7 +463,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) if (type == AST_GENIF && children.size() != 0) { AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { for (auto f : log_files) dumpAst(f, "verilog-ast> "); @@ -584,8 +601,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage) shift_expr = range->children[1]->clone(); AstNode *left_at_zero_ast = range->children[0]->clone(); AstNode *right_at_zero_ast = range->children[1]->clone(); - while (left_at_zero_ast->simplify(true, true, false, stage)) { } - while (right_at_zero_ast->simplify(true, true, false, stage)) { } + while (left_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } + while (right_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); @@ -636,16 +653,19 @@ skip_dynamic_range_lvalue_expansion:; wire_addr->str = id_addr; current_ast_mod->children.push_back(wire_addr); current_scope[wire_addr->str] = wire_addr; + while (wire_addr->simplify(true, false, false, 1, -1, false)) { } AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); wire_data->str = id_data; current_ast_mod->children.push_back(wire_data); current_scope[wire_data->str] = wire_data; + while (wire_data->simplify(true, false, false, 1, -1, false)) { } AstNode *wire_en = new AstNode(AST_WIRE); wire_en->str = id_en; current_ast_mod->children.push_back(wire_en); current_scope[wire_en->str] = wire_en; + while (wire_en->simplify(true, false, false, 1, -1, false)) { } std::vector<RTLIL::State> x_bits; x_bits.push_back(RTLIL::State::Sx); @@ -749,6 +769,7 @@ skip_dynamic_range_lvalue_expansion:; wire->is_input = false; wire->is_output = false; current_ast_mod->children.push_back(wire); + while (wire->simplify(true, false, false, 1, -1, false)) { } replace_rules[child->str] = wire->str; @@ -901,6 +922,8 @@ skip_dynamic_range_lvalue_expansion:; if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); newNode = mkconst_bits(y.bits, children[0]->is_signed); + // RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint), dummy_arg, sign_hint, false, width_hint); + // newNode = mkconst_bits(y.bits, sign_hint); } break; case AST_TERNARY: @@ -1048,12 +1071,14 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * wire_addr->is_reg = true; wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_addr); + while (wire_addr->simplify(true, false, false, 1, -1, false)) { } AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); wire_data->str = id_data; wire_data->is_reg = true; wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_data); + while (wire_data->simplify(true, false, false, 1, -1, false)) { } assert(block != NULL); size_t assign_idx = 0; @@ -1099,11 +1124,13 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * wire_addr->str = id_addr; wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_addr); + while (wire_addr->simplify(true, false, false, 1, -1, false)) { } AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); wire_data->str = id_data; wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_data); + while (wire_data->simplify(true, false, false, 1, -1, false)) { } AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc index 6fb37c7c1..05eb2af5d 100644 --- a/libs/ezsat/ezminisat.cc +++ b/libs/ezsat/ezminisat.cc @@ -17,8 +17,9 @@ * */ -// MiniSAT needs PRIi64 -#define __STDC_FORMAT_MACROS 1 +// needed for MiniSAT headers (see Minisat Makefile) +#define __STDC_LIMIT_MACROS +#define __STDC_FORMAT_MACROS #include "ezminisat.h" diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 0721d4fdf..583b8da9a 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -477,7 +477,7 @@ struct ShowWorker if (!design->selected_module(module->name)) continue; if (design->selected_whole_module(module->name)) { - if (module->get_bool_attribute("\\placeholder") > 0) { + if (module->get_bool_attribute("\\placeholder")) { log("Skipping placeholder module %s.\n", id2cstr(module->name)); continue; } else @@ -617,7 +617,7 @@ struct ShowPass : public Pass { if (format != "ps") { int modcount = 0; for (auto &mod_it : design->modules) { - if (mod_it.second->get_bool_attribute("\\placeholder") > 0) + if (mod_it.second->get_bool_attribute("\\placeholder")) continue; if (mod_it.second->cells.empty() && mod_it.second->connections.empty()) continue; diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 111f2f919..bebf7ce63 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -498,7 +498,7 @@ struct DfflibmapPass : public Pass { logmap_all(); for (auto &it : design->modules) - if (design->selected(it.second)) + if (design->selected(it.second) && !it.second->get_bool_attribute("\\placeholder")) dfflibmap(design, it.second); cell_mappings.clear(); diff --git a/techlibs/cmos/cmos_cells.lib b/techlibs/cmos/cmos_cells.lib index 1d7b8279c..164256c01 100644 --- a/techlibs/cmos/cmos_cells.lib +++ b/techlibs/cmos/cmos_cells.lib @@ -29,4 +29,18 @@ library(demo) { pin(Q) { direction: output; function: "IQ"; } } + cell(DFFSR) { + area: 18; + ff(IQ, IQN) { clocked_on: C; + next_state: D; + preset: S; + clear: R; } + pin(C) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(S) { direction: input; } + pin(R) { direction: input; } + } } diff --git a/techlibs/cmos/cmos_cells.v b/techlibs/cmos/cmos_cells.v index 802f58718..da75270cb 100644 --- a/techlibs/cmos/cmos_cells.v +++ b/techlibs/cmos/cmos_cells.v @@ -21,3 +21,15 @@ always @(posedge C) Q <= D; endmodule +module DFFSR(C, D, Q, S, R); +input C, D, S, R; +output reg Q; +always @(posedge C, posedge S, posedge R) + if (S) + Q <= 1'b1; + else if (R) + Q <= 1'b0; + else + Q <= D; +endmodule + diff --git a/tests/simple/vloghammer.v b/tests/simple/vloghammer.v index fffa35050..d1f55fdb4 100644 --- a/tests/simple/vloghammer.v +++ b/tests/simple/vloghammer.v @@ -73,3 +73,10 @@ module test10(a, b, c, y); assign y = ^(a ? b : c); endmodule +// module test11(a, b, y); +// input signed [3:0] a; +// input signed [3:0] b; +// output signed [5:0] y; +// assign y = -(5'd27); +// endmodule + |