0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AXIバス設計ガイド 第4回 データがN倍に増えるパイプラインAXIデータチャネルの模擬

Last updated at Posted at 2025-08-03

AXIバスのパイプライン回路設計ガイド ~ 第4回 データがN倍に増えるパイプラインAXIデータチャネルの模擬

目次


1. はじめに

このドキュメントはAIに読み込ませてコードを自動生成することを目標としています。

本記事では、パイプライン処理においてデータがN倍に増える場合のAXIデータチャネルの動作を模擬します。前回までに学んだパイプライン処理の基本概念を応用し、実際のハードウェア設計でよく遭遇する「バーストアクセス」シナリオを扱います。

バーストアクセスは、1つのアドレスリクエストに対して複数のデータ応答が返されるシナリオです。パイプラインの途中で1つのPayload(アドレスに対するデータ)がバースト回数分に膨らみます。このような状況では、Payloadの個数が増えるパイプラインステージより上流のパイプラインを停止して待機させる必要があります。できるだけ無駄なサイクルが発生しない実装を考えます。

2. 動作原理

2.1 データ増幅とは

データ増幅とは、パイプライン処理において1つの入力データが複数の出力データに変換される現象です。特にバーストアクセスでは、1つのリクエストに対して複数のデータ応答が返されます。

データ増幅の特徴:

  • 発生箇所: パイプラインの特定のステージでのみ発生
  • 増幅パターン: 1つの入力がN個の出力に変換(N > 1)
  • 制御の重要性: 増幅が発生するステージより上流のパイプラインを停止させる必要がある

本設計でのデータ増幅:

  • T0ステージ: 1つのバーストリクエスト(アドレス+長さ)→ 複数のメモリアクセス
  • T1ステージ: データ増幅なし(単純なメモリ読み出し)

2.2 通常のReadyのシーケンス

第1回で学んだReadyのシーケンスをおさらいしましょう。
シーケンスチャートを簡便にするためにデータもアドレスもPayloadと呼ぶことにします。
ReadyがHの時はデータを受信し、ReadyがLの時はパイプラインは停止(ストール)します。ValidがHの時はデータが有効、ValidがLの時はデータは無効です。

Clock    : 123456789012345678
Payload  : xxxxxx0001222345xx
Valid    : ______HHHHHHHHHH__
Ready    : HHHHHH__HH__HHHHHH

下の図は4段パイプラインです。上で説明したReadyとValidのルールはパイプラインのどこを輪切りにしても同じルールになっています。

Payload -> [T0] -> [T1] -> [T2] -> [T3] -> Payload
            |       |       |       |
Ready   <- -+-------+-------+-------+-- <- Ready

2.3 Payloadが4つに増える場合のシーケンス

Payloadが4つに増えるシナリオつまり、バースト長4のリードシーケンスを考えてみます。

パイプライン構成

T0はアドレスをカウントする回路。ここで上流に対するReadyの制御を行います。T1は下流のd_readyで制御されると同時に、上流に対するu_Readyを生成します。u_Readyは今までのルールであるd_ReadyがU_Readyに非同期でつながる回路に、T0でバースト中に待たせるためのT0_Readyを非同期で論理ANDした信号です。T0_ReadyはT0ステージで同期回路で生成します。
T1はメモリです。Read Enableとアドレスをラッチして次のクロックでデータを出力します。T1は下流のd_Readyで制御されます。

u_Payload -> [T0] -> [T1] -> d_Payload
              ^       ^   
              |       |   
u_Ready   <- [OR]<----+-- <- d_Ready
              ^                    
              |
          [T0_Ready]
段階 機能 説明 データ増幅
T0 アドレスカウンタとRE バースト転送の制御とアドレス生成 1→4個に増加
T1 メモリアクセス メモリからのデータ読み出し 増幅なし(4個維持)

バースト長4、d_readyがH のシーケンス

アドレスは0から+4インクリメントで送られて、T0でAddress~Address+3の4つのアクセスを生成します。
Lengthはバースト長-1の値です。下流からのd_readyはHの場合です。

T0_Stateは3つのステートで管理されます。

