aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/chdr_framer_2clk.v
blob: 4c430fbff2b35502dee27683c3ad1193d686b6fd (plain)
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
142
143
144
145
146
/////////////////////////////////////////////////////////////////////
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: chdr_framer_2clk
// Description:
//  - Takes a sample stream in and uses the tuser input to frame
//    a CHDR packet which is output by the module
//    samples at the output
//  - FIXME Currently only 32 / 64-bit input widths are supported.
//
/////////////////////////////////////////////////////////////////////

module chdr_framer_2clk #(
  parameter SIZE        = 10,
  parameter WIDTH       = 32,  // 32 or 64 only! TODO: Extend to other widths.
  parameter USE_SEQ_NUM = 0   // Use provided seq number in tuser
) (
  input samp_clk, input samp_rst, input pkt_clk, input pkt_rst,
  input [WIDTH-1:0] i_tdata, input [127:0] i_tuser, input i_tlast, input i_tvalid, output i_tready,
  output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
);

  wire          header_i_tvalid, header_i_tready;
  wire [63:0]   body_i_tdata;
  wire          body_i_tlast, body_i_tvalid, body_i_tready;

  wire [127:0]  header_o_tdata;
  wire          header_o_tvalid, header_o_tready;
  wire [63:0]   body_o_tdata;
  wire          body_o_tlast, body_o_tvalid, body_o_tready;
  reg  [15:0]   length;
  reg  [11:0]   seqnum;

  assign i_tready = header_i_tready & body_i_tready;
  assign header_i_tvalid = i_tlast & i_tvalid & i_tready;
  assign body_i_tlast = i_tlast;

  // Handle 32 and 64 widths
  generate
    if (WIDTH == 32) begin
     reg even = 1'b0;
     always @(posedge samp_clk)
       if(samp_rst)
         even <= 1'b0;
       else
         if(i_tvalid & i_tready)
           if(i_tlast)
             even <= 1'b0;
           else
             even <= ~even;

     reg [31:0] held_i_tdata;
     always @(posedge samp_clk) begin
       if (i_tvalid & i_tready) held_i_tdata <= i_tdata;
     end
     assign body_i_tvalid = i_tvalid & i_tready & (i_tlast | even);
     assign body_i_tdata  = even ? { held_i_tdata, i_tdata } : {i_tdata, i_tdata}; // really should be 0 in bottom, but this simplifies mux
    end else begin
     assign body_i_tvalid = i_tvalid & i_tready;
     assign body_i_tdata  = i_tdata;
    end
  endgenerate

  // FIXME handle lengths of partial 32-bit words
  always @(posedge samp_clk)
    if (samp_rst)
      length <= (WIDTH == 32) ? 16'd4 : 16'd8;
    else if(header_i_tready & header_i_tvalid)
      length <= (WIDTH == 32) ? 16'd4 : 16'd8;
    else if(i_tvalid & i_tready)
      length <= (WIDTH == 32) ? length + 16'd4 : length + 16'd8;

  // Extended reset signal to ensure longer reset on axi_fifo_2clk
  // as recommended by Xilinx. It clears all partial packets seen
  // after clearing the fifos.
  // This pulse stretch ratio works in this case and may not work
  // for all clocks.
  wire samp_rst_stretch;
  pulse_stretch #(.SCALE('d10)) samp_reset_i (
    .clk(samp_clk),
    .rst(1'b0),
    .pulse(samp_rst),
    .pulse_stretched(samp_rst_stretch)
  );

  axi_fifo_2clk #(.WIDTH(128), .SIZE(5)) hdr_fifo_i (
    .i_aclk(samp_clk), .o_aclk(pkt_clk), .reset(samp_rst_stretch),
    .i_tdata({i_tuser[127:112],length,i_tuser[95:0]}), .i_tvalid(header_i_tvalid), .i_tready(header_i_tready),
    .o_tdata(header_o_tdata), .o_tvalid(header_o_tvalid), .o_tready(header_o_tready)
  );

  axi_fifo_2clk #(.WIDTH(65), .SIZE(SIZE)) body_fifo_i (
    .i_aclk(samp_clk), .o_aclk(pkt_clk), .reset(samp_rst_stretch),
    .i_tdata({body_i_tlast,body_i_tdata}), .i_tvalid(body_i_tvalid), .i_tready(body_i_tready),
    .o_tdata({body_o_tlast,body_o_tdata}), .o_tvalid(body_o_tvalid), .o_tready(body_o_tready)
  );

  reg [1:0] chdr_state;
  localparam [1:0] ST_IDLE = 0;
  localparam [1:0] ST_HEAD = 1;
  localparam [1:0] ST_TIME = 2;
  localparam [1:0] ST_BODY = 3;

  always @(posedge pkt_clk)
    if(pkt_rst)
      chdr_state <= ST_IDLE;
    else
      case(chdr_state)
      ST_IDLE :
        if(header_o_tvalid & body_o_tvalid)
          chdr_state <= ST_HEAD;
      ST_HEAD :
        if(o_tready)
          if(header_o_tdata[125])  // time
            chdr_state <= ST_TIME;
          else
            chdr_state <= ST_BODY;
      ST_TIME :
        if(o_tready)
          chdr_state <= ST_BODY;
      ST_BODY :
        if(o_tready & body_o_tlast)
          chdr_state <= ST_IDLE;
      endcase

  always @(posedge pkt_clk)
    if(pkt_rst)
      seqnum <= 12'd0;
    else
      if(o_tvalid & o_tready & o_tlast)
        seqnum <= seqnum + 12'd1;

  wire [15:0] out_length = header_o_tdata[111:96] + (header_o_tdata[125] ? 16'd16 : 16'd8);

  assign o_tvalid = (chdr_state == ST_HEAD) | (chdr_state == ST_TIME) | (body_o_tvalid & (chdr_state == ST_BODY));
  assign o_tlast = (chdr_state == ST_BODY) & body_o_tlast;
  assign o_tdata = (chdr_state == ST_HEAD) ? {header_o_tdata[127:124], (USE_SEQ_NUM == 1 ? header_o_tdata[123:112] : seqnum), out_length, header_o_tdata[95:64] } :
                     (chdr_state == ST_TIME) ? header_o_tdata[63:0] :
                        body_o_tdata;
  assign body_o_tready = (chdr_state == ST_BODY) & o_tready;
  assign header_o_tready = ((chdr_state == ST_TIME) | ((chdr_state == ST_HEAD) & ~header_o_tdata[125])) & o_tready;

endmodule