Make stickybit available for non-intr fields and add support for sticky

This commit is contained in:
Dennis Potter 2021-10-03 15:48:27 -07:00
parent f30dce67c2
commit 694f7c124e
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
4 changed files with 147 additions and 80 deletions

View File

@ -700,43 +700,100 @@ class Field(Component):
self.rtl_footer = [*self.rtl_footer, swmod_props, swacc_props]
def __add_interrupt(self):
if self.obj.get_property('intr'):
self.properties['intr'] = True
def __add_sticky(self, latch_signal: str, force_trigger_generation: bool = False):
bit_type = None
trigger_signal = None
if self.obj.get_property('stickybit'):
bit_type = 'stickybit'
elif self.obj.get_property('sticky'):
bit_type = 'sticky'
# Determine what causes the interrupt to get set, i.e.,
# is it a trigger that is passed to the module through an
# input or is it an internal signal
if bit_type or force_trigger_generation:
if next_val := self.obj.get_property('next'):
intr_trigger = self.get_signal_name(next_val)
trigger_signal = self.get_signal_name(next_val)
else:
intr_trigger = \
trigger_signal =\
self.process_yaml(
Field.templ_dict['interrupt_trigger_input'],
{'path': self.path_underscored}
Field.templ_dict['trigger_input'],
{'path': self.path_underscored,
'field_type': self.field_type,
}
)
if self.obj.get_property('stickybit'):
if bit_type:
self.access_rtl['hw_write'] = ([
self.process_yaml(
Field.templ_dict['sticky_intr'],
Field.templ_dict[bit_type],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'width': self.obj.width,
'field_type': self.field_type,
'trigger_signal': trigger_signal,
}
)
],
False)
# Create logic that contains condition for trigger
if self.obj.get_property('intr type') != InterruptType.level:
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict[str(latch_signal)],
{'trigger_signal': trigger_signal,
'path': self.path_underscored,
'genvars': self.genvars_str,
'field_type': self.field_type,
}
)
)
return (bit_type, trigger_signal)
def __add_interrupt(self):
if self.obj.get_property('intr'):
self.properties['intr'] = True
intr_type = self.obj.get_property('intr type')
# Check if it is a sticky(bit) interrupt and generate logic
sticky_type, trigger_signal = self.__add_sticky(
latch_signal = intr_type,
force_trigger_generation = True)
# Interrupts can have special types of sticky bits: posedge, negedge, bothedge.
# Normal fields with the sticky(bit) property are always level.
#
# The actual sticky field already got generated in __add_sticky()
if sticky_type:
if self.obj.width > 1 \
and sticky_type == 'sticky' \
and intr_type in \
(InterruptType.posedge,
InterruptType.negedge,
InterruptType.bothedge):
self.logger.info(
f"Found '{intr_type}' property for interrupt field that is "\
"wider than 1-bit and has the sticky (rather than the "\
"stickybit property. In this case, the value will be "\
"latched if _any_ bit in the signal changes according to "\
"'{intr_type}'"
)
if intr_type != InterruptType.level:
if self.rst['name']:
reset_intr_header = \
self.process_yaml(
Field.templ_dict['rst_intr_header'],
{'interrupt_trigger_input': intr_trigger,
{'trigger_signal': trigger_signal,
'rst_name': self.rst['name'],
'rst_negl': "!" if self.rst['active'] == "active_low" else "",
'genvars': self.genvars_str,
'field_type': self.field_type,
}
)
else:
@ -745,23 +802,11 @@ class Field(Component):
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict['always_ff_block_intr'],
{'interrupt_trigger_input': intr_trigger,
{'trigger_signal': trigger_signal,
'always_ff_header': self.always_ff_header,
'reset_intr_header': reset_intr_header,
'genvars': self.genvars_str,
}
)
)
# This should be implemented as a match case statement later,
# but for now use the old if/elif/else construct to ensure
# compatibility with Python versions before 2021
self.rtl_footer.append(
self.process_yaml(
Field.templ_dict[str(self.obj.get_property('intr type'))],
{'interrupt_trigger_input': intr_trigger,
'path': self.path_underscored,
'genvars': self.genvars_str,
'field_type': self.field_type,
}
)
)
@ -772,7 +817,7 @@ class Field(Component):
self.process_yaml(
Field.templ_dict['nonsticky_intr'],
{'path': self.path_underscored,
'assignment': intr_trigger,
'assignment': trigger_signal,
'genvars': self.genvars_str,
}
)
@ -782,12 +827,12 @@ class Field(Component):
# Generate masked & enabled version of interrupt to be
# picked up by the register at the top level
if mask := self.obj.get_property('mask'):
self.itr_masked = ' && !'.join([
self.itr_masked = ' & ~'.join([
self.register_name,
self.get_signal_name(mask)
])
elif enable := self.obj.get_property('enable'):
self.itr_masked = ' && '.join([
self.itr_masked = ' & '.join([
self.register_name,
self.get_signal_name(enable)
])
@ -797,14 +842,14 @@ class Field(Component):
# Generate haltmasked & haltenabled version of interrupt to be
# picked up by the register at the top level
if haltmask := self.obj.get_property('haltmask'):
self.itr_haltmasked = ' && !'.join([
self.itr_haltmasked = ' & ~'.join([
self.register_name,
self.get_signal_name(haltmask)
])
self.properties['halt'] = True
elif haltenable := self.obj.get_property('haltenable'):
self.itr_haltmasked = ' && '.join([
self.itr_haltmasked = ' & ~'.join([
self.register_name,
self.get_signal_name(haltenable)
])
@ -851,7 +896,11 @@ class Field(Component):
enable_mask_idx = ''
# Define hardware access (if applicable)
if self.obj.get_property('counter'):
sticky, _ = self.__add_sticky(latch_signal = InterruptType.level)
if sticky:
self.logger.info(f"Found {sticky} property.")
elif self.obj.get_property('counter'):
self.access_rtl['hw_write'] = ([
self.process_yaml(
Field.templ_dict['hw_access_counter'],
@ -1022,14 +1071,14 @@ class Field(Component):
order_list = [
'sw_write',
'sw_read',
'hw_write',
'hw_setclr',
'hw_write',
'singlepulse'
]
else:
order_list = [
'hw_write',
'hw_setclr',
'hw_write',
'sw_write',
'sw_read',
'singlepulse'
@ -1306,3 +1355,10 @@ class Field(Component):
"but simultanously, the next property is set. Since "\
"this would reflect wrong behavior in documentation, "\
"the next property is ignored.")
# If a stick(bit) is defined, the counter property will be ignored
if (self.obj.get_property('stickybit') or self.obj.get_property('sticky')) \
and self.obj.get_property('counter'):
self.logger.error("It's not possible to combine the sticky(bit) "\
"property with the counter property. The counter property "\
"will be ignored.")

View File

@ -103,7 +103,6 @@ class Register(Component):
]
def __add_interrupts(self):
# Semantics on the intr and halt property:
# a) The intr and halt register properties are outputs; they should only
# occur on the right-hand side of an assignment in SystemRDL.
@ -119,7 +118,7 @@ class Register(Component):
Register.templ_dict['interrupt_intr'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'list': ') || ('.join([
'list': ') || |('.join([
x.itr_masked for x in self.children.values() if x.itr_masked])
}
)
@ -131,7 +130,7 @@ class Register(Component):
Register.templ_dict['interrupt_halt'],
{'path': self.path_underscored,
'genvars': self.genvars_str,
'list': ') || ('.join([
'list': ') || |('.join([
x.itr_haltmasked for x in self.children.values() if x.itr_haltmasked])
}
)

View File

@ -89,9 +89,6 @@ hw_access_field:
signals:
- name: '{path}_q'
signal_type: '{field_type}'
input_ports:
- name: '{path}_in'
signal_type: '{field_type}'
hw_access_field__assignment__input:
rtl: |-
{path}_in{genvars}{idx}
@ -470,22 +467,22 @@ external_wr_assignments:
external_wr_mask_segment:
rtl: |-
{{{width}{{b2r.byte_en[{idx}]}}}}
interrupt_trigger_input:
trigger_input:
rtl: |-
{path}_set_intr
{path}_in
input_ports:
- name: '{path}_set_intr'
signal_type: 'logic'
- name: '{path}_in'
signal_type: '{field_type}'
rst_intr_header:
rtl: |-
if ({rst_negl}{rst_name})
<<INDENT>>
{interrupt_trigger_input}_q{genvars} <= 1'b0;
{trigger_signal}_q{genvars} <= 1'b0;
<<UNINDENT>>
else
signals:
- name: '{interrupt_trigger_input}_q'
signal_type: 'logic'
- name: '{trigger_signal}_q'
signal_type: '{field_type}'
always_ff_block_intr:
rtl: |-
@ -493,53 +490,68 @@ always_ff_block_intr:
{always_ff_header}
{reset_intr_header}
<<INDENT>>
{interrupt_trigger_input}_q{genvars} <= {interrupt_trigger_input}{genvars};
{trigger_signal}_q{genvars} <= {trigger_signal}{genvars};
<<UNINDENT>>
signals:
- name: '{interrupt_trigger_input}_q'
signal_type: 'logic'
- name: '{trigger_signal}_q'
signal_type: '{field_type}'
InterruptType.posedge:
rtl: |-
// Define signal that causes the interrupt to be set (posedge-type interrupt)
assign {path}_intr_latch{genvars} = !{interrupt_trigger_input}_q{genvars} && {interrupt_trigger_input}{genvars};
assign {path}_sticky_latch{genvars} = !{trigger_signal}_q{genvars} & {trigger_signal}{genvars};
signals:
- name: '{path}_intr_latch'
signal_type: 'logic'
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
InterruptType.negedge:
rtl: |-
// Define signal that causes the interrupt to be set (negedge-type interrupt)
assign {path}_intr_latch{genvars} = {interrupt_trigger_input}_q{genvars} && !{interrupt_trigger_input}{genvars};
assign {path}_sticky_latch{genvars} = {trigger_signal}_q{genvars} & !{trigger_signal}{genvars};
signals:
- name: '{path}_intr_latch'
signal_type: 'logic'
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
InterruptType.bothedge:
rtl: |-
// Define signal that causes the interrupt to be set (bothedge-type interrupt)
assign {path}_intr_latch{genvars} = ({interrupt_trigger_input}_q{genvars} && !{interrupt_trigger_input}{genvars}) || (!{interrupt_trigger_input}_q{genvars} && {interrupt_trigger_input}{genvars});
assign {path}_sticky_latch{genvars} = ({trigger_signal}_q{genvars} & !{trigger_signal}{genvars}) | (!{trigger_signal}_q{genvars} & {trigger_signal}{genvars});
signals:
- name: '{path}_intr_latch'
signal_type: 'logic'
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
InterruptType.level:
rtl: |-
// Define signal that causes the interrupt to be set (level-type interrupt)
assign {path}_intr_latch{genvars} = {interrupt_trigger_input}{genvars};
assign {path}_sticky_latch{genvars} = {trigger_signal}{genvars};
signals:
- name: '{path}_intr_latch'
signal_type: 'logic'
sticky_intr:
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
sticky:
rtl: |-
if ({path}_intr_latch{genvars})
if (|{path}_sticky_latch{genvars} && !(|{path}_q{genvars}))
begin
// Sticky interrupt. Keep value until software clears it
{path}_q{genvars} <= 1'b1;
// Sticky. Keep value until software clears it
{path}_q{genvars} <= {trigger_signal};
end
signals:
- name: '{path}_intr_latch'
signal_type: 'logic'
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
stickybit:
rtl: |-
begin
for (int i = 0; i < {width}; i++)
begin
if ({path}_sticky_latch{genvars}[i])
begin
// Stickybit. Keep value until software clears it
{path}_q{genvars}[i] <= 1'b1;
end
end
end
signals:
- name: '{path}_sticky_latch'
signal_type: '{field_type}'
nonsticky_intr:
rtl: |-
begin

View File

@ -141,7 +141,7 @@ interrupt_comment:
interrupt_intr:
rtl: |-
// Register has at least one interrupt field
assign {path}_intr{genvars} = ({list});
assign {path}_intr{genvars} = |({list});
output_ports:
- name: '{path}_intr'
signal_type: 'logic'
@ -149,7 +149,7 @@ interrupt_halt:
rtl: |-
// Register has at least one interrupt field with halt property set
assign {path}_halt{genvars} = ({list});
assign {path}_halt{genvars} = |({list});
output_ports:
- name: '{path}_halt'
signal_type: 'logic'