Skip to content

Commit

Permalink
multiaddr_decode: Add logic implementing multi-address decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
colluca committed Sep 19, 2023
1 parent e8ef41d commit 593a14d
Showing 1 changed file with 80 additions and 87 deletions.
167 changes: 80 additions & 87 deletions src/multiaddr_decode.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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 <wroennin@ethz.ch>
// Author: Luca Colagrande <colluca@ethz.ch>

/// 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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 593a14d

Please sign in to comment.