Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Protocol Metrics Reference
  • Protocols Glossary
  • Reading Path
  • Protocol Fundamentals25
  • AMBA Family (APB / AHB / AXI)17
    • APB & AHB Basics
    • AXI Channels & Transfers
    • AXI-Lite & AXI-Stream
      • AXI-Lite & AXI-Stream: Concepts
      • AXI-Lite & AXI-Stream: Integration
      • AXI-Lite & AXI-Stream: Debug
      • AXI-Lite & AXI-Stream: Interview
    • AMBA Integration Debug
    • AMBA Family (APB / AHB / AXI): Tricky Q&A
  • Coherence Fabrics (ACE / CHI)17
  • Memory Interfaces (DDR / LPDDR / HBM)17
  • PCIe & CXL17
  • Embedded Peripherals (I2C / SPI / UART)17
  • High-Speed I/O (USB / Ethernet / MIPI)17
  • Protocol Verification & Compliance21
  • Protocols Interview Prep5
  • Interface Protocols Cheatsheet
Loading lesson

←AXI-Lite & AXI-Stream
  1. Topics
  2. Bus Protocols
  3. AMBA Family (APB / AHB / AXI)
  4. AXI-Lite & AXI-Stream
  5. AXI-Lite & AXI-Stream: Concepts
On this page

    Interface ProtocolsAll levels

    AXI-Lite & AXI-Stream: Concepts

    Concepts for AXI-Lite & AXI-Stream.

    Core concepts

    AXI4-Lite: control path simplicity

    Read first: AXI Channels & Transfers: independent channel acceptance

    AXI4-Lite removes burst and ID signals: each request describes one data transfer, and responses are returned in order. Implementations can still support more than one outstanding transfer within the protocol's ordering and buffering rules. The reduced signal/ordering contract can simplify endpoints, but throughput and complexity depend on the complete path.

    Register and configuration blocks are common AXI4-Lite users. A low-rate data mover could also use it if its single-transfer throughput, latency and outstanding behaviour meet the product requirement; high-bandwidth DMA commonly benefits from full AXI bursts. Choose by measured traffic and implementation cost rather than a blanket block-type ban.

    Use a local 32-bit Lite register-interface comparison, outside reset on a 100 MHz clock. This is a variant of the LEN_BYTES exercise, not a new connection in the base AXI-to-APB system. The register starts at zero. A single address at 0x4000_0008 pairs with WDATA=0x0000_0010 and WSTRB=1111. There is no WLAST port on Lite. The exercise target admits one write at a time but retains AW and W independently, so either can arrive first.

    Early W retained until AW arrives

    AW V/R, W V/R and B V/R are per-channel sampled VALID/READY. The chosen target commits once both items are present, then blocks new write admission until B completes. Its successful response is BRESP=OKAY.

    Edge

    AW V/R

    W V/R

    B V/R

    State after edge

    E1

    0/1

    1/1

    0/0

    Retain WDATA/strobes; register still 0

    E2

    1/1

    0/0

    0/0

    AW joins stored W; commit 0x10 once

    E3

    0/0

    0/0

    1/0

    Retain B response; register stays 0x10

    E4

    0/0

    0/0

    1/1

    Response accepted; admission can reopen

    What to notice

    • Independent AW/W storage is required by this target's admission policy; waiting for both to be valid in the same cycle would lose legal cases.

    • B backpressure must not repeat the register write or discard its pending response.

    Check yourself Could the target return B after E1 because the one Lite data beat has arrived?

    No. AW has not been accepted, so there is no paired write address. Lite retains the AXI4 requirement to accept both the address and data before offering the write response.

    • AWPROT and ARPROT carry protection attributes. AXI4-Lite has no AWCACHE or ARCACHE signals; its accesses have fixed Non-modifiable, Non-bufferable attributes, equivalent to AxCACHE=0b0000 on full AXI.

    • Write response (B) is required even for single-beat writes.

    • A memory-like target can honour WSTRB per byte. Register targets must document supported strobe handling, including their treatment of unsupported patterns.

    Lite attributes, strobes and completion

    AXI4-Lite B1.1 in Arm IHI 0022H defines a fixed 32- or 64-bit data width and single-transfer requests. AWPROT/ARPROT remain; AWCACHE/ARCACHE do not appear on the Lite interface. Its fixed attributes are Non-modifiable and Non-bufferable, equivalent to AxCACHE=0000 when discussing full AXI transactions. Do not invent a Lite AxSIZE signal: strobes select written bytes within the fixed data width.

    For this register exercise the target honours all four strobes. A sparse write changes enabled bytes only, and an all-zero-strobe write still receives a B response without changing LEN_BYTES. Read and write response streams are independent; sharing a register address does not itself create a universal cross-channel ordering promise. A software sequence must follow its platform's ordering and completion rules.

    Independent snapshots of a 32-bit register initially 0x11223344

    WDATA=0xAABBCCDD in each replay; lane 0 is bits 7:0. These byte-update results describe this target's chosen strobe contract.

    WSTRB

    New register value

    Required write response

    1111

    0xAABBCCDD

    One B

    0011

    0x1122CCDD

    One B

    0000

    0x11223344 unchanged

    One B

    What to notice: value changes to hexadecimal 10 and commits reaches one only after the request parts pair. BREADY stays low throughout this short window, so it does not show response retirement. dropped_value belongs to the deliberately faulty data-retention target.

    Check yourself Has the requester accepted B by the end of this capture, and did the correct register write occur?

    The register write occurred once, as value and commits show. B was not accepted because BREADY remains low; the response is still pending.

    Check yourself Does WSTRB=0000 mean the bridge may forget the accepted write and omit B?

    No. The accepted transaction still needs completion. In this exercise it updates no bytes and returns one OKAY response after the address/data prerequisites are satisfied.

    Check yourself How can independent Lite buffers be checked against early response and lost data?

    This write-only local target uses one AW slot, one W slot and one retained B. Its bench covers W-first, AW-first, simultaneous arrival, sparse/zero strobes and B stalls. A second target deliberately loses early data. A separate reusable response observer receives independently scripted legal AW-first, W-first and simultaneous traces, then three illegal B offers with AW, W or both missing; the generator does not read checker state. The test requires clear results for every legal trace, detection for every faulty trace and a reset/clean replay afterwards. The good target separately commits exactly four paired writes and returns four responses. The model omits AR/R and does not claim complete Lite compliance.

    lite_write.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    // Local 32-bit AXI4-Lite write-only exercise; one pending write and one register.
    // DROP_EARLY is a deliberate storage fault. AR/R, reset-in-traffic and full compliance are outside scope.
    `timescale 1ns/1ps
    module lite_write #(parameter DROP_EARLY = 0) (
      input wire clk, rst,
      input wire awvalid,
      output wire awready,
      input wire [31:0] awaddr,
      input wire wvalid,
      output wire wready,
      input wire [31:0] wdata,
      input wire [3:0] wstrb,
      output reg bvalid,
      input wire bready,
      output reg [1:0] bresp,
      output reg [31:0] value,
      output integer commits
    );
      reg aw_full, w_full;
      reg [31:0] saved_addr, saved_data;
      reg [3:0] saved_strb;
      wire aw_accept = awvalid && awready;
      wire w_accept = wvalid && wready;
      wire [31:0] address_now = aw_full ? saved_addr : awaddr;
      wire [31:0] data_now = w_full ? saved_data : wdata;
      wire [3:0] strb_now = w_full ? saved_strb : wstrb;
      assign awready = !aw_full && !bvalid;
      assign wready = !w_full && !bvalid;
      always @(posedge clk) begin
        if (rst) begin
          aw_full <= 0; w_full <= 0; bvalid <= 0; bresp <= 0; value <= 0; commits <= 0;
        end else begin
          if (aw_accept) begin aw_full <= 1; saved_addr <= awaddr; end
          if (w_accept) begin
            w_full <= 1; saved_strb <= wstrb;
            saved_data <= DROP_EARLY && !aw_full && !aw_accept ? 32'h0 : wdata;
          end
          if (!bvalid && (aw_full || aw_accept) && (w_full || w_accept)) begin
            bvalid <= 1;
            bresp <= address_now == 32'h4000_0008 ? 2'b00 : 2'b10;
            if (address_now == 32'h4000_0008) begin
              for (integer lane = 0; lane < 4; lane = lane + 1)
                if (strb_now[lane]) value[8*lane +: 8] <= data_now[8*lane +: 8];
              commits <= commits + 1;
            end
            aw_full <= 0; w_full <= 0;
          end
          if (bvalid && bready) bvalid <= 0;
        end
      end
    endmodule
    
    // Single-outstanding response prerequisite observer. Inputs are sampled offers
    // and acceptance events; fault stimulus never reads this observer's state.
    module lite_response_guard (
      input wire clk, rst, aw_fire, w_fire, bvalid, bready,
      output reg violation
    );
      reg have_aw, have_w;
      always @(posedge clk) begin
        if (rst) begin have_aw <= 0; have_w <= 0; violation <= 0; end
        else begin
          if (aw_fire) have_aw <= 1;
          if (w_fire) have_w <= 1;
          if (bvalid && !(have_aw && have_w)) violation <= 1;
          if (bvalid && bready) begin have_aw <= 0; have_w <= 0; end
        end
      end
    endmodule
    
    module tb;
      reg clk = 0;
      always #5 clk = ~clk;
      reg rst = 1, awvalid = 0, wvalid = 0, bready = 0;
      reg [31:0] awaddr = 32'h4000_0008, wdata = 0;
      reg [3:0] wstrb = 0;
      wire awready, wready, bvalid, drop_awready, drop_wready, drop_bvalid;
      wire [1:0] bresp, drop_bresp;
      wire [31:0] value, dropped_value;
      integer commits, drop_commits;
      integer aw_count = 0, w_count = 0, responses = 0;
      reg aw_seen = 0, w_seen = 0, lost_data_seen = 0;
      wire good_response_error, premature_seen;
      reg probe_rst = 1, probe_aw = 0, probe_w = 0, probe_b = 0, probe_ready = 0;
      lite_response_guard good_guard (
        .clk(clk), .rst(rst), .aw_fire(awvalid && awready), .w_fire(wvalid && wready),
        .bvalid(bvalid), .bready(bready), .violation(good_response_error)
      );
      lite_response_guard probe_guard (
        .clk(clk), .rst(probe_rst), .aw_fire(probe_aw), .w_fire(probe_w),
        .bvalid(probe_b), .bready(probe_ready), .violation(premature_seen)
      );
      lite_write good (.*);
      lite_write #(.DROP_EARLY(1)) dropped (
        .clk(clk), .rst(rst), .awvalid(awvalid), .awready(drop_awready), .awaddr(awaddr),
        .wvalid(wvalid), .wready(drop_wready), .wdata(wdata), .wstrb(wstrb),
        .bvalid(drop_bvalid), .bready(bready), .bresp(drop_bresp),
        .value(dropped_value), .commits(drop_commits)
      );
      always @(posedge clk) begin
        if (rst) begin
          aw_seen <= 0; w_seen <= 0; aw_count <= 0; w_count <= 0; responses <= 0;
        end else begin
          if (awvalid && awready) begin aw_seen <= 1; aw_count <= aw_count + 1; end
          if (wvalid && wready) begin w_seen <= 1; w_count <= w_count + 1; end
          if (bvalid && !(aw_seen && w_seen)) begin $display("FAIL: good target offered B before AW/W"); $finish; end
          if (good_response_error) begin $display("FAIL: good response guard flagged legal traffic"); $finish; end
          if (bvalid && bready) begin aw_seen <= 0; w_seen <= 0; responses <= responses + 1; end
          if (awready !== drop_awready || wready !== drop_wready || bvalid !== drop_bvalid || bresp !== drop_bresp) begin
            $display("FAIL: data-loss variant unexpectedly changed admission/response timing"); $finish;
          end
        end
      end
      task send_aw;
        begin
          @(negedge clk); awvalid = 1;
          @(posedge clk); if (!awready) begin $display("FAIL: test AW not accepted"); $finish; end
          @(negedge clk); awvalid = 0;
        end
      endtask
      task send_w(input [31:0] data, input [3:0] strb);
        begin
          @(negedge clk); wvalid = 1; wdata = data; wstrb = strb;
          @(posedge clk); if (!wready) begin $display("FAIL: test W not accepted"); $finish; end
          @(negedge clk); wvalid = 0; wdata = 0; wstrb = 0;
        end
      endtask
      task finish_b(input integer stalls, input [31:0] expected_value, input integer expected_commits);
        begin
          @(negedge clk);
          if (!bvalid || bresp !== 2'b00 || value !== expected_value || commits != expected_commits) begin
            $display("FAIL: bounded target did not create the expected OKAY write result"); $finish;
          end
          repeat (stalls) begin
            @(negedge clk);
            if (!bvalid || bresp != 0 || commits != expected_commits || value !== expected_value) begin
              $display("FAIL: stalled B changed response or repeated/lost write"); $finish;
            end
          end
          bready = 1;
          @(negedge clk); bready = 0;
          if (bvalid || value !== expected_value || commits != expected_commits) begin
            $display("FAIL: write value/count or B retirement"); $finish;
          end
        end
      endtask
      task probe_step(input a, input w, input b, input ready);
        begin
          @(negedge clk); probe_aw = a; probe_w = w; probe_b = b; probe_ready = ready;
          @(posedge clk); #1;
        end
      endtask
      task check_response_trace(input integer scenario, input expected_error);
        begin
          @(negedge clk); probe_rst = 1; probe_aw = 0; probe_w = 0; probe_b = 0; probe_ready = 0;
          @(posedge clk); #1;
          @(negedge clk); probe_rst = 0;
          // Explicit independent traffic: no signal here depends on guard state.
          case (scenario)
            0: begin probe_step(1,0,0,0); probe_step(0,1,0,0); end // AW first
            1: begin probe_step(0,1,0,0); probe_step(1,0,0,0); end // W first
            2: probe_step(1,1,0,0); // same-edge acceptance
            3: probe_step(0,1,0,0); // fault: missing AW
            4: probe_step(1,0,0,0); // fault: missing W
            5: probe_step(0,0,0,0); // fault: neither prerequisite
            default: begin $display("FAIL: unknown guard scenario"); $finish; end
          endcase
          probe_step(0,0,1,0); // offered response, even though receiver is stalled
          if (premature_seen !== expected_error) begin
            $display("FAIL: guard scenario=%0d expected=%0d actual=%0d", scenario, expected_error, premature_seen); $finish;
          end
          probe_step(0,0,1,1);
          probe_step(0,0,0,0);
          if (premature_seen !== expected_error) begin $display("FAIL: guard lost sticky result"); $finish; end
          $display("guard scenario=%0d error=%0d", scenario, premature_seen);
        end
      endtask
      initial begin
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        repeat (2) @(posedge clk);
        @(negedge clk); rst = 0;
        send_w(32'h10, 4'hf); send_aw(); finish_b(2, 32'h10, 1);
        lost_data_seen = dropped_value === 32'h0;
        send_aw(); send_w(32'h1122_3344, 4'hf); finish_b(0, 32'h1122_3344, 2);
        @(negedge clk); awvalid = 1; wvalid = 1; wdata = 32'haabb_ccdd; wstrb = 4'h3;
        @(posedge clk);
        if (!awready || !wready) begin $display("FAIL: simultaneous AW/W were not accepted"); $finish; end
        @(negedge clk); awvalid = 0; wvalid = 0; wdata = 0;
        finish_b(0, 32'h1122_ccdd, 3);
        send_w(32'hffff_ffff, 0); send_aw(); finish_b(0, 32'h1122_ccdd, 4);
        if (!lost_data_seen || good_response_error || aw_count != 4 || w_count != 4 || responses != 4) begin
          $display("FAIL: negative cases or exact-once traffic counts"); $finish;
        end
        check_response_trace(0, 0);
        check_response_trace(1, 0);
        check_response_trace(2, 0);
        check_response_trace(3, 1);
        check_response_trace(4, 1);
        check_response_trace(5, 1);
        check_response_trace(0, 0); // reset and clean replay after all faults
        $display("PASS: Lite target and independent response guard checked; three legal orders and three missing-prerequisite faults");
        $finish;
      end
    endmodule

    AXI-Stream: address-free data movement

    AXI4-Stream moves data without per-transfer addresses. TVALID and TREADY define beat acceptance. When implemented, TLAST can mark a packet boundary, TKEEP qualifies byte lanes, and TID/TDEST/TUSER carry stream, routing or application-defined context. The source and destination must agree which optional signals and semantics form their interface contract.

    This local example uses the AXI4-Stream subset in Arm IHI 0051B: one stream, 32-bit input, 16-bit output, TKEEP and TLAST present, no position bytes or packet interleaving. TSTRB, TID, TDEST and TUSER are absent under the agreed interface contract. Bytes are packed from lane 0 and the last input beat may be partial. These are exercise restrictions, not requirements on every AXI-Stream interface.

    A packet carries bytes 11,22,33,44,55 in that order. Its input transfers are TDATA=0x44332211/TKEEP=1111/TLAST=0 and TDATA=0x00000055/TKEEP=0001/TLAST=1. Count bytes only when TVALID && TREADY; retain data, keep mask and last flag together during a stall. TLAST high without acceptance does not end the received packet.

    Final input beat stalls before packet completion

    Input-side rising-edge observations outside reset; initially no bytes accepted. V/R means TVALID/TREADY.

    Edge

    V/R

    TDATA / TKEEP / TLAST

    Bytes accepted

    Packet ended?

    E1

    1/1

    44332211 / 1111 / 0

    11,22,33,44

    No

    E2

    1/0

    00000055 / 0001 / 1

    None

    No

    E3

    1/0

    00000055 / 0001 / 1

    None

    No

    E4

    1/1

    00000055 / 0001 / 1

    55

    Yes

    What to notice

    • Three visible cycles of the last offer contribute one byte, accepted once at E4.

    • Retain the final flag with its data and keep mask through a stall.

    Check yourself May the receiver count a completed packet at E2 because TLAST is high?

    No. TREADY is low at E2/E3. The final byte and TLAST are accepted together at E4.

    Width conversion preserves bytes and packet boundaries

    The 32-to-16 converter emits the same five bytes as three narrower transfers. Its output handshake is separate from its input handshake. The exercise buffers one input word and admits no new input while that word drains. It can omit an unused upper half whose lanes are null, while TLAST remains on the final emitted transfer for the packet.

    Compare the accepted byte sequence and accepted packet-end marker at both boundaries, not the raw beat counts. General converters also need contracts for position bytes, TID/TDEST, TUSER and zero-byte TLAST transfers; this packed-data exercise does not implement those cases.

    Expected accepted output sequence

    Transfer

    TDATA

    TKEEP

    TLAST

    Bytes

    First

    0x2211

    11

    0

    11,22

    Second

    0x4433

    11

    0

    33,44

    Third

    0x0055

    01

    1

    55

    What to notice: Use output handshakes to advance output_bytes through 2, 4 and 5. The retained output does not count again during a stall. TLAST identifies the final accepted output beat, not the first presentation of input TLAST.

    Check yourself Why does the final output add one byte instead of the output interface's full two-byte width?

    Its keep mask qualifies only the remaining byte. The accepted byte total reaches five and the packet count reaches one.

    Check yourself Could TLAST mark the second output transfer because it drains a full input word?

    No. That input word was not the packet's last word. Byte 55 still follows, so TLAST belongs on the third output transfer.

    Check yourself How can a bench distinguish correct downsizing from byte and packet-end loss?

    This bounded converter handles the shown packed-data subset. Its bench checks accepted output bytes against 11,22,33,44,55, verifies the partial final keep mask through byte counts, holds an output through two stalled edges and requires one packet end. Two matched-timing variants corrupt a byte or remove TLAST; each fault must be detected. It does not model arbitrary holes, position bytes, multiple streams or zero-byte packet ends.

    stream_downsize.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    // Packed-data exercise only: 32-to-16, one stream, no position bytes or zero-byte packet ends.
    // BUG_BYTE corrupts one output byte; BUG_LAST removes the packet-end marker.
    `timescale 1ns/1ps
    module stream_downsize #(parameter BUG_BYTE = 0, parameter BUG_LAST = 0) (
      input wire clk, rst,
      input wire in_valid,
      output wire in_ready,
      input wire [31:0] in_data,
      input wire [3:0] in_keep,
      input wire in_last,
      output wire out_valid,
      input wire out_ready,
      output wire [15:0] out_data,
      output wire [1:0] out_keep,
      output wire out_last
    );
      reg busy, upper;
      reg [31:0] saved_data;
      reg [3:0] saved_keep;
      reg saved_last;
      assign in_ready = !busy;
      assign out_valid = busy;
      assign out_data = (upper ? saved_data[31:16] : saved_data[15:0]) ^
                        (BUG_BYTE && upper ? 16'h1 : 16'h0);
      assign out_keep = upper ? saved_keep[3:2] : saved_keep[1:0];
      assign out_last = BUG_LAST ? 1'b0 : saved_last && (upper || saved_keep[3:2] == 0);
      always @(posedge clk) begin
        if (rst) begin busy <= 0; upper <= 0; saved_data <= 0; saved_keep <= 0; saved_last <= 0; end
        else begin
          if (in_valid && in_ready) begin
            saved_data <= in_data; saved_keep <= in_keep; saved_last <= in_last;
            busy <= 1; upper <= 0;
          end
          if (out_valid && out_ready) begin
            if (!upper && saved_keep[3:2] != 0) upper <= 1;
            else begin busy <= 0; upper <= 0; end
          end
        end
      end
    endmodule
    
    module tb;
      reg clk = 0;
      always #5 clk = ~clk;
      reg rst = 1, in_valid = 0, in_last = 0, out_ready = 0;
      reg [31:0] in_data = 0;
      reg [3:0] in_keep = 0;
      wire in_ready, out_valid, out_last;
      wire [15:0] out_data;
      wire [1:0] out_keep;
      wire byte_ready, byte_valid, byte_last, last_ready, last_valid, last_last;
      wire [15:0] byte_data, last_data;
      wire [1:0] byte_keep, last_keep;
      integer accepted_inputs = 0, output_beats = 0, output_bytes = 0, packets = 0;
      integer next_index;
      reg [7:0] expected [0:4];
      reg byte_fault_seen = 0, last_fault_seen = 0, was_stalled = 0;
      reg [18:0] held_output;
    
      stream_downsize good (.*);
      stream_downsize #(.BUG_BYTE(1)) bad_byte (
        .clk(clk), .rst(rst), .in_valid(in_valid), .in_ready(byte_ready), .in_data(in_data),
        .in_keep(in_keep), .in_last(in_last), .out_valid(byte_valid), .out_ready(out_ready),
        .out_data(byte_data), .out_keep(byte_keep), .out_last(byte_last)
      );
      stream_downsize #(.BUG_LAST(1)) bad_last (
        .clk(clk), .rst(rst), .in_valid(in_valid), .in_ready(last_ready), .in_data(in_data),
        .in_keep(in_keep), .in_last(in_last), .out_valid(last_valid), .out_ready(out_ready),
        .out_data(last_data), .out_keep(last_keep), .out_last(last_last)
      );
      always @(posedge clk) begin
        if (rst) begin
          accepted_inputs <= 0; output_beats <= 0; output_bytes <= 0; packets <= 0;
          byte_fault_seen <= 0; last_fault_seen <= 0; was_stalled <= 0;
        end else begin
          if (in_ready !== byte_ready || in_ready !== last_ready ||
              out_valid !== byte_valid || out_valid !== last_valid ||
              out_keep !== byte_keep || out_keep !== last_keep) begin
            $display("FAIL: fault variants changed the intended handshake/keep schedule"); $finish;
          end
          if (was_stalled && (!out_valid || {out_data, out_keep, out_last} !== held_output)) begin
            $display("FAIL: good converter changed its stalled output"); $finish;
          end
          was_stalled <= out_valid && !out_ready;
          held_output <= {out_data, out_keep, out_last};
          if (in_valid && in_ready) begin
            if (!(in_keep == 4'hf || in_keep == 4'h1)) begin
              $display("FAIL: stimulus outside this exercise's declared keep subset"); $finish;
            end
            accepted_inputs <= accepted_inputs + 1;
          end
          if (out_valid && out_ready) begin
            next_index = output_bytes;
            for (integer lane = 0; lane < 2; lane = lane + 1) begin
              if (out_keep[lane]) begin
                if (next_index >= 5) begin $display("FAIL: unexpected extra output byte"); $finish; end
                if (out_data[8*lane +: 8] !== expected[next_index]) begin
                  $display("FAIL: good converter changed accepted byte order/value"); $finish;
                end
                if (byte_data[8*lane +: 8] !== expected[next_index]) byte_fault_seen <= 1;
                next_index = next_index + 1;
              end
            end
            if (out_last !== (next_index == 5)) begin $display("FAIL: good packet-end position"); $finish; end
            if (last_last !== (next_index == 5)) last_fault_seen <= 1;
            output_bytes <= next_index;
            output_beats <= output_beats + 1;
            if (out_last) packets <= packets + 1;
          end
        end
      end
      task send_word(input [31:0] data, input [3:0] keep, input last);
        begin
          @(negedge clk); in_valid = 1; in_data = data; in_keep = keep; in_last = last;
          begin : wait_accept
            for (integer cycle = 0; cycle < 8; cycle = cycle + 1) begin
              @(posedge clk);
              if (in_ready) begin @(negedge clk); in_valid = 0; disable wait_accept; end
            end
            $display("FAIL: bounded test source exceeded its acceptance budget"); $finish;
          end
        end
      endtask
      initial begin
        expected[0] = 8'h11; expected[1] = 8'h22; expected[2] = 8'h33;
        expected[3] = 8'h44; expected[4] = 8'h55;
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        repeat (2) @(posedge clk);
        @(negedge clk); rst = 0;
        send_word(32'h4433_2211, 4'hf, 0);
        repeat (2) @(negedge clk);
        out_ready = 1;
        send_word(32'h0000_0055, 4'h1, 1);
        repeat (3) @(negedge clk);
        if (accepted_inputs != 2 || output_beats != 3 || output_bytes != 5 || packets != 1 ||
            !byte_fault_seen || !last_fault_seen || out_valid) begin
          $display("FAIL: input/output counts, drain or intended negative detections"); $finish;
        end
        $display("PASS: stalled conversion preserved five bytes and one packet; byte/TLAST faults detected");
        $finish;
      end
    endmodule

    Key takeaways

    • Lite AW and W can arrive separately; retain both and provide one response for each paired write.

    • A stalled Stream offer retains data, keep mask and packet-end flag until acceptance.

    • Width conversion preserves the agreed byte sequence and packet boundaries, not the original number of beats.

    Apply the mechanism

    Continue to the integration exercise

    Then trace a failure

    Previous

    38 / 158

    Next: AXI-Lite & AXI-Stream: Integration

    Up next

    Up nextAXI-Lite & AXI-Stream: Integration

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • AXI-Lite & AXI-Stream: Debug
    • AXI-Lite & AXI-Stream: Interview
    clkawvalidawreadywvalidwreadybvalidbreadyvalue010commits01premature_seendropped_value0
    AXI4-Lite: early W is retained, AW joins it, and the resulting B offer waits.
    clkin_validin_readyin_keepf1in_lastout_validout_readyout_data022114433221155out_keep031out_lastoutput_bytes0245packets01
    AXI4-Stream: five accepted bytes emerge in three output beats with one packet end.