155 lines
5.8 KiB
Python
155 lines
5.8 KiB
Python
# coding=utf8
|
|
# ***************************************************************************************
|
|
# This project is licensed under Mulan PSL v2.
|
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
# You may obtain a copy of Mulan PSL v2 at:
|
|
# http://license.coscl.org.cn/MulanPSL2
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
#
|
|
# See the Mulan PSL v2 for more details.
|
|
# **************************************************************************************/
|
|
|
|
__all__ = [
|
|
"get_all_rtl_files",
|
|
"extract_signals"
|
|
]
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from .utils import get_root_dir
|
|
from ..cfg.model import UnityChipConfig
|
|
from ..functions.cfg import get_rtl_dir
|
|
|
|
|
|
def get_defined_modules(file: str) -> List[str]:
|
|
cmd = ["sed", "-n", r"s/^ *module \+\([^ (]\+\).*/\1/p", file]
|
|
ret = subprocess.run(cmd, cwd=get_root_dir(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
assert ret.returncode == 0, ret.stderr
|
|
return ret.stdout.decode().splitlines()
|
|
|
|
|
|
def get_instant_modules(file: str) -> List[str]:
|
|
verible_bin = shutil.which("verible-verilog-syntax")
|
|
assert verible_bin, "verible-verilog-syntax is not installed"
|
|
verible_cmd = [verible_bin, "--printtokens", file]
|
|
sed_cmd = [
|
|
"sed", "-n",
|
|
r'/#SymbolIdentifier/ { N; /#SymbolIdentifier.*\n.*#SymbolIdentifier/ s/.*"\([^"]*\)".*\n.*/\1/p }'
|
|
]
|
|
sort_cmd = ["sort", "-u"]
|
|
|
|
# Run verible-verilog-syntax to get the tokens of verilog file
|
|
verible_proc = subprocess.Popen(verible_cmd, cwd=get_root_dir(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
# Run sed to get the names of all instant modules
|
|
sed_proc = subprocess.Popen(sed_cmd, stdin=verible_proc.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
# Run sort to unique output
|
|
sort_proc = subprocess.Popen(sort_cmd, stdin=sed_proc.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
verible_proc.stdout.close()
|
|
verible_out, verible_err = verible_proc.communicate()
|
|
assert verible_proc.returncode == 0, verible_err
|
|
|
|
sed_proc.stdout.close()
|
|
sed_out, sed_err = sed_proc.communicate()
|
|
assert sed_proc.returncode == 0, sed_err
|
|
|
|
sort_out, sout_err = sort_proc.communicate()
|
|
return sort_out.decode().splitlines()
|
|
|
|
|
|
def get_all_rtl_files(top_module: str, cfg: UnityChipConfig = None) -> list[str]:
|
|
"""Recursively finds all Verilog source files for a given top module.
|
|
|
|
This function traverses the Verilog module hierarchy starting from `top_module`
|
|
to identify all dependent source files. It parses Verilog files to find
|
|
module instantiations and recursively resolves their file paths.
|
|
|
|
Note:
|
|
This function assumes that each Verilog file contains only one module
|
|
definition and that the filename matches the module name (e.g., module
|
|
`my_fifo` is in `my_fifo.v`).
|
|
|
|
Args:
|
|
top_module (str): The name of the top-level module to start the search from.
|
|
cfg: A configuration object used to determine the root directory for the
|
|
RTL file search.
|
|
|
|
Returns:
|
|
list[str]: An ordered list of file paths for the top module and all its
|
|
submodule dependencies.
|
|
"""
|
|
from collections import OrderedDict, deque
|
|
|
|
module_path: dict[str, str] = OrderedDict()
|
|
instants = deque([top_module])
|
|
rtl_dir = Path(get_rtl_dir(cfg=cfg))
|
|
while instants:
|
|
t = instants.popleft()
|
|
for path in rtl_dir.rglob(f"**/{t}.*v"):
|
|
if t in module_path:
|
|
break
|
|
p = str(path.absolute())
|
|
module_path[t] = p
|
|
for i in get_instant_modules(p):
|
|
if i not in module_path:
|
|
instants.append(i)
|
|
dependencies = list(module_path.values())
|
|
return dependencies
|
|
|
|
|
|
def extract_signals(verilog_file: str, output_file: str) -> None:
|
|
"""Extracts wire/reg signals from a Verilog file into a YAML file.
|
|
|
|
This function parses a Verilog source file to find all `wire` and `reg`
|
|
declarations. It then formats these signal definitions and writes them
|
|
to a specified YAML file. The `reg` type is converted to `logic`.
|
|
|
|
Args:
|
|
verilog_file (str): The path to the input Verilog source file.
|
|
output_file (str): The path where the output YAML file will be saved.
|
|
"""
|
|
# 定义匹配 wire 和 reg 的正则表达式
|
|
signal_pattern = re.compile(r'\b(wire|reg)\b\s*(\[[^\]]+\])?\s*([\w, ]+)(;|=)')
|
|
|
|
extracted_signals = []
|
|
|
|
# 读取 sv 文件内容
|
|
with open(verilog_file, 'r') as file:
|
|
lines = file.readlines()
|
|
|
|
# 逐行解析
|
|
for line in lines:
|
|
match = signal_pattern.search(line)
|
|
if not match:
|
|
continue
|
|
signal_type = match.group(1) # wire or reg
|
|
width = match.group(2) if match.group(2) else "" # [8:0] or empty
|
|
names = match.group(3) # 信号名
|
|
if names.startswith("_GEN"):
|
|
continue
|
|
if signal_type == "reg":
|
|
signal_type = "logic"
|
|
# 分解信号名并格式化
|
|
if width == "":
|
|
for name in names.split(','):
|
|
extracted_signals.append(f" - \"{signal_type} {name.strip()}\"")
|
|
else:
|
|
for name in names.split(','):
|
|
extracted_signals.append(f" - \"{signal_type} {width.strip()} {name.strip()}\"")
|
|
|
|
# 写入到 yaml 文件
|
|
basename = os.path.basename(verilog_file)
|
|
filename = os.path.splitext(basename)[0]
|
|
with open(output_file, 'w') as file:
|
|
file.write(filename + ':\n')
|
|
for signal in extracted_signals:
|
|
file.write(signal + '\n')
|