Add onread/onwrite properties to field class

This commit also created a seperate private method for access related
RTL and for the always_ff header.

Furthermore, a bug which caused the singlepulse property to always show
up was resolved.

Lastly, the summary method was made truely public. So, rather than
writing to the RTL list, it now returns a list and the calling
method/function can decide what to do with that list.
This commit is contained in:
Dennis Potter 2021-05-16 12:49:17 +02:00
parent 3acd7516d3
commit 92d61dd7c8
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
2 changed files with 233 additions and 126 deletions

View File

@ -4,10 +4,9 @@ import yaml
from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node from systemrdl import RDLCompiler, RDLCompileError, RDLWalker, RDLListener, node
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
from systemrdl.rdltypes import PrecedenceType, AccessType from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType
# Local modules # Local modules
from log.log import create_logger
from components.component import Component, Port from components.component import Component, Port
from . import templates from . import templates
@ -34,126 +33,11 @@ class Field(Component):
# can be found here: https://github.com/SystemRDL/systemrdl-compiler/issues/51 # can be found here: https://github.com/SystemRDL/systemrdl-compiler/issues/51
################################################################################## ##################################################################################
# Print a summary # Print a summary
self.summary() self.rtl_header.append(self.summary())
# Handle always_ff
sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst'
self.rtl_header.append(
Field.templ_dict[sense_list].format(
clk_name = "clk",
rst_edge = self.rst['edge'],
rst_name = self.rst['name']))
# Add actual reset line
if self.rst['name']:
self.rtl_header.append(
Field.templ_dict['rst_field_assign'].format(
path = self.path_underscored,
rst_name = self.rst['name'],
rst_negl = "!" if self.rst['active'] == "active_high" else "",
rst_value = self.rst['value'],
genvars = self.genvars_str))
self.rtl_header.append("begin")
# Not all access types are required and the order might differ
# depending on what types are defined and what precedence is
# set. Therefore, first add all RTL into a dictionary and
# later place it in the right order.
#
# The following RTL blocks are defined:
# - hw_write --> write access for the hardware interface
# - sw_write --> write access for the software interface
#
access_rtl = dict([])
# Define hardware access (if applicable)
access_rtl['hw_write'] = []
if self.hw_access in (AccessType.rw, AccessType.w):
if self.we_or_wel:
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_we_wel'].format(
negl = '!' if obj.get_property('wel') else '',
path = self.path_underscored,
genvars = self.genvars_str))
else:
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_no_we_wel'])
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_field'].format(
path = self.path_underscored,
genvars = self.genvars_str))
# Define software access (if applicable)
access_rtl['sw_write'] = []
if self.sw_access in (AccessType.rw, AccessType.w):
access_rtl['sw_write'].append(
Field.templ_dict['sw_access_field'].format(
path_wo_field = self.path_wo_field,
genvars = self.genvars_str))
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'].append(
Field.templ_dict['sw_access_byte'].format(
path = self.path_underscored,
genvars = self.genvars_str,
i = i,
msb_bus = str(8*(i+1)-1 if i != self.msbyte else self.obj.msb),
bus_w = str(8 if i != self.msbyte else self.obj.width-(8*j)),
msb_field = str(8*(j+1)-1 if i != self.msbyte else self.obj.width-1),
field_w = str(8 if i != self.msbyte else self.obj.width-(8*j))))
access_rtl['sw_write'].append("end")
# Add singlepulse property
access_rtl['singlepulse'] = [
Field.templ_dict['singlepulse'].format(
path = self.path_underscored,
genvars = self.genvars_str)
]
# Define else
access_rtl['else'] = ["else"]
# Add empty string
access_rtl[''] = ['']
# Check if hardware has precedence (default `precedence = sw`)
if self.precedence == 'PrecedenceType.sw':
order_list = ['sw_write',
'hw_write',
'singlepulse']
else:
order_list = ['hw_write',
'sw_write',
'singlepulse']
# Add appropriate else
order_list_rtl = []
for i in order_list:
# Still a loop and not a list comprehension since this might
# get longer in the future and thus become unreadable
if len(access_rtl[i]) > 0:
order_list_rtl = [*order_list_rtl, *access_rtl[i]]
order_list_rtl.append("else")
# Remove last pop
order_list_rtl.pop()
# Chain access RTL to the rest of the RTL
self.rtl_header = [*self.rtl_header, *order_list_rtl]
self.rtl_header.append(
Field.templ_dict['end_field_ff'].format(
path = self.path_underscored))
# Generate RTL
self.__add_always_ff()
self.__add_access_rtl()
self.__add_combo() self.__add_combo()
self.__add_ports() self.__add_ports()
@ -217,7 +101,7 @@ class Field(Component):
self.rst['name'] = rst_signal.inst_name self.rst['name'] = rst_signal.inst_name
self.rst['async'] = rst_signal.get_property("async") self.rst['async'] = rst_signal.get_property("async")
self.rst['type'] = "asynchronous" if rst['async'] else "synchronous" self.rst['type'] = "asynchronous" if self.rst['async'] else "synchronous"
# Active low or active high? # Active low or active high?
if rst_signal.get_property("activelow"): if rst_signal.get_property("activelow"):
@ -253,7 +137,7 @@ class Field(Component):
misc_flags.discard('reset') misc_flags.discard('reset')
# Add comment with summary on field's properties # Add comment with summary on field's properties
self.rtl_header.append( return \
Field.templ_dict['field_comment'].format( Field.templ_dict['field_comment'].format(
name = self.obj.inst_name, name = self.obj.inst_name,
hw_access = str(self.hw_access)[11:], hw_access = str(self.hw_access)[11:],
@ -265,7 +149,179 @@ class Field(Component):
misc_flags = misc_flags if misc_flags else '-', misc_flags = misc_flags if misc_flags else '-',
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)
def __add_always_ff(self):
# Handle always_ff
sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst'
self.rtl_header.append(
Field.templ_dict[sense_list].format(
clk_name = "clk",
rst_edge = self.rst['edge'],
rst_name = self.rst['name']))
# Add actual reset line
if self.rst['name']:
self.rtl_header.append(
Field.templ_dict['rst_field_assign'].format(
path = self.path_underscored,
rst_name = self.rst['name'],
rst_negl = "!" if self.rst['active'] == "active_high" else "",
rst_value = self.rst['value'],
genvars = self.genvars_str))
self.rtl_header.append("begin")
def __add_access_rtl(self):
# Not all access types are required and the order might differ
# depending on what types are defined and what precedence is
# set. Therefore, first add all RTL into a dictionary and
# later place it in the right order.
#
# The following RTL blocks are defined:
# - hw_write --> write access for the hardware interface
# - sw_write --> write access for the software interface
#
access_rtl = dict([])
# Define hardware access (if applicable)
access_rtl['hw_write'] = []
if self.hw_access in (AccessType.rw, AccessType.w):
if self.we_or_wel:
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_we_wel'].format(
negl = '!' if self.obj.get_property('wel') else '',
path = self.path_underscored,
genvars = self.genvars_str))
else:
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_no_we_wel'])
access_rtl['hw_write'].append(
Field.templ_dict['hw_access_field'].format(
path = self.path_underscored,
genvars = self.genvars_str))
# Define software access (if applicable)
access_rtl['sw_write'] = []
if self.sw_access in (AccessType.rw, AccessType.w):
access_rtl['sw_write'].append(
Field.templ_dict['sw_access_field'].format(
path_wo_field = self.path_wo_field,
genvars = self.genvars_str))
# Check if an onwrite property is set
onwrite = self.obj.get_property('onwrite')
if onwrite:
if onwrite == OnWriteType.wuser:
self.logger.warning("The OnReadType.wuser is not yet supported!")
elif onwrite in (OnWriteType.wclr, OnWriteType.wset):
access_rtl['sw_write'].append(
Field.templ_dict[str(onwrite)].format(
path = self.path_underscored,
genvars = self.genvars_str,
path_wo_field = self.path_wo_field
)
)
else:
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'].append(
Field.templ_dict[str(onwrite)].format(
path = self.path_underscored,
genvars = self.genvars_str,
i = i,
msb_bus = str(8*(i+1)-1 if i != self.msbyte else self.obj.msb),
bus_w = str(8 if i != self.msbyte else self.obj.width-(8*j)),
msb_field = str(8*(j+1)-1 if i != self.msbyte else self.obj.width-1),
field_w = str(8 if i != self.msbyte else self.obj.width-(8*j))))
else:
# Normal write
# If field spans multiple bytes, every byte shall have a seperate enable!
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
access_rtl['sw_write'].append(
Field.templ_dict['sw_access_byte'].format(
path = self.path_underscored,
genvars = self.genvars_str,
i = i,
msb_bus = str(8*(i+1)-1 if i != self.msbyte else self.obj.msb),
bus_w = str(8 if i != self.msbyte else self.obj.width-(8*j)),
msb_field = str(8*(j+1)-1 if i != self.msbyte else self.obj.width-1),
field_w = str(8 if i != self.msbyte else self.obj.width-(8*j))))
access_rtl['sw_write'].append("end")
onread = self.obj.get_property('onread')
access_rtl['sw_read'] = []
if self.sw_access in (AccessType.rw, AccessType.r) and onread:
if onread == OnReadType.ruser:
self.logger.warning("The OnReadType.ruser is not yet supported!")
else:
access_rtl['sw_read'].append(
Field.templ_dict[str(onread)].format(
path = self.path_underscored,
genvars = self.genvars_str,
path_wo_field = self.path_wo_field
)
)
# Add singlepulse property
if self.obj.get_property('singlepulse'):
access_rtl['singlepulse'] = [
Field.templ_dict['singlepulse'].format(
path = self.path_underscored,
genvars = self.genvars_str)
]
else:
access_rtl['singlepulse'] = []
# Define else
access_rtl['else'] = ["else"]
# Add empty string
access_rtl[''] = ['']
# Check if hardware has precedence (default `precedence = sw`)
if self.precedence == PrecedenceType.sw:
order_list = [
'sw_write',
'sw_read',
'hw_write',
'singlepulse'
]
else:
order_list = [
'hw_write',
'sw_write',
'sw_read',
'singlepulse'
]
# Add appropriate else
order_list_rtl = []
for i in order_list:
# Still a loop and not a list comprehension since this might
# get longer in the future and thus become unreadable
if len(access_rtl[i]) > 0:
order_list_rtl = [*order_list_rtl, *access_rtl[i]]
order_list_rtl.append("else")
# Remove last pop
order_list_rtl.pop()
# Chain access RTL to the rest of the RTL
self.rtl_header = [*self.rtl_header, *order_list_rtl]
self.rtl_header.append(
Field.templ_dict['end_field_ff'].format(
path = self.path_underscored))
def __add_ports(self): def __add_ports(self):
# Port is writable by hardware --> Input port from hardware # Port is writable by hardware --> Input port from hardware
@ -291,6 +347,13 @@ class Field(Component):
self.dimensions self.dimensions
)) ))
# Connect flops to output port
self.rtl_header.append(
Field.templ_dict['out_port_assign'].format(
genvars = self.genvars_str,
path = self.path_underscored))
def sanity_checks(self): def sanity_checks(self):
# If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write # If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write
if not self.we_or_wel and\ if not self.we_or_wel and\

