1//
2// Copyright 2016 Ettus Research LLC
3//
4
5module aurora_axis_mac #(
6   parameter PHY_ENDIANNESS   = "LITTLE", //{"LITTLE, "BIG"}
7   parameter PACKET_MODE      = 0,
8   parameter MAX_PACKET_SIZE  = 512,
9   parameter BIST_ENABLED     = 1
10) (
11   // Clocks and resets
12   input             phy_clk,
13   input             phy_rst,
14   input             sys_clk,
15   input             sys_rst,
16   input             clear,
17   // PHY TX Interface (Synchronous to phy_clk)
18   output [63:0]     phy_m_axis_tdata,
19   output            phy_m_axis_tvalid,
20   input             phy_m_axis_tready,
21   // PHY RX Interface (Synchronous to phy_clk)
22   input  [63:0]     phy_s_axis_tdata,
23   input             phy_s_axis_tvalid,
24   // User TX Interface (Synchronous to sys_clk)
25   input  [63:0]     s_axis_tdata,
26   input             s_axis_tlast,
27   input             s_axis_tvalid,
28   output            s_axis_tready,
29   // User RX Interface (Synchronous to sys_clk)
30   output [63:0]     m_axis_tdata,
31   output            m_axis_tlast,
32   output            m_axis_tvalid,
33   input             m_axis_tready,
34   // PHY Status Inputs (Synchronous to phy_clk)
35   input             channel_up,
36   input             hard_err,
37   input             soft_err,
38   // Status and Error Outputs (Synchronous to sys_clk)
39   output [31:0]     overruns,
40   output [31:0]     soft_errors,
41   output reg [31:0] checksum_errors,
42   output critical_err,
43   // BIST Interface (Synchronous to sys_clk)
44   input             bist_gen_en,
45   input  [5:0]      bist_gen_rate,
46   input             bist_checker_en,
47   input             bist_loopback_en,
48   output reg        bist_checker_locked,
49   output reg [47:0] bist_checker_samps,
50   output reg [47:0] bist_checker_errors
51);
52
53   // ----------------------------------------------
54   // Resets, Clears, Clock crossings
55   // ----------------------------------------------
56
57   wire phy_s_axis_tready;    // Internal only. The PHY has no backpressure signal.
58
59   // Stay idle if the PHY is not up or if it experiences a fatal error
60   wire clear_sysclk, clear_phyclk;
61   synchronizer #(.INITIAL_VAL(1'b1)) clear_sync_phyclk_i (
62      .clk(phy_clk), .rst(1'b0 /* no reset */), .in((~channel_up) | hard_err | clear), .out(clear_phyclk));
63   synchronizer #(.INITIAL_VAL(1'b1)) clear_sync_sysclk_i (
64      .clk(sys_clk), .rst(1'b0 /* no reset */), .in(clear_phyclk), .out(clear_sysclk));
65
66   // ----------------------------------------------
67   // Counters
68   // ----------------------------------------------
69
70   reg [31:0] overruns_reg;
71   reg [31:0] soft_errors_reg;
72
73   // Counter for recoverable errors. For reporting only.
74   always @(posedge phy_clk)
75      if (phy_rst | clear_phyclk)
76         soft_errors_reg <= 32'd0;
77      else if (soft_err)
78         soft_errors_reg <= soft_errors_reg + 32'd1;
79
80   // Tag an overrun if the FIFO is full. Samples will get dropped
81   always @(posedge phy_clk)
82      if (phy_rst | clear_phyclk)
83         overruns_reg <= 32'd0;
84      else if (phy_s_axis_tvalid & ~phy_s_axis_tready)
85         overruns_reg <= overruns_reg + 32'd1;
86
87   wire [7:0] dummy0;
88   fifo_short_2clk status_counters_2clk_i (
89      .rst(phy_rst),
90      .wr_clk(phy_clk), .din({8'h00, soft_errors_reg, overruns_reg}), .wr_en(1'b1), .full(), .wr_data_count(),
91      .rd_clk(sys_clk), .dout({dummy0, soft_errors, overruns}), .rd_en(1'b1), .empty(), .rd_data_count()
92   );
93
94   // ----------------------------------------------
95   // BIST Wires
96   // ----------------------------------------------
97
98   wire [63:0] bist_o_tdata;
99   wire        bist_o_tvalid, bist_o_tready;
100   wire [63:0] bist_i_tdata;
101   wire        bist_i_tvalid, bist_i_tready;
102   wire [63:0] loopback_tdata;
103   wire        loopback_tvalid, loopback_tready;
104   reg         bist_gen_en_reg = 1'b0, bist_checker_en_reg = 1'b0, bist_loopback_en_reg = 1'b0;
105   reg  [5:0]  bist_gen_rate_reg = 'd0;
106
107   generate if (BIST_ENABLED == 1) begin
108      // Pipeline control signals
109      always @(posedge sys_clk) begin
110         if (sys_rst | clear_sysclk) begin
111            bist_gen_en_reg      <= 1'b0;
112            bist_checker_en_reg  <= 1'b0;
113            bist_loopback_en_reg <= 1'b0;
114            bist_gen_rate_reg    <= 'd0;
115         end else begin
116            bist_gen_en_reg      <= bist_gen_en;
117            bist_checker_en_reg  <= bist_checker_en;
118            bist_loopback_en_reg <= bist_loopback_en;
119            bist_gen_rate_reg    <= bist_gen_rate;
120         end
121      end
122   end endgenerate
123   // ----------------------------------------------
124   // RX Data Path
125   // ----------------------------------------------
126
127   wire [63:0]    i_raw_tdata;
128   wire           i_raw_tvalid, i_raw_tready;
129
130   wire [63:0]    i_pip_tdata;
131   wire           i_pip_tvalid, i_pip_tready;
132
133   wire [63:0]    i_pkt_tdata;
134   wire           i_pkt_tlast, i_pkt_tvalid, i_pkt_tready;
135
136   wire [63:0]    i_gt_tdata;
137   wire           i_gt_tlast, i_gt_tvalid, i_gt_tready;
138
139   wire           checksum_err;
140
141   wire [63:0]    phy_s_axis_tdata_endian, phy_m_axis_tdata_endian;
142
143   generate if (PHY_ENDIANNESS == "BIG") begin
144      assign phy_s_axis_tdata_endian = {
145         phy_s_axis_tdata[7:0], phy_s_axis_tdata[15:8], phy_s_axis_tdata[23:16], phy_s_axis_tdata[31:24],
146         phy_s_axis_tdata[39:32], phy_s_axis_tdata[47:40], phy_s_axis_tdata[55:48], phy_s_axis_tdata[63:56]
147      };
148      assign phy_m_axis_tdata = {
149         phy_m_axis_tdata_endian[7:0], phy_m_axis_tdata_endian[15:8], phy_m_axis_tdata_endian[23:16], phy_m_axis_tdata_endian[31:24],
150         phy_m_axis_tdata_endian[39:32], phy_m_axis_tdata_endian[47:40], phy_m_axis_tdata_endian[55:48], phy_m_axis_tdata_endian[63:56]
151      };
152   end else begin
153      assign phy_s_axis_tdata_endian = phy_s_axis_tdata;
154      assign phy_m_axis_tdata = phy_m_axis_tdata_endian;
155   end endgenerate
156
157   // Large FIFO must be able to run input side at 64b@156MHz to sustain 10Gb Rx.
158   axi64_4k_2clk_fifo ingress_fifo_i (
159      .s_aresetn(~phy_rst), .s_aclk(phy_clk),
160      .s_axis_tdata(phy_s_axis_tdata_endian), .s_axis_tlast(phy_s_axis_tvalid), .s_axis_tuser(4'h0),
161      .s_axis_tvalid(phy_s_axis_tvalid), .s_axis_tready(phy_s_axis_tready), .axis_wr_data_count(),
162      .m_aclk(sys_clk),
163      .m_axis_tdata(i_raw_tdata), .m_axis_tlast(), .m_axis_tuser(),
164      .m_axis_tvalid(i_raw_tvalid), .m_axis_tready(i_raw_tready), .axis_rd_data_count()
165   );
166
167   // AXI-Flop to ease timing
168   axi_fifo_flop #(.WIDTH(64)) input_pipe_i0 (
169      .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
170      .i_tdata(i_raw_tdata), .i_tvalid(i_raw_tvalid), .i_tready(i_raw_tready),
171      .o_tdata(i_pip_tdata), .o_tvalid(i_pip_tvalid),
172      .o_tready(bist_checker_en_reg ? bist_i_tready : (bist_loopback_en_reg ? loopback_tready : i_pip_tready)),
173      .space(), .occupied()
174   );
175
176   assign bist_i_tdata     = i_pip_tdata;
177   assign bist_i_tvalid    = i_pip_tvalid & bist_checker_en_reg;
178
179   assign loopback_tdata   = i_pip_tdata;
180   assign loopback_tvalid  = i_pip_tvalid & bist_loopback_en_reg;
181
182   axi_strip_preamble #(.WIDTH(64), .MAX_PKT_SIZE(MAX_PACKET_SIZE)) axi_strip_preamble_i (
183      .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
184      .i_tdata(i_pip_tdata), .i_tvalid(i_pip_tvalid & ~bist_checker_en_reg & ~bist_loopback_en_reg), .i_tready(i_pip_tready),
185      .o_tdata(i_gt_tdata), .o_tlast(i_gt_tlast), .o_tvalid(i_gt_tvalid), .o_tready(i_gt_tready),
186      .crc_err(checksum_err), .pkt_dropped(), .crit_error(critical_err)
187   );
188
189  axi_fifo_flop #(.WIDTH(65)) input_pipe_i1 (
190     .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
191     .i_tdata({i_gt_tlast, i_gt_tdata}), .i_tvalid(i_gt_tvalid), .i_tready(i_gt_tready),
192     .o_tdata({m_axis_tlast, m_axis_tdata}), .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
193     .space(), .occupied()
194  );
195
196   always @(posedge sys_clk)
197      if (sys_rst | clear_sysclk)
198         checksum_errors <= 32'd0;
199      else if (checksum_err)
200         checksum_errors <= checksum_errors + 32'd1;
201
202   // ----------------------------------------------
203   // TX Data Path
204   // ----------------------------------------------
205
206   wire [63:0]    o_pkt_tdata;
207   wire           o_pkt_tlast, o_pkt_tvalid, o_pkt_tready;
208
209   wire [63:0]    o_pip_tdata;
210   wire           o_pip_tvalid, o_pip_tready;
211
212   wire [63:0]    o_raw_tdata;
213   wire           o_raw_tvalid, o_raw_tready;
214
215   // AXI-Flop to ease timing
216   axi_fifo_flop #(.WIDTH(65)) output_pipe_i0 (
217      .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
218      .i_tdata({s_axis_tlast, s_axis_tdata}), .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
219      .o_tdata({o_pkt_tlast, o_pkt_tdata}), .o_tvalid(o_pkt_tvalid), .o_tready(o_pkt_tready),
220      .space(), .occupied()
221   );
222
223   // Insert preamble and EOP
224   axi_add_preamble #(.WIDTH(64)) axi_add_preamble_i (
225      .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
226      .i_tdata(o_pkt_tdata), .i_tlast(o_pkt_tlast), .i_tvalid(o_pkt_tvalid), .i_tready(o_pkt_tready),
227      .o_tdata(o_pip_tdata), .o_tvalid(o_pip_tvalid), .o_tready(o_pip_tready & ~bist_gen_en_reg & ~bist_loopback_en_reg)
228   );
229
230   // AXI-Flop to ease timing
231   axi_fifo_flop #(.WIDTH(64)) output_pipe_i1 (
232      .clk(sys_clk), .reset(sys_rst), .clear(clear_sysclk),
233      .i_tdata(bist_gen_en_reg ? bist_o_tdata : (bist_loopback_en_reg ? loopback_tdata : o_pip_tdata)),
234      .i_tvalid(bist_gen_en_reg ? bist_o_tvalid : (bist_loopback_en_reg ? loopback_tvalid : o_pip_tvalid)),
235      .i_tready(o_pip_tready),
236      .o_tdata(o_raw_tdata), .o_tvalid(o_raw_tvalid), .o_tready(o_raw_tready),
237      .space(), .occupied()
238   );
239
240   assign bist_o_tready    = o_pip_tready;
241   assign loopback_tready  = o_pip_tready;
242
243   // Egress FIFO
244   axi64_4k_2clk_fifo egress_fifo_i (
245      .s_aresetn(~phy_rst), .s_aclk(sys_clk),
246      .s_axis_tdata(o_raw_tdata), .s_axis_tlast(o_raw_tvalid), .s_axis_tuser(4'h0),
247      .s_axis_tvalid(o_raw_tvalid), .s_axis_tready(o_raw_tready), .axis_wr_data_count(),
248      .m_aclk(phy_clk),
249      .m_axis_tdata(phy_m_axis_tdata_endian), .m_axis_tlast(), .m_axis_tuser(),
250      .m_axis_tvalid(phy_m_axis_tvalid), .m_axis_tready(phy_m_axis_tready), .axis_rd_data_count()
251   );
252
253   // -------------------------------------------------
254   // BIST: Generator and checker for a LFSR polynomial
255   // -------------------------------------------------
256   localparam LFSR_LEN  = 32;
257   localparam LFSR_SEED = {LFSR_LEN{1'b1}};
258
259   function [LFSR_LEN-1:0] compute_lfsr_next;
260      input [LFSR_LEN-1:0] current;
261      // Maximal length polynomial: x^32 + x^22 + x^2 + x^1 + 1
262      compute_lfsr_next = {current[30:0], current[31]^current[21]^current[1]^current[0]};
263   endfunction
264
265   function [63:0] lfsr_to_axis;
266      input [LFSR_LEN-1:0] lfsr;
267      lfsr_to_axis = {~lfsr, lfsr};
268   endfunction
269
270   function [LFSR_LEN-1:0] axis_to_lfsr;
271      input [63:0] axis;
272      axis_to_lfsr = axis[LFSR_LEN-1:0];
273   endfunction
274
275   generate if (BIST_ENABLED == 1) begin
276      // Throttle outgoing LFSR to based on the specified rate
277      // BIST Throughput = sys_clk BW * (bist_gen_rate+1)/64
278      reg [5:0] throttle_cnt;
279      always @(posedge sys_clk) begin
280         if (sys_rst | clear_sysclk)
281            throttle_cnt <= 6'd0;
282         else if (bist_gen_en_reg)
283            throttle_cnt <= throttle_cnt + 6'd1;
284      end
285      // NOTE: This techinically violates AXIS spec (valid revocation)
286      assign bist_o_tvalid = bist_gen_en_reg && (throttle_cnt <= bist_gen_rate_reg);
287
288      // Unsynchronized LFSR generator (for BIST output)
289      reg [LFSR_LEN-1:0] lfsr_gen = LFSR_SEED, lfsr_check = LFSR_SEED;
290      always @(posedge sys_clk) begin
291         if (sys_rst | clear_sysclk | ~bist_gen_en_reg)
292            lfsr_gen <= LFSR_SEED;
293         else if (bist_o_tready & bist_o_tvalid)
294            lfsr_gen <= compute_lfsr_next(lfsr_gen);
295      end
296      assign bist_o_tdata = lfsr_to_axis(lfsr_gen);
297
298      // Synchronized LFSR checker (for BIST input)
299      wire [LFSR_LEN-1:0] lfsr_next = compute_lfsr_next(lfsr_check);;
300      always @(posedge sys_clk) begin
301         if (sys_rst | clear_sysclk | ~bist_checker_en_reg) begin
302            bist_checker_locked <= 1'b0;
303            lfsr_check <= LFSR_SEED;
304         end else if (bist_i_tvalid && bist_i_tready) begin
305            lfsr_check <= axis_to_lfsr(bist_i_tdata);
306            if (bist_i_tdata == lfsr_to_axis(LFSR_SEED))
307               bist_checker_locked <= 1'b1;
308         end
309      end
310
311      // LFSR checker
312      always @(posedge sys_clk) begin
313         if (bist_checker_locked) begin
314            if (bist_i_tvalid & bist_i_tready) begin
315               bist_checker_samps <= bist_checker_samps + 48'd1;
316               if (bist_i_tdata != lfsr_to_axis(lfsr_next)) begin
317                  bist_checker_errors <= bist_checker_errors + 48'd1;
318               end
319            end
320         end else begin
321            bist_checker_samps  <= 48'd0;
322            bist_checker_errors <= 48'd0;
323         end
324      end
325      assign bist_i_tready = 1'b1;
326   end endgenerate
327
328endmodule
329
330