Converts a subset of python generator functions into synthesizable sequential SystemVerilog
Project description
python2verilog
Converts a subset of python generator functions into synthesizable sequential SystemVerilog.
A use case is for drawing shapes on grids (for VGA output), where the user may prototype the algorithm in python and then convert it to verilog for use in an FPGA.
Supports Python Generator functions as well as the following block types:
if
while
A testbench can also be generated and asserted against the Python outputs.
Usage
Installation
python3 -m pip install --upgrade pip
python3 -m pip install python2verilog
Basics
Create a python file containing a generator function with output type hints, named python.py
.
You can find a sample here, and a directory of samples here
Run python3 -m python2verilog python.py
to generate a testbench file at python.sv
.
Use the arg --help
for additional options, including outputting a testbench and running optimizers.
Testing
Requirements
A Ubuntu environment (WSL2 works too, make sure to have the repo on the Ubuntu partition, as os.mkfifo
is used for speed)
Install required python libraries with python3 -m pip install -r tests/requirements.txt
For automatic Verilog simulation and testing, install Icarus Verilog and its dependencies with
sudo apt-get install iverilog expected
(uses the unbuffer
in expected
).
The online simulator EDA Playground can be used as a subsitute if you manually copy-paste the module and testbench files to it.
For most up-to-date information, refer to the pytest github workflow.
Creating New Test
To create a new test case and set up configs, run python3 tests/integration/new_test_case.py <test-name>
.
Running Tests
To run tests, use python3 -m pytest -sv
.
Additional CLI flags can be found in tests/conftest.py.
Use git clean -dxf
to remove gitignored files.
Tested Generations
The Github Actions run all the tests with writing enabled. You may find its output as a Github Artifact.
function_name | py_yields | module_nchars | ver_clks | wires | wire_bits | public_wires | public_wire_bits | memories | memory_bits | processes | cells | add | ge | gt | mul | sub |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
circle_lines -O0 | 296 | 4326 | 464 | 81 | 2313 | 21 | 517 | 0 | 0 | 1 | 45 | 21 | 1 | 1 | 3 | 19 |
circle_lines -O1 | 296 | 4101 | 299 | 104 | 3049 | 21 | 517 | 0 | 0 | 1 | 68 | 35 | 1 | 1 | 6 | 25 |
circle_lines -O2 | 296 | 4101 | 299 | 104 | 3049 | 21 | 517 | 0 | 0 | 1 | 68 | 35 | 1 | 1 | 6 | 25 |
circle_lines -O4 | 296 | 4101 | 299 | 104 | 3049 | 21 | 517 | 0 | 0 | 1 | 68 | 35 | 1 | 1 | 6 | 25 |
circle_lines -O8 | 296 | 4101 | 299 | 104 | 3049 | 21 | 517 | 0 | 0 | 1 | 68 | 35 | 1 | 1 | 6 | 25 |
dumb_loop -O0 | 2 | 834 | 1047 | 18 | 328 | 10 | 165 | 0 | 0 | 1 | 2 | 1 | nan | nan | nan | nan |
dumb_loop -O1 | 2 | 832 | 523 | 19 | 329 | 10 | 165 | 0 | 0 | 1 | 3 | 1 | nan | nan | nan | nan |
dumb_loop -O2 | 2 | 1193 | 263 | 25 | 459 | 10 | 165 | 0 | 0 | 1 | 9 | 5 | nan | nan | nan | nan |
dumb_loop -O4 | 2 | 2275 | 133 | 46 | 1007 | 10 | 165 | 0 | 0 | 1 | 30 | 22 | nan | nan | nan | nan |
dumb_loop -O8 | 2 | 5879 | 68 | 124 | 3255 | 10 | 165 | 0 | 0 | 1 | 108 | 92 | nan | nan | nan | nan |
fib -O0 | 43 | 1397 | 271 | 25 | 552 | 13 | 261 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
fib -O1 | 43 | 1021 | 46 | 27 | 585 | 13 | 261 | 0 | 0 | 1 | 5 | 3 | nan | nan | nan | nan |
fib -O2 | 43 | 1021 | 46 | 27 | 585 | 13 | 261 | 0 | 0 | 1 | 5 | 3 | nan | nan | nan | nan |
fib -O4 | 43 | 1021 | 46 | 27 | 585 | 13 | 261 | 0 | 0 | 1 | 5 | 3 | nan | nan | nan | nan |
fib -O8 | 43 | 1021 | 46 | 27 | 585 | 13 | 261 | 0 | 0 | 1 | 5 | 3 | nan | nan | nan | nan |
floor_div -O0 | 612 | 1414 | 1963 | 32 | 652 | 11 | 197 | 0 | 0 | 1 | 14 | 2 | nan | nan | nan | 1 |
floor_div -O1 | 612 | 2358 | 643 | 66 | 1399 | 11 | 197 | 0 | 0 | 1 | 48 | 11 | nan | nan | nan | 3 |
floor_div -O2 | 612 | 4400 | 615 | 124 | 2821 | 11 | 197 | 0 | 0 | 1 | 106 | 34 | nan | nan | nan | 6 |
floor_div -O4 | 612 | 9924 | 615 | 294 | 7393 | 11 | 197 | 0 | 0 | 1 | 276 | 134 | nan | nan | nan | 12 |
floor_div -O8 | 612 | 26732 | 615 | 850 | 23449 | 11 | 197 | 0 | 0 | 1 | 832 | 550 | nan | nan | nan | 24 |
happy_face -O0 | 696 | 6594 | 1816 | 115 | 3153 | 21 | 517 | 0 | 0 | 1 | 79 | 32 | 1 | 1 | 3 | 22 |
happy_face -O1 | 696 | 8686 | 727 | 227 | 5807 | 21 | 517 | 0 | 0 | 1 | 191 | 77 | 1 | 1 | 3 | 35 |
happy_face -O2 | 696 | 17319 | 699 | 445 | 10861 | 21 | 517 | 0 | 0 | 1 | 409 | 161 | 1 | 1 | 3 | 59 |
happy_face -O4 | 696 | 50299 | 699 | 1199 | 28541 | 21 | 517 | 0 | 0 | 1 | 1163 | 455 | 1 | 1 | 3 | 143 |
happy_face -O8 | 696 | 199275 | 699 | 3979 | 94189 | 21 | 517 | 0 | 0 | 1 | 3943 | 1547 | 1 | 1 | 3 | 455 |
operators -O0 | 69 | 1404 | 88 | 47 | 1070 | 13 | 261 | 0 | 0 | 1 | 26 | 1 | 1 | nan | 1 | 2 |
operators -O1 | 69 | 1347 | 73 | 47 | 1070 | 13 | 261 | 0 | 0 | 1 | 26 | 1 | 1 | nan | 1 | 2 |
operators -O2 | 69 | 1347 | 73 | 47 | 1070 | 13 | 261 | 0 | 0 | 1 | 26 | 1 | 1 | nan | 1 | 2 |
operators -O4 | 69 | 1347 | 73 | 47 | 1070 | 13 | 261 | 0 | 0 | 1 | 26 | 1 | 1 | nan | 1 | 2 |
operators -O8 | 69 | 1347 | 73 | 47 | 1070 | 13 | 261 | 0 | 0 | 1 | 26 | 1 | 1 | nan | 1 | 2 |
rectangle_filled -O0 | 1667 | 1568 | 5228 | 35 | 841 | 18 | 421 | 0 | 0 | 1 | 6 | 4 | nan | nan | nan | nan |
rectangle_filled -O1 | 1667 | 2101 | 1723 | 53 | 1231 | 18 | 421 | 0 | 0 | 1 | 24 | 16 | nan | nan | nan | nan |
rectangle_filled -O2 | 1667 | 3410 | 1670 | 83 | 1943 | 18 | 421 | 0 | 0 | 1 | 54 | 38 | nan | nan | nan | nan |
rectangle_filled -O4 | 1667 | 6748 | 1670 | 170 | 4231 | 18 | 421 | 0 | 0 | 1 | 141 | 109 | nan | nan | nan | nan |
rectangle_filled -O8 | 1667 | 16304 | 1670 | 452 | 12263 | 18 | 421 | 0 | 0 | 1 | 423 | 359 | nan | nan | nan | nan |
rectangle_lines -O0 | 294 | 1884 | 599 | 41 | 1033 | 18 | 421 | 0 | 0 | 1 | 12 | 8 | nan | nan | nan | 2 |
rectangle_lines -O1 | 294 | 1938 | 297 | 51 | 1260 | 18 | 421 | 0 | 0 | 1 | 22 | 15 | nan | nan | nan | 2 |
rectangle_lines -O2 | 294 | 1938 | 297 | 51 | 1260 | 18 | 421 | 0 | 0 | 1 | 22 | 15 | nan | nan | nan | 2 |
rectangle_lines -O4 | 294 | 1938 | 297 | 51 | 1260 | 18 | 421 | 0 | 0 | 1 | 22 | 15 | nan | nan | nan | 2 |
rectangle_lines -O8 | 294 | 1938 | 297 | 51 | 1260 | 18 | 421 | 0 | 0 | 1 | 22 | 15 | nan | nan | nan | 2 |
testing -O0 | 20 | 1275 | 83 | 21 | 424 | 11 | 197 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
testing -O1 | 20 | 793 | 23 | 21 | 424 | 11 | 197 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
testing -O2 | 20 | 1086 | 23 | 21 | 424 | 11 | 197 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
testing -O4 | 20 | 1912 | 23 | 21 | 424 | 11 | 197 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
testing -O8 | 20 | 4524 | 23 | 21 | 424 | 11 | 197 | 0 | 0 | 1 | 3 | 2 | nan | nan | nan | nan |
For Developers
To setup pre-commit, run pre-commit install
.
Github Issues is used for tracking.
Epics
- Support arrays (and their unrolling)
- Mimic combinational logic with "regular" Python functions
- Division approximations (and area/timing analysis)
Docs
Uses sphinx. Run commands used by Github Actions.
Random Planning, Design, and Notes
More Variable Data
How to convert a Python ast.Name
? We want to be able to map those variables to
Start, Ready, Reset
How should these signals be handled while minimizing unnecessary clock cycles?
- If module sees high
reset
, module resets and holdsready
high one cycle later - If module sees high
start
, module captures inputs in the same cycle, then holdsready
low until done - If user sees high
ready
, they can assertstart
in the same clock cycle (e.g. connectready
tostart
is valid)
What needs to be duplicated in testbenches?
declare I/O and other signals declare DUT start clock
loop for each test case
- start signal
- while wait for done signal
- clock
- set start zero
- display output endloop
Potential API
import python2verilog as p2v
import ast
func = ast.parse(code).body[0]
ir = p2v.from_python_get_ir(func.body)
# Optimization passes
ir = p2v.optimizations.replace_single_item_cases(ir)
ir = p2v.optimizations.remove_nesting(ir)
ir = p2v.optimizations.combine_statements(ir)
verilog = p2v.Verilog()
verilog.from_python_do_setup(ir.get_context()) # module I/O is dependent on Python
verilog.from_ir_fill_body(ir.get_root()) # fills the body
# whether has valid or done signal,
# whether initialization is always happening or only on start,
# verilog sim name
verilog.config(has_valid=True, has_done=True, lazy_start=True, verilog_sim="iverilog")
with open(f"{verilog.get_module_name()}.sv", mode="w") as module:
module.write(verilog.get_module())
with open(f"{verilog.get_module_name()}_tb.sv", mode="w") as tb:
tb.write(verilog.get_testbench())
print(verilog.get_verilog_run_cmd())
assert verilog.test_outputs() # checks if verilog and python output same
Rectangle Filled
def draw_rectangle(s_x, s_y, height, width) -> tuple[int, int]:
for i0 in range(0, width):
for i1 in range(0, height):
yield (s_x + i1, s_y + i0)
case (STATE)
0: begin
if (i0 < width) begin
STATE <= STATE_1;
end else begin
case (STATE_INNER)
0: begin
if (i1 < height) begin
STATE_INNER <= STATE_INNER + 1;
end else begin
case (STATE_INNER_INNER)
0: begin
out0 <= s_x + i1;
out1 <= s_y + i0;
i1 <= i1 + 1;
STATE_INNER_INNER <= STATE_INNER_INNER + 1;
STATE_INNER_INNER <= 0; // flag to either wrap around or remain
end
end
STATE_INNER <= 0;
end
endcase
end
end
STATE_1: begin
done <= 1;
end
endcase
Converting a While Loop
i = 0
while <condition>:
<statement 1>
<statement 2>
...
case (STATE)
0: begin
// For loop start
if (condition) begin
STATE <= STATE + 1;
end else begin
case (STATE_INNER)
0: begin
// statement 1
end
1: begin
// statement 2
end
// ...
10: begin
STATE_INNER <= 0;
end
endcase
end
// For loop end
end
// ...
endcase
If Statement Analysis
// IF START
case (_STATE_IF)
0: begin
if (condition) _STATE_IF <= 1;
else _STATE_IF <= 2;
end
1: begin
// THEN BODY START
case ()
// ...
_STATE_IF <= 0;
// THEN BODY END
end
2: begin
// ELSE BODY START
// ...
__STATE_IF <= 0;
// ELSE BODY END
end
endcase
// IF END
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file python2verilog-0.1.5.tar.gz
.
File metadata
- Download URL: python2verilog-0.1.5.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.17
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 505e6134f37d45ca3dc18d0580b42972b2f30a2ab76c5a97ecab5ef2825684b1 |
|
MD5 | 4d8b3cb58b6a5d0617df119bd4258184 |
|
BLAKE2b-256 | 3869008538f4dd61057b5c9d71a64c4c29a10c14228e0e995b9e1be3db443842 |
File details
Details for the file python2verilog-0.1.5-py3-none-any.whl
.
File metadata
- Download URL: python2verilog-0.1.5-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.17
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 129899a3e0a1b6ddd759f2a8560c8c13e81f761f384f486f08e625c466fdd603 |
|
MD5 | b2b1078c2284e5c74a9613c83916c97e |
|
BLAKE2b-256 | 24ff0cdbfba5ff1f0dcd578be4b27d06238927468d49f85c3dafe5048984b19b |