1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// 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
|