Skip to content

Commit ac79fa4

Browse files
authored
Merge pull request #55 from antmicro/yowasp
Continuation: Use yowasp_yosys instead of system-installed yosys
2 parents c65e8e3 + 598533d commit ac79fa4

File tree

8 files changed

+379
-43
lines changed

8 files changed

+379
-43
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ jobs:
2222
- source env/conda/bin/activate sphinxcontrib-verilog-diagrams
2323
- cd tests && python3 -m unittest test.TestYosysScript
2424

25+
- stage: Tests
26+
name: "Test verilog_diagram_yosys config variable"
27+
script:
28+
- source env/conda/bin/activate sphinxcontrib-verilog-diagrams
29+
- cd tests && python3 -m unittest test.TestYosysType
30+
2531
- stage: Build
2632
name: "Build"
2733
script:

README.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ Required
8282
.. |yosys| replace:: ``yosys``
8383
.. _yosys: https://github.com/YosysHQ/yosys
8484

85+
By default, ``verilog-diagram`` uses the ``yowasp-yosys`` package provided in PyPI.
86+
It can be installed by running ``pip install -r requirements.txt``.
87+
However, you could also use Yosys that is installed on your system,
88+
or point to the specific Yosys binary using ``verilog_diagram_yosys`` variable
89+
in the Sphinx ``conf.py`` file:
90+
91+
To use Yosys that is available in your system, use the following setting::
92+
93+
verilog_diagram_yosys = "system"
94+
95+
If you want to point to the specific Yosys binary, provide the path to the program::
96+
97+
verilog_diagram_yosys = "<path-to-Yosys>"
98+
8599
Optional
86100
~~~~~~~~
87101

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ setuptools
22
docutils
33
sphinx
44

5+
yowasp-yosys>=0.9.post4547.dev9
6+
57
# Needed to upload to PyPi
68
twine
79

sphinxcontrib_verilog_diagrams/__init__.py

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ class VerilogDiagram(Directive):
160160
'caption': directives.unchanged,
161161
}
162162

163+
global_variable_options = {
164+
"verilog_diagram_output_format": ["svg", "png"],
165+
"verilog_diagram_skin": ["default"], # or path
166+
"verilog_diagram_yosys_script": ["default"], # or path
167+
"verilog_diagram_yosys": ["yowasp", "system"] # or path
168+
}
169+
163170
def run(self):
164171
# type: () -> List[nodes.Node]
165172

@@ -229,19 +236,38 @@ def run(self):
229236
return [node]
230237

231238

232-
def run_yosys(src, cmd):
233-
ycmd = "yosys -p '{cmd}' {src}".format(src=src, cmd=cmd)
234-
print("Running yosys:", ycmd)
235-
subprocess.check_output(ycmd, shell=True)
239+
def run_yosys(src, cmd, yosys='yowasp'):
240+
if yosys == 'yowasp':
241+
import yowasp_yosys
242+
ycmd = ["-q", "-p", "{}".format(cmd), src]
243+
print("Running YoWASP yosys: {}".format(ycmd))
244+
yowasp_yosys.run_yosys(ycmd)
245+
elif yosys == 'system':
246+
ycmd = "yosys -p '{cmd}' {src}".format(src=src, cmd=cmd)
247+
print("Running yosys: {}".format(ycmd))
248+
subprocess.check_output(ycmd, shell=True)
249+
else:
250+
ycmd = "{yosys} -p '{cmd}' {src}".format(yosys=yosys, src=src, cmd=cmd)
251+
print("Running yosys: {}".format(ycmd))
252+
subprocess.check_output(ycmd, shell=True)
253+
254+
255+
def diagram_yosys(ipath, opath, module='top', flatten=False,
256+
yosys_script='default', yosys='yowasp'):
236257

258+
# Assertions
237259

