mirror of
https://github.com/Silicon1602/srdl2sv.git
synced 2024-12-22 06:58:41 +00:00
Make stickybit available for non-intr fields and add support for sticky
This commit is contained in:
parent
f30dce67c2
commit
694f7c124e
@ -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.")
|
||||
|
@ -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])
|
||||
}
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user