"localparam N_MASTER_LOG2 = \$clog2(N_MASTER)"는 어떤 작업을 하는 것인가?
<<StartOfFile:DMAC/SIMv2/DMAC_ARBITER.sv>>
module DMAC_ARBITER
#(
    N_MASTER = 4,
    DATA_SIZE = 32
)
(
    input wire clk,
    input wire rst_n, // _n means active low

    // configuration registers
    input wire src_valid_i[N_MASTER],
    output reg src_ready_o[N_MASTER],
    input wire [DATA_SIZE-1:0] src_data_i[N_MASTER],

    output reg dst_valid_o,
    input wire dst_ready_i,
    output reg [DATA_SIZE-1:0] dst_data_o
);
  localparam N_MASTER_LOG2 = \$clog2(N_MASTER);

  reg dst_valid, dst_valid_n;
  reg [DATA_SIZE-1:0] dst_data, dst_data_n;
  reg [N_MASTER_LOG2-1:0] last_mst, last_mst_n;

  always_ff @(posedge clk) begin
    if (~rst_n) begin
      dst_valid <= 1'b0;
      dst_data <= 'd0;
      last_mst <= 'd0;
    end
    else begin
      dst_valid <= dst_valid_n;
      dst_data <= dst_data_n;
      last_mst <= last_mst_n;
    end
  end

  // round-robin arbiter
  always_comb begin
    dst_valid_n = dst_valid;
    dst_data_n = dst_data;
    last_mst_n = last_mst;

    for (int i=0; i<N_MASTER; i++) begin
      src_ready_o[i] = 1'b0;
    end

    if (!dst_valid | dst_ready_i) begin
      if (last_mst == 'd0) begin
        if (src_valid_i[1]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[1];
          src_ready_o[1] = 1'b1;
          last_mst_n = 'd1;
        end
        else if (src_valid_i[2]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[2];
          src_ready_o[2] = 1'b1;
          last_mst_n = 'd2;
        end
        else if (src_valid_i[3]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[3];
          src_ready_o[3] = 1'b1;
          last_mst_n = 'd3;
        end
        else if (src_valid_i[0]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[0];
          src_ready_o[0] = 1'b1;
          last_mst_n = 'd0;
        end
        else begin
          dst_valid_n = 1'b0;
        end
      end
      else if (last_mst == 'd1) begin
        if (src_valid_i[2]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[2];
          src_ready_o[2] = 1'b1;
          last_mst_n = 'd2;
        end
        else if (src_valid_i[3]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[3];
          src_ready_o[3] = 1'b1;
          last_mst_n = 'd3;
        end
        else if (src_valid_i[0]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[0];
          src_ready_o[0] = 1'b1;
          last_mst_n = 'd0;
        end
        else if (src_valid_i[1]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[1];
          src_ready_o[1] = 1'b1;
          last_mst_n = 'd1;
        end
        else begin
          dst_valid_n = 1'b0;
        end
      end
      else if (last_mst == 'd2) begin
        if (src_valid_i[3]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[3];
          src_ready_o[3] = 1'b1;
          last_mst_n = 'd3;
        end
        else if (src_valid_i[0]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[0];
          src_ready_o[0] = 1'b1;
          last_mst_n = 'd0;
        end
        else if (src_valid_i[1]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[1];
          src_ready_o[1] = 1'b1;
          last_mst_n = 'd1;
        end
        else if (src_valid_i[2]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[2];
          src_ready_o[2] = 1'b1;
          last_mst_n = 'd2;
        end
        else begin
          dst_valid_n = 1'b0;
        end
      end
      else if (last_mst == 'd3) begin
        if (src_valid_i[0]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[0];
          src_ready_o[0] = 1'b1;
          last_mst_n = 'd0;
        end
        else if (src_valid_i[1]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[1];
          src_ready_o[1] = 1'b1;
          last_mst_n = 'd1;
        end
        else if (src_valid_i[2]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[2];
          src_ready_o[2] = 1'b1;
          last_mst_n = 'd2;
        end
        else if (src_valid_i[3]) begin
          dst_valid_n = 1'b1;
          dst_data_n = src_data_i[3];
          src_ready_o[3] = 1'b1;
          last_mst_n = 'd3;
        end
        else begin
          dst_valid_n = 1'b0;
        end
      end
    end
  end

  assign dst_valid_o = dst_valid;
  assign dst_data_o = dst_data;

endmodule
<<EndOfFile:DMAC/SIMv2/DMAC_ARBITER.sv>>

<<StartOfFile:DMAC/RTLv2/DMAC_CFG.sv>>
module DMAC_CFG
(
    input wire clk,
    input wire rst_n, // _n means active low

    // AMBA APB interface
    input wire psel_i,
    input wire penable_i,
    input wire [11:0] paddr_i,
    input wire pwrite_i,
    input wire [31:0] pwdata_i,
    output reg pready_o,
    output reg [31:0] prdata_o,

    // configuration registers for CH0
    output reg [31:0] ch0_src_addr_o,
    output reg [31:0] ch0_dst_addr_o,
    output reg [15:0] ch0_byte_len_o,
    output wire ch0_start_o,
    input wire ch0_done_i,

    // configuration registers for CH1
    output reg [31:0] ch1_src_addr_o,
    output reg [31:0] ch1_dst_addr_o,
    output reg [15:0] ch1_byte_len_o,
    output wire ch1_start_o,
    input wire ch1_done_i,

    // configuration registers for CH2
    output reg [31:0] ch2_src_addr_o,
    output reg [31:0] ch2_dst_addr_o,
    output reg [15:0] ch2_byte_len_o,
    output wire ch2_start_o,
    input wire ch2_done_i,

    // configuration registers for CH3
    output reg [31:0] ch3_src_addr_o,
    output reg [31:0] ch3_dst_addr_o,
    output reg [15:0] ch3_byte_len_o,
    output wire ch3_start_o,
    input wire ch3_done_i
);

    // Configuration register for CH0 to read/write
    reg [31:0] ch0_src_addr;
    reg [31:0] ch0_dst_addr;
    reg [15:0] ch0_byte_len;

    // Configuration register for CH1 to read/write
    reg [31:0] ch1_src_addr;
    reg [31:0] ch1_dst_addr;
    reg [15:0] ch1_byte_len;

    // Configuration register for CH2 to read/write
    reg [31:0] ch2_src_addr;
    reg [31:0] ch2_dst_addr;
    reg [15:0] ch2_byte_len;

    // Configuration register for CH3 to read/write
    reg [31:0] ch3_src_addr;
    reg [31:0] ch3_dst_addr;
    reg [15:0] ch3_byte_len;

    //----------------------------------------------------------
    // Write
    //----------------------------------------------------------
    // an APB write occurs when PSEL & PENABLE & PWRITE
    // clk : __--__--__--__--__--__--__--__--__--__--
    // psel : ___--------_____________________________
    // penable : _______----_____________________________
    // pwrite : ___--------_____________________________
    // wren : _______----_____________________________
    //
    // DMA start command must be asserted when APB writes 1 to the DMA_CMD
    // register
    // clk : __--__--__--__--__--__--__--__--__--__--
    // psel : ___--------_____________________________
    // penable : _______----_____________________________
    // pwrite : ___--------_____________________________
    // paddr : |DMA_CMD|
    // pwdata : | 1 |
    // start : _______----_____________________________

    wire wren = penable_i & psel_i & pwrite_i;
    wire start = wren & pwdata_i[0];

    always @(posedge clk) begin
  // Fill
  // your
  // code
  // here
        if (!rst_n) begin
            ch0_src_addr <= 'b0;
            ch0_dst_addr <= 'b0;
            ch0_byte_len <= 'b0;
            // ch0_start <= 'b0;
            ch1_src_addr <= 'b0;
            ch1_dst_addr <= 'b0;
            ch1_byte_len <= 'b0;
            // ch1_start <= 'b0;
            ch2_src_addr <= 'b0;
            ch2_dst_addr <= 'b0;
            ch2_byte_len <= 'b0;
            // ch2_start <= 'b0;
            ch3_src_addr <= 'b0;
            ch3_dst_addr <= 'b0;
            ch3_byte_len <= 'b0;
            // ch3_start <= 'b0;
        end
        else if (wren) begin
            if (paddr_i == 'h100) begin
                ch0_src_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h104) begin
                ch0_dst_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h108) begin
                ch0_byte_len <= pwdata_i[15:0];
            end
            else if (paddr_i == 'h200) begin
                ch1_src_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h204) begin
                ch1_dst_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h208) begin
                ch1_byte_len <= pwdata_i[15:0];
            end
            else if (paddr_i == 'h300) begin
                ch2_src_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h304) begin
                ch2_dst_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h308) begin
                ch2_byte_len <= pwdata_i[15:0];
            end
            else if (paddr_i == 'h400) begin
                ch3_src_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h404) begin
                ch3_dst_addr <= pwdata_i[31:0];
            end
            else if (paddr_i == 'h408) begin
                ch3_byte_len <= pwdata_i[15:0]; 
            end
        end
 end

 // Fill your code here (wren, ch0_start, ch1_start...)

    //----------------------------------------------------------
    // READ
    //----------------------------------------------------------
    // an APB read occurs when PSEL & PENABLE & !PWRITE
    // To make read data a direct output from register,
    // this code shall buffer the muxed read data into a register
    // in the SETUP cycle (PSEL & !PENABLE)
    // clk : __--__--__--__--__--__--__--__--__--__--
    // psel : ___--------_____________________________
    // penable : _______----_____________________________
    // pwrite : ________________________________________
    // reg update : ___----_________________________________
    // prdata : |DATA

    reg [31:0] rdata;

    always @(posedge clk) begin
  // Fill
  // your
  // code
  // here
        if (!rst_n) begin
            rdata <= 'd0;
        end
        else if (!penable_i & psel_i & !pwrite_i) begin
            if (paddr_i == 'h000) begin
                rdata <= 32'h0002_0101;
            end
            else if (paddr_i == 'h100) begin
                rdata <= ch0_src_addr;
            end
            else if (paddr_i == 'h104) begin
                rdata <= ch0_dst_addr;
            end
            else if (paddr_i == 'h108) begin
                rdata <= {16'b0, ch0_byte_len};
            end
            else if (paddr_i == 'h110) begin
                rdata <= {31'b0, ch0_done_i};
            end
            else if (paddr_i == 'h200) begin
                rdata <= ch1_src_addr;
            end
            else if (paddr_i == 'h204) begin
                rdata <= ch1_dst_addr;
            end
            else if (paddr_i == 'h208) begin
                rdata <= {16'b0, ch1_byte_len};
            end
            else if (paddr_i == 'h210) begin
                rdata <= {31'b0, ch1_done_i};
            end
            else if (paddr_i == 'h300) begin
                rdata <= ch2_src_addr;
            end
            else if (paddr_i == 'h304) begin
                rdata <= ch2_dst_addr;
            end
            else if (paddr_i == 'h308) begin
                rdata <= {16'b0, ch2_byte_len};
            end
            else if (paddr_i == 'h310) begin
                rdata <= {31'b0, ch2_done_i};
            end
            else if (paddr_i == 'h400) begin
                rdata <= ch3_src_addr;
            end
            else if (paddr_i == 'h404) begin
                rdata <= ch3_dst_addr;
            end
            else if (paddr_i == 'h408) begin
                rdata <= {16'b0, ch3_byte_len};
            end
            else if (paddr_i == 'h410) begin
                rdata <= {31'b0, ch3_done_i};
            end
            else begin
                rdata <= 'd0;
            end
        end
 end

    // output assignments
    assign pready_o = 1'b1;
    assign prdata_o = rdata;

    assign ch0_src_addr_o = ch0_src_addr;
    assign ch0_dst_addr_o = ch0_dst_addr;
    assign ch0_byte_len_o = ch0_byte_len;
    assign ch0_start_o = start & (paddr_i=='h10C);

    assign ch1_src_addr_o = ch1_src_addr;
    assign ch1_dst_addr_o = ch1_dst_addr;
    assign ch1_byte_len_o = ch1_byte_len;
    assign ch1_start_o = start & (paddr_i=='h20C);

    assign ch2_src_addr_o = ch2_src_addr;
    assign ch2_dst_addr_o = ch2_dst_addr;
    assign ch2_byte_len_o = ch2_byte_len;
    assign ch2_start_o = start & (paddr_i=='h30C);

    assign ch3_src_addr_o = ch3_src_addr;
    assign ch3_dst_addr_o = ch3_dst_addr;
    assign ch3_byte_len_o = ch3_byte_len;
    assign ch3_start_o = start & (paddr_i=='h40C);

endmodule
 <<EndOfFile:DMAC/RTLv2/DMAC_CFG.sv>>

<<StartOfFile:DMAC/RTLv2/DMAC_ENGINE.sv>>
module DMAC_ENGINE
(
    input wire clk,
    input wire rst_n, // _n means active low

    // configuration registers
    input wire [31:0] src_addr_i,
    input wire [31:0] dst_addr_i,
    input wire [15:0] byte_len_i,
    input wire start_i,
    output wire done_o,

    // AMBA AXI interface (AW channel)
    output wire [31:0] awaddr_o,
    output wire [3:0] awlen_o,
    output wire [2:0] awsize_o,
    output wire [1:0] awburst_o,
    output wire awvalid_o,
    input wire awready_i,

    // AMBA AXI interface (W channel)
    output wire [31:0] wdata_o,
    output wire [3:0] wstrb_o,
    output wire wlast_o,
    output wire wvalid_o,
    input wire wready_i,

    // AMBA AXI interface (B channel)
    input wire [1:0] bresp_i,
    input wire bvalid_i,
    output wire bready_o,

    // AMBA AXI interface (AR channel)
    output wire [31:0] araddr_o,
    output wire [3:0] arlen_o,
    output wire [2:0] arsize_o,
    output wire [1:0] arburst_o,
    output wire arvalid_o,
    input wire arready_i,

    // AMBA AXI interface (R channel)
    input wire [31:0] rdata_i,
    input wire [1:0] rresp_i,
    input wire rlast_i,
    input wire rvalid_i,
    output wire rready_o
);

    // mnemonics for state values
    localparam S_IDLE = 3'd0,
                                S_RREQ = 3'd1,
                                S_RDATA = 3'd2,
                                S_WREQ = 3'd3,
                                S_WDATA = 3'd4,
                                S_WAIT = 3'd5; // new state for project 1

    reg [2:0] state, state_n;

    reg [31:0] src_addr, src_addr_n;
    reg [31:0] dst_addr, dst_addr_n;
    reg [15:0] cnt, cnt_n;
    reg [3:0] wcnt, wcnt_n;

    reg arvalid,
                                rready,
                                awvalid,
                                wvalid,
                                wlast,
                                done;

    wire fifo_full,
                                fifo_empty;
    reg fifo_wren,
                                fifo_rden;
    wire [31:0] fifo_rdata;

    reg [15:0] outstanding_wr_cnt;
    reg [15:0] outstanding_wr_cnt_n;


    // it's desirable to code registers in a simple way
    always_ff @(posedge clk)
        if (!rst_n) begin
            state <= S_IDLE;

            src_addr <= 32'd0;
            dst_addr <= 32'd0;
            cnt <= 16'd0;

            wcnt <= 4'd0;
            outstanding_wr_cnt <= 16'd0;
        end
        else begin
            state <= state_n;

            src_addr <= src_addr_n;
            dst_addr <= dst_addr_n;
            cnt <= cnt_n;

            wcnt <= wcnt_n;
            outstanding_wr_cnt <= outstanding_wr_cnt_n;
        end


    // this block programs output values and next register values
    // based on states.
    always_comb begin
        state_n = state;

        src_addr_n = src_addr;
        dst_addr_n = dst_addr;
        cnt_n = cnt;
        wcnt_n = wcnt;

        arvalid = 1'b0;
        rready = 1'b0;
        awvalid = 1'b0;
        wvalid = 1'b0;
        wlast = 1'b0;
        done = 1'b0;

        fifo_wren = 1'b0;
        fifo_rden = 1'b0;

        case (state)
            S_IDLE: begin
                done = 1'b1;
                if (start_i & byte_len_i!=16'd0) begin
                    src_addr_n = src_addr_i;
                    dst_addr_n = dst_addr_i;
                    cnt_n = byte_len_i;

                    state_n = S_RREQ;
                end
            end
            S_RREQ: begin
                arvalid = 1'b1;

                if (arready_i) begin
                    state_n = S_RDATA;
                    src_addr_n = src_addr + 'd64;
                end
            end
            S_RDATA: begin
                rready = 1'b1;

                if (rvalid_i) begin
                    fifo_wren = 1'b1;
                    if (rlast_i) begin
                        state_n = S_WREQ;
                    end
                end
            end
            S_WREQ: begin
                awvalid = 1'b1;

                if (awready_i) begin
                    state_n = S_WDATA;
                    dst_addr_n = dst_addr + 'd64;
                    wcnt_n = awlen_o;
                    if (cnt>='d64) begin
                        cnt_n = cnt[15:0] - 32'h4 - (arlen_o<<2);
                    end
                    else begin
                        cnt_n = 'd0;
                    end
                end
            end
            S_WDATA: begin
                wvalid = 1'b1;
                wlast = (wcnt==4'd0);

                if (wready_i) begin
                    fifo_rden = 1'b1;

                    if (wlast) begin
                        if (cnt==16'd0) begin
                            state_n = S_IDLE;
                        end
                        else begin
                            state_n = S_RREQ;
                        end
                    end
                    else begin
                        wcnt_n = wcnt - 4'd1;
                    end
                end
            end
            // FIXME: implement S_WAIT state for project 1
            S_WAIT: begin
                if (outstanding_wr_cnt == 'd0) begin
                    state_n = S_IDLE;
                end
            end
        endcase
    end

    // FIXME: implement outstanding_wr_cnt
    wire outstanding_wr_pp = awready_i & awvalid_o;
    wire outstanding_wr_mm = bready_o & bvalid_i & (bresp_i == 'd0);

    always_comb begin
        outstanding_wr_cnt_n = outstanding_wr_cnt;
        if (outstanding_wr_pp & !outstanding_wr_mm) begin
            outstanding_wr_cnt_n = outstanding_wr_cnt + 'd1;
        end
        if (!outstanding_wr_pp & outstanding_wr_mm) begin
            outstanding_wr_cnt_n = outstanding_wr_cnt - 'd1;
        end
    end

    DMAC_FIFO u_fifo
    (
        .clk (clk),
        .rst_n (rst_n),

        .full_o (fifo_full),
        .wren_i (fifo_wren),
        .wdata_i (rdata_i),

        .empty_o (fifo_empty),
        .rden_i (fifo_rden),
        .rdata_o (fifo_rdata)
    );

    // Output assigments
    assign done_o = done;

    assign awaddr_o = dst_addr;
    assign awlen_o = (cnt >= 'd64) ? 4'hF: cnt[5:2]-4'h1;
    assign awsize_o = 3'b010; // 4 bytes per transfer
    assign awburst_o = 2'b01; // incremental
    assign awvalid_o = awvalid;

    assign wdata_o = fifo_rdata;
    assign wstrb_o = 4'b1111; // all bytes within 4 byte are valid
    assign wlast_o = wlast;
    assign wvalid_o = wvalid;

    assign bready_o = 1'b1;

    assign arvalid_o = arvalid;
    assign araddr_o = src_addr;
    assign arlen_o = (cnt >= 'd64) ? 4'hF: cnt[5:2]-4'h1;
    assign arsize_o = 3'b010; // 4 bytes per transfer
    assign arburst_o = 2'b01; // incremental
    assign arvalid_o = arvalid;

    assign rready_o = rready & !fifo_full;

endmodule
<<EndOfFile:DMAC/RTLv2/DMAC_ENGINE.sv>>

<<StartOfFile:DMAC/RTLv2/DMAC_FIFO.sv>>
module DMAC_FIFO #(
    parameter DEPTH_LG2 = 4,
    parameter DATA_WIDTH = 32
    )
(
    input wire clk,
    input wire rst_n,

    output wire full_o,
    input wire wren_i,
    input wire [DATA_WIDTH-1:0] wdata_i,

    output wire empty_o,
    input wire rden_i,
    output wire [DATA_WIDTH-1:0] rdata_o
);

    localparam FIFO_DEPTH = (1<<DEPTH_LG2);

    reg [DATA_WIDTH-1:0] mem[FIFO_DEPTH];

    reg full, full_n,
                                        empty, empty_n;
    reg [DEPTH_LG2:0] wrptr, wrptr_n,
                                        rdptr, rdptr_n;

    always_ff @(posedge clk)
        if (!rst_n) begin
            for (int i=0; i<FIFO_DEPTH; i++) begin
                mem[i] <= {DATA_WIDTH{1'b0}};
            end
        end
        else begin
            if (wren_i) begin
                mem[wrptr[DEPTH_LG2-1:0]] <= wdata_i;
            end
        end

    always_ff @(posedge clk)
        if (!rst_n) begin
            full <= 1'b0;
            empty <= 1'b1; // empty after as reset

            wrptr <= {(DEPTH_LG2+1){1'b0}};
            rdptr <= {(DEPTH_LG2+1){1'b0}};
        end
        else begin
            full <= full_n;
            empty <= empty_n;

            wrptr <= wrptr_n;
            rdptr <= rdptr_n;
        end

    always_comb begin
        wrptr_n = wrptr;
        rdptr_n = rdptr;

        if (wren_i) begin
            wrptr_n = wrptr + 'd1;
        end

        if (rden_i) begin
            rdptr_n = rdptr + 'd1;
        end

        empty_n = (wrptr_n == rdptr_n);
        full_n = (wrptr_n[DEPTH_LG2]!=rdptr_n[DEPTH_LG2])
                                     &(wrptr_n[DEPTH_LG2-1:0]==rdptr_n[DEPTH_LG2-1:0]);

    end

    // synthesis translate_off
    always @(posedge clk) begin
        if (full_o & wren_i) begin
            \$display("FIFO overflow");
            @(posedge clk);
            \$finish;
        end
    end

    always @(posedge clk) begin
        if (empty_o & rden_i) begin
            \$display("FIFO underflow");
            @(posedge clk);
            \$finish;
        end
    end
    // synthesis translate_on

    assign full_o = full;
    assign empty_o = empty;
    assign rdata_o = mem[rdptr[DEPTH_LG2-1:0]];

endmodule
<<EndOfFile:DMAC/RTLv2/DMAC_FIFO.sv>>

<<StartOfFile:DMAC/RTLv2/DMAC_TOP.sv>>
module DMAC_TOP
(
    input wire clk,
    input wire rst_n, // _n means active low

    // AMBA APB interface
    input wire psel_i,
    input wire penable_i,
    input wire [11:0] paddr_i,
    input wire pwrite_i,
    input wire [31:0] pwdata_i,
    output reg pready_o,
    output reg [31:0] prdata_o,
    output reg pslverr_o,

    // AMBA AXI interface (AW channel)
    output wire [3:0] awid_o,
    output wire [31:0] awaddr_o,
    output wire [3:0] awlen_o,
    output wire [2:0] awsize_o,
    output wire [1:0] awburst_o,
    output wire awvalid_o,
    input wire awready_i,

    // AMBA AXI interface (AW channel)
    output wire [3:0] wid_o,
    output wire [31:0] wdata_o,
    output wire [3:0] wstrb_o,
    output wire wlast_o,
    output wire wvalid_o,
    input wire wready_i,

    // AMBA AXI interface (B channel)
    input wire [3:0] bid_i,
    input wire [1:0] bresp_i,
    input wire bvalid_i,
    output wire bready_o,

    // AMBA AXI interface (AR channel)
    output wire [3:0] arid_o,
    output wire [31:0] araddr_o,
    output wire [3:0] arlen_o,
    output wire [2:0] arsize_o,
    output wire [1:0] arburst_o,
    output wire arvalid_o,
    input wire arready_i,

    // AMBA AXI interface (R channel)
    input wire [3:0] rid_i,
    input wire [31:0] rdata_i,
    input wire [1:0] rresp_i,
    input wire rlast_i,
    input wire rvalid_i,
    output wire rready_o
);
    localparam N_CH = 4;

    wire [31:0] src_addr_vec[N_CH];
    wire [31:0] dst_addr_vec[N_CH];
    wire [15:0] byte_len_vec[N_CH];
    wire start_vec[N_CH];
    wire done_vec[N_CH];

    wire [3:0] awid_vec[N_CH];
    wire [31:0] awaddr_vec[N_CH];
    wire [3:0] awlen_vec[N_CH];
    wire [2:0] awsize_vec[N_CH];
    wire [1:0] awburst_vec[N_CH];
    wire awvalid_vec[N_CH];
    wire awready_vec[N_CH];

    wire [3:0] wid_vec[N_CH];
    wire [31:0] wdata_vec[N_CH];
    wire [3:0] wstrb_vec[N_CH];
    wire wlast_vec[N_CH];
    wire wvalid_vec[N_CH];
    wire wready_vec[N_CH];
    wire bready_vec[N_CH];
    wire [3:0] arid_vec[N_CH];
    wire [31:0] araddr_vec[N_CH];
    wire [3:0] arlen_vec[N_CH];
    wire [2:0] arsize_vec[N_CH];
    wire [1:0] arburst_vec[N_CH];
    wire arvalid_vec[N_CH];
    wire arready_vec[N_CH];
    wire rready_vec[N_CH];

    DMAC_CFG u_cfg(
        .clk (clk),
        .rst_n (rst_n),

        // AMBA APB interface
        .psel_i (psel_i),
        .penable_i (penable_i),
        .paddr_i (paddr_i),
        .pwrite_i (pwrite_i),
        .pwdata_i (pwdata_i),
        .pready_o (pready_o),
        .prdata_o (prdata_o),

        .ch0_src_addr_o (src_addr_vec[0]),
        .ch0_dst_addr_o (dst_addr_vec[0]),
        .ch0_byte_len_o (byte_len_vec[0]),
        .ch0_start_o (start_vec[0]),
        .ch0_done_i (done_vec[0]),

        .ch1_src_addr_o (src_addr_vec[1]),
        .ch1_dst_addr_o (dst_addr_vec[1]),
        .ch1_byte_len_o (byte_len_vec[1]),
        .ch1_start_o (start_vec[1]),
        .ch1_done_i (done_vec[1]),

        .ch2_src_addr_o (src_addr_vec[2]),
        .ch2_dst_addr_o (dst_addr_vec[2]),
        .ch2_byte_len_o (byte_len_vec[2]),
        .ch2_start_o (start_vec[2]),
        .ch2_done_i (done_vec[2]),

        .ch3_src_addr_o (src_addr_vec[3]),
        .ch3_dst_addr_o (dst_addr_vec[3]),
        .ch3_byte_len_o (byte_len_vec[3]),
        .ch3_start_o (start_vec[3]),
        .ch3_done_i (done_vec[3])
    );

 assign pslverr_o = 1'b0;

    DMAC_ARBITER #(
        .N_MASTER (N_CH),
        .DATA_SIZE (\$bits(arid_o)+\$bits(araddr_o)+\$bits(arlen_o)+\$bits(arsize_o)+\$bits(arburst_o))
    )
    u_ar_arbiter
    (
        .clk (clk),
        .rst_n (rst_n),

        .src_valid_i (arvalid_vec),
        .src_ready_o (arready_vec),
        .src_data_i ({
                                  {arid_vec[0], araddr_vec[0], arlen_vec[0], arsize_vec[0], arburst_vec[0]},
                                  {arid_vec[1], araddr_vec[1], arlen_vec[1], arsize_vec[1], arburst_vec[1]},
                                  {arid_vec[2], araddr_vec[2], arlen_vec[2], arsize_vec[2], arburst_vec[2]},
                                  {arid_vec[3], araddr_vec[3], arlen_vec[3], arsize_vec[3], arburst_vec[3]}}),

        .dst_valid_o (arvalid_o),
        .dst_ready_i (arready_i),
        .dst_data_o ({arid_o, araddr_o, arlen_o, arsize_o, arburst_o})
    );

    DMAC_ARBITER #(
        .N_MASTER (N_CH),
        .DATA_SIZE (\$bits(awid_o)+\$bits(awaddr_o)+\$bits(awlen_o)+\$bits(awsize_o)+\$bits(awburst_o))
    )
    u_aw_arbiter
    (
        .clk (clk),
        .rst_n (rst_n),

        .src_valid_i (awvalid_vec),
        .src_ready_o (awready_vec),
        .src_data_i ({
                                  {awid_vec[0], awaddr_vec[0], awlen_vec[0], awsize_vec[0], awburst_vec[0]},
                                  {awid_vec[1], awaddr_vec[1], awlen_vec[1], awsize_vec[1], awburst_vec[1]},
                                  {awid_vec[2], awaddr_vec[2], awlen_vec[2], awsize_vec[2], awburst_vec[2]},
                                  {awid_vec[3], awaddr_vec[3], awlen_vec[3], awsize_vec[3], awburst_vec[3]}}),

        .dst_valid_o (awvalid_o),
        .dst_ready_i (awready_i),
        .dst_data_o ({awid_o, awaddr_o, awlen_o, awsize_o, awburst_o})
    );

    DMAC_ARBITER #(
        .N_MASTER (N_CH),
        .DATA_SIZE (\$bits(wid_o)+\$bits(wdata_o)+\$bits(wstrb_o)+\$bits(wlast_o))
    )
    u_w_arbiter
    (
        .clk (clk),
        .rst_n (rst_n),

        .src_valid_i (wvalid_vec),
        .src_ready_o (wready_vec),
        .src_data_i ({
                                  {wid_vec[0], wdata_vec[0], wstrb_vec[0], wlast_vec[0]},
                                  {wid_vec[1], wdata_vec[1], wstrb_vec[1], wlast_vec[1]},
                                  {wid_vec[2], wdata_vec[2], wstrb_vec[2], wlast_vec[2]},
                                  {wid_vec[3], wdata_vec[3], wstrb_vec[3], wlast_vec[3]}}),

        .dst_valid_o (wvalid_o),
        .dst_ready_i (wready_i),
        .dst_data_o ({wid_o, wdata_o, wstrb_o, wlast_o})
    );

    assign bready_o = (bid_i=='d0) ? bready_vec[0] :
                                      (bid_i=='d1) ? bready_vec[1] :
                                      (bid_i=='d2) ? bready_vec[2] :
                                                     bready_vec[3];

    assign rready_o = (rid_i=='d0) ? rready_vec[0] :
                                      (rid_i=='d1) ? rready_vec[1] :
                                      (rid_i=='d2) ? rready_vec[2] :
                                                     rready_vec[3];

    genvar ch;
    generate
        for (ch=0; ch<N_CH; ch++) begin: channel
            DMAC_ENGINE u_engine(
                .clk (clk),
                .rst_n (rst_n),

                // configuration registers
                .src_addr_i (src_addr_vec[ch]),
                .dst_addr_i (dst_addr_vec[ch]),
                .byte_len_i (byte_len_vec[ch]),
                .start_i (start_vec[ch]),
                .done_o (done_vec[ch]),

                // AMBA AXI interface (AW channel)
                .awaddr_o (awaddr_vec[ch]),
                .awlen_o (awlen_vec[ch]),
                .awsize_o (awsize_vec[ch]),
                .awburst_o (awburst_vec[ch]),
                .awvalid_o (awvalid_vec[ch]),
                .awready_i (awready_vec[ch]),

                // AMBA AXI interface (W channel)
                .wdata_o (wdata_vec[ch]),
                .wstrb_o (wstrb_vec[ch]),
                .wlast_o (wlast_vec[ch]),
                .wvalid_o (wvalid_vec[ch]),
                .wready_i (wready_vec[ch]),

                // AMBA AXI interface (B channel)
                .bresp_i (bresp_i),
                .bvalid_i (bvalid_i & (bid_i==ch)),
                .bready_o (bready_vec[ch]),

                // AMBA AXI interface (AR channel)
                .araddr_o (araddr_vec[ch]),
                .arlen_o (arlen_vec[ch]),
                .arsize_o (arsize_vec[ch]),
                .arburst_o (arburst_vec[ch]),
                .arvalid_o (arvalid_vec[ch]),
                .arready_i (arready_vec[ch]),

                // AMBA AXI interface (R channel)
                .rdata_i (rdata_i),
                .rresp_i (rresp_i),
                .rlast_i (rlast_i),
                .rvalid_i (rvalid_i & (rid_i==ch)),
                .rready_o (rready_vec[ch])
            );

            assign awid_vec[ch] = ch;
            assign wid_vec[ch] = ch;
            assign arid_vec[ch] = ch;
        end
    endgenerate

endmodule
<<EndOfFile:DMAC/RTLv2/DMAC_TOP.sv>>

<<StartOfFile:DMAC/SIMv2/TB/DMAC_TOP_TB.sv>>
`define IP_VER 32'h000
`define CH_SFR_SIZE 32'h100
`define SRC_OFFSET 32'h0
`define DST_OFFSET 32'h4
`define LEN_OFFSET 32'h8
`define CMD_OFFSET 32'hc
`define STAT_OFFSET 32'h10

`define TIMEOUT_DELAY 99999999

`define SRC_REGION_START 32'h0000_0000
`define SRC_REGION_SIZE 32'h0000_2000
`define DST_REGION_STRIDE 32'h0000_2000

`define RANDOM_SEED 12123344
`define TEST_CNT 100

module DMAC_TOP_TB ();

    // inject random seed
    initial begin
        \$srandom(`RANDOM_SEED);
    end

    //----------------------------------------------------------
    // clock and reset generation
    //----------------------------------------------------------
    reg clk;
    reg rst_n;

    reg [3:0] data1, data2;
    initial begin
        data1 = 'b101x;
        data2 = 'b1010;
        \$display("Result: %d %d", data1===data2, data1!==data2);
        \$display("Result: %d %d", data1==data2, data1!=data2);
        data1 = 'b101x;
        data2 = 'b101x;
        \$display("Result: %d", data1===data2);
        \$display("Result: %d", data1==data2);
    end

    // clock generation
    initial begin
        clk = 1'b0;

        forever #10 clk = !clk;
    end

    // reset generation
    initial begin
        rst_n = 1'b0; // active at time 0

        repeat (3) @(posedge clk); // after 3 cycles,
        rst_n = 1'b1; // release the reset
    end

 // timeout
 initial begin
  #`TIMEOUT_DELAY \$display("Timeout!");
  \$finish;
 end

    // enable waveform dump
    initial begin
        \$dumpvars(0, u_DUT);
        \$dumpfile("dump.vcd");
    end

    //----------------------------------------------------------
    // Connection between DUT and test modules
    //----------------------------------------------------------
    APB apb_if (.clk(clk));

    AXI_AW_CH aw_ch (.clk(clk));
    AXI_W_CH w_ch (.clk(clk));
    AXI_B_CH b_ch (.clk(clk));
    AXI_AR_CH ar_ch (.clk(clk));
    AXI_R_CH r_ch (.clk(clk));

    DMAC_TOP u_DUT (
        .clk (clk),
        .rst_n (rst_n),

        // APB interface
        .psel_i (apb_if.psel),
        .penable_i (apb_if.penable),
        .paddr_i (apb_if.paddr[11:0]),
        .pwrite_i (apb_if.pwrite),
        .pwdata_i (apb_if.pwdata),
        .pready_o (apb_if.pready),
        .prdata_o (apb_if.prdata),
        .pslverr_o (apb_if.pslverr),

        // AXI AW channel
        .awid_o (aw_ch.awid),
        .awaddr_o (aw_ch.awaddr),
        .awlen_o (aw_ch.awlen),
        .awsize_o (aw_ch.awsize),
        .awburst_o (aw_ch.awburst),
        .awvalid_o (aw_ch.awvalid),
        .awready_i (aw_ch.awready),

        // AXI W channel
        .wid_o (w_ch.wid),
        .wdata_o (w_ch.wdata),
        .wstrb_o (w_ch.wstrb),
        .wlast_o (w_ch.wlast),
        .wvalid_o (w_ch.wvalid),
        .wready_i (w_ch.wready),

        // AXI B channel
        .bid_i (b_ch.bid),
        .bresp_i (b_ch.bresp),
        .bvalid_i (b_ch.bvalid),
        .bready_o (b_ch.bready),

        // AXI AR channel
        .arid_o (ar_ch.arid),
        .araddr_o (ar_ch.araddr),
        .arlen_o (ar_ch.arlen),
        .arsize_o (ar_ch.arsize),
        .arburst_o (ar_ch.arburst),
        .arvalid_o (ar_ch.arvalid),
        .arready_i (ar_ch.arready),

        // AXI R channel
        .rid_i (r_ch.rid),
        .rdata_i (r_ch.rdata),
        .rresp_i (r_ch.rresp),
        .rlast_i (r_ch.rlast),
        .rvalid_i (r_ch.rvalid),
        .rready_o (r_ch.rready)
    );

    AXI_SLAVE u_mem (
        .clk (clk),
        .rst_n (rst_n),

        .aw_ch (aw_ch),
        .w_ch (w_ch),
        .b_ch (b_ch),
        .ar_ch (ar_ch),
        .r_ch (r_ch)
    );

    //----------------------------------------------------------
    // Testbench starts
    //----------------------------------------------------------
    task test_init();
        int data;
        apb_if.init();

        @(posedge rst_n); // wait for a release of the reset
        repeat (10) @(posedge clk); // wait another 10 cycles

        apb_if.read(`IP_VER, data);
        \$display("---------------------------------------------------");
        \$display("IP version: %x", data);
        \$display("---------------------------------------------------");

        \$display("---------------------------------------------------");
        \$display("Load data to memory");
        \$display("---------------------------------------------------");
        for (int i=0; i<`SRC_REGION_SIZE; i=i+4) begin
            // write random data
            u_mem.write_word(`SRC_REGION_START+i, \$random);
        end
    endtask

    // this task must be declared automatic so that each invocation uses
    // different memories
    task automatic test_dma(input int ch, input int src, input int dst, input int len);
        int data;
        \$display("Ch[%d] DMA configure %x -> %x (%x)", ch, src, dst, len);
        apb_if.write((ch+1)*`CH_SFR_SIZE+`SRC_OFFSET, src);
        apb_if.read((ch+1)*`CH_SFR_SIZE+`SRC_OFFSET, data);
        if (data!==src) begin
            \$display("DMA_SRC[%d](fail): %x", ch, data);
            @(posedge clk);
            \$finish;
        end
        apb_if.write((ch+1)*`CH_SFR_SIZE+`DST_OFFSET, dst);
        apb_if.read((ch+1)*`CH_SFR_SIZE+`DST_OFFSET, data);
        if (data!==dst) begin
            \$display("DMA_DST[%d](fail): %x", ch, data);
            @(posedge clk);
            \$finish;
        end
        apb_if.write((ch+1)*`CH_SFR_SIZE+`LEN_OFFSET, len);
        apb_if.read((ch+1)*`CH_SFR_SIZE+`LEN_OFFSET, data);
        if (data!==len) begin
            \$display("DMA_LEN[%d](fail): %x", ch, data);
            @(posedge clk);
            \$finish;
        end

        \$display("Ch[%d] DMA start", ch);
        apb_if.write((ch+1)*`CH_SFR_SIZE+`CMD_OFFSET, 32'h1);

        data = 0;
        while (data!=1) begin
            apb_if.read((ch+1)*`CH_SFR_SIZE+`STAT_OFFSET, data);
            repeat (100) @(posedge clk);
            \$display("Ch[%d] Waiting for a DMA completion", ch);
        end
        @(posedge clk);

        \$display("Ch[%d] DMA completed", ch);
        for (int i=0; i<len; i=i+4) begin
            logic [31:0] src_word;
            logic [31:0] dst_word;
            src_word = u_mem.read_word(src+i);
            dst_word = u_mem.read_word(dst+i);
            if (src_word!==dst_word) begin
                \$display("Ch[%d] Mismatch! (src:%x @%x, dst:%x @%x", ch, src_word, src+i, dst_word, dst+i);
                @(posedge clk);
                \$finish;
            end
        end
        \$display("Ch[%d] DMA passed", ch);
    endtask

    // this task must be declared automatic so that each invocation uses
    // different memories
    task automatic test_channel(input int ch, input int test_cnt);
        int src_offset, dst_offset, len;
        src_offset = 0;
        dst_offset = 0;

        for (int i=0; i<test_cnt; i++) begin
            len = 'h0100;
            test_dma(ch, `SRC_REGION_START+src_offset, (ch+1)*`DST_REGION_STRIDE+dst_offset, len);
            src_offset = src_offset + len;
            dst_offset = dst_offset + len;
        end
    endtask


    // main
    initial begin
        test_init();

        // run 4 channel tests simultaneously
        fork
            test_channel(0, `TEST_CNT);
            test_channel(1, `TEST_CNT);
            test_channel(2, `TEST_CNT);
            test_channel(3, `TEST_CNT);
        join

        \$finish;
    end


endmodule
<<EndOfFile:DMAC/SIMv2/TB/DMAC_TOP_TB.sv>>

<<StartOfFile:DMAC/SIMv2/TB/AXI_INTF.sv>>
`include "../TB/AXI_TYPEDEF.svh"

interface AXI_AW_CH
#(
    parameter ADDR_WIDTH = `AXI_ADDR_WIDTH,
    parameter ID_WIDTH = `AXI_ID_WIDTH
 )
(
    input clk
);
    logic awvalid;
    logic awready;
    logic [ID_WIDTH-1:0] awid;
    logic [ADDR_WIDTH-1:0] awaddr;
    logic [3:0] awlen;
    logic [2:0] awsize;
    logic [1:0] awburst;

endinterface

interface AXI_W_CH
#(
    parameter DATA_WIDTH = `AXI_DATA_WIDTH,
    parameter ID_WIDTH = `AXI_ID_WIDTH
 )
(
    input clk
);
    logic wvalid;
    logic wready;
    logic [ID_WIDTH-1:0] wid;
    logic [DATA_WIDTH-1:0] wdata;
    logic [DATA_WIDTH/8-1:0] wstrb;
    logic wlast;

endinterface

interface AXI_B_CH
#(
    parameter ID_WIDTH = `AXI_ID_WIDTH
 )
(
    input clk
);
    logic bvalid;
    logic bready;
    logic [ID_WIDTH-1:0] bid;
    logic [1:0] bresp;

endinterface

interface AXI_AR_CH
#(
    parameter ADDR_WIDTH = `AXI_ADDR_WIDTH,
    parameter ID_WIDTH = `AXI_ID_WIDTH
 )
(
    input clk
);
    logic arvalid;
    logic arready;
    logic [ID_WIDTH-1:0] arid;
    logic [ADDR_WIDTH-1:0] araddr;
    logic [3:0] arlen;
    logic [2:0] arsize;
    logic [1:0] arburst;

endinterface

interface AXI_R_CH
#(
    parameter DATA_WIDTH = `AXI_DATA_WIDTH,
    parameter ID_WIDTH = `AXI_ID_WIDTH
 )
(
    input clk
);
    logic rvalid;
    logic rready;
    logic [ID_WIDTH-1:0] rid;
    logic [DATA_WIDTH-1:0] rdata;
    logic [1:0] rresp;
    logic rlast;

endinterface

interface APB (
    input clk
);
    logic psel;
    logic penable;
    logic [31:0] paddr;
    logic pwrite;
    logic [31:0] pwdata;
    logic pready;
    logic [31:0] prdata;
    logic pslverr;

    // a semaphore to allow only one access at a time
    semaphore sema;
    initial begin
        sema = new(1);
    end

    modport master (
        input clk,
        input pready, prdata, pslverr,
        output psel, penable, paddr, pwrite, pwdata
    );

    task init();
        psel = 1'b0;
        penable = 1'b0;
        paddr = 32'd0;
        pwrite = 1'b0;
        pwdata = 32'd0;
    endtask

    task automatic write(input int addr,
                         input int data);
        // during a write, another threads cannot access APB
        sema.get(1);
        #1
        psel = 1'b1;
        penable = 1'b0;
        paddr = addr;
        pwrite = 1'b1;
        pwdata = data;
        @(posedge clk);
        #1
        penable = 1'b1;
        @(posedge clk);

        while (pready==1'b0) begin
            @(posedge clk);
        end

        psel = 1'b0;
        penable = 1'b0;
        paddr = 'hX;
        pwrite = 1'bx;
        pwdata = 'hX;

        // release the semaphore
        sema.put(1);
    endtask

    task automatic read(input int addr,
                        output int data);
        // during a read, another threads cannot access APB
        sema.get(1);

        #1
        psel = 1'b1;
        penable = 1'b0;
        paddr = addr;
        pwrite = 1'b0;
        pwdata = 'hX;
        @(posedge clk);
        #1
        penable = 1'b1;
        @(posedge clk);

        while (pready==1'b0) begin
            @(posedge clk);
        end
        data = prdata;

        psel = 1'b0;
        penable = 1'b0;
        paddr = 'hX;
        pwrite = 1'bx;
        pwdata = 'hX;

        // release the semaphore
        sema.put(1);
    endtask

endinterface
<<EndOfFile:DMAC/SIMv2/TB/AXI_INTF.sv>>

<<StartOfFile:DMAC/SIMv2/TB/AXI_SLAVE.sv>>
`include "../TB/AXI_TYPEDEF.svh"

module AXI_SLAVE
#(
    parameter ADDR_WIDTH = 16,
    parameter DATA_WIDTH = `AXI_DATA_WIDTH,
    parameter ID_WIDTH = `AXI_ID_WIDTH,
    parameter AWREADY_DELAY = 1,
    parameter ARREADY_DELAY = 1,
    parameter AR2R_DELAY = 50
)
(
    input wire clk,
    input wire rst_n, // _n means active low

    AXI_AW_CH aw_ch,
    AXI_W_CH w_ch,
    AXI_B_CH b_ch,
    AXI_AR_CH ar_ch,
    AXI_R_CH r_ch
);

    localparam DATA_DEPTH = 1<<ADDR_WIDTH;

    logic [7:0] mem[DATA_DEPTH];

    function void write_byte(int addr, input bit [7:0] wdata);
        mem[addr] = wdata;
    endfunction

    function void write_word(int addr, input bit [31:0] wdata);
        for (int i=0; i<4; i++) begin
            write_byte(addr+i, wdata[8*i +: 8]); // [i*8+7:i*8]
        end
    endfunction

    function bit [7:0] read_byte(int addr);
        read_byte = mem[addr];
    endfunction

    function bit [31:0] read_word(int addr);
        for (int i=0; i<4; i++) begin
            read_word[8*i +: 8] = read_byte(addr+i);// [i*8+7:i*8]
        end
    endfunction

    //----------------------------------------------------------
    // write channels (AW, W, B)
    //----------------------------------------------------------
    localparam logic [1:0] S_W_IDLE = 0,
                                S_W_AWREADY = 1,
                                S_W_BURST = 2,
                                S_W_RESP = 3;

    logic [1:0] wstate, wstate_n;
    logic [7:0] wcnt, wcnt_n;

    logic [ADDR_WIDTH-1:0] waddr, waddr_n;
    logic [ID_WIDTH-1:0] wid, wid_n;
    logic [3:0] wlen, wlen_n;

    always_ff @(posedge clk)
        if (!rst_n) begin
            wstate <= S_W_IDLE;

            wcnt <= 8'd0;
            waddr <= {ADDR_WIDTH{1'b0}};
            wid <= {ID_WIDTH{1'b0}};
            wlen <= 4'd0;
        end
        else begin
            wstate <= wstate_n;

            wcnt <= wcnt_n;
            waddr <= waddr_n;
            wid <= wid_n;
            wlen <= wlen_n;
        end

    always @(*) begin
        wstate_n = wstate;

        wcnt_n = wcnt;
        waddr_n = waddr;
        wid_n = wid;
        wlen_n = wlen;

        aw_ch.awready = 1'b0;
        w_ch.wready = 1'b0;
        b_ch.bvalid = 1'b0;

        case (wstate)
            S_W_IDLE: begin
                if (aw_ch.awvalid) begin
                    if (AWREADY_DELAY == 0) begin
                        waddr_n = aw_ch.awaddr;
                        wid_n = aw_ch.awid;
                        wlen_n = aw_ch.awlen;
                        aw_ch.awready = 1'b1;
                        wstate_n = S_W_BURST;
                    end
                    else begin
                        wcnt_n = AWREADY_DELAY-1;
                        wstate_n = S_W_AWREADY;
                    end
                end
            end
            S_W_AWREADY: begin
                if (wcnt==0) begin
                    waddr_n = aw_ch.awaddr;
                    wid_n = aw_ch.awid;
                    wlen_n = aw_ch.awlen;
                    aw_ch.awready = 1'b1;
                    wstate_n = S_W_BURST;
                end
                else begin
                    wcnt_n = wcnt - 8'd1;
                end
            end
            S_W_BURST: begin
                w_ch.wready = 1'b1;
                if (w_ch.wvalid) begin
                    for (int i=0; i<DATA_WIDTH/8; i++) begin
                        write_byte(waddr + i, w_ch.wdata[i*8 +: 8]); // [i*8+7:i*8]
                    end
                    waddr_n = waddr + (DATA_WIDTH/8);
                    if (wlen==4'd0) begin
                        wstate_n = S_W_RESP;
                    end
                    else begin
                        wlen_n = wlen - 4'd1;
                    end
                end
            end
            S_W_RESP: begin
                b_ch.bvalid = 1'b1;
                if (b_ch.bready) begin
                    wstate_n = S_W_IDLE;
                end
            end
        endcase
    end

    //----------------------------------------------------------
    // read channel (AR, R)
    //----------------------------------------------------------
    localparam logic [1:0] S_R_IDLE = 0,
                                S_R_ARREADY = 1,
                                S_R_DELAY = 2,
                                S_R_BURST = 3;

    logic [1:0] rstate, rstate_n;
    logic [7:0] rcnt, rcnt_n;

    logic [ADDR_WIDTH-1:0] raddr, raddr_n;
    logic [ID_WIDTH-1:0] rid, rid_n;
    logic [3:0] rlen, rlen_n;

    always_ff @(posedge clk)
        if (!rst_n) begin
            rstate <= S_R_IDLE;

            rcnt <= 8'd0;
            raddr <= {ADDR_WIDTH{1'b0}};
            rid <= {ID_WIDTH{1'b0}};
            rlen <= 4'd0;
        end
        else begin
            rstate <= rstate_n;

            rcnt <= rcnt_n;
            raddr <= raddr_n;
            rid <= rid_n;
            rlen <= rlen_n;
        end

    always_comb begin
        rstate_n = rstate;

        rcnt_n = rcnt;
        raddr_n = raddr;
        rid_n = rid;
        rlen_n = rlen;

        ar_ch.arready = 1'b0;
        r_ch.rvalid = 1'b0;
        r_ch.rlast = 1'b0;

        case (rstate)
            S_R_IDLE: begin
                if (ar_ch.arvalid) begin
                    if (ARREADY_DELAY == 0) begin
                        raddr_n = ar_ch.araddr;
                        rid_n = ar_ch.arid;
                        rlen_n = ar_ch.arlen;
                        ar_ch.arready = 1'b1;

                        rcnt_n = AR2R_DELAY - 1;
                        rstate_n = S_R_DELAY;
                    end
                    else begin
                        rcnt_n = ARREADY_DELAY-1;
                        rstate_n = S_R_ARREADY;
                    end
                end
            end
            S_R_ARREADY: begin
                if (rcnt==0) begin
                    raddr_n = ar_ch.araddr;
                    rid_n = ar_ch.arid;
                    rlen_n = ar_ch.arlen;
                    ar_ch.arready = 1'b1;

                    rcnt_n = AR2R_DELAY - 1;
                    rstate_n = S_R_DELAY;
                end
                else begin
                    rcnt_n = rcnt - 8'd1;
                end
            end
            S_R_DELAY: begin
                if (rcnt==0) begin
                    rstate_n = S_R_BURST;
                end
                else begin
                    rcnt_n = rcnt - 8'd1;
                end
            end
            S_R_BURST: begin
                r_ch.rvalid = 1'b1;
                r_ch.rlast = (rlen==4'd0);
                for (int i=0; i<DATA_WIDTH/8; i++) begin
                    r_ch.rdata[i*8 +: 8] = read_byte(raddr + i); // [i*8+7:i*8]
                end
                if (r_ch.rready) begin
                    raddr_n = raddr + (DATA_WIDTH/8);
                    if (rlen==4'd0) begin
                        rstate_n = S_R_IDLE;
                    end
                    else begin
                        rlen_n = rlen - 4'd1;
                    end
                end
            end
        endcase
    end

    // output assignments
    assign b_ch.bid = wid;
    assign b_ch.bresp = 2'd0;
    assign r_ch.rid = rid;
    assign r_ch.rresp = 2'd0;

endmodule
<<EndOfFile:DMAC/SIMv2/TB/AXI_SLAVE.sv>>

<<StartOfFile:DMAC/SIMv2/TB/AXI_TYPEDEF.svh>>
`ifndef __AXI_TYPEDEF_SVH__
`define __AXI_TYPEDEF_SVH__

`define AXI_ADDR_WIDTH 32
`define AXI_DATA_WIDTH 32
`define AXI_ID_WIDTH 4

`endif /* __AXI_TYPEDEF_SVH__ */
<<EndOfFile:DMAC/SIMv2/TB/AXI_TYPEDEF.svh>>