387 lines
8.9 KiB
Verilog
387 lines
8.9 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Filename: axinarbiter.v
|
|
// {{{
|
|
// Project: 10Gb Ethernet switch
|
|
//
|
|
// Purpose: Arbitrates from among NIN packet sources to select and forward
|
|
// one of those sources forward.
|
|
//
|
|
// Creator: Dan Gisselquist, Ph.D.
|
|
// Gisselquist Technology, LLC
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// }}}
|
|
// Copyright (C) 2023, Gisselquist Technology, LLC
|
|
// {{{
|
|
// This file is part of the ETH10G project.
|
|
//
|
|
// The ETH10G project contains free software and gateware, licensed under the
|
|
// Apache License, Version 2.0 (the "License"). You may not use this project,
|
|
// or this file, except in compliance with the License. You may obtain a copy
|
|
// of the License at
|
|
// }}}
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// {{{
|
|
// Unless required by applicable law or agreed to in writing, files
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
`default_nettype none
|
|
// }}}
|
|
module axinarbiter #(
|
|
// {{{
|
|
parameter NIN = 4, // Number of incoming eth ports
|
|
parameter DW = 64, // Bits per clock cycle
|
|
parameter WBITS = $clog2(DW/8),
|
|
parameter [0:0] OPT_SKIDBUFFER = 1,
|
|
parameter [0:0] OPT_LOWPOWER = 0
|
|
// }}}
|
|
) (
|
|
// {{{
|
|
input wire i_clk, i_reset,
|
|
// Incoming packets from all interfaces
|
|
// {{{
|
|
input wire [NIN-1:0] S_VALID,
|
|
output wire [NIN-1:0] S_READY,
|
|
input wire [NIN*DW-1:0] S_DATA,
|
|
input wire [NIN*WBITS-1:0] S_BYTES,
|
|
input wire [NIN-1:0] S_LAST,
|
|
input wire [NIN-1:0] S_ABORT,
|
|
// }}}
|
|
// Outgoing packet
|
|
// {{{
|
|
output reg M_VALID,
|
|
input wire M_READY,
|
|
output reg [DW-1:0] M_DATA,
|
|
output reg [WBITS-1:0] M_BYTES,
|
|
output reg M_LAST,
|
|
output reg M_ABORT
|
|
// }}}
|
|
// }}}
|
|
);
|
|
|
|
// Local declarations
|
|
// {{{
|
|
genvar gk;
|
|
integer ik;
|
|
|
|
wire [NIN-1:0] grant;
|
|
wire [NIN-1:0] midpkt;
|
|
reg [DW-1:0] merged_data;
|
|
reg [WBITS-1:0] merged_bytes;
|
|
reg merged_last;
|
|
wire stalled;
|
|
|
|
wire [NIN-1:0] skd_valid, skd_ready;
|
|
wire [NIN*DW-1:0] skd_data;
|
|
wire [NIN*WBITS-1:0] skd_bytes;
|
|
wire [NIN-1:0] skd_last, skd_abort;
|
|
// }}}
|
|
|
|
pktarbiter #(
|
|
.W(NIN)
|
|
) u_arbiter (
|
|
// {{{
|
|
.i_clk(i_clk), .i_reset_n(!i_reset),
|
|
.i_req(S_VALID), .i_stall(stalled),
|
|
.o_grant(grant)
|
|
// }}}
|
|
);
|
|
|
|
generate if (OPT_SKIDBUFFER)
|
|
begin : GEN_SKIDBUFFER
|
|
|
|
for(gk=0; gk<NIN; gk=gk+1)
|
|
begin : NETSKID
|
|
|
|
netskid #(
|
|
.DW(DW+WBITS)
|
|
) u_netskid (
|
|
.i_clk(i_clk), .i_reset(i_reset),
|
|
//
|
|
.S_AXIN_VALID(S_VALID[gk]),
|
|
.S_AXIN_READY(S_READY[gk]),
|
|
.S_AXIN_DATA({ S_BYTES[gk*WBITS +: WBITS],
|
|
S_DATA[gk*DW +: DW] }),
|
|
.S_AXIN_LAST(S_LAST[gk]),
|
|
.S_AXIN_ABORT(S_ABORT[gk]),
|
|
//
|
|
.M_AXIN_VALID(skd_valid[gk]),
|
|
.M_AXIN_READY(skd_ready[gk]),
|
|
.M_AXIN_DATA({ skd_bytes[gk*WBITS +: WBITS],
|
|
skd_data[gk*DW +: DW] }),
|
|
.M_AXIN_LAST(skd_last[gk]),
|
|
.M_AXIN_ABORT(skd_abort[gk])
|
|
);
|
|
end
|
|
end else begin : NO_SKID
|
|
|
|
assign skd_valid = S_VALID;
|
|
assign S_READY = skd_ready;
|
|
assign skd_data = S_DATA;
|
|
assign skd_bytes = S_BYTES;
|
|
assign skd_last = S_LAST;
|
|
assign skd_abort = S_ABORT;
|
|
|
|
end endgenerate
|
|
|
|
generate for(gk=0; gk<NIN; gk=gk+1)
|
|
begin : GEN_MIDPKT
|
|
reg r_midpkt;
|
|
|
|
always @(posedge i_clk)
|
|
if (i_reset)
|
|
r_midpkt <= 0;
|
|
else if (skd_abort[gk] && (!skd_valid[gk] || skd_ready[gk]))
|
|
r_midpkt <= 1'b0;
|
|
else if (skd_valid[gk] && skd_ready[gk])
|
|
r_midpkt <= !skd_last[gk];
|
|
|
|
assign midpkt[gk] = r_midpkt;
|
|
end endgenerate
|
|
|
|
assign stalled = |midpkt || |(skd_valid & skd_ready);
|
|
assign skd_ready = (!M_VALID || M_READY) ? grant : 0;
|
|
|
|
// M_VALID
|
|
// {{{
|
|
initial M_VALID = 0;
|
|
always @(posedge i_clk)
|
|
if (i_reset)
|
|
M_VALID <= 0;
|
|
else if (!M_VALID || M_READY)
|
|
M_VALID <= |(skd_valid & skd_ready & (~skd_abort | midpkt));
|
|
// }}}
|
|
|
|
// merged_data, merged_bytes, merged_last
|
|
// {{{
|
|
always @(*)
|
|
begin
|
|
merged_data = 0;
|
|
merged_bytes = 0;
|
|
merged_last = 0;
|
|
|
|
for(ik=0; ik<NIN; ik=ik+1)
|
|
if (grant[ik])
|
|
begin
|
|
merged_data = merged_data | skd_data[ik * DW +: DW];
|
|
merged_bytes = merged_bytes
|
|
| skd_bytes[ik * WBITS +: WBITS];
|
|
merged_last = merged_last | skd_last[ik];
|
|
end
|
|
end
|
|
// }}}
|
|
|
|
// M_DATA, M_BYTES, M_LAST
|
|
// {{{
|
|
always @(posedge i_clk)
|
|
if (i_reset && OPT_LOWPOWER)
|
|
begin
|
|
M_DATA <= 0;
|
|
M_BYTES <= 0;
|
|
M_LAST <= 0;
|
|
end else if (!M_VALID || M_READY)
|
|
begin
|
|
M_DATA <= merged_data;
|
|
M_BYTES <= merged_bytes;
|
|
M_LAST <= merged_last;
|
|
|
|
if (OPT_LOWPOWER && (skd_valid & skd_ready & ~skd_abort) == 0)
|
|
begin
|
|
M_DATA <= 0;
|
|
M_BYTES <= 0;
|
|
M_LAST <= 0;
|
|
end
|
|
end
|
|
// }}}
|
|
|
|
// M_ABORT
|
|
// {{{
|
|
initial M_ABORT = 0;
|
|
always @(posedge i_clk)
|
|
if (i_reset)
|
|
M_ABORT <= 0;
|
|
else if (M_VALID && !M_READY && M_LAST && !M_ABORT)
|
|
M_ABORT <= 0;
|
|
else if (|(skd_abort & grant & midpkt))
|
|
M_ABORT <= 1'b1;
|
|
else if (!M_VALID || M_READY)
|
|
M_ABORT <= 1'b0;
|
|
// }}}
|
|
|
|
// Keep Verilator happy
|
|
// {{{
|
|
// Verilator lint_off UNUSED
|
|
wire unused;
|
|
assign unused = &{ 1'b0 };
|
|
// Verilator lint_on UNUSED
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
`ifdef FORMAL
|
|
wire [10:0] fmst_word;
|
|
wire [11:0] fmst_packets;
|
|
(* anyconst *) reg [DW:0] fnvr_data;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Interface properties
|
|
// {{{
|
|
generate for(gk=0; gk<NIN; gk=gk+1)
|
|
begin : F_SLAVE
|
|
wire [10:0] fslv_word;
|
|
wire [11:0] fslv_packets;
|
|
|
|
faxin_slave #(
|
|
.DATA_WIDTH(DW), .WBITS(WBITS)
|
|
) fslv (
|
|
// {{{
|
|
.S_AXI_ACLK(i_clk), .S_AXI_ARESETN(!i_reset),
|
|
//
|
|
.S_AXIN_VALID(S_VALID[gk]),
|
|
.S_AXIN_READY(S_READY[gk]),
|
|
.S_AXIN_DATA(S_DATA[gk*DW +: DW]),
|
|
.S_AXIN_BYTES(S_BYTES[gk*WBITS +: WBITS]),
|
|
.S_AXIN_LAST(S_LAST[gk]),
|
|
.S_AXIN_ABORT(S_ABORT[gk]),
|
|
//
|
|
.f_stream_word(fslv_word),
|
|
.f_packets_rcvd(fslv_packets)
|
|
// }}}
|
|
);
|
|
|
|
always @(*)
|
|
if (!i_reset && !grant[gk])
|
|
assert(fslv_word == 0);
|
|
|
|
always @(*)
|
|
if (!i_reset && fslv_word > 0)
|
|
begin
|
|
assert(grant[gk]);
|
|
assert(midpkt[gk]);
|
|
end
|
|
|
|
always @(*)
|
|
if (!i_reset && grant[gk])
|
|
begin
|
|
if (M_VALID && !M_ABORT)
|
|
assert(M_LAST || fslv_word != 0);
|
|
|
|
if (M_ABORT || (M_VALID && M_LAST))
|
|
begin
|
|
assert(fslv_word == 0);
|
|
assert(!midpkt[gk] || (S_VALID && S_ABORT));
|
|
end else begin
|
|
assert(midpkt[gk] == (fslv_word != 0));
|
|
assert(fslv_word
|
|
== (fmst_word + (M_VALID ? 1:0)));
|
|
end
|
|
end
|
|
|
|
always @(*)
|
|
if (!i_reset && S_VALID[gk])
|
|
assume({ S_LAST[gk],S_DATA[DW*gk +: DW] } != fnvr_data);
|
|
end endgenerate
|
|
|
|
faxin_master #(
|
|
.DATA_WIDTH(DW), .WBITS(WBITS)
|
|
) fslv (
|
|
// {{{
|
|
.S_AXI_ACLK(i_clk), .S_AXI_ARESETN(!i_reset),
|
|
//
|
|
.S_AXIN_VALID(M_VALID),
|
|
.S_AXIN_READY(M_READY),
|
|
.S_AXIN_DATA(M_DATA),
|
|
.S_AXIN_BYTES(M_BYTES),
|
|
.S_AXIN_LAST(M_LAST),
|
|
.S_AXIN_ABORT(M_ABORT),
|
|
//
|
|
.f_stream_word(fmst_word),
|
|
.f_packets_rcvd(fmst_packets)
|
|
// }}}
|
|
);
|
|
|
|
always @(*)
|
|
if (!i_reset && grant == 0)
|
|
assert(!M_VALID && fmst_word == 0);
|
|
|
|
always @(posedge i_clk)
|
|
if (!i_reset && $rose(M_VALID) && fmst_word == 0)
|
|
assert(!M_ABORT);
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Never properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
(* anyconst *) reg fnvr_abort;
|
|
|
|
always @(*)
|
|
if (!i_reset && fnvr_abort)
|
|
assume(0 == ((S_VALID | midpkt) & S_ABORT));
|
|
|
|
always @(*)
|
|
if (!i_reset && fnvr_abort)
|
|
assert(!M_ABORT);
|
|
|
|
always @(*)
|
|
if (!i_reset && M_VALID && !M_ABORT)
|
|
assert({ M_LAST,M_DATA } != fnvr_data);
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Low power checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
always @(*)
|
|
if (!i_reset && OPT_LOWPOWER && !M_VALID)
|
|
begin
|
|
assert(M_DATA == 0);
|
|
assert(M_BYTES == 0);
|
|
assert(M_LAST == 0);
|
|
end
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cover checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
always @(*)
|
|
cover(!i_reset && M_VALID && M_READY && M_LAST && !M_ABORT);
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Careless assumptions
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// }}}
|
|
`endif
|
|
// }}}
|
|
endmodule
|