Add `-param`/`-port` options to `public_flat*` control directives (#6685)

This commit is contained in:
Geza Lore 2025-11-13 11:59:02 +00:00 committed by GitHub
parent 5c0ad5bd1f
commit a1056c6ae9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 284 additions and 24 deletions

View File

@ -2424,17 +2424,23 @@ The grammar of control commands is as follows:
.. option:: public [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"]
.. option:: public_flat [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"]
.. option:: public_flat [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
.. option:: public_flat_rd [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"]
.. option:: public_flat_rd [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
.. option:: public_flat_rw [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"] ["@(edge)"]
.. option:: public_flat_rw [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"] ["@(edge)"]
Sets the variable to be public. Same as
Sets the specified signal to be public. Same as
:option:`/*verilator&32;public*/` or
:option:`/*verilator&32;public_flat*/`, etc., metacomments. See
also :ref:`VPI Example`.
Using :code:`-port` only selects matching ports, :code:`-param` matches
parameters and localparams, and :code:`-var` matches any signal (including
ports, parameters, and regular variables or nets). In all three, the
following :code:`<signame>` can contain :code:`*` and :code:`?` wildcard
characters that match any substring or any single character respectively.
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
.. option:: sc_bv -module "<modulename>" [-task "<taskname>"] -var "<signame>"

View File

@ -176,6 +176,8 @@ public:
// Function or task: Have variables and properties
class V3ControlFTask final {
V3ControlVarResolver m_params; // Parameters in function/task
V3ControlVarResolver m_ports; // Ports in function/task
V3ControlVarResolver m_vars; // Variables in function/task
bool m_isolate = false; // Isolate function return
bool m_noinline = false; // Don't inline function/task
@ -188,9 +190,13 @@ public:
if (f.m_isolate) m_isolate = true;
if (f.m_noinline) m_noinline = true;
if (f.m_public) m_public = true;
m_params.update(f.m_params);
m_ports.update(f.m_ports);
m_vars.update(f.m_vars);
}
V3ControlVarResolver& params() { return m_params; }
V3ControlVarResolver& ports() { return m_ports; }
V3ControlVarResolver& vars() { return m_vars; }
void setIsolate(bool set) { m_isolate = set; }
@ -214,6 +220,8 @@ using V3ControlFTaskResolver = V3ControlWildcardResolver<V3ControlFTask>;
class V3ControlModule final {
V3ControlFTaskResolver m_tasks; // Functions/tasks in module
V3ControlVarResolver m_params; // Parameters in module
V3ControlVarResolver m_ports; // Ports in module
V3ControlVarResolver m_vars; // Variables in module
std::unordered_set<std::string> m_coverageOffBlocks; // List of block names for coverage_off
std::set<VPragmaType> m_modPragmas; // List of Pragmas for modules
@ -225,6 +233,8 @@ public:
void update(const V3ControlModule& m) {
m_tasks.update(m.m_tasks);
m_params.update(m.m_params);
m_ports.update(m.m_ports);
m_vars.update(m.m_vars);
for (const string& i : m.m_coverageOffBlocks) m_coverageOffBlocks.insert(i);
if (!m_inline) {
@ -237,6 +247,8 @@ public:
}
V3ControlFTaskResolver& ftasks() { return m_tasks; }
V3ControlVarResolver& params() { return m_params; }
V3ControlVarResolver& ports() { return m_ports; }
V3ControlVarResolver& vars() { return m_vars; }
void addCoverageBlockOff(const string& name) { m_coverageOffBlocks.insert(name); }
@ -700,7 +712,8 @@ void V3Control::addScopeTraceOn(bool on, const string& scope, int levels) {
}
void V3Control::addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& var, VAttrType attr, AstSenTree* sensep) {
VarSpecKind kind, const string& pattern, VAttrType attr,
AstSenTree* sensep) {
if (sensep) {
FileLine* const flp = sensep->fileline();
// Historical, not actually needed, only parsed for compatibility, delete it
@ -711,8 +724,20 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta
return;
}
}
if (kind != VarSpecKind::VAR) {
switch (attr) {
case VAttrType::VAR_PUBLIC_FLAT:
case VAttrType::VAR_PUBLIC_FLAT_RD:
case VAttrType::VAR_PUBLIC_FLAT_RW: break;
default:
fl->v3error("'"s + attr.ascii() + "' attribute does not accept -param/-port");
return;
}
}
// Semantics: Most of the attributes operate on signals
if (var.empty()) {
if (pattern.empty()) {
if (attr == VAttrType::VAR_ISOLATE_ASSIGNMENTS) {
if (ftask.empty()) {
fl->v3error("isolate_assignments only applies to signals or functions/tasks");
@ -737,15 +762,23 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta
} else if (!ftask.empty()) {
fl->v3error("Signals inside functions/tasks cannot be marked forceable");
} else {
V3ControlResolver::s().modules().at(module).vars().at(var).add(attr);
V3ControlResolver::s().modules().at(module).vars().at(pattern).add(attr);
}
} else {
V3ControlModule& mod = V3ControlResolver::s().modules().at(module);
if (ftask.empty()) {
mod.vars().at(var).add(attr);
} else {
mod.ftasks().at(ftask).vars().at(var).add(attr);
}
V3ControlVar& controlVar = [&]() -> V3ControlVar& {
if (ftask.empty()) {
if (kind == VarSpecKind::PARAM) return mod.params().at(pattern);
if (kind == VarSpecKind::PORT) return mod.ports().at(pattern);
UASSERT_OBJ(kind == VarSpecKind::VAR, fl, "Unexpected VarSpecKind");
return mod.vars().at(pattern);
}
if (kind == VarSpecKind::PARAM) return mod.ftasks().at(ftask).params().at(pattern);
if (kind == VarSpecKind::PORT) return mod.ftasks().at(ftask).ports().at(pattern);
UASSERT_OBJ(kind == VarSpecKind::VAR, fl, "Unexpected VarSpecKind");
return mod.ftasks().at(ftask).vars().at(pattern);
}();
controlVar.add(attr);
}
}
}
@ -794,20 +827,30 @@ void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) {
if (ftp) ftp->apply(ftaskp);
}
template <typename T_Resolver>
static void resolveThenApply(T_Resolver& resolver, AstVar* varp) {
const std::string name = varp->prettyDehashOrigOrName();
if (const V3ControlVar* const vp = resolver.vars().resolve(name)) vp->apply(varp);
if (varp->isParam()) {
if (const V3ControlVar* const vp = resolver.params().resolve(name)) vp->apply(varp);
}
if (varp->isIO()) {
if (const V3ControlVar* const vp = resolver.ports().resolve(name)) vp->apply(varp);
}
}
void V3Control::applyVarAttr(const AstNodeModule* modulep, const AstNodeFTask* ftaskp,
AstVar* varp) {
const V3ControlVar* vp;
V3ControlModule* const modp
= V3ControlResolver::s().modules().resolve(modulep->prettyDehashOrigOrName());
if (!modp) return;
if (ftaskp) {
V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->prettyDehashOrigOrName());
if (!ftp) return;
vp = ftp->vars().resolve(varp->prettyDehashOrigOrName());
resolveThenApply(*ftp, varp);
} else {
vp = modp->vars().resolve(varp->prettyDehashOrigOrName());
resolveThenApply(*modp, varp);
}
if (vp) vp->apply(varp);
}
int V3Control::getHierWorkers(const string& model) {

View File

@ -29,6 +29,12 @@
class V3Control final {
public:
enum class VarSpecKind {
PARAM, // Select only matching parameters
PORT, // Select only matching ports
VAR // Select any matching AstVar (including params and ports)
};
static void addCaseFull(const string& file, int lineno);
static void addCaseParallel(const string& file, int lineno);
static void addCoverageBlockOff(const string& file, int lineno);
@ -44,7 +50,8 @@ public:
uint64_t cost);
static void addScopeTraceOn(bool on, const string& scope, int levels);
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& signal, VAttrType type, AstSenTree* nodep);
VarSpecKind kind, const string& pattern, VAttrType type,
AstSenTree* nodep);
static void applyCase(AstCase* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);

View File

@ -37,6 +37,7 @@ public:
VVarType m_varDecl = VVarType::UNKNOWN; // Type for next signal declaration (reg/wire/etc)
VDirection m_varIO = VDirection::NONE; // Direction for next signal declaration (reg/wire/etc)
VLifetime m_varLifetime; // Static/Automatic for next signal
V3Control::VarSpecKind m_vltVarSpecKind = V3Control::VarSpecKind::VAR;
bool m_impliedDecl = false; // Allow implied wire declarations
bool m_varDeclTyped = false; // Var got reg/wire for dedup check
bool m_pinAnsi = false; // In ANSI parameter or port list

View File

@ -113,7 +113,7 @@ struct V3ParseBisonYYSType final {
bool flag = false; // Passed up some rules
union {
V3Number* nump;
string* strp;
std::string* strp;
int cint;
double cdouble;
bool cbool;

View File

@ -155,6 +155,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
-?"-model" { FL; return yVLT_D_MODEL; }
-?"-module" { FL; return yVLT_D_MODULE; }
-?"-mtask" { FL; return yVLT_D_MTASK; }
-?"-param" { FL; return yVLT_D_PARAM; }
-?"-port" { FL; return yVLT_D_PORT; }
-?"-rule" { FL; return yVLT_D_RULE; }
-?"-scope" { FL; return yVLT_D_SCOPE; }
-?"-task" { FL; return yVLT_D_TASK; }

View File

@ -282,6 +282,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVLT_D_MODEL "--model"
%token<fl> yVLT_D_MODULE "--module"
%token<fl> yVLT_D_MTASK "--mtask"
%token<fl> yVLT_D_PARAM "--param"
%token<fl> yVLT_D_PORT "--port"
%token<fl> yVLT_D_RULE "--rule"
%token<fl> yVLT_D_SCOPE "--scope"
%token<fl> yVLT_D_TASK "--task"
@ -995,6 +997,14 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
// Blank lines for type insertion
%start source_text
@ -7991,9 +8001,9 @@ vltItem:
} else {
V3Control::addScopeTraceOn(true, *$2, $3->toUInt());
}}
| vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrVarE attr_event_controlE
{ V3Control::addVarAttr($<fl>1, *$2, *$3, *$4, $1, $5); }
| vltVarAttrFrontDeprecated vltDModuleE vltDFTaskE vltVarAttrVarE
| vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrSpecE attr_event_controlE
{ V3Control::addVarAttr($<fl>1, *$2, *$3, GRAMMARP->m_vltVarSpecKind, *$4, $1, $5); }
| vltVarAttrFrontDeprecated vltDModuleE vltDFTaskE vltVarAttrSpecE
{ /* Historical, now has no effect */ }
| vltInlineFront vltDModuleE vltDFTaskE
{ V3Control::addInline($<fl>1, *$2, *$3, $1); }
@ -8115,9 +8125,15 @@ vltInlineFront<cbool>:
| yVLT_NO_INLINE { $$ = false; }
;
vltVarAttrVarE<strp>:
/* empty */ { static string empty; $$ = &empty; }
| yVLT_D_VAR str { $$ = $2; }
vltVarAttrSpecE<strp>:
/* empty */
{ GRAMMARP->m_vltVarSpecKind = V3Control::VarSpecKind::VAR; static std::string empty; $$ = &empty; }
| yVLT_D_PARAM str
{ GRAMMARP->m_vltVarSpecKind = V3Control::VarSpecKind::PARAM; $$ = $2; }
| yVLT_D_PORT str
{ GRAMMARP->m_vltVarSpecKind = V3Control::VarSpecKind::PORT; $$ = $2; }
| yVLT_D_VAR str
{ GRAMMARP->m_vltVarSpecKind = V3Control::VarSpecKind::VAR; $$ = $2; }
;
vltVarAttrFront<attrtypeen>:

View File

@ -0,0 +1,35 @@
scopesDump:
SCOPE 0x#: TOP
VAR 0x#: top_port_i
VAR 0x#: top_port_o
SCOPE 0x#: top
VAR 0x#: TOP_LOCALPARAM
VAR 0x#: TOP_PARAM
VAR 0x#: top_port_i
VAR 0x#: top_port_o
SCOPE 0x#: top.i_mid_0
VAR 0x#: MID_LOCALPARAM
VAR 0x#: MID_PARM
VAR 0x#: mid_tmp_b
SCOPE 0x#: top.i_mid_0.i_sub_0
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
VAR 0x#: sub_port_i
VAR 0x#: sub_port_o
SCOPE 0x#: top.i_mid_0.i_sub_1
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
VAR 0x#: sub_port_i
VAR 0x#: sub_port_o
SCOPE 0x#: top.i_mid_1
VAR 0x#: MID_LOCALPARAM
VAR 0x#: MID_PARM
VAR 0x#: mid_tmp_b
SCOPE 0x#: top.i_mid_1.i_sub_0
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
VAR 0x#: sub_port_i
VAR 0x#: sub_port_o
SCOPE 0x#: top.i_mid_1.i_sub_1
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
VAR 0x#: sub_port_i
VAR 0x#: sub_port_o
*-* All Finished *-*

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
test.execute()
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
test.passes()

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module top #(
parameter int TOP_PARAM = 42
) (
input int top_port_i,
output int top_port_o
);
localparam int TOP_LOCALPARAM = 111;
int top_tmp;
mid #(.MID_PARM(TOP_PARAM + TOP_LOCALPARAM)) i_mid_0(top_port_i, top_tmp);
mid #(.MID_PARM(TOP_PARAM + TOP_LOCALPARAM)) i_mid_1(top_tmp, top_port_o);
function static void f(input int top_f_port_i, output int top_f_port_o);
localparam int TOP_F_LOCALPARAM = 1;
top_f_port_o = top_f_port_i + TOP_F_LOCALPARAM;
endfunction
initial begin
$c("Verilated::scopesDump();");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module mid #(
parameter int MID_PARM = 42
) (
input int mid_port_i,
output int mid_port_o
);
localparam int MID_LOCALPARAM = 11;
int mid_tmp_a;
int mid_tmp_b;
sub #(.SUB_PARAM(MID_PARM + MID_LOCALPARAM)) i_sub_0(mid_port_i, mid_tmp_a);
assign mid_tmp_b = mid_tmp_a;
sub #(.SUB_PARAM(MID_PARM + MID_LOCALPARAM)) i_sub_1(mid_tmp_b, mid_port_o);
function static void f(input int mid_f_port_i, output int mid_f_port_o);
localparam int MID_F_LOCALPARAM = 1;
mid_f_port_o = mid_f_port_i + MID_F_LOCALPARAM;
endfunction
endmodule
module sub #(
parameter int SUB_PARAM = 42
) (
input int sub_port_i,
output int sub_port_o
);
localparam int SUB_LOCALPARAM = 1;
int sub_tmp;
assign sub_tmp = sub_port_i + SUB_PARAM;
assign sub_port_o = sub_tmp + SUB_LOCALPARAM;
function static void f(input int sub_f_port_i, output int sub_f_port_o);
localparam int SUB_F_LOCALPARAM = 1;
sub_f_port_o = sub_f_port_i + SUB_F_LOCALPARAM;
endfunction
endmodule

View File

@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`verilator_config
public_flat_rd -module "top" -param "*"
public_flat_rw -module "top" -port "*"
public_flat_rd -module "mid" -param "*"
public_flat_rw -module "mid" -function "*" -port "top_f_port_o"
public_flat -module "mid" -var "mid_tmp_b"
public_flat -module "sub" -port "*"
public_flat_rd -module "sub" -function "*" -param "SUB_F_LOCALPARAM"

View File

@ -0,0 +1,8 @@
%Error: t/t_vlt_var_spec_bad.vlt:9:1: 'VAR_SC_BV' attribute does not accept -param/-port
9 | sc_bv -module "top" -param "*"
| ^~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_vlt_var_spec_bad.vlt:10:1: 'VAR_SC_BV' attribute does not accept -param/-port
10 | sc_bv -module "top" -port "*"
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
# Doesn't matter which one
test.top_filename = "t/t_vlt_public_spec.v"
test.compile(verilator_flags2=[test.name + ".vlt"],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,10 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`verilator_config
sc_bv -module "top" -param "*"
sc_bv -module "top" -port "*"