Skip to main content

Powerful yet simple-to-use templating tool for SystemVerilog with plenty of useful features.

Project description

KaravaiSV

What is this?

KaravaiSV is an advanced templating tool for SystemVerilog.
It gives a developer much more abilities to parametrize design compared to classic SystemVerilog generate and define constructions.
Last but not least it gives ability to write lint errors free code with ease.

Why do you want to use it?

  1. Write once - use everywhere. Simple module with couple of parameters gives ability to use it everywhere only slightly changing config file.
  2. Flexible architecture. Let your module serve single purpose but many ways choosen design-time.
  3. Speed up development. Putting same modules with different configs rather than zoo of them does not spend your time.
  4. No repeated verification. Verify module once - use million of times.
  5. Debug with ease. Developing a new feature and found a bug? Turn off your WIP feature with parameter and see does this bug existed before.
  6. No fear to edit. Changing generation config rather than code itself leads to less manual edits and followed mistakes as well.
  7. Generation automatization. Automatization of project generation will become easy as pie.

Installation and building project

The main goal of KaravaiSV is to make everything as easy as ABC and so installation and building follows the same rule.

A: Install KaravaiSV

pip install karavaisv

B: Modify generation config for your needs

vim config/default.yaml

C: Run generate script

python generate.py

Ta-da!
Now you generated project. All generated files are located somewhere here. By default all rendered conted appears in build folder.

The A step should only be performed once per system or virtual enviroment.

Usage

There are mainly two ways of usage KSV - CLI and generation scripts.
Generation scripts are always preferred as they provides ability to make parameters correction checks, calculation derived parameters, etc.
However, CLI interface still can be used for debugging and temporarily purposes.

All of the below described examples of KSV are a best practices to write a unified generating scripts. It serves purpose to make all ksv-templated project compliant with each other.
For the compliant reasons there are strong recomendations for single modules as they are likely to be used in big projects and should be integrated with ease.
It is highly recommended, but not a mandatory to follow this rules.

Recommended usage for single module

For a repository that contains single ksv-templated module the following generate script pattern is preferred.

import karavaisv as ksv
# other imports if needed

# function that performs validity check on given parameters
def ksv_check_parameters(parameters):
    <...>
    assert parameters['data_width'] > 0, "data_width must be positive integer"
    <...>
    assert len(parameters['input_ports']) > 0, "module must have at least one input port"
    <...>


# function that calculates derived parameters
def ksv_calculate_derived_parameters(parameters):
    <...>
    # example parameter fifo_width equals max width of all input ports
    data_widths = [port['data_width'] for port in parameters['input_ports']]
    parameters['fifo_width'] = max(data_widths)
    <...>
    return parameters


# function that render code when runnung separately
def main():
    FILEPATH_DST = "build/output.sv"
    FILEPATH_PARAMS = "config/default.yaml"
    FILEPATH_SRC = "rtl/module.ksv"

    parameters = ksv.read_params_from_yaml(FILEPATH_PARAMS)
    source_code = ksv.read_source_from_file(FILEPATH_SRC)

    ksv_check_parameters(parameters)
    parameters = ksv_calculate_derived_parameters(parameters)

    rendered_code = ksv.render(source_code, parameters, filepath=FILEPATH)
    ksv.write_rendered_to_file(rendered_code, FILEPATH)

# python syntax construction to run main() function only when script is called explicitly
if __name__ == "__main__":
    main()

Feel free to take this script into all of you project as it is good enough to deal with all the job. The only thing to be changed to accumulate your needs is a ksv_check_parameters and ksv_calculate_derived_parameters function bodies and FILEPATH_DST, FILEPATH_PARAMS and FILEPATH_SRC constants in main function (their assignment can also be moved somewhere else).

Why should you use such a big script with dividing into functions? Why not single multi-line script?
The answer is simplicity to integrate. In case of your module is about to be used as an IP block in bigger projects, main generate script at whole project will perform a call to ksv_check_parameters and ksv_calculate_derived_parameters functions in every IP block to verify and calculate their parameters.
Learn more and see example at the folowing paragraph.

Described above integration ability is the reason why main function are separated and if __name__ == "__main__" are present. Other way the main function could've been executed when generate script imported.

Recommended usage for big project

For a repository that contains massive project including submodules or simple KSV files the following generate script pattern is preferred.

import karavaisv as ksv
# other imports if needed

# function that performs validity check on given parameters
def ksv_check_parameters(parameters):
    <...>
    # one more example of parameter checking
    assert parameters['arbiter'] in {'RR', 'FE', 'RND'}, "arbiter type must be one of: RR (RoundRobin), FE (FirstEntry), RND (Random)"
    <...>


# function that calculates derived parameters
# this function is also can calculate parameters for submodules
def ksv_calculate_derived_parameters(parameters):
    <...>
    return parameters


