Skip to content

Commit

Permalink
addr_decode: Add generic address decoder (#58)
Browse files Browse the repository at this point in the history
* added address map decoder
* fixed typos
* fix alignment
* added pull req suggestions and a sample struct
* fix parameter
* fixed typos
* added tb for addr_decoder
* changed trigger condition for assumptions
  • Loading branch information
WRoenninger authored and zarubaf committed Dec 9, 2019
1 parent b786dd2 commit 13767c8
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sources:
# Files in level 1 only depend on files in level 0, files in level 2 on files in levels 1 and 0,
# etc. Files within a level are ordered alphabetically.
# Level 0
- src/addr_decode.sv
- src/cdc_2phase.sv
- src/cf_math_pkg.sv
- src/clk_div.sv
Expand Down Expand Up @@ -69,6 +70,7 @@ sources:

- target: test
files:
- test/addr_decode_tb.sv
- test/cb_filter_tb.sv
- test/cdc_2phase_tb.sv
- test/cdc_fifo_tb.sv
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## Unreleased

### Fixed
### Added
- Added address map decoder module

### Fixed
- Handle degenerated `lzc` with `WIDTH == 1`

## 1.14.0 - 2019-10-08
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Please note that cells with status *deprecated* are not to be used for new desig

| Name | Description | Status | Superseded By |
| :--------------------------- | :----------------------------------------------------------------------------- | :------------- | :------------ |
| `addr_decode ` | Address map decoder | active | |
| `binary_to_gray` | Binary to gray code converter | active | |
| `find_first_one` | Leading-one finder / leading-zero counter | *deprecated* | `lzc` |
| `gray_to_binary` | Gray code to binary converter | active | |
Expand Down
137 changes: 137 additions & 0 deletions src/addr_decode.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// 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 <[email protected]>

// 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.
//
// The address decoder expects three fields in `rule_t`:
//
// typedef struct packed {
// int unsigned idx;
// addr_t start_addr;
// addr_t end_addr;
// } 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
//
// 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.
//
// 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`.
//
// 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 #(
parameter int unsigned NoIndices = 1, // number indices in rules
parameter int unsigned NoRules = 1, // total number of rules
parameter type addr_t = logic, // address type
parameter type rule_t = logic, // has to be overridden, see above!
// DEPENDENT PARAMETERS DO NOT OVERWRITE!
parameter type idx_t = logic [$clog2(NoIndices)-1:0] // index type
) (
input addr_t addr_i, // address to decode
input rule_t [NoRules-1:0] addr_map_i, // address map: rule with the highest position wins
output idx_t idx_o, // decoded index
output logic dec_valid_o, // decode is valid
output logic dec_error_o, // decode is not valid
// Default index mapping enable
input logic en_default_idx_i, // enable default port mapping
input idx_t default_idx_i // default port index
);

logic [NoRules-1:0] matched_rules; // purely for address map debugging

always_comb begin
// 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;

// 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
matched_rules[i] = 1'b1;
dec_valid_o = 1'b1;
dec_error_o = 1'b0;
idx_o = idx_t'(addr_map_i[i].idx);
end
end
end

// Assumptions and assertions
`ifndef VERILATOR
// 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"));
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.
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> 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
end
end
end
// pragma translate_on
`endif
endmodule
148 changes: 148 additions & 0 deletions test/addr_decode_tb.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2019 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// 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 <[email protected]>

// test bench for addr_decode module

timeunit 1ns/1ns;

module addr_decode_tb;
localparam int unsigned NoIndices = 2;
localparam int unsigned NoRules = 3;
localparam int unsigned AddrWidth = 12;


typedef logic [AddrWidth-1:0] addr_t;
typedef logic [$clog2(NoIndices)-1:0] idx_t;
// struct to be used in the tb
typedef struct packed {
int unsigned idx;
addr_t start_addr;
addr_t end_addr;
} tb_rule_t;

// normal map
localparam tb_rule_t [NoRules-1:0] map_0 = '{
'{idx: 32'd0, start_addr: 12'h000, end_addr: 12'h010},
'{idx: 32'd1, start_addr: 12'h010, end_addr: 12'h020},
'{idx: 32'd0, start_addr: 12'hF00, end_addr: 12'hFFF}
};

// overlapping map
localparam tb_rule_t [NoRules-1:0] map_1 = '{
'{idx: 32'd0, start_addr: 12'h000, end_addr: 12'h010},
'{idx: 32'd1, start_addr: 12'h00D, end_addr: 12'h020},
'{idx: 32'd1, start_addr: 12'h100, end_addr: 12'hFFF}
};

