module addr_decode_dync #( parameter int unsigned NoIndices = 32'd0, parameter int unsigned NoRules = 32'd0, parameter type addr_t = logic, parameter type rule_t = logic, parameter bit Napot = 0, parameter int unsigned IdxWidth = cf_math_pkg::idx_width(NoIndices), parameter type idx_t = logic [IdxWidth-1:0] ) ( input addr_t addr_i, input rule_t [NoRules-1:0] addr_map_i, output idx_t idx_o, output logic dec_valid_o, output logic dec_error_o, input logic en_default_idx_i, input idx_t default_idx_i, input logic config_ongoing_i );
基本的な設計方針としては、入力アドレスaddr_iを複数のルールaddr_map_i[NoRules-1:0]と順次比較し、マッチしたルールのインデックスを出力する。
NoIndices で出力先のインデックスを示し NoRules でルールの数を示す (このパラメータが別々に用意されているのは、複数のルールで1つのインデックスを指定できるようにするためだ)。
config_ongoing_i信号により動的な設定変更中は出力を無効化できる。
通常の範囲比較モード(Napot=0)とNAPOTマスク比較モード(Napot=1)の2つの動作モードをサポートする。
addr_map_i は rule_t の配列として定義される。 rule_t はデフォルトでは logic 型として定義されているが、これではもちろん動かないので、適切な型を設定する必要がある。 rule_t に搭載されること想定されるメンバは:
idx: ルールのインデックスを示すstart_addr: 範囲アドレスのスタート位置end_addr: 範囲アドレスの終了位置
/// 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 /// if `end_addr == '0` end of address space is assumed
end_addr == 0 に設定すると、最後のアドレスを指定せず、アドレス範囲の最大までが指定されたことになる。
NoRules=3の場合のパラメータと動作
以下の設定例で動作を解析する:
NoIndices = 4 NoRules = 3 addr_t = logic[15:0] (16ビットアドレス) Napot = 0 (通常の範囲比較モード) ルール定義 (rule_t): addr_map_i[0]: idx=0, start_addr=16'h0000, end_addr=16'h4000 // 16KB addr_map_i[1]: idx=1, start_addr=16'h4000, end_addr=16'h8000 // 16KB addr_map_i[2]: idx=2, start_addr=16'h8000, end_addr=16'hC000 // 16KB
内部信号は matched_rules[2:0] で各ルールのマッチング状態を保持する。
アドレスデコードの詳細動作
初期化フェーズ
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;
en_default_idx_iの状態により初期値が変わる。つまり、en_default_idx_i が有効な場合には、すべてのルールに引っかからない場合には default_idx_i が設定され、エラー状態にはならない。
en_default_idx_i = 0の場合:dec_error_o = 1(エラー状態)idx_o = 0
en_default_idx_i = 1, default_idx_i = 3の場合:dec_error_o = 0(エラーなし)idx_o = 3(デフォルトインデックス)
例1: addr_i = 16'h5000, config_ongoing_i = 1'b0 の場合
- ループ i=0 (Rule[0]のチェック)
- ループ0: 条件チェック:
!Napot && (addr_i >= addr_map_i[0].start_addr) && ((addr_i < addr_map_i[0].end_addr) || (addr_map_i[0].end_addr == '0))
= !0 && (16'h5000 >= 16'h0000) && (16'h5000 < 16'h4000) = 1 && 1 && 0 = 0 → マッチせず
- ループ0: 状態:
matched_rules[0] = 0 dec_valid_o = 0 dec_error_o = 1 (初期値のまま) idx_o = 0 (初期値のまま)
- ループ i=1 (Rule[1]のチェック)
- ループ1: 条件チェック:
!Napot && (addr_i >= addr_map_i[1].start_addr) && ((addr_i < addr_map_i[1].end_addr) || (addr_map_i[1].end_addr == '0))
= 1 && (16'h5000 >= 16'h4000) && (16'h5000 < 16'h8000) = 1 && 1 && 1 = 1 → マッチ!
- ループ1: マッチング処理:
matched_rules[1] = ~config_ongoing_i = ~0 = 1 dec_valid_o = ~config_ongoing_i = 1 dec_error_o = 1'b0 idx_o = config_ongoing_i ? default_idx_i : idx_t'(addr_map_i[1].idx) = 0 ? default_idx_i : 1 = 1
- ループ0: 状態更新:
matched_rules = 3'b010 dec_valid_o = 1 dec_error_o = 0 idx_o = 1
- ループ i=2 (Rule[2]のチェック)
- ループ2: 条件チェック:
= 1 && (16'h5000 >= 16'h8000) && (16'h5000 < 16'hC000) = 1 && 0 && 1 = 0 → マッチせず
- 状態: 変化なし
- 最終出力:
matched_rules = 3'b010 dec_valid_o = 1 dec_error_o = 0 idx_o = 1
addr_i = 16'h6000, config_ongoing_i = 1 の場合 (設定変更中)
config_ongoing_i == 1 の場合、 dec_valid_o が強制的に無効化される。
重複するルールがある場合 (優先順位の動作)
forループで順次上書きされるため、配列の後ろ側(インデックスの大きい方)のルールが優先される。
NAPOTモード (Napot=1) の詳細動作
NAPOTモードでは、start_addrがベースアドレス、end_addrがマスクとして機能する。
マッチング条件の詳細
if ( Napot && (addr_map_i[i].start_addr & addr_map_i[i].end_addr) == (addr_i & addr_map_i[i].end_addr) ) begin
例: NAPOT モードでの 4KB 領域のデコード
addr_map_i[0]: idx=0, base=16'h4000, mask=16'hF000 ← 4KB境界でアライン
addr_i = 16'h4800 の場合:
- ループ i=0:
- ループ0: 条件チェック:
Napot && (addr_map_i[0].start_addr & addr_map_i[0].end_addr) == (addr_i & addr_map_i[0].end_addr)
- ループ0: 計算:
(base & mask) = 16'h4000 & 16'hF000 = 16'h4000 (addr_i & mask) = 16'h4800 & 16'hF000 = 16'h4000 16'h4000 == 16'h4000 → True, マッチ
- ループ0: 結果:
matched_rules[0] = 1 dec_valid_o = 1 idx_o = 0
例: NAPOT モードでマッチしない場合
同じルール設定で: addr_i = 16'h5800 の場合:
- 計算:
(base & mask) = 16'h4000 & 16'hF000 = 16'h4000 (addr_i & mask) = 16'h5800 & 16'hF000 = 16'h5000 16'h4000 == 16'h5000 → False, マッチせず
- 結果:
matched_rules[0] = 0 dec_valid_o = 0 (初期値のまま) idx_o = default_idx_i or 0 (en_default_idx_i による)
アサーションによる検証
1. matched_rulesのワンホット性チェック
`ASSERT_FINAL(more_than_1_bit_set, $onehot0(matched_rules) || config_ongoing_i, "More than one bit set in the one-hot signal, matched_rules")
$onehot0(matched_rules)は、matched_rulesが0個または1個のビットしかセットされていないことを確認する。複数のルールがマッチする場合、警告が出る(ただし動作自体は後のルールが優先される)。
例:
matched_rules = 3'b000→ OK (0個)matched_rules = 3'b010→ OK (1個)matched_rules = 3'b011→ NG (2個、警告)
2. start_addr < end_addr のチェック (通常モード)
`ASSUME_I(check_start, Napot || addr_map_i[i].start_addr < addr_map_i[i].end_addr || addr_map_i[i].end_addr == '0, ...)
通常モード(Napot=0)では、start_addr < end_addrまたはend_addr == '0でなければならない。
例:
start_addr=16'h4000, end_addr=16'h8000→ OK (0x4000 < 0x8000)start_addr=16'h8000, end_addr=16'h4000→ NG (エラー)
3. インデックスの範囲チェック
`ASSUME_I(check_idx, addr_map_i[i].idx < NoIndices, ...)
すべてのルールのidxがNoIndices未満であることを確認。
4. 重複領域の警告 (通常モード)
`A`ASSUME_I(check_overlap, Napot || !((addr_map_i[j].start_addr < addr_map_i[i].end_addr) && (addr_map_i[j].end_addr > addr_map_i[i].start_addr)) || ...)
通常モードで重複する領域がある場合、警告を出す。
例:
- Rule[0]:
[0x4000, 0x8000), Rule[1]:[0x8000, 0xC000)→ OK (重複なし) - Rule[0]:
[0x4000, 0x8000), Rule[1]:[0x6000, 0xA000)→ 警告 (重複あり)