Skip to content

Commit

Permalink
Merge pull request #212 from harshkhandeparkar/verilog-mako
Browse files Browse the repository at this point in the history
[GSoC] Common Verilog Generation API and Implementation in `temp-sense-gen`
  • Loading branch information
msaligane authored Jul 18, 2023
2 parents ec5a8c2 + cbf4381 commit e421346
Show file tree
Hide file tree
Showing 22 changed files with 569 additions and 81 deletions.
90 changes: 90 additions & 0 deletions docs/source/common-python-api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Common Python API
=================

.. contents:: Contents
:local:

Introduction
------------

OpenFASoC generators have a Python script that runs the generator flow (See `Generators <generators.html>`_ for more information). Many of the steps in the Python script are common across generators.

A common Python module is exported in ``openfasoc/generators/common/``to reduce code duplication and simplify the creation of new generators. This module exports various functions and constants which may be used in generators.
Importing
---------
Since the common Python module is in the ``openfasoc/generators/common/`` directory, which is a parent directory, the path to this directory must be added using ``sys.path.append()``.

.. code-block:: python
import sys
sys.path.append(
# This is the relative path to the `openfasoc/generators/common/` directory.
os.path.join(os.path.dirname(__file__), '..', '..')
)
Once the directory is added to the path, the exported modules can be imported using the standard Python import syntax.

.. code-block:: python
from common.verilog_generation import generate_verilog
See `Exported Modules`_ for a list of exported modules and their respective functions and constants.

Exported Modules
----------------
The top level module ``common`` exports the following submodules.

1. Verilog Generation (``common.verilog_generation``)
#####################################################
This module exports functions and constants used in the Verilog generation step. See `Temperature Sensor Generator <flow-tempsense.html#verilog-generation>`_ for more about Verilog generation.

This module uses the `Mako <https://www.makotemplates.org/>`_ templating library to convert source Verilog templates into final Verilog files used in the OpenROAD flow.

Functions
^^^^^^^^^
1. ``generate_verilog(parameters: dict, src_dir: str, out_dir: str) -> None``

Reads source Verilog files from ``src_dir`` and generates output Verilog files for synthesis in the OpenROAD flow. The source files are `Mako <https://makotemplates.org>`_ templates.

The ``parameters`` argument is a dictionary of all the parameters used in the source Verilog Mako templates. Use the ``${parameter}}`` syntax in the source files to insert parameters. For example, the number of inverters in the `Temperature Sensor Generator <flow-tempsense.html>`_ is a parameter.

This function maintains the source directory (``src_dir``) structure in the output directory (``out_dir``). i.e., source files from a subdirectory of the ``src_dir`` will be generated in a subdirectory in ``out_dir`` with the same name.

Arguments:
- ``src_dir`` (str): Path to the directory with the source Verilog templates. (default: ``src/``)
- ``out_dir`` (str): Path to the directory in which the output will be generated. (default: ``flow/design/src/``)
- ``parameters`` (dict): Dictionary of all the parameters used in the `Mako templates <https://makotemplates.org>`_.

Example:
.. code-block:: python
generate_verilog(
# Generates the output in flow/design/src/
out_dir=os.path.join('flow', 'design', 'src', 'tempsense'),
# Sets the parameters used in the design
parameters={
"ninv": 6,
"nhead": 3,
"design_name": "tempsenseInst_error",
}
)
See the generators' Python files in ``tools/`` for more examples.

This function also appends (can be directly used in the source Verilog files) the following Mako `defs <https://docs.makotemplates.org/en/latest/defs.html>`_:
- ``cell(name)``

This def returns the name of a standard cell for a given platform. Currently, it only supports the sky130 platform. The naming scheme for sky130 is ``${cell_prefix}${name}${cell_suffix}``.

Here ``name`` is an argument passed to the ``cell()`` def, and ``cell_prefix`` and ``cell_suffix`` are set in the ``parameters`` argument passed to the ``generate_verilog()`` function.

For example, an inverter cell can be inserted using the syntax ``${cell('inv')}``. If the prefix is ``sky130_fd_sc_hd__`` (sky130hd) and the suffix is ``_1``, the cell will be replaced with ``sky130_fd_sc_hd__inv_1``. The same statement will be replaced with ``sky130_fd_sc_hs__inv_1`` for the sky130hs platform.

