fpga: rfnoc: add TS-on-first-only option to axi_rate_change_ms

Add a parameter to the axi_rate_change_ms module called
EN_TIME_ALL_PKTS.
Set to 1, the module will calculate timestamps
for all packets of the output burst.
Set to 0, the module will only insert a timestamp in the first
pkt of each generated output burst.
This commit is contained in:
Niels Garibaldi 2026-08-06 13:56:10 +02:00 committed by mkoop-ni
parent 45ea2365b1
commit d91c2293d1
3 changed files with 73 additions and 24 deletions

View File

@ -18,17 +18,20 @@
// internal state machine will terminate each burst even if the tlast does not
// make it through the user logic due to input data not matching the decimation
// rate.
// Generates timestamps on each packet of the burst or none depending on the
// first received packet of the burst.
// Generates timestamps on each packet of the burst or only the first packet,
// depending on EN_TIME_ALL_PKTS, when the burst is timed.
//
// Parameters:
//
// WIDTH : Data bus width in bits (must match user logic).
// SPC : Samples per clock cycle (must match user logic).
// Width of sample stream tags.
// SPC_MTU_LOG2 : Log2 of the maximum packet size in internal
// words(SPC*WIDTH wide). Internal counter widths
// are derived from this value.
// WIDTH : Data bus width in bits (must match user logic).
// SPC : Samples per clock cycle (must match user logic).
// Width of sample stream tags.
// SPC_MTU_LOG2 : Log2 of the maximum packet size in internal
// words(SPC*WIDTH wide). Internal counter widths
// are derived from this value.
// EN_TIME_ALL_PKTS : When set to 1, generate timestamps for all packets in
// a timed burst. When 0, generate a timestamp only for
// the first packet in a timed burst.
// MAX_USER_LOGIC_LATENCY : Maximum latency of the user logic in cycles for the transfer from
// m_axis_data_tlast to s_axis_data_tlast assuming that s_axis_data_tready is
// always high.
@ -41,6 +44,7 @@ module axi_rate_change_ms
int WIDTH = 32,
int SPC = 1,
int SPC_MTU_LOG2 = 10,
bit EN_TIME_ALL_PKTS = 1'b1,
int MAX_USER_LOGIC_LATENCY = 85 + 5*SPC // current value for DDC Multisample user logic
)(
input wire logic clk,
@ -247,6 +251,7 @@ module axi_rate_change_ms
logic first_pkt_timestamp_valid;
logic burst_has_time = '0;
logic first_burst_transfer = '1;
logic first_output_packet = '1;
// determine first packet in burst
always_ff @(posedge clk) begin
@ -288,6 +293,16 @@ module axi_rate_change_ms
end
end
// Track the first output packet boundary in the burst. The state is
// consumed only when the packet boundary is accepted by the NoC shell.
always_ff @(posedge clk) begin
if (rst || clear) begin
first_output_packet <= 1'b1;
end else if (o_tvalid && o_tready && o_tlast) begin
first_output_packet <= o_teob;
end
end
// update outgoing timestamp
// always updated at the beginning of the burst through input state machine
logic [CHDR_TIMESTAMP_W-1:0] timestamp_out = '0;
@ -360,7 +375,8 @@ module axi_rate_change_ms
// insert generated signals
assign o_tlast = end_of_packet | o_teob | terminate_burst;
assign o_ttimestamp = timestamp_out;
assign o_thas_time = o_tlast && burst_has_time; //only valid on tlast
assign o_thas_time = o_tlast && burst_has_time &&
(EN_TIME_ALL_PKTS || first_output_packet);
endmodule

View File

@ -43,6 +43,14 @@ module axi_rate_change_ms_all_tb;
.MAX_M(1)
) tb_4 ();
// First-packet-only timestamp mode
axi_rate_change_ms_tb #(
.WIDTH(32),
.MAX_N(1),
.MAX_M(1),
.EN_TIME_ALL_PKTS(1'b0)
) tb_5 ();
endmodule : axi_rate_change_ms_all_tb
`default_nettype wire

View File

@ -17,7 +17,8 @@
module axi_rate_change_ms_tb #(
parameter int WIDTH = 32,
parameter int MAX_N = 16,
parameter int MAX_M = 16
parameter int MAX_M = 16,
parameter bit EN_TIME_ALL_PKTS = 1'b1
);
`include "test_exec.svh"
@ -130,7 +131,8 @@ module axi_rate_change_ms_tb #(
axi_rate_change_ms #(
.WIDTH(WIDTH),
.SPC(SPC),
.SPC_MTU_LOG2(SPC_MTU_LOG2)
.SPC_MTU_LOG2(SPC_MTU_LOG2),
.EN_TIME_ALL_PKTS(EN_TIME_ALL_PKTS)
) dut (
.clk(clk),
.rst(rst),
@ -402,7 +404,7 @@ module axi_rate_change_ms_tb #(
always_ff @(posedge clk) begin
if (clock_cnt_en) begin
if (noc_out.tvalid && !clock_cnt_start) begin
if (noc_out.tvalid && noc_out.tready && !clock_cnt_start) begin
clock_cnt_start <= 1'b1;
clock_cnt <= clock_cnt + 1;
end else if (clock_cnt_start) begin
@ -675,8 +677,10 @@ module axi_rate_change_ms_tb #(
int last_idx = actual_pkt_size - 1;
user_t actual_user = user_t'(actual_packet.user[last_idx]);
logic [CHDR_TIMESTAMP_W-1:0] expected_last_timestamp;
bit expected_has_time;
expected_last_timestamp = expected_timestamp;
expected_has_time = EN_TIME_ALL_PKTS || (pkt_idx == 0);
`ASSERT_ERROR(
actual_user.eob == is_last_packet,
@ -685,17 +689,19 @@ module axi_rate_change_ms_tb #(
pkt_idx, actual_user.eob, is_last_packet)
)
`ASSERT_ERROR(
actual_user.has_time == 1'b1,
actual_user.has_time == expected_has_time,
$sformatf({"random_spp pkt %0d:",
" Last-word HAS_TIME mismatch. Actual=%0d Expected=1"},
pkt_idx, actual_user.has_time)
)
`ASSERT_ERROR(
actual_user.timestamp == expected_last_timestamp,
$sformatf({"random_spp pkt %0d:",
" Last-word timestamp mismatch. Actual=%0d Expected=%0d"},
pkt_idx, actual_user.timestamp, expected_last_timestamp)
" Last-word HAS_TIME mismatch. Actual=%0d Expected=%0d"},
pkt_idx, actual_user.has_time, expected_has_time)
)
if (expected_has_time) begin
`ASSERT_ERROR(
actual_user.timestamp == expected_last_timestamp,
$sformatf({"random_spp pkt %0d:",
" Last-word timestamp mismatch. Actual=%0d Expected=%0d"},
pkt_idx, actual_user.timestamp, expected_last_timestamp)
)
end
end
// Advance running timestamp for the next packet
@ -830,6 +836,7 @@ module axi_rate_change_ms_tb #(
axis_pkt_t packet = new();
int words_in_packet;
bit is_last_output_packet;
bit has_time;
if (words_left_to_recv >= spp) begin
words_in_packet = spp;
@ -837,6 +844,7 @@ module axi_rate_change_ms_tb #(
words_in_packet = words_left_to_recv;
end
is_last_output_packet = (words_left_to_recv <= spp);
has_time = EN_TIME_ALL_PKTS || (expected_packets.size() == 0);
for (int i = 0; i < words_in_packet; i++) begin
user_t user_word;
@ -853,7 +861,7 @@ module axi_rate_change_ms_tb #(
user_word = pack_user(
16'd0,
expected_timestamp,
1'b1,
has_time,
is_last_output_packet
);
end
@ -1007,8 +1015,9 @@ module axi_rate_change_ms_tb #(
string tb_name;
tb_name = $sformatf(
"AXI Rate Change CHDR Testbench (WIDTH=%0d, MAX_N=%0d, MAX_M=%0d)",
WIDTH, MAX_N, MAX_M);
{"AXI Rate Change CHDR Testbench (WIDTH=%0d, MAX_N=%0d, MAX_M=%0d, ",
"EN_TIME_ALL_PKTS=%0d)"},
WIDTH, MAX_N, MAX_M, EN_TIME_ALL_PKTS);
test.start_tb(tb_name, 1s);
//`ASSERT_ERROR(
@ -1070,6 +1079,22 @@ module axi_rate_change_ms_tb #(
test_rate(1, 1, DEFAULT_SPP * 4, DEFAULT_SPP * 4, 1'b0, 1'b0, 0);
test.end_test();
if (!EN_TIME_ALL_PKTS) begin
test.start_test("Test First Packet Timestamp Only");
repeat (2) begin
test_rate(
.n(1),
.m(1),
.num_words(DEFAULT_SPP * 3),
.spp(DEFAULT_SPP),
.rand_delay_in(1'b1),
.rand_delay_out(1'b1),
.time_incr(3)
);
end
test.end_test();
end
if (MAX_M >= 2) begin
test.start_test("Test Interpolation Early Sideband Metadata");
test_rate(