// 3-stage pipelined multiplier (LUT-based, no DSP) // latency=3, throughput=1 result/cycle (* use_dsp = "no", use_dsp48 = "no", syn_multstyle = "logic" *) module pipelined_mult_32x32 ( input clk, input rstn, input [31:0] a, input [31:0] b, output reg [63:0] p ); reg [31:0] a_r, b_r; (* use_dsp = "no", use_dsp48 = "no", syn_multstyle = "logic" *) reg [63:0] prod; function automatic [63:0] lut_mul32; input [31:0] lhs; input [31:0] rhs; integer bit_idx; reg [63:0] acc; begin acc = 64'b0; for (bit_idx = 0; bit_idx < 32; bit_idx = bit_idx + 1) begin if (rhs[bit_idx]) acc = acc + ({32'b0, lhs} << bit_idx); end lut_mul32 = acc; end endfunction always @(posedge clk) begin if (!rstn) begin a_r <= 32'b0; b_r <= 32'b0; prod <= 64'b0; p <= 64'b0; end else begin a_r <= a; // Stage 1: register inputs b_r <= b; prod <= lut_mul32(a_r, b_r); // Stage 2: LUT-based multiply p <= prod; // Stage 3: register output end end endmodule // AXI Burst Slave + Ping-Pong + Stream CRC32 module matmul_axi_slave ( input clk, input resetn, // AXI Write Address Channel input [4:0] s_axi_awid, input [31:0] s_axi_awaddr, input [7:0] s_axi_awlen, input [2:0] s_axi_awsize, input [1:0] s_axi_awburst, input s_axi_awlock, input [3:0] s_axi_awcache, input [2:0] s_axi_awprot, input s_axi_awvalid, output reg s_axi_awready, // AXI Write Data Channel input [4:0] s_axi_wid, input [31:0] s_axi_wdata, input [3:0] s_axi_wstrb, input s_axi_wlast, input s_axi_wvalid, output reg s_axi_wready, // AXI Write Response Channel output [4:0] s_axi_bid, output [1:0] s_axi_bresp, output reg s_axi_bvalid, input s_axi_bready, // AXI Read Address Channel input [4:0] s_axi_arid, input [31:0] s_axi_araddr, input [7:0] s_axi_arlen, input [2:0] s_axi_arsize, input [1:0] s_axi_arburst, input s_axi_arlock, input [3:0] s_axi_arcache, input [2:0] s_axi_arprot, input s_axi_arvalid, output reg s_axi_arready, // AXI Read Data Channel output reg [4:0] s_axi_rid, output reg [31:0] s_axi_rdata, output [1:0] s_axi_rresp, output reg s_axi_rlast, output reg s_axi_rvalid, input s_axi_rready, // AXI master port for autonomous matrix stream load/store output reg [3:0] m_axi_awid, output reg [31:0] m_axi_awaddr, output reg [7:0] m_axi_awlen, output reg [2:0] m_axi_awsize, output reg [1:0] m_axi_awburst, output reg m_axi_awlock, output reg [3:0] m_axi_awcache, output reg [2:0] m_axi_awprot, output reg m_axi_awvalid, input m_axi_awready, output reg [31:0] m_axi_wdata, output reg [3:0] m_axi_wstrb, output reg m_axi_wlast, output reg m_axi_wvalid, input m_axi_wready, input [3:0] m_axi_bid, input [1:0] m_axi_bresp, input m_axi_bvalid, output reg m_axi_bready, output reg [3:0] m_axi_arid, output reg [31:0] m_axi_araddr, output reg [7:0] m_axi_arlen, output reg [2:0] m_axi_arsize, output reg [1:0] m_axi_arburst, output reg m_axi_arlock, output reg [3:0] m_axi_arcache, output reg [2:0] m_axi_arprot, output reg m_axi_arvalid, input m_axi_arready, input [3:0] m_axi_rid, input [31:0] m_axi_rdata, input [1:0] m_axi_rresp, input m_axi_rlast, input m_axi_rvalid, output reg m_axi_rready, // Interrupt output (active high level, pulsed by calc_done) output intr ); // ========================================== // Memory Map // ========================================== localparam ADDR_CTRL = 12'h000; localparam ADDR_STATUS = 12'h004; localparam ADDR_CRC = 12'h010; localparam ADDR_DMA_SRC_BASE = 12'h020; localparam ADDR_DMA_DST_BASE = 12'h024; localparam ADDR_DMA_GROUPS = 12'h028; localparam ADDR_DMA_SRC_STRIDE = 12'h02C; localparam ADDR_DMA_DST_STRIDE = 12'h030; localparam ADDR_B0_A = 12'h100; localparam ADDR_B0_B = 12'h140; localparam ADDR_B0_C = 12'h180; localparam ADDR_B1_A = 12'h300; localparam ADDR_B1_B = 12'h340; localparam ADDR_B1_C = 12'h380; // ========================================== // Registers and data buffers // ========================================== reg req_0, req_1; reg busy; reg [1:0] done_count; reg error; wire done = (done_count != 2'd0); reg [31:0] b0_a [0:15]; reg [31:0] b0_b [0:15]; reg [65:0] b0_c [0:15]; reg [31:0] b1_a [0:15]; reg [31:0] b1_b [0:15]; reg [65:0] b1_c [0:15]; reg [31:0] dma_src_base; reg [31:0] dma_dst_base; reg [31:0] dma_groups; reg [31:0] dma_src_stride; reg [31:0] dma_dst_stride; reg dma_busy; reg dma_done; reg [31:0] load_group; reg [31:0] store_group; reg [31:0] dma_next_rd_addr; reg [31:0] dma_next_wr_addr; reg [31:0] completed_groups; reg [1:0] bank_loaded; reg [1:0] bank_store_pending; reg [1:0] bank_storing; reg [1:0] bank_compute_pending; reg dma_compute_bank; reg dma_load_bank; reg dma_store_bank; reg dma_start_pulse; reg dma_done_clear_pulse; reg dma_req0_pulse; reg dma_req1_pulse; function [3:0] c_elem_from_word; input [5:0] word_idx; begin case (word_idx) 6'd0, 6'd1, 6'd2: c_elem_from_word = 4'd0; 6'd3, 6'd4, 6'd5: c_elem_from_word = 4'd1; 6'd6, 6'd7, 6'd8: c_elem_from_word = 4'd2; 6'd9, 6'd10, 6'd11: c_elem_from_word = 4'd3; 6'd12, 6'd13, 6'd14: c_elem_from_word = 4'd4; 6'd15, 6'd16, 6'd17: c_elem_from_word = 4'd5; 6'd18, 6'd19, 6'd20: c_elem_from_word = 4'd6; 6'd21, 6'd22, 6'd23: c_elem_from_word = 4'd7; 6'd24, 6'd25, 6'd26: c_elem_from_word = 4'd8; 6'd27, 6'd28, 6'd29: c_elem_from_word = 4'd9; 6'd30, 6'd31, 6'd32: c_elem_from_word = 4'd10; 6'd33, 6'd34, 6'd35: c_elem_from_word = 4'd11; 6'd36, 6'd37, 6'd38: c_elem_from_word = 4'd12; 6'd39, 6'd40, 6'd41: c_elem_from_word = 4'd13; 6'd42, 6'd43, 6'd44: c_elem_from_word = 4'd14; default: c_elem_from_word = 4'd15; endcase end endfunction function [1:0] c_part_from_word; input [5:0] word_idx; begin case (word_idx) 6'd0, 6'd3, 6'd6, 6'd9, 6'd12, 6'd15, 6'd18, 6'd21, 6'd24, 6'd27, 6'd30, 6'd33, 6'd36, 6'd39, 6'd42, 6'd45: c_part_from_word = 2'd0; 6'd1, 6'd4, 6'd7, 6'd10, 6'd13, 6'd16, 6'd19, 6'd22, 6'd25, 6'd28, 6'd31, 6'd34, 6'd37, 6'd40, 6'd43, 6'd46: c_part_from_word = 2'd1; default: c_part_from_word = 2'd2; endcase end endfunction function [31:0] apply_wstrb; input [31:0] old_value; input [31:0] new_value; input [3:0] strobe; begin apply_wstrb = old_value; if (strobe[0]) apply_wstrb[7:0] = new_value[7:0]; if (strobe[1]) apply_wstrb[15:8] = new_value[15:8]; if (strobe[2]) apply_wstrb[23:16] = new_value[23:16]; if (strobe[3]) apply_wstrb[31:24] = new_value[31:24]; end endfunction // ========================================== // CRC32 Engine (IEEE 802.3, polynomial 0xEDB88320) // // AXI-stream: single-cycle crc32_update_word (4 LUT lookup depth, ok for // AXI timing—the bus runs at or below internal compute rate). // DMA-calc: 2-stage pipelined word engine (2 bytes/stage) + 6-cycle // serial FSM for 66-bit C-elements, with 16-deep FIFO to absorb the // compute pipeline burst. Critical path: 2 table lookups + 1 XOR per // stage vs 12 table lookups in the original single-cycle design. // ========================================== reg [31:0] crc32_table [0:255]; reg [31:0] crc_reg; // ---- AXI-stream CRC (single-cycle) ---------------------------------- wire [31:0] crc_stream_data = s_axi_rdata; wire crc_axi_valid = (r_state == R_DATA) && s_axi_rvalid && s_axi_rready && ((r_offset >= ADDR_B0_C && r_offset < (ADDR_B0_C + 12'h0C0)) || (r_offset >= ADDR_B1_C && r_offset < (ADDR_B1_C + 12'h0C0))); wire crc_stream_valid = crc_axi_valid; wire [31:0] crc_next = crc32_update_word(crc_reg, crc_stream_data); function [31:0] crc32_update_word; input [31:0] crc_in; input [31:0] data_in; reg [31:0] c0; reg [31:0] c1; reg [31:0] c2; begin c0 = crc32_table[(crc_in[7:0] ^ data_in[7:0])] ^ {8'h00, crc_in[31:8]}; c1 = crc32_table[(c0[7:0] ^ data_in[15:8])] ^ {8'h00, c0[31:8]}; c2 = crc32_table[(c1[7:0] ^ data_in[23:16])] ^ {8'h00, c1[31:8]}; crc32_update_word = crc32_table[(c2[7:0] ^ data_in[31:24])] ^ {8'h00, c2[31:8]}; end endfunction // ---- DMA-calc 3-stage shift register pipeline ----------------------- // Stage 0: capture acc_result, compute word0 CRC. // Stage 1: shift, compute word1 CRC. // Final: crc_reg updated when stage 1 valid (word2 CRC). // Latency 3 cycles; compute FSM inserts issue gaps so result spacing // is ≥3 cycles, ensuring crc_reg is committed before the next capture. reg crc_c66_s0_valid, crc_c66_s1_valid; reg [31:0] crc_c66_s0_crc, crc_c66_s1_crc; reg [31:0] crc_c66_s0_word1, crc_c66_s1_word2; reg [31:0] crc_c66_s0_word2; reg crc_c66_s0_last, crc_c66_s1_last; wire [31:0] crc_c66_s0_next = crc32_update_word(crc_reg, acc_result[31:0]); wire [31:0] crc_c66_s1_next = crc32_update_word(crc_c66_s0_crc, crc_c66_s0_word1); wire [31:0] crc_c66_final_next = crc32_update_word(crc_c66_s1_crc, crc_c66_s1_word2); wire crc_c66_busy = crc_c66_s0_valid || crc_c66_s1_valid; initial begin crc32_table[0 ] = 32'h00000000; crc32_table[1 ] = 32'h77073096; crc32_table[2 ] = 32'hEE0E612C; crc32_table[3 ] = 32'h990951BA; crc32_table[4 ] = 32'h076DC419; crc32_table[5 ] = 32'h706AF48F; crc32_table[6 ] = 32'hE963A535; crc32_table[7 ] = 32'h9E6495A3; crc32_table[8 ] = 32'h0EDB8832; crc32_table[9 ] = 32'h79DCB8A4; crc32_table[10 ] = 32'hE0D5E91E; crc32_table[11 ] = 32'h97D2D988; crc32_table[12 ] = 32'h09B64C2B; crc32_table[13 ] = 32'h7EB17CBD; crc32_table[14 ] = 32'hE7B82D07; crc32_table[15 ] = 32'h90BF1D91; crc32_table[16 ] = 32'h1DB71064; crc32_table[17 ] = 32'h6AB020F2; crc32_table[18 ] = 32'hF3B97148; crc32_table[19 ] = 32'h84BE41DE; crc32_table[20 ] = 32'h1ADAD47D; crc32_table[21 ] = 32'h6DDDE4EB; crc32_table[22 ] = 32'hF4D4B551; crc32_table[23 ] = 32'h83D385C7; crc32_table[24 ] = 32'h136C9856; crc32_table[25 ] = 32'h646BA8C0; crc32_table[26 ] = 32'hFD62F97A; crc32_table[27 ] = 32'h8A65C9EC; crc32_table[28 ] = 32'h14015C4F; crc32_table[29 ] = 32'h63066CD9; crc32_table[30 ] = 32'hFA0F3D63; crc32_table[31 ] = 32'h8D080DF5; crc32_table[32 ] = 32'h3B6E20C8; crc32_table[33 ] = 32'h4C69105E; crc32_table[34 ] = 32'hD56041E4; crc32_table[35 ] = 32'hA2677172; crc32_table[36 ] = 32'h3C03E4D1; crc32_table[37 ] = 32'h4B04D447; crc32_table[38 ] = 32'hD20D85FD; crc32_table[39 ] = 32'hA50AB56B; crc32_table[40 ] = 32'h35B5A8FA; crc32_table[41 ] = 32'h42B2986C; crc32_table[42 ] = 32'hDBBBC9D6; crc32_table[43 ] = 32'hACBCF940; crc32_table[44 ] = 32'h32D86CE3; crc32_table[45 ] = 32'h45DF5C75; crc32_table[46 ] = 32'hDCD60DCF; crc32_table[47 ] = 32'hABD13D59; crc32_table[48 ] = 32'h26D930AC; crc32_table[49 ] = 32'h51DE003A; crc32_table[50 ] = 32'hC8D75180; crc32_table[51 ] = 32'hBFD06116; crc32_table[52 ] = 32'h21B4F4B5; crc32_table[53 ] = 32'h56B3C423; crc32_table[54 ] = 32'hCFBA9599; crc32_table[55 ] = 32'hB8BDA50F; crc32_table[56 ] = 32'h2802B89E; crc32_table[57 ] = 32'h5F058808; crc32_table[58 ] = 32'hC60CD9B2; crc32_table[59 ] = 32'hB10BE924; crc32_table[60 ] = 32'h2F6F7C87; crc32_table[61 ] = 32'h58684C11; crc32_table[62 ] = 32'hC1611DAB; crc32_table[63 ] = 32'hB6662D3D; crc32_table[64 ] = 32'h76DC4190; crc32_table[65 ] = 32'h01DB7106; crc32_table[66 ] = 32'h98D220BC; crc32_table[67 ] = 32'hEFD5102A; crc32_table[68 ] = 32'h71B18589; crc32_table[69 ] = 32'h06B6B51F; crc32_table[70 ] = 32'h9FBFE4A5; crc32_table[71 ] = 32'hE8B8D433; crc32_table[72 ] = 32'h7807C9A2; crc32_table[73 ] = 32'h0F00F934; crc32_table[74 ] = 32'h9609A88E; crc32_table[75 ] = 32'hE10E9818; crc32_table[76 ] = 32'h7F6A0DBB; crc32_table[77 ] = 32'h086D3D2D; crc32_table[78 ] = 32'h91646C97; crc32_table[79 ] = 32'hE6635C01; crc32_table[80 ] = 32'h6B6B51F4; crc32_table[81 ] = 32'h1C6C6162; crc32_table[82 ] = 32'h856530D8; crc32_table[83 ] = 32'hF262004E; crc32_table[84 ] = 32'h6C0695ED; crc32_table[85 ] = 32'h1B01A57B; crc32_table[86 ] = 32'h8208F4C1; crc32_table[87 ] = 32'hF50FC457; crc32_table[88 ] = 32'h65B0D9C6; crc32_table[89 ] = 32'h12B7E950; crc32_table[90 ] = 32'h8BBEB8EA; crc32_table[91 ] = 32'hFCB9887C; crc32_table[92 ] = 32'h62DD1DDF; crc32_table[93 ] = 32'h15DA2D49; crc32_table[94 ] = 32'h8CD37CF3; crc32_table[95 ] = 32'hFBD44C65; crc32_table[96 ] = 32'h4DB26158; crc32_table[97 ] = 32'h3AB551CE; crc32_table[98 ] = 32'hA3BC0074; crc32_table[99 ] = 32'hD4BB30E2; crc32_table[100] = 32'h4ADFA541; crc32_table[101] = 32'h3DD895D7; crc32_table[102] = 32'hA4D1C46D; crc32_table[103] = 32'hD3D6F4FB; crc32_table[104] = 32'h4369E96A; crc32_table[105] = 32'h346ED9FC; crc32_table[106] = 32'hAD678846; crc32_table[107] = 32'hDA60B8D0; crc32_table[108] = 32'h44042D73; crc32_table[109] = 32'h33031DE5; crc32_table[110] = 32'hAA0A4C5F; crc32_table[111] = 32'hDD0D7CC9; crc32_table[112] = 32'h5005713C; crc32_table[113] = 32'h270241AA; crc32_table[114] = 32'hBE0B1010; crc32_table[115] = 32'hC90C2086; crc32_table[116] = 32'h5768B525; crc32_table[117] = 32'h206F85B3; crc32_table[118] = 32'hB966D409; crc32_table[119] = 32'hCE61E49F; crc32_table[120] = 32'h5EDEF90E; crc32_table[121] = 32'h29D9C998; crc32_table[122] = 32'hB0D09822; crc32_table[123] = 32'hC7D7A8B4; crc32_table[124] = 32'h59B33D17; crc32_table[125] = 32'h2EB40D81; crc32_table[126] = 32'hB7BD5C3B; crc32_table[127] = 32'hC0BA6CAD; crc32_table[128] = 32'hEDB88320; crc32_table[129] = 32'h9ABFB3B6; crc32_table[130] = 32'h03B6E20C; crc32_table[131] = 32'h74B1D29A; crc32_table[132] = 32'hEAD54739; crc32_table[133] = 32'h9DD277AF; crc32_table[134] = 32'h04DB2615; crc32_table[135] = 32'h73DC1683; crc32_table[136] = 32'hE3630B12; crc32_table[137] = 32'h94643B84; crc32_table[138] = 32'h0D6D6A3E; crc32_table[139] = 32'h7A6A5AA8; crc32_table[140] = 32'hE40ECF0B; crc32_table[141] = 32'h9309FF9D; crc32_table[142] = 32'h0A00AE27; crc32_table[143] = 32'h7D079EB1; crc32_table[144] = 32'hF00F9344; crc32_table[145] = 32'h8708A3D2; crc32_table[146] = 32'h1E01F268; crc32_table[147] = 32'h6906C2FE; crc32_table[148] = 32'hF762575D; crc32_table[149] = 32'h806567CB; crc32_table[150] = 32'h196C3671; crc32_table[151] = 32'h6E6B06E7; crc32_table[152] = 32'hFED41B76; crc32_table[153] = 32'h89D32BE0; crc32_table[154] = 32'h10DA7A5A; crc32_table[155] = 32'h67DD4ACC; crc32_table[156] = 32'hF9B9DF6F; crc32_table[157] = 32'h8EBEEFF9; crc32_table[158] = 32'h17B7BE43; crc32_table[159] = 32'h60B08ED5; crc32_table[160] = 32'hD6D6A3E8; crc32_table[161] = 32'hA1D1937E; crc32_table[162] = 32'h38D8C2C4; crc32_table[163] = 32'h4FDFF252; crc32_table[164] = 32'hD1BB67F1; crc32_table[165] = 32'hA6BC5767; crc32_table[166] = 32'h3FB506DD; crc32_table[167] = 32'h48B2364B; crc32_table[168] = 32'hD80D2BDA; crc32_table[169] = 32'hAF0A1B4C; crc32_table[170] = 32'h36034AF6; crc32_table[171] = 32'h41047A60; crc32_table[172] = 32'hDF60EFC3; crc32_table[173] = 32'hA867DF55; crc32_table[174] = 32'h316E8EEF; crc32_table[175] = 32'h4669BE79; crc32_table[176] = 32'hCB61B38C; crc32_table[177] = 32'hBC66831A; crc32_table[178] = 32'h256FD2A0; crc32_table[179] = 32'h5268E236; crc32_table[180] = 32'hCC0C7795; crc32_table[181] = 32'hBB0B4703; crc32_table[182] = 32'h220216B9; crc32_table[183] = 32'h5505262F; crc32_table[184] = 32'hC5BA3BBE; crc32_table[185] = 32'hB2BD0B28; crc32_table[186] = 32'h2BB45A92; crc32_table[187] = 32'h5CB36A04; crc32_table[188] = 32'hC2D7FFA7; crc32_table[189] = 32'hB5D0CF31; crc32_table[190] = 32'h2CD99E8B; crc32_table[191] = 32'h5BDEAE1D; crc32_table[192] = 32'h9B64C2B0; crc32_table[193] = 32'hEC63F226; crc32_table[194] = 32'h756AA39C; crc32_table[195] = 32'h026D930A; crc32_table[196] = 32'h9C0906A9; crc32_table[197] = 32'hEB0E363F; crc32_table[198] = 32'h72076785; crc32_table[199] = 32'h05005713; crc32_table[200] = 32'h95BF4A82; crc32_table[201] = 32'hE2B87A14; crc32_table[202] = 32'h7BB12BAE; crc32_table[203] = 32'h0CB61B38; crc32_table[204] = 32'h92D28E9B; crc32_table[205] = 32'hE5D5BE0D; crc32_table[206] = 32'h7CDCEFB7; crc32_table[207] = 32'h0BDBDF21; crc32_table[208] = 32'h86D3D2D4; crc32_table[209] = 32'hF1D4E242; crc32_table[210] = 32'h68DDB3F8; crc32_table[211] = 32'h1FDA836E; crc32_table[212] = 32'h81BE16CD; crc32_table[213] = 32'hF6B9265B; crc32_table[214] = 32'h6FB077E1; crc32_table[215] = 32'h18B74777; crc32_table[216] = 32'h88085AE6; crc32_table[217] = 32'hFF0F6A70; crc32_table[218] = 32'h66063BCA; crc32_table[219] = 32'h11010B5C; crc32_table[220] = 32'h8F659EFF; crc32_table[221] = 32'hF862AE69; crc32_table[222] = 32'h616BFFD3; crc32_table[223] = 32'h166CCF45; crc32_table[224] = 32'hA00AE278; crc32_table[225] = 32'hD70DD2EE; crc32_table[226] = 32'h4E048354; crc32_table[227] = 32'h3903B3C2; crc32_table[228] = 32'hA7672661; crc32_table[229] = 32'hD06016F7; crc32_table[230] = 32'h4969474D; crc32_table[231] = 32'h3E6E77DB; crc32_table[232] = 32'hAED16A4A; crc32_table[233] = 32'hD9D65ADC; crc32_table[234] = 32'h40DF0B66; crc32_table[235] = 32'h37D83BF0; crc32_table[236] = 32'hA9BCAE53; crc32_table[237] = 32'hDEBB9EC5; crc32_table[238] = 32'h47B2CF7F; crc32_table[239] = 32'h30B5FFE9; crc32_table[240] = 32'hBDBDF21C; crc32_table[241] = 32'hCABAC28A; crc32_table[242] = 32'h53B39330; crc32_table[243] = 32'h24B4A3A6; crc32_table[244] = 32'hBAD03605; crc32_table[245] = 32'hCDD70693; crc32_table[246] = 32'h54DE5729; crc32_table[247] = 32'h23D967BF; crc32_table[248] = 32'hB3667A2E; crc32_table[249] = 32'hC4614AB8; crc32_table[250] = 32'h5D681B02; crc32_table[251] = 32'h2A6F2B94; crc32_table[252] = 32'hB40BBE37; crc32_table[253] = 32'hC30C8EA1; crc32_table[254] = 32'h5A05DF1B; crc32_table[255] = 32'h2D02EF8D; end // ========================================== // AXI Write (INCR Burst) // ========================================== localparam W_IDLE = 2'd0; localparam W_DATA = 2'd1; localparam W_RESP = 2'd2; reg [1:0] w_state; reg [31:0] w_addr; reg [4:0] w_id; reg [1:0] w_burst; wire [11:0] w_offset = w_addr[11:0]; assign s_axi_bid = w_id; assign s_axi_bresp = 2'b00; localparam C_IDLE = 2'd0; localparam C_RUN = 2'd1; reg [1:0] state; reg cur_bank; reg calc_done_pulse; localparam DMA_RD_IDLE = 2'd0; localparam DMA_RD_ADDR = 2'd1; localparam DMA_RD_DATA = 2'd2; reg [1:0] dma_rd_state; reg [4:0] dma_rd_count; reg dma_rd_bank; wire [3:0] dma_rd_word_idx = dma_rd_count[3:0]; wire dma_rd_commit = (dma_rd_state == DMA_RD_DATA) && m_axi_rvalid && m_axi_rready; localparam DMA_WR_IDLE = 2'd0; localparam DMA_WR_ADDR = 2'd1; localparam DMA_WR_DATA = 2'd2; localparam DMA_WR_RESP = 2'd3; reg [1:0] dma_wr_state; reg [5:0] dma_wr_count; wire crc_clear_pulse = (w_state == W_DATA) && s_axi_wvalid && s_axi_wready && (w_offset == ADDR_CRC); wire done_clear_pulse = (w_state == W_DATA) && s_axi_wvalid && s_axi_wready && (w_offset == ADDR_STATUS) && s_axi_wdata[1]; always @(posedge clk) begin if (!resetn) begin w_state <= W_IDLE; s_axi_awready <= 1'b1; s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b0; req_0 <= 1'b0; req_1 <= 1'b0; done_count <= 2'd0; error <= 1'b0; dma_src_base <= 32'd0; dma_dst_base <= 32'd0; dma_groups <= 32'd0; dma_src_stride <= 32'd128; dma_dst_stride <= 32'd192; dma_start_pulse <= 1'b0; dma_done_clear_pulse <= 1'b0; end else begin dma_start_pulse <= 1'b0; dma_done_clear_pulse <= 1'b0; if (dma_rd_commit) begin if (dma_rd_bank == 1'b0) begin if (dma_rd_count < 5'd16) b0_a[dma_rd_word_idx] <= m_axi_rdata; else b0_b[dma_rd_word_idx] <= m_axi_rdata; end else begin if (dma_rd_count < 5'd16) b1_a[dma_rd_word_idx] <= m_axi_rdata; else b1_b[dma_rd_word_idx] <= m_axi_rdata; end end if (req_0 && state == C_RUN && cur_bank == 1'b0) req_0 <= 1'b0; if (req_1 && state == C_RUN && cur_bank == 1'b1) req_1 <= 1'b0; if (dma_req0_pulse) req_0 <= 1'b1; if (dma_req1_pulse) req_1 <= 1'b1; case (w_state) W_IDLE: begin s_axi_awready <= 1'b1; if (s_axi_awvalid && s_axi_awready) begin s_axi_awready <= 1'b0; s_axi_wready <= 1'b1; w_addr <= s_axi_awaddr; w_id <= s_axi_awid; w_burst <= s_axi_awburst; w_state <= W_DATA; end end W_DATA: begin if (s_axi_wvalid && s_axi_wready) begin if (w_offset == ADDR_CTRL) begin if (s_axi_wdata[0]) req_0 <= 1'b1; if (s_axi_wdata[1]) req_1 <= 1'b1; if (s_axi_wdata[8]) dma_start_pulse <= 1'b1; end else if (w_offset == ADDR_STATUS) begin if (s_axi_wdata[2]) error <= 1'b0; if (s_axi_wdata[4]) dma_done_clear_pulse <= 1'b1; end else if (w_offset == ADDR_DMA_SRC_BASE) dma_src_base <= apply_wstrb(dma_src_base, s_axi_wdata, s_axi_wstrb); else if (w_offset == ADDR_DMA_DST_BASE) dma_dst_base <= apply_wstrb(dma_dst_base, s_axi_wdata, s_axi_wstrb); else if (w_offset == ADDR_DMA_GROUPS) dma_groups <= apply_wstrb(dma_groups, s_axi_wdata, s_axi_wstrb); else if (w_offset == ADDR_DMA_SRC_STRIDE) dma_src_stride <= apply_wstrb(dma_src_stride, s_axi_wdata, s_axi_wstrb); else if (w_offset == ADDR_DMA_DST_STRIDE) dma_dst_stride <= apply_wstrb(dma_dst_stride, s_axi_wdata, s_axi_wstrb); // CRC register is now managed by the pipelined CRC engine else if (w_offset >= ADDR_B0_A && w_offset < ADDR_B0_B) b0_a[(w_offset - ADDR_B0_A) >> 2] <= apply_wstrb(b0_a[(w_offset - ADDR_B0_A) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B0_B && w_offset < ADDR_B0_C) b0_b[(w_offset - ADDR_B0_B) >> 2] <= apply_wstrb(b0_b[(w_offset - ADDR_B0_B) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B1_A && w_offset < ADDR_B1_B) b1_a[(w_offset - ADDR_B1_A) >> 2] <= apply_wstrb(b1_a[(w_offset - ADDR_B1_A) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset >= ADDR_B1_B && w_offset < ADDR_B1_C) b1_b[(w_offset - ADDR_B1_B) >> 2] <= apply_wstrb(b1_b[(w_offset - ADDR_B1_B) >> 2], s_axi_wdata, s_axi_wstrb); else if (w_offset != ADDR_CRC) begin error <= 1'b1; end if (w_burst == 2'b01) w_addr <= w_addr + 32'd4; if (s_axi_wlast) begin s_axi_wready <= 1'b0; s_axi_bvalid <= 1'b1; w_state <= W_RESP; end end end W_RESP: begin if (s_axi_bvalid && s_axi_bready) begin s_axi_bvalid <= 1'b0; s_axi_awready <= 1'b1; w_state <= W_IDLE; end end default: w_state <= W_IDLE; endcase case ({calc_done_pulse, done_clear_pulse}) 2'b10: if (done_count != 2'd3) done_count <= done_count + 2'd1; 2'b01: if (done_count != 2'd0) done_count <= done_count - 2'd1; default: done_count <= done_count; endcase end end // ========================================== // AXI Read (INCR Burst) // ========================================== localparam R_IDLE = 2'd0; localparam R_DATA = 2'd1; reg [1:0] r_state; reg [31:0] r_addr; reg [7:0] r_cnt; reg [7:0] r_len; reg [1:0] r_burst; wire [11:0] r_offset = r_addr[11:0]; assign s_axi_rresp = 2'b00; wire [5:0] c0_offset = (r_offset - ADDR_B0_C) >> 2; wire [3:0] c0_elem = c_elem_from_word(c0_offset); wire [1:0] c0_part = c_part_from_word(c0_offset); wire [65:0] target_c0 = b0_c[c0_elem]; wire [5:0] c1_offset = (r_offset - ADDR_B1_C) >> 2; wire [3:0] c1_elem = c_elem_from_word(c1_offset); wire [1:0] c1_part = c_part_from_word(c1_offset); wire [65:0] target_c1 = b1_c[c1_elem]; wire crc_read_en = (r_offset >= ADDR_B0_C && r_offset < (ADDR_B0_C + 12'h0C0)) || (r_offset >= ADDR_B1_C && r_offset < (ADDR_B1_C + 12'h0C0)); always @(posedge clk) begin if (!resetn) begin r_state <= R_IDLE; s_axi_arready <= 1'b1; s_axi_rvalid <= 1'b0; s_axi_rlast <= 1'b0; s_axi_rid <= 5'b0; end else begin case (r_state) R_IDLE: begin s_axi_arready <= 1'b1; if (s_axi_arvalid && s_axi_arready) begin s_axi_arready <= 1'b0; r_addr <= s_axi_araddr; r_len <= s_axi_arlen; r_burst <= s_axi_arburst; r_cnt <= 8'd0; s_axi_rvalid <= 1'b1; s_axi_rlast <= (s_axi_arlen == 8'd0); s_axi_rid <= s_axi_arid; r_state <= R_DATA; end end R_DATA: begin if (s_axi_rvalid && s_axi_rready) begin if (r_cnt == r_len) begin s_axi_rvalid <= 1'b0; s_axi_rlast <= 1'b0; s_axi_arready <= 1'b1; r_state <= R_IDLE; end else begin r_cnt <= r_cnt + 8'd1; s_axi_rlast <= (r_cnt + 8'd1 == r_len); if (r_burst == 2'b01) r_addr <= r_addr + 32'd4; end end end default: r_state <= R_IDLE; endcase end end wire [11:0] current_r_offset = (r_state == R_DATA) ? r_offset : s_axi_araddr[11:0]; always @(*) begin s_axi_rdata = 32'b0; if (r_state == R_DATA || (r_state == R_IDLE && s_axi_arvalid)) begin if (current_r_offset == ADDR_CTRL) s_axi_rdata = {30'b0, req_1, req_0}; else if (current_r_offset == ADDR_CRC) s_axi_rdata = crc_reg ^ 32'hFFFFFFFF; else if (current_r_offset == ADDR_STATUS) s_axi_rdata = {26'b0, crc_c66_busy, dma_done, dma_busy, error, done, busy}; else if (current_r_offset == ADDR_DMA_SRC_BASE) s_axi_rdata = dma_src_base; else if (current_r_offset == ADDR_DMA_DST_BASE) s_axi_rdata = dma_dst_base; else if (current_r_offset == ADDR_DMA_GROUPS) s_axi_rdata = dma_groups; else if (current_r_offset == ADDR_DMA_SRC_STRIDE) s_axi_rdata = dma_src_stride; else if (current_r_offset == ADDR_DMA_DST_STRIDE) s_axi_rdata = dma_dst_stride; else if (current_r_offset >= ADDR_B0_A && current_r_offset < ADDR_B0_B) s_axi_rdata = b0_a[(current_r_offset - ADDR_B0_A) >> 2]; else if (current_r_offset >= ADDR_B0_B && current_r_offset < ADDR_B0_C) s_axi_rdata = b0_b[(current_r_offset - ADDR_B0_B) >> 2]; else if (current_r_offset >= ADDR_B0_C && current_r_offset < (ADDR_B0_C + 12'h0C0)) begin if (c0_part == 2'd0) s_axi_rdata = target_c0[31:0]; else if (c0_part == 2'd1) s_axi_rdata = target_c0[63:32]; else s_axi_rdata = {30'b0, target_c0[65:64]}; end else if (current_r_offset >= ADDR_B1_A && current_r_offset < ADDR_B1_B) s_axi_rdata = b1_a[(current_r_offset - ADDR_B1_A) >> 2]; else if (current_r_offset >= ADDR_B1_B && current_r_offset < ADDR_B1_C) s_axi_rdata = b1_b[(current_r_offset - ADDR_B1_B) >> 2]; else if (current_r_offset >= ADDR_B1_C && current_r_offset < (ADDR_B1_C + 12'h0C0)) begin if (c1_part == 2'd0) s_axi_rdata = target_c1[31:0]; else if (c1_part == 2'd1) s_axi_rdata = target_c1[63:32]; else s_axi_rdata = {30'b0, target_c1[65:64]}; end end end // ========================================== // Compute FSM // ========================================== wire [63:0] mult_p [0:3]; reg [31:0] m_in_a [0:3]; reg [31:0] m_in_b [0:3]; genvar i; generate for (i = 0; i < 4; i = i + 1) begin : mult_inst pipelined_mult_32x32 u_mult ( .clk (clk), .rstn (resetn), .a (m_in_a[i]), .b (m_in_b[i]), .p (mult_p[i]) ); end endgenerate // Pipeline delay shift registers for accumulation (4 stages = 3 multiplier pipe + 1) reg [3:0] acc_idx_d1, acc_idx_d2, acc_idx_d3, acc_idx_d4; reg acc_bank_d1, acc_bank_d2, acc_bank_d3, acc_bank_d4; reg acc_valid_d1, acc_valid_d2, acc_valid_d3, acc_valid_d4; reg [4:0] pipe_cnt; wire [65:0] acc_result = ({2'b0, mult_p[0]} + {2'b0, mult_p[1]}) + ({2'b0, mult_p[2]} + {2'b0, mult_p[3]}); wire crc_calc_valid = dma_busy && acc_valid_d4; // DMA CRC issue spacing: compute FSM stalls via this counter to ensure // ≥3 cycles between consecutive acc_result captures, matching the 3-stage // CRC pipeline latency so crc_reg is committed before the next element. reg [1:0] dma_crc_issue_gap; wire dma_crc_issue_ready = !dma_busy || ((dma_crc_issue_gap == 2'd0) && !crc_c66_busy); // CRC register update: DMA calc result (pipelined, 3-cycle latency) // takes priority over AXI-stream (single-cycle). The stream path is // only permitted when the DMA pipeline is idle (!crc_c66_busy). always @(posedge clk) begin if (!resetn) begin crc_reg <= 32'hFFFFFFFF; crc_c66_s0_valid <= 1'b0; crc_c66_s0_crc <= 32'b0; crc_c66_s0_word1 <= 32'b0; crc_c66_s0_word2 <= 32'b0; crc_c66_s0_last <= 1'b0; crc_c66_s1_valid <= 1'b0; crc_c66_s1_crc <= 32'b0; crc_c66_s1_word2 <= 32'b0; crc_c66_s1_last <= 1'b0; end else if (crc_clear_pulse) begin crc_reg <= 32'hFFFFFFFF; crc_c66_s0_valid <= 1'b0; crc_c66_s0_crc <= 32'b0; crc_c66_s0_word1 <= 32'b0; crc_c66_s0_word2 <= 32'b0; crc_c66_s0_last <= 1'b0; crc_c66_s1_valid <= 1'b0; crc_c66_s1_crc <= 32'b0; crc_c66_s1_word2 <= 32'b0; crc_c66_s1_last <= 1'b0; end else begin // Stage 1 → crc_reg finalize (word2 CRC) if (crc_c66_s1_valid) crc_reg <= crc_c66_final_next; else if (crc_stream_valid && !crc_c66_busy) crc_reg <= crc_next; // Stage 0 → Stage 1 shift crc_c66_s1_valid <= crc_c66_s0_valid; if (crc_c66_s0_valid) begin crc_c66_s1_crc <= crc_c66_s1_next; crc_c66_s1_word2 <= crc_c66_s0_word2; crc_c66_s1_last <= crc_c66_s0_last; end else begin crc_c66_s1_last <= 1'b0; end // Capture → Stage 0 crc_c66_s0_valid <= crc_calc_valid; if (crc_calc_valid) begin crc_c66_s0_crc <= crc_c66_s0_next; crc_c66_s0_word1 <= acc_result[63:32]; crc_c66_s0_word2 <= {30'b0, acc_result[65:64]}; crc_c66_s0_last <= (acc_idx_d4 == 4'd15); end else begin crc_c66_s0_last <= 1'b0; end end end task start_calc_bank; input bank; begin cur_bank <= bank; busy <= 1'b1; state <= C_RUN; pipe_cnt <= 5'd1; dma_crc_issue_gap <= dma_busy ? 2'd2 : 2'd0; acc_idx_d1 <= 4'd0; acc_bank_d1 <= bank; acc_valid_d1 <= 1'b1; if (bank == 1'b0) begin m_in_a[0] <= b0_a[4'd0]; m_in_b[0] <= b0_b[4'd0]; m_in_a[1] <= b0_a[4'd1]; m_in_b[1] <= b0_b[4'd4]; m_in_a[2] <= b0_a[4'd2]; m_in_b[2] <= b0_b[4'd8]; m_in_a[3] <= b0_a[4'd3]; m_in_b[3] <= b0_b[4'd12]; end else begin m_in_a[0] <= b1_a[4'd0]; m_in_b[0] <= b1_b[4'd0]; m_in_a[1] <= b1_a[4'd1]; m_in_b[1] <= b1_b[4'd4]; m_in_a[2] <= b1_a[4'd2]; m_in_b[2] <= b1_b[4'd8]; m_in_a[3] <= b1_a[4'd3]; m_in_b[3] <= b1_b[4'd12]; end end endtask always @(posedge clk) begin if (!resetn) begin state <= C_IDLE; busy <= 1'b0; cur_bank <= 1'b0; calc_done_pulse <= 1'b0; pipe_cnt <= 5'd0; dma_crc_issue_gap <= 2'd0; acc_idx_d1 <= 4'd0; acc_idx_d2 <= 4'd0; acc_idx_d3 <= 4'd0; acc_idx_d4 <= 4'd0; acc_bank_d1 <= 1'b0; acc_bank_d2 <= 1'b0; acc_bank_d3 <= 1'b0; acc_bank_d4 <= 1'b0; acc_valid_d1 <= 1'b0; acc_valid_d2 <= 1'b0; acc_valid_d3 <= 1'b0; acc_valid_d4 <= 1'b0; end else begin calc_done_pulse <= 1'b0; if (dma_crc_issue_gap != 2'd0) dma_crc_issue_gap <= dma_crc_issue_gap - 2'd1; // Pipeline shift (4 cycles total) acc_idx_d4 <= acc_idx_d3; acc_idx_d3 <= acc_idx_d2; acc_idx_d2 <= acc_idx_d1; acc_idx_d1 <= 4'd0; acc_bank_d4 <= acc_bank_d3; acc_bank_d3 <= acc_bank_d2; acc_bank_d2 <= acc_bank_d1; acc_bank_d1 <= 1'b0; acc_valid_d4 <= acc_valid_d3; acc_valid_d3 <= acc_valid_d2; acc_valid_d2 <= acc_valid_d1; acc_valid_d1 <= 1'b0; case (state) C_IDLE: begin if (req_0) begin start_calc_bank(1'b0); end else if (req_1) begin start_calc_bank(1'b1); end else begin pipe_cnt <= 5'd0; busy <= 1'b0; end end C_RUN: begin // Issue 4 new operands per round. DMA mode inserts gap // so the 3-stage CRC pipeline can commit before the next // C-element capture. if (pipe_cnt < 5'd16) begin if (dma_crc_issue_ready) begin acc_idx_d1 <= pipe_cnt[3:0]; acc_bank_d1 <= cur_bank; acc_valid_d1 <= 1'b1; if (cur_bank == 1'b0) begin m_in_a[0] <= b0_a[{pipe_cnt[3:2], 2'd0}]; m_in_b[0] <= b0_b[{2'd0, pipe_cnt[1:0]}]; m_in_a[1] <= b0_a[{pipe_cnt[3:2], 2'd1}]; m_in_b[1] <= b0_b[{2'd1, pipe_cnt[1:0]}]; m_in_a[2] <= b0_a[{pipe_cnt[3:2], 2'd2}]; m_in_b[2] <= b0_b[{2'd2, pipe_cnt[1:0]}]; m_in_a[3] <= b0_a[{pipe_cnt[3:2], 2'd3}]; m_in_b[3] <= b0_b[{2'd3, pipe_cnt[1:0]}]; end else begin m_in_a[0] <= b1_a[{pipe_cnt[3:2], 2'd0}]; m_in_b[0] <= b1_b[{2'd0, pipe_cnt[1:0]}]; m_in_a[1] <= b1_a[{pipe_cnt[3:2], 2'd1}]; m_in_b[1] <= b1_b[{2'd1, pipe_cnt[1:0]}]; m_in_a[2] <= b1_a[{pipe_cnt[3:2], 2'd2}]; m_in_b[2] <= b1_b[{2'd2, pipe_cnt[1:0]}]; m_in_a[3] <= b1_a[{pipe_cnt[3:2], 2'd3}]; m_in_b[3] <= b1_b[{2'd3, pipe_cnt[1:0]}]; end if (dma_busy) dma_crc_issue_gap <= 2'd2; pipe_cnt <= pipe_cnt + 5'd1; end end // Accumulate results from 4 cycles ago. if (acc_valid_d4) begin if (acc_bank_d4 == 1'b0) b0_c[acc_idx_d4] <= acc_result; else b1_c[acc_idx_d4] <= acc_result; end if (acc_valid_d4 && acc_idx_d4 == 4'd15) begin calc_done_pulse <= 1'b1; end if (pipe_cnt >= 5'd16) begin if (dma_busy) begin if (acc_valid_d4 && acc_idx_d4 == 4'd15) begin if (cur_bank == 1'b0 && req_1) start_calc_bank(1'b1); else if (cur_bank == 1'b1 && req_0) start_calc_bank(1'b0); else begin busy <= 1'b0; state <= C_IDLE; end end else begin pipe_cnt <= pipe_cnt + 5'd1; end end else if (cur_bank == 1'b0 && req_1) begin start_calc_bank(1'b1); end else if (cur_bank == 1'b1 && req_0) begin start_calc_bank(1'b0); end else if (acc_valid_d4 && acc_idx_d4 == 4'd15) begin busy <= 1'b0; state <= C_IDLE; end else begin pipe_cnt <= pipe_cnt + 5'd1; end end end endcase end end // ========================================== // Autonomous AXI master loader with inline result CRC. // ========================================== always @(posedge clk) begin if (!resetn) begin dma_busy <= 1'b0; dma_done <= 1'b0; load_group <= 32'd0; store_group <= 32'd0; dma_next_rd_addr <= 32'd0; dma_next_wr_addr <= 32'd0; completed_groups <= 32'd0; bank_loaded <= 2'b00; bank_store_pending <= 2'b00; bank_storing <= 2'b00; bank_compute_pending <= 2'b00; dma_compute_bank <= 1'b0; dma_load_bank <= 1'b0; dma_store_bank <= 1'b0; dma_rd_state <= DMA_RD_IDLE; dma_rd_count <= 5'd0; dma_rd_bank <= 1'b0; dma_wr_state <= DMA_WR_IDLE; dma_wr_count <= 6'd0; m_axi_arid <= 4'd0; m_axi_araddr <= 32'd0; m_axi_arlen <= 8'd31; m_axi_arsize <= 3'd2; m_axi_arburst <= 2'b01; m_axi_arlock <= 1'b0; m_axi_arcache <= 4'b0; m_axi_arprot <= 3'b0; m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b0; m_axi_awid <= 4'd0; m_axi_awaddr <= 32'd0; m_axi_awlen <= 8'd47; m_axi_awsize <= 3'd2; m_axi_awburst <= 2'b01; m_axi_awlock <= 1'b0; m_axi_awcache <= 4'b0; m_axi_awprot <= 3'b0; m_axi_awvalid <= 1'b0; m_axi_wdata <= 32'd0; m_axi_wstrb <= 4'hF; m_axi_wlast <= 1'b0; m_axi_wvalid <= 1'b0; m_axi_bready <= 1'b0; dma_req0_pulse <= 1'b0; dma_req1_pulse <= 1'b0; end else begin dma_req0_pulse <= 1'b0; dma_req1_pulse <= 1'b0; if (dma_done_clear_pulse) dma_done <= 1'b0; if (dma_start_pulse && !dma_busy && dma_groups != 32'd0) begin dma_busy <= 1'b1; dma_done <= 1'b0; load_group <= 32'd0; store_group <= 32'd0; dma_next_rd_addr <= dma_src_base; dma_next_wr_addr <= dma_dst_base; completed_groups <= 32'd0; bank_loaded <= 2'b00; bank_store_pending <= 2'b00; bank_storing <= 2'b00; bank_compute_pending <= 2'b00; dma_compute_bank <= 1'b0; dma_load_bank <= 1'b0; dma_store_bank <= 1'b0; dma_rd_state <= DMA_RD_IDLE; dma_wr_state <= DMA_WR_IDLE; end if (dma_busy) begin if (state == C_RUN) bank_compute_pending[cur_bank] <= 1'b0; if (bank_loaded[0] && !req_0 && !bank_compute_pending[0] && !(state == C_RUN && cur_bank == 1'b0)) begin dma_req0_pulse <= 1'b1; bank_loaded[0] <= 1'b0; bank_compute_pending[0] <= 1'b1; dma_compute_bank <= 1'b0; end else if (bank_loaded[1] && !req_1 && !bank_compute_pending[1] && !(state == C_RUN && cur_bank == 1'b1)) begin dma_req1_pulse <= 1'b1; bank_loaded[1] <= 1'b0; bank_compute_pending[1] <= 1'b1; dma_compute_bank <= 1'b1; end end if (calc_done_pulse && dma_busy) begin store_group <= store_group + 32'd1; completed_groups <= completed_groups + 32'd1; if ((completed_groups + 32'd1) >= dma_groups) begin dma_busy <= 1'b0; dma_done <= 1'b1; end end case (dma_rd_state) DMA_RD_IDLE: begin m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b0; if (dma_busy && load_group < dma_groups && !bank_loaded[dma_load_bank] && !bank_compute_pending[dma_load_bank] && !((dma_load_bank == 1'b0) ? req_0 : req_1) && !(state == C_RUN && cur_bank == dma_load_bank)) begin dma_rd_bank <= dma_load_bank; dma_rd_count <= 5'd0; m_axi_araddr <= dma_next_rd_addr; m_axi_arlen <= 8'd31; m_axi_arsize <= 3'd2; m_axi_arburst <= 2'b01; m_axi_arvalid <= 1'b1; dma_rd_state <= DMA_RD_ADDR; end end DMA_RD_ADDR: begin if (m_axi_arvalid && m_axi_arready) begin m_axi_arvalid <= 1'b0; m_axi_rready <= 1'b1; dma_rd_state <= DMA_RD_DATA; end end DMA_RD_DATA: begin if (m_axi_rvalid && m_axi_rready) begin if (m_axi_rlast) begin m_axi_rready <= 1'b0; bank_loaded[dma_rd_bank] <= 1'b1; load_group <= load_group + 32'd1; dma_next_rd_addr <= dma_next_rd_addr + dma_src_stride; dma_load_bank <= ~dma_load_bank; dma_rd_state <= DMA_RD_IDLE; end else begin dma_rd_count <= dma_rd_count + 5'd1; end end end default: dma_rd_state <= DMA_RD_IDLE; endcase case (dma_wr_state) DMA_WR_IDLE: begin m_axi_awvalid <= 1'b0; m_axi_wvalid <= 1'b0; m_axi_bready <= 1'b0; end DMA_WR_ADDR: begin m_axi_awvalid <= 1'b0; dma_wr_state <= DMA_WR_DATA; end DMA_WR_DATA: begin m_axi_wvalid <= 1'b0; m_axi_wlast <= 1'b0; if (dma_wr_count == 6'd47) begin bank_storing[dma_store_bank] <= 1'b0; dma_store_bank <= ~dma_store_bank; store_group <= store_group + 32'd1; completed_groups <= completed_groups + 32'd1; if ((completed_groups + 32'd1) >= dma_groups) begin dma_busy <= 1'b0; dma_done <= 1'b1; end dma_wr_state <= DMA_WR_IDLE; end else begin dma_wr_count <= dma_wr_count + 6'd1; end end DMA_WR_RESP: begin m_axi_bready <= 1'b0; dma_wr_state <= DMA_WR_IDLE; end default: dma_wr_state <= DMA_WR_IDLE; endcase end end assign intr = done; endmodule