From 36a206df4e65a7432b4c112e11b862e2c7797961 Mon Sep 17 00:00:00 2001 From: Aba Date: Wed, 15 Nov 2023 13:27:00 -0800 Subject: [PATCH] Python packaging & documentation --- .gitignore | 33 ++++++++- .readthedocs.yaml | 13 ++++ deepsocflow/__init__.py | 1 + deepsocflow/hardware.py | 138 ++++++++++++++++++++++++++++++++++++ deepsocflow/utils.py | 4 ++ docs/Makefile | 20 ++++++ docs/make.bat | 35 +++++++++ docs/requirements.txt | 5 ++ docs/source/conf.py | 59 +++++++++++++++ docs/source/deepsocflow.rst | 21 ++++++ docs/source/index.rst | 20 ++++++ docs/source/modules.rst | 7 ++ pyproject.toml | 18 +++++ 13 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 deepsocflow/__init__.py create mode 100644 deepsocflow/hardware.py create mode 100644 deepsocflow/utils.py create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt create mode 100644 docs/source/conf.py create mode 100644 docs/source/deepsocflow.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/modules.rst create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index 8687d87..9219d59 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,34 @@ test/temp test/py/* -docs/* -.svls.toml \ No newline at end of file +.svls.toml + + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Sphinx documentation +docs/_build/ \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3a7e5af --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: "2" + +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +python: + install: + - requirements: docs/requirements.txt + +sphinx: + configuration: docs/source/conf.py \ No newline at end of file diff --git a/deepsocflow/__init__.py b/deepsocflow/__init__.py new file mode 100644 index 0000000..1d1f13a --- /dev/null +++ b/deepsocflow/__init__.py @@ -0,0 +1 @@ +from deepsocflow.hardware import Hardware, example_function \ No newline at end of file diff --git a/deepsocflow/hardware.py b/deepsocflow/hardware.py new file mode 100644 index 0000000..1045ea0 --- /dev/null +++ b/deepsocflow/hardware.py @@ -0,0 +1,138 @@ +# from deepsocflow import Hardware, Bundle, QInput, BundleModel, QConvCore, QDenseCore, QAdd, QPool, Softmax, QLeakyReLu +import numpy as np +from abc import ABC, abstractmethod +import json +from deepsocflow.utils import * + + +class Hardware: + """_summary_ + """ + def __init__( + self, + processing_elements: (int, int) = (8,24), + frequency_mhz: int = 250, + bits_input: int = 4, + bits_weights: int = 4, + bits_sum: int = 16, + bits_bias: int = 16, + max_batch_size: int = 512, + max_channels_in: int = 512, + max_channels_out: int = 512, + max_kernel_size: int = 13, + max_image_size: int = 32, + weights_cache_kbytes: int =384, + edge_cache_kbytes: int|None = None + ): + + self.params = locals() + self.params = {k:self.params[k] for k in self.params if not k == 'self'} + + ''' + Validation + ''' + assert bits_input in [1,2,4,8] and bits_weights in [1,2,4,8] + assert bits_bias in [8,16,32] + + self.ROWS, self.COLS = processing_elements + self.FREQ = frequency_mhz + self.X_BITS = bits_input + self.K_BITS = bits_weights + self.Y_BITS = bits_sum + self.B_BITS = bits_bias + self.XN_MAX = max_batch_size + self.CI_MAX = max_channels_in + self.CO_MAX = max_channels_out + self.KH_MAX, self.KW_MAX = max_kernel_size if (type(max_kernel_size) == tuple) else (max_kernel_size, max_kernel_size) + self.XH_MAX, self.XW_MAX = max_image_size if (type(max_image_size) == tuple) else (max_image_size, max_image_size) + + ''' + Width of weights RAM = K_BITS * COLS + Number of weights RAMs = 2 + ''' + self.RAM_WEIGHTS_DEPTH = int((weights_cache_kbytes*1024)/(self.K_BITS*self.COLS*2)) + + ''' + Depth of RAM needed for edge padding = k != 1 ? ci*xw*(blocks-1) : 0 + ''' + self.RAM_EDGES_DEPTH = edge_cache_kbytes if edge_cache_kbytes is not None else int(self.CI_MAX * self.XW_MAX * np.ceil(self.XH_MAX/self.ROWS)-1) + + self.L_MAX = int(np.ceil(self.XH_MAX//self.ROWS)) + self.CONFIG_BEATS = 0 + self.X_PAD = int(np.ceil(self.KH_MAX//2)) + self.BITS_KW2 = clog2((self.KW_MAX+1)/2) + self.BITS_KH2 = clog2((self.KH_MAX+1)/2) + self.BITS_CIN_MAX = clog2(self.CI_MAX) + self.BITS_COLS_MAX = clog2(self.XW_MAX) + self.BITS_BLOCKS_MAX = clog2(self.L_MAX) + self.BITS_XN_MAX = clog2(self.XN_MAX) + self.BITS_RAM_WEIGHTS_ADDR = clog2(self.RAM_WEIGHTS_DEPTH) + + self.IN_BITS = self.OUT_BITS = 64 + + + def export_json(self, path='./hardware.json'): + with open(path, 'w') as f: + json.dump(self.params, f, indent=4) + + + @staticmethod + def from_json(path='./hardware.json'): + with open(path, 'r') as f: + hw = Hardware(**json.load(f)) + return hw + + + def export(self): + + with open('rtl/include/config_hw.svh', 'w') as f: + f.write(f''' +// Written from Hardware.export() + +`define ROWS {self.ROWS :<10} // PE rows, constrained by resources +`define COLS {self.COLS :<10} // PE cols, constrained by resources +`define X_BITS {self.X_BITS :<10} // Bits per word in input +`define K_BITS {self.K_BITS :<10} // Bits per word in input +`define Y_BITS {self.Y_BITS :<10} // Bits per word in output of conv + +`define KH_MAX {self.KH_MAX :<10} // max of kernel height, across layers +`define KW_MAX {self.KW_MAX :<10} // max of kernel width, across layers +`define XH_MAX {self.XH_MAX :<10} // max of input image height, across layers +`define XW_MAX {self.XW_MAX :<10} // max of input image width, across layers +`define XN_MAX {self.XN_MAX :<10} // max of input batch size, across layers +`define CI_MAX {self.CI_MAX :<10} // max of input channels, across layers +`define CONFIG_BEATS {self.CONFIG_BEATS :<10} // constant, for now +`define RAM_WEIGHTS_DEPTH {self.RAM_WEIGHTS_DEPTH :<10} // CONFIG_BEATS + max(KW * CI), across layers +`define RAM_EDGES_DEPTH {self.RAM_EDGES_DEPTH :<10} // max (KW * CI * XW), across layers when KW != 1 + +`define DELAY_ACC 1 // constant, for now +`define DELAY_MUL 2 // constant, for now +`define DELAY_W_RAM 2 // constant, for now + +`define S_WEIGHTS_WIDTH_LF {self.IN_BITS :<10} // constant (64), for now +`define S_PIXELS_WIDTH_LF {self.IN_BITS :<10} // constant (64), for now +`define M_OUTPUT_WIDTH_LF {self.OUT_BITS :<10} // constant (64), for now +''') + + + with open('fpga/scripts/config_hw.tcl', 'w') as f: + f.write(f''' +# Written from Hardware.export() + +set RAM_WEIGHTS_DEPTH {self.RAM_WEIGHTS_DEPTH} +set ROWS {self.ROWS} +set COLS {self.COLS} +set X_BITS {self.X_BITS} +set K_BITS {self.K_BITS} +set Y_BITS {self.Y_BITS} +set DELAY_W_RAM 2 +set RAM_EDGES_DEPTH {self.RAM_EDGES_DEPTH} +set KH_MAX {self.KH_MAX} +set S_WEIGHTS_WIDTH_LF {self.IN_BITS} +set S_PIXELS_WIDTH_LF {self.IN_BITS} +set M_OUTPUT_WIDTH_LF {self.OUT_BITS} +''') + + +def example_function(): + print("Hello World!") \ No newline at end of file diff --git a/deepsocflow/utils.py b/deepsocflow/utils.py new file mode 100644 index 0000000..7d9f731 --- /dev/null +++ b/deepsocflow/utils.py @@ -0,0 +1,4 @@ +import numpy as np + +def clog2(x): + return int(np.ceil(np.log2(x))) \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..dc1312a --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..1707b04 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +sphinx==5.0.2 +sphinx-rtd-theme==1.3.0 +numpy==1.23.5 +qkeras==0.9.0 +tensorflow==2.12.0 \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..5811428 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,59 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('../../')) + + +# -- Project information ----------------------------------------------------- + +project = 'deepsocflow' +copyright = '2023, Abarajithan G, Zhenghua Ma' +author = 'Abarajithan G, Zhenghua Ma' + +# The full version, including alpha/beta/rc tags +release = '0.0.1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx_rtd_theme', + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] \ No newline at end of file diff --git a/docs/source/deepsocflow.rst b/docs/source/deepsocflow.rst new file mode 100644 index 0000000..c4397d5 --- /dev/null +++ b/docs/source/deepsocflow.rst @@ -0,0 +1,21 @@ +deepsocflow package +=================== + +Submodules +---------- + +deepsocflow.hardware module +--------------------------- + +.. automodule:: deepsocflow.hardware + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: deepsocflow + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..3b5a200 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,20 @@ +.. deepsocflow documentation master file, created by + sphinx-quickstart on Wed Nov 15 10:48:07 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to deepsocflow's documentation! +======================================= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 0000000..85f21e5 --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,7 @@ +deepsocflow +=========== + +.. toctree:: + :maxdepth: 4 + + deepsocflow diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..cba8cf7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "deepsocflow" +authors = [{name = "Abarajithan G", email = "abarajithan07@gmail.com"}] +version = "0.0.1" +description = "Your DNNs to FPGA/ASIC SoCs in minutes!" +requires-python = ">=3.10" +license = {file = "LICENSE"} +readme = "README.md" +repository = "https://github.com/abarajithan11/deepsocflow" +classifiers=[ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3.10", + "Operating System :: OS Independent", +] \ No newline at end of file