`timescale 1ns / 1ps module accumulator #( parameter DATA_WIDTH = 12, parameter ACCUM_WIDTH = 32, parameter N_MAX = 4096, parameter PACKET_SIZE = 8, parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) ) ( input clk_in, input rst, input [DATA_WIDTH-1:0] s_axis_tdata, input s_axis_tvalid, input start, input [31:0] smp_num, input [15:0] seq_num, input [31:0] window_size, output [ACCUM_WIDTH-1:0] out_data, output out_valid, output readout_begin, input batch_req, input finish, output logic accum_done ); logic [31:0] smp_num_reg, cnt_smp_num; logic [31:0] window_size_reg; logic [15:0] seq_num_reg, cnt_seq_num; logic [15:0] cnt_addr, addra, addrb; logic [ACCUM_WIDTH-1:0] data; logic valid_data; logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; logic wea, enb; logic readout_begin_reg; logic [ACCUM_WIDTH-1:0] out_data_reg; logic out_valid_reg; logic finish_reg, finish_buf; typedef enum logic [3:0] { IDLE, INIT_MEM, BEGIN_SEQ, REQ_WORD_B, ACCUM, READOUT_START, READOUT_AWAIT, READOUT_DELAY, READOUT_PUT, READOUT_LAST, FINISH } wr_state_t; (* MARK_DEBUG="true" *) wr_state_t wr_state; // One word per clock accumulation pipeline // On every sum_valid in ACCUM we launch a BRAM read for cnt_addr // On the next clock the saved sum_data is added to doutb and written back logic accum_pipe_valid; logic [15:0] accum_pipe_addr; logic [ACCUM_WIDTH-1:0] accum_pipe_data; // case smp_num // window_size == 1 // Then the next sequence can read the same address it is written logic accum_pipe_bypass_valid; logic [ACCUM_WIDTH-1:0] accum_pipe_bypass_data; logic accum_accept_last; logic accum_accept_last_all; logic [ACCUM_WIDTH-1:0] accum_write_base; logic [ACCUM_WIDTH-1:0] accum_write_value; wire [31:0] window_size_safe = (window_size == 32'd0) ? 32'd1 : window_size; wire start_accept = start && (wr_state == IDLE); assign accum_accept_last = (cnt_smp_num + window_size_reg >= smp_num_reg); assign accum_accept_last_all = accum_accept_last && (cnt_seq_num == seq_num_reg - 1); assign accum_write_base = accum_pipe_bypass_valid ? accum_pipe_bypass_data : data_bram_out; assign accum_write_value = accum_pipe_data + accum_write_base; // Memory controls to XPM // In accumulation/init states they are driven directly from the current // state and pipeline registers. That avoids an extra register stage logic mem_wea; logic mem_enb; logic [15:0] mem_addra; logic [15:0] mem_addrb; logic [ACCUM_WIDTH-1:0] mem_dina; assign mem_wea = (wr_state == INIT_MEM) ? valid_data : (wr_state == ACCUM) ? accum_pipe_valid : 1'b0; assign mem_addra = (wr_state == INIT_MEM) ? cnt_addr : (wr_state == ACCUM) ? accum_pipe_addr : addra; assign mem_dina = (wr_state == INIT_MEM) ? data : (wr_state == ACCUM) ? accum_write_value : data_bram_in; assign mem_enb = (wr_state == ACCUM) ? valid_data : enb; assign mem_addrb = (wr_state == ACCUM) ? cnt_addr : addrb; // registers for port b data request reg req_data_b; reg [15:0] req_addr_b; always @(posedge clk_in) begin if (rst) begin smp_num_reg <= '0; cnt_smp_num <= '0; window_size_reg <= 32'd1; seq_num_reg <= '0; cnt_seq_num <= '0; cnt_addr <= '0; addra <= '0; addrb <= '0; data_bram_in <= '0; wea <= 0; enb <= 0; wr_state <= IDLE; finish_reg <= 0; finish_buf <= 0; readout_begin_reg <= 0; out_data_reg <= '0; out_valid_reg <= 0; accum_pipe_valid <= 0; accum_pipe_addr <= '0; accum_pipe_data <= '0; accum_pipe_bypass_valid <= 0; accum_pipe_bypass_data <= '0; accum_done <= 0; end else begin finish_buf <= finish; // FSM case(wr_state) IDLE: begin // wait for start signal wea <= 0; enb <= 0; readout_begin_reg <= 0; finish_reg <= 0; out_valid_reg <= 0; accum_pipe_valid <= 0; accum_pipe_bypass_valid <= 0; accum_done <= 0; cnt_smp_num <= '0; cnt_seq_num <= '0; cnt_addr <= '0; addrb <= '0; if (start) begin smp_num_reg <= smp_num; seq_num_reg <= seq_num; window_size_reg <= window_size_safe; wr_state <= INIT_MEM; end end INIT_MEM: begin // First sequence wea <= 0; enb <= 0; out_valid_reg <= 0; accum_pipe_valid <= 0; accum_pipe_bypass_valid <= 0; accum_done <= 0; if (valid_data) begin // mem_wea/mem_addra/mem_dina do the actual write in this clock data_bram_in <= data; addra <= cnt_addr; wea <= 1; if (cnt_smp_num + window_size_reg >= smp_num_reg) begin cnt_smp_num <= '0; cnt_addr <= '0; if (seq_num_reg <= 16'd1) begin cnt_seq_num <= '0; addrb <= '0; accum_done <= 1; wr_state <= READOUT_START; end else begin // start further accumulation cnt_seq_num <= 16'd1; wr_state <= ACCUM; end end else begin cnt_smp_num <= cnt_smp_num + window_size_reg; cnt_addr <= cnt_addr + 1; end end end BEGIN_SEQ: begin // FIXME: unused wea <= 0; enb <= 0; wr_state <= ACCUM; end REQ_WORD_B: begin // FIXME: depr wea <= 0; enb <= 0; wr_state <= ACCUM; end ACCUM: begin // accum pipeline wea <= 0; enb <= 0; out_valid_reg <= 0; if (accum_pipe_valid) begin // mem_wea/mem_addra/mem_dina do the actual write this clock addra <= accum_pipe_addr; data_bram_in <= accum_write_value; wea <= 1; end if (accum_done) begin // Last input word was accepted on the previous clk accum_pipe_valid <= 0; accum_pipe_bypass_valid <= 0; cnt_smp_num <= '0; cnt_seq_num <= '0; cnt_addr <= '0; addrb <= '0; enb <= 0; wr_state <= READOUT_START; end else if (valid_data) begin // mem_enb/mem_addrb launch the actual read this clock enb <= 1; addrb <= cnt_addr; accum_pipe_valid <= 1; accum_pipe_addr <= cnt_addr; accum_pipe_data <= data; // case window_size=1 && smp_num is small accum_pipe_bypass_valid <= accum_pipe_valid && (accum_pipe_addr == cnt_addr); accum_pipe_bypass_data <= accum_write_value; if (accum_accept_last) begin cnt_smp_num <= '0; cnt_addr <= '0; if (cnt_seq_num == seq_num_reg - 1) begin accum_done <= 1; end else begin cnt_seq_num <= cnt_seq_num + 1; end end else begin cnt_smp_num <= cnt_smp_num + window_size_reg; cnt_addr <= cnt_addr + 1; end end else begin accum_pipe_valid <= 0; accum_pipe_bypass_valid <= 0; end end READOUT_START: begin readout_begin_reg <= 1'b1; wr_state <= READOUT_AWAIT; enb <= 0; wea <= 0; end READOUT_AWAIT: begin // req await + delay for every-clock readout wea <= 0; if (batch_req) begin enb <= 1; wr_state <= READOUT_DELAY; end else if (finish_buf) begin wr_state <= FINISH; end else begin enb <= 0; out_valid_reg <= 0; end end READOUT_DELAY: begin // wait for mem latency wea <= 0; addrb <= addrb + 1; wr_state <= READOUT_PUT; end READOUT_PUT: begin // main data output wea <= 0; if ((addrb % READ_BATCH_SIZE) == 0) begin wr_state <= READOUT_LAST; enb <= 0; end else addrb <= addrb + 1; out_valid_reg <= 1; out_data_reg <= data_bram_out; end READOUT_LAST: begin // last word of packet wea <= 0; out_valid_reg <= 0; out_data_reg <= data_bram_out; wr_state <= READOUT_START; end FINISH: begin out_valid_reg <= 0; enb <= 0; wea <= 0; wr_state <= IDLE; end default: wr_state <= IDLE; endcase end end adder #( .DATA_WIDTH(DATA_WIDTH), .ACCUM_WIDTH(ACCUM_WIDTH) ) adder_dut ( .clk_in(clk_in), .rst(rst), .start(start_accept), .window_size(window_size), .s_axis_tdata(s_axis_tdata), .s_axis_tvalid(s_axis_tvalid), .sum_data(data), .sum_valid(valid_data) ); xpm_memory_sdpram #( .ADDR_WIDTH_A(16), // DECIMAL .ADDR_WIDTH_B(16), // DECIMAL .AUTO_SLEEP_TIME(0), // DECIMAL .BYTE_WRITE_WIDTH_A(ACCUM_WIDTH), // DECIMAL .CASCADE_HEIGHT(0), // DECIMAL .CLOCKING_MODE("common_clock"), // String .ECC_MODE("no_ecc"), // String .MEMORY_INIT_FILE("none"), // String .MEMORY_INIT_PARAM("0"), // String .MEMORY_OPTIMIZATION("true"), // String .MEMORY_PRIMITIVE("auto"), // String .MEMORY_SIZE(N_MAX*ACCUM_WIDTH), // DECIMAL .MESSAGE_CONTROL(0), // DECIMAL .READ_DATA_WIDTH_B(ACCUM_WIDTH), // DECIMAL .READ_LATENCY_B(1), // DECIMAL .READ_RESET_VALUE_B("0"), // String .RST_MODE_A("SYNC"), // String .RST_MODE_B("SYNC"), // String .SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages .USE_EMBEDDED_CONSTRAINT(0), // DECIMAL .USE_MEM_INIT(1), // DECIMAL .USE_MEM_INIT_MMI(0), // DECIMAL .WAKEUP_TIME("disable_sleep"), // String .WRITE_DATA_WIDTH_A(ACCUM_WIDTH), // DECIMAL .WRITE_MODE_B("no_change"), // String .WRITE_PROTECT(1) // DECIMAL ) xpm_memory_sdpram_inst ( .doutb(data_bram_out), .addra(mem_addra), .addrb(mem_addrb), .clka(clk_in), .clkb(clk_in), .dina(mem_dina), .ena(1'b1), .enb(mem_enb), .wea(mem_wea) ); assign readout_begin = readout_begin_reg; assign out_data = out_data_reg; assign out_valid = out_valid_reg; endmodule