Merge pull request #945 from YosysHQ/clifford/libwb

New behavior for read_verilog handling of whiteboxes
This commit is contained in:
Clifford Wolf 2019-04-22 08:38:52 +02:00 committed by GitHub
commit 632a666448
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 41 deletions

View File

@ -310,12 +310,20 @@ Verilog Attributes and non-standard features
that have the same ports as the real thing but do not contain information 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 on the internal configuration. This modules are only used by the synthesis
passes to identify input and output ports of cells. The Verilog backend passes to identify input and output ports of cells. The Verilog backend
also does not output blackbox modules on default. also does not output blackbox modules on default. ``read_verilog``, unless
called with ``-noblackbox`` will automatically set the blackbox attribute
on any empty module it reads.
- The ``noblackbox`` attribute set on an empty module prevents ``read_verilog``
from automatically setting the blackbox attribute on the module.
- The ``whitebox`` attribute on modules triggers the same behavior as - The ``whitebox`` attribute on modules triggers the same behavior as
``blackbox``, but is for whitebox modules, i.e. library modules that ``blackbox``, but is for whitebox modules, i.e. library modules that
contain a behavioral model of the cell type. contain a behavioral model of the cell type.
- The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog``
is run in `-lib` mode. Otherwise it's automatically removed.
- The ``dynports`` attribute is used by the Verilog front-end to mark modules - The ``dynports`` attribute is used by the Verilog front-end to mark modules
that have ports with a width that depends on a parameter. that have ports with a width that depends on a parameter.

View File

