1//
2// Copyright 2011 Ettus Research LLC
3//
4// Packet dispatcher with fifo36 interface and 3 outputs.
5//
6// The packet dispatcher expects 2-byte padded ethernet frames.
7// The frames will be inspected at ethernet, IPv4, UDP, and VRT layers.
8// Packets are dispatched into the following streams:
9//   * tx dsp stream
10//   * to cpu stream
11//   * to external stream
12//   * to both cpu and external
13//
14// The following registers are used for dispatcher control:
15//   * base + 0 = this ipv4 address (32 bits)
16//   * base + 1 = udp dst port (lower 16 bits)
17//
18
19module packet_dispatcher36_x3
20    #(
21        parameter BASE = 0
22    )
23    (
24        //clocking and reset interface:
25        input clk, input rst, input clr,
26
27        //setting register interface:
28        input set_stb, input [7:0] set_addr, input [31:0] set_data,
29
30        //input stream interfaces:
31        input [35:0] com_inp_data, input com_inp_valid, output com_inp_ready,
32
33        //output stream interfaces:
34        output [35:0] ext_out_data, output ext_out_valid, input ext_out_ready,
35        output [35:0] dsp_out_data, output dsp_out_valid, input dsp_out_ready,
36        output [35:0] cpu_out_data, output cpu_out_valid, input cpu_out_ready
37    );
38
39    //setting register to program the IP address
40    wire [31:0] my_ip_addr;
41    setting_reg #(.my_addr(BASE+0)) sreg_ip_addr(
42        .clk(clk),.rst(rst),
43        .strobe(set_stb),.addr(set_addr),.in(set_data),
44        .out(my_ip_addr),.changed()
45    );
46
47    //setting register to program the UDP DSP port
48    wire [15:0] dsp_udp_port;
49    setting_reg #(.my_addr(BASE+1), .width(16)) sreg_data_port(
50        .clk(clk),.rst(rst),
51        .strobe(set_stb),.addr(set_addr),.in(set_data),
52        .out(dsp_udp_port),.changed()
53    );
54
55    ////////////////////////////////////////////////////////////////////
56    // Communication input inspector
57    //   - inspect com input and send it to DSP, EXT, CPU, or BOTH
58    ////////////////////////////////////////////////////////////////////
59    localparam PD_STATE_READ_COM_PRE = 0;
60    localparam PD_STATE_READ_COM = 1;
61    localparam PD_STATE_WRITE_REGS = 2;
62    localparam PD_STATE_WRITE_LIVE = 3;
63
64    localparam PD_DEST_DSP = 0;
65    localparam PD_DEST_EXT = 1;
66    localparam PD_DEST_CPU = 2;
67    localparam PD_DEST_BOF = 3;
68
69    localparam PD_MAX_NUM_DREGS = 13; //padded_eth + ip + udp + seq + vrt_hdr
70    localparam PD_DREGS_DSP_OFFSET = 11; //offset to start dsp at
71
72    //output inspector interfaces
73    wire [35:0] pd_out_dsp_data;
74    wire        pd_out_dsp_valid;
75    wire        pd_out_dsp_ready;
76
77    wire [35:0] pd_out_ext_data;
78    wire        pd_out_ext_valid;
79    wire        pd_out_ext_ready;
80
81    wire [35:0] pd_out_cpu_data;
82    wire        pd_out_cpu_valid;
83    wire        pd_out_cpu_ready;
84
85    wire [35:0] pd_out_bof_data;
86    wire        pd_out_bof_valid;
87    wire        pd_out_bof_ready;
88
89    reg [1:0] pd_state;
90    reg [1:0] pd_dest;
91    reg [3:0] pd_dreg_count; //data registers to buffer headers
92    wire [3:0] pd_dreg_count_next = pd_dreg_count + 1'b1;
93    wire pd_dreg_counter_done = (pd_dreg_count_next == PD_MAX_NUM_DREGS)? 1'b1 : 1'b0;
94    reg [35:0] pd_dregs [PD_MAX_NUM_DREGS-1:0];
95
96    reg is_eth_dst_mac_bcast;
97    reg is_eth_type_ipv4;
98    reg is_eth_ipv4_proto_udp;
99    reg is_eth_ipv4_dst_addr_here;
100    reg is_eth_udp_dst_port_here;
101    wire is_vrt_size_zero = (com_inp_data[15:0] == 16'h0); //needed on the same cycle, so it cant be registered
102
103    //Inspector output flags special case:
104    //Inject SOF into flags at first DSP line.
105    wire [3:0] pd_out_flags = (
106        (pd_dreg_count == PD_DREGS_DSP_OFFSET) &&
107        (pd_dest == PD_DEST_DSP)
108    )? 4'b0001 : pd_dregs[pd_dreg_count][35:32];
109
110    //The communication inspector ouput data and valid signals:
111    //Mux between com input and data registers based on the state.
112    wire [35:0] pd_out_data = (pd_state == PD_STATE_WRITE_REGS)?
113        {pd_out_flags, pd_dregs[pd_dreg_count][31:0]} : com_inp_data
114    ;
115    wire pd_out_valid =
116        (pd_state == PD_STATE_WRITE_REGS)? 1'b1          : (
117        (pd_state == PD_STATE_WRITE_LIVE)? com_inp_valid : (
118    1'b0));
119
120    //The communication inspector ouput ready signal:
121    //Mux between the various destination ready signals.
122    wire pd_out_ready =
123        (pd_dest == PD_DEST_DSP)? pd_out_dsp_ready : (
124        (pd_dest == PD_DEST_EXT)? pd_out_ext_ready : (
125        (pd_dest == PD_DEST_CPU)? pd_out_cpu_ready : (
126        (pd_dest == PD_DEST_BOF)? pd_out_bof_ready : (
127    1'b0))));
128
129    //Always connected output data lines.
130    assign pd_out_dsp_data = pd_out_data;
131    assign pd_out_ext_data = pd_out_data;
132    assign pd_out_cpu_data = pd_out_data;
133    assign pd_out_bof_data = pd_out_data;
134
135    //Destination output valid signals:
136    //Comes from inspector valid when destination is selected, and otherwise low.
137    assign pd_out_dsp_valid = (pd_dest == PD_DEST_DSP)? pd_out_valid : 1'b0;
138    assign pd_out_ext_valid = (pd_dest == PD_DEST_EXT)? pd_out_valid : 1'b0;
139    assign pd_out_cpu_valid = (pd_dest == PD_DEST_CPU)? pd_out_valid : 1'b0;
140    assign pd_out_bof_valid = (pd_dest == PD_DEST_BOF)? pd_out_valid : 1'b0;
141
142    //The communication inspector ouput ready signal:
143    //Always ready when storing to data registers,
144    //comes from inspector ready output when live,
145    //and otherwise low.
146    assign com_inp_ready =
147        (pd_state == PD_STATE_READ_COM_PRE)  ? 1'b1         : (
148        (pd_state == PD_STATE_READ_COM)      ? 1'b1         : (
149        (pd_state == PD_STATE_WRITE_LIVE)    ? pd_out_ready : (
150    1'b0)));
151
152    //inspect the incoming data and mark register booleans
153    always @(posedge clk)
154    if (com_inp_ready & com_inp_valid) begin
155        case(pd_dreg_count)
156        0: begin
157            is_eth_dst_mac_bcast <= (com_inp_data[15:0] == 16'hffff);
158        end
159        1: begin
160            is_eth_dst_mac_bcast <= is_eth_dst_mac_bcast && (com_inp_data[31:0] == 32'hffffffff);
161        end
162        3: begin
163            is_eth_type_ipv4 <= (com_inp_data[15:0] == 16'h800);
164        end
165        6: begin
166            is_eth_ipv4_proto_udp <= (com_inp_data[23:16] == 8'h11);
167        end
168        8: begin
169            is_eth_ipv4_dst_addr_here <= (com_inp_data[31:0] == my_ip_addr);
170        end
171        9: begin
172            is_eth_udp_dst_port_here <= (com_inp_data[15:0] == dsp_udp_port);
173        end
174        endcase //pd_dreg_count
175    end
176
177    always @(posedge clk)
178    if(rst | clr) begin
179        pd_state <= PD_STATE_READ_COM_PRE;
180        pd_dreg_count <= 0;
181    end
182    else begin
183        case(pd_state)
184        PD_STATE_READ_COM_PRE: begin
185            if (com_inp_ready & com_inp_valid & com_inp_data[32]) begin
186                pd_state <= PD_STATE_READ_COM;
187                pd_dreg_count <= pd_dreg_count_next;
188                pd_dregs[pd_dreg_count] <= com_inp_data;
189            end
190        end
191
192        PD_STATE_READ_COM: begin
193            if (com_inp_ready & com_inp_valid) begin
194                pd_dregs[pd_dreg_count] <= com_inp_data;
195                if (pd_dreg_counter_done | com_inp_data[33]) begin
196                    pd_state <= PD_STATE_WRITE_REGS;
197                    pd_dreg_count <= 0;
198
199                    //---------- begin inspection decision -----------//
200                    //EOF or bcast or not IPv4 or not UDP:
201                    if (
202                        com_inp_data[33] || is_eth_dst_mac_bcast ||
203                        ~is_eth_type_ipv4 || ~is_eth_ipv4_proto_udp
204                    ) begin
205                        pd_dest <= PD_DEST_BOF;
206                    end
207
208                    //not my IP address:
209                    else if (~is_eth_ipv4_dst_addr_here) begin
210                        pd_dest <= PD_DEST_EXT;
211                    end
212
213                    //UDP data port and VRT:
214                    else if (is_eth_udp_dst_port_here && ~is_vrt_size_zero) begin
215                        pd_dest <= PD_DEST_DSP;
216                        pd_dreg_count <= PD_DREGS_DSP_OFFSET;
217                    end
218
219                    //other:
220                    else begin
221                        pd_dest <= PD_DEST_CPU;
222                    end
223                    //---------- end inspection decision -------------//
224
225                end
226                else begin
227                    pd_dreg_count <= pd_dreg_count_next;
228                end
229            end
230        end
231
232        PD_STATE_WRITE_REGS: begin
233            if (pd_out_ready & pd_out_valid) begin
234                if (pd_out_data[33]) begin
235                    pd_state <= PD_STATE_READ_COM_PRE;
236                    pd_dreg_count <= 0;
237                end
238                else if (pd_dreg_counter_done) begin
239                    pd_state <= PD_STATE_WRITE_LIVE;
240                    pd_dreg_count <= 0;
241                end
242                else begin
243                    pd_dreg_count <= pd_dreg_count_next;
244                end
245            end
246        end
247
248        PD_STATE_WRITE_LIVE: begin
249            if (pd_out_ready & pd_out_valid & pd_out_data[33]) begin
250                pd_state <= PD_STATE_READ_COM_PRE;
251            end
252        end
253
254        endcase //pd_state
255    end
256
257    //connect this fast-path signals directly to the DSP out
258    assign dsp_out_data = pd_out_dsp_data;
259    assign dsp_out_valid = pd_out_dsp_valid;
260    assign pd_out_dsp_ready = dsp_out_ready;
261
262    ////////////////////////////////////////////////////////////////////
263    // Splitter and output muxes for the bof packets
264    //   - split the bof packets into two streams
265    //   - mux split packets into cpu out and ext out
266    ////////////////////////////////////////////////////////////////////
267
268    //dummy signals to join the the splitter and muxes below
269    wire [35:0] _split_to_ext_data,  _split_to_cpu_data;
270    wire        _split_to_ext_valid, _split_to_cpu_valid;
271    wire        _split_to_ext_ready, _split_to_cpu_ready;
272
273    splitter36 bof_out_splitter(
274        .clk(clk), .rst(rst), .clr(clr),
275        .inp_data(pd_out_bof_data), .inp_valid(pd_out_bof_valid), .inp_ready(pd_out_bof_ready),
276        .out0_data(_split_to_ext_data), .out0_valid(_split_to_ext_valid), .out0_ready(_split_to_ext_ready),
277        .out1_data(_split_to_cpu_data), .out1_valid(_split_to_cpu_valid), .out1_ready(_split_to_cpu_ready)
278    );
279
280    fifo36_mux ext_out_mux(
281        .clk(clk), .reset(rst), .clear(clr),
282        .data0_i(pd_out_ext_data), .src0_rdy_i(pd_out_ext_valid), .dst0_rdy_o(pd_out_ext_ready),
283        .data1_i(_split_to_ext_data), .src1_rdy_i(_split_to_ext_valid), .dst1_rdy_o(_split_to_ext_ready),
284        .data_o(ext_out_data), .src_rdy_o(ext_out_valid), .dst_rdy_i(ext_out_ready)
285    );
286
287    fifo36_mux cpu_out_mux(
288        .clk(clk), .reset(rst), .clear(clr),
289        .data0_i(pd_out_cpu_data), .src0_rdy_i(pd_out_cpu_valid), .dst0_rdy_o(pd_out_cpu_ready),
290        .data1_i(_split_to_cpu_data), .src1_rdy_i(_split_to_cpu_valid), .dst1_rdy_o(_split_to_cpu_ready),
291        .data_o(cpu_out_data), .src_rdy_o(cpu_out_valid), .dst_rdy_i(cpu_out_ready)
292    );
293
294endmodule // packet_dispatcher36_x3
295