238-
def diagram_yosys(ipath, opath, module='top', flatten=False, yosys_script='default'):
239260
assert path.exists(ipath), 'Input file missing: {}'.format(ipath)
240261
assert not path.exists(opath), 'Output file exists: {}'.format(opath)
262+
yosys_options = VerilogDiagram.global_variable_options["verilog_diagram_yosys"]
263+
assert yosys in yosys_options or os.path.exists(yosys), "Invalid verilog_diagram_yosys value!"
241264
if yosys_script != 'default':
242265
assert path.exists(yosys_script), 'Yosys script file missing: {}'.format(yosys_script)
243266
oprefix, oext = path.splitext(opath)
244267
assert oext.startswith('.'), oext
268+
269+
# Diagram generation
270+
245271
oext = oext[1:]
246272

247273
if flatten:
@@ -254,12 +280,21 @@ def diagram_yosys(ipath, opath, module='top', flatten=False, yosys_script='defau
254280
else:
255281
yosys_script_cmd = "script {}".format(yosys_script)
256282

257-
run_yosys(
258-
src=ipath,
259-
cmd = """\
260-
prep -top {top} {flatten}; cd {top}; {script}; show -format {fmt} -prefix {oprefix}
261-
""".format(top=module, flatten=flatten, fmt=oext, oprefix=oprefix, script=yosys_script_cmd).strip(),
262-
)
283+
yosys_cmd = "prep -top {top} {flatten}; cd {top}; {script}; show -format {fmt} -prefix {oprefix}".format(
284+
top=module,
285+
flatten=flatten,
286+
fmt=oext,
287+
oprefix=oprefix,
288+
script=yosys_script_cmd
289+
).strip()
290+
run_yosys(ipath, yosys_cmd, yosys)
291+
292+
if yosys == 'yowasp':
293+
# somehow yowasp_yosys fails to execute `dot` to convert the dot file to svg,
294+
# which works on native yosys, perhaps a limitation with wasm
295+
svgdata = subprocess.check_output(["dot", "-Tsvg", "{}.dot".format(oprefix)])
296+
with open("{}.svg".format(oprefix), "wb") as img:
297+
img.write(svgdata)
263298

264299
assert path.exists(opath), 'Output file {} was not created!'.format(oopath)
265300
print('Output file created: {}'.format(opath))
@@ -268,7 +303,7 @@ def run_netlistsvg(ipath, opath, skin='default'):
268303
assert path.exists(ipath), 'Input file missing: {}'.format(ipath)
269304
assert not path.exists(opath), 'Output file exists: {}'.format(opath)
270305
if skin != 'default':
271-
assert path.exists(skin), 'Skin file missing: {}'.format(skin)
306+
assert path.exists(skin), 'Skin file missing: {}'.format(skin)
272307

273308
netlistsvg_cmd = "netlistsvg {ipath} -o {opath}".format(ipath=ipath, opath=opath)
274309
if skin != 'default':
@@ -281,15 +316,23 @@ def run_netlistsvg(ipath, opath, skin='default'):
281316
print('netlistsvg - Output file created: {}'.format(opath))
282317

283318

284-
def diagram_netlistsvg(ipath, opath, module='top', flatten=False, yosys_script='default', skin='default'):
319+
def diagram_netlistsvg(ipath, opath, module='top', flatten=False,
320+
yosys_script='default', skin='default', yosys='yowasp'):
321+
# Assertions
322+
285323
assert path.exists(ipath), 'Input file missing: {}'.format(ipath)
286324
assert not path.exists(opath), 'Output file exists: {}'.format(opath)
325+
yosys_options = VerilogDiagram.global_variable_options["verilog_diagram_yosys"]
326+
assert yosys in yosys_options or os.path.exists(yosys), "Invalid verilog_diagram_yosys value!"
287327
if yosys_script != 'default':
288328
assert path.exists(yosys_script), 'Yosys script file missing: {}'.format(yosys_script)
289329
if skin != 'default':
290330
assert path.exists(skin), 'Skin file missing: {}'.format(skin)
291331
oprefix, oext = path.splitext(opath)
292332
assert oext.startswith('.'), oext
333+
334+
# Diagram generation
335+
293336
oext = oext[1:]
294337

295338
if flatten:
@@ -306,11 +349,14 @@ def diagram_netlistsvg(ipath, opath, module='top', flatten=False, yosys_script='
306349
if path.exists(ojson):
307350
os.remove(ojson)
308351

309-
run_yosys(
310-
src=ipath,
311-
cmd = """\
312-
prep -top {top} {flatten}; cd {top}; {script}; write_json {ojson}
313-
""".format(top=module, flatten=flatten, ojson=ojson, script=yosys_script_cmd).strip())
352+
yosys_cmd = """prep -top {top} {flatten}; cd {top}; {script}; write_json {compat} {ojson}""".format(
353+
top=module,
354+
flatten=flatten,
355+
ojson=ojson,
356+
script=yosys_script_cmd,
357+
compat="-compat-int" if yosys == 'yowasp' else ""
358+
).strip()
359+
run_yosys(ipath, yosys_cmd, yosys)
314360
assert path.exists(ojson), 'Output file {} was not created!'.format(ojson)
315361

316362
run_netlistsvg(ojson, opath, skin)
@@ -336,14 +382,31 @@ def render_diagram(self, code, options, format, skin, yosys_script):
336382
yosys_script = options['yosys_script'] if options['yosys_script'] is not None else yosys_script
337383
skin = options['skin'] if options['skin'] is not None else skin
338384

385+
yosys = self.builder.config.verilog_diagram_yosys
386+
yosys_options = VerilogDiagram.global_variable_options["verilog_diagram_yosys"]
387+
if yosys not in yosys_options and not os.path.exists(yosys):
388+
raise VerilogDiagramError("Yosys not found!")
389+
else:
390+
yosys = yosys if yosys in yosys_options else os.path.realpath(yosys)
391+
339392
diagram_type = options['type']
340393
if diagram_type.startswith('yosys'):
341394
assert diagram_type.startswith('yosys-'), diagram_type
342395
diagram_yosys(
343-
verilog_path, outfn, module=options['module'], flatten=options['flatten'], yosys_script=yosys_script)
396+
verilog_path,
397+
outfn,
398+
module=options['module'],
399+
flatten=options['flatten'],
400+
yosys_script=yosys_script,
401+
yosys=yosys)
344402
elif diagram_type == 'netlistsvg':
345403
diagram_netlistsvg(
346-
verilog_path, outfn, module=options['module'], flatten=options['flatten'], skin=skin)
404+
verilog_path,
405+
outfn,
406+
module=options['module'],
407+
flatten=options['flatten'],
408+
skin=skin,
409+
yosys=yosys)
347410
else:
348411
raise Exception('Invalid diagram type "%s"' % diagram_type)
349412
#raise self.severe(\n' %
@@ -486,5 +549,5 @@ def setup(app):
486549
app.add_config_value('verilog_diagram_output_format', 'svg', 'html')
487550
app.add_config_value('verilog_diagram_skin', 'default', 'html')
488551
app.add_config_value('verilog_diagram_yosys_script', 'default', 'html')
552+
app.add_config_value('verilog_diagram_yosys', 'yowasp', 'html')
489553
return {'version': '1.0', 'parallel_read_safe': True}
490-

