rtl: implement axis UDP TX logic
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
// ethernet MAC with axi stream IO with UDP
|
||||
|
||||
`timescale 1 ns / 1 ns
|
||||
|
||||
module axis_mac
|
||||
@ -14,8 +16,19 @@ module axis_mac
|
||||
// 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
|
||||
(* MARK_DEBUG="true" *)input wire m_axis_rx_tready,
|
||||
(* MARK_DEBUG="true" *)output reg m_axis_rx_tlast,
|
||||
(* MARK_DEBUG="true" *)output [15:0] udp_rec_data_length,
|
||||
|
||||
// tx part
|
||||
(* MARK_DEBUG="true" *)input wire send_req,
|
||||
input wire [15:0] data_length,
|
||||
(* MARK_DEBUG="true" *)output reg req_ready,
|
||||
|
||||
(* MARK_DEBUG="true" *)input wire [7:0] s_axis_tx_tdata,
|
||||
(* MARK_DEBUG="true" *)input wire s_axis_tx_tvalid,
|
||||
(* MARK_DEBUG="true" *)output reg s_axis_tx_tready,
|
||||
(* MARK_DEBUG="true" *)input wire s_axis_tx_tlast
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@ -50,29 +63,27 @@ module axis_mac
|
||||
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;
|
||||
// TX signals
|
||||
reg [7:0] tx_ram_wr_data;
|
||||
reg tx_ram_wr_en;
|
||||
reg [15:0] udp_send_data_length;
|
||||
reg udp_tx_req;
|
||||
reg arp_request_req;
|
||||
wire mac_send_end;
|
||||
|
||||
wire [7:0] udp_rec_ram_rdata;
|
||||
wire udp_ram_data_req;
|
||||
wire udp_tx_end;
|
||||
wire almost_full;
|
||||
(* MARK_DEBUG="true" *)wire arp_found;
|
||||
wire mac_not_exist;
|
||||
wire [15:0] udp_ram_data_count;
|
||||
|
||||
// RX signals
|
||||
reg [10:0] udp_rec_ram_read_addr;
|
||||
wire [7:0] udp_rec_ram_rdata;
|
||||
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),
|
||||
@ -81,7 +92,7 @@ module axis_mac
|
||||
.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 пока)
|
||||
.destination_ip_addr (32'hc0a80003), // 192.168.0.3
|
||||
.udp_send_source_port (16'h1f90), // 8080
|
||||
.udp_send_destination_port (16'h1f90), // 8080
|
||||
|
||||
@ -111,9 +122,7 @@ module axis_mac
|
||||
.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
|
||||
@ -137,8 +146,6 @@ module axis_mac
|
||||
// - 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_NOTREADY = 2'd1;
|
||||
@ -230,7 +237,6 @@ module axis_mac
|
||||
rx_state <= RX_IDLE;
|
||||
end
|
||||
|
||||
|
||||
rx_index <= rx_index + 1'b1;
|
||||
udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte
|
||||
end
|
||||
@ -245,4 +251,185 @@ module axis_mac
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TX FSM
|
||||
// Semantics:
|
||||
// - send_req/data_length form a packet send request
|
||||
// - udp_tx_req is held HIGH until udp_ram_data_req pulses
|
||||
// - udp_ram_data_req is a pulse - start feeding payload now to RAM
|
||||
// - AXIS ready is asserted only during payload write phase
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
|
||||
localparam TX_IDLE = 3'd0;
|
||||
localparam TX_ARP_REQ = 3'd1;
|
||||
localparam TX_ARP_SEND = 3'd2;
|
||||
localparam TX_WAIT_ARP = 3'd3;
|
||||
localparam TX_WAIT_RAM_REQ = 3'd4;
|
||||
localparam TX_STREAM = 3'd5;
|
||||
localparam TX_WAIT_DRAIN = 3'd6;
|
||||
|
||||
(* MARK_DEBUG="true" *)reg [2:0] tx_state;
|
||||
|
||||
assign arp_request_req = (tx_state == TX_ARP_REQ) ;
|
||||
|
||||
reg [15:0] tx_req_len;
|
||||
reg [15:0] tx_bytes_written;
|
||||
reg [15:0] tx_release_threshold;
|
||||
|
||||
reg tx_req_inflight;
|
||||
|
||||
// register for long arp timeout, if no got no response
|
||||
reg [31:0] arp_delay;
|
||||
reg arp_cached;
|
||||
|
||||
always @(posedge gmii_tx_clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
tx_state <= TX_IDLE;
|
||||
|
||||
tx_ram_wr_data <= 8'd0;
|
||||
arp_cached <= 1'b0;
|
||||
tx_ram_wr_en <= 1'b0;
|
||||
udp_send_data_length <= 16'd0;
|
||||
udp_tx_req <= 1'b0;
|
||||
arp_delay <= 32'b0;
|
||||
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
req_ready <= 1'b0;
|
||||
|
||||
tx_req_len <= 16'd0;
|
||||
tx_bytes_written <= 16'd0;
|
||||
tx_release_threshold <= 16'd0;
|
||||
|
||||
tx_req_inflight <= 1'b0;
|
||||
end else begin
|
||||
// defaults
|
||||
tx_ram_wr_en <= 1'b0;
|
||||
|
||||
case (tx_state)
|
||||
// Ready to accept a new packet request
|
||||
TX_IDLE: begin
|
||||
udp_tx_req <= 1'b0;
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
tx_bytes_written <= 16'd0;
|
||||
tx_req_inflight <= 1'b0;
|
||||
|
||||
req_ready <= arp_cached && !almost_full;
|
||||
|
||||
if (send_req && req_ready) begin
|
||||
tx_req_len <= data_length;
|
||||
udp_send_data_length <= data_length;
|
||||
tx_req_inflight <= 1'b1;
|
||||
|
||||
// threshold for allowing next packet
|
||||
// to be written to the RAM
|
||||
if (data_length > 16'd16)
|
||||
tx_release_threshold <= data_length - 16'd16;
|
||||
else
|
||||
tx_release_threshold <= 16'd0;
|
||||
|
||||
tx_state <= TX_WAIT_RAM_REQ;
|
||||
end
|
||||
|
||||
// arp check
|
||||
if (!arp_cached) begin
|
||||
tx_state <= TX_ARP_REQ;
|
||||
end
|
||||
end
|
||||
|
||||
// Pulse ARP request
|
||||
TX_ARP_REQ: begin
|
||||
req_ready <= 1'b0;
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
udp_tx_req <= 1'b0;
|
||||
|
||||
arp_delay <= 32'ha000000;
|
||||
tx_state <= TX_ARP_SEND;
|
||||
end
|
||||
|
||||
// Wait until ARP is resolved
|
||||
TX_ARP_SEND: begin
|
||||
req_ready <= 1'b0;
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
udp_tx_req <= 1'b0;
|
||||
|
||||
// sent
|
||||
if (mac_send_end)
|
||||
tx_state <= TX_WAIT_ARP;
|
||||
end
|
||||
|
||||
// wait for ARP response
|
||||
TX_WAIT_ARP: begin
|
||||
if (arp_found) begin
|
||||
arp_cached <= 1'b1;
|
||||
tx_state <= TX_IDLE;
|
||||
end
|
||||
|
||||
// timeout to not spam ARPs
|
||||
if (arp_delay == 32'b0) begin
|
||||
// re-try
|
||||
tx_state <= TX_ARP_REQ;
|
||||
end else begin
|
||||
// wait
|
||||
arp_delay = arp_delay - 32'b1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
// Hold udp_tx_req until udp_ram_data_req pulse arrives
|
||||
TX_WAIT_RAM_REQ: begin
|
||||
req_ready <= 1'b0;
|
||||
udp_tx_req <= 1'b1;
|
||||
|
||||
if (udp_ram_data_req) begin
|
||||
udp_tx_req <= 1'b0;
|
||||
s_axis_tx_tready <= 1'b1;
|
||||
tx_state <= TX_STREAM;
|
||||
end
|
||||
end
|
||||
|
||||
// Accept AXIS bytes and write them into TX RAM
|
||||
TX_STREAM: begin
|
||||
req_ready <= 1'b0;
|
||||
udp_tx_req <= 1'b0;
|
||||
|
||||
// keep ready high while receiving payload bytes
|
||||
s_axis_tx_tready <= (tx_bytes_written < tx_req_len);
|
||||
|
||||
if (s_axis_tx_tvalid && s_axis_tx_tready) begin
|
||||
tx_ram_wr_data <= s_axis_tx_tdata;
|
||||
tx_ram_wr_en <= 1'b1;
|
||||
|
||||
tx_bytes_written <= tx_bytes_written + 1'b1;
|
||||
|
||||
if (tx_bytes_written + 1'b1 >= tx_req_len) begin
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
tx_state <= TX_WAIT_DRAIN;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Packet payload is already in RAM.
|
||||
// Wait until TX RAM starts draining enough to allow
|
||||
// the next request.
|
||||
TX_WAIT_DRAIN: begin
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
udp_tx_req <= 1'b0;
|
||||
|
||||
if (udp_ram_data_count <= tx_release_threshold)
|
||||
tx_state <= TX_IDLE;
|
||||
|
||||
end
|
||||
|
||||
default: begin
|
||||
tx_state <= TX_IDLE;
|
||||
tx_ram_wr_en <= 1'b0;
|
||||
udp_tx_req <= 1'b0;
|
||||
s_axis_tx_tready <= 1'b0;
|
||||
req_ready <= 1'b0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user