# function that render code when runnung separately
def main():
    FILEPATH_PARAMS = "config/default.yaml"

    parameters = ksv.read_params_from_yaml(FILEPATH_PARAMS)

    ksv_check_parameters(parameters)
    parameters = ksv_calculate_derived_parameters(parameters)

    # the below section of code should be repeated for every source file of that project itself (excluding submodules)

    filepath_src = "rtl/example/path/to/module.ksv"
    filepath_dst = "build/rtl/example/path/to/module.sv"

    source_code = ksv.read_source_from_file(filepath_src)

    rendered_code = ksv.render(source_code, parameters, filepath=filepath_src)
    ksv.write_rendered_to_file(rendered_code, filepath_src)

    # the below section of code should be repeated for every submodule file of that project (means that cloned as external repo)

    filepath_src = "rtl/example/path/to/module.ksv"
    filepath_dst = "build/rtl/example/path/to/module.sv"

    source_code = ksv.read_source_from_file(filepath_src)
    submodule_name_parameters = {
        <...>,
        "width": parameters['submodule_name_width'],
        <...>
    }

    rendered_code = ksv.render(source_code, submodule_name_parameters, filepath=filepath_src)
    ksv.write_rendered_to_file(rendered_code, filepath_src)


# python syntax construction to run main() function only when script is called explicitly
if __name__ == "__main__":
    main()

For submodules there are creates separated dict with parameters. This is performed as external submodules could have parameter names collision with main project files and this is one of the possible ways to solve this issue.

It is a common scenario when within a big project there are ksv-templated and simple sv files. There are two ways to solve this issue.
First, you can make main part of project non-templated and requre to run KaravaiSV once to generate necceserly ksv-templated files.
Second, preferred way, make project must always be generated via KaravaiSV and simply copy sv files to destination folder with rendered content or process them via KaravaiSV the same way it done with ksv-templated files. If sv files have no KSV syntax then they are not being affected.

CLI interface

KaravaiSV also provides simple CLI interface for the easiest debug purposes.
After instllation via pip it can be called directly from terminal via karavaisv or ksv commands.

Table with supported options for CLI program are shown below.

argument type required desription
input string Y path to an input file
output string Y path to an output file
parameters string N path to an parameters yaml file
logging N turns logging into terminal on

You can also get information about all supported parameters typing ksv --help in terminal.

Though, CLI interface is presented it does not allow to calculate any derived parameters or perform any other advanced manipulations, so it is strictly recommended to use it only for debug porposes.

Passing parameters to KSV engine

Honorable mention on KaravaiSV parameters passing logic.

When passing to render functions dictionary, the key-word values of that dictionary will became variables and their values at rendering process. This was made to avoid repeating of parameters['key'] code.

Feel free to see the example any time you struggling with something.

What it can do with code?

KaravaiSV gives you ability to template basicaly everything in your code.
Only your imagination can limit you.

Supported syntax

As for right now, KaravaiSV support two types of templating - inline and sectional.

Inline templating

Inline templating means that everywhere in code can be used special construction that replaces templated part with needed text.

KaravaiSV provides folowing syntax for inline templates: </ template_code />.

All the code before </ and after /> is not going to be affected. All the code inside is being interprented as Python code and it will be executed. As this is inline template it is going to be replaved with the result of executed content converted to string. Inline border symbols </ and /> is about to be removed.

Do not use inline templates to perform any calculations! They are only used to put templated strings in code.

Let's take a look at simple example.

Let's pretend we have a following code and int parameter data_width_max equals to 32:

logic [</ data_width_max /> - 1:0] data_reg;

After KSV rendering the following code will appear:

logic [32 - 1:0] data_reg;

No explicit cast from python int to str is needed as this done automatically inside KSV rendering engine.

Let's take a look at another more complex example.

It's a common practice to render single module multiple times with different configurations based on where they should be used. The usage place defined by module name prefix - parent module name.
Here's the example how to make this work with KaravaiSV.

Source code:

