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