Add initial version of widget-code and fix remaining SV compiler errors

This adds initial support for a dynamic bus-protocol to internal
register logic SHIM. The chosen default protocol at this point is AMBA 3
AHB Lite and the logic is still empty.

    -> TODO: Adding the widget instantiation showed that it is required
    to have a better interface to parametrize ports & signals in the
    YAML. At this point, only a limited set of variables are supported.

Furthermore, all remaining Verilator compilation issues in the field are
resolved. Those were mostly related to non-declared wires and wrongly named
wires.
This commit is contained in:
Dennis Potter 2021-06-05 15:37:09 +02:00
parent 21abdefac0
commit 9deb28ce4e
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
10 changed files with 216 additions and 43 deletions

View File

@ -19,6 +19,14 @@ class CliArguments():
self.parser = argparse.ArgumentParser( self.parser = argparse.ArgumentParser(
description="SystemRDL 2 SystemVerilog compiler") description="SystemRDL 2 SystemVerilog compiler")
self.parser.add_argument(
"-b",
"--bus",
choices=['amba3ahblite'],
default='amba3ahblite',
help="Set the bus protocol that shall be used by software to ',\
communicate with the registers. (default: %(default)s)")
self.parser.add_argument( self.parser.add_argument(
"-o", "-o",
"--out_dir", "--out_dir",
@ -129,4 +137,7 @@ class CliArguments():
# Set enums # Set enums
config['enums'] = not args.disable_enums config['enums'] = not args.disable_enums
# Set bus
config['bus'] = args.bus
return config return config

View File

@ -11,6 +11,7 @@ from components.component import Component
from components.regfile import RegFile from components.regfile import RegFile
from components.register import Register from components.register import Register
from . import templates from . import templates
from . import widgets
class AddrMap(Component): class AddrMap(Component):
@ -32,6 +33,10 @@ class AddrMap(Component):
(glbl_settings['field_reset'], glbl_settings['cpuif_reset']) = \ (glbl_settings['field_reset'], glbl_settings['cpuif_reset']) = \
self.__process_global_resets() self.__process_global_resets()
# Set defaults so that some of the common component methods work
self.total_dimensions = 0
self.total_array_dimensions = []
# Use global settings to define whether a component is already in a generate block # Use global settings to define whether a component is already in a generate block
glbl_settings['generate_active'] = False glbl_settings['generate_active'] = False
@ -69,6 +74,9 @@ class AddrMap(Component):
self.logger.info("Done generating all child-regfiles/registers") self.logger.info("Done generating all child-regfiles/registers")
# Add bus widget ports
self.__add_bus_widget_ports()
# Start assembling addrmap module # Start assembling addrmap module
self.logger.info("Starting to assemble input & output ports") self.logger.info("Starting to assemble input & output ports")
@ -147,18 +155,61 @@ class AddrMap(Component):
inputs = '\n'.join(input_ports_rtl), inputs = '\n'.join(input_ports_rtl),
outputs = '\n'.join(output_ports_rtl))) outputs = '\n'.join(output_ports_rtl)))
# Add wire/register instantiations
self.__add_signal_instantiation()
# Add bus widget RTL
self.__add_bus_widget_instantiation()
# Append genvars # Append genvars
self.__append_genvars()
# Add endmodule keyword
self.rtl_footer.append('endmodule')
def __add_signal_instantiation(self):
dict_list = [(key, value) for (key, value) in self.get_signals(True).items()]
signal_width = min(max([len(value[0]) for (_, value) in dict_list]), 40)
name_width = min(max([len(key) for (key, _) in dict_list]), 40)
self.rtl_header = [
*self.rtl_header,
'',
'// Internal signals',
*[AddrMap.templ_dict['signal_declaration'].format(
name = key,
type = value[0],
signal_width = signal_width,
name_width = name_width,
unpacked_dim = '[{}]'.format(
']['.join(
[str(y) for y in value[1]]))
if value[1] else '')
for (key, value) in dict_list],
''
]
def __add_bus_widget_ports(self):
self.widget_templ_dict = yaml.load(
pkg_resources.read_text(widgets, '{}.yaml'.format(self.config['bus'])),
Loader=yaml.FullLoader)
self.yaml_signals_to_list(self.widget_templ_dict['module_instantiation'])
def __add_bus_widget_instantiation(self):
self.rtl_header.append(
self.widget_templ_dict['module_instantiation']['rtl'])
def __append_genvars(self):
genvars = ''.join([ genvars = ''.join([
'\ngenvar ', '\ngenvar ',
','.join([chr(97+i) for i in range(self.get_max_dim_depth())]), ', '.join([chr(97+i) for i in range(self.get_max_dim_depth())]),
';\n' ';\n'
]) ])
self.rtl_header.append(genvars) self.rtl_header.append(genvars)
# Add endmodule keyword
self.rtl_footer.append('endmodule')
def __process_global_resets(self): def __process_global_resets(self):
field_reset_list = \ field_reset_list = \
[x for x in self.obj.signals() if x.get_property('field_reset')] [x for x in self.obj.signals() if x.get_property('field_reset')]

