diff --git a/Bender.yml b/Bender.yml index db99a04f..e09b657c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -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 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index acbe27b7..4feb5711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 794192bf..82b41079 100644 --- a/README.md +++ b/README.md @@ -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 | | diff --git a/src/addr_decode.sv b/src/addr_decode.sv new file mode 100644 index 00000000..58ad939f --- /dev/null +++ b/src/addr_decode.sv @@ -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 + +// 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 diff --git a/test/addr_decode_tb.sv b/test/addr_decode_tb.sv new file mode 100644 index 00000000..59781bcb --- /dev/null +++ b/test/addr_decode_tb.sv @@ -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 + +// 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 + diff --git a/test/waves/addr_decode_tb.wave.do b/test/waves/addr_decode_tb.wave.do new file mode 100644 index 00000000..5cf305b6 --- /dev/null +++ b/test/waves/addr_decode_tb.wave.do @@ -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}