Skip to content

Commit b8c6b99

Browse files
Add VHDL support via GHDL
Signed-off-by: Michael Riegert <michael@eowyn.net>
1 parent cb701d3 commit b8c6b99

File tree

1 file changed

+52
-9
lines changed

1 file changed

+52
-9
lines changed

sphinxcontrib_hdl_diagrams/__init__.py

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ class HDLDiagram(Directive):
165165
"hdl_diagram_output_format": ["svg", "png"],
166166
"hdl_diagram_skin": ["default"], # or path
167167
"hdl_diagram_yosys_script": ["default"], # or path
168-
"hdl_diagram_yosys": ["yowasp", "system"] # or path
168+
"hdl_diagram_yosys": ["yowasp", "system"], # or path
169+
"hdl_diagram_ghdl": ["module", "system"] # or path
169170
}
170171

171172
def run(self):
@@ -237,18 +238,20 @@ def run(self):
237238
return [node]
238239

239240

240-
def run_yosys(src, cmd, yosys='yowasp'):
241+
def run_yosys(src, cmd, yosys='yowasp', options=''):
241242
if yosys == 'yowasp':
242243
import yowasp_yosys
243244
ycmd = ["-q", "-p", "{}".format(cmd), src]
245+
if options != '':
246+
ycmd.insert(0, options)
244247
print("Running YoWASP yosys: {}".format(ycmd))
245248
yowasp_yosys.run_yosys(ycmd)
246249
elif yosys == 'system':
247-
ycmd = "yosys -p '{cmd}' {src}".format(src=src, cmd=cmd)
250+
ycmd = "yosys {options} -p '{cmd}' {src}".format(options=options, src=src, cmd=cmd)
248251
print("Running yosys: {}".format(ycmd))
249252
subprocess.check_output(ycmd, shell=True)
250253
else:
251-
ycmd = "{yosys} -p '{cmd}' {src}".format(yosys=yosys, src=src, cmd=cmd)
254+
ycmd = "{yosys} {options} -p '{cmd}' {src}".format(options=options, yosys=yosys, src=src, cmd=cmd)
252255
print("Running yosys: {}".format(ycmd))
253256
subprocess.check_output(ycmd, shell=True)
254257

@@ -372,6 +375,29 @@ def nmigen_to_rtlil(fname, oname):
372375
cmd = "{python} {script} > {output}".format(python=sys.executable, script=fname, output=oname)
373376
subprocess.run(cmd, shell=True, check=True)
374377

378+
def vhdl_to_verilog(fname, oname, module, ghdl, yosys):
379+
assert os.path.exists(fname)
380+
381+
if ghdl == "module":
382+
yosys_opt = "-m ghdl "
383+
elif ghdl == "prebuilt":
384+
yosys_opt = ""
385+
elif os.path.exists(ghdl):
386+
yosys_opt = "-m '{}'".format(ghdl)
387+
else:
388+
raise HDLDiagramError("hdl_diagram_ghdl can only be \"module\", \"prebuilt\", or "
389+
"a path to a ghdl-yosys-plugin shared library, not '{}'".format(ghdl))
390+
391+
# assert yosys != "yowasp", HDLDiagramError('Cannot use YoWASP for VHDL - GHDL is not compatible with YoWASP')
392+
393+
output_dir = os.path.dirname(oname)
394+
os.makedirs(output_dir, exist_ok=True)
395+
cmd = "ghdl {input} -e {module}; write_verilog {output}".format(
396+
module=module,
397+
input=fname,
398+
output=oname
399+
)
400+
run_yosys('', cmd, yosys, options=yosys_opt)
375401

376402
def render_diagram(self, code, options, format, skin, yosys_script):
377403
# type: (nodes.NodeVisitor, unicode, Dict, unicode, unicode) -> Tuple[unicode, unicode]
@@ -384,16 +410,25 @@ def render_diagram(self, code, options, format, skin, yosys_script):
384410
relfn = posixpath.join(self.builder.imgpath, fname)
385411
outfn = path.join(self.builder.outdir, self.builder.imagedir, fname)
386412