tests/test.py

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
VERILOG_DIAGRAMS_PATH = os.path.abspath("..")
1212

13+
## Helpers
1314

1415
def get_sphinx_dirs(test_build_dir):
1516
sphinx_dirs = {
@@ -29,8 +30,37 @@ def generate_sphinx_config(test_build_dir, **jinja_dict):
2930
template = env.get_template("conf.py.template")
3031
template.stream(**jinja_dict).dump(fd)
3132

33+
## Base class for tests
3234

33-
class TestSkins(unittest.TestCase):
35+
class TestBase(unittest.TestCase):
36+
37+
def print_test_header(self, case_name, test_name):
38+
print("")
39+
print("# ---------------------------------------------------------- #")
40+
print("# TEST CASE: {}".format(case_name))
41+
print("# TEST NAME: {}".format(test_name))
42+
print("# ---------------------------------------------------------- #")
43+
print("")
44+
45+
def prepare_test(self, test_name, test_build_dir, test_files, **test_jinja_dict):
46+
self.print_test_header(self.TEST_CASE_NAME, test_name)
47+
48+
# Create the TestCase build directory
49+
os.makedirs(test_build_dir, exist_ok=True)
50+
51+
# Generate a Sphinx config
52+
generate_sphinx_config(test_build_dir, **test_jinja_dict)
53+
54+
# Copy the test files
55+
for src in test_files:
56+
src_basename = os.path.basename(src)
57+
dst = os.path.join(test_build_dir, src_basename)
58+
shutil.copyfile(src, dst)
59+
60+
61+
## Test cases
62+
63+
class TestSkins(TestBase):
3464

3565
TEST_CASE_NAME = "TestSkins"
3666
TEST_CASE_BUILD_DIR = os.path.join("build", TEST_CASE_NAME)
@@ -50,17 +80,7 @@ def test_netlistsvg_diagram(self):
5080
"custom_variables": "verilog_diagram_skin = os.path.realpath('skin-purple.svg')"
5181
}
5282

