From 17b1829f08500d779559334d37bc625103b435af Mon Sep 17 00:00:00 2001 From: Reed Foster Date: Sun, 8 Oct 2023 21:58:08 -0400 Subject: [PATCH] working on new scalable buffer architecture for flexible multichannel capture; finished basic unit --- buffer_bank_test_behav.wcfg | 191 ++++++++++++++++++ .../sim_1/new/banked_sample_buffer_test.sv | 173 ++++++++++++++++ dds_test.srcs/sources_1/new/axis.sv | 11 +- .../sources_1/new/banked_sample_buffer.sv | 147 ++++++++++++++ src/rtl/banked_sample_buffer.sv | 1 + src/verif/banked_sample_buffer_test.sv | 1 + 6 files changed, 519 insertions(+), 5 deletions(-) create mode 100644 buffer_bank_test_behav.wcfg create mode 100644 dds_test.srcs/sim_1/new/banked_sample_buffer_test.sv create mode 100644 dds_test.srcs/sources_1/new/banked_sample_buffer.sv create mode 120000 src/rtl/banked_sample_buffer.sv create mode 120000 src/verif/banked_sample_buffer_test.sv diff --git a/buffer_bank_test_behav.wcfg b/buffer_bank_test_behav.wcfg new file mode 100644 index 0000000..f357e81 --- /dev/null +++ b/buffer_bank_test_behav.wcfg @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + clk + clk + + + reset + reset + + + start + start + + + stop + stop + + + full + full + + + sample_count[31:0] + sample_count[31:0] + + + CLK_RATE_HZ[31:0] + CLK_RATE_HZ[31:0] + + + dut + label + + + clk + clk + + + reset + reset + + + start + start + + + stop + stop + + + full + full + + + state[31:0] + state[31:0] + + + buffer[0:1023][31:0] + buffer[0:1023][31:0] + + + write_addr[9:0] + write_addr[9:0] + + + read_addr[9:0] + read_addr[9:0] + + + read_stop_addr[9:0] + read_stop_addr[9:0] + + + read_addr_d[2:0][9:0] + read_addr_d[2:0][9:0] + + + + data_out_d[3:0][31:0] + data_out_d[3:0][31:0] + + + + data_out_valid[3:0] + data_out_valid[3:0] + + + buffer_has_data + buffer_has_data + + + data_out_last + data_out_last + + + BUFFER_DEPTH[31:0] + BUFFER_DEPTH[31:0] + + + PARALLEL_SAMPLES[31:0] + PARALLEL_SAMPLES[31:0] + + + SAMPLE_WIDTH[31:0] + SAMPLE_WIDTH[31:0] + + + data_in + label + + + data[0:0][15:0] + data[0:0][15:0] + + + ready[0:0] + ready[0:0] + + + + valid[0:0] + valid[0:0] + + + + last[0:0] + last[0:0] + + + DWIDTH[31:0] + DWIDTH[31:0] + + + PARALLEL_CHANNELS[31:0] + PARALLEL_CHANNELS[31:0] + + + data_out + label + + + data[0:0][15:0] + data[0:0][15:0] + + + + ready[0:0] + ready[0:0] + + + + valid[0:0] + valid[0:0] + + + + last[0:0] + last[0:0] + + + + DWIDTH[31:0] + DWIDTH[31:0] + + + PARALLEL_CHANNELS[31:0] + PARALLEL_CHANNELS[31:0] + + diff --git a/dds_test.srcs/sim_1/new/banked_sample_buffer_test.sv b/dds_test.srcs/sim_1/new/banked_sample_buffer_test.sv new file mode 100644 index 0000000..31b520e --- /dev/null +++ b/dds_test.srcs/sim_1/new/banked_sample_buffer_test.sv @@ -0,0 +1,173 @@ +`timescale 1ns / 1ps +module buffer_bank_test (); + +logic clk = 0; +localparam CLK_RATE_HZ = 100_000_000; +always #(0.5s/CLK_RATE_HZ) clk = ~clk; + +logic reset; + +logic start, stop; +logic full; + +Axis_If #(.DWIDTH(16), .PARALLEL_CHANNELS(1)) data_in (); +Axis_If #(.DWIDTH(16), .PARALLEL_CHANNELS(1)) data_out (); + +buffer_bank #( + .BUFFER_DEPTH(1024), + .PARALLEL_SAMPLES(2), + .SAMPLE_WIDTH(16) +) dut_i ( + .clk, + .reset, + .data_in, + .data_out, + .start, + .stop, + .full +); + +int sample_count; +logic [15:0] data_sent [$]; +logic [15:0] data_received [$]; + +always @(posedge clk) begin + if (reset) begin + sample_count <= 0; + data_in.data <= '0; + end else begin + // send data + if (data_in.valid && data_in.ready) begin + sample_count <= sample_count + 1; + data_in.data <= $urandom_range(1<<16); + end + // save data that was sent + if (data_in.valid) begin + data_sent.push_front(data_in.data); + end + if (data_out.valid && data_out.ready) begin + data_received.push_front(data_out.data); + end + end +end + +task send_samples(input int n_samples, input int delay); + repeat (n_samples) begin + data_in.valid <= 1'b1; + @(posedge clk); + data_in.valid <= 1'b0; + repeat (delay) @(posedge clk); + end +endtask + +task do_readout(input bit wait_for_last); + data_out.ready <= 1'b0; + stop <= 1'b1; + @(posedge clk); + stop <= 1'b0; + repeat (500) @(posedge clk); + data_out.ready <= 1'b1; + repeat ($urandom_range(2,4)) @(posedge clk); + data_out.ready <= 1'b0; + repeat ($urandom_range(1,3)) @(posedge clk); + data_out.ready <= 1'b1; + if (wait_for_last) begin + while (!data_out.last) @(posedge clk); + end else begin + repeat (500) @(posedge clk); + end + @(posedge clk); +endtask + +task check_results(); + $display("data_sent.size() = %0d", data_sent.size()); + $display("data_received.size() = %0d", data_received.size()); + if ((data_sent.size() + 1) != data_received.size()) begin + $warning("mismatch in amount of sent/received data"); + end + if (data_received[$] != data_sent.size()) begin + $warning("incorrect sample count reported by buffer"); + end + data_received.pop_back(); // remove sample count + while (data_sent.size() > 0 && data_received.size() > 0) begin + // data from channel 0 can be reordered with data from channel 2 + if (data_sent[$] != data_received[$]) begin + $warning("data mismatch error (received %x, sent %x)", data_received[$], data_sent[$]); + end + data_sent.pop_back(); + data_received.pop_back(); + end +endtask + +initial begin + reset <= 1'b1; + start <= 1'b0; + stop <= 1'b0; + data_in.valid <= '0; + repeat (100) @(posedge clk); + reset <= 1'b0; + repeat (50) @(posedge clk); + // start + start <= 1'b1; + @(posedge clk); + start <= 1'b0; + repeat (100) @(posedge clk); + // send samples + send_samples(128, 3); + repeat (50) @(posedge clk); + do_readout(1'b1); + $display("######################################################"); + $display("# checking results for test with a few samples #"); + $display("######################################################"); + check_results(); + // do more tests + + // test with one sample + // start + start <= 1'b1; + @(posedge clk); + start <= 1'b0; + repeat (100) @(posedge clk); + // send samples + send_samples(1, 4); + repeat (50) @(posedge clk); + do_readout(1'b0); // don't wait for last signal + $display("######################################################"); + $display("# checking results for test with one sample #"); + $display("######################################################"); + check_results(); + + // test with no samples + // start + start <= 1'b1; + @(posedge clk); + start <= 1'b0; + repeat (100) @(posedge clk); + // don't send samples + repeat (50) @(posedge clk); + do_readout(1'b0); // don't wait for last signal + $display("######################################################"); + $display("# checking results for test with no samples #"); + $display("######################################################"); + check_results(); + + // fill up buffer + // start + start <= 1'b1; + @(posedge clk); + start <= 1'b0; + repeat (100) @(posedge clk); + // send samples + send_samples(1024, 1); + repeat (50) @(posedge clk); + do_readout(1'b1); + $display("######################################################"); + $display("# checking results for test with 1024 samples #"); + $display("######################################################"); + check_results(); + repeat (500) @(posedge clk); + $finish; + +end + +endmodule diff --git a/dds_test.srcs/sources_1/new/axis.sv b/dds_test.srcs/sources_1/new/axis.sv index 47685da..717bf43 100644 --- a/dds_test.srcs/sources_1/new/axis.sv +++ b/dds_test.srcs/sources_1/new/axis.sv @@ -1,12 +1,13 @@ // axi-stream interface interface Axis_If #( - parameter DWIDTH = 32 + parameter DWIDTH = 32, + parameter PARALLEL_CHANNELS = 1 ); -logic [DWIDTH - 1:0] data; -logic ready; -logic valid; -logic last; +logic [PARALLEL_CHANNELS-1:0][DWIDTH - 1:0] data; +logic [PARALLEL_CHANNELS-1:0] ready; +logic [PARALLEL_CHANNELS-1:0] valid; +logic [PARALLEL_CHANNELS-1:0] last; modport Master_Full ( input ready, diff --git a/dds_test.srcs/sources_1/new/banked_sample_buffer.sv b/dds_test.srcs/sources_1/new/banked_sample_buffer.sv new file mode 100644 index 0000000..7518f6c --- /dev/null +++ b/dds_test.srcs/sources_1/new/banked_sample_buffer.sv @@ -0,0 +1,147 @@ +// banked sample buffer +module banked_sample_buffer #( + parameter int N_CHANNELS = 8, // number of ADC channels + parameter int BUFFER_DEPTH = 8192 // maximum capacity across all channels +) ( + input wire clk, reset, + Axis_If.Slave_Simple data_in, // all channels in parallel + Axis_If.Master_Full data_out, + Axis_If.Slave_Simple config_in // {banking_mode, start, stop} +); +endmodule + +module buffer_bank #( + parameter int BUFFER_DEPTH = 1024, + parameter int PARALLEL_SAMPLES = 16, // 4.096 GS/s @ 256 MHz + parameter int SAMPLE_WIDTH = 16 // 12-bit ADC +) ( + input wire clk, reset, + Axis_If.Slave_Simple data_in, // one channel + Axis_If.Master_Full data_out, + input logic start, stop, + output logic full +); + +enum {IDLE, CAPTURE, PRETRANSFER, TRANSFER} state; +// IDLE: wait for start (either from user or from previous bank filling up in +// a high-memory, low-channel count banking mode) +// CAPTURE: save samples until full or until stop is supplied +// PRETRANSFER: output number of captured samples +// TRANSFER: output samples + +assign data_in.ready = state == CAPTURE; + +logic [PARALLEL_SAMPLES*SAMPLE_WIDTH-1:0] buffer [BUFFER_DEPTH]; +logic [$clog2(BUFFER_DEPTH)-1:0] write_addr, read_addr; +logic [1:0][$clog2(BUFFER_DEPTH)-1:0] read_addr_d; // delay so that we don't miss the last sample +logic [3:0][PARALLEL_SAMPLES*SAMPLE_WIDTH-1:0] data_out_d; +logic [3:0] data_out_valid; // extra valid to match latency of BRAM +logic [3:0] data_out_last; +logic buffer_has_data, readout_begun; +assign data_out.data = data_out_d[3]; +assign data_out.valid = data_out_valid[3]; +assign data_out.last = data_out_last[3]; + +assign full = write_addr == BUFFER_DEPTH - 1; + +// state machine +always_ff @(posedge clk) begin + if (reset) begin + state <= IDLE; + end else begin + unique case (state) + IDLE: if (start) state <= CAPTURE; + CAPTURE: if (stop || (data_in.valid && full)) state <= PRETRANSFER; + // only transition after successfully sending out number of captured samples: + PRETRANSFER: if (data_out.valid && data_out.ready) begin + if (data_out.last) begin + // no data was saved, so there's nothing to send + state <= IDLE; + end else begin + state <= TRANSFER; + end + end + TRANSFER: if (data_out.last && data_out.ready && data_out.valid) state <= IDLE; + endcase + end +end + +// sample buffer logic +always_ff @(posedge clk) begin + if (reset) begin + write_addr <= '0; + read_addr <= '0; + read_addr_d <= '0; + data_out_d <= '0; + data_out_valid <= '0; + data_out_last <= '0; + buffer_has_data <= '0; + readout_begun <= '0; + end else begin + unique case (state) + IDLE: begin + write_addr <= '0; + read_addr <= '0; + read_addr_d <= '0; + data_out_d <= '0; + data_out_valid <= '0; + data_out_last <= '0; + buffer_has_data <= '0; + readout_begun <= '0; + end + CAPTURE: begin + if (data_in.valid) begin + buffer[write_addr] <= data_in.data; + write_addr <= write_addr + 1'b1; + buffer_has_data <= 1'b1; + end + end + PRETRANSFER: begin + if (write_addr > 0) begin + data_out_d[3] <= write_addr; + end else if (buffer_has_data) begin + // if write_addr == 0 but we've written to the buffer, then it's full + data_out_d[3] <= BUFFER_DEPTH; + end else begin + data_out_d[3] <= '0; // we don't have any data to send + data_out_last[3] <= 1'b1; + end + if (data_out.ready) begin + // transaction will go through, so we should reset data_out_valid so + // that we don't accidentally send the sample count twice + data_out_valid[3] <= 1'b0; + // also reset last in case we don't have any data to send and the + // sample count is the only thing we're outputting + data_out_last[3] <= 1'b0; + end else begin + data_out_valid[3] <= 1'b1; + end + end + TRANSFER: begin + if (data_out.ready || (!data_out.valid)) begin + // in case the entire buffer was filled, we would never read anything out if we don't add + // the option to increment the address when readout hasn't been begun but the read/write + // addresses are both zero + // it is not possible to enter the TRANSFER state with an empty buffer, so we know if both are zero, + // then the buffer must be full + if ((read_addr != write_addr) || (!readout_begun)) begin + readout_begun <= 1'b1; + read_addr <= read_addr + 1'b1; + data_out_valid <= {data_out_valid[2:0], 1'b1}; + if (read_addr + 1'b1 == write_addr) begin + data_out_last <= {data_out_last[2:0], 1'b1}; + end + end else begin + // no more samples are read out + data_out_valid <= {data_out_valid[2:0], 1'b0}; + data_out_last <= {data_out_last[2:0], 1'b0}; + end + data_out_d <= {data_out_d[2:0], buffer[read_addr]}; + read_addr_d <= {read_addr_d[1:0], read_addr}; + end + end + endcase + end +end + +endmodule diff --git a/src/rtl/banked_sample_buffer.sv b/src/rtl/banked_sample_buffer.sv new file mode 120000 index 0000000..d62f05e --- /dev/null +++ b/src/rtl/banked_sample_buffer.sv @@ -0,0 +1 @@ +../../dds_test.srcs/sources_1/new/banked_sample_buffer.sv \ No newline at end of file diff --git a/src/verif/banked_sample_buffer_test.sv b/src/verif/banked_sample_buffer_test.sv new file mode 120000 index 0000000..420e205 --- /dev/null +++ b/src/verif/banked_sample_buffer_test.sv @@ -0,0 +1 @@ +../../dds_test.srcs/sim_1/new/banked_sample_buffer_test.sv \ No newline at end of file