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
|
//
// Copyright 2014-2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This block passes the in_* AXI port to the out_* AXI port only when it has
// enough flow control credits. Data is held when there are not enough credits.
// Credits are replenished with extension context packets which update the
// last_consumed packet register. Max credits are controlled by settings regs.
// The 2nd line of the packet contains the sequence number in the low 12 bits.
// These packets should not have a time value, but if they do it will be ignored.
module source_flow_control_legacy #(
parameter BASE=0
) (
input clk, input reset, input clear,
input set_stb, input [7:0] set_addr, input [31:0] set_data,
input [63:0] fc_tdata, input fc_tlast, input fc_tvalid, output fc_tready,
input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready,
output [63:0] out_tdata, output out_tlast, output out_tvalid, input out_tready,
output busy
);
reg [31:0] last_seqnum_consumed;
wire [31:0] window_size;
wire [31:0] go_until_seqnum = last_seqnum_consumed + window_size + 1;
reg [31:0] current_seqnum;
wire window_reset;
wire window_enable;
//Sets the size of the flow control window
setting_reg #(.my_addr(BASE)) sr_window_size
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(window_size),.changed());
//Setting to enable/disable the flow control window
//When this register is hit, the window will reset.
//As a part of the reset process, all FC blocked data upstream will be
//dropped by this module and it will reset to the SFC_HEAD head state.
//The reset sequence can take more than one cycle during which this
//module will hold off all flow control data.
setting_reg #(.my_addr(BASE+1), .width(1)) sr_window_enable
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(window_enable),.changed(window_reset));
reg go;
reg window_reseting;
reg [11:0] window_reset_cnt; //Counter to make reset tolerant to bubble cycles
reg [1:0] sfc_state;
always @(posedge clk) begin
if (reset | clear) begin
window_reseting <= 1'b0;
end else if (window_reset) begin //Reset start
window_reseting <= 1'b1;
window_reset_cnt <= 12'd0;
end else if (window_reseting & ~in_tvalid) begin //Reset end
window_reset_cnt <= window_reset_cnt + 12'd1;
window_reseting <= (window_reset_cnt == 12'hFFF);
end
end
localparam SFC_HEAD = 2'd0;
localparam SFC_TIME = 2'd1;
localparam SFC_BODY = 2'd2;
localparam SFC_DUMP = 2'd3;
always @(posedge clk)
if (reset | clear | window_reset) begin
last_seqnum_consumed <= 32'hFFFFFFFF;
sfc_state <= SFC_HEAD;
end else if (fc_tvalid & fc_tready)
case(sfc_state)
SFC_HEAD :
if(fc_tlast)
sfc_state <= SFC_HEAD; // Error. CHDR packet with only a header is an error.
else if(~fc_tdata[63]) // Is this NOT an extension context packet?
sfc_state <= SFC_DUMP; // Error. Only extension context packets should come in on this interface.
else if(fc_tdata[61]) // Does this packet have time?
sfc_state <= SFC_TIME;
else
sfc_state <= SFC_BODY;
SFC_TIME :
if(fc_tlast)
sfc_state <= SFC_HEAD; // Error, CHDR packet with only header and time is an error.
else
sfc_state <= SFC_BODY;
SFC_BODY :
begin
last_seqnum_consumed <= fc_tdata[31:0]; // Sequence number is in lower 32bits.
if(fc_tlast)
sfc_state <= SFC_HEAD;
else
sfc_state <= SFC_DUMP; // Error. Not expecting any more data in a CHDR packet.
end
SFC_DUMP : // shouldn't ever need to be here, this is an error condition
if(fc_tlast)
sfc_state <= SFC_HEAD;
endcase // case (sfc_state)
assign busy = window_reseting;
assign fc_tready = ~window_reseting; // Consume FC if not in reset
assign out_tdata = in_tdata; // CHDR data flows through combinatorially.
assign out_tlast = in_tlast;
assign in_tready = (go ? out_tready : 1'b0) | window_reseting;
assign out_tvalid = (go & ~window_reseting) ? in_tvalid : 1'b0;
//
// Each time we receive the end of an IF data packet increment the current_seqnum.
// We bravely assume that no packets go missing...or at least that they will be detected elsewhere
// and then handled appropriately.
// The SEQNUM needs to be initialized every time we start a new stream. In new_rx_framer this is done
// as a side effect of writing a new SID value to the setting reg.
//
// By incrementing current_seqnum on the last signal we get the nice effect that packet flow is
// always suspended between packets rather than within a packet.
//
always @(posedge clk)
if(reset | clear | window_reseting)
current_seqnum <= 32'd0;
else if (in_tvalid && in_tready && in_tlast)
current_seqnum <= current_seqnum + 32'd1;
always @(posedge clk)
if(reset | clear) begin
go <= 1'b0;
end else begin
if(~window_enable)
go <= 1'b1;
else
case(go)
1'b0:
// This test assumes the host is well behaved in sending good numbers for packets consumed
// and that current_seqnum increments always by 1 only.
// This way wraps are dealt with without a large logic penalty.
if (in_tvalid & (go_until_seqnum - current_seqnum != 0))
go <= 1'b1;
//if(in_tvalid & (go_until_seqnum > current_seqnum)) // FIXME will need to handle wrap of 32-bit seqnum
1'b1:
if(in_tvalid & in_tready & in_tlast)
go <= 1'b0;
endcase // case (go)
end
endmodule // source_flow_control_legacy
|