Closes #8: Certain fields shall be implemented as wires or constants

The software now detects whether a field shall be implemented with
flops, with wires, or as a constant. Everything should now follow Table
12 and Section 9.5.1 of the SystemRDL 2.0 LRM.
This commit is contained in:
Dennis Potter 2021-10-30 19:33:14 -07:00
parent 5a00d48c34
commit e46e51f3cf
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
2 changed files with 114 additions and 26 deletions

View File

@ -3,6 +3,7 @@ import math
import importlib.resources as pkg_resources import importlib.resources as pkg_resources
import sys import sys
from typing import Optional from typing import Optional
from enum import Enum
import yaml import yaml
from systemrdl.node import FieldNode, SignalNode from systemrdl.node import FieldNode, SignalNode
@ -13,6 +14,11 @@ from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteTy
from srdl2sv.components.component import Component, TypeDef from srdl2sv.components.component import Component, TypeDef
from srdl2sv.components import templates from srdl2sv.components import templates
class StorageType(Enum):
FLOPS = 0
WIRE = 1
CONST = 2
class Field(Component): class Field(Component):
# Save YAML template as class variable # Save YAML template as class variable
templ_dict = yaml.load( templ_dict = yaml.load(
@ -36,6 +42,9 @@ class Field(Component):
# Save and/or process important variables # Save and/or process important variables
self.__init_variables(obj) self.__init_variables(obj)
# Determine whether it is a wire, flops, or a wire
self.__init_storage_type()
# Determine field types # Determine field types
self.__init_fieldtype() self.__init_fieldtype()
@ -54,12 +63,19 @@ class Field(Component):
# HW Access can be handled in __init__ function but SW access # HW Access can be handled in __init__ function but SW access
# must be handled in a seperate method that can be called # must be handled in a seperate method that can be called
# seperately in case of alias registers # seperately in case of alias registers
if not self.config['external']: if self.config['external']:
pass
elif self.storage_type is not StorageType.FLOPS:
self.__add_wire_const()
self.__add_hw_rd_access()
self.add_sw_access(obj)
else:
self.__add_always_ff() self.__add_always_ff()
# Only add normal hardware access if field is not an interrupt field # Only add normal hardware access if field is not an interrupt field
if not self.__add_interrupt(): if not self.__add_interrupt():
self.__add_hw_access() self.__add_hw_wr_access()
self.__add_hw_rd_access()
self.__add_combo() self.__add_combo()
self.__add_swmod_swacc() self.__add_swmod_swacc()
@ -871,7 +887,23 @@ class Field(Component):
return self.properties['intr'] return self.properties['intr']
def __add_hw_access(self): def __add_wire_const(self):
field_templ = 'hw_wire' if self.storage_type is StorageType.WIRE else 'hw_const'
self.access_rtl['hw_write'] = ([
self._process_yaml(
Field.templ_dict[field_templ],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'field_type': self.field_type,
'width': self.obj.width,
'const': self.rst['value'],
}
)
],
True)
def __add_hw_wr_access(self):
# Mutually exclusive. systemrdl-compiler performs check for this # Mutually exclusive. systemrdl-compiler performs check for this
enable_mask_negl = '' enable_mask_negl = ''
enable_mask = False enable_mask = False
@ -1012,6 +1044,7 @@ class Field(Component):
else: else:
self.access_rtl['hw_setclr'] = ([], False) self.access_rtl['hw_setclr'] = ([], False)
def __add_hw_rd_access(self):
# Hookup flop to output port in case register is readable by hardware # Hookup flop to output port in case register is readable by hardware
if self.obj.get_property('hw') in (AccessType.rw, AccessType.r): if self.obj.get_property('hw') in (AccessType.rw, AccessType.r):
# Connect flops to output port # Connect flops to output port
@ -1131,6 +1164,7 @@ class Field(Component):
# Chain access RTL to the rest of the RTL # Chain access RTL to the rest of the RTL
self.rtl_header = [*self.rtl_header, *order_list_rtl] self.rtl_header = [*self.rtl_header, *order_list_rtl]
if self.storage_type is StorageType.FLOPS:
self.rtl_header.append( self.rtl_header.append(
self._process_yaml( self._process_yaml(
Field.templ_dict['end_field_ff'], Field.templ_dict['end_field_ff'],
@ -1267,17 +1301,12 @@ class Field(Component):
if self.rst['name']: if self.rst['name']:
self.resets.add(self.rst['name']) self.resets.add(self.rst['name'])
elif obj.get_property("reset") is not None:
self.logger.warning("Field has a reset value, but no reset "\
"signal was defined and connected to the "\
"field. Note that explicit connecting this "\
"is not required if a field_reset was defined.")
# Value of reset must always be determined on field level # Value of reset must always be determined on field level
# Don't use 'not obj.get_property("reset"), since the value # Don't use 'not obj.get_property("reset"), since the value
# could (and will often be) be '0' # could (and will often be) be '0'
self.rst['value'] = \ self.rst['value'] = \
'\'x' if obj.get_property("reset") == None else\ 'x' if obj.get_property("reset") is None else\
obj.get_property('reset') obj.get_property('reset')
# Define dict that holds all RTL # Define dict that holds all RTL
@ -1285,6 +1314,30 @@ class Field(Component):
self.access_rtl['else'] = (["else"], False) self.access_rtl['else'] = (["else"], False)
self.access_rtl[''] = ([''], False) self.access_rtl[''] = ([''], False)
def __init_storage_type(self):
# It is not required to check for illegal conditions because the
# compiler will take care of this
hw_prop = self.obj.get_property('hw')
sw_prop = self.obj.get_property('sw')
# Check the storage type, according to Table 12 of the SystemRDL 2.0 LRM
if hw_prop is AccessType.r and sw_prop is AccessType.r:
# hw=r/sw=r --> Constant
self.storage_type = StorageType.CONST
elif hw_prop is AccessType.na and sw_prop is AccessType.r:
# hw=na/sw=r --> Constant
self.storage_type = StorageType.CONST
elif hw_prop is AccessType.w and sw_prop is AccessType.r \
and self.obj.get_property("reset") is None \
and not self.we_or_wel:
# If hw=w/sw=r AND no reset or we/wel is defined, a simple wire is implemented.
# This isn't clear from Table 12, but '9.5.1 Semantics' describes this
self.storage_type = StorageType.WIRE
else:
self.storage_type = StorageType.FLOPS
self.logger.debug("Storage type of field detected as '%s'", self.storage_type)
def __summary(self): def __summary(self):
# Additional flags that are set # Additional flags that are set
# Use list, rather than set, to ensure the order stays the same # Use list, rather than set, to ensure the order stays the same
@ -1315,7 +1368,9 @@ class Field(Component):
external = self.config['external'], external = self.config['external'],
lsb = self.obj.lsb, lsb = self.obj.lsb,
msb = self.obj.msb, msb = self.obj.msb,
path_wo_field = self.path_wo_field) path_wo_field = self.path_wo_field,
storage_type = self.storage_type,
)
def __add_always_ff(self): def __add_always_ff(self):
# Handle always_ff # Handle always_ff
@ -1340,7 +1395,9 @@ class Field(Component):
'rst_negl': "!" if self.rst['active'] == "active_low" else "", 'rst_negl': "!" if self.rst['active'] == "active_low" else "",
'rst_value': self.rst['value'], 'rst_value': self.rst['value'],
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'field_type': self.field_type} 'field_type': self.field_type,
'width': self.obj.width,
}
) )
) )
@ -1374,6 +1431,17 @@ class Field(Component):
"property with the counter property. The counter property "\ "property with the counter property. The counter property "\
"will be ignored.") "will be ignored.")
# If there a reset value is defined but no reset value, throw a warning
# This is not true in case of a constant
if not self.rst['name'] \
and self.obj.get_property("reset") is not None \
and self.storage_type is StorageType.FLOPS:
self.logger.warning("Field has a reset value, but no reset "\
"signal was defined and connected to the "\
"field. Note that explicit connecting this "\
"is not required if a field_reset was defined.")
@staticmethod @staticmethod
def __process_reset_signal(reset_signal): def __process_reset_signal(reset_signal):
rst = {} rst = {}
@ -1395,7 +1463,7 @@ class Field(Component):
rst['async'] = False rst['async'] = False
rst['name'] = None rst['name'] = None
rst['edge'] = None rst['edge'] = None
rst['value'] = "'x" rst['value'] = "x"
rst['active'] = "-" rst['active'] = "-"
rst['type'] = "-" rst['type'] = "-"

