[TOC]
Hdlparse is a simple Python package implementing a rudimentary parser for SystemVerilog and VHDL.
It is not capable of fully parsing the entire language. Rather, it is meant to extract enough key information from a source file to create generated documentation.
This library is forked from kevinpt via zhelnio.
- Download or clone https://github.com/sd2k9/pyHDLParser
to directory (e.g.
/opt/hdlparse) as root - Create symbolic link
cd /usr/local/bin; ln -s /opt/hdlparse/bin/sv_extract_port_param_as_markdown
The script sv_extract_port_param_as_markdown extracts port and parameter tables from a
SystemVerilog file and writes them as Markdown tables to separate output files.
When there are multiple modules in the file only the first one is parsed.
python sv_extract_port_param_as_markdown \
-i <input.sv> \
--oport <ports_output.md> \
--oparam <params_output.md> \
[--force]| Option | Description |
|---|---|
-i, --ifile |
Input SystemVerilog file |
--oport |
Output Markdown file for ports |
--oparam |
Output Markdown file for parameters |
--force |
Overwrite existing output files |
-h, --help |
Show help message and exit |
-V, --version |
Show program version and exit |
Ports table (--oport):
| Direction | Width | Name | Description |
|-----------|-------|-------|-------------|
| Input | 1 | clk | Clock input |
| Output | [7:0] | data | |Parameters table (--oparam):
| Parameter Name | Default | Description |
|----------------|---------|---------------------|
| DATA_WIDTH | 8 | Width of data bus |When the module source code contains section metacomments the output is split into labelled sections with separate table headers.
Underscores in names are automatically escaped for LaTeX/Pandoc compatibility.
Use the VerilogExtractor class to parse Verilog source files or strings.
It also caches parsed objects.
import hdlparse.verilog_parser as vlog
vlog_ex = vlog.VerilogExtractor()
# Parse from a file
vlog_mods = vlog_ex.extract_objects(fname='example.sv')
# Parse from a string
with open('example.sv', 'rt') as fh:
code = fh.read()
vlog_mods = vlog_ex.extract_objects_from_source(text=code)Both methods return a list of VerilogModule objects.
You can pass an optional object type to filter the results for just that type.
Currently only VerilogModule is supported:
vlog_mods = vlog_ex.extract_objects(fname='example.sv', type_filter=VerilogModule)
vlog_mods = vlog_ex.extract_objects_from_source(text=code, type_filter=VerilogModule)If you don't require object caching you can use the following functions instead.
vlog_ex = vlog.parse_verilog_file('example.sv')
vlog_ex = vlog.parse_verilog(code)VerilogModule attributes:
| Attribute | Description |
|---|---|
name |
Module name |
generics |
List of VerilogParameter (parameters) |
ports |
List of VerilogParameter (ports) |
paramsections |
Dict mapping parameter index to section label |
portsections |
Dict mapping port index to section label |
desc |
Description |
Each VerilogParameter has:
| Attribute | Description |
|---|---|
name |
Parameter/port name |
mode |
Ports: Port mode, e.g. input, output |
Parameters: param |
|
data_type |
Net/variable type, e.g. wire, reg |
data_size |
Vector range string or None, e.g. [7:0] |
default_value |
Default value string or None |
desc |
Description |
Example code:
for m in vlog_mods:
print(f"Module {m.name}")
print(' Parameters:')
for p in m.generics:
print('\t{:20}{:8}{}'.format(p.name, p.mode, p.data_type))
print(' Ports:')
for p in m.ports:
print('\t{:20}{:8}{}'.format(p.name, p.mode, p.data_type))Output:
Module "modulename":
Parameters:
foo param real
bar param real
baz param real
zip param signed [7:0]
Ports:
x input
x2 input
y inout
y2_long_output inout
z output wire [4:1]
z2 output wire [4:1]Description comments can follow after a statement.
parameter C_S_AXI_DATA_WIDTH = 32, ///< AXI bus width, must be 32They can also span multiple lines.
parameter PIXEL_WIDTH = 10, ///< Width of pixel data inputs in bits.
///< Allowed values: 8, 10, 12Or start on the following line.
parameter CONFIGURATION = "VITOM_4K_HEAD", // Optional source code comment not being extracted
///< Module configuration, see [section 8.2](#module-configuration-and-device-support)And span multiple lines.
parameter logic [MGT_TX_CHANNELS-1:0] TX_FIFO_ENABLE = {MGT_TX_CHANNELS{1'b0}},
///< Enable DownLink FIFOs (0:\ remove logic, 1:\ enable),
///< Bit position corresponds to channelSystemVerilog source can include special comments to provide descriptions and logical sections.
//# {{ My Name }}When inside a port section it only applies to the following ports.
When inside a parameter section it only applies to the following parameters.
Outside ports and parameters it applies to both.
These section descriptions are accessible via the
dictionaries paramsections / portsections on the module.
The key is the parameter or port index following the section metacomment, starting with 0.
vlog.is_verilog('design.sv') # True
vlog.is_verilog('design.v') # True
vlog.is_verilog('design.vlog') # TrueThe VHDL parser can extract a variety of different objects from source code. It can be used to access package definitions and component declarations, type and subtype definitions, functions, and procedures found within a package. It will not process entity declarations or nested subprograms and types.
Use the VhdlExtractor class to parse VHDL source files or source strings.
It also caches parsed objects.
import hdlparse.vhdl_parser as vhdl
vhdl_ex = vhdl.VhdlExtractor()
# Parse from a file
vhdl_objs = vhdl_ex.extract_objects(fname='example.vhdl')
# Parse from a string
import io
with io.open('example.vhdl', 'rt', encoding='latin-1') as fh:
code = fh.read()
vhdl_objs = vhdl_ex.extract_objects_from_source(text=code)Both methods return a list of parsed objects subclassed from VhdlObject.
You can pass an optional subclass of VhdlObject to filter the results for just that type:
vhdl_comps = vhdl_ex.extract_objects(fname='example.vhdl', type_filter=VhdlComponent)If you don't require object caching you can use the following functions instead
vhdl_objs = parse_vhdl_file('example.vhdl')
vhdl_objs = parse_vhdl(code)Each port and generic is an instance of VhdlParameter containing
the name, mode (input, output, inout) and type.
for c in vhdl_comps:
print('Component "{}":'.format(c.name))
print(' Generics:')
for p in c.generics:
print('\t{:20}{:8} {}'.format(p.name, p.mode, p.data_type))
print(' Ports:')
for p in c.ports:
print('\t{:20}{:8} {}'.format(p.name, p.mode, p.data_type))Output:
Component "demo":
Generics:
GENERIC1 in boolean
GENERIC2 in integer
Ports:
a in std_ulogic
b in std_ulogic
c out std_ulogic_vector(7 downto 0)
d out std_ulogic_vector(7 downto 0)
e inout unsigned(7 downto 0)
f inout unsigned(7 downto 0)| Class | kind |
Description |
|---|---|---|
VhdlPackage |
package |
Package declaration |
VhdlComponent |
component |
Component declaration (ports, generics) |
VhdlEntity |
entity |
Entity declaration (ports, generics) |
VhdlFunction |
function |
Function declaration with parameters |
VhdlProcedure |
procedure |
Procedure declaration with parameters |
VhdlType |
type |
Type definition (array, record, enum, …) |
VhdlSubtype |
subtype |
Subtype definition |
VhdlConstant |
constant |
Constant declaration |
Each port/generic/parameter is a VhdlParameter instance with the following attributes:
| Attribute | Description |
|---|---|
name |
Name of the port/generic/parameter |
mode |
Direction: in, out, inout, buffer |
data_type |
VhdlParameterType |
default_value |
Default value string, or None |
desc |
Description from metacomments, or None |
param_desc |
Description of the parameter, or None |
VhdlParameterType attributes:
| Attribute | Description |
|---|---|
name |
Type name (e.g. std_logic_vector) |
direction |
Array direction: to or downto |
l_bound |
Left bound expression of array range |
r_bound |
Right bound expression of array range |
arange |
Full original array range string (e.g. (7 downto 0)) |
VhdlExtractor automatically tracks array type definitions it encounters.
Use is_array() to query whether a type name is an array:
vhdl_ex = vhdl.VhdlExtractor()
code = '''
package foobar is
type custom_array is array(integer range <>) of boolean;
subtype custom_subtype is custom_array(1 to 10);
end package;
'''
vhdl_ex.extract_objects_from_source(text=code)
print(vhdl_ex.is_array('unsigned')) # True (built-in)
print(vhdl_ex.is_array('custom_array')) # True
print(vhdl_ex.is_array('custom_subtype')) # True (subtype of array)The following IEEE standard array types are recognised by default:
std_ulogic_vector, std_logic_vector, signed, unsigned, bit_vector.
You can persist and restore array type registries:
# Save registry to file
vhdl_ex.save_array_types(fname='array_types.txt')
# Load registry from file
vhdl_ex.load_array_types(fname='array_types.txt')This lets you parse one set of files for type definitions and use the saved info for parsing other code at a different time.
You can also add array type definitions from source files.
vhdl_ex.register_array_types_from_sources(source_files=list_of_file_names)You can also seed the extractor with known array types at construction:
vhdl_ex = vhdl.VhdlExtractor(array_types={'my_custom_vec', 'another_type'})Two utility functions help work with functions and procedures.
Generate a canonical prototype string
vois either aVhdlFunctionorVhdlProcedureobject- Example result:
function afunc(q : std_ulogic; h : unsigned) return std_ulogic;
from hdlparse.vhdl_parser import subprogram_prototype
proto = subprogram_prototype(vo)Generate a signature string
vois either aVhdlFunctionorVhdlProcedureobject- When
fullnameisNone(default), use the name attribute fromvo - Example result:
afunc[std_ulogic,unsigned return std_ulogic]
from hdlparse.vhdl_parser import subprogram_signature
sig = subprogram_signature(vo, fullname)vhdl.is_vhdl('design.vhd') # True
vhdl.is_vhdl('design.vhdl') # TrueHdlparse is licensed under the terms of the MIT License.
Copyright © 2017 Kevin Thibedeau.
Further contributions see Git logs.