//
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Adapts from internal VITA to ethernet packets.  Also handles CPU and ethernet crossover interfaces.

module eth_interface #(
  parameter [15:0] PROTOVER    = {8'd1, 8'd0},
  parameter        MTU         = 10,
  parameter        NODE_INST   = 0,
  parameter        RT_TBL_SIZE = 6,
  parameter        REG_AWIDTH  = 14,
  parameter        BASE        = 0
) (
  input         clk,
  input         reset,
  input [15:0]  device_id,

  // Register port: Write port (domain: clk)
  input        reg_wr_req,
  input [REG_AWIDTH-1:0] reg_wr_addr,
  input [31:0] reg_wr_data,

  // Register port: Read port (domain: clk)
  input         reg_rd_req,
  input  [REG_AWIDTH-1:0] reg_rd_addr,
  output reg       reg_rd_resp,
  output reg [31:0] reg_rd_data,

  // Status ports (domain: clk)
  output [47:0] my_mac,
  output [31:0] my_ip,
  output [15:0] my_udp_port,

  // Ethernet ports
  output [63:0] eth_tx_tdata,
  output [3:0]  eth_tx_tuser,
  output        eth_tx_tlast,
  output        eth_tx_tvalid,
  input         eth_tx_tready,
  input [63:0]  eth_rx_tdata,
  input [3:0]   eth_rx_tuser,
  input         eth_rx_tlast,
  input         eth_rx_tvalid,
  output        eth_rx_tready,

  // Vita router interface
  output [63:0] e2v_tdata,
  output        e2v_tlast,
  output        e2v_tvalid,
  input         e2v_tready,
  input [63:0]  v2e_tdata,
  input         v2e_tlast,
  input         v2e_tvalid,
  output        v2e_tready,

  // CPU
  output [63:0] e2c_tdata,
  output [3:0]  e2c_tuser,
  output        e2c_tlast,
  output        e2c_tvalid,
  input         e2c_tready,
  input [63:0]  c2e_tdata,
  input [3:0]   c2e_tuser,
  input         c2e_tlast,
  input         c2e_tvalid,
  output        c2e_tready
);

  localparam [47:0] DEFAULT_MAC_ADDR  = {8'h00, 8'h80, 8'h2f, 8'h16, 8'hc5, 8'h2f};
  localparam [31:0] DEFAULT_IP_ADDR   = {8'd192, 8'd168, 8'd10, 8'd2};
  localparam [31:0] DEFAULT_UDP_PORT  = 16'd49153;

  //---------------------------------------------------------
  // Registers
  //---------------------------------------------------------

  // Allocate one full page for MAC
  localparam [REG_AWIDTH-1:0] REG_MAC_LSB        = BASE + 'h0000;
  localparam [REG_AWIDTH-1:0] REG_MAC_MSB        = BASE + 'h0004;

  // Source IP address
  localparam [REG_AWIDTH-1:0] REG_IP             = BASE + 'h1000;
  // Source UDP Port
  localparam [REG_AWIDTH-1:0] REG_UDP            = BASE + 'h1004;

  // Registers for Internal/Bridge Network Mode in CPU
  localparam [REG_AWIDTH-1:0] REG_BRIDGE_MAC_LSB = BASE + 'h1010;
  localparam [REG_AWIDTH-1:0] REG_BRIDGE_MAC_MSB = BASE + 'h1014;
  localparam [REG_AWIDTH-1:0] REG_BRIDGE_IP      = BASE + 'h1018;
  localparam [REG_AWIDTH-1:0] REG_BRIDGE_UDP     = BASE + 'h101c;
  localparam [REG_AWIDTH-1:0] REG_BRIDGE_ENABLE  = BASE + 'h1020;

  // MAC address for the dispatcher module.
  // This value is used to determine if the packet is meant
  // for this device should be consumed
  // IP address for the dispatcher module.
  // This value is used to determine if the packet is addressed
  // to this device
  // This module supports two destination ports
  reg [47:0]      mac_reg;
  reg [31:0]      ip_reg;
  reg [15:0]      udp_port;
  reg [47:0]      bridge_mac_reg;
  reg [31:0]      bridge_ip_reg;
  reg [15:0]      bridge_udp_port;
  reg             bridge_en;

  assign my_mac       = bridge_en ? bridge_mac_reg : mac_reg;
  assign my_ip        = bridge_en ? bridge_ip_reg : ip_reg;
  assign my_udp_port  = bridge_en ? bridge_udp_port : udp_port;

  always @(posedge clk) begin
    if (reset) begin
      mac_reg         <= DEFAULT_MAC_ADDR;
      ip_reg          <= DEFAULT_IP_ADDR;
      udp_port        <= DEFAULT_UDP_PORT;
      bridge_en       <= 1'b0;
      bridge_mac_reg  <= DEFAULT_MAC_ADDR;
      bridge_ip_reg   <= DEFAULT_IP_ADDR;
      bridge_udp_port <= DEFAULT_UDP_PORT;
    end
    else begin
      if (reg_wr_req)
        case (reg_wr_addr)

        REG_MAC_LSB:
          mac_reg[31:0]         <= reg_wr_data;

        REG_MAC_MSB:
          mac_reg[47:32]        <= reg_wr_data[15:0];

        REG_IP:
          ip_reg                <= reg_wr_data;

        REG_UDP:
          udp_port              <= reg_wr_data[15:0];

        REG_BRIDGE_MAC_LSB:
          bridge_mac_reg[31:0]  <= reg_wr_data;

        REG_BRIDGE_MAC_MSB:
          bridge_mac_reg[47:32] <= reg_wr_data[15:0];

        REG_BRIDGE_IP:
          bridge_ip_reg         <= reg_wr_data;

        REG_BRIDGE_UDP:
          bridge_udp_port       <= reg_wr_data[15:0];

        REG_BRIDGE_ENABLE:
          bridge_en             <= reg_wr_data[0];
        endcase
    end
  end

  always @ (posedge clk) begin
    // No reset handling required for readback
    if (reg_rd_req) begin
      // Assert read response one cycle after read request
      reg_rd_resp <= 1'b1;
      case (reg_rd_addr)
        REG_MAC_LSB:
          reg_rd_data <= mac_reg[31:0];

        REG_MAC_MSB:
          reg_rd_data <= {16'b0,mac_reg[47:32]};

        REG_IP:
          reg_rd_data <= ip_reg;

        REG_UDP:
          reg_rd_data <= {16'b0, udp_port};

        REG_BRIDGE_MAC_LSB:
          reg_rd_data <= bridge_mac_reg[31:0];

        REG_BRIDGE_MAC_MSB:
          reg_rd_data <= {16'b0,bridge_mac_reg[47:32]};

        REG_BRIDGE_IP:
          reg_rd_data <= bridge_ip_reg;

        REG_BRIDGE_UDP:
          reg_rd_data <= {16'b0, bridge_udp_port};

        REG_BRIDGE_ENABLE:
          reg_rd_data <= {31'b0,bridge_en};

        default:
          reg_rd_resp <= 1'b0;
      endcase
    end
    // Deassert read response after one clock cycle
    if (reg_rd_resp) begin
      reg_rd_resp <= 1'b0;
    end
  end

  // In AXI Stream, tkeep is the byte qualifier that indicates
  // whether the content of the associated byte
  // of TDATA is processed as part of the data stream.
  // tuser as used in eth_switch is the numbier of valid bytes

  eth_ipv4_chdr64_adapter #(
    .PROTOVER        (PROTOVER),
    .MTU             (MTU),
    .CPU_FIFO_SIZE   (MTU),
    .RT_TBL_SIZE     (RT_TBL_SIZE),
    .NODE_INST       (NODE_INST),
    .DROP_UNKNOWN_MAC(0),
    .IS_CPU_ARM      (1)
  ) eth_adapter_i (
    .clk             (clk          ),
    .rst             (reset        ),
    .device_id       (device_id    ),
    .s_mac_tdata     (eth_rx_tdata ),
    .s_mac_tuser     (eth_rx_tuser ),
    .s_mac_tlast     (eth_rx_tlast ),
    .s_mac_tvalid    (eth_rx_tvalid),
    .s_mac_tready    (eth_rx_tready),
    .m_mac_tdata     (eth_tx_tdata ),
    .m_mac_tuser     (eth_tx_tuser ),
    .m_mac_tlast     (eth_tx_tlast ),
    .m_mac_tvalid    (eth_tx_tvalid),
    .m_mac_tready    (eth_tx_tready),
    .s_chdr_tdata    (v2e_tdata    ),
    .s_chdr_tlast    (v2e_tlast    ),
    .s_chdr_tvalid   (v2e_tvalid   ),
    .s_chdr_tready   (v2e_tready   ),
    .m_chdr_tdata    (e2v_tdata    ),
    .m_chdr_tlast    (e2v_tlast    ),
    .m_chdr_tvalid   (e2v_tvalid   ),
    .m_chdr_tready   (e2v_tready   ),
    .s_cpu_tdata     (c2e_tdata    ),
    .s_cpu_tuser     (c2e_tuser    ),
    .s_cpu_tlast     (c2e_tlast    ),
    .s_cpu_tvalid    (c2e_tvalid   ),
    .s_cpu_tready    (c2e_tready   ),
    .m_cpu_tdata     (e2c_tdata    ),
    .m_cpu_tuser     (e2c_tuser    ),
    .m_cpu_tlast     (e2c_tlast    ),
    .m_cpu_tvalid    (e2c_tvalid   ),
    .m_cpu_tready    (e2c_tready   ),
    .my_eth_addr     (my_mac       ),
    .my_ipv4_addr    (my_ip        ),
    .my_udp_chdr_port(my_udp_port  )
  );


endmodule // eth_interface