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
42 changes: 6 additions & 36 deletions hdltools/apps/genstub.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,11 @@

"""This script generates a stub for the specified module."""

import argparse
import logging
import sys
from hdltools.cli_parser import cli_parser
from hdltools.hdl_controller import HDLController

from hdltools.mod_parse import ModParse
from hdltools.gen_file import GenFile


logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

parser = argparse.ArgumentParser()
parser.add_argument('--top')
parser.add_argument('--suffix', default='_stub')
parser.add_argument('--output')
parser.add_argument('file')
args = parser.parse_args()

modules = ModParse(args.file)
names = modules.get_names()

if not len(names) or (args.top and args.top not in names):
logging.error('module not found')
sys.exit(1)

if not args.top:
args.top = names[0]

module = modules.get_module(args.top)
module['name'] = args.top
module['suffix'] = args.suffix

top = GenFile()
top.render('stub', module)

if not args.output:
print(top)
else:
top.write(args.output)
args = cli_parser('stub')
ctrl = HDLController('stub')
ctrl.generate(args.file, args.top, args.suffix)
ctrl.write(args.output)
42 changes: 6 additions & 36 deletions hdltools/apps/genwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,11 @@

"""This script generates a wrapper for the specified module."""

import argparse
import logging
import sys
from hdltools.cli_parser import cli_parser
from hdltools.hdl_controller import HDLController

from hdltools.mod_parse import ModParse
from hdltools.gen_file import GenFile


logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

parser = argparse.ArgumentParser()
parser.add_argument('--top')
parser.add_argument('--suffix', default='_wrap')
parser.add_argument('--output')
parser.add_argument('file')
args = parser.parse_args()

modules = ModParse(args.file)
names = modules.get_names()

if not len(names) or (args.top and args.top not in names):
logging.error('module not found')
sys.exit(1)

if not args.top:
args.top = names[0]

module = modules.get_module(args.top)
module['name'] = args.top
module['suffix'] = args.suffix

top = GenFile()
top.render('wrap', module)

if not args.output:
print(top)
else:
top.write(args.output)
args = cli_parser('wrap')
ctrl = HDLController('wrap')
ctrl.generate(args.file, args.top, args.suffix)
ctrl.write(args.output)
41 changes: 11 additions & 30 deletions hdltools/apps/modcomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,22 @@

"""This script compares two Verilog modules."""

import argparse
import difflib
import json
import logging
import sys

from hdltools.mod_parse import ModParse
from hdltools.cli_parser import cli_parser
from hdltools.hdl_controller import HDLController


logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
args = cli_parser('comp')
ctrl = HDLController('comp')
mod1 = ctrl.generate(args.files[0], args.top1)
mod2 = ctrl.generate(args.files[1], args.top2)

parser = argparse.ArgumentParser()
parser.add_argument('--top1')
parser.add_argument('--top2')
parser.add_argument('files', nargs=2)
args = parser.parse_args()

modules = ModParse(args.files[0])
module1 = modules.get_module(args.top1)
if not module1:
logging.error('first module not found')
sys.exit(1)

modules = ModParse(args.files[1])
module2 = modules.get_module(args.top2)
if not module2:
logging.error('second module not found')
sys.exit(1)

if module1 == module2:
logging.info('modules are equal')
if mod1 == mod2:
print('INFO: modules are equal')
else:
logging.error('modules have differences')
text1 = json.dumps(module1, indent=2, sort_keys=True).splitlines()
text2 = json.dumps(module2, indent=2, sort_keys=True).splitlines()
diff = difflib.unified_diff(text1, text2, lineterm='')
print("\n".join(diff))
print('ERROR: modules are different\n')
diff = list(difflib.ndiff(mod1.splitlines(), mod2.splitlines()))
print('\n'.join(diff))
sys.exit(1)
24 changes: 24 additions & 0 deletions hdltools/cli_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# Copyright (C) 2025 HDLtools Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""This file implements the command-line parser."""

import argparse


def cli_parser(app):
"""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:
parser.add_argument('--top')
parser.add_argument('--suffix', default=f'_{app}')
parser.add_argument('--output')
parser.add_argument('file')
return parser.parse_args()
34 changes: 0 additions & 34 deletions hdltools/gen_file.py

This file was deleted.

66 changes: 66 additions & 0 deletions hdltools/hdl_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# Copyright (C) 2025 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.
"""

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."""

def __init__(self, template):
self.reader = HDLReader()
self.parser = ModParser()
self.writer = HDLWriter()
self.template = template

def generate(self, filepath, top=None, suffix=None):
"""Generates HDL code from the input file."""

try:
self.reader.read_file(filepath)
except (OSError, UnicodeDecodeError):
self._error(f'file not found ({filepath})')

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

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

context = self.parser.get_module(top)
context['name'] = top
context['suffix'] = suffix
self.writer.render(self.template, context)

return self.writer.get_code()

def write(self, filepath):
"""Writes the generated HDL code."""
if not filepath:
print(self.writer.get_code())
else:
self.writer.write_file(filepath)

@staticmethod
def _error(message, ecode=1):
print(f'ERROR: {message}')
sys.exit(ecode)
72 changes: 0 additions & 72 deletions hdltools/hdl_detect.py

This file was deleted.

41 changes: 41 additions & 0 deletions hdltools/hdl_reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# Copyright (C) 2025 HDLtools Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""
Reads and sanitizes the input HDL code by removing comments, extra whitespaces
and newlines.
"""

import re


class HDLReader:
"""Reads and sanitizes the input HDL code."""

def __init__(self, code=''):
self.code = code

def read_file(self, path):
"""Reads the HDL code from file."""
with open(path, 'r', encoding='utf-8') as fobj:
self.code = fobj.read()

def set_code(self, code):
"""Directly sets the HDL code."""
self.code = code

def is_vhdl(self):
"""Return True if the code seems to be VHDL."""
return 'endmodule' not in self.code.lower()

def get_code(self):
"""Retrieves the sanitized HDL code."""
if self.is_vhdl():
text = re.sub(r'--[^\n]*', '', self.code)
else:
text = re.sub(r'//[^\n]*', '', self.code)
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
return re.sub(r'\s+', ' ', text).strip()
Loading