aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README9
-rw-r--r--backends/blif/blif.cc2
-rw-r--r--frontends/ast/ast.cc32
-rw-r--r--frontends/ast/ast.h6
-rw-r--r--frontends/ast/genrtlil.cc80
-rw-r--r--frontends/ast/simplify.cc49
-rw-r--r--libs/ezsat/ezminisat.cc5
-rw-r--r--passes/cmds/show.cc4
-rw-r--r--passes/techmap/dfflibmap.cc2
-rw-r--r--techlibs/cmos/cmos_cells.lib14
-rw-r--r--techlibs/cmos/cmos_cells.v12
-rw-r--r--tests/simple/vloghammer.v7
12 files changed, 189 insertions, 33 deletions
diff --git a/README b/README
index 9d48f5d86..ef482b0f1 100644
--- a/README
+++ b/README
@@ -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
+