forked from XS-MLVP/UnityChipForXiangShan
Compare commits
7 Commits
add_rtl_di
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
0867b97b00 | |
|
|
bde2c7a01d | |
|
|
91cede469d | |
|
|
3754c85810 | |
|
|
cddb17b008 | |
|
|
deb6bec3a6 | |
|
|
a1bb767c7f |
|
|
@ -33,6 +33,7 @@ jobs:
|
|||
|
||||
- name: Check changed files
|
||||
run: |
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }}
|
||||
git diff --name-only origin/${{ github.event.pull_request.base.ref }} ${{ github.event.pull_request.head.sha }} > changed_files.txt
|
||||
cat changed_files.txt
|
||||
|
|
|
|||
|
|
@ -368,3 +368,108 @@ def module_name_with(names, prefix=None):
|
|||
elif isinstance(names, list):
|
||||
return [mname + "." + n for n in names]
|
||||
raise ValueError("Invalid names type")
|
||||
|
||||
|
||||
def get_all_rtl_files(top_module, cfg):
|
||||
"""
|
||||
Returns the file paths of all modules that the `top_module` depends on,
|
||||
with the path of `top_module` as the first element in the list.
|
||||
|
||||
This function assumes that there is **only one module** in the file and
|
||||
that the **file name matches the module name**.
|
||||
"""
|
||||
import re
|
||||
from glob import iglob
|
||||
from collections import OrderedDict
|
||||
|
||||
some_verilog_keywords = {
|
||||
'for', 'real', 'initial', 'input', 'endcase', 'typedef', 'primitive', 'always_comb', 'always_latch', 'negedge',
|
||||
'repeat', 'while', 'endfunction', 'int', 'output', 'wire', 'logic', 'reg', 'assign', 'function', 'case',
|
||||
'always_ff', 'if', 'posedge', 'table', 'end', 'task', 'forever', 'enum', 'endtask', 'module', 'localparam',
|
||||
'timescale', 'endprimitive', 'else', 'endtable', 'always', 'parameter', 'time', 'endmodule', 'begin',
|
||||
"and", "or", "not", "xor"
|
||||
}
|
||||
|
||||
module_pattern = re.compile(r"\bmodule\s+(\w+)\b")
|
||||
instance_pattern = re.compile(r"\b(\w+)\s+(?!module)(\w+)\s*\(")
|
||||
module_path_map = OrderedDict()
|
||||
|
||||
def resolve_verilog_file(path):
|
||||
module_set = set()
|
||||
instance_set = set()
|
||||
|
||||
def remove_inline_comments(s):
|
||||
# Remove the line comment first
|
||||
s = re.sub(r"//.*$", "", s)
|
||||
# Then remove the block comment
|
||||
return re.sub(r'/\*.*?\*/', "", s)
|
||||
|
||||
def parse_line(line_text: str) -> None:
|
||||
_line = remove_inline_comments(line_text)
|
||||
|
||||
# Extract names of declared modules
|
||||
module_matches = module_pattern.finditer(_line)
|
||||
for match in module_matches:
|
||||
_name = match.group(1)
|
||||
module_set.add(_name)
|
||||
|
||||
# Extract names of instanced modules
|
||||
instance_matches = instance_pattern.finditer(_line)
|
||||
for match in instance_matches:
|
||||
_name = match.group(1)
|
||||
if _name not in some_verilog_keywords:
|
||||
instance_set.add(_name)
|
||||
|
||||
# Code begin
|
||||
block_comment_depth = 0
|
||||
pending_line = ''
|
||||
|
||||
with open(path, "r") as file:
|
||||
while True:
|
||||
chunk = file.read(32768)
|
||||
if not chunk:
|
||||
break
|
||||
|
||||
lines = ("".join((pending_line, chunk))).split("\n")
|
||||
if lines:
|
||||
pending_line = lines.pop()
|
||||
|
||||
for line in lines:
|
||||
# for block comment
|
||||
start_pos = line.find("/*")
|
||||
end_pos = line.find("*/")
|
||||
while start_pos != -1 or end_pos != -1:
|
||||
# if '/*' appears before '*/', increase depth
|
||||
if start_pos != -1 and (end_pos == -1 or start_pos < end_pos):
|
||||
block_comment_depth += 1
|
||||
line = " ".join((line[:start_pos], line[start_pos + 2:]))
|
||||
# if '*/' appears before '/*', decrease depth
|
||||
elif end_pos != -1:
|
||||
block_comment_depth -= 1
|
||||
line = " ".join((line[:end_pos], line[end_pos + 2:]))
|
||||
start_pos = line.find("/*")
|
||||
end_pos = line.find("*/")
|
||||
|
||||
# skip if content of current line is in block comment
|
||||
if block_comment_depth > 0:
|
||||
continue
|
||||
parse_line(line)
|
||||
if pending_line:
|
||||
parse_line(pending_line)
|
||||
return module_set, instance_set
|
||||
|
||||
def get_rtl_helper(top_module_name) -> None:
|
||||
from comm import get_rtl_dir
|
||||
# Walk through the rtl dir
|
||||
rtl_dir = os.path.join(str(get_rtl_dir(cfg=cfg)), f"**/{top_module_name}.*v")
|
||||
for path in iglob(rtl_dir, recursive=True):
|
||||
module_set, inst_set = resolve_verilog_file(path)
|
||||
for _name in module_set:
|
||||
module_path_map[_name] = path
|
||||
module_set.clear()
|
||||
for _name in inst_set:
|
||||
if _name not in module_path_map:
|
||||
get_rtl_helper(_name)
|
||||
|
||||
get_rtl_helper(top_module)
|
||||
return list(module_path_map.values())
|
||||
|
|
|
|||
|
|
@ -217,3 +217,28 @@ print(comm.module_name_with(["X", "Y"], ,"../../x"))
|
|||
# out
|
||||
["a.b.c.x.X", "a.b.c.x.Y"]
|
||||
```
|
||||
|
||||
|
||||
#### `get_all_rtl_files(top_module, cfg)`
|
||||
|
||||
获取名称为 `top_module` 的模块所依赖的所有 RTL 文件(`.v` 或 `.sv`)的列表,并确保列表的第一个元素是 `top_module` 所在文件的绝对路径。所有 RTL 文件均位于 `UnityChipForXiangShan/rtl/rtl` 目录下。
|
||||
|
||||
- 输入:
|
||||
- `top_module`:模块名称,类型为 `str`。
|
||||
- `cfg`:配置信息,类型为 `CfgObject`。
|
||||
|
||||
- 输出:
|
||||
- 返回一个包含字符串的列表,列表中的每个字符串为模块依赖的 RTL 文件的绝对路径。列表的第一个元素为 `top_module` 所在文件的路径。
|
||||
|
||||
假设 `top_module` 为 `"ALU"`,且其依赖的 RTL 文件包括 `ALU.sv`、`adder.v` 和 `multiplier.v`:
|
||||
```python
|
||||
paths = get_all_rtl_files("ALU", cfg)
|
||||
|
||||
"""
|
||||
paths可能的内容:
|
||||
[
|
||||
"/path/to/UnityChipForXiangShan/rtl/rtl/ALU.sv",
|
||||
"/path/to/UnityChipForXiangShan/rtl/rtl/adder.v",
|
||||
"/path/to/UnityChipForXiangShan/rtl/rtl/multiplier.v"
|
||||
]
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
#### The RTL placeholder. Please do not add new files to this directory.
|
||||
Loading…
Reference in New Issue