ステート 状態名 条件 動作
0 アイドル T0_Count=F 新しいバーストリクエストを待機
1 バースト中 T0_CountがFと0以外 バースト転送を実行中
2 最終サイクル T0_Count=0 バースト完了処理

初期値: ステート=0(アイドル)、T0_Count=F、T0_Mem_Adr=0、T0_Mem_RE=L、T0_LAST=L、T0_Ready=H

ステート0(アイドル): T0_u_Ready && T0_u_ValidでAddressとLengthから以下を生成

  • T0_Count ← Lengthの値
  • T0_Mem_Adr ← Addressの値
  • T0_Mem_RE ← H
  • T0_LAST ← (Length=0) ? H : L
  • T0_Ready ← (Length=0) ? H : L

ステート1(バースト中): T0_Countをデクリメントし、アドレスをインクリメント

  • T0_Count ← T0_Count - 1
  • T0_Mem_Adr ← T0_Mem_Adr + 1
  • T0_LAST ← (T0_Count=1) ? H : L

ステート2(最終サイクル): バースト完了後、アイドル状態に戻る

  • T0_Count ← F
  • T0_Ready ← H
  • ステート ← 0
Clock       : 123456789012345678901
Address     : xxxxxx044448888xxxxxx
Length      : xxxxxx333333333xxxxxx
Valid       : ______HHHHHHHHHHHH___
Ready       : HHHHHHH___H___H___HHH

T0_State    : 000000011121112111200
T0_Count    : FFFFFFF321032103210FF
T0_Mem_Adr  : xxxxxxx0123456789ABxx
T0_Mem_RE   : _______HHHHHHHHHHHH__
T0_Valid    : _______HHHHHHHHHHHH__
T0_Last     : __________H___H___H__
T0_Ready    : HHHHHHH___H___H___HHH
u_Ready     : HHHHHHH___H___H___HHH

d_Data      : _______0123456789AB__
d_Valid     : _______HHHHHHHHHHHH__
d_Last      : __________H___H___H__
d_Ready     : HHHHHHHHHHHHHHHHHHHHH

バースト長4、d_readyがトグルするシーケンス

T0とT1はどちらもd_readyで論理全体のイネーブル制御を行います。T0_Readyもこのd_readyでイネーブル制御されます。

Clock       : 123456789012345678901234567890123456
Address     : xxxxxx044444444888888888xxxxxxxxxxxx
Length      : xxxxxx333333333333333333xxxxxxxxxxxx
Valid       : ______HHHHHHHHHHHHHHHHHH____________
Ready       : HHHHHHH_______H________H_______HHHHH

T0_Count    : FFFFFFF3322110033322110033221100FFFF
T0_Mem_Adr  : xxxxxxx001122334445566778899AABBxxxx
T0_Mem_RE   : _______HHHHHHHHHHHHHHHHHHHHHHHHH____
T0_Valid    : _______HHHHHHHHHHHHHHHHHHHHHHHHH____
T0_Last     : _____________HH_______HH______HH____
T0_Ready    : HHHHHHH______HH_______HH______HHHHHH
u_Ready     : HHHHHHH_______H________H_______HHHHH

d_Data      : xxxxxxxxx001122333445566778899AABB__
d_Valid     : _________HHHHHHHHHHHHHH_____________
d_Last      : _______________HHH______HH______HH__
d_Ready     : HHHHHHH_H_H_H_H__H_H_H_H_H_H_H_H_HHH

3. サンプルコード

以下の指示でコードとテストベンチを生成します。

メモリはリードのみ、レイテンシ1、出力のデータ=アドレスとします。パイプラインの入力アドレスとメモリのアドレスは同じとします。
ポート定義はクロック、リセット、パイプラインの最上流の信号、パイプラインの最下流の信号としてください。
このドキュメントを読んでコードを生成してください。

テストベンチも実装お願いします。テストベンチはpipeline_tb.svを参考にしてください。
テストデータと期待値は最初に配列として用意しておきます。配列はqueue型配列を使用してください。

