Compare commits
12 Commits
master
...
dev/accum_
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f99021168 | |||
| 9c70029f3e | |||
| 177c7d0bc2 | |||
| 8e5b3929ac | |||
| df5469d538 | |||
| 77526c44c2 | |||
| 5010c42ae3 | |||
| f0542845b0 | |||
| 46333c3601 | |||
| 2a3796a60f | |||
| 425c922690 | |||
| 02416a9621 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,6 +16,7 @@
|
||||
.Xil
|
||||
xvlog.pb
|
||||
*vivado_pid*
|
||||
**/work/*
|
||||
|
||||
# some generated files (they annoy me)
|
||||
update_config.tcl
|
||||
|
||||
@ -5,7 +5,6 @@ module accumulator
|
||||
parameter DATA_WIDTH = 12,
|
||||
parameter ACCUM_WIDTH = 32,
|
||||
parameter N_MAX = 4096,
|
||||
parameter WINDOW_SIZE = 4,
|
||||
parameter PACKET_SIZE = 8,
|
||||
parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH)
|
||||
)
|
||||
@ -17,6 +16,7 @@ module accumulator
|
||||
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,
|
||||
@ -26,6 +26,7 @@ module accumulator
|
||||
);
|
||||
|
||||
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;
|
||||
|
||||
@ -39,10 +40,6 @@ module accumulator
|
||||
logic out_valid_reg;
|
||||
logic finish_reg, finish_buf;
|
||||
|
||||
// registers for port b data request
|
||||
reg req_data_b;
|
||||
reg [15:0] req_addr_b;
|
||||
|
||||
typedef enum logic [3:0] {
|
||||
IDLE,
|
||||
INIT_MEM,
|
||||
@ -58,18 +55,85 @@ module accumulator
|
||||
} 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_done;
|
||||
|
||||
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;
|
||||
|
||||
@ -83,83 +147,137 @@ module accumulator
|
||||
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 run to initialize memory with first batch of values
|
||||
// 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;
|
||||
cnt_addr <= cnt_addr + 1;
|
||||
cnt_smp_num <= cnt_smp_num + WINDOW_SIZE;
|
||||
|
||||
end
|
||||
if (cnt_smp_num >= smp_num_reg) begin
|
||||
wr_state <= BEGIN_SEQ;
|
||||
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;
|
||||
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
|
||||
// start new acc seq
|
||||
// FIXME: unused
|
||||
wea <= 0;
|
||||
enb <= 0;
|
||||
if (cnt_seq_num == seq_num_reg - 1) begin
|
||||
cnt_seq_num <= '0;
|
||||
cnt_smp_num <= '0;
|
||||
cnt_addr <= '0;
|
||||
wr_state <= READOUT_START;
|
||||
addrb <= '0;
|
||||
enb <= 0;
|
||||
end else begin
|
||||
// beginning of new data sequence
|
||||
cnt_seq_num <= cnt_seq_num + 1;
|
||||
cnt_smp_num <= '0;
|
||||
cnt_addr <= '0;
|
||||
wea <= 0;
|
||||
addrb <= 0;
|
||||
wr_state <= REQ_WORD_B;
|
||||
end
|
||||
wr_state <= ACCUM;
|
||||
end
|
||||
|
||||
REQ_WORD_B: begin
|
||||
// pre-request data for port b
|
||||
// FIXME: depr
|
||||
wea <= 0;
|
||||
enb <= 1;
|
||||
addrb <= cnt_addr;
|
||||
enb <= 0;
|
||||
wr_state <= ACCUM;
|
||||
end
|
||||
|
||||
ACCUM: begin
|
||||
// sum mem+input
|
||||
// accum pipeline
|
||||
wea <= 0;
|
||||
enb <= 0;
|
||||
if (valid_data) begin
|
||||
addra <= cnt_addr;
|
||||
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;
|
||||
data_bram_in <= data + data_bram_out;
|
||||
cnt_smp_num <= cnt_smp_num + WINDOW_SIZE;
|
||||
if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin
|
||||
wr_state <= BEGIN_SEQ;
|
||||
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;
|
||||
wr_state <= REQ_WORD_B;
|
||||
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.
|
||||
// req await + delay for every-clock readout
|
||||
wea <= 0;
|
||||
if (batch_req) begin
|
||||
enb <= 1;
|
||||
wr_state <= READOUT_DELAY;
|
||||
@ -173,12 +291,14 @@ module accumulator
|
||||
|
||||
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;
|
||||
@ -190,6 +310,7 @@ module accumulator
|
||||
|
||||
READOUT_LAST: begin
|
||||
// last word of packet
|
||||
wea <= 0;
|
||||
out_valid_reg <= 0;
|
||||
out_data_reg <= data_bram_out;
|
||||
wr_state <= READOUT_START;
|
||||
@ -198,6 +319,7 @@ module accumulator
|
||||
FINISH: begin
|
||||
out_valid_reg <= 0;
|
||||
enb <= 0;
|
||||
wea <= 0;
|
||||
wr_state <= IDLE;
|
||||
end
|
||||
|
||||
@ -210,12 +332,13 @@ module accumulator
|
||||
adder
|
||||
#(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.WINDOW_SIZE(WINDOW_SIZE),
|
||||
.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),
|
||||
@ -254,14 +377,14 @@ module accumulator
|
||||
|
||||
.doutb(data_bram_out),
|
||||
|
||||
.addra(addra),
|
||||
.addrb(addrb),
|
||||
.addra(mem_addra),
|
||||
.addrb(mem_addrb),
|
||||
.clka(clk_in),
|
||||
.clkb(clk_in),
|
||||
.dina(data_bram_in),
|
||||
.dina(mem_dina),
|
||||
.ena(1'b1),
|
||||
.enb(enb),
|
||||
.wea(wea)
|
||||
.enb(mem_enb),
|
||||
.wea(mem_wea)
|
||||
);
|
||||
|
||||
assign readout_begin = readout_begin_reg;
|
||||
|
||||
@ -5,7 +5,6 @@ module accumulator_top
|
||||
parameter DATA_WIDTH = 12,
|
||||
parameter ACCUM_WIDTH = 32,
|
||||
parameter N_MAX = 4096,
|
||||
parameter WINDOW_SIZE = 65,
|
||||
parameter PACKET_SIZE = 1024,
|
||||
parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH)
|
||||
)
|
||||
@ -22,6 +21,7 @@ module accumulator_top
|
||||
input start,
|
||||
input [31:0] smp_num,
|
||||
input [15:0] seq_num,
|
||||
input [31:0] window_size,
|
||||
|
||||
// eth signals
|
||||
input eth_clk_in,
|
||||
@ -42,36 +42,67 @@ module accumulator_top
|
||||
wire readout_begin;
|
||||
wire batch_req;
|
||||
|
||||
logic finish_int;
|
||||
logic finish_int_d;
|
||||
logic finish_pulse;
|
||||
logic calc_active;
|
||||
logic start_accept;
|
||||
logic [31:0] window_size_reg;
|
||||
|
||||
wire [31:0] window_size_safe = (window_size == 32'd0) ? 32'd1 : window_size;
|
||||
|
||||
assign finish_pulse = finish_int && !finish_int_d;
|
||||
assign start_accept = start && !calc_active;
|
||||
assign finish = finish_int;
|
||||
|
||||
// Keep the top copy stable for blocks that start later than the accum itself
|
||||
always_ff @(posedge clk_in) begin
|
||||
if (rst) begin
|
||||
calc_active <= 1'b0;
|
||||
finish_int_d <= 1'b0;
|
||||
window_size_reg <= 32'd1;
|
||||
end else begin
|
||||
finish_int_d <= finish_int;
|
||||
|
||||
if (start_accept) begin
|
||||
calc_active <= 1'b1;
|
||||
window_size_reg <= window_size_safe;
|
||||
end else if (finish_pulse) begin
|
||||
calc_active <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
accumulator #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.N_MAX(N_MAX),
|
||||
.WINDOW_SIZE(WINDOW_SIZE),
|
||||
.PACKET_SIZE(PACKET_SIZE)
|
||||
) accum_main (
|
||||
.clk_in(clk_in),
|
||||
.rst(rst),
|
||||
.s_axis_tdata(s_axis_tdata),
|
||||
.s_axis_tvalid(s_axis_tvalid),
|
||||
.start(start),
|
||||
.start(start_accept),
|
||||
.smp_num(smp_num),
|
||||
.seq_num(seq_num),
|
||||
.window_size(window_size),
|
||||
.out_data(out_data),
|
||||
.out_valid(out_valid),
|
||||
.readout_begin(readout_begin),
|
||||
.batch_req(batch_req),
|
||||
.finish(finish)
|
||||
.finish(finish_int)
|
||||
);
|
||||
|
||||
out_axis_fifo #(
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.WINDOW_SIZE(WINDOW_SIZE),
|
||||
.PACKET_SIZE(PACKET_SIZE)
|
||||
) output_async_fifo (
|
||||
.eth_clk_in (eth_clk_in),
|
||||
.acc_clk_in (clk_in),
|
||||
.rst (rst),
|
||||
.smp_num (smp_num),
|
||||
.window_size (window_size_reg),
|
||||
|
||||
.m_axis_tdata (m_axis_tdata),
|
||||
.m_axis_tvalid (m_axis_tvalid),
|
||||
@ -87,6 +118,6 @@ module accumulator_top
|
||||
.send_req (send_req),
|
||||
|
||||
.batch_req (batch_req),
|
||||
.finish (finish)
|
||||
.finish (finish_int)
|
||||
);
|
||||
endmodule
|
||||
@ -4,12 +4,13 @@
|
||||
module adder
|
||||
#(
|
||||
parameter DATA_WIDTH = 12,
|
||||
parameter WINDOW_SIZE = 4,
|
||||
parameter ACCUM_WIDTH = 32
|
||||
)
|
||||
(
|
||||
input clk_in,
|
||||
input rst,
|
||||
input start,
|
||||
input [31:0] window_size,
|
||||
input [DATA_WIDTH-1:0] s_axis_tdata,
|
||||
input s_axis_tvalid,
|
||||
|
||||
@ -20,7 +21,10 @@ module adder
|
||||
logic [ACCUM_WIDTH-1:0] accum, res;
|
||||
logic [DATA_WIDTH-1:0] axis_data;
|
||||
logic res_valid, axis_valid;
|
||||
(* MARK_DEBUG = "TRUE" *) logic [15:0] cnt;
|
||||
(* MARK_DEBUG = "TRUE" *) logic [31:0] cnt;
|
||||
logic [31:0] window_size_reg;
|
||||
|
||||
wire [31:0] window_size_safe = (window_size == 32'd0) ? 32'd1 : window_size;
|
||||
|
||||
always @(posedge clk_in) begin
|
||||
if (rst) begin
|
||||
@ -28,19 +32,32 @@ module adder
|
||||
cnt <= '0;
|
||||
res <= '0;
|
||||
res_valid <= 0;
|
||||
axis_data <= '0;
|
||||
axis_valid <= 0;
|
||||
window_size_reg <= 32'd1;
|
||||
end else begin
|
||||
res_valid <= 0;
|
||||
axis_data <= s_axis_tdata;
|
||||
axis_valid <= s_axis_tvalid;
|
||||
if ( axis_valid) begin
|
||||
if (cnt == WINDOW_SIZE-1) begin
|
||||
res <= accum + axis_data;
|
||||
res_valid <= 1;
|
||||
accum <= '0;
|
||||
cnt <= '0;
|
||||
end else begin
|
||||
accum <= accum + axis_data;
|
||||
cnt <= cnt + 1;
|
||||
|
||||
if (start) begin
|
||||
accum <= '0;
|
||||
cnt <= '0;
|
||||
res <= '0;
|
||||
axis_data <= '0;
|
||||
axis_valid <= 0;
|
||||
window_size_reg <= window_size_safe;
|
||||
end else begin
|
||||
axis_data <= s_axis_tdata;
|
||||
axis_valid <= s_axis_tvalid;
|
||||
if (axis_valid) begin
|
||||
if (cnt == window_size_reg - 1) begin
|
||||
res <= accum + axis_data;
|
||||
res_valid <= 1;
|
||||
accum <= '0;
|
||||
cnt <= '0;
|
||||
end else begin
|
||||
accum <= accum + axis_data;
|
||||
cnt <= cnt + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
module out_axis_fifo #(
|
||||
parameter ACCUM_WIDTH = 32,
|
||||
parameter WINDOW_SIZE = 65,
|
||||
parameter PACKET_SIZE = 1024
|
||||
) (
|
||||
input logic eth_clk_in,
|
||||
input logic acc_clk_in,
|
||||
input logic rst,
|
||||
input logic [31:0] smp_num,
|
||||
input logic [31:0] window_size,
|
||||
|
||||
// AXI stream master for output, eth_clk_in domain
|
||||
output logic [7:0] m_axis_tdata,
|
||||
@ -85,6 +85,8 @@ module out_axis_fifo #(
|
||||
reg [31:0] wr_cnt; // current BIT mem ptr
|
||||
reg [31:0] wr_batch_tgt; // next 'target' that should be written from batch
|
||||
reg [31:0] wr_total; // total BITS to be sent!
|
||||
logic [31:0] window_size_reg;
|
||||
wire [31:0] window_size_safe = (window_size == 32'd0) ? 32'd1 : window_size;
|
||||
|
||||
wire empty;
|
||||
|
||||
@ -92,7 +94,7 @@ module out_axis_fifo #(
|
||||
|
||||
// NOTE:
|
||||
// each written "acc_din" ACCUM_WIDTH word
|
||||
// is counted as WINDOWS_SIZE samples actually
|
||||
// is counted as window_size samples actually
|
||||
// because hw division for counters is painful
|
||||
// so we just increased the counter sizes
|
||||
|
||||
@ -102,6 +104,7 @@ module out_axis_fifo #(
|
||||
wr_cnt <= 32'b0;
|
||||
wr_batch_tgt <= 32'b0;
|
||||
wr_total <= 32'b0;
|
||||
window_size_reg <= 32'd1;
|
||||
batch_req <= 0;
|
||||
finish <= 0;
|
||||
|
||||
@ -115,6 +118,7 @@ module out_axis_fifo #(
|
||||
wr_state <= WR_CHECK;
|
||||
wr_total <= smp_num * ACCUM_WIDTH;
|
||||
wr_batch_tgt <= 32'b0;
|
||||
window_size_reg <= window_size_safe;
|
||||
batch_req <= 0;
|
||||
finish <= 0;
|
||||
end
|
||||
@ -126,9 +130,9 @@ module out_axis_fifo #(
|
||||
if ((wr_data_count < (FIFO_WDEPTH - (PACKET_SIZE / (ACCUM_WIDTH / 8)))) && ~wr_rst_busy) begin
|
||||
batch_req <= 1;
|
||||
// should give us exactly PACKET_SIZE * 8 bits
|
||||
// multiplied by WINDOW_SIZE, because we count
|
||||
// each given ACCUM_WIDTH word as WINDOWS_SIZE samples !!!
|
||||
wr_batch_tgt <= wr_batch_tgt + (8 * WINDOW_SIZE * PACKET_SIZE);
|
||||
// multiplied by window_size, because we count
|
||||
// each given ACCUM_WIDTH word as window_size samples !!!
|
||||
wr_batch_tgt <= wr_batch_tgt + (8 * window_size_reg * PACKET_SIZE);
|
||||
wr_state <= WR_RUN;
|
||||
end else begin
|
||||
batch_req <= 0;
|
||||
@ -150,8 +154,8 @@ module out_axis_fifo #(
|
||||
|
||||
if (din_valid) begin
|
||||
// data supplied
|
||||
// count as we got WINDOW_SIZE samples
|
||||
wr_cnt <= wr_cnt + ACCUM_WIDTH * WINDOW_SIZE;
|
||||
// count as we got window_size samples
|
||||
wr_cnt <= wr_cnt + ACCUM_WIDTH * window_size_reg;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -13,20 +13,24 @@ FPGA_ARCH = artix7
|
||||
|
||||
RTL_DIR = ../src
|
||||
|
||||
# Simulation settings
|
||||
SIM_TOP = tb_accumulator_top
|
||||
SIM_RUNTIME ?= 10000 us
|
||||
|
||||
include ../../../scripts/vivado.mk
|
||||
# Design sources only
|
||||
SYN_FILES += $(filter-out %_tb.sv,$(sort $(shell find $(RTL_DIR) -type f -name '*.v' -o -type f -name '*.sv')))
|
||||
|
||||
SYN_FILES += $(sort $(shell find ../src -type f \( -name '*.v' -o -name '*.sv' \)))
|
||||
# Testbench sources. Vivado puts these into "sim_1", Questa compiles them into "work".
|
||||
TB_FILES += out_axis_fifo_tb.sv
|
||||
TB_FILES += accum_full_tb.sv
|
||||
|
||||
XCI_FILES = $(sort $(shell find ../src -type f -name '*.xci'))
|
||||
XCI_FILES = $(sort $(shell find $(RTL_DIR) -type f -name '*.xci'))
|
||||
|
||||
XDC_FILES += ../../../constraints/ax7a035b.xdc
|
||||
XDC_FILES += test_timing.xdc
|
||||
|
||||
SYN_FILES += out_axis_fifo_tb.sv
|
||||
SYN_FILES += accum_full_tb.sv
|
||||
SIM_TOP = tb_accumulator_top
|
||||
|
||||
include ../../../scripts/vivado.mk
|
||||
include ../../../scripts/questa.mk
|
||||
|
||||
program: $(PROJECT).bit
|
||||
echo "open_hw_manager" > program.tcl
|
||||
|
||||
@ -5,7 +5,7 @@ module tb_accumulator_top;
|
||||
localparam DATA_WIDTH = 12;
|
||||
localparam ACCUM_WIDTH = 32;
|
||||
localparam N_MAX = 4096;
|
||||
localparam WINDOW_SIZE = 65;
|
||||
localparam MAX_WINDOW_SIZE = 65;
|
||||
localparam PACKET_SIZE = 1024;
|
||||
localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH;
|
||||
localparam MAX_WORDS = N_MAX;
|
||||
@ -20,6 +20,7 @@ module tb_accumulator_top;
|
||||
logic start;
|
||||
logic [31:0] smp_num;
|
||||
logic [15:0] seq_num;
|
||||
logic [31:0] window_size;
|
||||
|
||||
logic req_ready;
|
||||
wire send_req;
|
||||
@ -50,7 +51,6 @@ module tb_accumulator_top;
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||
.N_MAX(N_MAX),
|
||||
.WINDOW_SIZE(WINDOW_SIZE),
|
||||
.PACKET_SIZE(PACKET_SIZE)
|
||||
) dut (
|
||||
.clk_in(clk_in),
|
||||
@ -60,6 +60,7 @@ module tb_accumulator_top;
|
||||
.start(start),
|
||||
.smp_num(smp_num),
|
||||
.seq_num(seq_num),
|
||||
.window_size(window_size),
|
||||
.eth_clk_in(eth_clk_in),
|
||||
.req_ready(req_ready),
|
||||
.send_req(send_req),
|
||||
@ -104,6 +105,7 @@ module tb_accumulator_top;
|
||||
s_axis_tvalid = 1'b0;
|
||||
smp_num = '0;
|
||||
seq_num = '0;
|
||||
window_size = 32'd1;
|
||||
req_ready = 1'b0;
|
||||
m_axis_tready = 1'b1;
|
||||
clear_scoreboard();
|
||||
@ -141,13 +143,14 @@ module tb_accumulator_top;
|
||||
|
||||
task automatic run_test(
|
||||
input integer test_id,
|
||||
input integer window_size_i,
|
||||
input integer seq_num_i,
|
||||
input integer smp_num_i,
|
||||
input bit randomize_data,
|
||||
input integer base_value,
|
||||
input string test_name
|
||||
);
|
||||
logic [DATA_WIDTH-1:0] sample_mem [0:MAX_SEQ_NUM-1][0:(N_MAX*WINDOW_SIZE)-1];
|
||||
logic [DATA_WIDTH-1:0] sample_mem [0:MAX_SEQ_NUM-1][0:(N_MAX*MAX_WINDOW_SIZE)-1];
|
||||
integer seq_idx;
|
||||
integer sample_idx;
|
||||
integer word_idx;
|
||||
@ -165,22 +168,25 @@ module tb_accumulator_top;
|
||||
tests_total = tests_total + 1;
|
||||
errors_before = total_errors;
|
||||
|
||||
if (smp_num_i <= 0 || smp_num_i > N_MAX * WINDOW_SIZE || (smp_num_i % WINDOW_SIZE) != 0)
|
||||
$fatal(1, "[%0s] invalid smp_num=%0d", test_name, smp_num_i);
|
||||
if (window_size_i <= 0 || window_size_i > MAX_WINDOW_SIZE)
|
||||
$fatal(1, "[%0s] invalid window_size=%0d", test_name, window_size_i);
|
||||
if (smp_num_i <= 0 || smp_num_i > N_MAX * window_size_i || (smp_num_i % window_size_i) != 0)
|
||||
$fatal(1, "[%0s] invalid smp_num=%0d for window_size=%0d", test_name, smp_num_i, window_size_i);
|
||||
if (seq_num_i <= 0 || seq_num_i > MAX_SEQ_NUM)
|
||||
$fatal(1, "[%0s] invalid seq_num=%0d", test_name, seq_num_i);
|
||||
|
||||
$display("\n========================================");
|
||||
$display("TEST %0d: %0s", test_id, test_name);
|
||||
$display("seq_num=%0d smp_num=%0d randomize=%0d", seq_num_i, smp_num_i, randomize_data);
|
||||
$display("window_size=%0d seq_num=%0d smp_num=%0d randomize=%0d", window_size_i, seq_num_i, smp_num_i, randomize_data);
|
||||
$display("========================================");
|
||||
|
||||
reset_dut();
|
||||
smp_num = smp_num_i;
|
||||
seq_num = seq_num_i;
|
||||
req_ready = 1'b1; // приемник готов заранее
|
||||
smp_num = smp_num_i;
|
||||
seq_num = seq_num_i;
|
||||
window_size = window_size_i;
|
||||
req_ready = 1'b1; // приемник готов заранее
|
||||
|
||||
exp_word_count = smp_num_i / WINDOW_SIZE;
|
||||
exp_word_count = smp_num_i / window_size_i;
|
||||
exp_packet_count = (exp_word_count + READ_BATCH_SIZE - 1) / READ_BATCH_SIZE;
|
||||
|
||||
for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin
|
||||
@ -196,8 +202,8 @@ module tb_accumulator_top;
|
||||
for (word_idx = 0; word_idx < exp_word_count; word_idx = word_idx + 1) begin
|
||||
local_sum = 0;
|
||||
for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin
|
||||
for (k = 0; k < WINDOW_SIZE; k = k + 1)
|
||||
local_sum = local_sum + sample_mem[seq_idx][word_idx * WINDOW_SIZE + k];
|
||||
for (k = 0; k < window_size_i; k = k + 1)
|
||||
local_sum = local_sum + sample_mem[seq_idx][word_idx * window_size_i + k];
|
||||
end
|
||||
expected_words[word_idx] = local_sum[ACCUM_WIDTH-1:0];
|
||||
$display(" expected[%0d] = %0d (0x%08x)", word_idx, expected_words[word_idx], expected_words[word_idx]);
|
||||
@ -318,16 +324,17 @@ module tb_accumulator_top;
|
||||
|
||||
reset_dut();
|
||||
|
||||
run_test(1, 1, 1 * WINDOW_SIZE, 1'b0, 1, "deterministic_small");
|
||||
// $finish;
|
||||
run_test(2, 2, 1 * WINDOW_SIZE, 1'b1, 0, "random_seq3_smp8");
|
||||
run_test(3, 1, 16 * WINDOW_SIZE, 1'b1, 0, "random_seq5_smp16_multi_packet");
|
||||
run_test(4, 2, 12 * WINDOW_SIZE, 1'b1, 0, "random_seq7_smp12");
|
||||
run_test(5, 4, 256 * WINDOW_SIZE, 1'b1, 0, "random_max_smpnum");
|
||||
run_test(6, 2, 1500 * WINDOW_SIZE, 1'b1, 0, "random_max_smpnum2");
|
||||
run_test(7, 20, 1 * WINDOW_SIZE, 1'b1, 0, "random_20seq");
|
||||
run_test(8, 20, 3 * WINDOW_SIZE, 1'b1, 0, "random_20seqx3");
|
||||
run_test(9, 200, 1 * WINDOW_SIZE, 1'b1, 0, "random_200seq");
|
||||
run_test(1, 1, 1, 1 * 1, 1'b0, 1, "w1_deterministic_small");
|
||||
run_test(2, 1, 2, 16 * 1, 1'b1, 0, "w1_random_seq2_smp16");
|
||||
run_test(3, 2, 2, 16 * 2, 1'b1, 0, "w2_random_seq2_smp32");
|
||||
run_test(4, 3, 1, 16 * 3, 1'b1, 0, "w3_random_seq1_smp48");
|
||||
run_test(5, 4, 2, 12 * 4, 1'b1, 0, "w4_random_seq2_smp48");
|
||||
run_test(6, 65, 4, 256 * 65, 1'b1, 0, "w65_random_seq4_smp16640");
|
||||
run_test(7, 2, 20, 3 * 2, 1'b1, 0, "w2_random_20seqx3");
|
||||
run_test(8, 65, 20, 3 * 65, 1'b1, 0, "w65_random_20seqx3");
|
||||
run_test(9, 1, 200, 1 * 1, 1'b1, 0, "w1_random_200seq");
|
||||
run_test(10, 2, 200, 2 * 1, 1'b1, 0, "w2_random_200seq");
|
||||
run_test(11, 65, 200, 65 * 1, 1'b1, 0, "w1_random_200seq");
|
||||
|
||||
$display("\n========================================");
|
||||
$display("ALL TESTS COMPLETED");
|
||||
|
||||
96
scripts/questa.mk
Normal file
96
scripts/questa.mk
Normal file
@ -0,0 +1,96 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
###################################################################
|
||||
# Questa/ModelSim simulation helper
|
||||
#
|
||||
# Expected variables from the project Makefile:
|
||||
# SYN_FILES - RTL sources
|
||||
# TB_FILES - testbench sources
|
||||
# INC_FILES - include files, optional
|
||||
# SIM_TOP - simulation top module
|
||||
# SIM_DEFS - defines, optional
|
||||
# SIM_RUNTIME - run time, for example "10000 us" or "-all"
|
||||
#
|
||||
# Useful overrides:
|
||||
# make sim-questa XILINX_VIVADO=/opt/Xilinx/Vivado/2024.2
|
||||
# make sim-questa-gui
|
||||
###################################################################
|
||||
|
||||
.PHONY: sim-questa sim-questa-gui questa-clean questa-xpm-clean
|
||||
|
||||
VLIB ?= vlib
|
||||
VMAP ?= vmap
|
||||
VLOG ?= vlog
|
||||
VSIM ?= vsim
|
||||
|
||||
QUESTA_WORK_LIB ?= work
|
||||
QUESTA_XPM_LIB ?= xpm
|
||||
QUESTA_DIR ?= questa_build
|
||||
QUESTA_TRANSCRIPT ?= transcript
|
||||
XILINX_VIVADO=/tools/Xilinx/2025.1/Vivado
|
||||
SIM_RUNTIME ?= 10000 us
|
||||
QUESTA_RUN ?= run $(SIM_RUNTIME)
|
||||
|
||||
# XPM is needed by this design because accum.sv uses xpm_memory_sdpram and
|
||||
# out_axis_fifo.sv uses xpm_fifo_async. Point XILINX_VIVADO to your Vivado
|
||||
# install if it is not already exported by settings64.sh.
|
||||
QUESTA_USE_XPM ?= 1
|
||||
|
||||
QUESTA_XPM_SRC = \
|
||||
$(XILINX_VIVADO)/data/ip/xpm/xpm_cdc/hdl/xpm_cdc.sv \
|
||||
$(XILINX_VIVADO)/data/ip/xpm/xpm_memory/hdl/xpm_memory.sv \
|
||||
$(XILINX_VIVADO)/data/ip/xpm/xpm_fifo/hdl/xpm_fifo.sv
|
||||
|
||||
QUESTA_GLBL_SRC = $(XILINX_VIVADO)/data/verilog/src/glbl.v
|
||||
|
||||
QUESTA_DEFS = $(foreach d,$(SIM_DEFS),+define+$(d))
|
||||
QUESTA_INC_DIRS = $(sort $(dir $(SYN_FILES) $(TB_FILES) $(INC_FILES)))
|
||||
QUESTA_INCS = $(foreach d,$(QUESTA_INC_DIRS),+incdir+$(d))
|
||||
QUESTA_LIBS = $(if $(filter 1,$(QUESTA_USE_XPM)),-L $(QUESTA_XPM_LIB),)
|
||||
QUESTA_SOURCES = $(SYN_FILES) $(TB_FILES) $(if $(wildcard $(QUESTA_GLBL_SRC)),$(QUESTA_GLBL_SRC),)
|
||||
|
||||
QUESTA_GLBL_TOP = $(if $(wildcard $(QUESTA_GLBL_SRC)),$(QUESTA_WORK_LIB).glbl,)
|
||||
|
||||
|
||||
$(QUESTA_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(QUESTA_DIR)/sources.f: Makefile | $(QUESTA_DIR)
|
||||
@rm -f $@
|
||||
@for inc in $(QUESTA_INCS); do echo $$inc >> $@; done
|
||||
@for src in $(QUESTA_SOURCES); do echo $$src >> $@; done
|
||||
|
||||
$(QUESTA_DIR)/xpm.stamp: | $(QUESTA_DIR)
|
||||
@if [ "$(QUESTA_USE_XPM)" = "1" ]; then \
|
||||
if [ -z "$(XILINX_VIVADO)" ]; then \
|
||||
echo "ERROR: XILINX_VIVADO is not set. Source Vivado settings64.sh or pass XILINX_VIVADO=/path/to/Vivado/<version>."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
for src in $(QUESTA_XPM_SRC); do \
|
||||
if [ ! -f $$src ]; then \
|
||||
echo "ERROR: XPM source not found: $$src"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
done; \
|
||||
$(VLIB) $(QUESTA_XPM_LIB); \
|
||||
$(VMAP) $(QUESTA_XPM_LIB) $(QUESTA_XPM_LIB); \
|
||||
$(VLOG) -sv -work $(QUESTA_XPM_LIB) $(QUESTA_XPM_SRC); \
|
||||
fi
|
||||
@touch $@
|
||||
|
||||
$(QUESTA_DIR)/compile.stamp: $(QUESTA_DIR)/sources.f $(SYN_FILES) $(TB_FILES) $(INC_FILES) $(QUESTA_DIR)/xpm.stamp
|
||||
$(VLIB) $(QUESTA_WORK_LIB)
|
||||
$(VMAP) $(QUESTA_WORK_LIB) $(QUESTA_WORK_LIB)
|
||||
$(VLOG) -sv -work $(QUESTA_WORK_LIB) $(QUESTA_DEFS) -timescale 1ns/1ps $(QUESTA_INCS) -f $(QUESTA_DIR)/sources.f
|
||||
@touch $@
|
||||
|
||||
sim-questa: $(QUESTA_DIR)/compile.stamp
|
||||
$(VSIM) -c $(QUESTA_LIBS) $(QUESTA_WORK_LIB).$(SIM_TOP) $(QUESTA_GLBL_TOP) -do "$(QUESTA_RUN); quit -f"
|
||||
|
||||
sim-questa-gui: $(QUESTA_DIR)/compile.stamp
|
||||
$(VSIM) $(QUESTA_LIBS) -voptargs="+acc" $(QUESTA_WORK_LIB).$(SIM_TOP) $(QUESTA_GLBL_TOP)
|
||||
|
||||
questa-clean:
|
||||
-rm -rf $(QUESTA_DIR) $(QUESTA_WORK_LIB) $(QUESTA_TRANSCRIPT) vsim.wlf *.wlf
|
||||
|
||||
questa-xpm-clean: questa-clean
|
||||
-rm -rf $(QUESTA_XPM_LIB)
|
||||
Reference in New Issue
Block a user