diff --git a/src/multiaddr_decode.sv b/src/multiaddr_decode.sv index 90a43a0d..0cbcbb1d 100644 --- a/src/multiaddr_decode.sv +++ b/src/multiaddr_decode.sv @@ -8,26 +8,26 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -// Author: Wolfgang Roenninger +// Author: Luca Colagrande -/// Address Decoder: Maps the input address combinatorially to an index. -/// The address map `addr_map_i` is a packed array of rule_t structs. -/// The ranges of any two rules may overlap. If so, the rule at the higher (more significant) -/// position in `addr_map_i` prevails. +/// Multi-address Decoder: Combinational module which takes an address set +/// in {addr, mask} representation and returns a bit mask `select_o` indicating which +/// address map rules in `addr_map_i` it matches. /// -/// There can be an arbitrary number of address rules. There can be multiple -/// ranges defined for the same index. The start address has to be less than the end address. +/// An address set is a set of addresses. The {addr, mask} pair is one possible way of encoding +/// an address set. Asserted bits in the mask indicate that the corresponding bit in addr can +/// be treated as a "don't care", meaning it can assume any value (0 or 1) to produce a valid +/// address in the address set. /// -/// There is the possibility to add a default mapping: -/// `en_default_idx_i`: Driving this port to `1'b1` maps all input addresses -/// for which no rule in `addr_map_i` exists to the default index specified by -/// `default_idx_i`. In this case, `dec_error_o` is always `1'b0`. +/// The address map `addr_map_i` is a packed array of rule_t structs. Each rule is itself an +/// address set. /// -/// Assertions: The module checks every time there is a change in the address mapping -/// if the resulting map is valid. It fatals if `start_addr` is higher than `end_addr` -/// or if a mapping targets an index that is outside the number of allowed indices. -/// It issues warnings if the address regions of any two mappings overlap. -module addr_decode #( +/// For each rule the decoder checks if there is an address in the {`addr_i`, `mask_i`} address +/// set which belongs to the address set of the rule. If so, the corresponding bit in `select_o` +/// is set. +/// For each rule, it also returns the subset of addresses in {`addr_i`, `mask_i`} which +/// match the rule {`addr_o[i]`, `mask_o[i]`}. +module multiaddr_decode #( /// Highest index which can happen in a rule. parameter int unsigned NoIndices = 32'd0, /// Total number of rules. @@ -39,43 +39,43 @@ module addr_decode #( /// /// typedef struct packed { /// int unsigned idx; - /// addr_t start_addr; - /// addr_t end_addr; + /// addr_t addr; + /// addr_t mask; /// } rule_t; /// - /// - `idx`: index of the rule, has to be < `NoIndices` - /// - `start_addr`: start address of the range the rule describes, value is included in range - /// - `end_addr`: end address of the range the rule describes, value is NOT included in range - parameter type rule_t = logic, - /// Dependent parameter, do **not** overwite! + /// - `idx`: index of the rule, has to be < `NoIndices`. + /// - `addr`: any address in the address space described by the rule + /// - `mask`: a bitmask of the same length as the address which transforms the address + /// above in a multi-address encoding. A '1' in this mask indicates that the + /// corresponding bit in address can take any value and it will still be part + /// of this rule's address space. /// - /// Width of the `idx_o` output port. - parameter int unsigned IdxWidth = cf_math_pkg::idx_width(NoIndices), - /// Dependent parameter, do **not** overwite! - /// - /// Type of the `idx_o` output port. - parameter type idx_t = logic [IdxWidth-1:0] + /// {addr, mask} is an alternative representation to the typical interval [start, end) + /// representation for a collection of addresses. With {addr, mask} we can represent contiguous + /// intervals of the form [start, end) so long that the latter satisfies the following properties: + /// - the length of the interval (end - start) is a power of 2 (i.e. 2^N for some integer N) + /// - the offset of the interval (start) is a multiple of the length + /// (i.e. M*2^N for some integer M) + /// When these properties are satisfied we can go from the [start, end) representation + /// to the {addr, mask} representation (and viceversa) using the following equations: + /// - mask = {'0, {log2(end - start){1'b1}}} + /// - addr = start + parameter type rule_t = logic ) ( - /// Address to decode. - input addr_t addr_i, - /// Address map: rule with the highest array position wins on collision - input rule_t [NoRules-1:0] addr_map_i, - /// Decoded index. - output idx_t idx_o, + /// Multi-address to decode. + input addr_t addr_i, + input addr_t mask_i, + /// Address map. + input rule_t [NoRules-1:0] addr_map_i, + /// Decoded indices. + output logic [NoIndices-1:0] select_o, + /// Decoded multi-address. + output addr_t [NoIndices-1:0] addr_o, + output addr_t [NoIndices-1:0] mask_o, /// Decode is valid. - output logic dec_valid_o, + output logic dec_valid_o, /// Decode is not valid, no matching rule found. - output logic dec_error_o, - /// Enable default port mapping. - /// - /// When not used, tie to `0`. - input logic en_default_idx_i, - /// Default port index. - /// - /// When `en_default_idx_i` is `1`, this will be the index when no rule matches. - /// - /// When not used, tie to `0`. - input idx_t default_idx_i + output logic dec_error_o ); logic [NoRules-1:0] matched_rules; // purely for address map debugging @@ -84,16 +84,37 @@ module addr_decode #( // default assignments matched_rules = '0; dec_valid_o = 1'b0; - dec_error_o = (en_default_idx_i) ? 1'b0 : 1'b1; - idx_o = (en_default_idx_i) ? default_idx_i : '0; + dec_error_o = 1'b1; + select_o = '0; + addr_o = '0; + mask_o = '0; - // match the rules + // Match the rules for (int unsigned i = 0; i < NoRules; i++) begin - if ((addr_i >= addr_map_i[i].start_addr) && (addr_i < addr_map_i[i].end_addr)) begin + automatic int unsigned idx = addr_map_i[i].idx; + // We have a match if at least one address of the input + // address set is a part of the rule's address set. + // We have this condition when all bits in the input address match + // all bits in `addr_map_i[i].addr`, with possible exception + // of those bits which are either masked in the input address + // or in the addrmap rule. In other words, any bit which is masked + // either in the input address or in the addrmap rule is treated as a don't care + automatic addr_t dont_care = mask_i | addr_map_i[i].mask; + automatic addr_t matching_bits = ~(addr_i ^ addr_map_i[i].addr); + automatic logic match = &(dont_care | matching_bits); + if (match) begin matched_rules[i] = 1'b1; dec_valid_o = 1'b1; dec_error_o = 1'b0; - idx_o = idx_t'(addr_map_i[i].idx); + select_o[idx] |= 1'b1; + // When there is a partial match, i.e. only a subset of the input address set + // falls in the address set of the rule, we want to return this subset + // {addr_o, mask_o}. + // Bits which are masked in the input address but not in the addrmap rule + // are resolved to the value in the addrmap, and are thus unmasked in the + // output address set. All other bits remain masked or unchanged. + mask_o[idx] = mask_i & addr_map_i[i].mask; + addr_o[idx] = (~mask_i & addr_i) | (mask_i & addr_map_i[i].addr); end end end @@ -103,58 +124,30 @@ module addr_decode #( `ifndef XSIM // pragma translate_off initial begin : proc_check_parameters - assume ($bits(addr_i) == $bits(addr_map_i[0].start_addr)) else - $warning($sformatf("Input address has %d bits and address map has %d bits.", - $bits(addr_i), $bits(addr_map_i[0].start_addr))); assume (NoRules > 0) else $fatal(1, $sformatf("At least one rule needed")); - assume (NoIndices > 0) else - $fatal(1, $sformatf("At least one index needed")); + assume ($bits(addr_i) == $bits(addr_map_i[0].addr)) else + $warning($sformatf("Input address has %d bits and address map has %d bits.", + $bits(addr_i), $bits(addr_map_i[0].addr))); end - assert final ($onehot0(matched_rules)) else - $warning("More than one bit set in the one-hot signal, matched_rules"); - // These following assumptions check the validity of the address map. - // The assumptions gets generated for each distinct pair of rules. - // Each assumption is present two times, as they rely on one rules being - // effectively ordered. Only one of the rules with the same function is - // active at a time for a given pair. - // check_start: Enforces a smaller start than end address. - // check_idx: Enforces a valid index in the rule. - // check_overlap: Warns if there are overlapping address regions. + // check_idx: Enforces a valid index in the rule. always @(addr_map_i) #0 begin : proc_check_addr_map if (!$isunknown(addr_map_i)) begin for (int unsigned i = 0; i < NoRules; i++) begin - check_start : assume (addr_map_i[i].start_addr < addr_map_i[i].end_addr) else - $fatal(1, $sformatf("This rule has a higher start than end address!!!\n\ - Violating rule %d.\n\ - Rule> IDX: %h START: %h END: %h\n\ - #####################################################", - i ,addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr)); // check the SLV ids check_idx : assume (addr_map_i[i].idx < NoIndices) else $fatal(1, $sformatf("This rule has a IDX that is not allowed!!!\n\ Violating rule %d.\n\ - Rule> IDX: %h START: %h END: %h\n\ + Rule> IDX: %h\n\ Rule> MAX_IDX: %h\n\ #####################################################", - i, addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr, - (NoIndices-1))); - for (int unsigned j = i + 1; j < NoRules; j++) begin - // overlap check - check_overlap : assume (!((addr_map_i[j].start_addr < addr_map_i[i].end_addr) && - (addr_map_i[j].end_addr > addr_map_i[i].start_addr))) else - $warning($sformatf("Overlapping address region found!!!\n\ - Rule %d: IDX: %h START: %h END: %h\n\ - Rule %d: IDX: %h START: %h END: %h\n\ - #####################################################", - i, addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr, - j, addr_map_i[j].idx, addr_map_i[j].start_addr, addr_map_i[j].end_addr)); - end + i, addr_map_i[i].idx, (NoIndices-1))); end end end + // pragma translate_on `endif `endif