diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index ace6c4f..6ea7261 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -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 - # 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 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} - ) - - if self.obj.get_property('stickybit'): - self.access_rtl['hw_write'] = ([ - self.process_yaml( - Field.templ_dict['sticky_intr'], + Field.templ_dict['trigger_input'], {'path': self.path_underscored, - 'genvars': self.genvars_str, + 'field_type': self.field_type, } ) - ], - False) - # Create logic that contains condition for trigger - if self.obj.get_property('intr type') != InterruptType.level: + if bit_type: + 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']: 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,34 +802,22 @@ 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, + '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: self.access_rtl['hw_write'] = ([ 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.") diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 8254eb4..e79ca07 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -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]) } ) diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index c410edd..7f0463b 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -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}) <> - {interrupt_trigger_input}_q{genvars} <= 1'b0; + {trigger_signal}_q{genvars} <= 1'b0; <> 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} <> - {interrupt_trigger_input}_q{genvars} <= {interrupt_trigger_input}{genvars}; + {trigger_signal}_q{genvars} <= {trigger_signal}{genvars}; <> 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 diff --git a/srdl2sv/components/templates/register.yaml b/srdl2sv/components/templates/register.yaml index 307f355..902ae5b 100644 --- a/srdl2sv/components/templates/register.yaml +++ b/srdl2sv/components/templates/register.yaml @@ -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'