diff --git a/srdl2sv/components/component.py b/srdl2sv/components/component.py index fe3bf33..fb2efc6 100644 --- a/srdl2sv/components/component.py +++ b/srdl2sv/components/component.py @@ -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) diff --git a/srdl2sv/components/field.py b/srdl2sv/components/field.py index 7f36891..1c3d6d2 100644 --- a/srdl2sv/components/field.py +++ b/srdl2sv/components/field.py @@ -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) diff --git a/srdl2sv/components/register.py b/srdl2sv/components/register.py index 11c8d4a..c9074d4 100644 --- a/srdl2sv/components/register.py +++ b/srdl2sv/components/register.py @@ -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))} ) diff --git a/srdl2sv/components/templates/addrmap.yaml b/srdl2sv/components/templates/addrmap.yaml index 13d8a89..1098a47 100644 --- a/srdl2sv/components/templates/addrmap.yaml +++ b/srdl2sv/components/templates/addrmap.yaml @@ -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; diff --git a/srdl2sv/components/templates/fields.yaml b/srdl2sv/components/templates/fields.yaml index 32cbc12..2892520 100644 --- a/srdl2sv/components/templates/fields.yaml +++ b/srdl2sv/components/templates/fields.yaml @@ -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}]}}}} diff --git a/srdl2sv/components/templates/register.yaml b/srdl2sv/components/templates/register.yaml index 3dd4481..d3a47f4 100644 --- a/srdl2sv/components/templates/register.yaml +++ b/srdl2sv/components/templates/register.yaml @@ -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' diff --git a/tests/external_registers.rdl b/tests/external_registers.rdl new file mode 100644 index 0000000..f874293 --- /dev/null +++ b/tests/external_registers.rdl @@ -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]; +};