Squashed B200 FPGA Source. Code from Josh Blum, Ian Buckley, and Matt Ettus.
Original-commit: 0df4b801a34697f2058b4a7b95e08d2a0576c9db
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
vita.txt
|
||||
xo.txt
|
||||
zpu.txt
|
||||
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Packet Processing Sources
|
||||
##################################################
|
||||
PACKET_PROC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/packet_proc/, \
|
||||
eth_dispatch.v \
|
||||
ip_hdr_checksum.v \
|
||||
vrlp_eth_framer.v \
|
||||
chdr_eth_framer.v \
|
||||
eth_interface.v \
|
||||
vrlp_to_compressed_vita.v \
|
||||
compressed_vita_to_vrlp.v \
|
||||
source_flow_control.v \
|
||||
cvita_insert_tlast.v \
|
||||
cvita_dest_lookup.v \
|
||||
cvita_chunker.v \
|
||||
cvita_dechunker.v \
|
||||
))
|
||||
@@ -0,0 +1,136 @@
|
||||
|
||||
// chdr_eth_framer
|
||||
// Takes a CHDR stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
//
|
||||
|
||||
module chdr_eth_framer
|
||||
#(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] in_tdata, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [7:0] sid;
|
||||
reg [15:0] chdr_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 8'd0;
|
||||
chdr_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tdata[7:0];
|
||||
chdr_len <= in_tdata[47:32];
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,chdr_len[2:0]} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + chdr_len); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + chdr_len);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [7:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(8)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // chdr_eth_framer
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
module compressed_vita_to_vrlp
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output [15:0] o_tuser, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
wire [19:0] vrlp_size = 20'd3 + {4'b0000,i_tdata[47:32]};
|
||||
reg odd_len;
|
||||
reg [2:0] cv2v_state;
|
||||
|
||||
reg [63:0] o_tdata_int;
|
||||
wire o_tlast_int, o_tvalid_int, o_tready_int;
|
||||
|
||||
localparam CV2V_VRLP = 3'd0; // VRLP header
|
||||
localparam CV2V_VRT_ECH = 3'd1; // Extension context header
|
||||
localparam CV2V_VRT_IFH = 3'd2; // IF Data header
|
||||
localparam CV2V_BODY = 3'd3;
|
||||
localparam CV2V_VEND_ODD = 3'd4;
|
||||
localparam CV2V_VEND_EVEN = 3'd4;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
odd_len <= 1'b0;
|
||||
end
|
||||
else
|
||||
case(cv2v_state)
|
||||
CV2V_VRLP :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
odd_len <= i_tdata[32];
|
||||
if(i_tdata[63])
|
||||
cv2v_state <= CV2V_VRT_ECH;
|
||||
else
|
||||
cv2v_state <= CV2V_VRT_IFH;
|
||||
end
|
||||
|
||||
CV2V_VRT_ECH, CV2V_VRT_IFH :
|
||||
if(i_tvalid & o_tready_int)
|
||||
cv2v_state <= CV2V_BODY;
|
||||
|
||||
CV2V_BODY :
|
||||
if(i_tlast & i_tvalid & o_tready_int)
|
||||
if(odd_len)
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
else
|
||||
cv2v_state <= CV2V_VEND_EVEN;
|
||||
|
||||
CV2V_VEND_EVEN :
|
||||
if(o_tready_int)
|
||||
cv2v_state <= CV2V_VRLP;
|
||||
endcase // case (cv2v_state)
|
||||
|
||||
assign i_tready = o_tready_int & (cv2v_state != CV2V_VRLP) & (cv2v_state != CV2V_VEND_EVEN);
|
||||
assign o_tvalid_int = i_tvalid | (cv2v_state == CV2V_VEND_EVEN);
|
||||
|
||||
always @*
|
||||
case(cv2v_state)
|
||||
CV2V_VRLP : o_tdata_int <= { 32'h5652_4c50 /*VRLP*/, i_tdata[59:48] /*seqnum*/, vrlp_size[19:0] };
|
||||
CV2V_VRT_ECH : o_tdata_int <= { 4'h5 /*type*/, 4'h0, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ };
|
||||
CV2V_VRT_IFH : o_tdata_int <= { 4'h1 /*type*/, 1'b0, i_tdata[62] /*TRL*/, 1'b0, i_tdata[60] /*eob*/, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ };
|
||||
CV2V_BODY : o_tdata_int <= (i_tlast & odd_len) ? { i_tdata[63:32], 32'h5645_4E44 /*VEND*/ } : i_tdata;
|
||||
CV2V_VEND_EVEN : o_tdata_int <= { 32'h5645_4E44 /*VEND*/, 32'h0};
|
||||
default : o_tdata_int <= i_tdata;
|
||||
endcase // case (cv2v_state)
|
||||
|
||||
assign o_tlast_int = (cv2v_state == CV2V_VEND_EVEN) | ((cv2v_state == CV2V_BODY) & i_tlast & odd_len);
|
||||
|
||||
// Short FIFO before output
|
||||
axi_fifo_short #(.WIDTH(81)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({o_tlast_int, i_tdata[15:0], o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast, o_tuser, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
endmodule // compressed_vita_to_vrlp
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
// Quantize cvita packets to a configurable quantum value. o_tlast and
|
||||
// i_tready will be held off until the entire quantized packet is xferred.
|
||||
// If quantum is changed, it is the responsibility of the client to clear
|
||||
// this module. error is asserted if a packet is larger than the quantum
|
||||
// error can be reset by asserting reset or clear.
|
||||
|
||||
module cvita_chunker # (
|
||||
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [15:0] frame_size,
|
||||
|
||||
input [63:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output error
|
||||
);
|
||||
|
||||
localparam ST_HEADER = 2'd0;
|
||||
localparam ST_DATA = 2'd1;
|
||||
localparam ST_PADDING = 2'd2;
|
||||
localparam ST_ERROR = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [15:0] frame_rem;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else if (o_tvalid & o_tready) begin
|
||||
case (state)
|
||||
ST_HEADER: begin
|
||||
if (axi_len > frame_size)
|
||||
state <= ST_ERROR;
|
||||
else if (i_tlast)
|
||||
state <= ST_PADDING;
|
||||
else
|
||||
state <= ST_DATA;
|
||||
|
||||
frame_rem <= frame_size - 16'd1;
|
||||
end
|
||||
|
||||
ST_DATA: begin
|
||||
if (i_tlast) begin
|
||||
state <= o_tlast ? ST_HEADER : ST_PADDING;
|
||||
frame_rem <= o_tlast ? 16'd0 : (frame_rem - 16'd1);
|
||||
end else begin
|
||||
state <= ST_DATA;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
|
||||
ST_PADDING: begin
|
||||
if (o_tlast) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_PADDING;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign i_tready = o_tready & (state != ST_PADDING);
|
||||
|
||||
assign o_tvalid = i_tvalid | (state == ST_PADDING);
|
||||
assign o_tlast = (frame_rem != 0) ? (frame_rem == 16'd1) : (axi_len == 16'd1);
|
||||
assign o_tdata = (state == ST_PADDING) ? PAD_VALUE : i_tdata;
|
||||
|
||||
assign error = (state == ST_ERROR);
|
||||
|
||||
endmodule // cvita_chunker
|
||||
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
`timescale 500ps/1ps
|
||||
|
||||
module cvita_chunker_tb();
|
||||
|
||||
// TB stimulus
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
reg clear = 0;
|
||||
reg [15:0] quantum;
|
||||
|
||||
// Check vars
|
||||
reg [31:0] o_xfer_count = 0, i_xfer_count = 0;
|
||||
reg [63:0] o_last_tdata = 0;
|
||||
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_chunker_tb.vcd");
|
||||
initial $dumpvars(0,cvita_chunker_tb);
|
||||
|
||||
function check_result;
|
||||
input [31:0] o_xfer_count_arg;
|
||||
input [31:0] i_xfer_count_arg;
|
||||
input [63:0] o_last_tdata_arg;
|
||||
input error_arg;
|
||||
begin
|
||||
//Check vars
|
||||
check_result = 1;
|
||||
check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0);
|
||||
check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0);
|
||||
check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0);
|
||||
check_result = check_result & ((error_arg == error) != 0);
|
||||
|
||||
if (check_result) begin
|
||||
$display ("... Passed");
|
||||
end else begin
|
||||
$display ("... FAILED!!!");
|
||||
$display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg);
|
||||
$display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg);
|
||||
$display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg);
|
||||
$display ("error = %d (Expected %d)",error,error_arg);
|
||||
end
|
||||
|
||||
//Reset vars
|
||||
o_xfer_count = 0;
|
||||
i_xfer_count = 0;
|
||||
o_last_tdata = 64'h0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
if(len < 9) begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end else begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(((len-1)/8)-1) begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task reset_quantum_atomic;
|
||||
input [15:0] quant;
|
||||
begin
|
||||
quantum <= quant;
|
||||
clear <= 1;
|
||||
@(posedge clk);
|
||||
clear <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // reset_quantum_atomic
|
||||
|
||||
|
||||
initial begin
|
||||
#100 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready;
|
||||
wire error;
|
||||
|
||||
initial begin
|
||||
quantum <= 256;
|
||||
i_tvalid <= 0;
|
||||
while(reset) @(posedge clk);
|
||||
|
||||
$write ("Running test case: First packet after reset");
|
||||
send_packet(64'h00000001_00000000, 128);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(256,16,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) < quantum");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(8,5,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
reset_quantum_atomic(5);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(5,5,64'h00000030_00000008,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum - 64bits");
|
||||
send_packet(64'h00000001_00000000, 32);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(5,4,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum + 64bits");
|
||||
send_packet(64'h00000001_00000000, 48);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1);
|
||||
|
||||
$write ("Running test case: Error reset");
|
||||
reset_quantum_atomic(8);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) > quantum");
|
||||
send_packet(64'h00000001_00000000, 80);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == 2");
|
||||
send_packet(64'h00000001_00000000, 8);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(8,1,64'hFFFFFFFF_FFFFFFFF,0);
|
||||
|
||||
$write ("Running test case: Multiple packets back-to-back");
|
||||
send_packet(64'h00000001_00000000, 40);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
send_packet(64'h00000001_00000000, 16);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
send_packet(64'h00000001_00000000, 64);
|
||||
while(o_tvalid) @(posedge clk);
|
||||
check_result(24,15,64'h0000004e0000000e,0);
|
||||
|
||||
end // initial begin
|
||||
|
||||
|
||||
cvita_chunker dut (
|
||||
.clk(clk), .reset(reset), .clear(clear), .frame_size(quantum),
|
||||
.i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.error(error));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (o_tvalid & o_tready) begin
|
||||
o_xfer_count <= o_xfer_count + 1;
|
||||
o_last_tdata <= o_tdata;
|
||||
end
|
||||
if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1;
|
||||
end
|
||||
|
||||
endmodule // cvita_chunker_tb
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
module cvita_dechunker # (
|
||||
parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [15:0] frame_size,
|
||||
|
||||
input [63:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
|
||||
output error
|
||||
);
|
||||
|
||||
localparam ST_HEADER = 2'd0;
|
||||
localparam ST_DATA = 2'd1;
|
||||
localparam ST_PADDING = 2'd2;
|
||||
localparam ST_ERROR = 2'd3;
|
||||
|
||||
reg [1:0] state;
|
||||
reg [15:0] frame_rem, pkt_rem;
|
||||
wire i_tlast;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
pkt_rem <= 16'd0;
|
||||
end else if (i_tvalid & i_tready) begin
|
||||
case (state)
|
||||
ST_HEADER: begin
|
||||
if (axi_len > frame_size)
|
||||
state <= ST_ERROR;
|
||||
else if (~o_tlast)
|
||||
state <= ST_DATA;
|
||||
else
|
||||
state <= ST_PADDING;
|
||||
|
||||
frame_rem <= frame_size - 16'd1;
|
||||
pkt_rem <= axi_len - 16'd1;
|
||||
end
|
||||
|
||||
ST_DATA: begin
|
||||
if (o_tlast) begin
|
||||
state <= i_tlast ? ST_HEADER : ST_PADDING;
|
||||
pkt_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_DATA;
|
||||
pkt_rem <= pkt_rem - 16'd1;
|
||||
end
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
|
||||
ST_PADDING: begin
|
||||
if (i_tlast) begin
|
||||
state <= ST_HEADER;
|
||||
frame_rem <= 16'd0;
|
||||
end else begin
|
||||
state <= ST_PADDING;
|
||||
frame_rem <= frame_rem - 16'd1;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign i_tready = o_tready | (state == ST_PADDING);
|
||||
assign i_tlast = (frame_rem == 16'd1); //Temp signal
|
||||
|
||||
assign o_tvalid = i_tvalid & (state != ST_PADDING);
|
||||
assign o_tlast = (pkt_rem != 0) ? (pkt_rem == 16'd1) : (axi_len == 16'd1);
|
||||
assign o_tdata = i_tdata;
|
||||
|
||||
assign error = (state == ST_ERROR);
|
||||
|
||||
endmodule // cvita_dechunker
|
||||
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
//
|
||||
|
||||
|
||||
`timescale 500ps/1ps
|
||||
|
||||
module cvita_dechunker_tb();
|
||||
|
||||
// TB stimulus
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
reg clear = 0;
|
||||
reg [15:0] quantum;
|
||||
wire error;
|
||||
|
||||
// Check vars
|
||||
reg [31:0] o_xfer_count = 0, i_xfer_count = 0;
|
||||
reg [63:0] o_last_tdata = 0;
|
||||
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_dechunker_tb.vcd");
|
||||
initial $dumpvars(0,cvita_dechunker_tb);
|
||||
|
||||
function check_result;
|
||||
input [31:0] o_xfer_count_arg;
|
||||
input [31:0] i_xfer_count_arg;
|
||||
input [63:0] o_last_tdata_arg;
|
||||
input error_arg;
|
||||
begin
|
||||
//Check vars
|
||||
check_result = 1;
|
||||
check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0);
|
||||
check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0);
|
||||
check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0);
|
||||
check_result = check_result & ((error_arg == error) != 0);
|
||||
|
||||
if (check_result) begin
|
||||
$display ("... Passed");
|
||||
end else begin
|
||||
$display ("... FAILED!!!");
|
||||
$display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg);
|
||||
$display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg);
|
||||
$display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg);
|
||||
$display ("error = %d (Expected %d)",error,error_arg);
|
||||
end
|
||||
|
||||
//Reset vars
|
||||
o_xfer_count = 0;
|
||||
i_xfer_count = 0;
|
||||
o_last_tdata = 64'h0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
input [31:0] quant;
|
||||
|
||||
begin
|
||||
if(quant < 2) begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end else begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(quant - 2) begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task reset_quantum_atomic;
|
||||
input [15:0] quant;
|
||||
begin
|
||||
quantum <= quant;
|
||||
clear <= 1;
|
||||
@(posedge clk);
|
||||
clear <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // reset_quantum_atomic
|
||||
|
||||
|
||||
initial begin
|
||||
#100 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready;
|
||||
|
||||
initial begin
|
||||
quantum <= 8;
|
||||
i_tvalid <= 0;
|
||||
while(reset) @(posedge clk);
|
||||
|
||||
$write ("Running test case: First packet after reset");
|
||||
send_packet(64'h00000001_00000000, 32, 8);
|
||||
@(posedge clk);
|
||||
check_result(4,8,64'hxxxxxxxx_xxxxxx06, 0);
|
||||
|
||||
reset_quantum_atomic(10);
|
||||
|
||||
$write ("Running test case: sizeof(packet) < quantum");
|
||||
send_packet(64'h00000001_00000000, 64, 10);
|
||||
@(posedge clk);
|
||||
check_result(8,10,64'hxxxxxxxx_xxxxxx0e, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum");
|
||||
send_packet(64'h00000001_00000000, 80, 10);
|
||||
@(posedge clk);
|
||||
check_result(10,10,64'hxxxxxxxx_xxxxxx12, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum - 64bits");
|
||||
send_packet(64'h00000001_00000000, 72, 10);
|
||||
@(posedge clk);
|
||||
check_result(9,10,64'hxxxxxxxx_xxxxxx10, 0);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == quantum + 64bits");
|
||||
send_packet(64'h00000001_00000000, 88, 10);
|
||||
@(posedge clk);
|
||||
check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1);
|
||||
|
||||
reset_quantum_atomic(10);
|
||||
|
||||
$write ("Running test case: sizeof(packet) > quantum");
|
||||
send_packet(64'h00000001_00000000, 88, 10);
|
||||
@(posedge clk);
|
||||
check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1);
|
||||
|
||||
reset_quantum_atomic(8);
|
||||
|
||||
$write ("Running test case: sizeof(packet) == 2");
|
||||
send_packet(64'h00000001_00000000, 8, 8);
|
||||
@(posedge clk);
|
||||
check_result(1,8,64'hxxxxxxxx_xxxxxx00, 0);
|
||||
|
||||
$write ("Running test case: Multiple packets");
|
||||
send_packet(64'h00000001_00000000, 8, 8);
|
||||
send_packet(64'h00000001_00000000, 16, 8);
|
||||
send_packet(64'h00000001_00000000, 24, 8);
|
||||
send_packet(64'h00000001_00000000, 32, 8);
|
||||
@(posedge clk);
|
||||
check_result(10,32,64'hxxxxxxxx_xxxxxx06, 0);
|
||||
|
||||
end // initial begin
|
||||
|
||||
|
||||
cvita_dechunker dut (
|
||||
.clk(clk), .reset(reset), .clear(clear), .frame_size(quantum),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.error(error));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (o_tvalid & o_tready) begin
|
||||
o_xfer_count <= o_xfer_count + 1;
|
||||
o_last_tdata <= o_tdata;
|
||||
end
|
||||
if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1;
|
||||
end
|
||||
|
||||
endmodule // cvita_dechunker_tb
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
// Map the endpoint dest part of the SID in the CVITA header to a destination
|
||||
// This destination (o_tdest) signal will be valid with o_tdata
|
||||
// This only works with VALID CVITA frames
|
||||
|
||||
module cvita_dest_lookup
|
||||
#(
|
||||
parameter DEST_WIDTH = 4
|
||||
)
|
||||
(
|
||||
input clk, input rst,
|
||||
input set_stb, input [7:0] set_addr, input [DEST_WIDTH-1:0] set_data,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready,
|
||||
output [DEST_WIDTH-1:0] o_tdest
|
||||
);
|
||||
|
||||
reg [7:0] endpoint;
|
||||
ram_2port #(.DWIDTH(DEST_WIDTH), .AWIDTH(8)) dest_lut
|
||||
(
|
||||
.clka(clk), .ena(1'b1), .wea(set_stb), .addra(set_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(endpoint), .dib(8'hff), .dob(o_tdest)
|
||||
);
|
||||
|
||||
reg forward;
|
||||
reg [1:0] count;
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
forward <= 1'b0;
|
||||
count <= 2'b0;
|
||||
end
|
||||
else if (forward == 1'b0 && i_tvalid) begin
|
||||
if (count == 2'b11) forward <= 1'b1;
|
||||
endpoint <= i_tdata[23:16];
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
else if (forward == 1'b1 && i_tvalid && i_tready && i_tlast) begin
|
||||
forward <= 1'b0;
|
||||
count <= 2'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
assign o_tlast = i_tlast;
|
||||
assign o_tvalid = i_tvalid && forward;
|
||||
assign i_tready = o_tready && forward;
|
||||
|
||||
endmodule // cvita_dest_lookup
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
// Insert tlast bit for fifos that don't support it. This only works with VALID CVITA frames
|
||||
// A single partial or invalid frame will make this wrong FOREVER
|
||||
|
||||
module cvita_insert_tlast
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready);
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
assign o_tvalid = i_tvalid;
|
||||
assign i_tready = o_tready;
|
||||
|
||||
wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7;
|
||||
wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]};
|
||||
|
||||
reg [15:0] count;
|
||||
|
||||
assign o_tlast = (count != 0) ? (count == 16'd1) : (axi_len == 16'd1);
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
count <= 16'd0;
|
||||
end
|
||||
else
|
||||
if(i_tready & i_tvalid)
|
||||
if(count != 16'd0)
|
||||
count <= count - 16'd1;
|
||||
else
|
||||
count <= axi_len - 16'd1;
|
||||
|
||||
endmodule // cvita_insert_tlast
|
||||
@@ -0,0 +1,92 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module cvita_insert_tlast_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("cvita_insert_tlast_tb.vcd");
|
||||
initial $dumpvars(0,cvita_insert_tlast_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
if(len < 9)
|
||||
begin
|
||||
{i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
repeat(((len-2)/2)-1+len[0])
|
||||
begin
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
@(posedge clk);
|
||||
end
|
||||
i_tdata <= i_tdata + 64'h0000_0002_0000_0002;
|
||||
i_tlast <= 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
end // else: !if(len < 3)
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] i_tdata;
|
||||
reg i_tlast;
|
||||
reg i_tvalid;
|
||||
wire i_tready;
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast, o_tvalid, o_tready, o_tlast_regen;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
send_packet(64'hA0000000_A0000001, 24);
|
||||
send_packet(64'hA0000000_A0000001, 20);
|
||||
send_packet(64'hA0000000_A0000001, 16);
|
||||
send_packet(64'hA0000000_A0000001, 12);
|
||||
send_packet(64'hA0000000_A0000001, 8);
|
||||
send_packet(64'hA0000000_A0000001, 4);
|
||||
send_packet(64'hA0000000_A0000001, 4);
|
||||
send_packet(64'hA0000000_A0000001, 8);
|
||||
send_packet(64'hA0000000_A0000001, 12);
|
||||
end // initial begin
|
||||
|
||||
cvita_insert_tlast dut
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast_regen), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
assign o_tready = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
begin
|
||||
$display ("TLAST %x\t TLAST_REGEN %x",i_tlast, o_tlast_regen);
|
||||
if(i_tlast != o_tlast_regen)
|
||||
$display("ERROR!!!!!!");
|
||||
end
|
||||
endmodule // cvita_insert_tlast_tb
|
||||
@@ -0,0 +1,573 @@
|
||||
|
||||
// Ethernet dispatcher
|
||||
// Incoming ethernet packets are examined and sent to the correct destination
|
||||
// There are 3 destinations, ZPU, other ethernet port (out), and vita router
|
||||
// Packets going to the vita router will have the ethernet/ip/udp headers stripped off.
|
||||
//
|
||||
// To make things simpler, we start out by sending all packets to zpu and out port.
|
||||
// By the end of the eth/ip/udp headers, we can determine where the correct destination is.
|
||||
// If the correct destination is vita, we send an error indication on the zpu and out ports,
|
||||
// which will cause the axi_packet_gate to drop those packets, and send the vita frame to
|
||||
// the vita port.
|
||||
//
|
||||
// If at the end of the headers we determine the packet should go to zpu, then we send an
|
||||
// error indication on the out port, the rest of the packet to zpu and nothing on vita.
|
||||
// If it should go to out, we send the error indication to zpu, the rest of the packet to out,
|
||||
// and nothing on vita.
|
||||
//
|
||||
// Downstream we should have adequate fifo space, otherwise we could get backed up here.
|
||||
//
|
||||
// No tuser bits sent to vita, as vita assumes there are no errors and that occupancy is
|
||||
// indicated by the length field of the vita header.
|
||||
|
||||
//
|
||||
// Rules for forwarding:
|
||||
//
|
||||
// Ethernet Broadcast (Dst MAC = ff:ff:ff:ff:ff:ff). Forward to both ZPU and XO MAC.
|
||||
// ? Ethernet Multicast (Dst MAC = USRP_NEXT_HOP). Forward only to ZPU.
|
||||
// ? Ethernet Multicast (Dst MAC = Unknown). Forward only to XO.
|
||||
// Ethernet Unicast (Dst MAC = Unknown). Forward only to XO.
|
||||
// Ethernet Unicast (Dst MAC = local). Look deeper......
|
||||
// IP Broadcast. Forward to both ZPU and XO MAC. (Should be coverd by Eth broadcast)
|
||||
// IP Multicast. ? Unknow Action.
|
||||
// IP Unicast (Dst IP = local). Look deeper....
|
||||
// UDP (Port = Listed) and its a VRLP packet. Forward only to VITA Radio Core.
|
||||
// UDP (Port = Unknown). Forward only to ZPU.
|
||||
//
|
||||
//
|
||||
|
||||
module eth_dispatch
|
||||
#(parameter BASE=0)
|
||||
(
|
||||
// Clocking and reset interface
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Setting register interface
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
input [63:0] in_tdata,
|
||||
input [3:0] in_tuser,
|
||||
input in_tlast,
|
||||
input in_tvalid,
|
||||
output in_tready,
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
output [63:0] vita_tdata,
|
||||
output [3:0] vita_tuser,
|
||||
output vita_tlast,
|
||||
output vita_tvalid,
|
||||
input vita_tready,
|
||||
// Output AXI-Stream interface to ZPU
|
||||
output [63:0] zpu_tdata,
|
||||
output [3:0] zpu_tuser,
|
||||
output zpu_tlast,
|
||||
output zpu_tvalid,
|
||||
input zpu_tready,
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
output [63:0] xo_tdata,
|
||||
output [3:0] xo_tuser,
|
||||
output xo_tlast,
|
||||
output xo_tvalid,
|
||||
input xo_tready,
|
||||
// Debug
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
//
|
||||
// State machine declarations
|
||||
//
|
||||
reg [2:0] state;
|
||||
|
||||
localparam WAIT_PACKET = 0;
|
||||
localparam READ_HEADER = 1;
|
||||
localparam FORWARD_ZPU = 2;
|
||||
localparam FORWARD_ZPU_AND_XO = 3;
|
||||
localparam FORWARD_XO = 4;
|
||||
localparam FORWARD_RADIO_CORE = 5;
|
||||
localparam DROP_PACKET = 6;
|
||||
localparam CLASSIFY_PACKET = 7;
|
||||
|
||||
|
||||
//
|
||||
// Small RAM stores packet header during parsing.
|
||||
//
|
||||
localparam HEADER_RAM_SIZE = 9;
|
||||
(*ram_style="distributed"*)
|
||||
reg [68:0] header_ram [HEADER_RAM_SIZE-1:0];
|
||||
reg [3:0] header_ram_addr;
|
||||
reg drop_this_packet;
|
||||
|
||||
wire header_done = (header_ram_addr == HEADER_RAM_SIZE-1);
|
||||
reg fwd_input;
|
||||
|
||||
//
|
||||
reg [63:0] in_tdata_reg;
|
||||
|
||||
//
|
||||
wire out_tvalid;
|
||||
wire out_tready;
|
||||
wire out_tlast;
|
||||
wire [3:0] out_tuser;
|
||||
wire [63:0] out_tdata;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
wire [63:0] vita_pre_tdata;
|
||||
wire [3:0] vita_pre_tuser;
|
||||
wire vita_pre_tlast;
|
||||
wire vita_pre_tvalid;
|
||||
wire vita_pre_tready;
|
||||
// Output AXI-Stream interface to ZPU
|
||||
wire [63:0] zpu_pre_tdata;
|
||||
wire [3:0] zpu_pre_tuser;
|
||||
wire zpu_pre_tlast;
|
||||
wire zpu_pre_tvalid;
|
||||
wire zpu_pre_tready;
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
wire [63:0] xo_pre_tdata;
|
||||
wire [3:0] xo_pre_tuser;
|
||||
wire xo_pre_tlast;
|
||||
wire xo_pre_tvalid;
|
||||
wire xo_pre_tready;
|
||||
|
||||
//
|
||||
// Packet Parse Flags
|
||||
//
|
||||
reg is_eth_dst_addr;
|
||||
reg is_eth_broadcast;
|
||||
reg is_eth_type_ipv4;
|
||||
reg is_ipv4_dst_addr;
|
||||
reg is_ipv4_proto_udp;
|
||||
reg is_ipv4_proto_icmp;
|
||||
reg [1:0] is_udp_dst_ports;
|
||||
reg is_icmp_no_fwd;
|
||||
reg is_chdr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Settings regs
|
||||
//
|
||||
|
||||
wire [47:0] my_mac;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(16), .width(32)) sr_my_mac_lsb
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_mac[31:0]),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(16), .width(16)) sr_my_mac_msb
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_mac[47:32]),.changed());
|
||||
|
||||
wire [31:0] my_ip;
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(16), .width(32)) sr_my_ip
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(my_ip[31:0]),.changed());
|
||||
|
||||
wire [15:0] my_port0, my_port1;
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(16), .width(32)) sr_udp_port
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({my_port1[15:0],my_port0[15:0]}),.changed());
|
||||
|
||||
wire forward_ndest, forward_bcast;
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(16), .width(2)) sr_forward_ctrl
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({forward_ndest, forward_bcast}),.changed());
|
||||
|
||||
wire [7:0] my_icmp_type, my_icmp_code;
|
||||
setting_reg #(.my_addr(BASE+5), .awidth(16), .width(16)) sr_icmp_ctrl
|
||||
(.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out({my_icmp_type, my_icmp_code}),.changed());
|
||||
|
||||
assign debug =
|
||||
{
|
||||
1'b0, state, //4
|
||||
1'b0, in_tvalid, in_tready, in_tlast, //4
|
||||
1'b0, is_eth_dst_addr, is_eth_broadcast, is_eth_type_ipv4,
|
||||
is_ipv4_dst_addr, is_ipv4_proto_udp, is_udp_dst_ports, //8
|
||||
header_ram_addr[3:0], //4
|
||||
4'b0, 8'b0
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Packet Forwarding State machine.
|
||||
//
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset || clear) begin
|
||||
state <= WAIT_PACKET;
|
||||
header_ram_addr <= 0;
|
||||
drop_this_packet <= 0;
|
||||
fwd_input <= 0;
|
||||
end else begin
|
||||
// Defaults.
|
||||
drop_this_packet <= 0;
|
||||
|
||||
case(state)
|
||||
//
|
||||
// Wait for start of a packet
|
||||
// IJB: Add protection for a premature EOF here
|
||||
//
|
||||
WAIT_PACKET: begin
|
||||
if (in_tvalid && in_tready) begin
|
||||
header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata};
|
||||
header_ram_addr <= header_ram_addr + 1;
|
||||
state <= READ_HEADER;
|
||||
end
|
||||
fwd_input <= 0;
|
||||
end
|
||||
//
|
||||
// Continue to read full packet header into RAM.
|
||||
//
|
||||
READ_HEADER: begin
|
||||
if (in_tvalid && in_tready) begin
|
||||
header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata};
|
||||
// Have we reached end of fields we parse in header or got a short packet?
|
||||
if (header_done || in_tlast) begin
|
||||
// Make decision about where this packet is forwarded to.
|
||||
state <= CLASSIFY_PACKET;
|
||||
end // if (header_done || in_tlast)
|
||||
else begin
|
||||
header_ram_addr <= header_ram_addr + 1;
|
||||
state <= READ_HEADER;
|
||||
end // else: !if(header_done || in_tlast)
|
||||
end // if (in_tvalid && in_tready)
|
||||
end // case: READ_HEADER
|
||||
|
||||
//
|
||||
// Classify Packet
|
||||
//
|
||||
CLASSIFY_PACKET: begin
|
||||
// Make decision about where this packet is forwarded to.
|
||||
if (is_eth_type_ipv4 && is_ipv4_proto_icmp && is_icmp_no_fwd) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= FORWARD_ZPU;
|
||||
end else if (is_eth_broadcast) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= forward_bcast? FORWARD_ZPU_AND_XO : FORWARD_ZPU;
|
||||
end else if (!is_eth_dst_addr) begin
|
||||
header_ram_addr <= 0;
|
||||
state <= forward_ndest? FORWARD_XO : DROP_PACKET;
|
||||
end else if ((is_udp_dst_ports != 0) && is_chdr) begin
|
||||
header_ram_addr <= 6; // Jump to CHDR
|
||||
state <= FORWARD_RADIO_CORE;
|
||||
end else if (drop_this_packet) begin
|
||||
header_ram_addr <= HEADER_RAM_SIZE-1;
|
||||
state <= DROP_PACKET;
|
||||
end else begin
|
||||
header_ram_addr <= 0;
|
||||
state <= FORWARD_ZPU;
|
||||
end
|
||||
end // case: CLASSIFY_PACKET
|
||||
|
||||
//
|
||||
// Forward this packet only to local ZPU
|
||||
//
|
||||
FORWARD_ZPU: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to both local ZPU and XO
|
||||
//
|
||||
FORWARD_ZPU_AND_XO: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to XO only
|
||||
//
|
||||
FORWARD_XO: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Forward this packet to the Radio Core only
|
||||
//
|
||||
FORWARD_RADIO_CORE: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Drop this packet on the ground
|
||||
//
|
||||
DROP_PACKET: begin
|
||||
if (out_tvalid && out_tready) begin
|
||||
if (out_tlast) begin
|
||||
state <= WAIT_PACKET;
|
||||
end
|
||||
if (header_done) fwd_input <= 1;
|
||||
header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1;
|
||||
end
|
||||
end
|
||||
endcase // case (state)
|
||||
end // else: !if(reset || clear)
|
||||
|
||||
//
|
||||
// Classifier State machine.
|
||||
// Deep packet inspection during header ingress.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset || clear) begin
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
is_eth_broadcast <= 1'b0;
|
||||
is_eth_type_ipv4 <= 1'b0;
|
||||
is_ipv4_dst_addr <= 1'b0;
|
||||
is_ipv4_proto_udp <= 1'b0;
|
||||
is_ipv4_proto_icmp <= 1'b0;
|
||||
is_udp_dst_ports <= 0;
|
||||
is_icmp_no_fwd <= 0;
|
||||
is_chdr <= 1'b0;
|
||||
|
||||
// space_in_fifo <= 0;
|
||||
// is_there_fifo_space <= 1;
|
||||
// packet_length <= 0;
|
||||
end else if (in_tvalid && in_tready) begin // if (reset || clear)
|
||||
in_tdata_reg <= in_tdata;
|
||||
|
||||
case (header_ram_addr)
|
||||
// Pipelined, so nothing to look at first cycle.
|
||||
// Reset all the flags here.
|
||||
0: begin
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
is_eth_broadcast <= 1'b0;
|
||||
is_eth_type_ipv4 <= 1'b0;
|
||||
is_ipv4_dst_addr <= 1'b0;
|
||||
is_ipv4_proto_udp <= 1'b0;
|
||||
is_ipv4_proto_icmp <= 1'b0;
|
||||
is_udp_dst_ports <= 0;
|
||||
is_icmp_no_fwd <= 0;
|
||||
is_chdr <= 1'b0;
|
||||
end
|
||||
1: begin
|
||||
// Look at upper 16bits of MAC Dst Addr.
|
||||
if (in_tdata_reg[15:0] == 16'hFFFF)
|
||||
is_eth_broadcast <= 1'b1;
|
||||
if (in_tdata_reg[15:0] == my_mac[47:32])
|
||||
is_eth_dst_addr <= 1'b1;
|
||||
end
|
||||
2: begin
|
||||
// Look at lower 32bits of MAC Dst Addr.
|
||||
if (is_eth_broadcast && (in_tdata_reg[63:32] == 32'hFFFFFFFF))
|
||||
is_eth_broadcast <= 1'b1;
|
||||
else
|
||||
is_eth_broadcast <= 1'b0;
|
||||
if (is_eth_dst_addr && (in_tdata_reg[63:32] == my_mac[31:0]))
|
||||
is_eth_dst_addr <= 1'b1;
|
||||
else
|
||||
is_eth_dst_addr <= 1'b0;
|
||||
end // case: 2
|
||||
3: begin
|
||||
// Look at Ethertype
|
||||
if (in_tdata_reg[47:32] == 16'h0800)
|
||||
is_eth_type_ipv4 <= 1'b1;
|
||||
// Extract Packet Length
|
||||
// ADD THIS HERE.
|
||||
end
|
||||
4: begin
|
||||
// Look at protocol enapsulated by IPv4
|
||||
if ((in_tdata_reg[23:16] == 8'h11) && is_eth_type_ipv4)
|
||||
is_ipv4_proto_udp <= 1'b1;
|
||||
if ((in_tdata_reg[23:16] == 8'h01) && is_eth_type_ipv4)
|
||||
is_ipv4_proto_icmp <= 1'b1;
|
||||
end
|
||||
5: begin
|
||||
// Look at IP DST Address.
|
||||
if ((in_tdata_reg[31:0] == my_ip[31:0]) && is_eth_type_ipv4)
|
||||
is_ipv4_dst_addr <= 1'b1;
|
||||
end
|
||||
6: begin
|
||||
// Look at UDP dest port
|
||||
if ((in_tdata_reg[47:32] == my_port0[15:0]) && is_ipv4_proto_udp)
|
||||
is_udp_dst_ports[0] <= 1'b1;
|
||||
if ((in_tdata_reg[47:32] == my_port1[15:0]) && is_ipv4_proto_udp)
|
||||
is_udp_dst_ports[1] <= 1'b1;
|
||||
// Look at ICMP type and code
|
||||
if (in_tdata_reg[63:48] == {my_icmp_type, my_icmp_code} && is_ipv4_proto_icmp)
|
||||
is_icmp_no_fwd <= 1'b1;
|
||||
end
|
||||
7: begin
|
||||
// Look for a possible CHDR header string
|
||||
if (in_tdata_reg[63:32] != 32'h0)
|
||||
is_chdr <= 1'b1;
|
||||
end
|
||||
8: begin
|
||||
// Check VRT Stream ID
|
||||
// ADD THIS HERE.
|
||||
end
|
||||
endcase // case (header_ram_addr)
|
||||
end // if (in_tvalid && in_tready)
|
||||
|
||||
|
||||
//
|
||||
// Output (Egress) Interface muxing
|
||||
//
|
||||
assign out_tready =
|
||||
(state == DROP_PACKET) ||
|
||||
((state == FORWARD_RADIO_CORE) && vita_pre_tready) ||
|
||||
((state == FORWARD_XO) && xo_pre_tready) ||
|
||||
((state == FORWARD_ZPU) && zpu_pre_tready) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready && xo_pre_tready);
|
||||
|
||||
assign out_tvalid = ((state == FORWARD_RADIO_CORE) ||
|
||||
(state == FORWARD_XO) ||
|
||||
(state == FORWARD_ZPU) ||
|
||||
(state == FORWARD_ZPU_AND_XO) ||
|
||||
(state == DROP_PACKET)) && (!fwd_input || in_tvalid);
|
||||
|
||||
assign {out_tlast,out_tuser,out_tdata} = fwd_input ? {in_tlast,in_tuser,in_tdata} : header_ram[header_ram_addr];
|
||||
|
||||
assign in_tready = (state == WAIT_PACKET) ||
|
||||
(state == READ_HEADER) ||
|
||||
(state == DROP_PACKET) ||
|
||||
(out_tready && fwd_input);
|
||||
|
||||
|
||||
// Because we can forward to both the ZPU and XO FIFO's concurrently
|
||||
// we have to make sure both can accept data in the same cycle.
|
||||
// This makes it possible for either destination to block the other.
|
||||
assign xo_pre_tvalid = out_tvalid &&
|
||||
((state == FORWARD_XO) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready));
|
||||
assign zpu_pre_tvalid = out_tvalid &&
|
||||
((state == FORWARD_ZPU) ||
|
||||
((state == FORWARD_ZPU_AND_XO) && xo_pre_tready));
|
||||
assign vita_pre_tvalid = out_tvalid && (state == FORWARD_RADIO_CORE);
|
||||
|
||||
assign {zpu_pre_tuser,zpu_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_ZPU)) ?
|
||||
{out_tuser,out_tdata} : 0;
|
||||
|
||||
assign {xo_pre_tuser,xo_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_XO)) ?
|
||||
{out_tuser,out_tdata} : 0;
|
||||
|
||||
assign {vita_pre_tuser,vita_pre_tdata} = (state == FORWARD_RADIO_CORE) ? {out_tuser,out_tdata} : 0;
|
||||
|
||||
assign zpu_pre_tlast = out_tlast && ((state == FORWARD_ZPU) || (state == FORWARD_ZPU_AND_XO));
|
||||
|
||||
assign xo_pre_tlast = out_tlast && ((state == FORWARD_XO) || (state == FORWARD_ZPU_AND_XO));
|
||||
|
||||
assign vita_pre_tlast = out_tlast && (state == FORWARD_RADIO_CORE);
|
||||
|
||||
//
|
||||
// Egress FIFO's (Large)
|
||||
//
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_zpu (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({zpu_pre_tlast,zpu_pre_tuser,zpu_pre_tdata}),
|
||||
.i_tvalid(zpu_pre_tvalid),
|
||||
.i_tready(zpu_pre_tready),
|
||||
.o_tdata({zpu_tlast,zpu_tuser,zpu_tdata}),
|
||||
.o_tvalid(zpu_tvalid),
|
||||
.o_tready(zpu_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_xo (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({xo_pre_tlast,xo_pre_tuser,xo_pre_tdata}),
|
||||
.i_tvalid(xo_pre_tvalid),
|
||||
.i_tready(xo_pre_tready),
|
||||
.o_tdata({xo_tlast,xo_tuser,xo_tdata}),
|
||||
.o_tvalid(xo_tvalid),
|
||||
.o_tready(xo_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(10))
|
||||
axi_fifo_vita (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.i_tdata({vita_pre_tlast,vita_pre_tuser,vita_pre_tdata}),
|
||||
.i_tvalid(vita_pre_tvalid),
|
||||
.i_tready(vita_pre_tready),
|
||||
.o_tdata({vita_tlast,vita_tuser,vita_tdata}),
|
||||
.o_tvalid(vita_tvalid),
|
||||
.o_tready(vita_tready),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
|
||||
/* -----\/----- EXCLUDED -----\/-----
|
||||
|
||||
wire vready, zready, oready;
|
||||
wire vvalid, zvalid, ovalid;
|
||||
|
||||
reg [2:0] ed_state;
|
||||
localparam ED_IDLE = 3'd0;
|
||||
localparam ED_IN_HDR = 3'd1;
|
||||
localparam ED_VITA = 3'd2;
|
||||
localparam ED_ZPU = 3'd3;
|
||||
localparam ED_OUT = 3'd4;
|
||||
localparam ED_DROP = 3'd5;
|
||||
-----/\----- EXCLUDED -----/\----- */
|
||||
|
||||
// for now, send everything to zpu
|
||||
/*
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
ed_state <= ED_IDLE;
|
||||
else
|
||||
case(ed_state)
|
||||
ED_IDLE:
|
||||
if(vready & zready & oready & in_tvalid)
|
||||
;
|
||||
endcase // case (ed_state)
|
||||
*/
|
||||
|
||||
/* -----\/----- EXCLUDED -----\/-----
|
||||
axi_packet_gate #(.WIDTH(64), .SIZE(10)) vita_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(in_tdata), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(vready),
|
||||
.o_tdata(vita_tdata), .o_tlast(vita_tlast), .o_tvalid(vita_tvalid), .o_tready(vita_tready));
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) zpu_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({in_tuser,in_tdata}), .i_tlast(in_tlast), .i_terror(in_tuser[3]), .i_tvalid(in_tvalid), .i_tready(in_tready),
|
||||
.o_tdata({zpu_tuser,zpu_tdata}), .o_tlast(zpu_tlast), .o_tvalid(zpu_tvalid), .o_tready(zpu_tready));
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) out_gate
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({in_tuser,in_tdata}), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(oready),
|
||||
.o_tdata({out_tuser,out_tdata}), .o_tlast(out_tlast), .o_tvalid(out_tvalid), .o_tready(out_tready));
|
||||
-----/\----- EXCLUDED -----/\----- */
|
||||
|
||||
endmodule // eth_dispatch
|
||||
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
//
|
||||
|
||||
`timescale 1 ps / 1 ps
|
||||
|
||||
module eth_dispatch_tb();
|
||||
|
||||
|
||||
// Clocking and reset interface
|
||||
reg clk;
|
||||
reg reset;
|
||||
reg clear;
|
||||
// Setting register interface
|
||||
reg set_stb;
|
||||
reg [15:0] set_addr;
|
||||
reg [31:0] set_data;
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
wire [63:0] in_tdata;
|
||||
wire [3:0] in_tuser;
|
||||
wire in_tlast;
|
||||
wire in_tvalid;
|
||||
wire in_tready;
|
||||
// Output AXI-Stream interface to VITA Radio Core
|
||||
wire [63:0] vita_tdata;
|
||||
wire [3:0] vita_tuser;
|
||||
wire vita_tlast;
|
||||
wire vita_tvalid;
|
||||
wire vita_tready;
|
||||
// Output AXI-Stream interface to ZPU
|
||||
wire [63:0] zpu_tdata;
|
||||
wire [3:0] zpu_tuser;
|
||||
wire zpu_tlast;
|
||||
wire zpu_tvalid;
|
||||
wire zpu_tready;
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
wire [63:0] xo_tdata;
|
||||
wire [3:0] xo_tuser;
|
||||
wire xo_tlast;
|
||||
wire xo_tvalid;
|
||||
wire xo_tready;
|
||||
|
||||
reg [63:0] data_in;
|
||||
reg [3:0] user_in;
|
||||
reg valid_in;
|
||||
wire ready_in;
|
||||
reg last_in;
|
||||
|
||||
eth_dispatch
|
||||
#(.BASE(0))
|
||||
eth_dispatch_i
|
||||
(
|
||||
// Clocking and reset interface
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Setting register interface
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
// Input 68bit AXI-Stream interface (from MAC)
|
||||
.in_tdata(in_tdata),
|
||||
.in_tuser(in_tuser),
|
||||
.in_tlast(in_tlast),
|
||||
.in_tvalid(in_tvalid),
|
||||
.in_tready(in_tready),
|
||||
// Output AXI-STream interface to VITA Radio Core
|
||||
.vita_tdata(vita_tdata),
|
||||
.vita_tuser(vita_tuser),
|
||||
.vita_tlast(vita_tlast),
|
||||
.vita_tvalid(vita_tvalid),
|
||||
.vita_tready(vita_tready),
|
||||
// Output AXI-Stream interface to ZPU
|
||||
.zpu_tdata(zpu_tdata),
|
||||
.zpu_tuser(zpu_tuser),
|
||||
.zpu_tlast(zpu_tlast),
|
||||
.zpu_tvalid(zpu_tvalid),
|
||||
.zpu_tready(zpu_tready),
|
||||
// Output AXI-Stream interface to cross-over MAC
|
||||
.xo_tdata(xo_tdata),
|
||||
.xo_tuser(xo_tuser),
|
||||
.xo_tlast(xo_tlast),
|
||||
.xo_tvalid(xo_tvalid),
|
||||
.xo_tready(xo_tready)
|
||||
);
|
||||
|
||||
//
|
||||
// Define Clocks
|
||||
//
|
||||
initial begin
|
||||
clk = 1'b1;
|
||||
end
|
||||
|
||||
// 125MHz clock
|
||||
always #4000 clk = ~clk;
|
||||
|
||||
//
|
||||
// Good starting state
|
||||
//
|
||||
initial begin
|
||||
reset <= 0;
|
||||
clear <= 0;
|
||||
set_stb <= 0;
|
||||
set_addr <= 0;
|
||||
set_data <= 0;
|
||||
data_in <= 0;
|
||||
user_in <= 0;
|
||||
valid_in <= 0;
|
||||
last_in <= 0;
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Task Libaray
|
||||
//
|
||||
task write_setting_bus;
|
||||
input [15:0] address;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
|
||||
@(negedge clk);
|
||||
set_stb = 1'b0;
|
||||
set_addr = 16'h0;
|
||||
set_data = 32'h0;
|
||||
@(negedge clk);
|
||||
set_stb = 1'b1;
|
||||
set_addr = address;
|
||||
set_data = data;
|
||||
@(negedge clk);
|
||||
set_stb = 1'b0;
|
||||
set_addr = 16'h0;
|
||||
set_data = 32'h0;
|
||||
|
||||
end
|
||||
endtask // write_setting_bus
|
||||
|
||||
|
||||
task enqueue_line;
|
||||
input last;
|
||||
input [2:0] keep;
|
||||
input [63:0] data;
|
||||
begin
|
||||
data_in <= {keep, data};
|
||||
last_in <= last;
|
||||
valid_in <= 1;
|
||||
while (~ready_in) begin
|
||||
@(negedge clk);
|
||||
end
|
||||
@(negedge clk);
|
||||
data_in <= 0;
|
||||
last_in <= 0;
|
||||
valid_in <= 0;
|
||||
end
|
||||
endtask // enqueue_line
|
||||
|
||||
task enqueue_arp_req;
|
||||
input [47:0] src_mac;
|
||||
input [31:0] src_ip;
|
||||
input [47:0] dst_mac;
|
||||
input [31:0] dst_ip;
|
||||
|
||||
begin
|
||||
@(negedge clk);
|
||||
// Line 0
|
||||
enqueue_line( 0, 3'b0, {48'h0,16'hffff});
|
||||
// Line 1 - Eth
|
||||
enqueue_line( 0, 3'b0, {32'hffffffff,src_mac[47:16]});
|
||||
// Line 2 - Eth+ARP (HTYPE = 1, PTYPE = 0x0800)
|
||||
enqueue_line( 0, 3'b0, {src_mac[15:0],16'h0806,16'h0001,16'h0800});
|
||||
// Line 3 - HLEN=6, PLEN=4 OPER=1
|
||||
enqueue_line( 0, 3'b0, {8'h06,8'h04,16'h0001,src_mac[47:16]});
|
||||
// Line 4 - ARP
|
||||
enqueue_line( 0, 3'b0, {src_mac[15:0],src_ip[31:0],dst_mac[47:32]});
|
||||
// Line 5 - ARP
|
||||
enqueue_line( 1, 3'b0, {dst_mac[31:0],dst_ip[31:0]});
|
||||
end
|
||||
endtask // enqueue_arp_req
|
||||
|
||||
|
||||
|
||||
reg [11:0] frame_count =12'h0;
|
||||
|
||||
task enqueue_vita_pkt;
|
||||
// We assume that we always have SID and TSF fields.
|
||||
input [47:0] mac;
|
||||
input [31:0] ip;
|
||||
input [15:0] udp;
|
||||
input [15:0] vita_size;
|
||||
input [63:0] vita_tsf;
|
||||
input [31:0] vita_sid;
|
||||
|
||||
|
||||
integer i;
|
||||
reg [15:0] j;
|
||||
reg [19:0] vrl_size;
|
||||
reg [15:0] udp_size;
|
||||
reg [15:0] ip_size;
|
||||
|
||||
|
||||
begin
|
||||
vrl_size = vita_size + 3;
|
||||
udp_size = vrl_size*4 + 8;
|
||||
ip_size = udp_size + 20;
|
||||
@(negedge clk);
|
||||
// Line 0
|
||||
enqueue_line( 0, 3'b0, {48'h0,mac[47:32]});
|
||||
// Line 1 - Eth
|
||||
enqueue_line( 0, 3'b0, {mac[31:0],32'h11223344});
|
||||
// Line 2 - Eth+IP
|
||||
enqueue_line( 0, 3'b0, {16'h5566,16'h0800,16'h0000,ip_size});
|
||||
// Line 3 - IP
|
||||
enqueue_line( 0, 3'b0, 'h11<<16);
|
||||
// Line 4 - IP
|
||||
enqueue_line( 0, 3'b0, {32'h09080706, ip});
|
||||
// Line 5 - UDP
|
||||
enqueue_line( 0, 3'b0, {16'h1234, udp, udp_size, 16'h0});
|
||||
// Line 6 - VRL
|
||||
enqueue_line( 0, 3'b0, {"VRLP",frame_count,vrl_size});
|
||||
// Line 7 - VRT
|
||||
enqueue_line( 0, 3'b0, {16'b0001000000010000, vita_size,vita_sid}); //vita hdr + SID
|
||||
enqueue_line( 0, 3'b0, vita_tsf);
|
||||
j = 0;
|
||||
|
||||
for (i = 6; i < vita_size; i = i + 2) begin
|
||||
enqueue_line( 0 , 3'b0, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
j = j + 4;
|
||||
end
|
||||
|
||||
if (i-vita_size==0) // 2x32words to finish VITA packet.
|
||||
enqueue_line( 1, 3'b0, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
else // 1x32bit word to finish VITA packet
|
||||
enqueue_line( 1, 3'h4, {j,j+16'h1,j+16'h2,j+16'h3});
|
||||
end
|
||||
|
||||
endtask // enqueue_packet
|
||||
|
||||
|
||||
//
|
||||
// Simulation specific testbench is included here
|
||||
//
|
||||
`include "simulation_script.v"
|
||||
|
||||
|
||||
//
|
||||
// Input FIFO
|
||||
//
|
||||
axi_fifo_short
|
||||
#(.WIDTH(69)) axi_fifo_short_in
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.o_tdata({in_tlast,in_tuser,in_tdata}),
|
||||
.o_tvalid(in_tvalid),
|
||||
.o_tready(in_tready),
|
||||
.i_tdata({last_in,user_in,data_in}),
|
||||
.i_tvalid(valid_in),
|
||||
.i_tready(ready_in),
|
||||
.space(),
|
||||
.occupied()
|
||||
);
|
||||
|
||||
//
|
||||
// Output Sinks
|
||||
//
|
||||
axi_probe_tb
|
||||
#(.FILENAME("zpu.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_zpu
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(zpu_tdata),
|
||||
.tvalid(zpu_tvalid),
|
||||
.tready(zpu_tready),
|
||||
.tlast(zpu_tlast)
|
||||
);
|
||||
|
||||
assign zpu_tready = 1'b1;
|
||||
|
||||
axi_probe_tb
|
||||
#(.FILENAME("xo.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_xo
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(xo_tdata),
|
||||
.tvalid(xo_tvalid),
|
||||
.tready(xo_tready),
|
||||
.tlast(xo_tlast)
|
||||
);
|
||||
|
||||
assign xo_tready = 1'b1;
|
||||
|
||||
axi_probe_tb
|
||||
#(.FILENAME("vita.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001),.START_AT_VRL(1)) axi_probe_tb_vita
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.tdata(vita_tdata),
|
||||
.tvalid(vita_tvalid),
|
||||
.tready(vita_tready),
|
||||
.tlast(vita_tlast)
|
||||
);
|
||||
|
||||
assign vita_tready = 1'b1;
|
||||
|
||||
endmodule // eth_dispatch_tb
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
// Adapts from internal VITA to ethernet packets. Also handles ZPU and ethernet crossover interfaces.
|
||||
|
||||
module eth_interface
|
||||
#(parameter BASE=0,
|
||||
parameter XO_FIFOSIZE=10,
|
||||
parameter ZPU_FIFOSIZE=10,
|
||||
parameter VITA_FIFOSIZE=10,
|
||||
parameter ETHOUT_FIFOSIZE=10)
|
||||
(input clk, input reset, input clear,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
// Eth ports
|
||||
output [63:0] eth_tx_tdata, output [3:0] eth_tx_tuser, output eth_tx_tlast, output eth_tx_tvalid, input eth_tx_tready,
|
||||
input [63:0] eth_rx_tdata, input [3:0] eth_rx_tuser, input eth_rx_tlast, input eth_rx_tvalid, output eth_rx_tready,
|
||||
// Vita router interface
|
||||
output [63:0] e2v_tdata, output e2v_tlast, output e2v_tvalid, input e2v_tready,
|
||||
input [63:0] v2e_tdata, input v2e_tlast, input v2e_tvalid, output v2e_tready,
|
||||
// Ethernet crossover
|
||||
output [63:0] xo_tdata, output [3:0] xo_tuser, output xo_tlast, output xo_tvalid, input xo_tready,
|
||||
input [63:0] xi_tdata, input [3:0] xi_tuser, input xi_tlast, input xi_tvalid, output xi_tready,
|
||||
// ZPU
|
||||
output [63:0] e2z_tdata, output [3:0] e2z_tuser, output e2z_tlast, output e2z_tvalid, input e2z_tready,
|
||||
input [63:0] z2e_tdata, input [3:0] z2e_tuser, input z2e_tlast, input z2e_tvalid, output z2e_tready,
|
||||
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
wire [63:0] v2ef_tdata;
|
||||
wire [3:0] v2ef_tuser;
|
||||
wire v2ef_tlast, v2ef_tvalid, v2ef_tready;
|
||||
|
||||
// //////////////////////////////////////////////////////////////
|
||||
// Incoming Ethernet path
|
||||
// Includes FIFO on the output going to ZPU
|
||||
|
||||
wire [63:0] epg_tdata_int;
|
||||
wire [3:0] epg_tuser_int;
|
||||
wire epg_tlast_int, epg_tvalid_int, epg_tready_int;
|
||||
|
||||
axi_packet_gate #(.WIDTH(68), .SIZE(10)) packet_gater //holds 8K pkts
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
|
||||
.i_tdata({eth_rx_tuser, eth_rx_tdata}), .i_tlast(eth_rx_tlast),
|
||||
.i_terror(eth_rx_tuser[3]), //top bit of user bus is error
|
||||
.i_tvalid(eth_rx_tvalid), .i_tready(eth_rx_tready),
|
||||
|
||||
.o_tdata({epg_tuser_int, epg_tdata_int}), .o_tlast(epg_tlast_int),
|
||||
.o_tvalid(epg_tvalid_int), .o_tready(epg_tready_int));
|
||||
|
||||
wire [63:0] e2z_tdata_int;
|
||||
wire [3:0] e2z_tuser_int;
|
||||
wire e2z_tlast_int, e2z_tvalid_int, e2z_tready_int;
|
||||
|
||||
eth_dispatch #(.BASE(BASE+8)) eth_dispatch
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data),
|
||||
.in_tdata(epg_tdata_int), .in_tuser(epg_tuser_int), .in_tlast(epg_tlast_int), .in_tvalid(epg_tvalid_int), .in_tready(epg_tready_int),
|
||||
.vita_tdata(e2v_tdata), .vita_tlast(e2v_tlast), .vita_tvalid(e2v_tvalid), .vita_tready(e2v_tready),
|
||||
.zpu_tdata(e2z_tdata_int), .zpu_tuser(e2z_tuser_int), .zpu_tlast(e2z_tlast_int), .zpu_tvalid(e2z_tvalid_int), .zpu_tready(e2z_tready_int),
|
||||
.xo_tdata(xo_tdata), .xo_tuser(xo_tuser), .xo_tlast(xo_tlast), .xo_tvalid(xo_tvalid), .xo_tready(xo_tready), // to other eth port
|
||||
.debug(debug));
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(ZPU_FIFOSIZE)) zpu_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({e2z_tlast_int,e2z_tuser_int,e2z_tdata_int}), .i_tvalid(e2z_tvalid_int), .i_tready(e2z_tready_int),
|
||||
.o_tdata({e2z_tlast,e2z_tuser,e2z_tdata}), .o_tvalid(e2z_tvalid), .o_tready(e2z_tready));
|
||||
|
||||
// //////////////////////////////////////////////////////////////
|
||||
// Outgoing Ethernet path
|
||||
// Includes FIFOs on path from VITA router, from ethernet crossover, and on the overall output
|
||||
|
||||
wire [63:0] eth_tx_tdata_int;
|
||||
wire [3:0] eth_tx_tuser_int;
|
||||
wire eth_tx_tlast_int, eth_tx_tvalid_int, eth_tx_tready_int;
|
||||
|
||||
wire [63:0] xi_tdata_int;
|
||||
wire [3:0] xi_tuser_int;
|
||||
wire xi_tlast_int, xi_tvalid_int, xi_tready_int;
|
||||
|
||||
wire [63:0] v2e_tdata_int;
|
||||
wire v2e_tlast_int, v2e_tvalid_int, v2e_tready_int;
|
||||
|
||||
axi_fifo #(.WIDTH(65),.SIZE(VITA_FIFOSIZE)) vitaout_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({v2e_tlast,v2e_tdata}), .i_tvalid(v2e_tvalid), .i_tready(v2e_tready),
|
||||
.o_tdata({v2e_tlast_int,v2e_tdata_int}), .o_tvalid(v2e_tvalid_int), .o_tready(v2e_tready_int));
|
||||
|
||||
chdr_eth_framer #(.BASE(BASE)) my_eth_framer
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data),
|
||||
.in_tdata(v2e_tdata_int), .in_tlast(v2e_tlast_int), .in_tvalid(v2e_tvalid_int), .in_tready(v2e_tready_int),
|
||||
.out_tdata(v2ef_tdata), .out_tuser(v2ef_tuser), .out_tlast(v2ef_tlast), .out_tvalid(v2ef_tvalid), .out_tready(v2ef_tready),
|
||||
.debug());
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(XO_FIFOSIZE)) xo_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({xi_tlast,xi_tuser,xi_tdata}), .i_tvalid(xi_tvalid), .i_tready(xi_tready),
|
||||
.o_tdata({xi_tlast_int,xi_tuser_int,xi_tdata_int}), .o_tvalid(xi_tvalid_int), .o_tready(xi_tready_int));
|
||||
|
||||
axi_mux4 #(.PRIO(0), .WIDTH(68)) eth_mux
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i0_tdata({z2e_tuser,z2e_tdata}), .i0_tlast(z2e_tlast), .i0_tvalid(z2e_tvalid), .i0_tready(z2e_tready),
|
||||
.i1_tdata({v2ef_tuser,v2ef_tdata}), .i1_tlast(v2ef_tlast), .i1_tvalid(v2ef_tvalid), .i1_tready(v2ef_tready),
|
||||
.i2_tdata({xi_tuser_int,xi_tdata_int}), .i2_tlast(xi_tlast_int), .i2_tvalid(xi_tvalid_int), .i2_tready(xi_tready_int),
|
||||
.i3_tdata(), .i3_tlast(), .i3_tvalid(1'b0), .i3_tready(),
|
||||
.o_tdata({eth_tx_tuser_int,eth_tx_tdata_int}), .o_tlast(eth_tx_tlast_int), .o_tvalid(eth_tx_tvalid_int), .o_tready(eth_tx_tready_int));
|
||||
|
||||
axi_fifo #(.WIDTH(69),.SIZE(ETHOUT_FIFOSIZE)) ethout_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({eth_tx_tlast_int,eth_tx_tuser_int,eth_tx_tdata_int}), .i_tvalid(eth_tx_tvalid_int), .i_tready(eth_tx_tready_int),
|
||||
.o_tdata({eth_tx_tlast,eth_tx_tuser,eth_tx_tdata}), .o_tvalid(eth_tx_tvalid), .o_tready(eth_tx_tready));
|
||||
|
||||
endmodule // eth_interface
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
// Compute IP header checksum. 2 cycles of latency.
|
||||
module ip_hdr_checksum
|
||||
(input clk, input [159:0] in, output reg [15:0] out);
|
||||
|
||||
wire [18:0] padded [0:9];
|
||||
reg [18:0] sum_a, sum_b;
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for(i=0 ; i<10 ; i=i+1)
|
||||
assign padded[i] = {3'b000,in[i*16+15:i*16]};
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk) sum_a = padded[0] + padded[1] + padded[2] + padded[3] + padded[4];
|
||||
always @(posedge clk) sum_b = padded[5] + padded[6] + padded[7] + padded[8] + padded[9];
|
||||
|
||||
wire [18:0] sum = sum_a + sum_b;
|
||||
|
||||
always @(posedge clk)
|
||||
out <= ~(sum[15:0] + {13'd0,sum[18:16]});
|
||||
|
||||
|
||||
endmodule // ip_hdr_checksum
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
module ip_hdr_checksum_tb();
|
||||
|
||||
initial $dumpfile("ip_hdr_checksum_tb.vcd");
|
||||
initial $dumpvars(0,ip_hdr_checksum_tb);
|
||||
|
||||
reg clk;
|
||||
|
||||
wire [159:0] in = {
|
||||
16'h4500,
|
||||
16'h0030,
|
||||
16'h4422,
|
||||
16'h4000,
|
||||
16'h8006,
|
||||
16'h0000,
|
||||
16'h8c7c,
|
||||
16'h19ac,
|
||||
16'hae24,
|
||||
16'h1e2b
|
||||
};
|
||||
|
||||
wire [15:0] out;
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk),
|
||||
.in(in),
|
||||
.out(out));
|
||||
|
||||
initial
|
||||
begin
|
||||
clk <= 0;
|
||||
#100 clk <= 1;
|
||||
#100 clk <= 0;
|
||||
#100 clk <= 1;
|
||||
#100 $display("Computed 0x%x, should be 0x442e", out);
|
||||
#100 $finish;
|
||||
end
|
||||
|
||||
endmodule // ip_hdr_checksum_tb
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
// source_flow_control.v
|
||||
//
|
||||
// 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
|
||||
#(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);
|
||||
|
||||
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;
|
||||
wire setting_changed;
|
||||
|
||||
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_changed));
|
||||
|
||||
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 [1:0] sfc_state;
|
||||
|
||||
|
||||
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 fc_tready = 1'b1; // Always consume FC -- FIXME Even if we are getting reset?
|
||||
assign out_tdata = in_tdata; // CHDR data flows through combinatorially.
|
||||
assign out_tlast = in_tlast;
|
||||
assign in_tready = go ? out_tready : 1'b0;
|
||||
assign out_tvalid = go ? in_tvalid : 1'b0;
|
||||
|
||||
//
|
||||
// Each time we recieve 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_reset)
|
||||
current_seqnum <= 0;
|
||||
else if (in_tvalid && in_tready && in_tlast)
|
||||
current_seqnum <= current_seqnum + 1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
go <= 1'b0;
|
||||
else
|
||||
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))
|
||||
// if(in_tvalid & (go_until_seqnum > current_seqnum)) // FIXME will need to handle wrap of 32-bit seqnum
|
||||
go <= 1'b1;
|
||||
|
||||
1'b1 :
|
||||
if(in_tvalid & in_tready & in_tlast)
|
||||
go <= 1'b0;
|
||||
endcase // case (go)
|
||||
|
||||
|
||||
endmodule // source_flow_control
|
||||
@@ -0,0 +1,249 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module source_flow_control_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("source_flow_control_tb.vcd");
|
||||
initial $dumpvars(0,source_flow_control_tb);
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#20000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] tdata;
|
||||
wire [63:0] tdata_int;
|
||||
reg tlast;
|
||||
wire tlast_int;
|
||||
reg tvalid = 1'b0;
|
||||
wire tvalid_int;
|
||||
wire tready, tready_int;
|
||||
|
||||
reg [63:0] fc_tdata;
|
||||
reg fc_tlast, fc_tvalid;
|
||||
wire fc_tready;
|
||||
|
||||
wire [63:0] out_tdata;
|
||||
wire out_tlast, out_tready, out_tvalid;
|
||||
|
||||
wire [15:0] occ_in, occ_out;
|
||||
reg set_stb = 0;
|
||||
reg [7:0] set_addr;
|
||||
reg [31:0] set_data;
|
||||
|
||||
|
||||
task send_fc_packet;
|
||||
input [31:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input always_go;
|
||||
|
||||
begin
|
||||
@(posedge clk);
|
||||
fc_tlast <= 1'b0;
|
||||
fc_tdata <= { 1'b1, 1'b0, 1'b0, 1'b0, 12'hABC, 16'd4, sid };
|
||||
fc_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
fc_tlast <= 1'b1;
|
||||
//fc_tdata <= { 52'h0,seqnum };
|
||||
fc_tdata <= { 31'h0,always_go, seqnum };
|
||||
@(posedge clk);
|
||||
fc_tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task send_packet;
|
||||
input ec;
|
||||
input timed;
|
||||
input [11:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input [63:0] vtime;
|
||||
input [15:0] addr;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
|
||||
tvalid <= 1;
|
||||
@(posedge clk);
|
||||
if(timed)
|
||||
begin
|
||||
tdata <= vtime;
|
||||
@(posedge clk);
|
||||
end
|
||||
tlast <= 1'b1;
|
||||
tdata <= { 16'h0, addr, data };
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
tvalid <= 1'b0;
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
// Set flow control window to be 2
|
||||
set_stb <= 1;
|
||||
set_addr <= 0;
|
||||
set_data <= 2;
|
||||
@(posedge clk);
|
||||
set_stb <= 0;
|
||||
// ExtContext. Time. Seq=0, SID=DEAD_6789, Time=10
|
||||
send_packet(1'b1,1'b1,12'h0,32'hDEAD_6789,64'h10,16'hB,32'hF00D_1234);
|
||||
send_packet(1'b1,1'b1,12'h1,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h2,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h3,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h4,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h5,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h8,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd1,32'h3,1'b0);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd2,32'h3,1'b0);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd4,32'h3,1'b0);
|
||||
#400;
|
||||
// Send same SEQ ID again to test it causes no changes.
|
||||
send_fc_packet(32'd4,32'h3,1'b0);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd5,32'h3,1'b0);
|
||||
#500;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'd7,32'h3,1'b0);
|
||||
#500;
|
||||
send_packet(1'b1,1'b1,12'h9,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hA,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
#300;
|
||||
// Consumed 1 packet
|
||||
send_fc_packet(32'd8,32'h3,1'b0);
|
||||
//
|
||||
// Now force internal sequence count to close to wrap value to test corner case
|
||||
//
|
||||
#100;
|
||||
source_flow_control.current_seqnum <= 32'hFFFF_FFFC;
|
||||
#100;
|
||||
send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0);
|
||||
#100;
|
||||
send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0);
|
||||
send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'h0,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 2 packets
|
||||
send_fc_packet(32'h2,32'h3,1'b0);
|
||||
#500;
|
||||
//
|
||||
// Again force internal sequence count to close to wrap value to test new corner case
|
||||
//
|
||||
#100;
|
||||
source_flow_control.current_seqnum <= 32'hFFFF_FFFC;
|
||||
#100;
|
||||
send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0);
|
||||
#100;
|
||||
send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFB,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0);
|
||||
send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFD,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'hFFFF_FFFF,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h0,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h1,32'h3,1'b0);
|
||||
#200;
|
||||
// Consumed 1 packets
|
||||
send_fc_packet(32'h2,32'h3,1'b0);
|
||||
#500;
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_in
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
|
||||
.o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int),
|
||||
.occupied(occ_in));
|
||||
|
||||
source_flow_control source_flow_control
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.fc_tdata(fc_tdata), .fc_tlast(fc_tlast), .fc_tvalid(fc_tvalid), .fc_tready(fc_tready),
|
||||
.in_tdata(tdata_int), .in_tlast(tlast_int), .in_tvalid(tvalid_int), .in_tready(tready_int),
|
||||
.out_tdata(out_tdata), .out_tlast(out_tlast), .out_tvalid(out_tvalid), .out_tready(out_tready)
|
||||
);
|
||||
|
||||
wire [63:0] dump_tdata;
|
||||
wire dump_tlast, dump_tvalid, dump_tready;
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({out_tlast,out_tdata}), .i_tvalid(out_tvalid), .i_tready(out_tready),
|
||||
.o_tdata({dump_tlast,dump_tdata}), .o_tvalid(dump_tvalid), .o_tready(dump_tready),
|
||||
.occupied(occ_out));
|
||||
|
||||
assign dump_tready = 0;
|
||||
|
||||
always @(posedge clk)
|
||||
if(out_tvalid & out_tready)
|
||||
begin
|
||||
$display("%x",out_tdata);
|
||||
if(out_tlast)
|
||||
$display("TLAST");
|
||||
end
|
||||
endmodule // source_flow_control_tb
|
||||
@@ -0,0 +1,137 @@
|
||||
|
||||
// vita_eth_framer
|
||||
// Takes a vita stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
|
||||
module vita_eth_framer
|
||||
#(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] in_tdata, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [31:0] sid;
|
||||
reg [15:0] vita_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 32'd0;
|
||||
vita_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tdata[31:0];
|
||||
vita_len <= in_tdata[47:32];
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vita_len[0],2'b00} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [15:0] vita_len_in_bytes = {vita_len[13:0],2'b00}; // Vita length is in 32-bit words
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + vita_len_in_bytes); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + vita_len_in_bytes);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // vita_eth_framer
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
// vrlp_eth_framer
|
||||
// Takes a vrlp stream in and adds udp, ip, and ethernet framing
|
||||
// Uses 8 setting reg addresses. First 4 are simple registers:
|
||||
// BASE+0 : Upper 16 bits of ethernet src mac
|
||||
// BASE+1 : Lower 32 bits of ethernet src mac
|
||||
// BASE+2 : IP src address
|
||||
// BASE+3 : UDP src port
|
||||
//
|
||||
// Next 4 control write ports on a RAM indexed by destination field of stream ID
|
||||
// BASE+4 : Dest SID for next 3 regs
|
||||
// BASE+5 : Dest IP
|
||||
// BASE+6 : Dest UDP port, upper 16 bits of dest mac
|
||||
// BASE+7 : Lower 32 bits of dest mac
|
||||
//
|
||||
// in_tuser holds streamid
|
||||
|
||||
module vrlp_eth_framer
|
||||
#(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] in_tdata, input [15:0] in_tuser, input in_tlast, input in_tvalid, output in_tready,
|
||||
output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready,
|
||||
output [31:0] debug );
|
||||
|
||||
localparam SR_AWIDTH = 8;
|
||||
|
||||
reg [15:0] sid;
|
||||
reg [15:0] vrlp_len;
|
||||
|
||||
reg [2:0] vef_state;
|
||||
localparam VEF_IDLE = 3'd0;
|
||||
localparam VEF_PAYLOAD = 3'd7;
|
||||
|
||||
reg [63:0] tdata;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
vef_state <= VEF_IDLE;
|
||||
sid <= 16'd0;
|
||||
vrlp_len <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(vef_state)
|
||||
VEF_IDLE :
|
||||
if(in_tvalid)
|
||||
begin
|
||||
vef_state <= 1;
|
||||
sid <= in_tuser[15:0];
|
||||
vrlp_len <= in_tdata[15:0]; // modified for VRLP header
|
||||
end
|
||||
VEF_PAYLOAD :
|
||||
if(in_tvalid & out_tready)
|
||||
if(in_tlast)
|
||||
vef_state <= VEF_IDLE;
|
||||
default :
|
||||
if(out_tready)
|
||||
vef_state <= vef_state + 3'd1;
|
||||
endcase // case (vef_state)
|
||||
|
||||
wire [15:0] vrlp_len_in_bytes = {vrlp_len[13:0],2'b00}; // Vrlp length is in 32-bit words
|
||||
|
||||
assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0;
|
||||
assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1;
|
||||
assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0;
|
||||
assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vrlp_len_in_bytes[2:0]} : 4'b0000;
|
||||
assign out_tdata = tdata;
|
||||
|
||||
wire [47:0] pad = 48'h0;
|
||||
wire [47:0] mac_src, mac_dst;
|
||||
wire [15:0] eth_type = 16'h0800;
|
||||
wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
|
||||
wire [15:0] ip_len = (16'd28 + vrlp_len_in_bytes); // 20 for IP, 8 for UDP
|
||||
wire [15:0] ident = 16'h0;
|
||||
wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
|
||||
wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
|
||||
wire [15:0] iphdr_checksum;
|
||||
wire [31:0] ip_src, ip_dst;
|
||||
wire [15:0] udp_src, udp_dst;
|
||||
wire [15:0] udp_len = (16'd8 + vrlp_len_in_bytes);
|
||||
wire [15:0] udp_checksum = 16'h0;
|
||||
|
||||
setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[47:32]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(mac_src[31:0]), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ip_src), .changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(udp_src), .changed());
|
||||
|
||||
// Tables of MAC/IP/UDP addresses
|
||||
wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr
|
||||
(.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(ram_addr), .changed());
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]}));
|
||||
|
||||
ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower
|
||||
(.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(),
|
||||
.clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0]));
|
||||
|
||||
ip_hdr_checksum ip_hdr_checksum
|
||||
(.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}),
|
||||
.out(iphdr_checksum));
|
||||
|
||||
always @*
|
||||
case(vef_state)
|
||||
1 : tdata <= { pad[47:0], mac_dst[47:32]};
|
||||
2 : tdata <= { mac_dst[31:0], mac_src[47:16]};
|
||||
3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] };
|
||||
4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]};
|
||||
5 : tdata <= { ip_src, ip_dst};
|
||||
6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum};
|
||||
default : tdata <= in_tdata;
|
||||
endcase // case (vef_state)
|
||||
|
||||
endmodule // vrlp_eth_framer
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
module vrlp_to_compressed_vita
|
||||
(input clk, input reset, input clear,
|
||||
input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
|
||||
output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready);
|
||||
|
||||
wire [63:0] o_tdata_int;
|
||||
wire o_tlast_int, o_tvalid_int, o_tready_int;
|
||||
|
||||
reg [1:0] v2cv_state;
|
||||
reg [11:0] seqnum;
|
||||
reg trim_line;
|
||||
|
||||
localparam V2CV_VRLP = 2'd0;
|
||||
localparam V2CV_VRTH = 2'd1;
|
||||
localparam V2CV_BODY = 2'd2;
|
||||
localparam V2CV_DUMP = 2'd3;
|
||||
|
||||
wire is_ec = i_tdata[63:60] == 4'h5;
|
||||
wire has_trailer = i_tdata[58] & ~is_ec;
|
||||
wire has_time = |i_tdata[53:52];
|
||||
wire eob = i_tdata[56] & ~is_ec;
|
||||
wire [15:0] len = i_tdata[47:32];
|
||||
wire [31:0] sid = i_tdata[31:0];
|
||||
|
||||
wire [63:0] compressed_hdr = { is_ec, has_trailer, has_time, eob, seqnum, len, sid };
|
||||
wire bad_vita = |i_tdata[55:54] /* has secs */ | i_tdata[59] /* has class */ | ( {i_tdata[63],i_tdata[61:60]} != 3'b001 );
|
||||
reg [15:0] len_reg;
|
||||
wire [16:0] vita_words32 = i_tdata[16:0]-17'd4;
|
||||
assign trim_now = 0;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
begin
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
seqnum <= 12'd0;
|
||||
trim_line <= 1'b0;
|
||||
len_reg <= 16'd0;
|
||||
end
|
||||
else
|
||||
case(v2cv_state)
|
||||
V2CV_VRLP :
|
||||
if(i_tvalid)
|
||||
begin
|
||||
seqnum <= i_tdata[31:20];
|
||||
trim_line <= i_tdata[0];
|
||||
len_reg <= vita_words32[16:1];
|
||||
if(~i_tlast)
|
||||
v2cv_state <= V2CV_VRTH;
|
||||
end
|
||||
|
||||
V2CV_VRTH :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
len_reg <= len_reg - 16'd1;
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
else if(bad_vita)
|
||||
v2cv_state <= V2CV_DUMP;
|
||||
else
|
||||
v2cv_state <= V2CV_BODY;
|
||||
end
|
||||
|
||||
V2CV_BODY :
|
||||
if(i_tvalid & o_tready_int)
|
||||
begin
|
||||
len_reg <= len_reg - 16'd1;
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
else if(len_reg == 16'd0)
|
||||
v2cv_state <= V2CV_DUMP;
|
||||
end
|
||||
V2CV_DUMP :
|
||||
if(i_tvalid)
|
||||
if(i_tlast)
|
||||
v2cv_state <= V2CV_VRLP;
|
||||
endcase // case (v2cv_state)
|
||||
|
||||
assign o_tdata_int = (v2cv_state == V2CV_VRTH) ? compressed_hdr : i_tdata;
|
||||
assign o_tlast_int = i_tlast | (len_reg == 16'd0);
|
||||
assign o_tvalid_int = i_tvalid && (((v2cv_state == V2CV_VRTH) && !bad_vita) || (v2cv_state == V2CV_BODY));
|
||||
assign i_tready = o_tready_int | (v2cv_state == V2CV_VRLP) | (v2cv_state == V2CV_DUMP);
|
||||
|
||||
axi_fifo_short #(.WIDTH(65)) short_fifo
|
||||
(.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
|
||||
endmodule // vrlp_to_compressed_vita
|
||||
@@ -0,0 +1,120 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module vrlp_to_compressed_vita_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("vrlp_to_compressed_vita_tb.vcd");
|
||||
initial $dumpvars(0,vrlp_to_compressed_vita_tb);
|
||||
|
||||
task send_packet;
|
||||
input [63:0] data_start;
|
||||
input [31:0] len;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
{i_tlast, i_tdata} <= { 1'b0, data_start };
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= 64'hAAAA_BBBB_CCCC_0000;
|
||||
|
||||
repeat(len-2)
|
||||
begin
|
||||
i_tvalid <= 1;
|
||||
@(posedge clk);
|
||||
i_tdata <= i_tdata + 1;
|
||||
end
|
||||
i_tlast <= 1;
|
||||
i_tdata <= i_tdata + 1;
|
||||
@(posedge clk);
|
||||
i_tvalid <= 1'b0;
|
||||
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#200000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
wire [63:0] o_tdata;
|
||||
reg [63:0] i_tdata;
|
||||
wire [2:0] o_tuser;
|
||||
reg [2:0] i_tuser;
|
||||
reg i_tlast;
|
||||
wire o_tlast;
|
||||
wire o_tvalid, i_tready;
|
||||
reg i_tvalid, o_tready;
|
||||
reg i_terror;
|
||||
|
||||
localparam RPT_COUNT = 16;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tvalid <= 0;
|
||||
o_tready <= 0;
|
||||
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
@(posedge clk);
|
||||
|
||||
//send_packet(64'hA0,3'd0, 16, 0);
|
||||
send_packet(64'hAABC_0008_DEAD_BEEF, 4);
|
||||
send_packet(64'h7DEF_0008_8765_4321, 4);
|
||||
send_packet(64'hAABC_0007_F00D_1234, 4);
|
||||
send_packet(64'h7DEF_0007_ABCD_4321, 4);
|
||||
o_tready <= 1;
|
||||
//send_packet(64'hC0,3'd0, 16, 1);
|
||||
//send_packet(64'hD0,3'd0, 16, 0);
|
||||
//send_packet(64'hE0,3'd0, 16, 0);
|
||||
//send_packet(64'hF0,3'd0, 16, 0);
|
||||
|
||||
@(posedge clk);
|
||||
|
||||
end // initial begin
|
||||
|
||||
wire i_terror_int, i_tlast_int, i_tready_int, i_tvalid_int;
|
||||
wire [2:0] i_tuser_int;
|
||||
wire [63:0] i_tdata_int;
|
||||
wire o_tlast_int, o_tready_int, o_tvalid_int;
|
||||
wire [2:0] o_tuser_int;
|
||||
wire [63:0] o_tdata_int;
|
||||
wire [63:0] vrlp_tdata;
|
||||
wire vrlp_tlast, vrlp_tvalid, vrlp_tready;
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({i_tlast_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int));
|
||||
|
||||
compressed_vita_to_vrlp dut0
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(i_tdata_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int),
|
||||
.o_tdata(vrlp_tdata), .o_tlast(vrlp_tlast), .o_tvalid(vrlp_tvalid), .o_tready(vrlp_tready));
|
||||
|
||||
vrlp_to_compressed_vita dut1
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(vrlp_tdata), .i_tlast(vrlp_tlast), .i_tvalid(vrlp_tvalid), .i_tready(vrlp_tready),
|
||||
.o_tdata(o_tdata_int), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int));
|
||||
|
||||
axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready));
|
||||
|
||||
always @(posedge clk)
|
||||
if(o_tvalid & o_tready)
|
||||
begin
|
||||
$display("%x",o_tdata);
|
||||
if(o_tlast)
|
||||
$display("======EOF========");
|
||||
end
|
||||
endmodule // vrlp_to_compressed_vita_tb
|
||||
Reference in New Issue
Block a user