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] self.rtl_footer = [*self.rtl_footer, swmod_props, swacc_props]
def __add_interrupt(self): def __add_sticky(self, latch_signal: str, force_trigger_generation: bool = False):
if self.obj.get_property('intr'): bit_type = None
self.properties['intr'] = True trigger_signal = None
# Determine what causes the interrupt to get set, i.e., if self.obj.get_property('stickybit'):
# is it a trigger that is passed to the module through an bit_type = 'stickybit'
# input or is it an internal signal 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'): 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: else:
intr_trigger = \ trigger_signal =\
self.process_yaml( self.process_yaml(
Field.templ_dict['interrupt_trigger_input'], Field.templ_dict['trigger_input'],
{'path': self.path_underscored}
)
if self.obj.get_property('stickybit'):
self.access_rtl['hw_write'] = ([
self.process_yaml(
Field.templ_dict['sticky_intr'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'field_type': self.field_type,
} }
) )
],
False)
# Create logic that contains condition for trigger if bit_type:
if self.obj.get_property('intr type') != InterruptType.level: self.access_rtl['hw_write'] = ([
self.process_yaml(
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)
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']: if self.rst['name']:
reset_intr_header = \ reset_intr_header = \
self.process_yaml( self.process_yaml(
Field.templ_dict['rst_intr_header'], Field.templ_dict['rst_intr_header'],
{'interrupt_trigger_input': intr_trigger, {'trigger_signal': trigger_signal,
'rst_name': self.rst['name'], 'rst_name': self.rst['name'],
'rst_negl': "!" if self.rst['active'] == "active_low" else "", 'rst_negl': "!" if self.rst['active'] == "active_low" else "",
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'field_type': self.field_type,
} }
) )
else: else:
@ -745,34 +802,22 @@ class Field(Component):
self.rtl_footer.append( self.rtl_footer.append(
self.process_yaml( self.process_yaml(
Field.templ_dict['always_ff_block_intr'], Field.templ_dict['always_ff_block_intr'],
{'interrupt_trigger_input': intr_trigger, {'trigger_signal': trigger_signal,
'always_ff_header': self.always_ff_header, 'always_ff_header': self.always_ff_header,
'reset_intr_header': reset_intr_header, 'reset_intr_header': reset_intr_header,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'field_type': self.field_type,
} }
) )
) )
# 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,
}
)
)
else: else:
self.access_rtl['hw_write'] = ([ self.access_rtl['hw_write'] = ([
self.process_yaml( self.process_yaml(
Field.templ_dict['nonsticky_intr'], Field.templ_dict['nonsticky_intr'],
{'path': self.path_underscored, {'path': self.path_underscored,
'assignment': intr_trigger, 'assignment': trigger_signal,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
} }
) )
@ -782,12 +827,12 @@ class Field(Component):
# Generate masked & enabled version of interrupt to be # Generate masked & enabled version of interrupt to be
# picked up by the register at the top level # picked up by the register at the top level
if mask := self.obj.get_property('mask'): if mask := self.obj.get_property('mask'):
self.itr_masked = ' && !'.join([ self.itr_masked = ' & ~'.join([
self.register_name, self.register_name,
self.get_signal_name(mask) self.get_signal_name(mask)
]) ])
elif enable := self.obj.get_property('enable'): elif enable := self.obj.get_property('enable'):
self.itr_masked = ' && '.join([ self.itr_masked = ' & '.join([
self.register_name, self.register_name,
self.get_signal_name(enable) self.get_signal_name(enable)
]) ])
@ -797,14 +842,14 @@ class Field(Component):
# Generate haltmasked & haltenabled version of interrupt to be # Generate haltmasked & haltenabled version of interrupt to be
# picked up by the register at the top level # picked up by the register at the top level
if haltmask := self.obj.get_property('haltmask'): if haltmask := self.obj.get_property('haltmask'):
self.itr_haltmasked = ' && !'.join([ self.itr_haltmasked = ' & ~'.join([
self.register_name, self.register_name,
self.get_signal_name(haltmask) self.get_signal_name(haltmask)
]) ])
self.properties['halt'] = True self.properties['halt'] = True
elif haltenable := self.obj.get_property('haltenable'): elif haltenable := self.obj.get_property('haltenable'):
self.itr_haltmasked = ' && '.join([ self.itr_haltmasked = ' & ~'.join([
self.register_name, self.register_name,
self.get_signal_name(haltenable) self.get_signal_name(haltenable)
]) ])
@ -851,7 +896,11 @@ class Field(Component):
enable_mask_idx = '' enable_mask_idx = ''
# Define hardware access (if applicable) # 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.access_rtl['hw_write'] = ([
self.process_yaml( self.process_yaml(
Field.templ_dict['hw_access_counter'], Field.templ_dict['hw_access_counter'],
@ -1022,14 +1071,14 @@ class Field(Component):
order_list = [ order_list = [
'sw_write', 'sw_write',
'sw_read', 'sw_read',
'hw_write',
'hw_setclr', 'hw_setclr',
'hw_write',
'singlepulse' 'singlepulse'
] ]
else: else:
order_list = [ order_list = [
'hw_write',
'hw_setclr', 'hw_setclr',
'hw_write',
'sw_write', 'sw_write',
'sw_read', 'sw_read',
'singlepulse' 'singlepulse'
@ -1306,3 +1355,10 @@ class Field(Component):
"but simultanously, the next property is set. Since "\ "but simultanously, the next property is set. Since "\
"this would reflect wrong behavior in documentation, "\ "this would reflect wrong behavior in documentation, "\
"the next property is ignored.") "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): def __add_interrupts(self):
# Semantics on the intr and halt property: # Semantics on the intr and halt property:
# a) The intr and halt register properties are outputs; they should only # a) The intr and halt register properties are outputs; they should only
# occur on the right-hand side of an assignment in SystemRDL. # occur on the right-hand side of an assignment in SystemRDL.
@ -119,7 +118,7 @@ class Register(Component):
Register.templ_dict['interrupt_intr'], Register.templ_dict['interrupt_intr'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'list': ') || ('.join([ 'list': ') || |('.join([
x.itr_masked for x in self.children.values() if x.itr_masked]) 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'], Register.templ_dict['interrupt_halt'],
{'path': self.path_underscored, {'path': self.path_underscored,
'genvars': self.genvars_str, 'genvars': self.genvars_str,
'list': ') || ('.join([ 'list': ') || |('.join([
x.itr_haltmasked for x in self.children.values() if x.itr_haltmasked]) x.itr_haltmasked for x in self.children.values() if x.itr_haltmasked])
} }
) )

View File

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

View File

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