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:
Dennis Potter 2021-09-06 18:48:37 -07:00
parent a3b6e1caf8
commit 24d5534037
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
7 changed files with 302 additions and 17 deletions

View File

@ -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)

View File

@ -44,6 +44,7 @@ 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
if not self.config['external']:
self.__add_always_ff()
self.__add_hw_access()
self.__add_combo()
@ -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)

View File

@ -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))}
)

View File

@ -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;

View File

@ -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}]}}}}

View File

@ -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'

View 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];
};