Initial commit of SRDL2SV

The compiler in this commit is still useless and only contains a
very rough skeleton of the code. SRDL2SV is only able to
create a simple register with hw=rw/sw=rw fields.
This commit is contained in:
Dennis Potter 2021-05-02 00:58:43 +02:00
commit 861a020aff
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
12 changed files with 551 additions and 0 deletions

141
.gitignore vendored Normal file
View File

@ -0,0 +1,141 @@
# Grabbed from https://github.com/github/gitignore/blob/master/Python.gitignore on 05/02/2021
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# 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
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

2
LIMITATIONS.md Normal file
View File

@ -0,0 +1,2 @@
- Depth of an array is limited to X
- [Any limitations to the systemrdl-compiler](https://systemrdl-compiler.readthedocs.io/en/latest/known_issues.html) also apply to the SystemRDL2SystemVerilog compiler.

0
srdl2sv/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,48 @@
import yaml
import re
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node
from systemrdl.node import FieldNode
# Local packages
from components.register import Register
from . import templates
# Import templates
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 `importlib_resources`.
import importlib_resources as pkg_resources
class AddrMap:
def __init__(self, rdlc: RDLCompiler, obj: node.RootNode):
self.rdlc = rdlc
template = pkg_resources.read_text(templates, 'addrmap.sv')
# Read template for SystemVerilog module
tmpl_addrmap = re.compile("{addrmap_name}")
self.rtl = tmpl_addrmap.sub(obj.inst_name, template)
# Empty list of register logic
self.registers = set()
# Traverse through children
for child in obj.children():
if isinstance(child, node.AddrmapNode):
pass
elif isinstance(child, node.RegfileNode):
pass
elif isinstance(child, node.RegNode):
self.registers.add(Register(child))
for i in self.registers:
print("\n\n")
for j in i.rtl:
print(j)
def get_rtl(self) -> str:
return '\n'.join(self.rtl)

183
srdl2sv/components/field.py Normal file
View File

@ -0,0 +1,183 @@
import yaml
import math
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node
from systemrdl.node import FieldNode
from systemrdl.rdltypes import PrecedenceType, AccessType
TAB = " "
class Field:
# Save YAML template as class variable
with open('srdl2sv/components/templates/fields.yaml', 'r') as file:
templ_dict = yaml.load(file, Loader=yaml.FullLoader)
def __init__(self, obj: node.RootNode, indent_lvl: int, dimensions: int):
self.obj = obj
self.rtl = []
self.bytes = math.ceil(obj.width / 8)
##################################################################################
# LIMITATION:
# v1.x of the systemrdl-compiler does not support non-homogeneous arrays.
# It is planned, however, for v2.0.0 of the compiler. More information
# can be found here: https://github.com/SystemRDL/systemrdl-compiler/issues/51
##################################################################################
# Determine resets. This includes checking for async/sync resets,
# the reset value, and whether the field actually has a reset
try:
rst_signal = obj.get_property("resetsignal")
rst_name = rst_signal.inst_name
rst_async = rst_signal.get_property("async")
rst_type = "asynchronous" if rst_async else "synchronous"
# Active low or active high?
if rst_signal.get_property("activelow"):
rst_edge = "negedge"
rst_negl = "!"
rst_active = "active_low"
else:
rst_edge = "posedge"
rst_negl = ""
rst_active = "active_high"
print(obj.get_property('reset'))
# Value of reset?
rst_value = '\'x' if obj.get_property("reset") == None else obj.get_property('reset')
except:
rst_async = False
rst_name = None
rst_negl = None
rst_edge = None
rst_value = "'x"
rst_active = "-"
rst_type = "-"
# Get certain properties
hw_access = obj.get_property('hw')
sw_access = obj.get_property('sw')
precedence = obj.get_property('precedence')
# Add comment with summary on field's properties
self.rtl.append(
Field.templ_dict['field_comment'].format(
name = obj.inst_name,
hw_access = str(hw_access)[11:],
sw_access = str(sw_access)[11:],
hw_precedence = '(precedence)' if precedence == PrecedenceType.hw else '',
sw_precedence = '(precedence)' if precedence == PrecedenceType.sw else '',
rst_active = rst_active,
rst_type = rst_type,
indent = self.indent(indent_lvl)))
# Handle always_ff
sense_list = 'sense_list_rst' if rst_async else 'sense_list_no_rst'
self.rtl.append(
Field.templ_dict[sense_list].format(
clk_name = "clk",
rst_edge = rst_edge,
rst_name = rst_name,
indent = self.indent(indent_lvl)))
# Calculate how many genvars shall be added
genvars = ['[{}]'.format(chr(97+i)) for i in range(dimensions)]
genvars_str = ''.join(genvars)
# Add actual reset line
if rst_name:
indent_lvl += 1
self.rtl.append(
Field.templ_dict['rst_field_assign'].format(
field_name = obj.inst_name,
rst_name = rst_name,
rst_negl = rst_negl,
rst_value = rst_value,
genvars = genvars_str,
indent = self.indent(indent_lvl)))
self.rtl.append("{}begin".format(self.indent(indent_lvl)))
indent_lvl += 1
# Define hardware access (if applicable)
hw_access_rtl = []
if hw_access == AccessType.rw or hw_access == AccessType.w:
if obj.get_property('we') or obj.get_property('wel'):
hw_access_rtl.append(
Field.templ_dict['hw_access_we_wel'].format(
negl = '!' if obj.get_property('wel') else '',
reg_name = obj.parent.inst_name,
field_name = obj.inst_name,
genvars = genvars_str,
indent = self.indent(indent_lvl)))
hw_access_rtl.append(
Field.templ_dict['hw_access_field'].format(
reg_name = obj.parent.inst_name,
field_name = obj.inst_name,
genvars = genvars_str,
indent = self.indent(indent_lvl)))
# Define software access (if applicable)
sw_access_rtl = []
# TODO: if sw_access_enabled
sw_access_rtl.append(
Field.templ_dict['sw_access_field'].format(
reg_name = obj.parent.inst_name,
field_name = obj.inst_name,
genvars = genvars_str,
indent = self.indent(indent_lvl)))
indent_lvl += 1
# If field spans multiple bytes, every byte shall have a seperate enable!
for i in range(self.bytes):
sw_access_rtl.append(
Field.templ_dict['sw_access_byte'].format(
reg_name = obj.parent.inst_name,
field_name = obj.inst_name,
genvars = genvars_str,
i = i,
indent = self.indent(indent_lvl)))
sw_access_rtl.append("")
indent_lvl -= 1
sw_access_rtl.append("{}end".format(self.indent(indent_lvl)))
# Check if hardware has precedence (default `precedence = sw`)
if precedence == 'PrecedenceType.sw':
self.rtl = [*self.rtl,
*sw_access_rtl,
'{}else'.format(self.indent(indent_lvl)),
*hw_access_rtl]
else:
self.rtl = [*self.rtl,
*sw_access_rtl,
'{}else'.format(self.indent(indent_lvl)),
*hw_access_rtl]
indent_lvl -= 1
self.rtl.append(
Field.templ_dict['end_field_ff'].format(
reg_name = obj.parent.inst_name,
field_name = obj.inst_name,
indent = self.indent(indent_lvl)))
@staticmethod
def indent(level):
return TAB*level
def get_rtl(self) -> str:
return '\n'.join(self.rtl)

View File

@ -0,0 +1,81 @@
import yaml
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node
from systemrdl.node import FieldNode
from components.field import Field
TAB = " "
class Register:
# Save YAML template as class variable
with open('srdl2sv/components/templates/regs.yaml', 'r') as file:
templ_dict = yaml.load(file, Loader=yaml.FullLoader)
def __init__(self, obj: node.RootNode):
self.obj = obj
self.name = obj.inst_name
self.rtl = []
if obj.is_array:
sel_arr = 'array'
array_dimensions = obj.array_dimensions
else:
sel_arr = 'single'
array_dimensions = [1]
depth = '[{}]'.format(']['.join(f"{i}" for i in array_dimensions))
dimensions = len(array_dimensions)
indent_lvl = 0
# Create comment and provide user information about register he/she
# is looking at.
self.rtl.append(
Register.templ_dict['reg_comment'].format(
name = obj.inst_name,
dimensions = dimensions,
depth = depth))
# Create wires every register
self.rtl.append(
Register.templ_dict['rw_wire_declare'].format(
name = obj.inst_name,
depth = depth))
# Create generate block for register and add comment
self.rtl.append("generate")
for i in range(dimensions):
self.rtl.append(
Register.templ_dict['generate_for_start'].format(
iterator = chr(97+i),
limit = array_dimensions[i],
indent = self.indent(i)))
indent_lvl = i
indent_lvl += 1
# Create RTL for fields
# Fields should be in order in RTL,therefore, use list
self.fields = []
for field in obj.fields():
field_obj = Field(field, indent_lvl, dimensions)
self.fields.append(field_obj)
self.rtl += field_obj.rtl
# End loops
for i in range(dimensions-1, -1, -1):
self.rtl.append(
Register.templ_dict['generate_for_end'].format(
dimension = chr(97+i),
indent = self.indent(i)))
@staticmethod
def indent(level):
return TAB*level
def get_rtl(self) -> str:
return '\n'.join(self.rtl)

View File

View File

@ -0,0 +1,9 @@
module {addrmap_name} (
{bus_io}
{io_list}
);
{bus_widget}
{registers}
endmodule

View File

@ -0,0 +1,31 @@
---
sense_list_rst: |-
{indent}always_ff @(posedge {clk_name} or {rst_edge} {rst_name})
sense_list_no_rst: |-
{indent}always_ff @(posedge {clk_name})
rst_field_assign: |-
{indent}if ({rst_negl}{rst_name})
{indent} {field_name}_q{genvars} <= {rst_value};
{indent}else
sw_access_field: |-
{indent}if ({reg_name}_{field_name}_sw_wr{genvars})
{indent}begin
sw_access_byte: |-
{indent}if (byte_enable[{i}])
{indent} {reg_name}_{field_name}_q{genvars}[8*({i}+1)-1 -: 8] <= sw_wr_bus[8*({i}+1)-1 -: 8];
hw_access_we_wel: |-
{indent}if ({negl}{reg_name}_{field_name}_hw_wr{genvars})
hw_access_field: |-
{indent}begin
{indent} {reg_name}_{field_name}_q{genvars} <= {reg_name}_{field_name}_in{genvars};
{indent}end
end_field_ff: |-
{indent}end // of {reg_name}_{field_name}'s always_ff
field_comment: |-
{indent}//-----------------FIELD SUMMARY-----------------
{indent}// name : {name}
{indent}// access : hw = {hw_access} {hw_precedence}
{indent}// sw = {sw_access} {sw_precedence}
{indent}// reset : {rst_active} / {rst_type}
{indent}//-----------------------------------------------

View File

@ -0,0 +1,20 @@
---
rw_wire_declare: |
logic {name}_wr {depth};
logic {name}_rd {depth};
rw_wire_assign: |
assign {name}_bus_wr[i] = addr == {} && r_vld;
assign {name}_bus_wr[i] = addr == {} && r_vld;
reg_comment: |-
/*******************************************************************
*******************************************************************
* REGISTER : {name}
* DIMENSION : {dimensions}
* DEPTHS (per dimension): {depth}
*******************************************************************
*******************************************************************/
generate_for_start: |-
{indent}for ({iterator} = 0; {iterator} < {limit}; {iterator}++)
{indent}begin
generate_for_end: |-
{indent}end // of for loop with iterator {dimension}

36
srdl2sv/main.py Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import sys
import os
import re
import time
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node
from systemrdl.node import FieldNode
from components.addrmap import AddrMap
if __name__ == "__main__":
# Take start timestamp
start = time.time()
# Compile and elaborate files provided from the command line
input_files = sys.argv[1:]
rdlc = RDLCompiler()
try:
for input_file in input_files:
rdlc.compile_file(input_file)
root = rdlc.elaborate()
except RDLCompileError:
sys.exit(1)
addrmap = AddrMap(rdlc, root.top)
print("====================================================")
print("Elapsed time: {} seconds".format(time.time() - start))
print("====================================================")