rtl: add axis eth rx prototype
This commit is contained in:
@ -10,7 +10,7 @@ module mac_rx_top
|
|||||||
input rst_n,
|
input rst_n,
|
||||||
|
|
||||||
input rx_dv,
|
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 [31:0] local_ip_addr,
|
||||||
input [47:0] local_mac_addr,
|
input [47:0] local_mac_addr,
|
||||||
@ -21,14 +21,14 @@ module mac_rx_top
|
|||||||
output [47:0] arp_rec_source_mac_addr,
|
output [47:0] arp_rec_source_mac_addr,
|
||||||
|
|
||||||
|
|
||||||
output [7:0] udp_rec_ram_rdata ,
|
(* MARK_DEBUG="true" *)output [7:0] udp_rec_ram_rdata ,
|
||||||
input [10:0] udp_rec_ram_read_addr,
|
(* MARK_DEBUG="true" *)input [10:0] udp_rec_ram_read_addr,
|
||||||
output [15:0] udp_rec_data_length,
|
(* MARK_DEBUG="true" *)output [15:0] udp_rec_data_length,
|
||||||
output udp_rec_data_valid,
|
(* MARK_DEBUG="true" *)output udp_rec_data_valid,
|
||||||
|
|
||||||
output [7:0] mac_rx_dataout,
|
(* MARK_DEBUG="true" *)output [7:0] mac_rx_dataout,
|
||||||
output [15:0] upper_layer_data_length ,
|
(* MARK_DEBUG="true" *)output [15:0] upper_layer_data_length ,
|
||||||
output [15:0] ip_total_data_length,
|
(* MARK_DEBUG="true" *)output [15:0] ip_total_data_length,
|
||||||
output icmp_rx_req,
|
output icmp_rx_req,
|
||||||
output icmp_rev_error,
|
output icmp_rev_error,
|
||||||
|
|
||||||
|
|||||||
@ -24,13 +24,13 @@ module mac_tx_top
|
|||||||
input arp_request_req,
|
input arp_request_req,
|
||||||
|
|
||||||
|
|
||||||
input [7:0] ram_wr_data,
|
(* MARK_DEBUG="true" *)input [7:0] ram_wr_data,
|
||||||
input ram_wr_en,
|
(* MARK_DEBUG="true" *)input ram_wr_en,
|
||||||
input udp_tx_req,
|
(* MARK_DEBUG="true" *)input udp_tx_req,
|
||||||
output udp_ram_data_req,
|
(* MARK_DEBUG="true" *)output udp_ram_data_req,
|
||||||
input [15:0] udp_send_data_length,
|
(* MARK_DEBUG="true" *)input [15:0] udp_send_data_length,
|
||||||
output udp_tx_end,
|
(* MARK_DEBUG="true" *)output udp_tx_end,
|
||||||
output almost_full,
|
(* MARK_DEBUG="true" *)output almost_full,
|
||||||
|
|
||||||
output upper_data_req,
|
output upper_data_req,
|
||||||
input icmp_tx_ready,
|
input icmp_tx_ready,
|
||||||
@ -40,9 +40,9 @@ module mac_tx_top
|
|||||||
output icmp_tx_ack,
|
output icmp_tx_ack,
|
||||||
input [15:0] icmp_send_data_length,
|
input [15:0] icmp_send_data_length,
|
||||||
|
|
||||||
output mac_data_valid,
|
(* MARK_DEBUG="true" *)output mac_data_valid,
|
||||||
output mac_send_end,
|
(* MARK_DEBUG="true" *)output mac_send_end,
|
||||||
output [7:0] mac_tx_data
|
(* MARK_DEBUG="true" *)output [7:0] mac_tx_data
|
||||||
) ;
|
) ;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
47
rtl/ethernet-udp/tests/eth_axis/Makefile
Normal file
47
rtl/ethernet-udp/tests/eth_axis/Makefile
Normal 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;
|
||||||
229
rtl/ethernet-udp/tests/eth_minimal/axis_mac.sv
Normal file
229
rtl/ethernet-udp/tests/eth_minimal/axis_mac.sv
Normal 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
|
||||||
Reference in New Issue
Block a user