53-
# Create the TestCase build directory
54-
os.makedirs(TEST_BUILD_DIR, exist_ok=True)
55-
56-
# Generate a Sphinx config
57-
generate_sphinx_config(TEST_BUILD_DIR, **TEST_JINJA_DICT)
58-
59-
# Copy the test files
60-
for src in TEST_FILES:
61-
src_basename = os.path.basename(src)
62-
dst = os.path.join(TEST_BUILD_DIR, src_basename)
63-
shutil.copyfile(src, dst)
83+
self.prepare_test(TEST_NAME, TEST_BUILD_DIR, TEST_FILES, **TEST_JINJA_DICT)
6484

6585
# Run the Sphinx
6686
sphinx_dirs = get_sphinx_dirs(TEST_BUILD_DIR)
@@ -69,7 +89,7 @@ def test_netlistsvg_diagram(self):
6989
app.build(force_all=True)
7090

7191

72-
class TestYosysScript(unittest.TestCase):
92+
class TestYosysScript(TestBase):
7393

7494
TEST_CASE_NAME = "TestYosysScript"
7595
TEST_CASE_BUILD_DIR = os.path.join("build", TEST_CASE_NAME)
@@ -89,24 +109,86 @@ def test_yosys_script(self):
89109
"custom_variables": "verilog_diagram_yosys_script = os.path.realpath('yosys_script.ys')"
90110
}
91111

92-
# Create the TestCase build directory
93-
os.makedirs(TEST_BUILD_DIR, exist_ok=True)
112+
self.prepare_test(TEST_NAME, TEST_BUILD_DIR, TEST_FILES, **TEST_JINJA_DICT)
94113

95-
# Generate a Sphinx config
96-
generate_sphinx_config(TEST_BUILD_DIR, **TEST_JINJA_DICT)
114+
# Run the Sphinx
115+
sphinx_dirs = get_sphinx_dirs(TEST_BUILD_DIR)
116+
with docutils_namespace():
117+
app = Sphinx(buildername="html", warningiserror=True, **sphinx_dirs)
118+
app.build(force_all=True)
97119