module </ parent_module_name + '_' if parent_module_name else '' />reducing_fifo (

Rendered code (parent_module_name = ''):

module reducing_fifo (

Rendered code (parent_module_name = 'input_buffer'):

module input_buffer_reducing_fifo (

This examples is a tiny bit of what inline templating gives opportunity to do.

More interesting example at Usage example chapter.

Sectional templating

Sectional templating means that are some section in code that can be rendered conditionally (if/elif/else) or multiple times (for/while).

KaravaiSV provides folowing syntax for sectional templates: <$ template_code $>.

The sectional template string must took all the line and should never be mixed with any other code within the single line! This resctriction is done because sectional templated string are not rendered itself rather then gives render engine an instruction of what to do with code around.

Currently, KaravaiSV supports following keywords for sectional templtating: if, elif, else, for, while, with, match, case, end.

Special keyword end was added as an equavalent of python reduce level of intentation.

Let's take a look at an example.
This example combines both inline and sectional templates.

Source code:

<$ for port in input_ports: $>  // data & valid inputs for each port

  input  logic [</ port['data_width'] /> - 1:0] </ port['name'] />_data_i,
  input  logic                                  </ port['name'] />_valid_i,
<$ end                      $>

Parameters:

input_ports:
  - name: "n1"
    data_width : 8
  - name: "n2"
    data_width : 16
  - name: "n3"
    data_width : 32

Rendered code:

  input  logic [8 - 1:0] n1_data_i,
  input  logic                                  n1_valid_i,

  input  logic [16 - 1:0] n2_data_i,
  input  logic                                  n2_valid_i,

  input  logic [32 - 1:0] n3_data_i,
  input  logic                                  n3_valid_i,

One more example.

Source code:

<$ if assertions: $>
// assertions

<$   for port in input_ports: $>
assert property (@(posedge clk) disable iff ~rstn !$is_unknown(</ port['name'] />_valid_i) ) else $error("</ port['name'] />_valid_i should be always known!");
<$   end                      $>
<$ end            $>

Parameters:

assertions: true
input_ports:
  - name: "n1"
    data_width : 8
  - name: "n2"
    data_width : 16
  - name: "n3"
    data_width : 32

Rendered code:

// assertions

assert property (@(posedge clk) disable iff ~rstn !$is_unknown(n1_valid_i) ) else $error("n1_valid_i should be always known!");
assert property (@(posedge clk) disable iff ~rstn !$is_unknown(n2_valid_i) ) else $error("n2_valid_i should be always known!");
assert property (@(posedge clk) disable iff ~rstn !$is_unknown(n3_valid_i) ) else $error("n3_valid_i should be always known!");

Usage with non-sv files

Even though KaravaiSV is called this way it can render not only SystemVerilog.

Simplest example is to render filelist file depending on modules usage.

Example:

<...>
build/rtl/rv_extensions/compr_instructions_parser.sv
<$ if (atomic_support): $>
build/rtl/rv_extensions/amo_microinstructions_ctrl.sv
<$ end                  $>
build/rtl/rv_extensions/mdu_unit.sv
<...>

Turn your creativity on and use KaravaiSV as a templating tool for the whole project!

Little additions

Comments

Though, it is possible in both types of templates it is strictly recommended not to use python comments as they makes code more dirty.

Preferred way to make comment is to make SystemVerilog style comments on sectional templating lines. This comments are not being rendered. This fact is shown in one of the above examples, comment // data & valid inputs for each port is never been rendered.

File formats

It is strongly recommended to give an ksv-templated SystemVerilog files a .ksv extensions to explicitly mark them as KaravaiSV files and make navigation in repo easier.
Same recomendation works for Verilog files which are recommended to mark with .kv extension.

However such decision likely would break code highlighning.
KaravaiSV templating engine can process files of any extensions, so you are free to choose any variant you want.

Code style

KaravaiSV suggest to use following codestyle to ensure that code is easily readable.

Firstly. Sectional templating borders allign

Allign sectional templating borders to make easily visible where every section of code begins and ends.

Example:

<...>
<$ if (atomics_support): $>
<...>
<$ end                   $>
<...>

Secondly. Nested sectional templating borders indent

Add extra identation for the righ border of nested templating borders, even if it leads to extra spaces.
This would be much more easy to find nested constructions.

Example:

<...>
<$ if (len(afb_ports) > 0 and !external_afb): $>
<$   for (port in afb_ports):                     $>
<...>
<$     if (port['output_async_fifo']):              $>
<...>
<$     end                                          $>
<$   end                                          $>
<$ end                                        $>
<...>

There's no strict rule on how much spaces needed for each identation block, but KaravaiSV recommends using 2 or 4 spaces.

Addition information

This is a small project made by single humanoid. It can contain mistakes, bugs, errors, bloops and many more.
This project was made for personal purposes for pet-projects but it seems so useful that I decided to give it a way into a bigger world.
Feel free to give your suggestions and ideas on any improvments that will make this project better.

It also would be great if your projects will use KaravaiSV for advanced templating.
I personally think that using such tool could give small project much more using scenarios thanks to new level parametrization.

Issues

If you fould any issues related to this project, please create an issue on GitHub.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

karavaisv-0.1.0.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

karavaisv-0.1.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file karavaisv-0.1.0.tar.gz.

File metadata

  • Download URL: karavaisv-0.1.0.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for karavaisv-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c803738a33a1ae014b63876f529da04d4d8916b9aacbb81c4250c573904acd02
MD5 7b5d7e1d0fe423108c0180ecfc8a5637
BLAKE2b-256 9eee8ea224490e4d19023a28724f6e977ccf65b552ef401fb247b42c418fb4ce

See more details on using hashes here.

File details

Details for the file karavaisv-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: karavaisv-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for karavaisv-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 921aa91f3258f2100ba29f39157374829ccb08565d12be927fe0584060c745cc
MD5 4284a74980b14713695d268478a2c594
BLAKE2b-256 0deff530d7d711b2047346a1bd403bef7b32261f963cb07d829a35f63cb55c44

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page