396 lines
12 KiB
Systemverilog
396 lines
12 KiB
Systemverilog
`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
|