1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
//
// Copyright 2023 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axis_pkt_throttle
//
// Description:
//
// This module takes in AXI-Stream and outputs the same stream, inserting
// gaps between packets in order to maintain a specific data rate. The amount
// of time between packets is controlled in such a way that the length of a
// packet divided by the time between the start of that packet and the next
// does not exceed some rate R.
//
// This module does NOT insert stalls within a packet, only between packets,
// so the peak data rate is not restricted and packet contiguity is not
// affected. Also, the average data rate could be slightly higher than the
// configured rate due to rounding error (within a single clock cycle per
// packet).
//
// The "throttle" input port controls the rate. In order to set the rate to
// R, where R is a fraction in the range (0,1], set throttle (T) using the
// formula T = (1/R)-1. In other words, R = 1/(T+1).
//
// The "throttle" input is represented as an unsigned fixed-point value with
// THROTTLE_W/2 whole bits and THROTTLE_W/2 fractional bits (UQn.n format).
// Throttle is therefore in the range [0,1).
//
// For example, if THROTTLE_W is 8 bits then throttle is in UQ4.4 format (4
// whole bits and 4 fractional bits). In this case, a throttle value of 0
// corresponds to a rate of 1/(0+1) = 1.0, which is 100% or full throttle. A
// throttle value of 15.9375 (i.e., 0xFF, or the max value) corresponds to a
// rate of 1/(15.9375+1) = 0.05904059, or 5.9% of the maximum rate.
//
// The throttle input is sampled between packets. Changing the throttle
// during a packet, or before its inserted stall time has elapsed, has no
// effect until the next packet.
//
// Parameters:
//
// THROTTLE_W : Width of the throttle input in bits.
// DATA_W : Width of data bus for AXI-Stream in bits.
// MTU : The maximum supported packet length is 2**MTU.
//
`default_nettype none
module axis_pkt_throttle #(
parameter int THROTTLE_W = 8,
parameter int DATA_W = 64,
parameter int MTU = 10
) (
input wire clk,
input wire rst,
input wire [THROTTLE_W-1:0] throttle,
// Input AXI-Stream
input wire [ DATA_W-1:0] i_tdata,
input wire i_tlast,
input wire i_tvalid,
output wire i_tready,
// Output AXI-Stream
output wire [ DATA_W-1:0] o_tdata,
output wire o_tlast,
output wire o_tvalid,
input wire o_tready
);
//---------------------------------------------------------------------------
// Throttle Control Logic
//---------------------------------------------------------------------------
//
// This logic monitors the data flow and determines when we should pass data
// through and when we should stall in order to limit the data rate.
//
//---------------------------------------------------------------------------
// Length of the fractional part of our fixed-point throttle and count.
localparam int FRAC_W = THROTTLE_W/2;
// Length of the whole-number part of our fixed-point throttle.
localparam int WHOLE_W = THROTTLE_W - FRAC_W;
// Width of an unsigned fixed-point value to track the amount of time to
// ensure that we have between the starts of packets. This must be large
// enough to store (2**MTU) * (2**THROTTLE_W-1).
localparam int TIME_W = MTU + THROTTLE_W;
// Width of an unsigned counter to track time between packets. Same as
// TIME_W, but the whole-number part.
localparam int COUNT_W = TIME_W - FRAC_W;
// Compute the minimum count value we can have and still guarantee that the
// next stall time adjustment won't cause underflow.
localparam longint MIN_COUNT = -(2**TIME_W) + (2**THROTTLE_W-1);
// Fixed-point accumulator that tracks amount of time to stall between
// packets. We add an extra bit for the sign since this value can be negative.
logic signed [TIME_W:0] stall_time;
// Counter to track the whole number of clock cycles to stall.
logic [COUNT_W-1:0] wait_count;
// Flag to indicate if underflow occurred and our count can't be trusted.
logic underflow;
// Register to control the flow of packets through this module. When 1, data
// flow is gated (stopped).
logic gate = 1'b0;
// Start of packet flag.
logic sop = 1'b1;
always_ff @(posedge clk) begin : throttle_control
if (gate) begin
wait_count <= wait_count-1;
gate <= (wait_count > 1);
// Update stall_time for next packet, in case throttle changes.
stall_time <= throttle;
end else begin
if (i_tvalid && o_tready) begin
if (i_tlast) begin
sop <= 1;
// End of the packet. Start stalling, if needed, and reset for the
// next packet.
if (!underflow && !stall_time[TIME_W]) begin
// No underflow and stall_time is non-negative, so start stalling
// the accumulated amount.
wait_count <= stall_time[FRAC_W+:COUNT_W];
gate <= (stall_time[FRAC_W+:COUNT_W] != 0);
end else begin
// We underflowed or stall_time was negative, so don't stall.
wait_count <= 0;
gate <= 0;
end
// Reset for next packet
underflow <= 0;
stall_time <= throttle;
end else begin
// A transfer is happening this cycle. Update stall time. Note that
// overflow is not possible as long as the MTU is honored.
stall_time <= stall_time + throttle;
sop <= 0;
end
end else begin
if (sop) begin
// We're in between packets. Update stall_time for next packet, in
// case throttle changes.
stall_time <= throttle;
end else begin
// An idle cycle (no transfer) is occurring this cycle so subtract
// 1.0 from our stall time. We must check for underflow since there
// is no limit to the number of idle cycles we might see.
stall_time <= stall_time - (1 << FRAC_W);
if (stall_time < MIN_COUNT) begin
underflow <= 1;
end
end
end
end
if (rst) begin
sop <= 1;
stall_time <= 0;
wait_count <= 'X; // Don't care
gate <= 0;
underflow <= 0;
end
end : throttle_control
//---------------------------------------------------------------------------
// Data Pass-Through
//---------------------------------------------------------------------------
assign o_tdata = i_tdata;
assign o_tlast = i_tlast;
assign o_tvalid = i_tvalid & ~gate;
assign i_tready = o_tready & ~gate;
endmodule : axis_pkt_throttle
`default_nettype wire
|