Use the constant ``COMMON_PLATFORMS_PREFIX_MAP`` for mapping a sky130 platform to its platform.

Constants
^^^^^^^^^
1. ``COMMON_PLATFORMS_PREFIX_MAP``

This is a dictionary of common platforms (currently sky130) and their cell naming prefixes. See the ``cell()`` def in the ``generate_verilog()`` function for more information on how to use it.
27 changes: 15 additions & 12 deletions docs/source/flow-tempsense.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,23 @@ Verilog generation
Running ``make sky130hd_temp`` (temp for "temperature sensor") executes the `temp-sense-gen.py <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/tools/temp-sense-gen.py>`_ script from temp-sense-gen/tools/. This file takes the input specifications from `test.json <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/test.json>`_ and outputs Verilog files containing the description of the circuit.

.. note::
temp-sense-gen.py calls other modules from temp-sense-gen/tools/ during execution. For example, `readparamgen.py <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/tools/readparamgen.py>`_ is in charge of reading test.json, checking for correct user input and choosing the correct circuit elements.
temp-sense-gen.py calls other modules from temp-sense-gen/tools/ and generators/common/ during execution. For example, `readparamgen.py <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/tools/readparamgen.py>`_ is in charge of reading test.json, checking for correct user input and choosing the correct circuit elements.

The generator starts from a Verilog template of the temperature sensor circuit, located in `temp-sense-gen/src/ <https://github.com/idea-fasoc/OpenFASOC/tree/main/openfasoc/generators/temp-sense-gen/src>`_. The ``.v`` template files have lines marked with ``@@``, which are replaced according to the specifications.
The generator starts from a Verilog template of the temperature sensor circuit, located in `temp-sense-gen/src/ <https://github.com/idea-fasoc/OpenFASOC/tree/main/openfasoc/generators/temp-sense-gen/src>`_. The ``.v`` templates follow `Mako <https://makotemplates.org>`_ syntax. For example, ``${param}`` is replaced with the value of the parameter ``param``.

Example: `counter_generic.v line 31 <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/src/counter_generic.v#L31>`_ is replaced during Verilog generation.
See `Common Python API <common-python-api.html#verilog-generation-common-verilog-generation>`_ for more information.

Example: In `counter.v line 31 <https://github.com/idea-fasoc/OpenFASOC/blob/main/openfasoc/generators/temp-sense-gen/src/counter.v#L32>`_, ``${cell('buf')}`` with the buffer cell which is ``sky130_fd_sc_hd__buf_1`` for sky130hd.

.. code-block:: verilog
:emphasize-lines: 3
:emphasize-lines: 4
:linenos:
:lineno-start: 29
assign done_sens = WAKE_pre && doneb;
assign done_ref = WAKE && doneb;
@@ @np Buf_DONE(.A(done_pre), .nbout(DONE));
assign done_ref = WAKE && doneb;
${cell('buf')} Buf_DONE(.A(done_pre), .X(DONE));
always @ (*) begin
case (done_pre)
Expand Down Expand Up @@ -87,15 +90,15 @@ OpenROAD Flow takes a design from the temp-sense-gen/flow/design/ directory and
└── design
├── sky130hd
│ └── tempsense
│ ├── config.mk <--
│ ├── config.mk <--
│ └── constraint.sdc
└── src
└── tempsense
├── counter.v <--
├── TEMP_ANALOG_hv.nl.v <--
├── TEMP_ANALOG_lv.nl.v <--
├── TEMP_AUTO_def.v <--
└── tempsenseInst_error.v <--
├── counter.v <--
├── TEMP_ANALOG_hv.v <--
├── TEMP_ANALOG_lv.v <--
├── TEMP_AUTO_def.v <--
└── tempsenseInst.v <--
For more information on OpenROAD Flow, check their `docs <https://openroad.readthedocs.io/en/latest/user/GettingStarted.html>`_.

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ If you are willing to contribute, please visit the :doc:`developers-guide` secti

