Skip to content

Commit

Permalink
[sudef] add sudef
Browse files Browse the repository at this point in the history
  • Loading branch information
Dragon-Git committed Mar 2, 2024
1 parent 1461e0f commit 3b0e8e3
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 4 deletions.
104 changes: 100 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@

---
## Introduction
uvmgen includes a uvm testbench generation tool `uvmgen` and an experimental code snippets generation tool `sudef`.

uvmgen is a command-line interface (CLI) program for generating testbench structures based on a provided JSON/YAML/TOML/XML configuration file.

- `uvmgen` is a command-line interface (CLI) program for generating testbench structures based on a provided JSON/YAML/TOML/XML configuration file.
- `sudef` is an experimental command-line tool designed to process SystemVerilog files containing mako-style templates. By parsing specific comment formats, sudef generates SystemVerilog code that adheres to the defined templates.
## Installing
<details>
<summary>Prerequisites</summary>
Expand All @@ -28,7 +29,9 @@ Use Python's package installer pip to install uvmgen:
python3 -m pip install uvmgen
```

## Usage
## uvmgen - uvm testbench generator

### Usage

The basic usage of uvmgen is as follows:
```bash
Expand All @@ -51,7 +54,7 @@ uvmgen -h
### SyoSil ApS UVM Scoreboard
The [SyoSil ApS UVM Scoreboard](https://github.com/Dragon-Git/uvm_syoscb) is a feature of uvmgen that allows you to integrate SyoSil ApS UVM Scoreboard in environments. To use this feature, you need to add `pk_syoscb` to `import_pkgs` of env_pkg, and set the `SYOSCB_HOME` environment variables to SyoSil ApS UVM Scoreboard installed directory. The bash command is printed after uvmgen is successful, and the other shells can be replaced with the corresponding commands.

## Example
### Example

Suppose you have a JSON configuration file named testbench_config.json and you want to generate the testbench structure in a directory named tb, you would run the following command:

Expand All @@ -65,5 +68,98 @@ uvmgen -i testbench_config.json

You can use `test/json/example/typical.json` to generate a complete UVM environment, or use `test/json/base_pkg/***.***` to generate a single package.

## sudef - SV File Template Generator

### Overview

sudef is a command-line tool designed to process SystemVerilog (.sv) files that contain mako-style templates in comments. The tool generates code based on the provided templates, supporting both inline code generation and the inclusion of external files. By invoking sudef <svfile>, you can automatically generate the desired SystemVerilog code based on the specified template definitions.

### Usage

The basic usage of sudef is as follows:
```bash
sudef <srcfile>
```

- Inline Mode
When `super_define()` is called with no arguments, sudef operates in inline mode. In this mode, the generated code is directly inserted into the original .sv file, replacing the template placeholders with the appropriate values.
- External File Mode
When `super_define()` is called with arguments, sudef enters external file mode. In this mode, the generated code is written to a separate external file. The path and name of the external file are specified as arguments to `super_define()`.
### Experimental Nature
Due to its experimental nature, sudef may contain features that are not yet fully tested or implemented. You may encounter unexpected behavior or errors during usage. We encourage you to report any issues or suggested improvements to help us continually improve this program.

### Example

Let's consider a simple example where we have a SystemVerilog file named example.sv that contains a mako template:
```systemverilog
/* super_define()
<%
%>\
% for i in range(3):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(3):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
```
You can use sudef to generate the code by running:
```bash
sudef example.sv
```
The generated code will be:
```systemverilog
/* super_define()
<%
%>\
% for i in range(3):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(3):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
// super_define generate begin
int cfg_0;
int cfg_1;
int cfg_2;
`uvm_object_utils_begin(mycfg)
`uvm_field_int(cfg_0, UVM_DEFAULT)
`uvm_field_int(cfg_1, UVM_DEFAULT)
`uvm_field_int(cfg_2, UVM_DEFAULT)
`uvm_object_utils_end
// super_define generate end
```
If you want to generate the code to an external file, you can use super_define() with arguments:
```systemverilog
/* super_define(cfg_inc.svh)
<%
%>\
% for i in range(3):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(3):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
```
This will generate the same code as above but write it to a file named cfg_inc.svh and included by this file.

## Contribute
Contributions are always welcome! Simple fork this repo and submit a pull request.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dev = ['uvmgen[lint, doc, test]']

[project.scripts]
uvmgen = "uvmgen.uvmgen:main"
sudef = "uvmgen.super_define:main"
[project.urls]
"Homepage" = "https://github.com/Dragon-Git/icdk"
"issue tracker" = "https://github.com/Dragon-Git/icdk/issues"
Expand Down
43 changes: 43 additions & 0 deletions src/uvmgen/super_define.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pathlib import Path
import re
import fire
from mako.template import Template


def super_define(sv_file):
with open(sv_file, "r") as file:
content = file.read()
pattern = r"""\/\* super_define\((?:.*)\).*\*\/"""
matches = re.findall(pattern, content, re.DOTALL)

if matches:
for match in matches:
lines = match.split("\n")
result = re.match(r".*\((.*)\).*", lines[0])
lines = lines[1:-1]
t = Template("\n".join(lines))
generated_code = t.render() if not result.group(1) else f'`include "{result.group(1)}"\n'
new_string = f"{match}\n// super_define generate begin\n{generated_code}// super_define generate end"
pattern = r"""\/\* super_define\(.*\).*super_define generate end|\/\* super_define\(.*\).*\*\/"""
content = re.sub(pattern, new_string, content, 1, re.DOTALL)

if result.group(1):
inc_file = Path(sv_file).parent.joinpath(result.group(1))
with open(inc_file, "w") as f_inc, open(sv_file, "w") as f_src:
f_inc.write(t.render())
f_src.write(content)
print(f"super_define generate in '{inc_file}'.")
else:
with open(sv_file, "w") as file:
file.write(content)
print(f"super_define generate in '{sv_file}'.")
else:
print(f"Pattern '{pattern}' not found in file '{sv_file}'.")


def main():
fire.Fire(super_define)


if __name__ == "__main__":
main()
23 changes: 23 additions & 0 deletions test/super_define_data/cfg_inc_exp.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
int cfg_1;
int cfg_2;
int cfg_3;
int cfg_4;
int cfg_5;
int cfg_6;
int cfg_7;
int cfg_8;
int cfg_9;
int cfg_10;

`uvm_object_utils_begin(mycfg)
`uvm_field_int(cfg_1, UVM_DEFAULT)
`uvm_field_int(cfg_2, UVM_DEFAULT)
`uvm_field_int(cfg_3, UVM_DEFAULT)
`uvm_field_int(cfg_4, UVM_DEFAULT)
`uvm_field_int(cfg_5, UVM_DEFAULT)
`uvm_field_int(cfg_6, UVM_DEFAULT)
`uvm_field_int(cfg_7, UVM_DEFAULT)
`uvm_field_int(cfg_8, UVM_DEFAULT)
`uvm_field_int(cfg_9, UVM_DEFAULT)
`uvm_field_int(cfg_10, UVM_DEFAULT)
`uvm_object_utils_end
15 changes: 15 additions & 0 deletions test/super_define_data/sudef_inc.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* super_define(cfg_inc.svh)
<%
%>\
% for i in range(1, 11):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(1, 11):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
18 changes: 18 additions & 0 deletions test/super_define_data/sudef_inc_exp.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* super_define(cfg_inc.svh)
<%
%>\
% for i in range(1, 11):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(1, 11):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
// super_define generate begin
`include "cfg_inc.svh"
// super_define generate end
15 changes: 15 additions & 0 deletions test/super_define_data/sudef_inline.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* super_define()
<%
%>\
% for i in range(1, 11):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(1, 11):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
40 changes: 40 additions & 0 deletions test/super_define_data/sudef_inline_exp.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* super_define()
<%
%>\
% for i in range(1, 11):
int cfg_${i};
% endfor
`uvm_object_utils_begin(mycfg)
% for i in range(1, 11):
`uvm_field_int(cfg_${i}, UVM_DEFAULT)
% endfor
`uvm_object_utils_end
*/
// super_define generate begin
int cfg_1;
int cfg_2;
int cfg_3;
int cfg_4;
int cfg_5;
int cfg_6;
int cfg_7;
int cfg_8;
int cfg_9;
int cfg_10;

`uvm_object_utils_begin(mycfg)
`uvm_field_int(cfg_1, UVM_DEFAULT)
`uvm_field_int(cfg_2, UVM_DEFAULT)
`uvm_field_int(cfg_3, UVM_DEFAULT)
`uvm_field_int(cfg_4, UVM_DEFAULT)
`uvm_field_int(cfg_5, UVM_DEFAULT)
`uvm_field_int(cfg_6, UVM_DEFAULT)
`uvm_field_int(cfg_7, UVM_DEFAULT)
`uvm_field_int(cfg_8, UVM_DEFAULT)
`uvm_field_int(cfg_9, UVM_DEFAULT)
`uvm_field_int(cfg_10, UVM_DEFAULT)
`uvm_object_utils_end
// super_define generate end
22 changes: 22 additions & 0 deletions test/test_super_define.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import filecmp
from pathlib import Path
from uvmgen.super_define import super_define

base_json_paths = Path(__file__).parent.joinpath("json/base_pkg").iterdir()


def test_super_define_inline():
test_file = Path(__file__).parent.joinpath("super_define_data/sudef_inline.sv")
exp_file = Path(__file__).parent.joinpath("super_define_data/sudef_inline_exp.sv")
super_define(test_file)
assert filecmp.cmp(test_file, exp_file)


def test_super_define_inc():
test_file = Path(__file__).parent.joinpath("super_define_data/sudef_inc.sv")
exp_file = Path(__file__).parent.joinpath("super_define_data/sudef_inc_exp.sv")
inc_file = Path(__file__).parent.joinpath("super_define_data/cfg_inc.svh")
inc_exp_file = Path(__file__).parent.joinpath("super_define_data/cfg_inc_exp.svh")
super_define(test_file)
assert filecmp.cmp(test_file, exp_file)
assert filecmp.cmp(inc_file, inc_exp_file)

0 comments on commit 3b0e8e3

Please sign in to comment.