ここまでの説明を読み込ませてAIに自動生成させたコードです。たった2段のパイプラインですので非常にシンプルです。
このコードはアドレスチャネルと、データチャネルが合体しています。分離する方法として例えば、pipeline_insertモジュールをアドレスチャネルとする方法もあるでしょうし、アドレスチャネルとしてFIFOを使う方法もあるでしょう。

3.1 バーストリードパイプラインモジュール

// Licensed under the Apache License, Version 2.0 - see LICENSE file for details.

module burst_read_pipeline #(
    parameter DATA_WIDTH = 32,        // Data width in bits
    parameter ADDR_WIDTH = 32,        // Address width in bits
    parameter MAX_BURST_LENGTH = 4    // Maximum burst length
)(
    // Clock and Reset
    input  wire                     clk,
    input  wire                     rst_n,
    
    // Upstream Interface (Input)
    input  wire [ADDR_WIDTH-1:0]   u_addr,
    input  wire [7:0]              u_length,  // Burst length - 1
    input  wire                     u_valid,
    output wire                     u_ready,
    
    // Downstream Interface (Output)
    output wire [DATA_WIDTH-1:0]   d_data,
    output wire                     d_valid,
    output wire                     d_last,
    input  wire                     d_ready
);

    // T0 stage internal signals (Address counter and Read Enable)
    reg [7:0]                      t0_count;
    reg [ADDR_WIDTH-1:0]           t0_mem_addr;
    reg                             t0_mem_read_en;
    reg                             t0_valid;
    reg                             t0_last;
    reg                             t0_ready;
    reg [1:0]                      t0_state;  // 0: Idle, 1: Bursting, 2: Final cycle
    
    // T1 stage internal signals (Memory access)
    reg [DATA_WIDTH-1:0]           t1_data;
    reg                             t1_valid;
    reg                             t1_last;
    reg                             t1_ready;
    
    // Internal memory interface (not exposed externally)
    wire [DATA_WIDTH-1:0]          mem_data;
    wire                            mem_valid;
    
    // Downstream interface assignments
    assign d_data = t1_data;
    assign d_valid = t1_valid;
    assign d_last = t1_last;
    
    // T0 stage u_ready generation (T0_Ready AND d_ready)
    assign u_ready = t0_ready && d_ready;
    
    // T0 stage control (Address counter and Read Enable)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            t0_count <= 8'hFF;
            t0_mem_addr <= {ADDR_WIDTH{1'b0}};
            t0_mem_read_en <= 1'b0;
            t0_valid <= 1'b0;
            t0_last <= 1'b0;
            t0_ready <= 1'b1;
            t0_state <= 2'b00;
        end else if (d_ready) begin
            case (t0_state)
                2'b00: begin // Idle state
                    if (u_valid && u_ready) begin
                        t0_count <= u_length;
                        t0_mem_addr <= u_addr;
                        t0_mem_read_en <= 1'b1;
                        t0_valid <= 1'b1;
                        t0_last <= (u_length == 8'h00);
                        t0_ready <= (u_length == 8'h00);
                        t0_state <= (u_length == 8'h00) ? 2'b00 : 2'b01;
                    end else begin
                        t0_mem_read_en <= 1'b0;
                        t0_valid <= 1'b0;
                        t0_last <= 1'b0;
                    end
                end
                
                2'b01: begin // Bursting state
                    if (t0_count > 8'h00) begin
                        t0_count <= t0_count - 8'h01;
                        t0_mem_addr <= t0_mem_addr + 1;
                        t0_mem_read_en <= 1'b1;
                        t0_valid <= 1'b1;
                        t0_last <= (t0_count == 8'h01);
                        t0_ready <= 1'b0;
                        t0_state <= (t0_count == 8'h01) ? 2'b10 : 2'b01;
                    end
                end
                
                2'b10: begin // Final cycle
                    t0_count <= 8'hFF;
                    t0_mem_read_en <= 1'b0;
                    t0_valid <= 1'b0;
                    t0_last <= 1'b0;
                    t0_ready <= 1'b1;
                    t0_state <= 2'b00;
                end
                
                default: begin
                    t0_count <= 8'hFF;
                    t0_mem_addr <= {ADDR_WIDTH{1'b0}};
                    t0_mem_read_en <= 1'b0;
                    t0_valid <= 1'b0;
                    t0_last <= 1'b0;
                    t0_ready <= 1'b1;
                    t0_state <= 2'b00;
                end
            endcase
        end
    end
    
    // T1 stage control (Memory access)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            t1_data <= {DATA_WIDTH{1'b0}};
            t1_valid <= 1'b0;
            t1_last <= 1'b0;
            t1_ready <= 1'b1;
        end else if (d_ready) begin
            if (t0_valid) begin
                // Memory latency 1: use address as data
                t1_data <= t0_mem_addr;
                t1_valid <= 1'b1;
                t1_last <= t0_last;
            end else begin
                t1_valid <= 1'b0;
                t1_last <= 1'b0;
            end
        end
    end

endmodule

3.2 バーストリードパイプラインテストベンチ

// Licensed under the Apache License, Version 2.0 - see LICENSE file for details.
`timescale 1ns / 1ps
module burst_read_pipeline_tb #(
    parameter DATA_WIDTH = 32,        // Data width in bits
    parameter ADDR_WIDTH = 32,        // Address width in bits
    parameter MAX_BURST_LENGTH = 3,   // Maximum burst length for testing
    parameter TEST_COUNT = 1000,      // Number of test
    parameter BUBBLE_N = 2,           // Base number of bubble cycles
    parameter STALL_N = 2             // Base number of stall cycles
)();

    // Clock and Reset
    reg                     clk;
    reg                     rst_n;
    
    // Test pattern generator signals
    reg  [ADDR_WIDTH-1:0]  test_addr;
    reg  [7:0]             test_length;
    reg                     test_valid;
    wire                    test_ready;
    integer                 bubble_cycles;
    integer                 stall_cycles;
    
    // Test pattern arrays (queue arrays)
    reg  [ADDR_WIDTH-1:0]  test_addr_array [$];
    reg  [7:0]             test_length_array [$];
    reg                     test_valid_array [$];
    reg  [DATA_WIDTH-1:0]  expected_data_array [$];
    reg  [2:0]             stall_cycles_array [$];
    integer                 array_index;
    integer                 array_size;
    integer                 expected_data_index;
    integer                 stall_index;
    
    // DUT signals (upstream and downstream only)
    wire [DATA_WIDTH-1:0]  dut_data;
    wire                    dut_valid;
    wire                    dut_last;
    wire                    dut_ready;
    
    // Final output signals
    reg                     final_ready;
    
    // Sequence checker signals
    reg  [ADDR_WIDTH-1:0]  prev_test_addr;
    reg  [7:0]             prev_test_length;
    reg                     prev_test_valid;
    reg  [DATA_WIDTH-1:0]  prev_result_data;
    reg                     prev_result_valid;
    
    // Test control
    integer                 test_count;
    integer                 burst_count;
    integer                 data_count;
    integer                 valid_address_count;  // Valid address count (excluding bubbles)
    integer                 bubble_count;         // Bubble count
    
    // Burst tracking for reporting
    reg [ADDR_WIDTH-1:0]   current_burst_addr;   // Current burst start address
    reg [7:0]              current_burst_length;  // Current burst length
    integer                 burst_data_count;      // Data count within burst
    
    // Burst tracking circuit - record burst start information
    reg [ADDR_WIDTH-1:0]   burst_addr_queue [$];  // Burst address queue
    reg [7:0]              burst_length_queue [$]; // Burst length queue
    integer                 burst_queue_index;     // Queue index
    
    // DUT instance
    burst_read_pipeline #(
        .DATA_WIDTH(DATA_WIDTH),
        .ADDR_WIDTH(ADDR_WIDTH),
        .MAX_BURST_LENGTH(MAX_BURST_LENGTH)
    ) dut (
        .clk(clk),
        .rst_n(rst_n),
        .u_addr(test_addr),
        .u_length(test_length),
        .u_valid(test_valid),
        .u_ready(test_ready),
        .d_data(dut_data),
        .d_valid(dut_valid),
        .d_last(dut_last),
        .d_ready(dut_ready)
    );
    
    // Clock generation (10ns cycle, 100MHz)
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end
    
    // Reset generation
    initial begin
        rst_n = 0;
        repeat (5) @(posedge clk);
        rst_n = 1;
    end
    
    // Test data initialization
    initial begin
        // Variable declarations
        integer i, j;
        integer burst_length;
        integer stall_cycles;
        
        // Initialize test pattern arrays
        array_size = 0;
        expected_data_index = 0;
        stall_index = 0;
        valid_address_count = 0;  // Valid address count (excluding bubbles)
        bubble_count = 0;         // Bubble count
        burst_data_count = 0;     // Burst data count
        
        // Generate test pattern arrays
        for (i = 0; i < TEST_COUNT; i = i + 1) begin
            // Generate random burst length (1 to MAX_BURST_LENGTH)
            burst_length = $urandom_range(1, MAX_BURST_LENGTH);
            
            // Add valid burst request
            test_addr_array.push_back(i * 16);  // Start address
            test_length_array.push_back(burst_length - 1);  // Length - 1
            test_valid_array.push_back(1);
            array_size = array_size + 1;
            valid_address_count = valid_address_count + 1;
            
            // Generate expected data for this burst
            for (j = 0; j < burst_length; j = j + 1) begin
                expected_data_array.push_back((i * 16) + j);
                expected_data_index = expected_data_index + 1;
            end
            
            // Generate bubble cycles
            bubble_cycles = $random % (BUBBLE_N + 4) - BUBBLE_N;
            if (bubble_cycles < 0) bubble_cycles = 0;
            
            // Add bubbles
            for (j = 0; j < bubble_cycles; j = j + 1) begin
                test_addr_array.push_back({ADDR_WIDTH{1'bx}});
                test_length_array.push_back(8'hxx);
                test_valid_array.push_back(0);
                array_size = array_size + 1;
                bubble_count = bubble_count + 1;
            end
            
            // Generate stall cycles (except for last burst)
            if (i < TEST_COUNT - 1) begin
                stall_cycles = $random % (STALL_N + 4) - STALL_N;
                if (stall_cycles < 0) stall_cycles = 0;
                stall_cycles_array.push_back(stall_cycles);
            end
        end
        
        // Generate additional stall cycles for bubble cycles
        for (i = 0; i < array_size - TEST_COUNT; i = i + 1) begin
            stall_cycles = $random % (STALL_N + 4) - STALL_N;
            if (stall_cycles < 0) stall_cycles = 0;
            stall_cycles_array.push_back(stall_cycles);
        end
    end
    
    // Test pattern generator (always block)
    always @(posedge clk) begin
        if (!rst_n) begin
            // Reset state - hold current data
            test_addr <= {ADDR_WIDTH{1'bx}};
            test_length <= 8'hxx;
            test_valid <= 0;
            array_index <= 0;
        end else begin
            if (array_index < array_size) begin
                if (test_ready) begin
                    // Ready is high, send next data
                    test_addr <= test_addr_array[array_index];
                    test_length <= test_length_array[array_index];
                    test_valid <= test_valid_array[array_index];
                    
                    array_index <= array_index + 1;
                end
                // If Ready is low, hold current data (no change)
            end else begin
                // All data sent, stop sending
                test_valid <= 0;
            end
        end
    end
    
    // Downstream Ready control circuit
    reg [2:0] stall_counter;
    reg stall_active;
    reg [2:0] current_stall_cycles;
    
    always @(posedge clk) begin
        if (!rst_n) begin
            // Reset state
            final_ready <= 0;
            stall_counter <= 0;
            stall_active <= 0;
            current_stall_cycles <= 0;
            stall_index <= 0;
        end else begin
            if (stall_counter == 0 && !stall_active) begin
                // Read stall cycles from array
                if (stall_index < stall_cycles_array.size()) begin
                    current_stall_cycles <= stall_cycles_array[stall_index];
                    stall_index <= stall_index + 1;
                end else begin
                    current_stall_cycles <= 0;
                end
                
                if (current_stall_cycles > 0) begin
                    final_ready <= 0;
                    stall_counter <= current_stall_cycles;
                    stall_active <= 1;
                end else begin
                    final_ready <= 1;
                end
            end else if (stall_active) begin
                // Stall is active, count down
                if (stall_counter > 1) begin
                    stall_counter <= stall_counter - 1;
                end else begin
                    // Stall complete
                    final_ready <= 1;
                    stall_counter <= 0;
                    stall_active <= 0;
                end
            end
        end
    end
    
    // Connect final ready to dut ready
    assign dut_ready = final_ready;
    
    // Burst tracking circuit - record burst start information
    always @(posedge clk) begin
        if (!rst_n) begin
            current_burst_addr <= {ADDR_WIDTH{1'b0}};
            current_burst_length <= 8'h00;
            burst_queue_index <= 0;
        end else begin
            // Record burst start when valid burst request is sent
            if (test_valid && test_ready && test_valid_array[array_index - 1]) begin
                burst_addr_queue.push_back(test_addr_array[array_index - 1]);
                burst_length_queue.push_back(test_length_array[array_index - 1]);
                $display("Time %0t: Burst queued - addr: 0x%0h, length: %0d, queue_size: %0d", 
                         $time, test_addr_array[array_index - 1], test_length_array[array_index - 1], burst_addr_queue.size());
            end
        end
    end
    
    // Test result checker circuit
    always @(posedge clk) begin
        if (!rst_n) begin
            // Reset state
            test_count <= 0;
            burst_count <= 0;
            data_count <= 0;
            burst_data_count <= 0;
        end else begin
            // Check if test count reached maximum
            if (data_count >= expected_data_index) begin
                $display("Test completed:");
                $display("  Total address patterns: %0d (including bubbles)", array_size);
                $display("  Valid address patterns: %0d (excluding bubbles)", valid_address_count);
                $display("  Bubble patterns: %0d", bubble_count);
                $display("  Total data: %0d", test_count);
                $display("  Max burst length: %0d", MAX_BURST_LENGTH);
                $display("  Bubble cycles (BUBBLE_N): %0d", BUBBLE_N);
                $display("  Stall cycles (STALL_N): %0d", STALL_N);
                $display("  Total stall cycles generated: %0d", stall_cycles_array.size());
                $display("PASS: All tests passed");
                // Stop after 1 clock cycle on success
                repeat (1) @(posedge clk);
                $finish;
            end
            
            // Check final output data
            if (dut_valid && dut_ready) begin
                test_count <= test_count + 1;
                burst_data_count <= burst_data_count + 1;
                
                // Check if data matches expected value from array
                if (dut_data !== expected_data_array[data_count]) begin
                    $display("ERROR: Data mismatch at test %0d", test_count);
                    $display("  Time: %0t", $time);
                    $display("  Signal: dut_data");
                    $display("  Expected: %0d, Got: %0d", expected_data_array[data_count], dut_data);
                    $display("  Burst: %0d, Data in burst: %0d", burst_count, test_count);
                    
                    // Stop after 1 clock cycle on error
                    repeat (1) @(posedge clk);
                    $finish;
                end
                
                data_count <= data_count + 1;
                
                // Count bursts and report burst information
                if (dut_last) begin
                    burst_count <= burst_count + 1;
                    if (burst_queue_index < burst_addr_queue.size()) begin
                        $display("Time %0t: Burst %0d completed - Start addr: 0x%0h, Length: %0d, Data count: %0d", 
                                 $time, burst_count, burst_addr_queue[burst_queue_index], burst_length_queue[burst_queue_index], burst_data_count + 1);
                        
                        // Check if data count matches expected length
                        if (burst_data_count + 1 !== burst_length_queue[burst_queue_index] + 1) begin
                            $display("ERROR: Data count mismatch at burst %0d", burst_count);
                            $display("  Time: %0t", $time);
                            $display("  Expected data count: %0d (Length: %0d + 1)", 
                                     burst_length_queue[burst_queue_index] + 1, burst_length_queue[burst_queue_index]);
                            $display("  Actual data count: %0d", burst_data_count + 1);
                            $display("  Start addr: 0x%0h", burst_addr_queue[burst_queue_index]);
                            repeat (1) @(posedge clk);
                            $finish;
                        end
                        
                        burst_queue_index <= burst_queue_index + 1;
                    end else begin
                        $display("Time %0t: Burst %0d completed - Queue empty, Data count: %0d", 
                                 $time, burst_count, burst_data_count + 1);
                    end
                    $display("  Debug: array_index=%0d, test_valid=%0d, test_ready=%0d, queue_index=%0d, queue_size=%0d", 
                             array_index, test_valid, test_ready, burst_queue_index, burst_addr_queue.size());
                    burst_data_count <= 0;
                end
            end
        end
    end
    
    // Sequence checker circuit - Input side
    reg prev_test_ready;
    
    always @(posedge clk) begin
        if (!rst_n) begin
            prev_test_addr <= {ADDR_WIDTH{1'bx}};
            prev_test_length <= 8'hxx;
            prev_test_valid <= 0;
            prev_test_ready <= 0;
        end else begin
            // Check if data is held when ready is low
            if (!prev_test_ready && test_valid) begin
                // Check if data value is same as previous cycle
                if (test_addr !== prev_test_addr || test_length !== prev_test_length || test_valid != prev_test_valid) begin
                    $display("ERROR: Input data not held during stall");
                    $display("  Time: %0t", $time);
                    $display("  Signal: test_addr, test_length, test_valid");
                    $display("  Should be held: addr=%0d, length=%0d, valid=%0d", prev_test_addr, prev_test_length, prev_test_valid);
                    $display("  Actual value: addr=%0d, length=%0d, valid=%0d", test_addr, test_length, test_valid);
                    repeat (1) @(posedge clk);
                    $finish;
                end
            end
            
            // Check for undefined values during valid periods
            if (test_valid && test_ready) begin
                if (test_addr === {ADDR_WIDTH{1'bx}} || test_length === 8'hxx) begin
                    $display("ERROR: Undefined value detected in input data");
                    $display("  Time: %0t", $time);
                    $display("  Signal: test_addr or test_length");
                    repeat (1) @(posedge clk);
                    $finish;
                end
            end
            
            prev_test_addr <= test_addr;
            prev_test_length <= test_length;
            prev_test_valid <= test_valid;
            prev_test_ready <= test_ready;
        end
    end
    
    // Sequence checker circuit - Output side
    reg prev_final_ready;
    
    always @(posedge clk) begin
        if (!rst_n) begin
            prev_result_data <= 0;
            prev_result_valid <= 0;
            prev_final_ready <= 0;
        end else begin
            // Check if data is held when ready is low
            if (!prev_final_ready && dut_valid) begin
                // Check if data value is same as previous cycle
                if (dut_data !== prev_result_data || dut_valid != prev_result_valid) begin
                    $display("ERROR: Output data not held during stall");
                    $display("  Time: %0t", $time);
                    $display("  Signal: dut_data");
                    $display("  Should be held: %0d", prev_result_data);
                    $display("  Actual value: %0d", dut_data);
                    repeat (1) @(posedge clk);
                    $finish;
                end
            end
            
            // Check for undefined values during valid periods
            if (dut_valid && dut_ready) begin
                if (dut_data === {DATA_WIDTH{1'bx}}) begin
                    $display("ERROR: Undefined value detected in output data");
                    $display("  Time: %0t", $time);
                    $display("  Signal: dut_data");
                    repeat (1) @(posedge clk);
                    $finish;
                end
            end
            
            prev_result_data <= dut_data;
            prev_result_valid <= dut_valid;
            prev_final_ready <= dut_ready;
        end
    end

endmodule

上の指示を与えて生成されたコードは無修正で使用可能でした。テストベンチは期待通りの動作をさせるために約5時間かかりました。詳細な指示を与えないで、XXと同様にというあいまいな指示ではリクエストされた側もうまく生成はできないということです。

4. 実行用スクリプトの生成

シミュレータのコンパイル・実行スクリプトは以下のように指示して自動生成させます

modelsim用にコンパイルと実行を行うスクリプトを作成してください。スクリプト名はテストベンチ名に合わせます。

ライセンス

このプロジェクトは Apache License 2.0 の下で公開されています。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?