Optimize axis_arb_mux and improve latency

This commit is contained in:
Alex Forencich 2018-08-09 18:40:50 -07:00
parent 7a879aec1c
commit 8e5ec36ced
3 changed files with 370 additions and 105 deletions

View File

@ -120,49 +120,59 @@ wire [{{n-1}}:0] acknowledge;
wire [{{n-1}}:0] grant;
wire grant_valid;
wire [{{w-1}}:0] grant_encoded;
// internal datapath
reg [DATA_WIDTH-1:0] output_axis_tdata_int;
reg [KEEP_WIDTH-1:0] output_axis_tkeep_int;
reg output_axis_tvalid_int;
reg output_axis_tready_int_reg = 1'b0;
reg output_axis_tlast_int;
reg [ID_WIDTH-1:0] output_axis_tid_int;
reg [DEST_WIDTH-1:0] output_axis_tdest_int;
reg [USER_WIDTH-1:0] output_axis_tuser_int;
wire output_axis_tready_int_early;
{% for p in ports %}
assign acknowledge[{{p}}] = input_{{p}}_axis_tvalid & input_{{p}}_axis_tready & input_{{p}}_axis_tlast;
assign request[{{p}}] = input_{{p}}_axis_tvalid & ~acknowledge[{{p}}];
assign input_{{p}}_axis_tready = grant[{{p}}] & output_axis_tready_int_reg;
{%- endfor %}
// mux instance
axis_mux_{{n}} #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
mux_inst (
.clk(clk),
.rst(rst),
// mux for incoming packet
reg [DATA_WIDTH-1:0] current_input_tdata;
reg [KEEP_WIDTH-1:0] current_input_tkeep;
reg current_input_tvalid;
reg current_input_tready;
reg current_input_tlast;
reg [ID_WIDTH-1:0] current_input_tid;
reg [DEST_WIDTH-1:0] current_input_tdest;
reg [USER_WIDTH-1:0] current_input_tuser;
always @* begin
case (grant_encoded)
{%- for p in ports %}
.input_{{p}}_axis_tdata(input_{{p}}_axis_tdata),
.input_{{p}}_axis_tkeep(input_{{p}}_axis_tkeep),
.input_{{p}}_axis_tvalid(input_{{p}}_axis_tvalid & grant[{{p}}]),
.input_{{p}}_axis_tready(input_{{p}}_axis_tready),
.input_{{p}}_axis_tlast(input_{{p}}_axis_tlast),
.input_{{p}}_axis_tid(input_{{p}}_axis_tid),
.input_{{p}}_axis_tdest(input_{{p}}_axis_tdest),
.input_{{p}}_axis_tuser(input_{{p}}_axis_tuser),
{{w}}'d{{p}}: begin
current_input_tdata = input_{{p}}_axis_tdata;
current_input_tkeep = input_{{p}}_axis_tkeep;
current_input_tvalid = input_{{p}}_axis_tvalid;
current_input_tready = input_{{p}}_axis_tready;
current_input_tlast = input_{{p}}_axis_tlast;
current_input_tid = input_{{p}}_axis_tid;
current_input_tdest = input_{{p}}_axis_tdest;
current_input_tuser = input_{{p}}_axis_tuser;
end
{%- endfor %}
.output_axis_tdata(output_axis_tdata),
.output_axis_tkeep(output_axis_tkeep),
.output_axis_tvalid(output_axis_tvalid),
.output_axis_tready(output_axis_tready),
.output_axis_tlast(output_axis_tlast),
.output_axis_tid(output_axis_tid),
.output_axis_tdest(output_axis_tdest),
.output_axis_tuser(output_axis_tuser),
.enable(grant_valid),
.select(grant_encoded)
);
default: begin
current_input_tdata = {DATA_WIDTH{1'b0}};
current_input_tkeep = {KEEP_WIDTH{1'b0}};
current_input_tvalid = 1'b0;
current_input_tready = 1'b0;
current_input_tlast = 1'b0;
current_input_tid = {ID_WIDTH{1'b0}};
current_input_tdest = {DEST_WIDTH{1'b0}};
current_input_tuser = {USER_WIDTH{1'b0}};
end
endcase
end
// arbiter instance
arbiter #(
.PORTS({{n}}),
.TYPE(ARB_TYPE),
@ -179,6 +189,126 @@ arb_inst (
.grant_encoded(grant_encoded)
);
// request generation
{%- for p in ports %}
assign request[{{p}}] = input_{{p}}_axis_tvalid & ~acknowledge[{{p}}];
{%- endfor %}
// acknowledge generation
{%- for p in ports %}
assign acknowledge[{{p}}] = grant[{{p}}] & input_{{p}}_axis_tvalid & input_{{p}}_axis_tready & input_{{p}}_axis_tlast;
{%- endfor %}
always @* begin
// pass through selected packet data
output_axis_tdata_int = current_input_tdata;
output_axis_tkeep_int = current_input_tkeep;
output_axis_tvalid_int = current_input_tvalid & current_input_tready;
output_axis_tlast_int = current_input_tlast;
output_axis_tid_int = current_input_tid;
output_axis_tdest_int = current_input_tdest;
output_axis_tuser_int = current_input_tuser;
end
// output datapath logic
reg [DATA_WIDTH-1:0] output_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] output_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg output_axis_tvalid_reg = 1'b0, output_axis_tvalid_next;
reg output_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] output_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] output_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] output_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_axis_tvalid_reg = 1'b0, temp_axis_tvalid_next;
reg temp_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign output_axis_tdata = output_axis_tdata_reg;
assign output_axis_tkeep = KEEP_ENABLE ? output_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign output_axis_tvalid = output_axis_tvalid_reg;
assign output_axis_tlast = output_axis_tlast_reg;
assign output_axis_tid = ID_ENABLE ? output_axis_tid_reg : {ID_WIDTH{1'b0}};
assign output_axis_tdest = DEST_ENABLE ? output_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign output_axis_tuser = USER_ENABLE ? output_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign output_axis_tready_int_early = output_axis_tready | (~temp_axis_tvalid_reg & (~output_axis_tvalid_reg | ~output_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
output_axis_tvalid_next = output_axis_tvalid_reg;
temp_axis_tvalid_next = temp_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (output_axis_tready_int_reg) begin
// input is ready
if (output_axis_tready | ~output_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
output_axis_tvalid_next = output_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_axis_tvalid_next = output_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (output_axis_tready) begin
// input is not ready, but output is ready
output_axis_tvalid_next = temp_axis_tvalid_reg;
temp_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
output_axis_tvalid_reg <= 1'b0;
output_axis_tready_int_reg <= 1'b0;
temp_axis_tvalid_reg <= 1'b0;
end else begin
output_axis_tvalid_reg <= output_axis_tvalid_next;
output_axis_tready_int_reg <= output_axis_tready_int_early;
temp_axis_tvalid_reg <= temp_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
output_axis_tdata_reg <= output_axis_tdata_int;
output_axis_tkeep_reg <= output_axis_tkeep_int;
output_axis_tlast_reg <= output_axis_tlast_int;
output_axis_tid_reg <= output_axis_tid_int;
output_axis_tdest_reg <= output_axis_tdest_int;
output_axis_tuser_reg <= output_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
output_axis_tdata_reg <= temp_axis_tdata_reg;
output_axis_tkeep_reg <= temp_axis_tkeep_reg;
output_axis_tlast_reg <= temp_axis_tlast_reg;
output_axis_tid_reg <= temp_axis_tid_reg;
output_axis_tdest_reg <= temp_axis_tdest_reg;
output_axis_tuser_reg <= temp_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_axis_tdata_reg <= output_axis_tdata_int;
temp_axis_tkeep_reg <= output_axis_tkeep_int;
temp_axis_tlast_reg <= output_axis_tlast_int;
temp_axis_tid_reg <= output_axis_tid_int;
temp_axis_tdest_reg <= output_axis_tdest_int;
temp_axis_tuser_reg <= output_axis_tuser_int;
end
end
endmodule
""")

View File

@ -107,75 +107,88 @@ wire [3:0] grant;
wire grant_valid;
wire [1:0] grant_encoded;
assign acknowledge[0] = input_0_axis_tvalid & input_0_axis_tready & input_0_axis_tlast;
assign request[0] = input_0_axis_tvalid & ~acknowledge[0];
assign acknowledge[1] = input_1_axis_tvalid & input_1_axis_tready & input_1_axis_tlast;
assign request[1] = input_1_axis_tvalid & ~acknowledge[1];
assign acknowledge[2] = input_2_axis_tvalid & input_2_axis_tready & input_2_axis_tlast;
assign request[2] = input_2_axis_tvalid & ~acknowledge[2];
assign acknowledge[3] = input_3_axis_tvalid & input_3_axis_tready & input_3_axis_tlast;
assign request[3] = input_3_axis_tvalid & ~acknowledge[3];
// internal datapath
reg [DATA_WIDTH-1:0] output_axis_tdata_int;
reg [KEEP_WIDTH-1:0] output_axis_tkeep_int;
reg output_axis_tvalid_int;
reg output_axis_tready_int_reg = 1'b0;
reg output_axis_tlast_int;
reg [ID_WIDTH-1:0] output_axis_tid_int;
reg [DEST_WIDTH-1:0] output_axis_tdest_int;
reg [USER_WIDTH-1:0] output_axis_tuser_int;
wire output_axis_tready_int_early;
// mux instance
axis_mux_4 #(
.DATA_WIDTH(DATA_WIDTH),
.KEEP_ENABLE(KEEP_ENABLE),
.KEEP_WIDTH(KEEP_WIDTH),
.ID_ENABLE(ID_ENABLE),
.ID_WIDTH(ID_WIDTH),
.DEST_ENABLE(DEST_ENABLE),
.DEST_WIDTH(DEST_WIDTH),
.USER_ENABLE(USER_ENABLE),
.USER_WIDTH(USER_WIDTH)
)
mux_inst (
.clk(clk),
.rst(rst),
.input_0_axis_tdata(input_0_axis_tdata),
.input_0_axis_tkeep(input_0_axis_tkeep),
.input_0_axis_tvalid(input_0_axis_tvalid & grant[0]),
.input_0_axis_tready(input_0_axis_tready),
.input_0_axis_tlast(input_0_axis_tlast),
.input_0_axis_tid(input_0_axis_tid),
.input_0_axis_tdest(input_0_axis_tdest),
.input_0_axis_tuser(input_0_axis_tuser),
.input_1_axis_tdata(input_1_axis_tdata),
.input_1_axis_tkeep(input_1_axis_tkeep),
.input_1_axis_tvalid(input_1_axis_tvalid & grant[1]),
.input_1_axis_tready(input_1_axis_tready),
.input_1_axis_tlast(input_1_axis_tlast),
.input_1_axis_tid(input_1_axis_tid),
.input_1_axis_tdest(input_1_axis_tdest),
.input_1_axis_tuser(input_1_axis_tuser),
.input_2_axis_tdata(input_2_axis_tdata),
.input_2_axis_tkeep(input_2_axis_tkeep),
.input_2_axis_tvalid(input_2_axis_tvalid & grant[2]),
.input_2_axis_tready(input_2_axis_tready),
.input_2_axis_tlast(input_2_axis_tlast),
.input_2_axis_tid(input_2_axis_tid),
.input_2_axis_tdest(input_2_axis_tdest),
.input_2_axis_tuser(input_2_axis_tuser),
.input_3_axis_tdata(input_3_axis_tdata),
.input_3_axis_tkeep(input_3_axis_tkeep),
.input_3_axis_tvalid(input_3_axis_tvalid & grant[3]),
.input_3_axis_tready(input_3_axis_tready),
.input_3_axis_tlast(input_3_axis_tlast),
.input_3_axis_tid(input_3_axis_tid),
.input_3_axis_tdest(input_3_axis_tdest),
.input_3_axis_tuser(input_3_axis_tuser),
.output_axis_tdata(output_axis_tdata),
.output_axis_tkeep(output_axis_tkeep),
.output_axis_tvalid(output_axis_tvalid),
.output_axis_tready(output_axis_tready),
.output_axis_tlast(output_axis_tlast),
.output_axis_tid(output_axis_tid),
.output_axis_tdest(output_axis_tdest),
.output_axis_tuser(output_axis_tuser),
.enable(grant_valid),
.select(grant_encoded)
);
assign input_0_axis_tready = grant[0] & output_axis_tready_int_reg;
assign input_1_axis_tready = grant[1] & output_axis_tready_int_reg;
assign input_2_axis_tready = grant[2] & output_axis_tready_int_reg;
assign input_3_axis_tready = grant[3] & output_axis_tready_int_reg;
// mux for incoming packet
reg [DATA_WIDTH-1:0] current_input_tdata;
reg [KEEP_WIDTH-1:0] current_input_tkeep;
reg current_input_tvalid;
reg current_input_tready;
reg current_input_tlast;
reg [ID_WIDTH-1:0] current_input_tid;
reg [DEST_WIDTH-1:0] current_input_tdest;
reg [USER_WIDTH-1:0] current_input_tuser;
always @* begin
case (grant_encoded)
2'd0: begin
current_input_tdata = input_0_axis_tdata;
current_input_tkeep = input_0_axis_tkeep;
current_input_tvalid = input_0_axis_tvalid;
current_input_tready = input_0_axis_tready;
current_input_tlast = input_0_axis_tlast;
current_input_tid = input_0_axis_tid;
current_input_tdest = input_0_axis_tdest;
current_input_tuser = input_0_axis_tuser;
end
2'd1: begin
current_input_tdata = input_1_axis_tdata;
current_input_tkeep = input_1_axis_tkeep;
current_input_tvalid = input_1_axis_tvalid;
current_input_tready = input_1_axis_tready;
current_input_tlast = input_1_axis_tlast;
current_input_tid = input_1_axis_tid;
current_input_tdest = input_1_axis_tdest;
current_input_tuser = input_1_axis_tuser;
end
2'd2: begin
current_input_tdata = input_2_axis_tdata;
current_input_tkeep = input_2_axis_tkeep;
current_input_tvalid = input_2_axis_tvalid;
current_input_tready = input_2_axis_tready;
current_input_tlast = input_2_axis_tlast;
current_input_tid = input_2_axis_tid;
current_input_tdest = input_2_axis_tdest;
current_input_tuser = input_2_axis_tuser;
end
2'd3: begin
current_input_tdata = input_3_axis_tdata;
current_input_tkeep = input_3_axis_tkeep;
current_input_tvalid = input_3_axis_tvalid;
current_input_tready = input_3_axis_tready;
current_input_tlast = input_3_axis_tlast;
current_input_tid = input_3_axis_tid;
current_input_tdest = input_3_axis_tdest;
current_input_tuser = input_3_axis_tuser;
end
default: begin
current_input_tdata = {DATA_WIDTH{1'b0}};
current_input_tkeep = {KEEP_WIDTH{1'b0}};
current_input_tvalid = 1'b0;
current_input_tready = 1'b0;
current_input_tlast = 1'b0;
current_input_tid = {ID_WIDTH{1'b0}};
current_input_tdest = {DEST_WIDTH{1'b0}};
current_input_tuser = {USER_WIDTH{1'b0}};
end
endcase
end
// arbiter instance
arbiter #(
.PORTS(4),
.TYPE(ARB_TYPE),
@ -192,4 +205,126 @@ arb_inst (
.grant_encoded(grant_encoded)
);
// request generation
assign request[0] = input_0_axis_tvalid & ~acknowledge[0];
assign request[1] = input_1_axis_tvalid & ~acknowledge[1];
assign request[2] = input_2_axis_tvalid & ~acknowledge[2];
assign request[3] = input_3_axis_tvalid & ~acknowledge[3];
// acknowledge generation
assign acknowledge[0] = grant[0] & input_0_axis_tvalid & input_0_axis_tready & input_0_axis_tlast;
assign acknowledge[1] = grant[1] & input_1_axis_tvalid & input_1_axis_tready & input_1_axis_tlast;
assign acknowledge[2] = grant[2] & input_2_axis_tvalid & input_2_axis_tready & input_2_axis_tlast;
assign acknowledge[3] = grant[3] & input_3_axis_tvalid & input_3_axis_tready & input_3_axis_tlast;
always @* begin
// pass through selected packet data
output_axis_tdata_int = current_input_tdata;
output_axis_tkeep_int = current_input_tkeep;
output_axis_tvalid_int = current_input_tvalid & current_input_tready;
output_axis_tlast_int = current_input_tlast;
output_axis_tid_int = current_input_tid;
output_axis_tdest_int = current_input_tdest;
output_axis_tuser_int = current_input_tuser;
end
// output datapath logic
reg [DATA_WIDTH-1:0] output_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] output_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg output_axis_tvalid_reg = 1'b0, output_axis_tvalid_next;
reg output_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] output_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] output_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] output_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_axis_tvalid_reg = 1'b0, temp_axis_tvalid_next;
reg temp_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign output_axis_tdata = output_axis_tdata_reg;
assign output_axis_tkeep = KEEP_ENABLE ? output_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign output_axis_tvalid = output_axis_tvalid_reg;
assign output_axis_tlast = output_axis_tlast_reg;
assign output_axis_tid = ID_ENABLE ? output_axis_tid_reg : {ID_WIDTH{1'b0}};
assign output_axis_tdest = DEST_ENABLE ? output_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign output_axis_tuser = USER_ENABLE ? output_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign output_axis_tready_int_early = output_axis_tready | (~temp_axis_tvalid_reg & (~output_axis_tvalid_reg | ~output_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
output_axis_tvalid_next = output_axis_tvalid_reg;
temp_axis_tvalid_next = temp_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (output_axis_tready_int_reg) begin
// input is ready
if (output_axis_tready | ~output_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
output_axis_tvalid_next = output_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_axis_tvalid_next = output_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (output_axis_tready) begin
// input is not ready, but output is ready
output_axis_tvalid_next = temp_axis_tvalid_reg;
temp_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
output_axis_tvalid_reg <= 1'b0;
output_axis_tready_int_reg <= 1'b0;
temp_axis_tvalid_reg <= 1'b0;
end else begin
output_axis_tvalid_reg <= output_axis_tvalid_next;
output_axis_tready_int_reg <= output_axis_tready_int_early;
temp_axis_tvalid_reg <= temp_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
output_axis_tdata_reg <= output_axis_tdata_int;
output_axis_tkeep_reg <= output_axis_tkeep_int;
output_axis_tlast_reg <= output_axis_tlast_int;
output_axis_tid_reg <= output_axis_tid_int;
output_axis_tdest_reg <= output_axis_tdest_int;
output_axis_tuser_reg <= output_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
output_axis_tdata_reg <= temp_axis_tdata_reg;
output_axis_tkeep_reg <= temp_axis_tkeep_reg;
output_axis_tlast_reg <= temp_axis_tlast_reg;
output_axis_tid_reg <= temp_axis_tid_reg;
output_axis_tdest_reg <= temp_axis_tdest_reg;
output_axis_tuser_reg <= temp_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_axis_tdata_reg <= output_axis_tdata_int;
temp_axis_tkeep_reg <= output_axis_tkeep_int;
temp_axis_tlast_reg <= output_axis_tlast_int;
temp_axis_tid_reg <= output_axis_tid_int;
temp_axis_tdest_reg <= output_axis_tdest_int;
temp_axis_tuser_reg <= output_axis_tuser_int;
end
end
endmodule

View File

@ -509,7 +509,7 @@ def bench():
source_2.send(test_frame2)
yield clk.posedge
yield delay(150)
yield delay(100)
yield clk.posedge
source_1.send(test_frame1)
@ -531,12 +531,12 @@ def bench():
yield sink.wait()
rx_frame = sink.recv()
assert rx_frame == test_frame2
assert rx_frame == test_frame1
yield sink.wait()
rx_frame = sink.recv()
assert rx_frame == test_frame1
assert rx_frame == test_frame2
yield sink.wait()
rx_frame = sink.recv()