98-
# Copy the test files
99-
for src in TEST_FILES:
100-
src_basename = os.path.basename(src)
101-
dst = os.path.join(TEST_BUILD_DIR, src_basename)
102-
shutil.copyfile(src, dst)
120+
class TestYosysType(TestBase):
121+
122+
TEST_CASE_NAME = "TestYowasp"
123+
TEST_CASE_BUILD_DIR = os.path.join("build", TEST_CASE_NAME)
124+
125+
def test_yosys_yowasp(self):
126+
TEST_NAME = "test_yosys_yowasp"
127+
TEST_BUILD_DIR = os.path.join("build", self.TEST_CASE_NAME, TEST_NAME)
128+
TEST_FILES = [
129+
"test_yosys_type/test_yosys_yowasp.rst",
130+
"verilog/adder.v"
131+
]
132+
TEST_JINJA_DICT = {
133+
"verilog_diagrams_path": "'{}'".format(VERILOG_DIAGRAMS_PATH),
134+
"master_doc": "'test_yosys_yowasp'",
135+
"custom_variables": ""
136+
}
137+
138+
self.prepare_test(TEST_NAME, TEST_BUILD_DIR, TEST_FILES, **TEST_JINJA_DICT)
139+
140+
# Run the Sphinx
141+
sphinx_dirs = get_sphinx_dirs(TEST_BUILD_DIR)
142+
with docutils_namespace():
143+
app = Sphinx(buildername="html", warningiserror=True, **sphinx_dirs)
144+
app.build(force_all=True)
145+
146+
@unittest.skipIf(shutil.which('yosys') is None, 'Skipping test_yosys_system. Yosys is not installed!')
147+
def test_yosys_system(self):
148+
TEST_NAME = "test_yosys_system"
149+
TEST_BUILD_DIR = os.path.join("build", self.TEST_CASE_NAME, TEST_NAME)
150+
TEST_FILES = [
151+
"test_yosys_type/test_yosys_system.rst",
152+
"verilog/adder.v"
153+
]
154+
TEST_JINJA_DICT = {
155+
"verilog_diagrams_path": "'{}'".format(VERILOG_DIAGRAMS_PATH),
156+
"master_doc": "'test_yosys_system'",
157+
"custom_variables": "verilog_diagram_yosys = 'system'"
158+
}
159+
160+
self.prepare_test(TEST_NAME, TEST_BUILD_DIR, TEST_FILES, **TEST_JINJA_DICT)
103161

104162
# Run the Sphinx
105163
sphinx_dirs = get_sphinx_dirs(TEST_BUILD_DIR)
106164
with docutils_namespace():
107165
app = Sphinx(buildername="html", warningiserror=True, **sphinx_dirs)
108166
app.build(force_all=True)
109167

168+
@unittest.skipIf(shutil.which('yosys') is None, 'Skipping test_yosys_path. Yosys is not installed!')
169+
def test_yosys_path(self):
170+
TEST_NAME = "test_yosys_path"
171+
TEST_BUILD_DIR = os.path.join("build", self.TEST_CASE_NAME, TEST_NAME)
172+
TEST_FILES = [
173+
"test_yosys_type/test_yosys_path.rst",
174+
"verilog/adder.v"
175+
]
176+
177+
yosys_path = shutil.which("yosys")
178+
179+
TEST_JINJA_DICT = {
180+
"verilog_diagrams_path": "'{}'".format(VERILOG_DIAGRAMS_PATH),
181+
"master_doc": "'test_yosys_path'",
182+
"custom_variables": "verilog_diagram_yosys = '{}'".format(yosys_path)
183+
}
184+
185+
self.prepare_test(TEST_NAME, TEST_BUILD_DIR, TEST_FILES, **TEST_JINJA_DICT)
186+
187+
# Run the Sphinx
188+
sphinx_dirs = get_sphinx_dirs(TEST_BUILD_DIR)
189+
with docutils_namespace():
190+
app = Sphinx(buildername="html", warningiserror=True, **sphinx_dirs)
191+
app.build(force_all=True)
110192

111193
if __name__ == '__main__':
112194
unittest.main()

0 commit comments

Comments
 (0)