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
|
`timescale 1 ns / 1 ns
|
||||||
|
|
||||||
module axis_mac
|
module axis_mac
|
||||||
@ -14,8 +16,19 @@ module axis_mac
|
|||||||
// AXI-stream RX output (clock domain = gmii_rx_clk)
|
// 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 [7:0] m_axis_rx_tdata,
|
||||||
(* MARK_DEBUG="true" *)output reg m_axis_rx_tvalid,
|
(* MARK_DEBUG="true" *)output reg m_axis_rx_tvalid,
|
||||||
input wire m_axis_rx_tready,
|
(* MARK_DEBUG="true" *)input wire m_axis_rx_tready,
|
||||||
(* MARK_DEBUG="true" *)output reg m_axis_rx_tlast
|
(* 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
|
||||||
end
|
end
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// TX signals
|
||||||
// Unused user TX path into mac_top
|
reg [7:0] tx_ram_wr_data;
|
||||||
// We disable user UDP TX for now.
|
reg tx_ram_wr_en;
|
||||||
// ARP replies and ICMP replies inside mac_top still work.
|
reg [15:0] udp_send_data_length;
|
||||||
// ----------------------------------------------------------------
|
reg udp_tx_req;
|
||||||
wire udp_ram_data_req;
|
reg arp_request_req;
|
||||||
wire udp_tx_end;
|
wire mac_send_end;
|
||||||
wire almost_full;
|
|
||||||
wire mac_send_end;
|
|
||||||
wire arp_found;
|
|
||||||
wire mac_not_exist;
|
|
||||||
|
|
||||||
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;
|
reg [10:0] udp_rec_ram_read_addr;
|
||||||
|
wire [7:0] udp_rec_ram_rdata;
|
||||||
wire [15:0] udp_rec_data_length;
|
wire [15:0] udp_rec_data_length;
|
||||||
wire udp_rec_data_valid;
|
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 (
|
mac_top mac_top0 (
|
||||||
.gmii_tx_clk (gmii_tx_clk),
|
.gmii_tx_clk (gmii_tx_clk),
|
||||||
.gmii_rx_clk (gmii_rx_clk),
|
.gmii_rx_clk (gmii_rx_clk),
|
||||||
@ -81,7 +92,7 @@ module axis_mac
|
|||||||
.source_mac_addr (48'h00_0a_35_01_fe_c0),
|
.source_mac_addr (48'h00_0a_35_01_fe_c0),
|
||||||
.TTL (8'h80),
|
.TTL (8'h80),
|
||||||
.source_ip_addr (32'hc0a80002), // 192.168.0.2
|
.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_source_port (16'h1f90), // 8080
|
||||||
.udp_send_destination_port (16'h1f90), // 8080
|
.udp_send_destination_port (16'h1f90), // 8080
|
||||||
|
|
||||||
@ -111,9 +122,7 @@ module axis_mac
|
|||||||
.mac_not_exist (mac_not_exist)
|
.mac_not_exist (mac_not_exist)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
// Detect "new packet ready" on udp_rec_data_valid rising edge
|
// Detect "new packet ready" on udp_rec_data_valid rising edge
|
||||||
// ----------------------------------------------------------------
|
|
||||||
reg udp_rec_data_valid_d0;
|
reg udp_rec_data_valid_d0;
|
||||||
|
|
||||||
always @(posedge gmii_rx_clk or negedge rst_n) begin
|
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
|
// - read bytes 0 .. payload_len-1 from RX RAM
|
||||||
// - output them on AXIS
|
// - 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_IDLE = 2'd0;
|
||||||
localparam RX_NOTREADY = 2'd1;
|
localparam RX_NOTREADY = 2'd1;
|
||||||
@ -230,7 +237,6 @@ module axis_mac
|
|||||||
rx_state <= RX_IDLE;
|
rx_state <= RX_IDLE;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
rx_index <= rx_index + 1'b1;
|
rx_index <= rx_index + 1'b1;
|
||||||
udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte
|
udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte
|
||||||
end
|
end
|
||||||
@ -245,4 +251,185 @@ module axis_mac
|
|||||||
end
|
end
|
||||||
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
|
endmodule
|
||||||
Reference in New Issue
Block a user