getting-started
generators
common-python-api
tapeouts
examples
developers-guide
Expand Down
10 changes: 10 additions & 0 deletions openfasoc/generators/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""A common module used in OpenFASOC generators.
This module exports functions used in OpenFASOC generators. The following submodules and functions are exported.
- `common.verilog_generation`
1. `generate_verilog(parameters: dict, src_dir: str, out_dir: str) -> None`: Used to generate synthesizable Verilog files (for OpenROAD flow) from source Mako-based Verilog templates.
2. `COMMON_PLATFORMS_PREFIX_MAP` (dict): This is a dictionary of common platforms (currently sky130) and their cell naming prefixes.
See individual function documentation for more information on a particular function.
"""
102 changes: 102 additions & 0 deletions openfasoc/generators/common/verilog_generation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from mako.template import Template
from os import path, makedirs, listdir

# TODO: Find a better way to import common used defs in the future.
__COMMON_MAKO_DEFS = '''
<%def name="cell(name)">${cell_prefix}${name}${cell_suffix}</%def>
'''

def _mako_defs_preprocessor(input: str) -> str:
"""A Mako preprocessor that appens commonly used defs to the template.
Mako templates have a preprocessor argument. See https://docs.makotemplates.org/en/latest/usage.html#mako.template.Template.params.preprocessor.
This preprocessor adds defs commonly used in Verilog files to the template.
TODO: Find a better way to import common used defs in the future.
"""
return __COMMON_MAKO_DEFS + input

def _generate_file(input_path: str, output_path: str, parameters: dict) -> None:
"""Generates a single output Verilog file from its Mako template.
Arguments:
- `input_path` (str): Path to the input file (Mako template) with the extension.
- `output_path` (str): Path to the output file location with the extension.
- `parameters` (dict): Dictionary of all the parameters used in the Mako template.
"""

# TODO: Find a better way to import common used defs in the future.
template = Template(filename=input_path, preprocessor=_mako_defs_preprocessor)

out_file = open(output_path, "w")
out_file.write(template.render(**parameters))

def _generate_subdirectory(src_dir: str, out_dir: str, parameters: dict) -> None:
"""Generates the output Verilog files of a single subdirectory of Mako templates.
Reads Mako templates from a subdirectory (`src_dir`), generates the output files in the output directory (`out_dir`), and maintains the directory structure. i.e., templates from a subdirectory of the `src_dir` will be generated in a subdirectory in `out_dir` with the same name.
This function recursively calls itself for subdirectories.
Arguments:
- `src_dir` (str): Path to the source directory with Mako templates.
- `out_dir` (str): Path to the output directory.
- `parameters` (dict): Dictionary of all the parameters used in the Mako template.
"""
# generate the output directory if it doesn't exist
if not path.exists(out_dir):
makedirs(out_dir)

for filename in listdir(src_dir):
input_filepath = path.join(src_dir, filename)
output_filepath = path.join(out_dir, filename)

if path.isdir(input_filepath):
# if the path is a subdirectory, recursively call the function
_generate_subdirectory(
input_filepath,
output_filepath,
parameters
)
else:
# if the path is a fine, generate the output
_generate_file(
input_filepath,
output_filepath,
parameters
)

def generate_verilog(
parameters: dict,
src_dir: str = "src",
out_dir: str = path.join("flow", "design", "src")
) -> None:
"""Generates output Verilog files from source Mako templates.
Reads source Verilog files from `src_dir` and generates output Verilog files for synthesis in the OpenROAD flow.
The source files are Mako templates. See https://makotemplates.org for syntax and documentation.
This function maintains the source directory (`src_dir`) structure in the output directory (`out_dir`). i.e., source files from a subdirectory of the `src_dir` will be generated in a subdirectory in `out_dir` with the same name.
Arguments:
- `parameters` (dict): Dictionary of all the parameters used in the Mako templates. See https://makotemplates.org for documentation.
- `src_dir` (str): Path to the directory with the source Verilog templates.
- `out_dir` (str): Path to the directory in which the output will be generated.
"""
_generate_subdirectory(src_dir, out_dir, parameters)

# A dictionary of commonly used platforms and the prefix used in their cell naming
# Currently includes only sky130 platform prefixes
COMMON_PLATFORMS_PREFIX_MAP = {
"sky130hd": "sky130_fd_sc_hd__",
"sky130hs": "sky130_fd_sc_hs__",
"sky130hvl": "sky130_fd_sc_hvl__",
"sky130osu12Ths": "sky130_osu_sc_12T_hs__",
"sky130osu12Tms": "sky130_osu_sc_12T_ms__",
"sky130osu12Tls": "sky130_osu_sc_12T_ls__",
"sky130osu15Ths": "sky130_osu_sc_15T_hs__",
"sky130osu15Tms": "sky130_osu_sc_15T_ms__",
"sky130osu15Tls": "sky130_osu_sc_15T_ls__",
"sky130osu18Ths": "sky130_osu_sc_18T_hs__",
"sky130osu18Tms": "sky130_osu_sc_18T_ms__",
"sky130osu18Tls": "sky130_osu_sc_18T_ls__"
}
1 change: 1 addition & 0 deletions openfasoc/generators/temp-sense-gen/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ clean:
rm -f error_within_x.csv golden_error_opt.csv search_result.csv
rm -rf work
rm -rf tools/*.pyc tools/__pycache__/
rm -rf flow/design/src/tempsense
cd flow && make clean_all
cd simulations && rm -rf run

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tempsense/

This file was deleted.

4 changes: 0 additions & 4 deletions openfasoc/generators/temp-sense-gen/src/.gitignore

This file was deleted.

9 changes: 6 additions & 3 deletions openfasoc/generators/temp-sense-gen/src/TEMP_ANALOG_hv.v
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ counter async_counter_0(
);

(* keep *)
@@ @nf a_header_@nh(.VIN(VIN));
SLC
@@ @no a_buffer_0 (.A(lc_0), .nbout(lc_out));
% for i in range(nhead):
${header_cell} a_header_${i}(.VIN(VIN));
% endfor
// SLC
${slc_cell} a_lc_0(.IN(out), .INB(outb), .VOUT(lc_0));
${cell('buf')} a_buffer_0 (.A(lc_0), .X(lc_out));

endmodule
29 changes: 19 additions & 10 deletions openfasoc/generators/temp-sense-gen/src/TEMP_ANALOG_lv.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@ module TEMP_ANALOG_lv (EN, OUT, OUTB);
// inout in_vin;
output OUT, OUTB;
wire n;
@@ wire n@nn;

% for i in range(ninv + 1):
wire n${i + 1};
% endfor

wire nx1, nx2, nx3, nb1, nb2;
@@ @na a_nand_0 ( .A(EN), .B(n@n0), .Y(n1));
@@ @nb a_inv_@ni ( .A(n@n1), .Y(n@n2));
@@ @ng a_inv_m1 ( .A(n@n3), .Y(nx1));
@@ @nk a_inv_m2 ( .A(n@n4), .Y(nx2));
@@ @nm a_inv_m3 ( .A(nx2), .Y(nx3));
@@ @np a_buf_3 ( .A(nx3), .nbout(nb2));
@@ @nc a_buf_0 ( .A(nx1), .nbout(nb1));
@@ @nd a_buf_1 ( .A(nb1), .nbout(OUT));
@@ @ne a_buf_2 ( .A(nb2), .nbout(OUTB));

${cell('nand2')} a_nand_0 ( .A(EN), .B(n${ninv + 1}), .Y(n1));

% for i in range(ninv):
${cell('inv')} a_inv_${i} ( .A(n${i + 1}), .Y(n${i + 2}));
% endfor

${cell('inv')} a_inv_m1 ( .A(n${ninv + 1}), .Y(nx1));
${cell('inv')} a_inv_m2 ( .A(n${ninv + 1}), .Y(nx2));
${cell('inv')} a_inv_m3 ( .A(nx2), .Y(nx3));
${cell('buf')} a_buf_3 ( .A(nx3), .X(nb2));
${cell('buf')} a_buf_0 ( .A(nx1), .X(nb1));
${cell('buf')} a_buf_1 ( .A(nb1), .X(OUT));
${cell('buf')} a_buf_2 ( .A(nb2), .X(OUTB));

endmodule
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ module counter#
assign done_ref = WAKE && doneb;
// BUFH_X4M_A9TR Buf_DONE(.A(done_pre), .Y(DONE));
// BUF_X0P4N_A10P5PP84TR_C14 Buf_DONE(.A(done_pre), .Y(DONE));
@@ @np Buf_DONE(.A(done_pre), .nbout(DONE));

${cell('buf')} Buf_DONE(.A(done_pre), .X(DONE));
//assign RESET_CLK_REF = ~q1;

always @ (*) begin
Expand Down
2 changes: 1 addition & 1 deletion openfasoc/generators/temp-sense-gen/src/tempsenseInst.v
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module tempsenseInst_error
module ${design_name}
(
input CLK_REF,
input RESET_COUNTERn,
Expand Down
Loading

0 comments on commit e421346

Please sign in to comment.