diff --git a/dds_test.srcs/sim_1/new/sample_discriminator_test.sv b/dds_test.srcs/sim_1/new/sample_discriminator_test.sv new file mode 100644 index 0000000..27f0eb0 --- /dev/null +++ b/dds_test.srcs/sim_1/new/sample_discriminator_test.sv @@ -0,0 +1,152 @@ +`timescale 1ns / 1ps +module sample_discriminator_test(); + +logic clk = 0; +localparam CLK_RATE_HZ = 100_000_000; +always #(0.5s/CLK_RATE_HZ) clk = ~clk; + +logic reset; +logic [15:0] threshold_high, threshold_low; + +Axis_If #(.DWIDTH(32)) config_in_if(); +Axis_If #(.DWIDTH(16)) data_in_if(); +Axis_If #(.DWIDTH(16)) data_out_if(); + +assign config_in_if.data = {threshold_high, threshold_low}; + +sample_discriminator #( + .SAMPLE_WIDTH(16), + .CLOCK_WIDTH(56) +) dut_i ( + .clk, + .reset, + .data_in(data_in_if), + .data_out(data_out_if), + .config_in(config_in_if) +); + +logic [15:0] data_sent [$]; +logic [55:0] timestamps_sent [$]; +logic [15:0] data_received [$]; +logic [15:0] timestamps_received [$]; + +logic [55:0] sample_count; +logic [15:0] data_in_d; +logic data_in_valid_d; +logic is_high, is_high_d; +logic new_is_high; + +assign new_is_high = is_high & (!is_high_d); + +logic [15:0] data_range_low, data_range_high; + +always @(posedge clk) begin + if (reset) begin + sample_count <= '0; + data_in_if.data <= '0; + is_high_d <= '0; + is_high <= '0; + end else begin + data_in_valid_d <= data_in_if.valid; + if (data_in_if.valid && data_in_if.ready) begin + data_in_d <= data_in_if.data; + is_high_d <= is_high; + if (data_in_if.data > threshold_high) begin + is_high <= 1'b1; + end + sample_count <= sample_count + 1'b1; + data_in_if.data <= $urandom_range(data_range_low, data_range_high); + end + if (data_in_valid_d) begin + if (is_high) begin + data_sent.push_front(data_in_d & 16'hfff8); + end + if (new_is_high) begin + timestamps_sent.push_front(sample_count); + end + end + if (data_out_if.valid && data_out_if.ready) begin + if (data_out_if.data[1]) begin + // timestamp + timestamps_received.push_front(data_out_if.data & 16'hfffc); + end else begin + // data + data_received.push_front(data_out_if.data & 16'hfff8); + end + end + end +end + +// not 100% activity on output +always @(posedge clk) begin + data_out_if.ready <= $urandom_range(0,1); +end + +task send_samples(input int n_samples); + repeat (n_samples) begin + data_in_if.valid <= 1'b1; + @(posedge clk); + data_in_if.valid <= 1'b0; + repeat (4) @(posedge clk); + end +endtask + +task check_results(); + logic [55:0] tstamp_temp; + if (data_sent.size() != data_received.size()) begin + $display("mismatch in amount of sent/received data"); + $display("data_sent.size() = %0d", data_sent.size()); + $display("data_received.size() = %0d", data_received.size()); + for (int i = 0; i < data_sent.size(); i++) begin + $display("data_sent[%0d] = %x", i, data_sent[i]); + end + for (int i = 0; i < data_received.size(); i++) begin + $display("data_received[%0d] = %x", i, data_received[i]); + end + end + if (timestamps_sent.size()*4 != timestamps_received.size()) begin + $display("mismatch in amount of sent/received timestamps"); + $display("timestamps_sent.size() = %0d", timestamps_sent.size()); + $display("timestamps_received.size() = %0d", timestamps_received.size()); + for (int i = 0; i < timestamps_sent.size(); i++) begin + $display("timestamps_sent[%0d] = %x", i, timestamps_sent[i]); + end + for (int i = 0; i < timestamps_received.size(); i++) begin + $display("timestamps_received[%0d] = %x", i, timestamps_received[i]); + end + end + while (data_sent.size() > 0 && data_received.size() > 0) begin + if (data_sent[$] != data_received[$]) begin + $display("data mismatch error (received %x, sent %x)", data_received[$], data_sent[$]); + end + data_sent.pop_back(); + data_received.pop_back(); + end + while (timestamps_sent.size() > 0 && timestamps_received.size() > 0) begin + for (int i = 0; i < 4; i++) begin + tstamp_temp[i*14+:14] = timestamps_received.pop_back() >> 2; + end + if (timestamps_sent[$] != tstamp_temp) begin + $display("timestamp mismatch error (received %x, sent %x)", tstamp_temp, timestamps_sent[$]); + end + timestamps_sent.pop_back(); + end +endtask + +initial begin + reset <= 1'b1; + data_range_low <= 16'h0000; + data_range_high <= 16'hffff; + threshold_low <= '0; + threshold_high <= '0; + data_in_if.valid <= '0; + repeat (100) @(posedge clk); + reset <= 1'b0; + repeat (50) @(posedge clk); + send_samples(50); + repeat (50) @(posedge clk); + check_results(); + $finish; +end + +endmodule diff --git a/dds_test.srcs/sources_1/new/sample_discriminator.sv b/dds_test.srcs/sources_1/new/sample_discriminator.sv new file mode 100644 index 0000000..491f233 --- /dev/null +++ b/dds_test.srcs/sources_1/new/sample_discriminator.sv @@ -0,0 +1,125 @@ +// sample discriminator +// If input sample is above high threshold (with hysteresis), it is passed through, +// otherwise it is dropped. If the preceeding sample was below the low threshold, +// then a timestamp is also sent out after the current sample (indicated by +// setting some flag bits) +// data format (LSB 1'bx is channel index) +// sample: {sample[15:3], 1'bx, 1'b0, 1'bx} bit 2 1'bx is new_is_high +// timestamp (4 successive transactions): {clock[i*14+:14], 1'b1, 1'bx} for i = 0..3 +module sample_discriminator #( + parameter int SAMPLE_WIDTH = 16, + parameter int CLOCK_WIDTH = 56 // rolls over roughly every 10 years +) ( + input wire clk, reset, + Axis_If.Slave_Simple data_in, + Axis_If.Master_Simple data_out, + Axis_If.Slave_Simple config_in // {threshold_high, threshold_low} +); + +assign config_in.ready = 1'b1; +assign data_in.ready = 1'b1; // always process new samples; we'll just throw them away later if we don't need them + +logic [SAMPLE_WIDTH-1:0] threshold_low, threshold_high; +logic [SAMPLE_WIDTH-1:0] data_in_reg; +logic data_in_valid; +logic [CLOCK_WIDTH-1:0] sample_count; + +// update thresholds from config interface +always_ff @(posedge clk) begin + if (reset) begin + threshold_low <= '0; + threshold_high <= '0; + end else begin + if (config_in.valid) begin + threshold_high <= config_in.data[2*SAMPLE_WIDTH-1:SAMPLE_WIDTH]; + threshold_low <= config_in.data[SAMPLE_WIDTH-1:0]; + end + end +end + +logic is_high, is_high_d; +logic new_is_high; +assign new_is_high = is_high & (!is_high_d); + +always_ff @(posedge clk) begin + if (reset) begin + is_high <= '0; + is_high_d <= '0; + sample_count <= '0; + end else begin + data_in_valid <= data_in.valid; + if (data_in.valid) begin + data_in_reg <= data_in.data; + is_high_d <= is_high; + sample_count <= sample_count + 1'b1; + if (data_in.data > threshold_high) begin + is_high <= 1'b1; + end else if (data_in.data < threshold_low) begin + is_high <= 1'b0; + end + end + end +end + +Axis_If #(.DWIDTH(SAMPLE_WIDTH+CLOCK_WIDTH)) input_fifo_in (); +Axis_If #(.DWIDTH(SAMPLE_WIDTH+CLOCK_WIDTH)) input_fifo_out (); +Axis_If #(.DWIDTH(SAMPLE_WIDTH)) output_fifo_in (); + +fifo #( + .DATA_WIDTH(SAMPLE_WIDTH+CLOCK_WIDTH), + .ADDR_WIDTH(4) // add some buffer in case we get several samples with alternating noise in a row +) input_fifo_i ( + .clk, + .reset, + .data_out(input_fifo_out), + .data_in(input_fifo_in) +); + +localparam int SPLIT_TIMESTAMP_COUNT = CLOCK_WIDTH / (SAMPLE_WIDTH - 2); +logic [$clog2(SPLIT_TIMESTAMP_COUNT+1)-1:0] subword_sel; + +assign input_fifo_in.data = {sample_count, data_in_reg[SAMPLE_WIDTH-1:2], new_is_high, data_in_reg[0]}; +assign input_fifo_in.valid = data_in_valid & is_high; +assign input_fifo_out.ready = ((!input_fifo_out.data[1]) || (subword_sel == SPLIT_TIMESTAMP_COUNT)) + && input_fifo_out.valid; +assign output_fifo_in.valid = input_fifo_out.valid; + +always_comb begin + if (subword_sel == 0) begin + // set bit 1 to 0 to indicate the word contains a sample + // sample: {sample[15:3], 1'bx, 1'b0, 1'bx} bit 2 1'bx is new_is_high + output_fifo_in.data = {input_fifo_out.data[SAMPLE_WIDTH-1:3], + input_fifo_out.data[1], 1'b0, input_fifo_out.data[0]}; + end else begin + // set bit 1 to 1 to indicate the word contains a timestamp + // timestamp (4 successive transactions): {clock[i*14+:14], 1'b1, 1'bx} for i = 0..3 + output_fifo_in.data = {input_fifo_out.data[2+subword_sel*(SAMPLE_WIDTH-2)+:(SAMPLE_WIDTH-2)], + 1'b1, input_fifo_out.data[0]}; + end +end + +always_ff @(posedge clk) begin + if (reset) begin + subword_sel <= '0; + end else begin + if (input_fifo_out.valid && input_fifo_out.data[1]) begin + if (subword_sel == SPLIT_TIMESTAMP_COUNT) begin + subword_sel <= '0; + end else begin + subword_sel <= subword_sel + 1'b1; + end + end + end +end + +fifo #( + .DATA_WIDTH(SAMPLE_WIDTH), + .ADDR_WIDTH(4) +) output_fifo_i ( + .clk, + .reset, + .data_out(data_out), + .data_in(output_fifo_in) +); + +endmodule diff --git a/sample_discriminator_test_behav.wcfg b/sample_discriminator_test_behav.wcfg new file mode 100644 index 0000000..3eda6c4 --- /dev/null +++ b/sample_discriminator_test_behav.wcfg @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + clk + clk + + + reset + reset + + + threshold_high[15:0] + threshold_high[15:0] + + + threshold_low[15:0] + threshold_low[15:0] + + + sample_count[55:0] + sample_count[55:0] + + + is_high + is_high + + + is_high_d + is_high_d + + + new_is_high + new_is_high + + + data_range_low[15:0] + data_range_low[15:0] + + + data_range_high[15:0] + data_range_high[15:0] + + + CLK_RATE_HZ[31:0] + CLK_RATE_HZ[31:0] + + + dut + label + + + clk + clk + + + reset + reset + + + threshold_low[15:0] + threshold_low[15:0] + + + threshold_high[15:0] + threshold_high[15:0] + + + data_in_reg[15:0] + data_in_reg[15:0] + STYLE_ANALOG + 100 + ANALOG_YRANGETYPE_AUTO + 0.000000 + 0.000000 + ANALOG_INTERPOLATION_HOLD + ANALOG_OFFSCALE_HIDE + 0.000000 + + + data_in_valid + data_in_valid + + + sample_count[55:0] + sample_count[55:0] + + + is_high + is_high + + + is_high_d + is_high_d + + + new_is_high + new_is_high + + + subword_sel[2:0] + subword_sel[2:0] + + + SAMPLE_WIDTH[31:0] + SAMPLE_WIDTH[31:0] + + + CLOCK_WIDTH[31:0] + CLOCK_WIDTH[31:0] + + + SPLIT_TIMESTAMP_COUNT[31:0] + SPLIT_TIMESTAMP_COUNT[31:0] + + + data_out_if + label + + + data[15:0] + data[15:0] + + + ready + ready + + + valid + valid + + + last + last + + + DWIDTH[31:0] + DWIDTH[31:0] + + + input_fifo + label + + + clk + clk + + + reset + reset + + + buffer[0:15][71:0] + buffer[0:15][71:0] + + + + read_addr[4:0] + read_addr[4:0] + + + write_addr[4:0] + write_addr[4:0] + + + full + full + + + empty + empty + + + lsbs_equal + lsbs_equal + + + output_fifo + label + + + clk + clk + + + reset + reset + + + buffer[0:15][15:0] + buffer[0:15][15:0] + + + read_addr[4:0] + read_addr[4:0] + + + write_addr[4:0] + write_addr[4:0] + + + full + full + + + empty + empty + + + lsbs_equal + lsbs_equal + + + output_fifo_in_if + label + + + data[15:0] + data[15:0] + + + ready + ready + + + valid + valid + + + input_fifo_out_if + label + + + data[71:0] + data[71:0] + + + ready + ready + + + valid + valid + + + input_fifo_in_if + label + + + data[71:0] + data[71:0] + + + ready + ready + + + valid + valid + + diff --git a/src/rtl/sample_discriminator.sv b/src/rtl/sample_discriminator.sv new file mode 120000 index 0000000..ae4ed12 --- /dev/null +++ b/src/rtl/sample_discriminator.sv @@ -0,0 +1 @@ +../../dds_test.srcs/sources_1/new/sample_discriminator.sv \ No newline at end of file diff --git a/src/verif/sample_discriminator_test.sv b/src/verif/sample_discriminator_test.sv new file mode 120000 index 0000000..19dc364 --- /dev/null +++ b/src/verif/sample_discriminator_test.sv @@ -0,0 +1 @@ +../../dds_test.srcs/sim_1/new/sample_discriminator_test.sv \ No newline at end of file