Merge pull request 'dev/accum' (#7) from dev/accum into master
Reviewed-on: #7
This commit is contained in:
39
rtl/accum/README.md
Normal file
39
rtl/accum/README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Аккумулятор
|
||||||
|
Модуль аккумуляции данных для последующего усреднения. Принимает данные с входного потока АХI-Stream фиксированной ширины (задается параметрически), суммируя их сначала по окнам, а затем со значениями из предыдущей последовательности.
|
||||||
|
|
||||||
|
## Список парамтеров:
|
||||||
|
- DATA_WIDTH - ширина входных данных, получаемых с АЦП
|
||||||
|
- ACCUM_WIDTH - размер данных для аккумуляции, должен быть степенью числа 2. По умолчанию - 32
|
||||||
|
- N_MAX - максимальное число окон в последовательности. Должно быть степенью числа 2. Влияет на размер используемой памяти.
|
||||||
|
- WINDOW_SIZE - размер окна усреднения
|
||||||
|
- PACKET_SIZE - размер выходного пакета
|
||||||
|
|
||||||
|
## Иерархия:
|
||||||
|
```
|
||||||
|
├── accum_top - полная сборка аккумулятора
|
||||||
|
│ ├── accum - основная логика аккумуляции по окнам и последовательностям
|
||||||
|
│ │ ├── adder - модуль сложения по окнам
|
||||||
|
│ ├── out_axis_fifo - модуль для выдачи данных наружу в другом частотном домене
|
||||||
|
```
|
||||||
|
## Список входных портов:
|
||||||
|
- clk_in - частота входных данных
|
||||||
|
- rst - сброс всего
|
||||||
|
- [DATA_WIDTH-1:0] s_axis_tdata - входные данные
|
||||||
|
- s_axis_tvalid - валидность входных данных
|
||||||
|
- start - начало аккумуляции
|
||||||
|
- [31:0] smp_num - число сэмплов (должно быть кратно WINDOW_SIZE)
|
||||||
|
- [15:0] seq_num - число последовательностей аккумуляции
|
||||||
|
- eth_clk_in - частота для выходных данных на ethernet
|
||||||
|
- req_ready - готовность отправителя начать принимать данные
|
||||||
|
- m_axis_tready - готовность выходного axis
|
||||||
|
|
||||||
|
|
||||||
|
## Список выходных портов:
|
||||||
|
- send_req - сигнал начала отправки данных
|
||||||
|
- [7:0] m_axis_tdata - данные выходного axis
|
||||||
|
- m_axis_tvalid - валидность выходного axis
|
||||||
|
- m_axis_tlast - последний пакет в axis
|
||||||
|
- finish - конец отправки всех данных, полный цикл работы завершен
|
||||||
|
|
||||||
|
## Логика работы:
|
||||||
|
Модуль начинает работу при получении сигнала start. Сразу после начала работы можно подавать данные на входной axis, они будут суммироваться по WINDOW_SIZE штук и отправляться на хранение. Так будет сделано для последовательности длиной smp_num чисел, затем начинается новая последовательность - всего таких будет seq_num штук. Каждая последующая последовательность также суммируется по окнам, а затем полученные значения прибавляются к тем же значениям предыдущей последовательности. Таким образом, выполняется суммирование по двум осям, и из исходных данных seq_num по smp_num чисел остается вектор длиной 1 x (smp_num / WINDOW_SIZE). После накопления всех данных начинается выдача. Выдача осуществляется на выходной AXI stream, работающий в домене eth_clk, и имеющий ширину 8 бит - предполагается, что выдача пойдет на ethernet-udp. Когда поднят сигнал req_ready, модуль будет отправлять send_req (запрос отправки пакета), и по готовности m_axis_tready начнет выдавать пакет размер PACKET_SIZE байт. Если данные нельзя ровно разложить по пакетам, то в последнем пакете могут быть отправлены рандомные данные из памяти. После окончания отправки всех пакетов будет поднят сигнал finish.
|
||||||
271
rtl/accum/src/accum.sv
Normal file
271
rtl/accum/src/accum.sv
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
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,
|
||||||
|
|
||||||
|
output [ACCUM_WIDTH-1:0] out_data,
|
||||||
|
output out_valid,
|
||||||
|
output readout_begin,
|
||||||
|
input batch_req,
|
||||||
|
input finish
|
||||||
|
);
|
||||||
|
|
||||||
|
logic [31:0] smp_num_reg, cnt_smp_num;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// registers for port b data request
|
||||||
|
reg req_data_b;
|
||||||
|
reg [15:0] req_addr_b;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
always @(posedge clk_in) begin
|
||||||
|
if (rst) begin
|
||||||
|
smp_num_reg <= '0;
|
||||||
|
cnt_smp_num <= '0;
|
||||||
|
seq_num_reg <= '0;
|
||||||
|
cnt_seq_num <= '0;
|
||||||
|
cnt_addr <= '0;
|
||||||
|
wea <= 0;
|
||||||
|
enb <= 0;
|
||||||
|
wr_state <= IDLE;
|
||||||
|
finish_reg <= 0;
|
||||||
|
out_valid_reg <= 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;
|
||||||
|
if (start) begin
|
||||||
|
smp_num_reg <= smp_num;
|
||||||
|
seq_num_reg <= seq_num;
|
||||||
|
wr_state <= INIT_MEM;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
INIT_MEM: begin
|
||||||
|
// first run to initialize memory with first batch of values
|
||||||
|
wea <= 0;
|
||||||
|
if (valid_data) begin
|
||||||
|
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;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
BEGIN_SEQ: begin
|
||||||
|
// start new acc seq
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
REQ_WORD_B: begin
|
||||||
|
// pre-request data for port b
|
||||||
|
wea <= 0;
|
||||||
|
enb <= 1;
|
||||||
|
addrb <= cnt_addr;
|
||||||
|
wr_state <= ACCUM;
|
||||||
|
end
|
||||||
|
|
||||||
|
ACCUM: begin
|
||||||
|
// sum mem+input
|
||||||
|
enb <= 0;
|
||||||
|
if (valid_data) begin
|
||||||
|
addra <= cnt_addr;
|
||||||
|
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 else begin
|
||||||
|
cnt_addr <= cnt_addr + 1;
|
||||||
|
wr_state <= REQ_WORD_B;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
READOUT_START: begin
|
||||||
|
readout_begin_reg <= 1'b1;
|
||||||
|
wr_state <= READOUT_AWAIT;
|
||||||
|
enb <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
READOUT_AWAIT: begin
|
||||||
|
// req await + delay for every-clock readout.
|
||||||
|
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
|
||||||
|
addrb <= addrb + 1;
|
||||||
|
wr_state <= READOUT_PUT;
|
||||||
|
end
|
||||||
|
|
||||||
|
READOUT_PUT: begin
|
||||||
|
// main data output
|
||||||
|
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
|
||||||
|
out_valid_reg <= 0;
|
||||||
|
out_data_reg <= data_bram_out;
|
||||||
|
wr_state <= READOUT_START;
|
||||||
|
end
|
||||||
|
|
||||||
|
FINISH: begin
|
||||||
|
out_valid_reg <= 0;
|
||||||
|
enb <= 0;
|
||||||
|
wr_state <= IDLE;
|
||||||
|
end
|
||||||
|
|
||||||
|
default: wr_state <= IDLE;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
adder
|
||||||
|
#(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.WINDOW_SIZE(WINDOW_SIZE),
|
||||||
|
.ACCUM_WIDTH(ACCUM_WIDTH)
|
||||||
|
) adder_dut
|
||||||
|
(
|
||||||
|
.clk_in(clk_in),
|
||||||
|
.rst(rst),
|
||||||
|
.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(addra),
|
||||||
|
.addrb(addrb),
|
||||||
|
.clka(clk_in),
|
||||||
|
.clkb(clk_in),
|
||||||
|
.dina(data_bram_in),
|
||||||
|
.ena(1'b1),
|
||||||
|
.enb(enb),
|
||||||
|
.wea(wea)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign readout_begin = readout_begin_reg;
|
||||||
|
assign out_data = out_data_reg;
|
||||||
|
assign out_valid = out_valid_reg;
|
||||||
|
|
||||||
|
endmodule
|
||||||
92
rtl/accum/src/accum_top.sv
Normal file
92
rtl/accum/src/accum_top.sv
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
// main clk
|
||||||
|
input clk_in,
|
||||||
|
input rst,
|
||||||
|
|
||||||
|
// input data
|
||||||
|
input [DATA_WIDTH-1:0] s_axis_tdata,
|
||||||
|
input s_axis_tvalid,
|
||||||
|
|
||||||
|
// parameters
|
||||||
|
input start,
|
||||||
|
input [31:0] smp_num,
|
||||||
|
input [15:0] seq_num,
|
||||||
|
|
||||||
|
// eth signals
|
||||||
|
input eth_clk_in,
|
||||||
|
input req_ready,
|
||||||
|
output send_req,
|
||||||
|
|
||||||
|
// output axis
|
||||||
|
output logic [7:0] m_axis_tdata,
|
||||||
|
output logic m_axis_tvalid,
|
||||||
|
input logic m_axis_tready,
|
||||||
|
output logic m_axis_tlast,
|
||||||
|
|
||||||
|
output logic finish
|
||||||
|
);
|
||||||
|
|
||||||
|
wire [ACCUM_WIDTH-1:0] out_data;
|
||||||
|
wire out_valid;
|
||||||
|
wire readout_begin;
|
||||||
|
wire batch_req;
|
||||||
|
|
||||||
|
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),
|
||||||
|
.smp_num(smp_num),
|
||||||
|
.seq_num(seq_num),
|
||||||
|
.out_data(out_data),
|
||||||
|
.out_valid(out_valid),
|
||||||
|
.readout_begin(readout_begin),
|
||||||
|
.batch_req(batch_req),
|
||||||
|
.finish(finish)
|
||||||
|
);
|
||||||
|
|
||||||
|
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),
|
||||||
|
|
||||||
|
.m_axis_tdata (m_axis_tdata),
|
||||||
|
.m_axis_tvalid (m_axis_tvalid),
|
||||||
|
.m_axis_tready (m_axis_tready),
|
||||||
|
.m_axis_tlast (m_axis_tlast),
|
||||||
|
|
||||||
|
.acc_din (out_data),
|
||||||
|
.din_valid (out_valid),
|
||||||
|
|
||||||
|
.readout_begin (readout_begin),
|
||||||
|
|
||||||
|
.req_ready (req_ready),
|
||||||
|
.send_req (send_req),
|
||||||
|
|
||||||
|
.batch_req (batch_req),
|
||||||
|
.finish (finish)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
52
rtl/accum/src/adder.sv
Normal file
52
rtl/accum/src/adder.sv
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
|
||||||
|
module adder
|
||||||
|
#(
|
||||||
|
parameter DATA_WIDTH = 12,
|
||||||
|
parameter WINDOW_SIZE = 4,
|
||||||
|
parameter ACCUM_WIDTH = 32
|
||||||
|
)
|
||||||
|
(
|
||||||
|
input clk_in,
|
||||||
|
input rst,
|
||||||
|
input [DATA_WIDTH-1:0] s_axis_tdata,
|
||||||
|
input s_axis_tvalid,
|
||||||
|
|
||||||
|
output [ACCUM_WIDTH-1:0] sum_data,
|
||||||
|
output sum_valid
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
always @(posedge clk_in) begin
|
||||||
|
if (rst) begin
|
||||||
|
accum <= '0;
|
||||||
|
cnt <= '0;
|
||||||
|
res <= '0;
|
||||||
|
res_valid <= 0;
|
||||||
|
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;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign sum_valid = res_valid;
|
||||||
|
assign sum_data = res;
|
||||||
|
|
||||||
|
endmodule
|
||||||
324
rtl/accum/src/out_axis_fifo.sv
Normal file
324
rtl/accum/src/out_axis_fifo.sv
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
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,
|
||||||
|
|
||||||
|
// AXI stream master for output, eth_clk_in domain
|
||||||
|
output logic [7:0] m_axis_tdata,
|
||||||
|
output logic m_axis_tvalid,
|
||||||
|
input logic m_axis_tready,
|
||||||
|
output logic m_axis_tlast,
|
||||||
|
// eth handshake
|
||||||
|
input logic req_ready,
|
||||||
|
output logic send_req,
|
||||||
|
output logic [15:0] udp_data_length,
|
||||||
|
|
||||||
|
// data from acc
|
||||||
|
input logic [ACCUM_WIDTH-1:0] acc_din,
|
||||||
|
input logic din_valid,
|
||||||
|
|
||||||
|
// input pulse
|
||||||
|
input logic readout_begin,
|
||||||
|
|
||||||
|
// output pulses
|
||||||
|
output logic batch_req,
|
||||||
|
output logic finish
|
||||||
|
);
|
||||||
|
// sync reset
|
||||||
|
reg [1:0] rst_sync_ff;
|
||||||
|
reg rst_eth;
|
||||||
|
|
||||||
|
always @(posedge acc_clk_in or posedge rst) begin
|
||||||
|
if (rst) begin
|
||||||
|
rst_sync_ff <= 2'b11;
|
||||||
|
end else begin
|
||||||
|
rst_sync_ff <= {rst_sync_ff[0], 1'b0};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign rst_eth = rst_sync_ff[1];
|
||||||
|
|
||||||
|
logic [1:0] rst_acc_ff;
|
||||||
|
logic rst_acc;
|
||||||
|
|
||||||
|
always_ff @(posedge acc_clk_in or posedge rst) begin
|
||||||
|
if (rst)
|
||||||
|
rst_acc_ff <= 2'b11;
|
||||||
|
else
|
||||||
|
rst_acc_ff <= {rst_acc_ff[0], 1'b0};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign rst_acc = rst_acc_ff[1];
|
||||||
|
|
||||||
|
|
||||||
|
// fifo params calc
|
||||||
|
// round up to be enough for 2xPACKET_SIZE storage
|
||||||
|
localparam int MIN_BYTES = 2 * PACKET_SIZE;
|
||||||
|
localparam int MIN_BITS = MIN_BYTES * 8;
|
||||||
|
localparam int MIN_WR_WORDS = (MIN_BITS + ACCUM_WIDTH - 1) / ACCUM_WIDTH; // ceil div
|
||||||
|
localparam int WDEPTH_BITS = $clog2(MIN_WR_WORDS);
|
||||||
|
localparam int FIFO_WDEPTH = 1 << WDEPTH_BITS;
|
||||||
|
|
||||||
|
localparam int FIFO_RDEPTH = FIFO_WDEPTH * ACCUM_WIDTH / 8;
|
||||||
|
localparam int RDEPTH_BITS = $clog2(FIFO_RDEPTH) + 1;
|
||||||
|
|
||||||
|
wire wr_unavail;
|
||||||
|
wire wr_rst_busy;
|
||||||
|
reg rd_en;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum logic [2:0] {
|
||||||
|
WR_IDLE = 3'd0,
|
||||||
|
WR_CHECK = 3'd1,
|
||||||
|
WR_RUN = 3'd2,
|
||||||
|
WR_END = 3'd3
|
||||||
|
} wr_state_t;
|
||||||
|
|
||||||
|
(* MARK_DEBUG="true" *) wr_state_t wr_state;
|
||||||
|
|
||||||
|
// Write FSM
|
||||||
|
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!
|
||||||
|
|
||||||
|
wire empty;
|
||||||
|
|
||||||
|
wire [WDEPTH_BITS:0] wr_data_count;
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// each written "acc_din" ACCUM_WIDTH word
|
||||||
|
// is counted as WINDOWS_SIZE samples actually
|
||||||
|
// because hw division for counters is painful
|
||||||
|
// so we just increased the counter sizes
|
||||||
|
|
||||||
|
always_ff @(posedge acc_clk_in) begin
|
||||||
|
if (rst_acc) begin
|
||||||
|
wr_state <= WR_IDLE;
|
||||||
|
wr_cnt <= 32'b0;
|
||||||
|
wr_batch_tgt <= 32'b0;
|
||||||
|
wr_total <= 32'b0;
|
||||||
|
batch_req <= 0;
|
||||||
|
finish <= 0;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
case (wr_state)
|
||||||
|
// wait until readout is requested
|
||||||
|
WR_IDLE: begin
|
||||||
|
if (readout_begin) begin
|
||||||
|
wr_cnt <= 32'b0;
|
||||||
|
wr_state <= WR_CHECK;
|
||||||
|
wr_total <= smp_num * ACCUM_WIDTH;
|
||||||
|
wr_batch_tgt <= 32'b0;
|
||||||
|
batch_req <= 0;
|
||||||
|
finish <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// wait until we can request a word
|
||||||
|
// depends on prog_full signal
|
||||||
|
WR_CHECK: begin
|
||||||
|
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);
|
||||||
|
wr_state <= WR_RUN;
|
||||||
|
end else begin
|
||||||
|
batch_req <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// wait until all requested packet is written
|
||||||
|
WR_RUN: begin
|
||||||
|
batch_req <= 0;
|
||||||
|
if (wr_cnt == wr_batch_tgt) begin
|
||||||
|
// got enough words
|
||||||
|
wr_state <= WR_END;
|
||||||
|
end else if (wr_cnt > wr_batch_tgt) begin
|
||||||
|
// weird case when accum gave us too much words
|
||||||
|
// block resets
|
||||||
|
wr_cnt <= 32'hffffffff; // sort of signal for sim/ila
|
||||||
|
wr_state <= WR_END;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (din_valid) begin
|
||||||
|
// data supplied
|
||||||
|
// count as we got WINDOW_SIZE samples
|
||||||
|
wr_cnt <= wr_cnt + ACCUM_WIDTH * WINDOW_SIZE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// check if this was last data batch
|
||||||
|
WR_END: begin
|
||||||
|
// here we check that we sent enough data
|
||||||
|
// wr_cnt should be by design PACKET_SIZE-aligned
|
||||||
|
if (wr_cnt >= wr_total) begin
|
||||||
|
// wait until all data is sent
|
||||||
|
if (empty) begin
|
||||||
|
finish <= 1;
|
||||||
|
wr_state <= WR_IDLE;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
// next word
|
||||||
|
wr_state <= WR_CHECK;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Readout FSM with ethernet request
|
||||||
|
|
||||||
|
assign udp_data_length = PACKET_SIZE; // fixed packet size
|
||||||
|
reg [15:0] sent_cnt;
|
||||||
|
|
||||||
|
typedef enum logic [2:0] {
|
||||||
|
RD_IDLE = 3'd0,
|
||||||
|
RD_CHECK = 3'd1,
|
||||||
|
RD_SEND = 3'd2
|
||||||
|
} rd_state_t;
|
||||||
|
|
||||||
|
(* MARK_DEBUG="true" *) rd_state_t rd_state;
|
||||||
|
|
||||||
|
wire rd_valid;
|
||||||
|
wire [RDEPTH_BITS-1:0] rd_data_count;
|
||||||
|
|
||||||
|
always_ff @(posedge eth_clk_in) begin
|
||||||
|
if (rst_eth) begin
|
||||||
|
rd_state <= RD_IDLE;
|
||||||
|
send_req <= 1'b0;
|
||||||
|
sent_cnt <= 16'd0;
|
||||||
|
m_axis_tlast <= 1'b0;
|
||||||
|
m_axis_tvalid <= 1'b0;
|
||||||
|
rd_en <= 1'b0;
|
||||||
|
|
||||||
|
end else begin
|
||||||
|
|
||||||
|
case (rd_state)
|
||||||
|
// wait until fifo has enough data to send
|
||||||
|
RD_IDLE: begin
|
||||||
|
if (rd_data_count == PACKET_SIZE) begin
|
||||||
|
// enough data to send packet, begin
|
||||||
|
rd_state <= RD_CHECK;
|
||||||
|
end
|
||||||
|
send_req <= 1'b0;
|
||||||
|
sent_cnt <= 16'd0;
|
||||||
|
rd_en <= 1'b0;
|
||||||
|
m_axis_tlast <= 1'b0;
|
||||||
|
m_axis_tvalid <= 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
// await udp ready
|
||||||
|
RD_CHECK: begin
|
||||||
|
if (req_ready) begin
|
||||||
|
send_req <= 1'b1;
|
||||||
|
rd_state <= RD_SEND;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// send data
|
||||||
|
RD_SEND: begin
|
||||||
|
// udp is ready and fifo is ready = sent
|
||||||
|
send_req <= 1'b0;
|
||||||
|
if (m_axis_tready && rd_valid) begin
|
||||||
|
rd_en <= 1'b1;
|
||||||
|
m_axis_tvalid <= 1'b1;
|
||||||
|
sent_cnt <= sent_cnt + 1;
|
||||||
|
// final packet of the batch
|
||||||
|
if (sent_cnt == PACKET_SIZE - 1) begin
|
||||||
|
rd_state <= RD_IDLE;
|
||||||
|
m_axis_tlast <= 1'b1;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
rd_en <= 1'b0;
|
||||||
|
m_axis_tvalid <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
logic [ACCUM_WIDTH-1:0] fifo_din_r, acc_din_reg, din_valid_reg;
|
||||||
|
logic fifo_wr_en_r;
|
||||||
|
|
||||||
|
always_ff @(posedge acc_clk_in) begin
|
||||||
|
if (rst_acc) begin
|
||||||
|
fifo_din_r <= '0;
|
||||||
|
fifo_wr_en_r <= 1'b0;
|
||||||
|
|
||||||
|
din_valid_reg <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
fifo_wr_en_r <= 1'b0;
|
||||||
|
acc_din_reg <= acc_din;
|
||||||
|
|
||||||
|
if (!wr_rst_busy && din_valid_reg) begin
|
||||||
|
fifo_din_r <= acc_din_reg;
|
||||||
|
fifo_wr_en_r <= 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
din_valid_reg <= din_valid;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// xpm_fifo_async: Asynchronous FIFO
|
||||||
|
// Xilinx Parameterized Macro, version 2025.1
|
||||||
|
|
||||||
|
xpm_fifo_async #(
|
||||||
|
.DOUT_RESET_VALUE("0"), // String
|
||||||
|
.FIFO_READ_LATENCY(1), // DECIMAL
|
||||||
|
.FIFO_WRITE_DEPTH(FIFO_WDEPTH),
|
||||||
|
.FULL_RESET_VALUE(0),
|
||||||
|
.PROG_EMPTY_THRESH(PACKET_SIZE),
|
||||||
|
.PROG_FULL_THRESH(PACKET_SIZE / (ACCUM_WIDTH / 8)),
|
||||||
|
.RD_DATA_COUNT_WIDTH(RDEPTH_BITS),
|
||||||
|
.READ_DATA_WIDTH(8), // always 8 bit for eth
|
||||||
|
.READ_MODE("fwft"),
|
||||||
|
.SIM_ASSERT_CHK(1), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
|
||||||
|
.USE_ADV_FEATURES("1616"), // String
|
||||||
|
.WRITE_DATA_WIDTH(ACCUM_WIDTH),
|
||||||
|
.WR_DATA_COUNT_WIDTH(WDEPTH_BITS+1)
|
||||||
|
)
|
||||||
|
xpm_fifo_async_inst (
|
||||||
|
|
||||||
|
.data_valid(rd_valid), // 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the
|
||||||
|
// output bus (dout).
|
||||||
|
|
||||||
|
.dout(m_axis_tdata),
|
||||||
|
.empty(empty),
|
||||||
|
|
||||||
|
.full( ),
|
||||||
|
|
||||||
|
.prog_full(wr_unavail), // 1-bit output: Programmable Full: This signal is asserted when the number of words in the FIFO is greater than
|
||||||
|
// or equal to the programmable full threshold value. It is de-asserted when the number of words in the FIFO is
|
||||||
|
// less than the programmable full threshold value.
|
||||||
|
|
||||||
|
.rd_data_count(rd_data_count), // RD_DATA_COUNT_WIDTH-bit output: Read Data Count: This bus indicates the number of words read from the FIFO.
|
||||||
|
|
||||||
|
.wr_data_count(wr_data_count), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the
|
||||||
|
// FIFO.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.rd_clk(eth_clk_in), // 1-bit input: Read clock: Used for read operation. rd_clk must be a free running clock.
|
||||||
|
.rd_en(rd_en), // 1-bit input: Read Enable: If the FIFO is not empty, asserting this signal causes data (on dout) to be read
|
||||||
|
// from the FIFO. Must be held active-low when rd_rst_busy is active high.
|
||||||
|
|
||||||
|
.rst(rst),
|
||||||
|
|
||||||
|
.din(fifo_din_r), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO.
|
||||||
|
.wr_clk(acc_clk_in), // 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock.
|
||||||
|
.wr_en(fifo_wr_en_r),
|
||||||
|
|
||||||
|
.wr_rst_busy(wr_rst_busy)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
53
rtl/accum/tests/Makefile
Normal file
53
rtl/accum/tests/Makefile
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# - Alex Forencich
|
||||||
|
#
|
||||||
|
|
||||||
|
# FPGA settings
|
||||||
|
FPGA_PART = xc7a35tfgg484-1
|
||||||
|
FPGA_TOP = accumulator_top
|
||||||
|
FPGA_ARCH = artix7
|
||||||
|
|
||||||
|
RTL_DIR = ../src
|
||||||
|
|
||||||
|
|
||||||
|
include ../../../scripts/vivado.mk
|
||||||
|
|
||||||
|
SYN_FILES += $(sort $(shell find ../src -type f \( -name '*.v' -o -name '*.sv' \)))
|
||||||
|
|
||||||
|
XCI_FILES = $(sort $(shell find ../src -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
|
||||||
|
|
||||||
|
|
||||||
|
program: $(PROJECT).bit
|
||||||
|
echo "open_hw_manager" > program.tcl
|
||||||
|
echo "connect_hw_server" >> program.tcl
|
||||||
|
echo "open_hw_target" >> program.tcl
|
||||||
|
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||||
|
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||||
|
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||||
|
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||||
|
echo "exit" >> program.tcl
|
||||||
|
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||||
|
|
||||||
|
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||||
|
echo "write_cfgmem -force -format mcs -size 16 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||||
|
echo "exit" >> generate_mcs.tcl
|
||||||
|
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||||
|
mkdir -p rev
|
||||||
|
COUNT=100; \
|
||||||
|
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||||
|
do COUNT=$$((COUNT+1)); done; \
|
||||||
|
COUNT=$$((COUNT-1)); \
|
||||||
|
for x in .mcs .prm; \
|
||||||
|
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||||
|
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||||
345
rtl/accum/tests/accum_full_tb.sv
Normal file
345
rtl/accum/tests/accum_full_tb.sv
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module tb_accumulator_top;
|
||||||
|
|
||||||
|
localparam DATA_WIDTH = 12;
|
||||||
|
localparam ACCUM_WIDTH = 32;
|
||||||
|
localparam N_MAX = 4096;
|
||||||
|
localparam WINDOW_SIZE = 65;
|
||||||
|
localparam PACKET_SIZE = 1024;
|
||||||
|
localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH;
|
||||||
|
localparam MAX_WORDS = N_MAX;
|
||||||
|
localparam MAX_SEQ_NUM = 64;
|
||||||
|
|
||||||
|
logic clk_in;
|
||||||
|
logic eth_clk_in;
|
||||||
|
logic rst;
|
||||||
|
|
||||||
|
logic [DATA_WIDTH-1:0] s_axis_tdata;
|
||||||
|
logic s_axis_tvalid;
|
||||||
|
logic start;
|
||||||
|
logic [31:0] smp_num;
|
||||||
|
logic [15:0] seq_num;
|
||||||
|
|
||||||
|
logic req_ready;
|
||||||
|
wire send_req;
|
||||||
|
|
||||||
|
wire [7:0] m_axis_tdata;
|
||||||
|
wire m_axis_tvalid;
|
||||||
|
logic m_axis_tready;
|
||||||
|
wire m_axis_tlast;
|
||||||
|
|
||||||
|
wire finish;
|
||||||
|
|
||||||
|
integer seed;
|
||||||
|
integer total_errors;
|
||||||
|
integer tests_total;
|
||||||
|
integer tests_failed;
|
||||||
|
integer tests_passed;
|
||||||
|
|
||||||
|
integer packets_seen;
|
||||||
|
integer current_packet_byte_count;
|
||||||
|
integer total_words_captured;
|
||||||
|
|
||||||
|
byte packet_bytes [0:PACKET_SIZE-1];
|
||||||
|
logic [ACCUM_WIDTH-1:0] expected_words [0:MAX_WORDS-1];
|
||||||
|
logic [ACCUM_WIDTH-1:0] captured_words_le[0:MAX_WORDS-1];
|
||||||
|
logic [ACCUM_WIDTH-1:0] captured_words_be[0:MAX_WORDS-1];
|
||||||
|
|
||||||
|
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),
|
||||||
|
.rst(rst),
|
||||||
|
.s_axis_tdata(s_axis_tdata),
|
||||||
|
.s_axis_tvalid(s_axis_tvalid),
|
||||||
|
.start(start),
|
||||||
|
.smp_num(smp_num),
|
||||||
|
.seq_num(seq_num),
|
||||||
|
.eth_clk_in(eth_clk_in),
|
||||||
|
.req_ready(req_ready),
|
||||||
|
.send_req(send_req),
|
||||||
|
.m_axis_tdata(m_axis_tdata),
|
||||||
|
.m_axis_tvalid(m_axis_tvalid),
|
||||||
|
.m_axis_tready(m_axis_tready),
|
||||||
|
.m_axis_tlast(m_axis_tlast),
|
||||||
|
.finish(finish)
|
||||||
|
);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk_in = 1'b0;
|
||||||
|
forever #5 clk_in = ~clk_in;
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
eth_clk_in = 1'b0;
|
||||||
|
forever #4 eth_clk_in = ~eth_clk_in;
|
||||||
|
end
|
||||||
|
|
||||||
|
task automatic clear_scoreboard;
|
||||||
|
integer i;
|
||||||
|
begin
|
||||||
|
packets_seen = 0;
|
||||||
|
current_packet_byte_count = 0;
|
||||||
|
total_words_captured = 0;
|
||||||
|
for (i = 0; i < MAX_WORDS; i = i + 1) begin
|
||||||
|
expected_words[i] = '0;
|
||||||
|
captured_words_le[i] = '0;
|
||||||
|
captured_words_be[i] = '0;
|
||||||
|
end
|
||||||
|
for (i = 0; i < PACKET_SIZE; i = i + 1)
|
||||||
|
packet_bytes[i] = 8'h00;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic reset_dut;
|
||||||
|
begin
|
||||||
|
rst = 1'b1;
|
||||||
|
start = 1'b0;
|
||||||
|
s_axis_tdata = '0;
|
||||||
|
s_axis_tvalid = 1'b0;
|
||||||
|
smp_num = '0;
|
||||||
|
seq_num = '0;
|
||||||
|
req_ready = 1'b0;
|
||||||
|
m_axis_tready = 1'b1;
|
||||||
|
clear_scoreboard();
|
||||||
|
|
||||||
|
repeat(12) @(posedge clk_in);
|
||||||
|
rst = 1'b0;
|
||||||
|
repeat(8) @(posedge clk_in);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic pulse_start;
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
start <= 1'b1;
|
||||||
|
@(posedge clk_in);
|
||||||
|
start <= 1'b0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic send_one_sample(input logic [DATA_WIDTH-1:0] val);
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
s_axis_tdata <= val;
|
||||||
|
s_axis_tvalid <= 1'b1;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic stop_stream;
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
s_axis_tdata <= '0;
|
||||||
|
s_axis_tvalid <= 1'b0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic run_test(
|
||||||
|
input integer test_id,
|
||||||
|
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];
|
||||||
|
integer seq_idx;
|
||||||
|
integer sample_idx;
|
||||||
|
integer word_idx;
|
||||||
|
integer k;
|
||||||
|
integer exp_word_count;
|
||||||
|
integer exp_packet_count;
|
||||||
|
integer sample_value;
|
||||||
|
integer local_sum;
|
||||||
|
integer timeout_cnt;
|
||||||
|
bit le_ok;
|
||||||
|
bit be_ok;
|
||||||
|
integer errors_before;
|
||||||
|
integer i;
|
||||||
|
begin
|
||||||
|
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 (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("========================================");
|
||||||
|
|
||||||
|
reset_dut();
|
||||||
|
smp_num = smp_num_i;
|
||||||
|
seq_num = seq_num_i;
|
||||||
|
req_ready = 1'b1; // приемник готов заранее
|
||||||
|
|
||||||
|
exp_word_count = smp_num_i / WINDOW_SIZE;
|
||||||
|
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
|
||||||
|
for (sample_idx = 0; sample_idx < smp_num_i; sample_idx = sample_idx + 1) begin
|
||||||
|
if (randomize_data)
|
||||||
|
sample_value = $unsigned($random(seed)) % (1 << DATA_WIDTH);
|
||||||
|
else
|
||||||
|
sample_value = (base_value + seq_idx * smp_num_i + sample_idx) % (1 << DATA_WIDTH);
|
||||||
|
sample_mem[seq_idx][sample_idx] = sample_value[DATA_WIDTH-1:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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];
|
||||||
|
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]);
|
||||||
|
end
|
||||||
|
|
||||||
|
pulse_start();
|
||||||
|
|
||||||
|
for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin
|
||||||
|
for (sample_idx = 0; sample_idx < smp_num_i; sample_idx = sample_idx + 1)
|
||||||
|
send_one_sample(sample_mem[seq_idx][sample_idx]);
|
||||||
|
stop_stream();
|
||||||
|
repeat(2) @(posedge clk_in);
|
||||||
|
end
|
||||||
|
|
||||||
|
timeout_cnt = 0;
|
||||||
|
while (packets_seen < exp_packet_count && timeout_cnt < 50 * PACKET_SIZE) begin
|
||||||
|
@(posedge eth_clk_in);
|
||||||
|
timeout_cnt = timeout_cnt + 1;
|
||||||
|
end
|
||||||
|
if (packets_seen < exp_packet_count) begin
|
||||||
|
$display("[%0s] ERROR: timeout waiting packets, got=%0d exp=%0d",
|
||||||
|
test_name, packets_seen, exp_packet_count);
|
||||||
|
total_errors = total_errors + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
timeout_cnt = 0;
|
||||||
|
while (finish !== 1'b1 && timeout_cnt < 30000) begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
timeout_cnt = timeout_cnt + 1;
|
||||||
|
end
|
||||||
|
if (finish !== 1'b1) begin
|
||||||
|
$display("[%0s] ERROR: timeout waiting finish", test_name);
|
||||||
|
total_errors = total_errors + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
le_ok = 1'b1;
|
||||||
|
be_ok = 1'b1;
|
||||||
|
for (i = 0; i < exp_word_count; i = i + 1) begin
|
||||||
|
if (captured_words_le[i] !== expected_words[i]) le_ok = 1'b0;
|
||||||
|
if (captured_words_be[i] !== expected_words[i]) be_ok = 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (!le_ok && !be_ok) begin
|
||||||
|
$display("[%0s] ERROR: payload mismatch", test_name);
|
||||||
|
for (i = 0; i < exp_word_count; i = i + 1)
|
||||||
|
$display(" idx=%0d exp=0x%08x le=0x%08x be=0x%08x",
|
||||||
|
i, expected_words[i], captured_words_le[i], captured_words_be[i]);
|
||||||
|
total_errors = total_errors + 1;
|
||||||
|
end else if (le_ok) begin
|
||||||
|
$display("[%0s] payload check passed in little-endian", test_name);
|
||||||
|
end else begin
|
||||||
|
$display("[%0s] payload check passed in big-endian", test_name);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (total_errors == errors_before) begin
|
||||||
|
tests_passed = tests_passed + 1;
|
||||||
|
$display("TEST %0d PASSED: %0s", test_id, test_name);
|
||||||
|
end else begin
|
||||||
|
tests_failed = tests_failed + 1;
|
||||||
|
$display("TEST %0d FAILED: %0s", test_id, test_name);
|
||||||
|
end
|
||||||
|
|
||||||
|
req_ready = 1'b0;
|
||||||
|
repeat(10) @(posedge clk_in);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
always @(posedge eth_clk_in) begin : CAPTURE_AXIS
|
||||||
|
integer idx;
|
||||||
|
logic [31:0] tmp_le;
|
||||||
|
logic [31:0] tmp_be;
|
||||||
|
if (rst) begin
|
||||||
|
current_packet_byte_count = 0;
|
||||||
|
end else if (m_axis_tvalid && m_axis_tready) begin
|
||||||
|
if (current_packet_byte_count < PACKET_SIZE)
|
||||||
|
packet_bytes[current_packet_byte_count] = m_axis_tdata;
|
||||||
|
current_packet_byte_count = current_packet_byte_count + 1;
|
||||||
|
|
||||||
|
if (m_axis_tlast) begin
|
||||||
|
packets_seen = packets_seen + 1;
|
||||||
|
|
||||||
|
if (current_packet_byte_count != PACKET_SIZE) begin
|
||||||
|
$display("[packet] ERROR: packet size=%0d expected=%0d", current_packet_byte_count, PACKET_SIZE);
|
||||||
|
total_errors = total_errors + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
for (idx = 0; idx < READ_BATCH_SIZE; idx = idx + 1) begin
|
||||||
|
tmp_le = {
|
||||||
|
packet_bytes[idx*4 + 3],
|
||||||
|
packet_bytes[idx*4 + 2],
|
||||||
|
packet_bytes[idx*4 + 1],
|
||||||
|
packet_bytes[idx*4 + 0]
|
||||||
|
};
|
||||||
|
tmp_be = {
|
||||||
|
packet_bytes[idx*4 + 0],
|
||||||
|
packet_bytes[idx*4 + 1],
|
||||||
|
packet_bytes[idx*4 + 2],
|
||||||
|
packet_bytes[idx*4 + 3]
|
||||||
|
};
|
||||||
|
if (total_words_captured + idx < MAX_WORDS) begin
|
||||||
|
captured_words_le[total_words_captured + idx] = tmp_le;
|
||||||
|
captured_words_be[total_words_captured + idx] = tmp_be;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
total_words_captured = total_words_captured + READ_BATCH_SIZE;
|
||||||
|
current_packet_byte_count = 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
seed = 32'h1badf00d;
|
||||||
|
total_errors = 0;
|
||||||
|
tests_total = 0;
|
||||||
|
tests_failed = 0;
|
||||||
|
tests_passed = 0;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
$display("\n========================================");
|
||||||
|
$display("ALL TESTS COMPLETED");
|
||||||
|
$display("tests_total = %0d", tests_total);
|
||||||
|
$display("tests_passed = %0d", tests_passed);
|
||||||
|
$display("tests_failed = %0d", tests_failed);
|
||||||
|
$display("total_errors = %0d", total_errors);
|
||||||
|
$display("========================================");
|
||||||
|
|
||||||
|
if (total_errors != 0)
|
||||||
|
$fatal(1, "TB FAILED with %0d error(s)", total_errors);
|
||||||
|
else
|
||||||
|
$display("TB PASSED");
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
183
rtl/accum/tests/accum_tb.sv
Normal file
183
rtl/accum/tests/accum_tb.sv
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module tb_accumulator;
|
||||||
|
|
||||||
|
localparam DATA_WIDTH = 12;
|
||||||
|
localparam ACCUM_WIDTH = 32;
|
||||||
|
localparam N_MAX = 64;
|
||||||
|
localparam WINDOW_SIZE = 4;
|
||||||
|
localparam PACKET_SIZE = 8; // bytes
|
||||||
|
localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH; // = 2
|
||||||
|
|
||||||
|
reg clk_in;
|
||||||
|
reg rst;
|
||||||
|
reg [DATA_WIDTH-1:0] s_axis_tdata;
|
||||||
|
reg s_axis_tvalid;
|
||||||
|
reg start;
|
||||||
|
reg [31:0] smp_num;
|
||||||
|
reg [15:0] seq_num;
|
||||||
|
wire [ACCUM_WIDTH-1:0] out_data;
|
||||||
|
wire out_valid;
|
||||||
|
wire readout_begin;
|
||||||
|
reg batch_req;
|
||||||
|
reg finish;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
integer out_count;
|
||||||
|
|
||||||
|
reg [ACCUM_WIDTH-1:0] expected [0:READ_BATCH_SIZE-1];
|
||||||
|
reg [ACCUM_WIDTH-1:0] got [0:READ_BATCH_SIZE-1];
|
||||||
|
|
||||||
|
accumulator #(
|
||||||
|
.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),
|
||||||
|
.rst(rst),
|
||||||
|
.s_axis_tdata(s_axis_tdata),
|
||||||
|
.s_axis_tvalid(s_axis_tvalid),
|
||||||
|
.start(start),
|
||||||
|
.smp_num(smp_num),
|
||||||
|
.seq_num(seq_num),
|
||||||
|
.out_data(out_data),
|
||||||
|
.out_valid(out_valid),
|
||||||
|
.readout_begin(readout_begin),
|
||||||
|
.batch_req(batch_req),
|
||||||
|
.finish(finish)
|
||||||
|
);
|
||||||
|
|
||||||
|
// clock 100 MHz
|
||||||
|
initial begin
|
||||||
|
clk_in = 0;
|
||||||
|
forever #5 clk_in = ~clk_in;
|
||||||
|
end
|
||||||
|
|
||||||
|
// send one sample
|
||||||
|
task send_sample(input [DATA_WIDTH-1:0] val);
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
s_axis_tdata <= val;
|
||||||
|
s_axis_tvalid <= 1'b1;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// one idle cycle after valid stream
|
||||||
|
task end_stream;
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
s_axis_tvalid <= 1'b0;
|
||||||
|
s_axis_tdata <= '0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// pulse start
|
||||||
|
task pulse_start;
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
start <= 1'b1;
|
||||||
|
@(posedge clk_in);
|
||||||
|
start <= 1'b0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// pulse batch request
|
||||||
|
task pulse_batch_req;
|
||||||
|
begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
batch_req <= 1'b1;
|
||||||
|
@(posedge clk_in);
|
||||||
|
batch_req <= 1'b0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
repeat(100) @(posedge clk_in);
|
||||||
|
// init
|
||||||
|
rst = 1'b1;
|
||||||
|
s_axis_tdata = '0;
|
||||||
|
s_axis_tvalid= 1'b0;
|
||||||
|
start = 1'b0;
|
||||||
|
smp_num = 32'd8;
|
||||||
|
seq_num = 16'd2;
|
||||||
|
batch_req = 1'b0;
|
||||||
|
finish = 1'b0;
|
||||||
|
|
||||||
|
expected[0] = 32'd60;
|
||||||
|
expected[1] = 32'd92;
|
||||||
|
|
||||||
|
repeat(50) @(posedge clk_in);
|
||||||
|
rst = 1'b0;
|
||||||
|
repeat(50) @(posedge clk_in);
|
||||||
|
|
||||||
|
$display("=== TEST START ===");
|
||||||
|
|
||||||
|
pulse_start();
|
||||||
|
|
||||||
|
// seq 0: [1..8]
|
||||||
|
send_sample(12'd1);
|
||||||
|
send_sample(12'd2);
|
||||||
|
send_sample(12'd3);
|
||||||
|
send_sample(12'd4);
|
||||||
|
send_sample(12'd5);
|
||||||
|
send_sample(12'd6);
|
||||||
|
send_sample(12'd7);
|
||||||
|
send_sample(12'd8);
|
||||||
|
end_stream();
|
||||||
|
|
||||||
|
// небольшой зазор
|
||||||
|
repeat(5) @(posedge clk_in);
|
||||||
|
|
||||||
|
// seq 1: [11..18]
|
||||||
|
send_sample(12'd11);
|
||||||
|
send_sample(12'd12);
|
||||||
|
send_sample(12'd13);
|
||||||
|
send_sample(12'd14);
|
||||||
|
send_sample(12'd15);
|
||||||
|
send_sample(12'd16);
|
||||||
|
send_sample(12'd17);
|
||||||
|
send_sample(12'd18);
|
||||||
|
end_stream();
|
||||||
|
|
||||||
|
$display("[%0t] all input data sent, waiting readout_begin...", $time);
|
||||||
|
|
||||||
|
wait(readout_begin == 1'b1);
|
||||||
|
$display("[%0t] readout_begin asserted", $time);
|
||||||
|
repeat(22) @(posedge clk_in);
|
||||||
|
pulse_batch_req();
|
||||||
|
|
||||||
|
out_count = 0;
|
||||||
|
|
||||||
|
// ждём два слова
|
||||||
|
while (out_count < READ_BATCH_SIZE) begin
|
||||||
|
@(posedge clk_in);
|
||||||
|
if (out_valid) begin
|
||||||
|
got[out_count] = out_data;
|
||||||
|
$display("[%0t] out_valid: got[%0d] = %0d", $time, out_count, out_data);
|
||||||
|
out_count = out_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// проверка
|
||||||
|
for (i = 0; i < READ_BATCH_SIZE; i = i + 1) begin
|
||||||
|
if (got[i] !== expected[i]) begin
|
||||||
|
$error("Mismatch at index %0d: got=%0d expected=%0d", i, got[i], expected[i]);
|
||||||
|
end else begin
|
||||||
|
$display("OK index %0d: %0d", i, got[i]);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// завершаем readout
|
||||||
|
@(posedge clk_in);
|
||||||
|
finish <= 1'b1;
|
||||||
|
|
||||||
|
|
||||||
|
repeat(10) @(posedge clk_in);
|
||||||
|
|
||||||
|
$display("=== TEST PASSED ===");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
290
rtl/accum/tests/out_axis_fifo_tb.sv
Normal file
290
rtl/accum/tests/out_axis_fifo_tb.sv
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb_out_axis_fifo;
|
||||||
|
|
||||||
|
localparam int ACCUM_WIDTH = 32;
|
||||||
|
localparam int WINDOW_SIZE = 65;
|
||||||
|
localparam int PACKET_SIZE = 8;
|
||||||
|
localparam int BYTES_PER_WORD = ACCUM_WIDTH / 8;
|
||||||
|
localparam int WORDS_PER_BATCH = PACKET_SIZE / BYTES_PER_WORD; // 1024 / 4 = 256 слов
|
||||||
|
|
||||||
|
logic eth_clk_in;
|
||||||
|
logic acc_clk_in;
|
||||||
|
logic rst;
|
||||||
|
|
||||||
|
logic [31:0] smp_num;
|
||||||
|
|
||||||
|
logic [7:0] m_axis_tdata;
|
||||||
|
logic m_axis_tvalid;
|
||||||
|
logic m_axis_tready;
|
||||||
|
logic m_axis_tlast;
|
||||||
|
|
||||||
|
logic [ACCUM_WIDTH-1:0] acc_din;
|
||||||
|
logic din_valid;
|
||||||
|
|
||||||
|
logic send_req;
|
||||||
|
logic req_ready;
|
||||||
|
|
||||||
|
logic readout_begin;
|
||||||
|
|
||||||
|
logic batch_req;
|
||||||
|
logic finish;
|
||||||
|
|
||||||
|
out_axis_fifo #(
|
||||||
|
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||||||
|
.WINDOW_SIZE(WINDOW_SIZE),
|
||||||
|
.PACKET_SIZE(PACKET_SIZE)
|
||||||
|
) dut (
|
||||||
|
.eth_clk_in (eth_clk_in),
|
||||||
|
.acc_clk_in (acc_clk_in),
|
||||||
|
.rst (rst),
|
||||||
|
.smp_num (smp_num),
|
||||||
|
|
||||||
|
.m_axis_tdata (m_axis_tdata),
|
||||||
|
.m_axis_tvalid (m_axis_tvalid),
|
||||||
|
.m_axis_tready (m_axis_tready),
|
||||||
|
.m_axis_tlast (m_axis_tlast),
|
||||||
|
|
||||||
|
.acc_din (acc_din),
|
||||||
|
.din_valid (din_valid),
|
||||||
|
|
||||||
|
.readout_begin (readout_begin),
|
||||||
|
|
||||||
|
.req_ready (req_ready),
|
||||||
|
.send_req (send_req),
|
||||||
|
|
||||||
|
.batch_req (batch_req),
|
||||||
|
.finish (finish)
|
||||||
|
);
|
||||||
|
|
||||||
|
// clocks
|
||||||
|
initial begin
|
||||||
|
eth_clk_in = 0;
|
||||||
|
forever #6 eth_clk_in = ~eth_clk_in; // 125
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
acc_clk_in = 0;
|
||||||
|
forever #7.692307692 acc_clk_in = ~acc_clk_in; // 65
|
||||||
|
end
|
||||||
|
|
||||||
|
// scoreboard
|
||||||
|
byte expected_bytes[$];
|
||||||
|
int unsigned compared_bytes;
|
||||||
|
int unsigned mismatch_count;
|
||||||
|
int unsigned total_pushed_words;
|
||||||
|
|
||||||
|
task automatic scoreboard_reset();
|
||||||
|
begin
|
||||||
|
expected_bytes.delete();
|
||||||
|
compared_bytes = 0;
|
||||||
|
mismatch_count = 0;
|
||||||
|
total_pushed_words = 0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic push_expected_word(input logic [ACCUM_WIDTH-1:0] word);
|
||||||
|
begin
|
||||||
|
// queue push
|
||||||
|
expected_bytes.push_back(word[7:0]);
|
||||||
|
expected_bytes.push_back(word[15:8]);
|
||||||
|
expected_bytes.push_back(word[23:16]);
|
||||||
|
expected_bytes.push_back(word[31:24]);
|
||||||
|
total_pushed_words++;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic check_expected_empty(string case_name);
|
||||||
|
begin
|
||||||
|
if (expected_bytes.size() != 0) begin
|
||||||
|
$error("[%0t] %s: expected_bytes is not empty, remaining=%0d",
|
||||||
|
$time, case_name, expected_bytes.size());
|
||||||
|
end else begin
|
||||||
|
$display("[%0t] %s: scoreboard queue empty, all expected bytes were transmitted",
|
||||||
|
$time, case_name);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// axis check
|
||||||
|
always_ff @(posedge eth_clk_in or posedge rst) begin
|
||||||
|
byte exp_byte;
|
||||||
|
if (rst) begin
|
||||||
|
compared_bytes <= 0;
|
||||||
|
mismatch_count <= 0;
|
||||||
|
end else begin
|
||||||
|
if (m_axis_tvalid && m_axis_tready) begin
|
||||||
|
if (expected_bytes.size() == 0) begin
|
||||||
|
$error("[%0t] AXIS produced unexpected byte 0x%02x: expected queue is empty",
|
||||||
|
$time, m_axis_tdata);
|
||||||
|
mismatch_count <= mismatch_count + 1;
|
||||||
|
end else begin
|
||||||
|
exp_byte = expected_bytes.pop_front();
|
||||||
|
compared_bytes <= compared_bytes + 1;
|
||||||
|
|
||||||
|
if (m_axis_tdata !== exp_byte) begin
|
||||||
|
$error("[%0t] AXIS mismatch at byte #%0d: got=0x%02x expected=0x%02x",
|
||||||
|
$time, compared_bytes, m_axis_tdata, exp_byte);
|
||||||
|
mismatch_count <= mismatch_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
task automatic do_reset();
|
||||||
|
begin
|
||||||
|
rst = 1'b1;
|
||||||
|
readout_begin = 1'b0;
|
||||||
|
din_valid = 1'b0;
|
||||||
|
acc_din = '0;
|
||||||
|
smp_num = '0;
|
||||||
|
|
||||||
|
scoreboard_reset();
|
||||||
|
|
||||||
|
repeat (10) @(posedge acc_clk_in);
|
||||||
|
rst = 1'b0;
|
||||||
|
|
||||||
|
repeat (10) @(posedge acc_clk_in);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic pulse_readout_begin(input logic [31:0] smp_num_i);
|
||||||
|
begin
|
||||||
|
smp_num = smp_num_i;
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
readout_begin <= 1'b1;
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
readout_begin <= 1'b0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task automatic send_random_words(input int unsigned n_words);
|
||||||
|
int unsigned i;
|
||||||
|
logic [ACCUM_WIDTH-1:0] rand_word;
|
||||||
|
begin
|
||||||
|
for (i = 0; i < n_words; i++) begin
|
||||||
|
rand_word = $urandom;
|
||||||
|
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
din_valid <= 1'b1;
|
||||||
|
acc_din <= rand_word;
|
||||||
|
|
||||||
|
// expected result
|
||||||
|
push_expected_word(rand_word);
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
din_valid <= 1'b0;
|
||||||
|
acc_din <= '0;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
|
||||||
|
// 1. set smp_num
|
||||||
|
// 2. pulse readout_begon
|
||||||
|
// 3. send 1KB (PACKET_SIZE) after each batch_req pulse
|
||||||
|
// 4. wait for finish
|
||||||
|
// 5. compare axis result
|
||||||
|
task automatic run_case(input logic [31:0] smp_num_i);
|
||||||
|
int batch_count;
|
||||||
|
string case_name;
|
||||||
|
begin
|
||||||
|
batch_count = 0;
|
||||||
|
case_name = $sformatf("run_case(smp_num=%0d)", smp_num_i);
|
||||||
|
|
||||||
|
$display("[%0t] %s start", $time, case_name);
|
||||||
|
|
||||||
|
pulse_readout_begin(smp_num_i);
|
||||||
|
|
||||||
|
while (finish !== 1'b1) begin
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
|
||||||
|
if (batch_req) begin
|
||||||
|
batch_count++;
|
||||||
|
$display("[%0t] %s: batch_req #%0d -> send %0d words",
|
||||||
|
$time, case_name, batch_count, WORDS_PER_BATCH);
|
||||||
|
|
||||||
|
send_random_words(WORDS_PER_BATCH);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
repeat (200) @(posedge eth_clk_in);
|
||||||
|
|
||||||
|
$display("[%0t] %s done: batches=%0d, pushed_words=%0d, compared_bytes=%0d, mismatches=%0d, wr_cnt=%0d, wr_total=%0d",
|
||||||
|
$time, case_name, batch_count, total_pushed_words, compared_bytes, mismatch_count,
|
||||||
|
dut.wr_cnt, dut.wr_total);
|
||||||
|
|
||||||
|
check_expected_empty(case_name);
|
||||||
|
|
||||||
|
if (mismatch_count != 0) begin
|
||||||
|
$fatal(1, "[%0t] %s FAILED: mismatches=%0d", $time, case_name, mismatch_count);
|
||||||
|
end else begin
|
||||||
|
$display("[%0t] %s PASSED", $time, case_name);
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge acc_clk_in);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// eth beh simulator
|
||||||
|
int axis_byte_count;
|
||||||
|
|
||||||
|
always_ff @(posedge eth_clk_in or posedge rst) begin
|
||||||
|
if (rst) begin
|
||||||
|
axis_byte_count <= 0;
|
||||||
|
req_ready <= 0;
|
||||||
|
m_axis_tready <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
req_ready <= 1;
|
||||||
|
|
||||||
|
// request send
|
||||||
|
if (send_req) begin
|
||||||
|
m_axis_tready <= 1'b1;
|
||||||
|
req_ready <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (m_axis_tvalid && m_axis_tready) begin
|
||||||
|
axis_byte_count <= axis_byte_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
// main
|
||||||
|
initial begin
|
||||||
|
// init
|
||||||
|
rst = 1'b0;
|
||||||
|
readout_begin = 1'b0;
|
||||||
|
din_valid = 1'b0;
|
||||||
|
acc_din = '0;
|
||||||
|
smp_num = '0;
|
||||||
|
|
||||||
|
repeat (500) @(posedge acc_clk_in);
|
||||||
|
|
||||||
|
// 1
|
||||||
|
do_reset();
|
||||||
|
repeat (500) @(posedge acc_clk_in);
|
||||||
|
run_case(32'd17);
|
||||||
|
repeat (20) @(posedge acc_clk_in);
|
||||||
|
|
||||||
|
// 2
|
||||||
|
do_reset();
|
||||||
|
run_case(32'd1024);
|
||||||
|
repeat (20) @(posedge acc_clk_in);
|
||||||
|
|
||||||
|
// 3
|
||||||
|
do_reset();
|
||||||
|
run_case(32'd77777);
|
||||||
|
repeat (20) @(posedge acc_clk_in);
|
||||||
|
|
||||||
|
do_reset();
|
||||||
|
repeat (20) @(posedge acc_clk_in);
|
||||||
|
|
||||||
|
$display("[%0t] ALL TESTS DONE", $time);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
179
rtl/accum/tests/tb_accumulator_top_behav.wcfg
Normal file
179
rtl/accum/tests/tb_accumulator_top_behav.wcfg
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<wave_config>
|
||||||
|
<wave_state>
|
||||||
|
</wave_state>
|
||||||
|
<db_ref_list>
|
||||||
|
<db_ref path="tb_accumulator_top_behav.wdb" id="1">
|
||||||
|
<top_modules>
|
||||||
|
<top_module name="glbl" />
|
||||||
|
<top_module name="tb_accumulator_top" />
|
||||||
|
</top_modules>
|
||||||
|
</db_ref>
|
||||||
|
</db_ref_list>
|
||||||
|
<zoom_setting>
|
||||||
|
<ZoomStartTime time="0.000000 us"></ZoomStartTime>
|
||||||
|
<ZoomEndTime time="16.740001 us"></ZoomEndTime>
|
||||||
|
<Cursor1Time time="6.500000 us"></Cursor1Time>
|
||||||
|
</zoom_setting>
|
||||||
|
<column_width_setting>
|
||||||
|
<NameColumnWidth column_width="556"></NameColumnWidth>
|
||||||
|
<ValueColumnWidth column_width="111"></ValueColumnWidth>
|
||||||
|
</column_width_setting>
|
||||||
|
<WVObjectSize size="18" />
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/clk_in">
|
||||||
|
<obj_property name="ElementShortName">clk_in</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">clk_in</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/eth_clk_in">
|
||||||
|
<obj_property name="ElementShortName">eth_clk_in</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">eth_clk_in</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/rst">
|
||||||
|
<obj_property name="ElementShortName">rst</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rst</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/s_axis_tdata">
|
||||||
|
<obj_property name="ElementShortName">s_axis_tdata[11:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">s_axis_tdata[11:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/s_axis_tvalid">
|
||||||
|
<obj_property name="ElementShortName">s_axis_tvalid</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">s_axis_tvalid</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/start">
|
||||||
|
<obj_property name="ElementShortName">start</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">start</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/smp_num">
|
||||||
|
<obj_property name="ElementShortName">smp_num[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">smp_num[31:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/seq_num">
|
||||||
|
<obj_property name="ElementShortName">seq_num[15:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">seq_num[15:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/req_ready">
|
||||||
|
<obj_property name="ElementShortName">req_ready</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">req_ready</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#E0FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/send_req">
|
||||||
|
<obj_property name="ElementShortName">send_req</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">send_req</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#E0FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/m_axis_tdata">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tdata[7:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tdata[7:0]</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#008080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/m_axis_tready">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tready</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tready</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#00FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/m_axis_tlast">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tlast</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tlast</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#008080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/finish">
|
||||||
|
<obj_property name="ElementShortName">finish</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">finish</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FAAFBE</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/batch_req">
|
||||||
|
<obj_property name="ElementShortName">batch_req</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">batch_req</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#00FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/readout_begin">
|
||||||
|
<obj_property name="ElementShortName">readout_begin</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">readout_begin</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#00FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="group" fp_name="group25">
|
||||||
|
<obj_property name="label">acc</obj_property>
|
||||||
|
<obj_property name="DisplayName">label</obj_property>
|
||||||
|
<obj_property name="isExpanded"></obj_property>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/accum_main/PACKET_SIZE">
|
||||||
|
<obj_property name="ElementShortName">PACKET_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">PACKET_SIZE[31:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/accum_main/READ_BATCH_SIZE">
|
||||||
|
<obj_property name="ElementShortName">READ_BATCH_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">READ_BATCH_SIZE[31:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/accum_main/addrb">
|
||||||
|
<obj_property name="ElementShortName">addrb[15:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">addrb[15:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="group" fp_name="group27">
|
||||||
|
<obj_property name="label">fifo</obj_property>
|
||||||
|
<obj_property name="DisplayName">label</obj_property>
|
||||||
|
<obj_property name="isExpanded"></obj_property>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/acc_din">
|
||||||
|
<obj_property name="ElementShortName">acc_din[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">acc_din[31:0]</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FF0080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/output_async_fifo/din_valid">
|
||||||
|
<obj_property name="ElementShortName">din_valid</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">din_valid</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FF0080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/output_async_fifo/batch_req">
|
||||||
|
<obj_property name="ElementShortName">batch_req</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">batch_req</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/wr_state">
|
||||||
|
<obj_property name="ElementShortName">wr_state[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_state[2:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/rd_state">
|
||||||
|
<obj_property name="ElementShortName">rd_state[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_state[2:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/output_async_fifo/wr_unavail">
|
||||||
|
<obj_property name="ElementShortName">wr_unavail</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_unavail</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FFFF00</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/output_async_fifo/wr_rst_busy">
|
||||||
|
<obj_property name="ElementShortName">wr_rst_busy</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_rst_busy</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FFFF00</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_accumulator_top/dut/output_async_fifo/xpm_fifo_async_inst/empty">
|
||||||
|
<obj_property name="ElementShortName">empty</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">empty</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/xpm_fifo_async_inst/PROG_FULL_THRESH">
|
||||||
|
<obj_property name="ElementShortName">PROG_FULL_THRESH[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">PROG_FULL_THRESH[31:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/xpm_fifo_async_inst/wr_data_count">
|
||||||
|
<obj_property name="ElementShortName">wr_data_count[4:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_data_count[4:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_accumulator_top/dut/output_async_fifo/xpm_fifo_async_inst/rd_data_count">
|
||||||
|
<obj_property name="ElementShortName">rd_data_count[6:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_data_count[6:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
</wvobject>
|
||||||
|
</wave_config>
|
||||||
196
rtl/accum/tests/tb_out_axis_fifo_behav.wcfg
Normal file
196
rtl/accum/tests/tb_out_axis_fifo_behav.wcfg
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<wave_config>
|
||||||
|
<wave_state>
|
||||||
|
</wave_state>
|
||||||
|
<db_ref_list>
|
||||||
|
<db_ref path="tb_out_axis_fifo_behav.wdb" id="1">
|
||||||
|
<top_modules>
|
||||||
|
<top_module name="glbl" />
|
||||||
|
<top_module name="tb_out_axis_fifo" />
|
||||||
|
</top_modules>
|
||||||
|
</db_ref>
|
||||||
|
</db_ref_list>
|
||||||
|
<zoom_setting>
|
||||||
|
<ZoomStartTime time="18,433.000 ns"></ZoomStartTime>
|
||||||
|
<ZoomEndTime time="24,238.001 ns"></ZoomEndTime>
|
||||||
|
<Cursor1Time time="21,618.000 ns"></Cursor1Time>
|
||||||
|
</zoom_setting>
|
||||||
|
<column_width_setting>
|
||||||
|
<NameColumnWidth column_width="196"></NameColumnWidth>
|
||||||
|
<ValueColumnWidth column_width="147"></ValueColumnWidth>
|
||||||
|
</column_width_setting>
|
||||||
|
<WVObjectSize size="37" />
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/eth_clk_in">
|
||||||
|
<obj_property name="ElementShortName">eth_clk_in</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">eth_clk_in</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/acc_clk_in">
|
||||||
|
<obj_property name="ElementShortName">acc_clk_in</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">acc_clk_in</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/rst">
|
||||||
|
<obj_property name="ElementShortName">rst</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rst</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/smp_num">
|
||||||
|
<obj_property name="ElementShortName">smp_num[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">smp_num[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/acc_din">
|
||||||
|
<obj_property name="ElementShortName">acc_din[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">acc_din[31:0]</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FF0080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/din_valid">
|
||||||
|
<obj_property name="ElementShortName">din_valid</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">din_valid</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FF0080</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/fifo_din_r">
|
||||||
|
<obj_property name="ElementShortName">fifo_din_r[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">fifo_din_r[31:0]</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FFA500</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/fifo_wr_en_r">
|
||||||
|
<obj_property name="ElementShortName">fifo_wr_en_r</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">fifo_wr_en_r</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FFA500</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/readout_begin">
|
||||||
|
<obj_property name="ElementShortName">readout_begin</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">readout_begin</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#FFFF00</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/batch_req">
|
||||||
|
<obj_property name="ElementShortName">batch_req</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">batch_req</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/finish">
|
||||||
|
<obj_property name="ElementShortName">finish</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">finish</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#00FFFF</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/m_axis_tdata">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tdata[7:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tdata[7:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/m_axis_tvalid">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tvalid</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tvalid</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/m_axis_tready">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tready</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tready</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/m_axis_tlast">
|
||||||
|
<obj_property name="ElementShortName">m_axis_tlast</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">m_axis_tlast</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/axis_byte_count">
|
||||||
|
<obj_property name="ElementShortName">axis_byte_count[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">axis_byte_count[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
<obj_property name="CustomSignalColor">#F0E68C</obj_property>
|
||||||
|
<obj_property name="UseCustomSignalColor">true</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/ACCUM_WIDTH">
|
||||||
|
<obj_property name="ElementShortName">ACCUM_WIDTH[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">ACCUM_WIDTH[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/WINDOW_SIZE">
|
||||||
|
<obj_property name="ElementShortName">WINDOW_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">WINDOW_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/PACKET_SIZE">
|
||||||
|
<obj_property name="ElementShortName">PACKET_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">PACKET_SIZE[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/wr_state">
|
||||||
|
<obj_property name="ElementShortName">wr_state[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_state[2:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/wr_cnt">
|
||||||
|
<obj_property name="ElementShortName">wr_cnt[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_cnt[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/wr_batch_tgt">
|
||||||
|
<obj_property name="ElementShortName">wr_batch_tgt[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_batch_tgt[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/wr_total">
|
||||||
|
<obj_property name="ElementShortName">wr_total[31:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_total[31:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/prog_empty">
|
||||||
|
<obj_property name="ElementShortName">prog_empty</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">prog_empty</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/prog_full">
|
||||||
|
<obj_property name="ElementShortName">prog_full</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">prog_full</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/wr_ack">
|
||||||
|
<obj_property name="ElementShortName">wr_ack</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_ack</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/wr_data_count">
|
||||||
|
<obj_property name="ElementShortName">wr_data_count[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_data_count[2:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/wr_data_count">
|
||||||
|
<obj_property name="ElementShortName">wr_data_count[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_data_count[2:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/rd_data_count">
|
||||||
|
<obj_property name="ElementShortName">rd_data_count[4:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_data_count[4:0]</obj_property>
|
||||||
|
<obj_property name="Radix">UNSIGNEDDECRADIX</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/rst_sync_ff">
|
||||||
|
<obj_property name="ElementShortName">rst_sync_ff[1:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rst_sync_ff[1:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="array" fp_name="/tb_out_axis_fifo/dut/rd_state">
|
||||||
|
<obj_property name="ElementShortName">rd_state[2:0]</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_state[2:0]</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/rd_en">
|
||||||
|
<obj_property name="ElementShortName">rd_en</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_en</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/rd_valid">
|
||||||
|
<obj_property name="ElementShortName">rd_valid</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">rd_valid</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/overflow">
|
||||||
|
<obj_property name="ElementShortName">overflow</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">overflow</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/dut/xpm_fifo_async_inst/wr_rst_busy">
|
||||||
|
<obj_property name="ElementShortName">wr_rst_busy</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">wr_rst_busy</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/send_req">
|
||||||
|
<obj_property name="ElementShortName">send_req</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">send_req</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
<wvobject type="logic" fp_name="/tb_out_axis_fifo/req_ready">
|
||||||
|
<obj_property name="ElementShortName">req_ready</obj_property>
|
||||||
|
<obj_property name="ObjectShortName">req_ready</obj_property>
|
||||||
|
</wvobject>
|
||||||
|
</wave_config>
|
||||||
10
rtl/accum/tests/test_timing.xdc
Normal file
10
rtl/accum/tests/test_timing.xdc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Primary clocks
|
||||||
|
create_clock -name eth_clk -period 8.000 [get_ports eth_clk_in]
|
||||||
|
create_clock -name acc_clk -period 15.385 [get_ports clk_in]
|
||||||
|
|
||||||
|
|
||||||
|
# Asynchronous clock groups
|
||||||
|
|
||||||
|
set_clock_groups -name ASYNC_ETH_ACC -asynchronous \
|
||||||
|
-group [get_clocks eth_clk] \
|
||||||
|
-group [get_clocks acc_clk]
|
||||||
28
rtl/sampler/README.md
Normal file
28
rtl/sampler/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Сэмплер
|
||||||
|
Модуль выполняет задачу сбора данных с выхода АЦП, их обработку, упаковку, и передачу дальше с помощью AXI Stream интерфейса.
|
||||||
|
|
||||||
|
## Cписок параметров
|
||||||
|
DATA_WIDTH - ширина входных данных, получаемых с АЦП.
|
||||||
|
PACK_FACTOR - количество отсчетов, собираемых в один выходной пакет.
|
||||||
|
PROCESS_MODE - режим интерпретации входного кода. 0 - прямой код, 1 - дополнительный код.
|
||||||
|
|
||||||
|
## Список входных портов
|
||||||
|
clk_in - сигнал тактирования выходного интерфейса.
|
||||||
|
rst - сброс модуля и остановка подачи импульсов.
|
||||||
|
[DATA_WIDTH-1:0] data_in - входной сигнал с АЦП.
|
||||||
|
out_of_range - флаг выхода значений данных за допустимый диапазон. 0 - валидны, 1 - не валидны.
|
||||||
|
|
||||||
|
## Список выходных портов
|
||||||
|
[DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata - урезанный axis формат, выходные данные. Ширина шины считается исходя из битности данных и фактора упаковки.
|
||||||
|
m_axis_tvalid - урезанный axis формат, валидность выходных данных.
|
||||||
|
|
||||||
|
## Логика работы
|
||||||
|
На каждом такте принимаются data_in (значение АЦП) и out_of_range (флаг выхода значений данных за допустимый диапазон). Если out_of_range = 1, то данные игнорируются и не попадают во внутренний буффер. В противном случае, модуль накапливает данные во внутреннем буффере, идет его заполнение до количества данных, равное PACK_FACTOR. Когда буффер оказывается заполненным, он выдает пакет упакованных данных, сопровождая их импульсом m_axis_tvalid (готовность пакета). Если PROCESS_MODE = 1, данные выдаются в дополнительном коде, если PROCESS_MODE = 0 - в прямом.
|
||||||
|
|
||||||
|
## Симуляция
|
||||||
|
Тесты запускаются автоматически через make.
|
||||||
|
```
|
||||||
|
cd tests
|
||||||
|
make sim
|
||||||
|
```
|
||||||
|
При успешном завершении теста высвечивается "ALL PASSED".
|
||||||
@ -5,8 +5,8 @@
|
|||||||
module sampler
|
module sampler
|
||||||
#(
|
#(
|
||||||
parameter DATA_WIDTH = 12,
|
parameter DATA_WIDTH = 12,
|
||||||
parameter PACK_FACTOR = 3,
|
parameter PACK_FACTOR = 1,
|
||||||
parameter PROCESS_MODE = 1
|
parameter PROCESS_MODE = 0
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
input clk_in,
|
input clk_in,
|
||||||
@ -20,34 +20,61 @@ module sampler
|
|||||||
logic [DATA_WIDTH-1:0] data_converted;
|
logic [DATA_WIDTH-1:0] data_converted;
|
||||||
logic out_of_range_reg;
|
logic out_of_range_reg;
|
||||||
|
|
||||||
always @( posedge clk_in) begin
|
generate
|
||||||
|
if (PROCESS_MODE) begin
|
||||||
|
|
||||||
|
always @(posedge clk_in) begin
|
||||||
if (rst) begin
|
if (rst) begin
|
||||||
data_converted <= '0;
|
data_converted <= '0;
|
||||||
end else begin
|
out_of_range_reg <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
out_of_range_reg <= out_of_range;
|
out_of_range_reg <= out_of_range;
|
||||||
if (PROCESS_MODE) begin
|
if (data_in == {1'b1, {(DATA_WIDTH-1){1'b0}}})
|
||||||
if (data_in == 12'b100000000000) begin
|
|
||||||
data_converted <= data_in;
|
data_converted <= data_in;
|
||||||
end else begin
|
else
|
||||||
data_converted <= data_in[DATA_WIDTH-1] ? {1'b1, (~data_in[DATA_WIDTH-2:0] + 1'b1)} : data_in;
|
data_converted <= data_in[DATA_WIDTH-1] ?{1'b1, (~data_in[DATA_WIDTH-2:0] + 1'b1)}:data_in;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end else begin
|
end else begin
|
||||||
|
always @(posedge clk_in) begin
|
||||||
|
if (rst) begin
|
||||||
|
data_converted <= '0;
|
||||||
|
out_of_range_reg <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
out_of_range_reg <= out_of_range;
|
||||||
data_converted <= data_in;
|
data_converted <= data_in;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
logic [DATA_WIDTH*PACK_FACTOR-1:0] buffer;
|
logic [DATA_WIDTH*PACK_FACTOR-1:0] buffer;
|
||||||
logic buffer_ready;
|
logic buffer_ready;
|
||||||
|
|
||||||
logic [$clog2(PACK_FACTOR):0] cnt;
|
logic [$clog2(PACK_FACTOR):0] cnt;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (PACK_FACTOR == 1) begin
|
||||||
always @(posedge clk_in) begin
|
always @(posedge clk_in) begin
|
||||||
if (rst) begin
|
if (rst) begin
|
||||||
buffer <= '0;
|
buffer <= '0;
|
||||||
cnt <= -1; //
|
buffer_ready <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
buffer_ready <= 0;
|
||||||
|
if (!out_of_range_reg) begin
|
||||||
|
buffer <= data_converted;
|
||||||
|
buffer_ready <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
always @(posedge clk_in) begin
|
||||||
|
if (rst) begin
|
||||||
|
buffer <= '0;
|
||||||
|
cnt <= '0; //
|
||||||
buffer_ready <= 0;
|
buffer_ready <= 0;
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
@ -65,6 +92,30 @@ module sampler
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// always @(posedge clk_in) begin
|
||||||
|
// if (rst) begin
|
||||||
|
// buffer <= '0;
|
||||||
|
// cnt <= '0; //
|
||||||
|
// buffer_ready <= 0;
|
||||||
|
// end
|
||||||
|
// else begin
|
||||||
|
// buffer_ready <= 0;
|
||||||
|
// if (!out_of_range_reg) begin
|
||||||
|
// buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted};
|
||||||
|
// if (cnt == PACK_FACTOR-1) begin
|
||||||
|
// cnt <= 0;
|
||||||
|
// buffer_ready <= 1;
|
||||||
|
// buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted};
|
||||||
|
// end
|
||||||
|
// else begin
|
||||||
|
// cnt <= cnt + 1;
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
// end
|
||||||
|
|
||||||
assign m_axis_tdata = buffer;
|
assign m_axis_tdata = buffer;
|
||||||
assign m_axis_tvalid = buffer_ready;
|
assign m_axis_tvalid = buffer_ready;
|
||||||
|
|||||||
51
rtl/sampler/tests/Makefile
Normal file
51
rtl/sampler/tests/Makefile
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# Copyright (c) 2025 FPGA Ninja, LLC
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# - Alex Forencich
|
||||||
|
#
|
||||||
|
|
||||||
|
# FPGA settings
|
||||||
|
FPGA_PART = xc7a35tfgg484-1
|
||||||
|
FPGA_TOP = sampler
|
||||||
|
FPGA_ARCH = artix7
|
||||||
|
|
||||||
|
RTL_DIR = ../src
|
||||||
|
|
||||||
|
|
||||||
|
include ../../../scripts/vivado.mk
|
||||||
|
|
||||||
|
SYN_FILES += $(sort $(shell find ../src -type f \( -name '*.v' -o -name '*.sv' \)))
|
||||||
|
|
||||||
|
XCI_FILES = $(sort $(shell find ../src -type f -name '*.xci'))
|
||||||
|
|
||||||
|
XDC_FILES += ../../../constraints/ax7a035b.xdc
|
||||||
|
|
||||||
|
SYN_FILES += sampler_main_tb.sv
|
||||||
|
SIM_TOP = sampler_tb
|
||||||
|
|
||||||
|
|
||||||
|
program: $(PROJECT).bit
|
||||||
|
echo "open_hw_manager" > program.tcl
|
||||||
|
echo "connect_hw_server" >> program.tcl
|
||||||
|
echo "open_hw_target" >> program.tcl
|
||||||
|
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
|
||||||
|
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
|
||||||
|
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
|
||||||
|
echo "program_hw_devices [current_hw_device]" >> program.tcl
|
||||||
|
echo "exit" >> program.tcl
|
||||||
|
vivado -nojournal -nolog -mode batch -source program.tcl
|
||||||
|
|
||||||
|
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
|
||||||
|
echo "write_cfgmem -force -format mcs -size 16 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
|
||||||
|
echo "exit" >> generate_mcs.tcl
|
||||||
|
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
|
||||||
|
mkdir -p rev
|
||||||
|
COUNT=100; \
|
||||||
|
while [ -e rev/$*_rev$$COUNT.bit ]; \
|
||||||
|
do COUNT=$$((COUNT+1)); done; \
|
||||||
|
COUNT=$$((COUNT-1)); \
|
||||||
|
for x in .mcs .prm; \
|
||||||
|
do cp $*$$x rev/$*_rev$$COUNT$$x; \
|
||||||
|
echo "Output: rev/$*_rev$$COUNT$$x"; done;
|
||||||
132
rtl/sampler/tests/sampler_main_tb.sv
Normal file
132
rtl/sampler/tests/sampler_main_tb.sv
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module sampler_tb;
|
||||||
|
|
||||||
|
parameter DATA_WIDTH = 12;
|
||||||
|
parameter PROCESS_MODE = 0;
|
||||||
|
parameter CLK_PERIOD = 15.3846;
|
||||||
|
parameter TEST_NUM = 1000;
|
||||||
|
|
||||||
|
logic clk;
|
||||||
|
logic rst;
|
||||||
|
|
||||||
|
logic [DATA_WIDTH-1:0] data_in;
|
||||||
|
logic out_of_range;
|
||||||
|
|
||||||
|
logic [DATA_WIDTH-1:0] m_axis_tdata;
|
||||||
|
logic m_axis_tvalid;
|
||||||
|
|
||||||
|
integer errors = 0;
|
||||||
|
|
||||||
|
sampler #(
|
||||||
|
.DATA_WIDTH(DATA_WIDTH),
|
||||||
|
.PROCESS_MODE(PROCESS_MODE)
|
||||||
|
) dut (
|
||||||
|
.clk_in(clk),
|
||||||
|
.rst(rst),
|
||||||
|
.data_in(data_in),
|
||||||
|
.out_of_range(out_of_range),
|
||||||
|
.m_axis_tdata(m_axis_tdata),
|
||||||
|
.m_axis_tvalid(m_axis_tvalid)
|
||||||
|
);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 0;
|
||||||
|
forever #(CLK_PERIOD/2) clk = ~clk;
|
||||||
|
end
|
||||||
|
|
||||||
|
function automatic [DATA_WIDTH-1:0] ref_convert(input [DATA_WIDTH-1:0] din);
|
||||||
|
if (PROCESS_MODE == 0)
|
||||||
|
return din;
|
||||||
|
else if (din == {1'b1, {(DATA_WIDTH-1){1'b0}}})
|
||||||
|
return din;
|
||||||
|
else
|
||||||
|
return din[DATA_WIDTH-1] ?
|
||||||
|
{1'b1, (~din[DATA_WIDTH-2:0] + 1'b1)} :
|
||||||
|
din;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
task send(input [DATA_WIDTH-1:0] word, input bit oor);
|
||||||
|
@(posedge clk);
|
||||||
|
data_in <= word;
|
||||||
|
out_of_range <= oor;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
logic [DATA_WIDTH-1:0] exp_d0, exp_d1, exp_d2;
|
||||||
|
logic oor_d0, oor_d1, oor_d2;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$display("\n=== RANDOM SAMPLER TEST===\n");
|
||||||
|
|
||||||
|
rst = 1;
|
||||||
|
data_in = 0;
|
||||||
|
out_of_range = 0;
|
||||||
|
|
||||||
|
exp_d0 = 0;
|
||||||
|
exp_d1 = 0;
|
||||||
|
exp_d2 = 0;
|
||||||
|
|
||||||
|
oor_d0 = 1;
|
||||||
|
oor_d1 = 1;
|
||||||
|
oor_d2 = 1;
|
||||||
|
|
||||||
|
repeat(5) @(posedge clk);
|
||||||
|
rst = 0;
|
||||||
|
repeat(2) @(posedge clk);
|
||||||
|
|
||||||
|
repeat (TEST_NUM) begin
|
||||||
|
logic [DATA_WIDTH-1:0] rand_data;
|
||||||
|
bit rand_oor;
|
||||||
|
|
||||||
|
rand_data = $urandom_range(0, (1 << DATA_WIDTH) - 1);
|
||||||
|
rand_oor = ($urandom_range(0, 99) < 20);
|
||||||
|
|
||||||
|
@(negedge clk);
|
||||||
|
|
||||||
|
if (!oor_d2) begin
|
||||||
|
if (m_axis_tvalid !== 1) begin
|
||||||
|
$display("ERROR: valid=0");
|
||||||
|
errors++;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (m_axis_tdata !== exp_d2) begin
|
||||||
|
$display("ERROR: data mismatch");
|
||||||
|
$display(" expected = %h", exp_d2);
|
||||||
|
$display(" got = %h", m_axis_tdata);
|
||||||
|
errors++;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(rand_data, rand_oor);
|
||||||
|
|
||||||
|
exp_d2 = exp_d1;
|
||||||
|
exp_d1 = exp_d0;
|
||||||
|
exp_d0 = ref_convert(rand_data);
|
||||||
|
|
||||||
|
oor_d2 = oor_d1;
|
||||||
|
oor_d1 = oor_d0;
|
||||||
|
oor_d0 = rand_oor;
|
||||||
|
end
|
||||||
|
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
|
||||||
|
if (!oor_d2) begin
|
||||||
|
|
||||||
|
if (m_axis_tdata !== exp_d2) begin
|
||||||
|
$display("ERROR: final mismatch");
|
||||||
|
$display(" expected = %h", exp_d2);
|
||||||
|
$display(" got = %h", m_axis_tdata);
|
||||||
|
errors++;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (errors == 0)
|
||||||
|
$display("\n========== ALL PASSED ==========\n");
|
||||||
|
else
|
||||||
|
$display("\n========== FAILED: %0d errors ==========\n", errors);
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@ -1,120 +0,0 @@
|
|||||||
`timescale 1ns / 1ps
|
|
||||||
|
|
||||||
module sampler_tb;
|
|
||||||
|
|
||||||
parameter DATA_WIDTH = 12;
|
|
||||||
parameter PACK_FACTOR = 3;
|
|
||||||
parameter PROCESS_MODE = 1;
|
|
||||||
parameter CLK_PERIOD = 15.3846; // 65 MHz
|
|
||||||
|
|
||||||
logic clk;
|
|
||||||
logic rst;
|
|
||||||
logic [DATA_WIDTH-1:0] data_in;
|
|
||||||
logic out_of_range;
|
|
||||||
logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
|
|
||||||
logic m_axis_tvalid;
|
|
||||||
|
|
||||||
sampler #(
|
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.PACK_FACTOR(PACK_FACTOR),
|
|
||||||
.PROCESS_MODE(PROCESS_MODE)
|
|
||||||
) dut (
|
|
||||||
.clk_in(clk),
|
|
||||||
.rst(rst),
|
|
||||||
.data_in(data_in),
|
|
||||||
.out_of_range(out_of_range),
|
|
||||||
.m_axis_tdata(m_axis_tdata),
|
|
||||||
.m_axis_tvalid(m_axis_tvalid)
|
|
||||||
);
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
clk = 0;
|
|
||||||
forever #(CLK_PERIOD/2) clk = ~clk;
|
|
||||||
end
|
|
||||||
|
|
||||||
task send(input [DATA_WIDTH-1:0] word, input bit oor);
|
|
||||||
@(posedge clk);
|
|
||||||
data_in <= word;
|
|
||||||
out_of_range <= oor;
|
|
||||||
$display("Send: %h (%0d) OOR=%b", word, word, oor);
|
|
||||||
endtask
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
$display("\n=== SAMPLER TEST (MODE=%0d) ===\n", PROCESS_MODE);
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
rst = 1;
|
|
||||||
out_of_range = 0;
|
|
||||||
data_in = 0;
|
|
||||||
// send(12'h001, 0);
|
|
||||||
repeat(5) @(posedge clk);
|
|
||||||
rst = 0;
|
|
||||||
send(12'h001, 0);
|
|
||||||
repeat(1) @(posedge clk);
|
|
||||||
|
|
||||||
// 1. Positive
|
|
||||||
$display("\n--- Positive numbers ---");
|
|
||||||
// send(12'h001, 0);
|
|
||||||
send(12'h002, 0);
|
|
||||||
send(12'h003, 0);
|
|
||||||
|
|
||||||
send(12'h004, 0);
|
|
||||||
send(12'h005, 0);
|
|
||||||
send(12'h806, 0);
|
|
||||||
|
|
||||||
// 2. Negative
|
|
||||||
$display("\n--- Negative numbers ---");
|
|
||||||
send(12'hFFF, 0); // -1
|
|
||||||
send(12'hFFE, 0); // -2
|
|
||||||
send(12'hFFD, 0); // -3
|
|
||||||
|
|
||||||
send(12'h800, 0); // -2048
|
|
||||||
send(12'h801, 0); // -2047
|
|
||||||
send(12'h802, 0); // -2046
|
|
||||||
|
|
||||||
// 3. Boundary
|
|
||||||
$display("\n--- Boundary values ---");
|
|
||||||
send(12'h000, 0); // 0
|
|
||||||
send(12'h001, 0); // 1
|
|
||||||
send(12'h7FF, 0); // 2047 (max positive)
|
|
||||||
|
|
||||||
send(12'h7FE, 0); // 2046
|
|
||||||
send(12'h800, 0); // -2048 (min negative)
|
|
||||||
send(12'hFFF, 0); // -1
|
|
||||||
|
|
||||||
// 4. Out of range tests
|
|
||||||
$display("\n--- Out of range tests ---");
|
|
||||||
|
|
||||||
|
|
||||||
send(12'h00A, 0);
|
|
||||||
send(12'h00B, 1); //
|
|
||||||
send(12'h00C, 0);
|
|
||||||
send(12'h00D, 0);
|
|
||||||
send(12'h00E, 0);
|
|
||||||
send(12'h00F, 0);
|
|
||||||
|
|
||||||
send(12'h010, 0);
|
|
||||||
send(12'h011, 0);
|
|
||||||
send(12'h012, 1); //
|
|
||||||
|
|
||||||
send(12'h013, 0);
|
|
||||||
send(12'h014, 0);
|
|
||||||
send(12'h015, 0);
|
|
||||||
|
|
||||||
repeat(10) @(posedge clk);
|
|
||||||
$display("\n=== TEST FINISHED ===");
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
|
|
||||||
// Results
|
|
||||||
always @(posedge clk) begin
|
|
||||||
if (m_axis_tvalid) begin
|
|
||||||
$display("\n>>> PACKET RECEIVED at %0t ns:", $time);
|
|
||||||
$display(" Full: %h", m_axis_tdata);
|
|
||||||
$display(" Word0: %h", m_axis_tdata[11:0]);
|
|
||||||
$display(" Word1: %h", m_axis_tdata[23:12]);
|
|
||||||
$display(" Word2: %h\n", m_axis_tdata[35:24]);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
`timescale 1ns / 1ps
|
|
||||||
|
|
||||||
module sampler_tb;
|
|
||||||
|
|
||||||
parameter DATA_WIDTH = 12;
|
|
||||||
parameter PACK_FACTOR = 3;
|
|
||||||
parameter PROCESS_MODE = 0;
|
|
||||||
|
|
||||||
parameter CLK_PERIOD = 15.3846;
|
|
||||||
|
|
||||||
logic clk;
|
|
||||||
logic rst;
|
|
||||||
logic [DATA_WIDTH-1:0] data_in;
|
|
||||||
logic out_of_range;
|
|
||||||
|
|
||||||
logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata;
|
|
||||||
logic m_axis_tvalid;
|
|
||||||
|
|
||||||
// DUT
|
|
||||||
sampler #(
|
|
||||||
.DATA_WIDTH(DATA_WIDTH),
|
|
||||||
.PACK_FACTOR(PACK_FACTOR),
|
|
||||||
.PROCESS_MODE(PROCESS_MODE)
|
|
||||||
) dut (
|
|
||||||
.clk_in(clk),
|
|
||||||
.rst(rst),
|
|
||||||
.data_in(data_in),
|
|
||||||
.out_of_range(out_of_range),
|
|
||||||
.m_axis_tdata(m_axis_tdata),
|
|
||||||
.m_axis_tvalid(m_axis_tvalid)
|
|
||||||
);
|
|
||||||
|
|
||||||
// clock
|
|
||||||
initial begin
|
|
||||||
clk = 0;
|
|
||||||
forever #(CLK_PERIOD/2) clk = ~clk;
|
|
||||||
end
|
|
||||||
|
|
||||||
integer i;
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
clk = 0;
|
|
||||||
rst = 1;
|
|
||||||
data_in = 0;
|
|
||||||
out_of_range = 0;
|
|
||||||
|
|
||||||
#20;
|
|
||||||
rst = 0;
|
|
||||||
repeat(5) @(posedge clk);
|
|
||||||
|
|
||||||
for (i = 1; i < 20; i++) begin
|
|
||||||
@(posedge clk);
|
|
||||||
|
|
||||||
data_in <= i;
|
|
||||||
end
|
|
||||||
|
|
||||||
#50;
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
|
||||||
if (m_axis_tvalid) begin
|
|
||||||
$display("TIME=%0t PACKED DATA = %h", $time, m_axis_tdata);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@ -180,6 +180,8 @@ sim: $(PROJECT).xpr gen_ip
|
|||||||
echo "update_compile_order -fileset sources_1" >> run_sim.tcl
|
echo "update_compile_order -fileset sources_1" >> run_sim.tcl
|
||||||
echo "update_compile_order -fileset sim_1" >> run_sim.tcl
|
echo "update_compile_order -fileset sim_1" >> run_sim.tcl
|
||||||
echo "launch_simulation" >> run_sim.tcl
|
echo "launch_simulation" >> run_sim.tcl
|
||||||
|
echo "run 10000 us" >> run_sim.tcl
|
||||||
|
echo "quit" >> run_sim.tcl
|
||||||
vivado -mode batch -source run_sim.tcl
|
vivado -mode batch -source run_sim.tcl
|
||||||
|
|
||||||
simclean:
|
simclean:
|
||||||
|
|||||||
Reference in New Issue
Block a user