View File

@ -17,17 +17,57 @@ sw_access_byte: |-
begin begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}]; {path}_q{genvars}[{msb_field}-:{field_w}] <= sw_wr_bus[{msb_bus}-:{bus_w}];
end end
hw_access_we_wel: |- hw_access_we_wel: |-
if ({negl}{path}_hw_wr{genvars}) if ({negl}{path}_hw_wr{genvars})
hw_access_no_we_wel: |- hw_access_no_we_wel: |-
if (1) // we or wel property not set // we or wel property not set
hw_access_field: |- hw_access_field: |-
begin begin
{path}_q{genvars} <= {path}_in{genvars}; {path}_q{genvars} <= {path}_in{genvars};
end end
end_field_ff: |- end_field_ff: |-
end // of {path}'s always_ff end // of {path}'s always_ff
OnWriteType.woset: |-
if (byte_enable[{i}]) // woset property
begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= {path}_q{genvars}[{msb_field}-:{field_w}] | sw_wr_bus[{msb_bus}-:{bus_w}];
end
OnWriteType.woclr: |-
if (byte_enable[{i}]) // woclr property
begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= {path}_q{genvars}[{msb_field}-:{field_w}] & ~sw_wr_bus[{msb_bus}-:{bus_w}];
end
OnWriteType.wot: |-
if (byte_enable[{i}]) // wot property
begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= {path}_q{genvars}[{msb_field}-:{field_w}] ^ sw_wr_bus[{msb_bus}-:{bus_w}];
end
OnWriteType.wzs: |-
if (byte_enable[{i}]) // wzs property
begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= {path}_q{genvars}[{msb_field}-:{field_w}] & sw_wr_bus[{msb_bus}-:{bus_w}];
end
OnWriteType.wzt: |-
if (byte_enable[{i}]) // wzt property
begin
{path}_q{genvars}[{msb_field}-:{field_w}] <= {path}_q{genvars}[{msb_field}-:{field_w}] ~^ sw_wr_bus[{msb_bus}-:{bus_w}];
end
OnWriteType.wclr: |-
{path}_q{genvars} <= {{width{{1'b0}}}};
OnWriteType.wset: |-
{path}_q{genvars} <= {{width{{1'b1}}}};
OnReadType.rclr: |-
if ({path_wo_field}_sw_rd{genvars}) // rclr property
begin
{path}_q{genvars} <= {{width{{1'b0}}}};
end
OnReadType.rset: |-
if ({path_wo_field}_sw_rd{genvars}) // rset property
begin
{path}_q{genvars} <= {{width{{1'b1}}}};
end
field_comment: |- field_comment: |-
//-----------------FIELD SUMMARY----------------- //-----------------FIELD SUMMARY-----------------
@ -46,4 +86,8 @@ singlepulse: |-
begin begin
{path}{genvars} <= 0; {path}{genvars} <= 0;
end end
out_port_assign: |-
// Connect register to hardware output port
assign {path}_r{genvars} <= {path}_q{genvars};