mirror of
https://github.com/Silicon1602/srdl2sv.git
synced 2024-12-22 15:08:39 +00:00
Finish initial version of interrupt suport, closes #1
The software is now able to create most interrupt combinations of Section 9.9 of the SystemRDL 2.0 LRM. It supports stickybit/non-stickybit interrupts, it support posedge, negedge, bothedge, and level interrupts, and it is able to generate all surrounding logic. This commit also fixes a reset-bug that caused registers that were reset to 0 to be not reset (because 'if not reset_value' will return True if the 'reset_value' is 0).
This commit is contained in:
parent
c52e59abd0
commit
6359883c27
@ -228,8 +228,16 @@ class Component():
|
|||||||
|
|
||||||
return ''.join(name)
|
return ''.join(name)
|
||||||
|
|
||||||
def process_yaml(self, yaml_obj, values: dict = {}):
|
def process_yaml(self,
|
||||||
|
yaml_obj,
|
||||||
|
values: dict = {},
|
||||||
|
skip_signals: bool = False,
|
||||||
|
skip_inputs: bool = False,
|
||||||
|
skip_outputs: bool = False):
|
||||||
try:
|
try:
|
||||||
|
if skip_signals:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
for x in yaml_obj['signals']:
|
for x in yaml_obj['signals']:
|
||||||
self.signals[x['name'].format(**values)] =\
|
self.signals[x['name'].format(**values)] =\
|
||||||
(x['signal_type'].format(**values),
|
(x['signal_type'].format(**values),
|
||||||
@ -238,6 +246,9 @@ class Component():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if skip_inputs:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
for x in yaml_obj['input_ports']:
|
for x in yaml_obj['input_ports']:
|
||||||
self.ports['input'][x['name'].format(**values)] =\
|
self.ports['input'][x['name'].format(**values)] =\
|
||||||
(x['signal_type'].format(**values),
|
(x['signal_type'].format(**values),
|
||||||
@ -246,6 +257,9 @@ class Component():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if skip_outputs:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
for x in yaml_obj['output_ports']:
|
for x in yaml_obj['output_ports']:
|
||||||
self.ports['output'][x['name'].format(**values)] =\
|
self.ports['output'][x['name'].format(**values)] =\
|
||||||
(x['signal_type'].format(**values),
|
(x['signal_type'].format(**values),
|
||||||
|
@ -6,7 +6,7 @@ import yaml
|
|||||||
|
|
||||||
from systemrdl.node import FieldNode, SignalNode
|
from systemrdl.node import FieldNode, SignalNode
|
||||||
from systemrdl.component import Reg, Regfile, Addrmap, Root
|
from systemrdl.component import Reg, Regfile, Addrmap, Root
|
||||||
from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType
|
from systemrdl.rdltypes import PrecedenceType, AccessType, OnReadType, OnWriteType, InterruptType
|
||||||
|
|
||||||
# Local modules
|
# Local modules
|
||||||
from components.component import Component, TypeDef
|
from components.component import Component, TypeDef
|
||||||
@ -46,8 +46,11 @@ class Field(Component):
|
|||||||
# seperately in case of alias registers
|
# seperately in case of alias registers
|
||||||
if not self.config['external']:
|
if not self.config['external']:
|
||||||
self.__add_always_ff()
|
self.__add_always_ff()
|
||||||
self.__add_hw_access()
|
|
||||||
self.__add_interrupt()
|
# Only add normal hardware access if field is not an interrupt field
|
||||||
|
if not self.__add_interrupt():
|
||||||
|
self.__add_hw_access()
|
||||||
|
|
||||||
self.__add_combo()
|
self.__add_combo()
|
||||||
self.__add_swmod_swacc()
|
self.__add_swmod_swacc()
|
||||||
self.__add_counter()
|
self.__add_counter()
|
||||||
@ -125,7 +128,7 @@ class Field(Component):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# If field spans multiple bytes, every byte shall have a seperate enable!
|
# If field spans multiple bytes, every byte shall have a seperate enable!
|
||||||
for j, i in enumerate(range(self.lsbyte, self.msbyte+1)):
|
for i in range(self.lsbyte, self.msbyte+1):
|
||||||
msb_bus = 8*(i+1)-1 if i != self.msbyte else obj.msb
|
msb_bus = 8*(i+1)-1 if i != self.msbyte else obj.msb
|
||||||
lsb_bus = 8*i if i != self.lsbyte else obj.inst.lsb
|
lsb_bus = 8*i if i != self.lsbyte else obj.inst.lsb
|
||||||
|
|
||||||
@ -690,6 +693,81 @@ class Field(Component):
|
|||||||
if self.obj.get_property('intr'):
|
if self.obj.get_property('intr'):
|
||||||
self.intr = True
|
self.intr = True
|
||||||
|
|
||||||
|
# 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 next_val := self.obj.get_property('next'):
|
||||||
|
intr_trigger = self.get_signal_name(next_val)
|
||||||
|
else:
|
||||||
|
intr_trigger = \
|
||||||
|
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'],
|
||||||
|
{'path': self.path_underscored,
|
||||||
|
'genvars': self.genvars_str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
False)
|
||||||
|
|
||||||
|
# Create logic that contains condition for trigger
|
||||||
|
if self.obj.get_property('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,
|
||||||
|
'rst_name': self.rst['name'],
|
||||||
|
'rst_negl': "!" if self.rst['active'] == "active_low" else "",
|
||||||
|
'genvars': self.genvars_str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
reset_intr_header = ""
|
||||||
|
|
||||||
|
self.rtl_footer.append(
|
||||||
|
self.process_yaml(
|
||||||
|
Field.templ_dict['always_ff_block_intr'],
|
||||||
|
{'interrupt_trigger_input': intr_trigger,
|
||||||
|
'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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.access_rtl['hw_write'] = ([
|
||||||
|
self.process_yaml(
|
||||||
|
Field.templ_dict['nonsticky_intr'],
|
||||||
|
{'path': self.path_underscored,
|
||||||
|
'assignment': intr_trigger,
|
||||||
|
'genvars': self.genvars_str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
False)
|
||||||
|
|
||||||
# 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'):
|
||||||
@ -727,6 +805,8 @@ class Field(Component):
|
|||||||
self.itr_masked = False
|
self.itr_masked = False
|
||||||
self.itr_haltmasked = False
|
self.itr_haltmasked = False
|
||||||
|
|
||||||
|
return self.intr
|
||||||
|
|
||||||
def __add_hw_access(self):
|
def __add_hw_access(self):
|
||||||
# Mutually exclusive. systemrdl-compiler performs check for this
|
# Mutually exclusive. systemrdl-compiler performs check for this
|
||||||
enable_mask_negl = ''
|
enable_mask_negl = ''
|
||||||
@ -795,11 +875,15 @@ class Field(Component):
|
|||||||
|
|
||||||
assignment = self.get_signal_name(self.obj.get_property('next'))
|
assignment = self.get_signal_name(self.obj.get_property('next'))
|
||||||
|
|
||||||
|
skip_inputs = True
|
||||||
|
|
||||||
if self.we_or_wel:
|
if self.we_or_wel:
|
||||||
self.logger.info("This field has a 'we' or 'wel' property and "
|
self.logger.info("This field has a 'we' or 'wel' property and "
|
||||||
"uses the 'next' property. Make sure this is "
|
"uses the 'next' property. Make sure this is "
|
||||||
"is intentional.")
|
"is intentional.")
|
||||||
else:
|
else:
|
||||||
|
skip_inputs = False
|
||||||
|
|
||||||
# No special property. Assign input to register
|
# No special property. Assign input to register
|
||||||
assignment = \
|
assignment = \
|
||||||
self.process_yaml(
|
self.process_yaml(
|
||||||
@ -819,7 +903,8 @@ class Field(Component):
|
|||||||
'enable_mask_end': enable_mask_end_rtl,
|
'enable_mask_end': enable_mask_end_rtl,
|
||||||
'assignment': assignment,
|
'assignment': assignment,
|
||||||
'idx': enable_mask_idx,
|
'idx': enable_mask_idx,
|
||||||
'field_type': self.field_type}
|
'field_type': self.field_type},
|
||||||
|
skip_inputs = skip_inputs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -952,10 +1037,13 @@ class Field(Component):
|
|||||||
break
|
break
|
||||||
|
|
||||||
# Check if there is a list that shall be unlooped
|
# Check if there is a list that shall be unlooped
|
||||||
if isinstance(self.access_rtl[i], tuple):
|
try:
|
||||||
access_rtl = [self.access_rtl[i]]
|
if isinstance(self.access_rtl[i], tuple):
|
||||||
else:
|
access_rtl = [self.access_rtl[i]]
|
||||||
access_rtl = self.access_rtl[i]
|
else:
|
||||||
|
access_rtl = self.access_rtl[i]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
for unpacked_access_rtl in access_rtl:
|
for unpacked_access_rtl in access_rtl:
|
||||||
if len(unpacked_access_rtl[0]) == 0:
|
if len(unpacked_access_rtl[0]) == 0:
|
||||||
@ -1113,10 +1201,17 @@ class Field(Component):
|
|||||||
|
|
||||||
if self.rst['name']:
|
if self.rst['name']:
|
||||||
self.resets.add(self.rst['name'])
|
self.resets.add(self.rst['name'])
|
||||||
|
elif obj.get_property("reset") is not None:
|
||||||
|
self.logger.warning("Field has a reset value, but no reset "\
|
||||||
|
"signal was defined and connected to the "\
|
||||||
|
"field. Note that explicit connecting this "\
|
||||||
|
"is not required if a field_reset was defined.")
|
||||||
|
|
||||||
# Value of reset must always be determined on field level
|
# Value of reset must always be determined on field level
|
||||||
|
# Don't use 'not obj.get_property("reset"), since the value
|
||||||
|
# could (and will often be) be '0'
|
||||||
self.rst['value'] = \
|
self.rst['value'] = \
|
||||||
'\'x' if not obj.get_property("reset") else\
|
'\'x' if obj.get_property("reset") == None else\
|
||||||
obj.get_property('reset')
|
obj.get_property('reset')
|
||||||
|
|
||||||
# Define dict that holds all RTL
|
# Define dict that holds all RTL
|
||||||
@ -1155,13 +1250,14 @@ class Field(Component):
|
|||||||
# Handle always_ff
|
# Handle always_ff
|
||||||
sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst'
|
sense_list = 'sense_list_rst' if self.rst['async'] else 'sense_list_no_rst'
|
||||||
|
|
||||||
self.rtl_header.append(
|
self.always_ff_header = \
|
||||||
self.process_yaml(
|
self.process_yaml(
|
||||||
Field.templ_dict[sense_list],
|
Field.templ_dict[sense_list],
|
||||||
{'rst_edge': self.rst['edge'],
|
{'rst_edge': self.rst['edge'],
|
||||||
'rst_name': self.rst['name']}
|
'rst_name': self.rst['name']}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
self.rtl_header.append(self.always_ff_header)
|
||||||
|
|
||||||
# Add actual reset line
|
# Add actual reset line
|
||||||
if self.rst['name']:
|
if self.rst['name']:
|
||||||
@ -1179,9 +1275,6 @@ class Field(Component):
|
|||||||
|
|
||||||
self.rtl_header.append("begin")
|
self.rtl_header.append("begin")
|
||||||
|
|
||||||
# Add name of actual field to Signal field
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
def sanity_checks(self):
|
def sanity_checks(self):
|
||||||
# If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write
|
# If hw=rw/sw=[r]w and hw has no we/wel, sw will never be able to write
|
||||||
if not self.we_or_wel and\
|
if not self.we_or_wel and\
|
||||||
@ -1195,8 +1288,6 @@ class Field(Component):
|
|||||||
"write every cycle.")
|
"write every cycle.")
|
||||||
|
|
||||||
|
|
||||||
# TODO: Counter & hw=r shouldn't work
|
|
||||||
|
|
||||||
# If hw=ro and the next property is set, throw a fatal
|
# If hw=ro and the next property is set, throw a fatal
|
||||||
if self.obj.get_property('hw') == AccessType.r\
|
if self.obj.get_property('hw') == AccessType.r\
|
||||||
and self.obj.get_property('next'):
|
and self.obj.get_property('next'):
|
||||||
|
@ -469,4 +469,80 @@ external_wr_assignments:
|
|||||||
signal_type: 'logic'
|
signal_type: 'logic'
|
||||||
external_wr_mask_segment:
|
external_wr_mask_segment:
|
||||||
rtl: |-
|
rtl: |-
|
||||||
{{{width}{{b2r.byte_en[{idx}]}}}}
|
{{{width}{{b2r.byte_en[{idx}]}}}}
|
||||||
|
interrupt_trigger_input:
|
||||||
|
rtl: |-
|
||||||
|
{path}_set_intr
|
||||||
|
input_ports:
|
||||||
|
- name: '{path}_set_intr'
|
||||||
|
signal_type: 'logic'
|
||||||
|
rst_intr_header:
|
||||||
|
rtl: |-
|
||||||
|
if ({rst_negl}{rst_name})
|
||||||
|
<<INDENT>>
|
||||||
|
{interrupt_trigger_input}_q{genvars} <= 1'b0;
|
||||||
|
<<UNINDENT>>
|
||||||
|
else
|
||||||
|
signals:
|
||||||
|
- name: '{interrupt_trigger_input}_q'
|
||||||
|
signal_type: 'logic'
|
||||||
|
always_ff_block_intr:
|
||||||
|
rtl: |-
|
||||||
|
|
||||||
|
// Flops to generate appropriate interrupt signal
|
||||||
|
{always_ff_header}
|
||||||
|
{reset_intr_header}
|
||||||
|
<<INDENT>>
|
||||||
|
{interrupt_trigger_input}_q{genvars} <= {interrupt_trigger_input}{genvars};
|
||||||
|
<<UNINDENT>>
|
||||||
|
signals:
|
||||||
|
- name: '{interrupt_trigger_input}_q'
|
||||||
|
signal_type: 'logic'
|
||||||
|
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};
|
||||||
|
signals:
|
||||||
|
- name: '{path}_intr_latch'
|
||||||
|
signal_type: 'logic'
|
||||||
|
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};
|
||||||
|
signals:
|
||||||
|
- name: '{path}_intr_latch'
|
||||||
|
signal_type: 'logic'
|
||||||
|
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});
|
||||||
|
signals:
|
||||||
|
- name: '{path}_intr_latch'
|
||||||
|
signal_type: 'logic'
|
||||||
|
InterruptType.level:
|
||||||
|
rtl: |-
|
||||||
|
|
||||||
|
// Define signal that causes the interrupt to be set (level-type interrupt)
|
||||||
|
assign {path}_intr_latch{genvars} = {interrupt_trigger_input}{genvars};
|
||||||
|
signals:
|
||||||
|
- name: '{path}_intr_latch'
|
||||||
|
signal_type: 'logic'
|
||||||
|
sticky_intr:
|
||||||
|
rtl: |-
|
||||||
|
if ({path}_intr_latch{genvars})
|
||||||
|
begin
|
||||||
|
// Sticky interrupt. Keep value until software clears it
|
||||||
|
{path}_q{genvars} <= 1'b1;
|
||||||
|
end
|
||||||
|
signals:
|
||||||
|
- name: '{path}_intr_latch'
|
||||||
|
signal_type: 'logic'
|
||||||
|
nonsticky_intr:
|
||||||
|
rtl: |-
|
||||||
|
begin
|
||||||
|
// Non-sticky interrupt. Only keep value high if source keeps up
|
||||||
|
{path}_q{genvars} <= {assignment};
|
||||||
|
end
|
||||||
|
@ -138,7 +138,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'
|
||||||
|
56
tests/systemrdl/interrupts.rdl
Normal file
56
tests/systemrdl/interrupts.rdl
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
addrmap interrupts {
|
||||||
|
signal { activelow; async; field_reset;} field_reset_n;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=rw; intr; } intr1 [0:0] = 0;
|
||||||
|
field {sw=rw; hw=rw; bothedge intr; } intr2 [1:1] = 0;
|
||||||
|
field {sw=rw; hw=rw; negedge intr; } intr3 [2:2] = 0;
|
||||||
|
field {sw=rw; hw=rw; posedge intr; } intr4 [3:3] = 0;
|
||||||
|
field {sw=rw; hw=rw; } intr5 [4:4] = 0;
|
||||||
|
field {sw=rw; hw=rw; nonsticky intr;} intr6 [5:5] = 0;
|
||||||
|
} itrs_reg;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=r;} intr1 [0:0];
|
||||||
|
field {sw=rw; hw=r;} intr2 [1:1];
|
||||||
|
} itrs_mask;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=r;} intr5 [1:1];
|
||||||
|
} itrs_enable;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=r;} intr6 [1:1];
|
||||||
|
} itrs_next_assign;
|
||||||
|
|
||||||
|
itrs_reg.intr1->mask = itrs_mask.intr1;
|
||||||
|
itrs_reg.intr2->mask = itrs_mask.intr2;
|
||||||
|
itrs_reg.intr5->enable = itrs_enable.intr5;
|
||||||
|
itrs_reg.intr5->next = itrs_next_assign.intr6;
|
||||||
|
|
||||||
|
|
||||||
|
// HALT REGISTERS
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=rw; intr;} intr1 [0:0];
|
||||||
|
field {sw=rw; hw=rw; intr;} intr2 [1:1];
|
||||||
|
field {sw=rw; hw=rw; intr;} intr3 [2:2];
|
||||||
|
field {sw=rw; hw=rw; } intr4 [3:3];
|
||||||
|
field {sw=rw; hw=rw; intr;} intr5 [4:4];
|
||||||
|
} itrs_halteable_reg;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=r;} intr1 [0:0];
|
||||||
|
field {sw=rw; hw=r;} intr2 [1:1];
|
||||||
|
} itrs_halt;
|
||||||
|
|
||||||
|
itrs_halteable_reg.intr1->haltmask = itrs_halt.intr1;
|
||||||
|
itrs_halteable_reg.intr2->haltmask = itrs_halt.intr2;
|
||||||
|
|
||||||
|
// USE INTERRUPT
|
||||||
|
reg {
|
||||||
|
field {sw=rw; hw=rw;} itrs_reg_next [0:0];
|
||||||
|
field {sw=rw; hw=rw;} itrs_halteable_next [1:1];
|
||||||
|
} itrs_next;
|
||||||
|
|
||||||
|
itrs_next.itrs_reg_next->next = itrs_reg->intr;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user