View File

@ -9,7 +9,7 @@ rst_field_assign:
rtl: |- rtl: |-
if ({rst_negl}{rst_name}) if ({rst_negl}{rst_name})
begin begin
{path}_q{genvars} <= {rst_value}; {path}_q{genvars} <= {width}'d{rst_value};
end end
else else
signals: signals:
@ -108,6 +108,25 @@ hw_access_counter:
signal_type: 'logic' signal_type: 'logic'
- name: '{path}_next' - name: '{path}_next'
signal_type: '{field_type}' signal_type: '{field_type}'
hw_const:
rtl: |-
// Field is defined as a constant.
assign {path}_q{genvars} = {width}'d{const};
signals:
- name: '{path}_q'
signal_type: '{field_type}'
hw_wire:
rtl: |-
// Field is a simple wire.
// To generate a flop either add the we/wel property, add
// a reset, or change the sw/hw access properties
assign {path}_q{genvars} = {path}_in{genvars};
signals:
- name: '{path}_q'
signal_type: '{field_type}'
input_ports:
- name: '{path}_in'
signal_type: '{field_type}'
end_field_ff: end_field_ff:
rtl: |- rtl: |-
end // of {path}'s always_ff end // of {path}'s always_ff
@ -170,6 +189,7 @@ field_comment:
// reset : {rst_active} / {rst_type} // reset : {rst_active} / {rst_type}
// flags : {misc_flags} // flags : {misc_flags}
// external : {external} // external : {external}
// storage type : {storage_type}
//----------------------------------------------- //-----------------------------------------------
field_desc: field_desc:
rtl: |- rtl: |-