View File

@ -66,23 +66,18 @@ class Component():
return self.ports[port_type] return self.ports[port_type]
def get_max_dim_depth(self) -> int: def get_max_dim_depth(self) -> int:
try:
total_dimensions = self.total_dimensions
total_array_dimensions = self.total_array_dimensions
except AttributeError:
total_dimensions = 0
total_array_dimensions = []
self.logger.debug("Return depth '{}' for dimensions (including "\ self.logger.debug("Return depth '{}' for dimensions (including "\
"parents) '{}'".format(total_dimensions, total_array_dimensions)) "parents) '{}'".format(self.total_dimensions,
self.total_array_dimensions))
return max([ return max([
total_dimensions, self.total_dimensions,
*[x.get_max_dim_depth() for x in self.children] *[x.get_max_dim_depth() for x in self.children]
]) ])
def get_signals(self): def get_signals(self, no_children = False):
self.logger.debug("Return signal list") self.logger.debug("Return signal list")
if not no_children:
for x in self.children: for x in self.children:
self.signals |= x.get_signals() self.signals |= x.get_signals()
@ -171,7 +166,7 @@ class Component():
return path\ return path\
.replace('[]', '')\ .replace('[]', '')\
.replace('{}.'.format(owning_addrmap), '')\ .replace('{}.'.format(owning_addrmap), '')\
.replace('.', '_') .replace('.', '__')
@staticmethod @staticmethod
def split_dimensions(path: str): def split_dimensions(path: str):
@ -179,8 +174,7 @@ class Component():
new_path = re_dimensions.sub('', path) new_path = re_dimensions.sub('', path)
return (new_path, ''.join(re_dimensions.findall(path))) return (new_path, ''.join(re_dimensions.findall(path)))
@staticmethod def get_signal_name(self, obj):
def get_signal_name(obj):
name = [] name = []
try: try:
@ -199,9 +193,11 @@ class Component():
if isinstance(obj, node.FieldNode): if isinstance(obj, node.FieldNode):
name.append('_q') name.append('_q')
elif isinstance(obj, node.SignalNode): elif isinstance(obj, node.SignalNode):
pass # Must add it to signal list
self.ports['input'][obj.inst_name] =\
("logic" if obj.width == 1 else 'logic [{}:0]'.format(obj.width), [])
else: else:
name.append('_') name.append('__')
name.append(obj.name) name.append(obj.name)
name.append(split_name[1]) name.append(split_name[1])

View File

