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]
|
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.")
|
||||||
|
@ -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])
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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'
|
||||||
|
Loading…
Reference in New Issue
Block a user