// DUT signal definitions
addr_t addr; // input address
tb_rule_t [NoRules-1:0] addr_map; // input address map
idx_t idx; // output index
logic dec_valid, dec_error; // output flags
logic en_default_idx; // default enable
idx_t default_idx; // default index

longint unsigned passed_checks = 0;
longint unsigned failed_checks = 0;

// application of stimuli
initial begin : stimulus
passed_checks <= 0;
failed_checks <= 0;
addr_map <= map_0;
en_default_idx <= 1'b0;
default_idx <= idx_t'(1);
#500;

// count over all addresses
$info("Start address application");
for (int i = 0; i < 2**AddrWidth; i++) begin
addr <= addr_t'(i);
#1;
end

$info("Change addr map to an overlapping one expect warning");
addr_map <= map_1;
#100;
$info("Change addr map back and enable default decode to idx 1");
addr_map <= map_0;
en_default_idx <= 1'b1;
#100;

// count over all addresses
for (int i = 0; i < 2**AddrWidth; i++) begin
addr <= addr_t'(i);
#1;
end
#500

$info("Finished Simulation");
$display("Passed: %d", passed_checks);
$display("Failed: %d", failed_checks);
$stop();
end

// checker assertions, these assertion get triggered every time the input address changes
// serves as model for the address decoder
always @(addr) #0 begin : proc_check_decode
for (int unsigned i = 0; i < NoRules; i++) begin
if ((addr >= addr_map[i].start_addr) && (addr < addr_map[i].end_addr )) begin
// decode should pass
check_decode: assert (idx == addr_map[i].idx) passed_checks++; else begin
failed_checks++;
$warning("Decoder did not decode correctly.");
end
check_valid: assert (dec_valid == 1'b1 && dec_error == 1'b0) passed_checks++; else begin
failed_checks++;
$warning("Unexpected decode flag on assumed valid decode.");
end
end else begin
// check for the right decode error
if (dec_valid == 1'b0) begin
if (en_default_idx) begin
check_default: assert (default_idx == idx) passed_checks++; else begin
failed_checks++;
$warning("Enabled default index, however wrong default decoding.");
end
check_flags: assert (dec_error == 1'b0) passed_checks++; else begin
failed_checks++;
$warning("Unexpected decode flags on default decode enabled.");
end
end else begin
check_error: assert (dec_error == 1'b1) passed_checks++; else begin
failed_checks++;
$warning("Unexpected decode flags on assumed decode error.");
end
end
end
end
end
end

// DUT instantiation
addr_decode #(
.NoIndices ( NoIndices ), // number indices in rules
.NoRules ( NoRules ), // total number of rules
.addr_t ( addr_t ), // address type
.rule_t ( tb_rule_t ) // has to be overridden, see above!
) i_addr_decode_dut (
.addr_i ( addr ), // address to decode
.addr_map_i ( addr_map ), // address map: rule with the highest position wins
.idx_o ( idx ), // decoded index
.dec_valid_o ( dec_valid ), // decode is valid
.dec_error_o ( dec_error ), // decode is not valid
// Default index mapping enable
.en_default_idx_i( en_default_idx ), // enable default port mapping
.default_idx_i ( default_idx ) // default port index
);
endmodule

32 changes: 32 additions & 0 deletions test/waves/addr_decode_tb.wave.do
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate -divider {Address Input}
add wave -noupdate -label {INPUT ADDRESS} /addr_decode_tb/i_addr_decode_dut/addr_i
add wave -noupdate -label {ADDRESS MAP} /addr_decode_tb/i_addr_decode_dut/addr_map_i
add wave -noupdate -divider {Index output}
add wave -noupdate -label {DECODED INDEX} /addr_decode_tb/i_addr_decode_dut/idx_o
add wave -noupdate -label {DECODE VALID} /addr_decode_tb/i_addr_decode_dut/dec_valid_o
add wave -noupdate -label {DECODE ERROR} /addr_decode_tb/i_addr_decode_dut/dec_error_o
add wave -noupdate -divider {Default decode input}
add wave -noupdate -label {DEFAULT ENABLE} /addr_decode_tb/i_addr_decode_dut/en_default_idx_i
add wave -noupdate -label {DEFAULT INDEX} /addr_decode_tb/i_addr_decode_dut/default_idx_i
add wave -noupdate -divider {Matched rules}
add wave -noupdate -label {MATCHED RULES} /addr_decode_tb/i_addr_decode_dut/matched_rules
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {0 ns} 0}
quietly wave cursor active 0
configure wave -namecolwidth 146
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 0
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ns
update
WaveRestoreZoom {0 ns} {22958 ns}

0 comments on commit 13767c8

Please sign in to comment.