@ -133,10 +133,7 @@ class Field(Component):
except AttributeError: except AttributeError:
# In case of an AttributeError, the encode property is None. Hence, # In case of an AttributeError, the encode property is None. Hence,
# the field has a simple width # the field has a simple width
if self.obj.width > 1:
self.field_type = 'logic [{}:0]'.format(self.obj.width-1) self.field_type = 'logic [{}:0]'.format(self.obj.width-1)
else:
self.field_type = 'logic'
def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict): def __process_variables(self, obj: FieldNode, array_dimensions: list, glbl_settings: dict):
# Create full name # Create full name
@ -213,7 +210,6 @@ class Field(Component):
self.rtl_header.append( self.rtl_header.append(
Field.templ_dict[sense_list]['rtl'].format( Field.templ_dict[sense_list]['rtl'].format(
clk_name = "clk",
rst_edge = self.rst['edge'], rst_edge = self.rst['edge'],
rst_name = self.rst['name'])) rst_name = self.rst['name']))
@ -252,25 +248,25 @@ class Field(Component):
# Define hardware access (if applicable) # Define hardware access (if applicable)
if self.hw_access in (AccessType.rw, AccessType.w): if self.hw_access in (AccessType.rw, AccessType.w):
if self.we_or_wel: write_condition = 'hw_access_we_wel' if self.we_or_wel else 'hw_access_no_we_wel'
# if-line of hw-access
access_rtl['hw_write'] = ([ access_rtl['hw_write'] = ([
Field.templ_dict['hw_access_we_wel']['rtl'].format( Field.templ_dict[write_condition]['rtl'].format(
negl = '!' if self.obj.get_property('wel') else '', negl = '!' if self.obj.get_property('wel') else '',
path = self.path_underscored, path = self.path_underscored,
genvars = self.genvars_str) genvars = self.genvars_str)
], ],
False) write_condition == 'hw_access_no_we_wel') # Abort if no condition is set
else:
access_rtl['hw_write'] = ([
Field.templ_dict['hw_access_no_we_wel']['rtl']
],
True)
# Actual assignment of register
access_rtl['hw_write'][0].append( access_rtl['hw_write'][0].append(
Field.templ_dict['hw_access_field']['rtl'].format( Field.templ_dict['hw_access_field']['rtl'].format(
path = self.path_underscored, path = self.path_underscored,
genvars = self.genvars_str)) genvars = self.genvars_str))
# Get ports/signals from list
self.yaml_signals_to_list(Field.templ_dict[write_condition])
self.yaml_signals_to_list(Field.templ_dict['hw_access_field']) self.yaml_signals_to_list(Field.templ_dict['hw_access_field'])
else: else:
access_rtl['hw_write'] = ([], False) access_rtl['hw_write'] = ([], False)
@ -287,13 +283,13 @@ class Field(Component):
Field.templ_dict['sw_access_field_swwe']['rtl'].format( Field.templ_dict['sw_access_field_swwe']['rtl'].format(
path_wo_field = self.path_wo_field, path_wo_field = self.path_wo_field,
genvars = self.genvars_str, genvars = self.genvars_str,
swwe = Component.get_signal_name(swwe))) swwe = self.get_signal_name(swwe)))
elif isinstance(swwel, (FieldNode, SignalNode)): elif isinstance(swwel, (FieldNode, SignalNode)):
access_rtl['sw_write'][0].append( access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_field_swwel']['rtl'].format( Field.templ_dict['sw_access_field_swwel']['rtl'].format(
path_wo_field = self.path_wo_field, path_wo_field = self.path_wo_field,
genvars = self.genvars_str, genvars = self.genvars_str,
swwel = Component.get_signal_name(swwel))) swwel = self.get_signal_name(swwel)))
else: else:
access_rtl['sw_write'][0].append( access_rtl['sw_write'][0].append(
Field.templ_dict['sw_access_field']['rtl'].format( Field.templ_dict['sw_access_field']['rtl'].format(

View File

@ -7,6 +7,7 @@ module_declaration:
// Clock & Resets // Clock & Resets
input reg_clk, input reg_clk,
input bus_clk, input bus_clk,
input bus_rst_n,
{resets} {resets}
// Bus I/O // Bus I/O
@ -30,6 +31,8 @@ input_port:
output_port: output_port:
rtl: rtl:
output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim}, output {signal_type:{signal_width}} {name:{name_width}}{unpacked_dim},
signal_declaration: |-
{type:{signal_width}} {name:{name_width}}{unpacked_dim};
package_declaration: package_declaration:
rtl: |- rtl: |-
package {name}_pkg; package {name}_pkg;

View File

@ -1,10 +1,10 @@
--- ---
sense_list_rst: sense_list_rst:
rtl: |- rtl: |-
always_ff @(posedge {clk_name} or {rst_edge} {rst_name}) always_ff @(posedge reg_clk or {rst_edge} {rst_name})
sense_list_no_rst: sense_list_no_rst:
rtl: |- rtl: |-
always_ff @(posedge {clk_name}) always_ff @(posedge reg_clk)
rst_field_assign: rst_field_assign:
rtl: |- rtl: |-
if ({rst_negl}{rst_name}) if ({rst_negl}{rst_name})
@ -37,6 +37,9 @@ sw_access_byte:
hw_access_we_wel: hw_access_we_wel:
rtl: |- rtl: |-
if ({negl}{path}_hw_wr{genvars}) if ({negl}{path}_hw_wr{genvars})
input_ports:
- name: '{path}_hw_wr'
signal_type: '{field_type}'
hw_access_no_we_wel: hw_access_no_we_wel:
rtl: |- rtl: |-
// we or wel property not set // we or wel property not set

View File

View File

@ -0,0 +1,31 @@
module amba3ahblite_widget
(
// Register clock
input reg_clk,
// Outputs to internal logic
output [31:0] addr,
output w_vld,
output r_vld,
output [ 3:0] byte_enable,
output [31:0] sw_wr_bus,
// Bus protocol
input HRESETn,
input HCLK,
input [31:0] HADDR,
input HWRITE,
input [ 2:0] HSIZE,
input [ 2:0] HBURST,
input [ 3:0] HPROT,
input [ 1:0] HTRANS,
input HMASTLOCK,
input HREADY,
output HREADYOUT,
output HRESP,
output [31:0] HRDATA
);
endmodule

View File

@ -0,0 +1,72 @@
# This file only contains the instantiation of the module
module_instantiation:
rtl: |-
/****************************
* AMBA 3 AHB Lite Widget
****************************/
amba3ahblite_widget
amba3ahblite_widget_inst
(// Register clock
.reg_clk,
// Outputs to internal logic
.addr,
.w_vld,
.r_vld,
.byte_enable,
.sw_wr_bus,
// Bus protocol
.HRESETn,
.HCLK,
.HADDR,
.HWRITE,
.HSIZE,
.HBURST,
.HPROT,
.HTRANS,
.HMASTLOCK,
.HREADY,
.HREADYOUT,
.HRESP,
.HRDATA);
signals:
- name: 'addr'
signal_type: 'logic [31:0]'
- name: 'w_vld'
signal_type: 'logic'
- name: 'r_vld'
signal_type: 'logic'
- name: 'byte_enable'
signal_type: 'logic [ 3:0]'
- name: 'sw_wr_bus'
signal_type: 'logic [31:0]'
input_ports:
- name: 'HRESETn'
signal_type: ''
- name: 'HCLK'
signal_type: ''
- name: 'HADDR'
signal_type: '[31:0]'
- name: 'HWRITE'
signal_type: ''
- name: 'HSIZE'
signal_type: '[2:0]'
- name: 'HBURST'
signal_type: '[2:0]'
- name: 'HPROT'
signal_type: '[3:0]'
- name: 'HTRANS'
signal_type: '[1:0]'
- name: 'HMASTLOCK'
signal_type: ''
- name: 'HREADY'
signal_type: ''
output_ports:
- name: 'HREADYOUT'
signal_type: ''
- name: 'HRESP'
signal_type: ''
- name: 'HRDATA'
signal_type: '[31:0]'

View File

@ -4,6 +4,7 @@
import sys import sys
import time import time
import os import os
import importlib.resources as pkg_resources
# Imported modules # Imported modules
from systemrdl import RDLCompiler, RDLCompileError from systemrdl import RDLCompiler, RDLCompileError
@ -12,6 +13,7 @@ from systemrdl import RDLCompiler, RDLCompileError
from components.addrmap import AddrMap from components.addrmap import AddrMap
from cli.cli import CliArguments from cli.cli import CliArguments
from log.log import create_logger from log.log import create_logger
from components import widgets
if __name__ == "__main__": if __name__ == "__main__":
# Take start timestamp # Take start timestamp
@ -74,4 +76,12 @@ if __name__ == "__main__":
with open('{}/{}_pkg.sv'.format(config['output_dir'], key), 'w') as file: with open('{}/{}_pkg.sv'.format(config['output_dir'], key), 'w') as file:
file.write(value) file.write(value)
# Copy over widget RTL from widget directory
widget_rtl = pkg_resources.read_text(widgets, '{}.sv'.format(config['bus']))
out_widget_file = "{}/{}.sv".format(config['output_dir'], config['bus'])
with open(out_widget_file, 'w') as file:
file.write(widget_rtl)
logger.info("Elapsed time: %f seconds", time.time() - start) logger.info("Elapsed time: %f seconds", time.time() - start)