Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ name: 'lint'
on:
push:
paths:
- 'pyfpga/**'
- 'hdltools/**'
- 'tests/**'

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Install dependencies
run: pip install pycodestyle pylint
- name: Run linting
run: pip install pycodestyle pylint pyslang
- name: Run lint
run: make lint
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ name: 'test'
on:
push:
paths:
- 'pyfpga/**'
- 'hdltools/**'
- 'tests/**'

jobs:
test:
strategy:
matrix:
pyver: ['3.8', '3.9', '3.10', '3.11', '3.12']
pyver: ['3.11', '3.12', '3.13', '3.14', '3.15']
runs-on: ubuntu-latest
name: ${{ matrix.pyver }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Set up Python ${{ matrix.pyver }}
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.pyver }}
- name: Install dependencies
run: pip install pytest
- name: Run tests
run: pip install pyslang
- name: Run test
run: make test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ignore
__pycache__
ignore
venv
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
#!/usr/bin/make

CDIR := $(shell pwd)
export PYTHONPATH := $(CDIR)/src:$(PYTHONPATH)

all: lint test

lint:
pycodestyle hdltools tests
pycodestyle hdltools
pylint -s n hdltools
git diff --check --cached

test:
PYTHONPATH=$(PWD) pytest -vv
python3 hdltools/apps/genstub.py -h
python3 hdltools/apps/genwrap.py -h
python3 hdltools/apps/modcomp.py -h
python3 hdltools/apps/modjson.py -h

clean:
py3clean .
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

Scripts for automating HDL-related tasks.

> **NOTE:** this project is still in development but already functional (March 2025).
> **NOTE:** this project is still in development but already functional (Oct 2026).
2 changes: 1 addition & 1 deletion hdltools/apps/genstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@


args = cli_parser('stub')
ctrl = HDLController('stub')
ctrl = HDLController('stub', defines=args.defines)
ctrl.generate(args.file, args.top, args.suffix)
ctrl.write(args.output)
2 changes: 1 addition & 1 deletion hdltools/apps/genwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@


args = cli_parser('wrap')
ctrl = HDLController('wrap')
ctrl = HDLController('wrap', defines=args.defines)
ctrl.generate(args.file, args.top, args.suffix)
ctrl.write(args.output)
2 changes: 1 addition & 1 deletion hdltools/apps/modcomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


args = cli_parser('comp')
ctrl = HDLController('comp')
ctrl = HDLController('comp', defines=args.defines)
mod1 = ctrl.generate(args.files[0], args.top1)
mod2 = ctrl.generate(args.files[1], args.top2)

Expand Down
19 changes: 19 additions & 0 deletions hdltools/apps/modjson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright (C) 2026 HDLtools Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""Print collected info as JSON."""

import json

from hdltools.cli_parser import cli_parser
from hdltools.mod_parser import ModParser


args = cli_parser()
parser = ModParser(args.file, defines=args.defines)
parser.parse()
data = parser.get_modules()
print(json.dumps(data, indent=4))
22 changes: 18 additions & 4 deletions hdltools/cli_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (C) 2025 HDLtools Project
# Copyright (C) 2025-2026 HDLtools Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
Expand All @@ -9,16 +9,30 @@
import argparse


def cli_parser(app):
def cli_parser(app=None):
"""Parse command-line arguments based on the selected application."""

parser = argparse.ArgumentParser()

if app == 'comp':
parser.add_argument('--top1')
parser.add_argument('--top2')
parser.add_argument('files', nargs=2)
else:
elif app in ['stub', 'wrap']:
parser.add_argument('--top')
parser.add_argument('--suffix', default=f'_{app}')
parser.add_argument('--output')

parser.add_argument(
'-D', '--define',
dest='defines',
action='append',
default=[],
help='Define macros for the SV preprocessor'
)

if app == 'comp':
parser.add_argument('files', nargs=2)
else:
parser.add_argument('file')

return parser.parse_args()
35 changes: 18 additions & 17 deletions hdltools/hdl_controller.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
#
# Copyright (C) 2025 HDLtools Project
# Copyright (C) 2025-2026 HDLtools Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""
This module defines the HDLController class that orchestrates the entire flow
from reading and sanitizing HDL code, parsing modules, and generating output
HDL code using Jinja2 templates.
This module defines the HDLController class that orchestrates SystemVerilog
parsing (via pyslang) and output code generation (via Jinja2).
"""

import sys

from hdltools.hdl_reader import HDLReader
from hdltools.mod_parser import ModParser
from hdltools.hdl_writer import HDLWriter


class HDLController:
"""A central controller to orchestrate the HDL processing tasks."""
"""Orchestrate SV parsing and template-based code generation tasks."""

def __init__(self, template):
self.reader = HDLReader()
self.parser = ModParser()
def __init__(self, template, defines=None):
"""Initializes controller."""
self.parser = ModParser(defines=defines)
self.writer = HDLWriter()
self.template = template

def generate(self, filepath, top=None, suffix=None):
"""Generates HDL code from the input file."""
"""Validates source, parses with slang, and renders output template."""

try:
self.reader.read_file(filepath)
# Check to ensure the file is accessible
# before passing its path to the compiler driver.
with open(filepath, 'r', encoding='utf-8') as f:
f.read(1)
except (OSError, UnicodeDecodeError):
self._error(f'file not found ({filepath})')
self._error(f'file not found or unreadable ({filepath})')

raw_code = self.reader.get_code()
self.parser.set_code(raw_code)
self.parser.set_file(filepath)
self.parser.parse()

modules = self.parser.get_names()
if not modules:
self._error('module not found')
self._error('no modules found or parsing failed')
if top and top not in modules:
self._error(f'top not found ({top})')
self._error(f'top module not found ({top})')
if not top:
top = modules[0]

Expand All @@ -54,13 +54,14 @@ def generate(self, filepath, top=None, suffix=None):
return self.writer.get_code()

def write(self, filepath):
"""Writes the generated HDL code."""
"""Writes the generated code to a file, or stdout if empty."""
if not filepath:
print(self.writer.get_code())
else:
self.writer.write_file(filepath)

@staticmethod
def _error(message, ecode=1):
"""Logs an error message and terminates execution."""
print(f'ERROR: {message}')
sys.exit(ecode)
41 changes: 0 additions & 41 deletions hdltools/hdl_reader.py

This file was deleted.

Loading