`timescale 1 ns / 1 ns module axis_mac ( input rst_n, input gmii_tx_clk, input gmii_rx_clk, input gmii_rx_dv, input [7:0] gmii_rxd, output reg gmii_tx_en, output reg [7:0] gmii_txd, // AXI-stream RX output (clock domain = gmii_rx_clk) (* MARK_DEBUG="true" *)output reg [7:0] m_axis_rx_tdata, (* MARK_DEBUG="true" *)output reg m_axis_rx_tvalid, input wire m_axis_rx_tready, (* MARK_DEBUG="true" *)output reg m_axis_rx_tlast ); // ---------------------------------------------------------------- // GMII RX input registering // ---------------------------------------------------------------- reg gmii_rx_dv_d0; reg [7:0] gmii_rxd_d0; always @(posedge gmii_rx_clk or negedge rst_n) begin if (!rst_n) begin gmii_rx_dv_d0 <= 1'b0; gmii_rxd_d0 <= 8'd0; end else begin gmii_rx_dv_d0 <= gmii_rx_dv; gmii_rxd_d0 <= gmii_rxd; end end // ---------------------------------------------------------------- // TX path from mac_top // ---------------------------------------------------------------- wire gmii_tx_en_tmp; wire [7:0] gmii_txd_tmp; always @(posedge gmii_tx_clk or negedge rst_n) begin if (!rst_n) begin gmii_tx_en <= 1'b0; gmii_txd <= 8'd0; end else begin gmii_tx_en <= gmii_tx_en_tmp; gmii_txd <= gmii_txd_tmp; end end // ---------------------------------------------------------------- // Unused user TX path into mac_top // We disable user UDP TX for now. // ARP replies and ICMP replies inside mac_top still work. // ---------------------------------------------------------------- wire udp_ram_data_req; wire udp_tx_end; wire almost_full; wire mac_send_end; wire arp_found; wire mac_not_exist; wire [7:0] udp_rec_ram_rdata; reg [10:0] udp_rec_ram_read_addr; wire [15:0] udp_rec_data_length; wire udp_rec_data_valid; wire [7:0] tx_ram_wr_data = 8'd0; wire tx_ram_wr_en = 1'b0; wire [15:0] udp_send_data_length = 16'd0; wire udp_tx_req = 1'b0; wire arp_request_req = 1'b0; mac_top mac_top0 ( .gmii_tx_clk (gmii_tx_clk), .gmii_rx_clk (gmii_rx_clk), .rst_n (rst_n), .source_mac_addr (48'h00_0a_35_01_fe_c0), .TTL (8'h80), .source_ip_addr (32'hc0a80002), // 192.168.0.2 .destination_ip_addr (32'hc0a80003), // 192.168.0.3 (не используется для user TX пока) .udp_send_source_port (16'h1f90), // 8080 .udp_send_destination_port (16'h1f90), // 8080 .ram_wr_data (tx_ram_wr_data), .ram_wr_en (tx_ram_wr_en), .udp_ram_data_req (udp_ram_data_req), .udp_send_data_length (udp_send_data_length), .udp_tx_end (udp_tx_end), .almost_full (almost_full), .udp_tx_req (udp_tx_req), .arp_request_req (arp_request_req), .mac_send_end (mac_send_end), .mac_data_valid (gmii_tx_en_tmp), .mac_tx_data (gmii_txd_tmp), .rx_dv (gmii_rx_dv_d0), .mac_rx_datain (gmii_rxd_d0), .udp_rec_ram_rdata (udp_rec_ram_rdata), .udp_rec_ram_read_addr (udp_rec_ram_read_addr), .udp_rec_data_length (udp_rec_data_length), .udp_rec_data_valid (udp_rec_data_valid), .arp_found (arp_found), .mac_not_exist (mac_not_exist) ); // ---------------------------------------------------------------- // Detect "new packet ready" on udp_rec_data_valid rising edge // ---------------------------------------------------------------- reg udp_rec_data_valid_d0; always @(posedge gmii_rx_clk or negedge rst_n) begin if (!rst_n) udp_rec_data_valid_d0 <= 1'b0; else udp_rec_data_valid_d0 <= udp_rec_data_valid; end wire udp_pkt_done = udp_rec_data_valid & ~udp_rec_data_valid_d0; // ---------------------------------------------------------------- // RX RAM -> AXI-stream bridge // // Assumption: // udp_rec_data_length includes 8-byte UDP header, // so payload length = udp_rec_data_length - 8 // // This implementation is simple and safe: // - start on udp_pkt_done // - read bytes 0 .. payload_len-1 from RX RAM // - output them on AXIS // // Because the BRAM read port is synchronous, this bridge may insert // bubbles between bytes. For first bring-up this is fine. // ---------------------------------------------------------------- localparam RX_IDLE = 2'd0; localparam RX_ADDR = 2'd1; localparam RX_DATA = 2'd2; (* MARK_DEBUG="true" *) reg [1:0] rx_state; (* MARK_DEBUG="true" *) reg [15:0] rx_payload_len; (* MARK_DEBUG="true" *) reg [15:0] rx_index; always @(posedge gmii_rx_clk or negedge rst_n) begin if (!rst_n) begin rx_state <= RX_IDLE; rx_payload_len <= 16'd0; rx_index <= 16'd0; udp_rec_ram_read_addr <= 11'd0; m_axis_rx_tdata <= 8'd0; m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; end else begin case (rx_state) RX_IDLE: begin m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; rx_index <= 16'd0; udp_rec_ram_read_addr <= 11'd0; if (udp_pkt_done) begin // protect against pathological short values if (udp_rec_data_length > 16'd8) begin rx_payload_len <= udp_rec_data_length - 16'd8; udp_rec_ram_read_addr <= 11'd0; // issue read for byte 0 rx_state <= RX_ADDR; end else begin rx_payload_len <= 16'd0; rx_state <= RX_IDLE; end end end // one cycle for synchronous BRAM read latency RX_ADDR: begin m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; rx_state <= RX_DATA; end RX_DATA: begin // hold valid until accepted if (m_axis_rx_tvalid && !m_axis_rx_tready) begin m_axis_rx_tvalid <= m_axis_rx_tvalid; m_axis_rx_tlast <= m_axis_rx_tlast; end else begin // present current byte from RAM m_axis_rx_tdata <= udp_rec_ram_rdata; m_axis_rx_tvalid <= 1'b1; m_axis_rx_tlast <= (rx_index == rx_payload_len - 1); if (rx_index == rx_payload_len - 1) begin // last byte accepted immediately if ready=1, // otherwise valid/last remain asserted until ready if (m_axis_rx_tready) begin m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; rx_state <= RX_IDLE; end end else begin if (m_axis_rx_tready) begin rx_index <= rx_index + 1'b1; udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte rx_state <= RX_ADDR; m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; end end end end default: begin rx_state <= RX_IDLE; m_axis_rx_tvalid <= 1'b0; m_axis_rx_tlast <= 1'b0; end endcase end end endmodule