Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/cpp/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ extern "C"
// Don't output the informational header on top of generated Verilog files
BOOL _noVerilogHeader;

// Emit a /*verilator hier_block*/ metacomment in the exported core
// design module to enable hierarchical Verilation
BOOL _emitVerilatorHierBlocks;

// True to enable warnings about missing [[transaction_size]]
BOOL _enableTransactionSizeWarning;

Expand Down
11 changes: 11 additions & 0 deletions compiler/cpp/verilog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7118,6 +7118,17 @@ class VerilogCompiler

coreModule.FinishPorts();

// Optionally mark this module as a Verilator hierarchical block to enable
// hierarchical Verilation. The /*verilator hier_block*/ metacomment must
// appear inside the module body, after the port list.
if (GetCodeGenConfig()._emitVerilatorHierBlocks)
{
coreModule.AddVerbatimOp(GetUnknownLocation(), [&](VerbatimWriter &writer)
{
writer << "/*verilator hier_block*/";
});
}

DeclareDebugVariables();

DeclareStringTable();
Expand Down
1 change: 1 addition & 0 deletions compiler/hs/app/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ data Options
, template_iterations :: Int
, template_passes :: Int
, using :: [String]
, verilator_hier_blocks :: Bool
, warnings_state :: [WarningState WarningKind]
, warnings_as_errors :: Bool
, work_list_size :: Int
Expand Down
1 change: 1 addition & 0 deletions compiler/hs/app/Options/CmdArgs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ compile = Compile
, no_fifo_stutter = def &= groupname "Code generation" &= help "Disable fifos stutter"
, no_debug_view = def &= groupname "Code generation" &= help "Disables debug views"
, no_verilog_header = def &= groupname "Code generation" &= help "Don't output the informational header on top of generated Verilog files"
, verilator_hier_blocks = def &= groupname "Code generation" &= help "Emit a /*verilator hier_block*/ metacomment in the exported core design module to enable hierarchical Verilation" &= name "verilator-hier-blocks" &= explicit
, code_coverage = def &= groupname "Code generation" &= help "Enable generation of code coverage using SystemVerilog coverpoints"
, code_coverage_mux_threshold = 0 &= groupname "Code generation" &= help "Only generate code coverage code for muxes with this number of cases; the default value of 0 means to generate for all muxes"
, optimize = 2 &= groupname "Code generation" &= help "Optimize generated code" &= typ "LEVEL" &= name "O"
Expand Down
1 change: 1 addition & 0 deletions compiler/hs/app/Options/FFI.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ instance Storable Options where
#{poke CodeGenOptions, _fifoWriteDelay } ptrCodegen fifo_write_delay
#{poke CodeGenOptions, _fifoMergeDistance } ptrCodegen fifo_merge_distance
#{poke CodeGenOptions, _noVerilogHeader } ptrCodegen no_verilog_header
#{poke CodeGenOptions, _emitVerilatorHierBlocks } ptrCodegen verilator_hier_blocks
#{poke CodeGenOptions, _optimize } ptrCodegen optimize
#{poke CodeGenOptions, _logicRegisterRatio } ptrCodegen register_ratio
#{poke CodeGenOptions, _maxLogicRegisterRatio } ptrCodegen max_register_ratio
Expand Down
12 changes: 12 additions & 0 deletions test/compiler/cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,15 @@ add_cli_test(skip_circt_lowering
TEST
"python3 ${CMAKE_CURRENT_SOURCE_DIR}/check_skip_circt_lowering.py ${CMAKE_CURRENT_BINARY_DIR}/skip_circt_lowering"
)

add_cli_test(verilator_hier_blocks
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/verilator_hier_blocks.k
OPTIONS
--backend=sv
--verilator-hier-blocks
--base-library=${CMAKE_SOURCE_DIR}/library/mini-base.k
--import-dir=${CMAKE_SOURCE_DIR}/library
--place-iterations=1
TEST
"python3 ${CMAKE_CURRENT_SOURCE_DIR}/check_verilator_hier_blocks.py ${CMAKE_CURRENT_BINARY_DIR}/verilator_hier_blocks"
)
Binary file not shown.
122 changes: 122 additions & 0 deletions test/compiler/cli/check_verilator_hier_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ratio of test code to implementation code is high. Maybe a more general way to test this would be better? There could be performance tests that measure verilator compilation time. Or there could be a general script that is used to verify many things like this (input=kanagawa code + regex to search for in generated RTL).