413+
yosys = self.builder.config.hdl_diagram_yosys
414+
387415
if source_ext == '.py':
388416
module = 'top'
389417
ilfn = path.join(self.builder.outdir, self.builder.imagedir, options['outname'] + '.il')
390418
nmigen_to_rtlil(source_path, ilfn)
391419
source_path = ilfn
392420
elif source_ext == '.il' or source_ext == '.v':
393421
module = options['module']
422+
elif source_ext == '.vhd' or source_ext == '.vhdl':
423+
if yosys == "yowasp":
424+
raise HDLDiagramError("Cannot use YoWASP for VHDL")
425+
module = options['module']
426+
ilfn = path.join(self.builder.outdir, self.builder.imagedir, options['outname'] + '.v')
427+
vhdl_to_verilog(source_path, ilfn, module, self.builder.config.hdl_diagram_ghdl, yosys)
428+
source_path = ilfn
394429
else:
395430
raise HDLDiagramError("hdl_diagram_code file extension must be one of '.v', "
396-
"'.il', or '.py', but is %r" % source_ext)
431+
"'.il', '.py', '.vhd', or '.vhdl', but is %r" % source_ext)
397432

398433
if path.isfile(outfn):
399434
print('Exiting file:', outfn)
@@ -404,7 +439,6 @@ def render_diagram(self, code, options, format, skin, yosys_script):
404439
yosys_script = options['yosys_script'] if options['yosys_script'] is not None else yosys_script
405440
skin = options['skin'] if options['skin'] is not None else skin
406441

407-
yosys = self.builder.config.hdl_diagram_yosys
408442
yosys_options = HDLDiagram.global_variable_options["hdl_diagram_yosys"]
409443
if yosys not in yosys_options and not os.path.exists(yosys):
410444
raise HDLDiagramError("Yosys not found!")
@@ -441,6 +475,9 @@ def render_diagram_html(
441475
self, node, code, options, imgcls=None, alt=None):
442476
# type: (nodes.NodeVisitor, hdl_diagram, unicode, Dict, unicode, unicode, unicode) -> Tuple[unicode, unicode] # NOQA
443477

478+
diagram_error = False
479+
diagram_error_message = ""
480+
444481
yosys_script = self.builder.config.hdl_diagram_yosys_script
445482
if yosys_script != 'default' and not path.exists(yosys_script):
446483
raise HDLDiagramError("Yosys script file {} does not exist! Change hdl_diagram_yosys_script variable".format(yosys_script))
@@ -457,9 +494,14 @@ def render_diagram_html(
457494
fname, outfn = render_diagram(self, code, options, format, skin, yosys_script)
458495
except HDLDiagramError as exc:
459496
logger.warning('hdl_diagram code %r: ' % code + str(exc))
460-
raise nodes.SkipNode
461-
462-
if fname is None:
497+
diagram_error = True
498+
diagram_error_message = str(exc)
499+
# raise nodes.SkipNode
500+
501+
if diagram_error:
502+
self.body.append("WARNING: hdl_diagram code '{file}': {err}".format(
503+
file=self.encode(code).strip(), err=diagram_error_message))
504+
elif fname is None:
463505
self.body.append(self.encode(code))
464506
else:
465507
if alt is None:
@@ -568,4 +610,5 @@ def setup(app):
568610
app.add_config_value('hdl_diagram_skin', 'default', 'html')
569611
app.add_config_value('hdl_diagram_yosys_script', 'default', 'html')
570612
app.add_config_value('hdl_diagram_yosys', 'yowasp', 'html')
613+
app.add_config_value('hdl_diagram_ghdl', 'module', 'html')
571614
return {'version': '1.0', 'parallel_read_safe': True}

0 commit comments

Comments
 (0)