@ -46,7 +46,7 @@ namespace AST {
// instantiate global variables (private API) // instantiate global variables (private API)
namespace AST_INTERNAL { namespace AST_INTERNAL {
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_wb, flag_noopt, flag_icells, flag_autowire; bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_autowire;
AstNode *current_ast, *current_ast_mod; AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope; std::map<std::string, AstNode*> current_scope;
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
@ -942,6 +942,20 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
if (!defer) if (!defer)
{ {
bool blackbox_module = flag_lib;
if (!blackbox_module && !flag_noblackbox) {
blackbox_module = true;
for (auto child : ast->children) {
if (child->type == AST_WIRE && (child->is_input || child->is_output))
continue;
if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
continue;
blackbox_module = false;
break;
}
}
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
if (flag_dump_ast2) { if (flag_dump_ast2) {
@ -956,18 +970,63 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
log("--- END OF AST DUMP ---\n"); log("--- END OF AST DUMP ---\n");
} }
if (flag_wb) { if (flag_nowb && ast->attributes.count("\\whitebox")) {
if (!ast->attributes.count("\\whitebox")) delete ast->attributes.at("\\whitebox");
goto blackbox_module; ast->attributes.erase("\\whitebox");
AstNode *n = ast->attributes.at("\\whitebox");
if (n->type != AST_CONSTANT)
log_file_error(ast->filename, ast->linenum, "Whitebox attribute with non-constant value!\n");
if (!n->asBool())
goto blackbox_module;
} }
if (flag_lib) { if (ast->attributes.count("\\lib_whitebox")) {
blackbox_module: if (!flag_lib || flag_nowb) {
delete ast->attributes.at("\\lib_whitebox");
ast->attributes.erase("\\lib_whitebox");
} else {
if (ast->attributes.count("\\whitebox")) {
delete ast->attributes.at("\\whitebox");
ast->attributes.erase("\\whitebox");
}
AstNode *n = ast->attributes.at("\\lib_whitebox");
ast->attributes["\\whitebox"] = n;
ast->attributes.erase("\\lib_whitebox");
}
}
if (!blackbox_module && ast->attributes.count("\\blackbox")) {
AstNode *n = ast->attributes.at("\\blackbox");
if (n->type != AST_CONSTANT)
log_file_error(ast->filename, ast->linenum, "Got blackbox attribute with non-constant value!\n");
blackbox_module = n->asBool();
}
if (blackbox_module && ast->attributes.count("\\whitebox")) {
AstNode *n = ast->attributes.at("\\whitebox");
if (n->type != AST_CONSTANT)
log_file_error(ast->filename, ast->linenum, "Got whitebox attribute with non-constant value!\n");
blackbox_module = !n->asBool();
}
if (ast->attributes.count("\\noblackbox")) {
if (blackbox_module) {
AstNode *n = ast->attributes.at("\\noblackbox");
if (n->type != AST_CONSTANT)
log_file_error(ast->filename, ast->linenum, "Got noblackbox attribute with non-constant value!\n");
blackbox_module = !n->asBool();
}
delete ast->attributes.at("\\noblackbox");
ast->attributes.erase("\\noblackbox");
}
if (blackbox_module)
{
if (ast->attributes.count("\\whitebox")) {
delete ast->attributes.at("\\whitebox");
ast->attributes.erase("\\whitebox");
}
if (ast->attributes.count("\\lib_whitebox")) {
delete ast->attributes.at("\\lib_whitebox");
ast->attributes.erase("\\lib_whitebox");
}
std::vector<AstNode*> new_children; std::vector<AstNode*> new_children;
for (auto child : ast->children) { for (auto child : ast->children) {
if (child->type == AST_WIRE && (child->is_input || child->is_output)) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
@ -980,12 +1039,12 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
delete child; delete child;
} }
} }
ast->children.swap(new_children); ast->children.swap(new_children);
if (ast->attributes.count("\\whitebox")) {
delete ast->attributes.at("\\whitebox"); if (ast->attributes.count("\\blackbox") == 0) {
ast->attributes.erase("\\whitebox"); ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
} }
ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
} }
ignoreThisSignalsInInitial = RTLIL::SigSpec(); ignoreThisSignalsInInitial = RTLIL::SigSpec();
@ -1024,8 +1083,9 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
current_module->nomeminit = flag_nomeminit; current_module->nomeminit = flag_nomeminit;
current_module->nomem2reg = flag_nomem2reg; current_module->nomem2reg = flag_nomem2reg;
current_module->mem2reg = flag_mem2reg; current_module->mem2reg = flag_mem2reg;
current_module->noblackbox = flag_noblackbox;
current_module->lib = flag_lib; current_module->lib = flag_lib;
current_module->wb = flag_wb; current_module->nowb = flag_nowb;
current_module->noopt = flag_noopt; current_module->noopt = flag_noopt;
current_module->icells = flag_icells; current_module->icells = flag_icells;
current_module->autowire = flag_autowire; current_module->autowire = flag_autowire;
@ -1042,7 +1102,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
// create AstModule instances for all modules in the AST tree and add them to 'design' // 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_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool wb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
{ {
current_ast = ast; current_ast = ast;
flag_dump_ast1 = dump_ast1; flag_dump_ast1 = dump_ast1;
@ -1055,8 +1115,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_nomeminit = nomeminit; flag_nomeminit = nomeminit;
flag_nomem2reg = nomem2reg; flag_nomem2reg = nomem2reg;
flag_mem2reg = mem2reg; flag_mem2reg = mem2reg;
flag_noblackbox = noblackbox;
flag_lib = lib; flag_lib = lib;
flag_wb = wb; flag_nowb = nowb;
flag_noopt = noopt; flag_noopt = noopt;
flag_icells = icells; flag_icells = icells;
flag_autowire = autowire; flag_autowire = autowire;
@ -1390,8 +1451,9 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
flag_nomeminit = nomeminit; flag_nomeminit = nomeminit;
flag_nomem2reg = nomem2reg; flag_nomem2reg = nomem2reg;
flag_mem2reg = mem2reg; flag_mem2reg = mem2reg;
flag_noblackbox = noblackbox;
flag_lib = lib; flag_lib = lib;
flag_wb = wb; flag_nowb = nowb;
flag_noopt = noopt; flag_noopt = noopt;
flag_icells = icells; flag_icells = icells;
flag_autowire = autowire; flag_autowire = autowire;

View File

@ -283,13 +283,13 @@ namespace AST
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code // 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_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
bool nomem2reg, bool mem2reg, bool lib, bool wb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
// parametric modules are supported directly by the AST library // parametric modules are supported directly by the AST library
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
struct AstModule : RTLIL::Module { struct AstModule : RTLIL::Module {
AstNode *ast; AstNode *ast;
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, wb, noopt, icells, autowire; bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, autowire;
~AstModule() YS_OVERRIDE; ~AstModule() YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;

View File

@ -145,12 +145,18 @@ struct VerilogFrontend : public Frontend {
log(" -nodpi\n"); log(" -nodpi\n");
log(" disable DPI-C support\n"); log(" disable DPI-C support\n");
log("\n"); log("\n");
log(" -noblackbox\n");
log(" do not automatically add a (* blackbox *) attribute to an\n");
log(" empty module.\n");
log("\n");
log(" -lib\n"); log(" -lib\n");
log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); log(" only create empty blackbox modules. This implies -DBLACKBOX.\n");
log(" modules with the (* whitebox *) attribute will be preserved.\n");
log(" (* lib_whitebox *) will be treated like (* whitebox *).\n");
log("\n"); log("\n");
log(" -wb\n"); log(" -nowb\n");
log(" like -lib, except do not touch modules with the whitebox\n"); log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n");
log(" attribute set. This also implies -DBLACKBOX.\n"); log(" all modules.\n");
log("\n"); log("\n");
log(" -noopt\n"); log(" -noopt\n");
log(" don't perform basic optimizations (such as const folding) in the\n"); log(" don't perform basic optimizations (such as const folding) in the\n");
@ -231,8 +237,9 @@ struct VerilogFrontend : public Frontend {
formal_mode = false; formal_mode = false;
norestrict_mode = false; norestrict_mode = false;
assume_asserts_mode = false; assume_asserts_mode = false;
noblackbox_mode = false;
lib_mode = false; lib_mode = false;
wb_mode = false; nowb_mode = false;
default_nettype_wire = true; default_nettype_wire = true;
log_header(design, "Executing Verilog-2005 frontend.\n"); log_header(design, "Executing Verilog-2005 frontend.\n");
@ -334,14 +341,17 @@ struct VerilogFrontend : public Frontend {
flag_nodpi = true; flag_nodpi = true;
continue; continue;
} }
if (arg == "-lib" && !wb_mode) { if (arg == "-noblackbox") {
noblackbox_mode = true;
continue;
}
if (arg == "-lib") {
lib_mode = true; lib_mode = true;
defines_map["BLACKBOX"] = string(); defines_map["BLACKBOX"] = string();
continue; continue;
} }
if (arg == "-wb" && !lib_mode) { if (arg == "-nowb") {
wb_mode = true; nowb_mode = true;
defines_map["BLACKBOX"] = string();
continue; continue;
} }
if (arg == "-noopt") { if (arg == "-noopt") {
@ -439,7 +449,8 @@ struct VerilogFrontend : public Frontend {
if (flag_nodpi) if (flag_nodpi)
error_on_dpi_function(current_ast); error_on_dpi_function(current_ast);
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, wb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
flag_nomeminit, flag_nomem2reg, flag_mem2reg, noblackbox_mode, lib_mode, nowb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
if (!flag_nopp) if (!flag_nopp)
delete lexin; delete lexin;

View File

@ -69,11 +69,14 @@ namespace VERILOG_FRONTEND
// running in -assert-assumes mode // running in -assert-assumes mode
extern bool assert_assumes_mode; extern bool assert_assumes_mode;
// running in -noblackbox mode
extern bool noblackbox_mode;
// running in -lib mode // running in -lib mode
extern bool lib_mode; extern bool lib_mode;
// running in -wb mode // running in -nowb mode
extern bool wb_mode; extern bool nowb_mode;
// lexer input stream // lexer input stream
extern std::istream *lexin; extern std::istream *lexin;

View File

@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND {
std::vector<char> case_type_stack; std::vector<char> case_type_stack;
bool do_not_require_port_stubs; bool do_not_require_port_stubs;
bool default_nettype_wire; bool default_nettype_wire;
bool sv_mode, formal_mode, lib_mode, wb_mode; bool sv_mode, formal_mode, noblackbox_mode, lib_mode, nowb_mode;
bool noassert_mode, noassume_mode, norestrict_mode; bool noassert_mode, noassume_mode, norestrict_mode;
bool assume_asserts_mode, assert_assumes_mode; bool assume_asserts_mode, assert_assumes_mode;
bool current_wire_rand, current_wire_const; bool current_wire_rand, current_wire_const;
@ -1906,7 +1906,7 @@ basic_expr:
if ($4->substr(0, 1) != "'") if ($4->substr(0, 1) != "'")
frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. (<expr>)<constval> , while %s is not a sized constant.", $4->c_str()); frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. (<expr>)<constval> , while %s is not a sized constant.", $4->c_str());
AstNode *bits = $2; AstNode *bits = $2;
AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if (val == NULL) if (val == NULL)
log_error("Value conversion failed: `%s'\n", $4->c_str()); log_error("Value conversion failed: `%s'\n", $4->c_str());
$$ = new AstNode(AST_TO_BITS, bits, val); $$ = new AstNode(AST_TO_BITS, bits, val);
@ -1917,7 +1917,7 @@ basic_expr:
frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. <ID>\'d0, while %s is not a sized constant.", $2->c_str()); frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. <ID>\'d0, while %s is not a sized constant.", $2->c_str());
AstNode *bits = new AstNode(AST_IDENTIFIER); AstNode *bits = new AstNode(AST_IDENTIFIER);
bits->str = *$1; bits->str = *$1;
AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if (val == NULL) if (val == NULL)
log_error("Value conversion failed: `%s'\n", $2->c_str()); log_error("Value conversion failed: `%s'\n", $2->c_str());
$$ = new AstNode(AST_TO_BITS, bits, val); $$ = new AstNode(AST_TO_BITS, bits, val);
@ -1925,14 +1925,14 @@ basic_expr:
delete $2; delete $2;
} | } |
TOK_CONSTVAL TOK_CONSTVAL { TOK_CONSTVAL TOK_CONSTVAL {
$$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if ($$ == NULL || (*$2)[0] != '\'') if ($$ == NULL || (*$2)[0] != '\'')
log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str());
delete $1; delete $1;
delete $2; delete $2;
} | } |
TOK_CONSTVAL { TOK_CONSTVAL {
$$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if ($$ == NULL) if ($$ == NULL)
log_error("Value conversion failed: `%s'\n", $1->c_str()); log_error("Value conversion failed: `%s'\n", $1->c_str());
delete $1; delete $1;

View File

@ -1036,7 +1036,7 @@ struct TechmapPass : public Pass {
simplemap_get_mappers(worker.simplemap_mappers); simplemap_get_mappers(worker.simplemap_mappers);
std::vector<std::string> map_files; std::vector<std::string> map_files;
std::string verilog_frontend = "verilog -nooverwrite"; std::string verilog_frontend = "verilog -nooverwrite -noblackbox";
int max_iter = -1; int max_iter = -1;
size_t argidx; size_t argidx;

View File

@ -7,7 +7,7 @@ use_modelsim=false
verbose=false verbose=false
keeprunning=false keeprunning=false
makejmode=false makejmode=false
frontend="verilog" frontend="verilog -noblackbox"
backend_opts="-noattr -noexpr -siminit" backend_opts="-noattr -noexpr -siminit"
autotb_opts="" autotb_opts=""
include_opts="" include_opts=""
@ -137,7 +137,7 @@ do
egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext} egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext}
else else
"$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn} "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn}
frontend="verilog" frontend="verilog -noblackbox"
fi fi
if [ ! -f ../${bn}_tb.v ]; then if [ ! -f ../${bn}_tb.v ]; then

View File

@ -53,6 +53,7 @@ echo -n " no explicit top - "
module noTop(a, y); module noTop(a, y);
input a; input a;
output [31:0] y; output [31:0] y;
assign y = a;
endmodule endmodule
EOV EOV
hierarchy -auto-top hierarchy -auto-top