# Licensed under the MIT License.
"""
Verify the output of a `--verilator-hier-blocks` compile.

Contract of the flag: the exported design (core) module should carry a
`/*verilator hier_block*/` metacomment, which enables hierarchical
Verilation. Verilator requires this metacomment to appear inside the
module body (after the `module name(...);` port list). The metacomment is
*selective*: it is only emitted on the core design module, not on the
non-exported helper modules that the compiler generates alongside it (the
ESI wrapper, the per-basic-block modules, etc.).

Checks:
1. At least one .sv file exists.
2. The `/*verilator hier_block*/` metacomment appears inside at least one
generated module body (i.e. after that module's `module name(...);`
declaration, as Verilator requires).
3. At least one other generated module exists that does *not* contain the
metacomment, proving the annotation is confined to the exported design
module and is not blanket-applied to non-exported modules.

Exits non-zero on failure.
"""
import argparse
import re
import sys
from pathlib import Path

HIER_BLOCK = '/*verilator hier_block*/'

# Matches a SystemVerilog module declaration, e.g. `module Foo (`, capturing
# the module name.
MODULE_DECL = re.compile(r'(?:^|\s)module\s+(\w+)', re.MULTILINE)


def collect_modules(sv_files):
"""Return a list of (sv_name, module_name, body) for every module.

Modules do not nest in SystemVerilog, so each module body is the text
spanning from its `module name` declaration up to the next module
declaration (or end of file). Slicing on declaration boundaries avoids
relying on an `endmodule` token, which could otherwise appear inside a
comment or string and truncate the body prematurely.
"""
modules = []
for sv in sv_files:
text = sv.read_text()
decls = list(MODULE_DECL.finditer(text))
for i, decl in enumerate(decls):
end = decls[i + 1].start() if i + 1 < len(decls) else len(text)
modules.append((sv.name, decl.group(1), text[decl.start():end]))
return modules


def has_hier_block_after_port_list(module_body):
"""Return True if HIER_BLOCK appears after the declaration port list."""
port_list_end = module_body.find(');')
if port_list_end == -1:
return False
return module_body.find(HIER_BLOCK, port_list_end + 2) != -1


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('output_dir', help='Directory containing compiler outputs')
args = parser.parse_args()

output_dir = Path(args.output_dir)
if not output_dir.is_dir():
print(f"Output directory does not exist: {output_dir}")
return 1

sv_files = sorted(output_dir.glob('*.sv'))
if not sv_files:
print("--verilator-hier-blocks should produce a .sv file, but none was found.")
return 1

modules = collect_modules(sv_files)
if not modules:
names = ', '.join(s.name for s in sv_files)
print(f"No SystemVerilog modules found in {names}.")
return 1

with_meta = [m for m in modules if HIER_BLOCK in m[2]]
with_meta_after_ports = [m for m in modules if has_hier_block_after_port_list(m[2])]
without_meta = [m for m in modules if HIER_BLOCK not in m[2]]
module_names = ', '.join(m[1] for m in modules)

# Check 2: the metacomment must appear inside at least one module body.
Comment thread
teqdruid marked this conversation as resolved.
if not with_meta:
print(
f"--verilator-hier-blocks must emit {HIER_BLOCK!r} into a generated "
f"module, but none of these modules contain it: {module_names}."
)
return 1

if not with_meta_after_ports:
print(
f"--verilator-hier-blocks must emit {HIER_BLOCK!r} after a generated "
"module's port list (after `);`), but no module matches this placement "
f"requirement: {module_names}."
)
return 1

# Check 3: the metacomment must be selective. The compiler emits several
# non-exported helper modules (ESI wrapper, per-basic-block modules); these
# must not carry the metacomment.
if not without_meta:
print(
f"--verilator-hier-blocks should only annotate the exported design "
f"module, but every generated module contains {HIER_BLOCK!r}: "
f"{module_names}."
)
return 1

return 0


if __name__ == '__main__':
sys.exit(main())
14 changes: 14 additions & 0 deletions test/compiler/cli/verilator_hier_blocks.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Minimal program used by the --verilator-hier-blocks CLI test.
class VerilatorHierBlock
{
public:
uint32 TimesTwo(uint32 x)
{
return x + x;
}
}

export VerilatorHierBlock;
Loading