mirror of
https://github.com/Silicon1602/srdl2sv.git
synced 2024-12-22 06:58:41 +00:00
Add support for external registers
Every single field and every single alias (!) has its own interface to the surrounding hardware. This is required to give users the maximum amount of freedom when defining certain properties in RDL.
This commit is contained in:
parent
a3b6e1caf8
commit
24d5534037
@ -37,7 +37,7 @@ class Component():
|
||||
self.create_underscored_path()
|
||||
|
||||
# Save config
|
||||
self.config = config
|
||||
self.config = config.copy()
|
||||
|
||||
# Create logger object
|
||||
self.create_logger("{}".format(self.full_path), config)
|
||||
|
@ -44,11 +44,12 @@ class Field(Component):
|
||||
# HW Access can be handled in __init__ function but SW access
|
||||
# must be handled in a seperate method that can be called
|
||||
# seperately in case of alias registers
|
||||
self.__add_always_ff()
|
||||
self.__add_hw_access()
|
||||
self.__add_combo()
|
||||
self.__add_swmod_swacc()
|
||||
self.__add_counter()
|
||||
if not self.config['external']:
|
||||
self.__add_always_ff()
|
||||
self.__add_hw_access()
|
||||
self.__add_combo()
|
||||
self.__add_swmod_swacc()
|
||||
self.__add_counter()
|
||||
|
||||
self.add_sw_access(obj)
|
||||
|
||||
@ -211,6 +212,9 @@ class Field(Component):
|
||||
self.access_rtl['sw_read'] = [access_rtl['sw_read']]
|
||||
self.access_rtl['sw_write'] = [access_rtl['sw_write']]
|
||||
|
||||
self.path_underscored_vec.append(path_underscored)
|
||||
self.path_wo_field_vec.append(path_wo_field)
|
||||
|
||||
def __add_counter(self):
|
||||
if self.obj.get_property('counter'):
|
||||
self.logger.debug("Detected counter property")
|
||||
@ -825,7 +829,51 @@ class Field(Component):
|
||||
)
|
||||
)
|
||||
|
||||
def create_rtl(self):
|
||||
def create_external_rtl(self):
|
||||
if self.obj.get_property('sw') in (AccessType.rw, AccessType.w):
|
||||
for i, alias in enumerate(self.path_underscored_vec):
|
||||
# Create bit-wise mask so that outside logic knows what
|
||||
# bits it may change
|
||||
mask = []
|
||||
for j, byte_idx in enumerate(range(self.msbyte, self.lsbyte-1, -1)):
|
||||
if byte_idx == self.lsbyte:
|
||||
width = (self.lsbyte+1)*8 - self.lsb
|
||||
elif byte_idx == self.msbyte:
|
||||
width = 8 - ((self.msbyte+1)*8-1 - self.msb)
|
||||
else:
|
||||
width = 8
|
||||
|
||||
mask.append(
|
||||
Field.templ_dict['external_wr_mask_segment']['rtl'].format(
|
||||
idx = byte_idx,
|
||||
width = width)
|
||||
)
|
||||
|
||||
self.rtl_footer.append(self.process_yaml(
|
||||
Field.templ_dict['external_wr_assignments'],
|
||||
{'path': alias,
|
||||
'path_wo_field': self.path_wo_field_vec[i],
|
||||
'genvars': self.genvars_str,
|
||||
'msb_bus': self.msb,
|
||||
'lsb_bus': self.lsb,
|
||||
'mask': ','.join(mask),
|
||||
'width': self.obj.width-1,
|
||||
'field_type': self.field_type
|
||||
}
|
||||
))
|
||||
|
||||
if self.obj.get_property('sw') in (AccessType.rw, AccessType.r):
|
||||
for i, alias in enumerate(self.path_underscored_vec):
|
||||
self.rtl_footer.append(self.process_yaml(
|
||||
Field.templ_dict['external_rd_assignments'],
|
||||
{'path': alias,
|
||||
'path_wo_field': self.path_wo_field_vec[i],
|
||||
'genvars': self.genvars_str,
|
||||
'field_type': self.field_type
|
||||
}
|
||||
))
|
||||
|
||||
def create_internal_rtl(self):
|
||||
# Not all access types are required and the order might differ
|
||||
# depending on what types are defined and what precedence is
|
||||
# set. Therefore, first add all RTL into a dictionary and
|
||||
@ -990,6 +1038,9 @@ class Field(Component):
|
||||
# Create full name
|
||||
self.path_wo_field = '__'.join(self.path.split('.', -1)[0:-1])
|
||||
|
||||
self.path_underscored_vec = []
|
||||
self.path_wo_field_vec = []
|
||||
|
||||
# Save dimensions of unpacked dimension
|
||||
self.array_dimensions = array_dimensions
|
||||
self.total_array_dimensions = array_dimensions
|
||||
@ -1051,6 +1102,7 @@ class Field(Component):
|
||||
rst_active = self.rst['active'],
|
||||
rst_type = self.rst['type'],
|
||||
misc_flags = misc_flags if misc_flags else '-',
|
||||
external = self.config['external'],
|
||||
lsb = self.obj.lsb,
|
||||
msb = self.obj.msb,
|
||||
path_wo_field = self.path_wo_field)
|
||||
|
@ -29,6 +29,8 @@ class Register(Component):
|
||||
# Save and/or process important variables
|
||||
self.__process_variables(obj, parents_dimensions, parents_stride, glbl_settings)
|
||||
|
||||
self.config['external'] = obj.external
|
||||
|
||||
# Create RTL for fields of initial, non-alias register
|
||||
for field in obj.fields():
|
||||
# Use range to save field in an array. Reason is, names are allowed to
|
||||
@ -37,7 +39,7 @@ class Register(Component):
|
||||
|
||||
self.children[field_range] = Field(field,
|
||||
self.total_array_dimensions,
|
||||
config,
|
||||
self.config,
|
||||
glbl_settings)
|
||||
|
||||
if not config['disable_sanity']:
|
||||
@ -45,7 +47,10 @@ class Register(Component):
|
||||
|
||||
def create_rtl(self):
|
||||
# Create RTL of children
|
||||
[x.create_rtl() for x in self.children.values()]
|
||||
if self.config['external']:
|
||||
[x.create_external_rtl() for x in self.children.values()]
|
||||
else:
|
||||
[x.create_internal_rtl() for x in self.children.values()]
|
||||
|
||||
# Create generate block for register and add comment
|
||||
if self.dimensions and not self.generate_active:
|
||||
@ -123,6 +128,8 @@ class Register(Component):
|
||||
|
||||
empty_bits = accesswidth - current_bit + 1
|
||||
|
||||
no_reads = not list_of_fields
|
||||
|
||||
if empty_bits > 0:
|
||||
list_of_fields.append("{}'b0".format(empty_bits))
|
||||
|
||||
@ -149,16 +156,88 @@ class Register(Component):
|
||||
# Return an error if *no* read or *no* write can be succesful.
|
||||
# If some bits cannot be read/write but others are succesful, don't return
|
||||
# an error.
|
||||
#
|
||||
# Furthermore, consider an error indication that is set for external registers
|
||||
bytes_read_format = ["b2r.byte_en[{}]".format(x) for x in list(map(str, bytes_read))]
|
||||
bytes_written_format = ["b2r.byte_en[{}]".format(x) for x in list(map(str, bytes_written))]
|
||||
|
||||
sw_err_condition = self.process_yaml(
|
||||
sw_err_condition_vec = []
|
||||
|
||||
sw_err_condition_vec.append(self.process_yaml(
|
||||
Register.templ_dict['sw_err_condition'],
|
||||
{'rd_byte_list_ored':
|
||||
' || '.join(bytes_read_format) if bytes_read else "1'b0",
|
||||
'wr_byte_list_ored':
|
||||
' || '.join(bytes_written_format) if bytes_written else "1'b0"}
|
||||
)
|
||||
)
|
||||
|
||||
if self.config['external']:
|
||||
if bytes_read:
|
||||
for field in self.children.values():
|
||||
sw_err_condition_vec.append(self.process_yaml(
|
||||
Register.templ_dict['external_err_condition'],
|
||||
{'path': '__'.join([na_map[0], field.name]),
|
||||
'genvars': self.genvars_str,
|
||||
'rd_or_wr': 'r'}
|
||||
)
|
||||
)
|
||||
|
||||
if bytes_written:
|
||||
for field in self.children.values():
|
||||
sw_err_condition_vec.append(self.process_yaml(
|
||||
Register.templ_dict['external_err_condition'],
|
||||
{'path': '__'.join([na_map[0], field.name]),
|
||||
'genvars': self.genvars_str,
|
||||
'rd_or_wr': 'w'}
|
||||
)
|
||||
)
|
||||
|
||||
sw_err_condition = ' || '.join(sw_err_condition_vec)
|
||||
|
||||
# If registers are implemented in RTL, they will be ready immediately. However,
|
||||
# if they are defined as 'external', there might be some delay
|
||||
if self.config['external']:
|
||||
if bytes_read:
|
||||
sw_rdy_condition_vec = ['(']
|
||||
|
||||
for field in self.children.values():
|
||||
sw_rdy_condition_vec.append(self.process_yaml(
|
||||
Register.templ_dict['external_rdy_condition'],
|
||||
{'path': '__'.join([na_map[0], field.name]),
|
||||
'genvars': self.genvars_str,
|
||||
'rd_or_wr': 'r'}
|
||||
)
|
||||
)
|
||||
|
||||
sw_rdy_condition_vec.append(' && ')
|
||||
|
||||
sw_rdy_condition_vec.pop()
|
||||
sw_rdy_condition_vec.append(' && b2r.r_vld)')
|
||||
|
||||
if bytes_read and bytes_written:
|
||||
sw_rdy_condition_vec.append(' || ')
|
||||
|
||||
if bytes_written:
|
||||
sw_rdy_condition_vec.append('(')
|
||||
|
||||
for field in self.children.values():
|
||||
sw_rdy_condition_vec.append(self.process_yaml(
|
||||
Register.templ_dict['external_rdy_condition'],
|
||||
{'path': '__'.join([na_map[0], field.name]),
|
||||
'genvars': self.genvars_str,
|
||||
'rd_or_wr': 'w'}
|
||||
)
|
||||
)
|
||||
|
||||
sw_rdy_condition_vec.append(' && ')
|
||||
|
||||
sw_rdy_condition_vec.pop()
|
||||
sw_rdy_condition_vec.append(' && b2r.w_vld)')
|
||||
|
||||
sw_rdy_condition = ''.join(sw_rdy_condition_vec)
|
||||
else:
|
||||
sw_rdy_condition = "1'b1"
|
||||
|
||||
# Assign all values
|
||||
self.rtl_footer.append(
|
||||
@ -167,8 +246,8 @@ class Register(Component):
|
||||
{'sw_data_assignment_var_name': self.sw_mux_assignment_var_name[-1][0],
|
||||
'sw_rdy_assignment_var_name': self.sw_mux_assignment_var_name[-1][1],
|
||||
'sw_err_assignment_var_name': self.sw_mux_assignment_var_name[-1][2],
|
||||
'genvars': self.genvars_str,
|
||||
'rdy_condition': "1'b1",
|
||||
'genvars': self.genvars_str if not no_reads else '',
|
||||
'rdy_condition': sw_rdy_condition,
|
||||
'err_condition': sw_err_condition,
|
||||
'list_of_fields': ', '.join(reversed(list_of_fields))}
|
||||
)
|
||||
|
@ -121,7 +121,7 @@ default_mux_case:
|
||||
rtl: |-
|
||||
default:
|
||||
begin
|
||||
// In case the address is not found, return an error
|
||||
// If the address is not found, return an error
|
||||
r2b.data = 0;
|
||||
r2b.err = 1;
|
||||
r2b.rdy = b2r.r_vld || b2r.w_vld;
|
||||
|
@ -170,6 +170,7 @@ field_comment:
|
||||
// sw = {sw_access} {sw_precedence}
|
||||
// reset : {rst_active} / {rst_type}
|
||||
// flags : {misc_flags}
|
||||
// external : {external}
|
||||
//-----------------------------------------------
|
||||
combo_operation_comment:
|
||||
rtl: |-
|
||||
@ -390,3 +391,80 @@ counter_underflow:
|
||||
output_ports:
|
||||
- name: '{path}_underflow'
|
||||
signal_type: 'logic'
|
||||
external_rd_assignments:
|
||||
rtl: |-
|
||||
|
||||
/**********************************
|
||||
* Handle external read interface *
|
||||
**********************************
|
||||
* The '{path}_ext_r_req' output will be asserted once a read
|
||||
* is requested by the bus and will stay high until '{path}_ext_r_ack'
|
||||
* gets set. During a read, byte-enables will be ignored.
|
||||
*
|
||||
* '{path}_ext_r_ack' shall be held 1'b1 until all fields in the register
|
||||
* acknowledged the read. In practice, this means until '{path}_ext_r_req'
|
||||
* goes back to 1'b0.
|
||||
*
|
||||
* If '{path}_ext_r_err' gets set, it must also be held during the
|
||||
* complete time '{path}_ext_r_ack' is high.
|
||||
*/
|
||||
// Actual data
|
||||
assign {path}_ext_r_req{genvars} = {path_wo_field}_sw_rd{genvars};
|
||||
|
||||
// Assign return from outside hardware
|
||||
assign {path}_q{genvars} = {path}_ext_r_data;
|
||||
|
||||
signals:
|
||||
- name: '{path}_q'
|
||||
signal_type: '{field_type}'
|
||||
input_ports:
|
||||
- name: '{path}_ext_r_data'
|
||||
signal_type: '{field_type}'
|
||||
- name: '{path}_ext_r_ack'
|
||||
signal_type: 'logic'
|
||||
- name: '{path}_ext_r_err'
|
||||
signal_type: 'logic'
|
||||
output_ports:
|
||||
- name: '{path}_ext_r_req'
|
||||
signal_type: 'logic'
|
||||
external_wr_assignments:
|
||||
rtl: |-
|
||||
|
||||
/***********************************
|
||||
* Handle external write interface *
|
||||
***********************************
|
||||
* The '{path}_ext_w_req' output will be asserted once a write
|
||||
* is requested by the bus and will stay high until '{path}_ext_w_ack'
|
||||
* gets set. During a write, hardware shall not touch any bits that
|
||||
* are not defined in '{path}_ext_w_mask'.
|
||||
*
|
||||
* '{path}_ext_w_ack' shall be held 1'b1 until all fields in the register
|
||||
* acknowledged the read. In practice, this means until '{path}_ext_w_req'
|
||||
* goes back to 1'b0.
|
||||
*
|
||||
* If '{path}_ext_w_err' gets set, it must also be held during the
|
||||
* complete time '{path}_ext_w_ack' is high.
|
||||
*/
|
||||
// Write request
|
||||
assign {path}_ext_w_req{genvars} = {path_wo_field}_sw_wr{genvars};
|
||||
|
||||
// Assign value from bus to output
|
||||
assign {path}_ext_w_data = b2r.data[{msb_bus}:{lsb_bus}];
|
||||
|
||||
// Provide bit-wise mask. Only bits set to 1'b1 shall be written
|
||||
assign {path}_ext_w_mask = {{{mask}}};
|
||||
output_ports:
|
||||
- name: '{path}_ext_w_req'
|
||||
signal_type: 'logic'
|
||||
- name: '{path}_ext_w_data'
|
||||
signal_type: '{field_type}'
|
||||
- name: '{path}_ext_w_mask'
|
||||
signal_type: 'logic [{width}:0]'
|
||||
input_ports:
|
||||
- name: '{path}_ext_w_ack'
|
||||
signal_type: 'logic'
|
||||
- name: '{path}_ext_w_err'
|
||||
signal_type: 'logic'
|
||||
external_wr_mask_segment:
|
||||
rtl: |-
|
||||
{{{width}{{b2r.byte_en[{idx}]}}}}
|
||||
|
@ -78,6 +78,7 @@ sw_err_condition:
|
||||
!((b2r.r_vld && ({rd_byte_list_ored})) || (b2r.w_vld && ({wr_byte_list_ored})))
|
||||
sw_data_assignment:
|
||||
rtl: |-
|
||||
|
||||
/**************************************
|
||||
* Assign all fields to signal to Mux *
|
||||
**************************************/
|
||||
@ -87,8 +88,37 @@ sw_data_assignment:
|
||||
// Internal registers are ready immediately
|
||||
assign {sw_rdy_assignment_var_name}{genvars} = {rdy_condition};
|
||||
|
||||
// Return an error if *no* read and *no* write wa be succesful.
|
||||
// If some bits cannot be read/write but others are succesful, don't return
|
||||
// an error. Hence, as long as one action can be succesful, no error will be
|
||||
// returned.
|
||||
// Return an error if *no* read and *no* write was succesful. If some bits
|
||||
// cannot be read/written but others are succesful, don't return and error
|
||||
// Hence, as long as one action can be succesful, no error will be returned.
|
||||
assign {sw_err_assignment_var_name}{genvars} = {err_condition};
|
||||
external_rtl_wr:
|
||||
rtl: |-
|
||||
// This output will be asserted once a read is requested and will
|
||||
// stay high until '{path}_ext_w_ack' gets set.
|
||||
assign {path}_ext_wr_req{genvars} = reg_ext1_sw_wr{genvars};
|
||||
output_ports:
|
||||
- name: '{path}_ext_wr_req'
|
||||
signal_type: 'logic'
|
||||
external_rtl_rd:
|
||||
rtl: |-
|
||||
// This output will be asserted once a read is requested and will
|
||||
// stay high until '{path}_ext_r_ack' gets set.
|
||||
assign {path}_ext_rd_req{genvars} = reg_ext1_sw_rd{genvars};
|
||||
output_ports:
|
||||
- name: '{path}_ext_rd_req'
|
||||
signal_type: 'logic'
|
||||
external_rdy_condition:
|
||||
rtl: |-
|
||||
{path}_ext_{rd_or_wr}_ack{genvars}
|
||||
input_ports:
|
||||
- name: '{path}_ext_{rd_or_wr}_ack'
|
||||
signal_type: 'logic'
|
||||
external_err_condition:
|
||||
rtl: |-
|
||||
({path}_ext_{rd_or_wr}_err{genvars} && {path}_ext_{rd_or_wr}_ack{genvars} && b2r.{rd_or_wr}_vld)
|
||||
input_ports:
|
||||
- name: '{path}_ext_{rd_or_wr}_err'
|
||||
signal_type: 'logic'
|
||||
- name: '{path}_ext_{rd_or_wr}_ack'
|
||||
signal_type: 'logic'
|
||||
|
46
tests/external_registers.rdl
Normal file
46
tests/external_registers.rdl
Normal file
@ -0,0 +1,46 @@
|
||||
addrmap external_registers {
|
||||
|
||||
reg {
|
||||
field {} f1 [15:0];
|
||||
field {} f2 [31:16];
|
||||
f2->sw = w;
|
||||
} reg_int0;
|
||||
|
||||
// The registers below shall not be implemented by RTL that is
|
||||
// generated by srdl2sv but rather, the tool shall provide an
|
||||
// interface to communicate with the bus.
|
||||
external reg {
|
||||
field {} f1 [15:0];
|
||||
field {} f2 [31:16];
|
||||
f2->sw = w;
|
||||
} reg_ext0;
|
||||
|
||||
// Multi-dimensional registers must work
|
||||
external reg {
|
||||
field {} f1 [15:0];
|
||||
field {} f2 [31:20];
|
||||
f2->sw = w;
|
||||
} reg_ext1 [2];
|
||||
|
||||
external reg {
|
||||
field {} f1 [14:3];
|
||||
field {} f2 [31:20];
|
||||
f1->sw = rw;
|
||||
f2->sw = rw;
|
||||
} reg_ext2 [2];
|
||||
|
||||
// Add an alias to verify that alias capabilities work fine for
|
||||
// external registers
|
||||
reg reg_ext2_alias_t {
|
||||
field {} f1 [14:3];
|
||||
f1->sw = rw;
|
||||
};
|
||||
|
||||
external alias reg_ext2 reg_ext2_alias_t reg_ext2_alias;
|
||||
|
||||
reg {
|
||||
field {} f1 [15:0];
|
||||
field {} f2 [31:16];
|
||||
f2->sw = w;
|
||||
} reg_int1 [2];
|
||||
};
|
Loading…
Reference in New Issue
Block a user