rtl: add axis eth rx prototype

This commit is contained in:
Phil
2026-04-01 11:44:46 +03:00
parent a1386fc8a4
commit 0b9fb64193
4 changed files with 294 additions and 18 deletions

View File

@ -10,7 +10,7 @@ module mac_rx_top
input rst_n,
input rx_dv,
input [7:0] mac_rx_datain,
(* MARK_DEBUG="true" *)input [7:0] mac_rx_datain,
input [31:0] local_ip_addr,
input [47:0] local_mac_addr,
@ -21,14 +21,14 @@ module mac_rx_top
output [47:0] arp_rec_source_mac_addr,
output [7:0] udp_rec_ram_rdata ,
input [10:0] udp_rec_ram_read_addr,
output [15:0] udp_rec_data_length,
output udp_rec_data_valid,
(* MARK_DEBUG="true" *)output [7:0] udp_rec_ram_rdata ,
(* MARK_DEBUG="true" *)input [10:0] udp_rec_ram_read_addr,
(* MARK_DEBUG="true" *)output [15:0] udp_rec_data_length,
(* MARK_DEBUG="true" *)output udp_rec_data_valid,
output [7:0] mac_rx_dataout,
output [15:0] upper_layer_data_length ,
output [15:0] ip_total_data_length,
(* MARK_DEBUG="true" *)output [7:0] mac_rx_dataout,
(* MARK_DEBUG="true" *)output [15:0] upper_layer_data_length ,
(* MARK_DEBUG="true" *)output [15:0] ip_total_data_length,
output icmp_rx_req,
output icmp_rev_error,

View File

@ -24,13 +24,13 @@ module mac_tx_top
input arp_request_req,
input [7:0] ram_wr_data,
input ram_wr_en,
input udp_tx_req,
output udp_ram_data_req,
input [15:0] udp_send_data_length,
output udp_tx_end,
output almost_full,
(* MARK_DEBUG="true" *)input [7:0] ram_wr_data,
(* MARK_DEBUG="true" *)input ram_wr_en,
(* MARK_DEBUG="true" *)input udp_tx_req,
(* MARK_DEBUG="true" *)output udp_ram_data_req,
(* MARK_DEBUG="true" *)input [15:0] udp_send_data_length,
(* MARK_DEBUG="true" *)output udp_tx_end,
(* MARK_DEBUG="true" *)output almost_full,
output upper_data_req,
input icmp_tx_ready,
@ -40,9 +40,9 @@ module mac_tx_top
output icmp_tx_ack,
input [15:0] icmp_send_data_length,
output mac_data_valid,
output mac_send_end,
output [7:0] mac_tx_data
(* MARK_DEBUG="true" *)output mac_data_valid,
(* MARK_DEBUG="true" *)output mac_send_end,
(* MARK_DEBUG="true" *)output [7:0] mac_tx_data
) ;

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2025 FPGA Ninja, LLC
#
# Authors:
# - Alex Forencich
#
# FPGA settings
FPGA_PART = xc7a35tfgg484-1
FPGA_TOP = ethernet_test_minimal
FPGA_ARCH = artix7
RTL_DIR = ../../src
# Files for synthesis
SYN_FILES = ethernet_test_minimal.v
include ../../../../scripts/vivado.mk
SYN_FILES += $(sort $(shell find ../../src -type f \( -name '*.v' -o -name '*.sv' \)))
XCI_FILES = $(sort $(shell find ../../src -type f -name '*.xci'))
program: $(PROJECT).bit
echo "open_hw_manager" > program.tcl
echo "connect_hw_server" >> program.tcl
echo "open_hw_target" >> program.tcl
echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl
echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl
echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl
echo "program_hw_devices [current_hw_device]" >> program.tcl
echo "exit" >> program.tcl
vivado -nojournal -nolog -mode batch -source program.tcl
$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit
echo "write_cfgmem -force -format mcs -size 16 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl
echo "exit" >> generate_mcs.tcl
vivado -nojournal -nolog -mode batch -source generate_mcs.tcl
mkdir -p rev
COUNT=100; \
while [ -e rev/$*_rev$$COUNT.bit ]; \
do COUNT=$$((COUNT+1)); done; \
COUNT=$$((COUNT-1)); \
for x in .mcs .prm; \
do cp $*$$x rev/$*_rev$$COUNT$$x; \
echo "Output: rev/$*_rev$$COUNT$$x"; done;

View File

@ -0,0 +1,229 @@
`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