Compare commits
2 Commits
0b9fb64193
...
c75443d170
| Author | SHA1 | Date | |
|---|---|---|---|
| c75443d170 | |||
| 3a58119960 |
248
rtl/ethernet-udp/src/eth/axis_mac.sv
Normal file
248
rtl/ethernet-udp/src/eth/axis_mac.sv
Normal file
@ -0,0 +1,248 @@
|
||||
`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_NOTREADY = 2'd1;
|
||||
localparam RX_START = 2'd2;
|
||||
localparam RX_DATA = 2'd3;
|
||||
|
||||
(* 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_NOTREADY;
|
||||
end else begin
|
||||
rx_payload_len <= 16'd0;
|
||||
rx_state <= RX_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// one cycle for synchronous BRAM read latency
|
||||
RX_NOTREADY: begin
|
||||
m_axis_rx_tvalid <= 1'b0;
|
||||
m_axis_rx_tlast <= 1'b0;
|
||||
|
||||
if (m_axis_rx_tready)
|
||||
rx_state <= RX_START;
|
||||
end
|
||||
|
||||
RX_START: begin
|
||||
if (m_axis_rx_tready) begin
|
||||
// put current data
|
||||
// end of data?
|
||||
if (rx_index == (rx_payload_len - 1)) begin
|
||||
rx_state <= RX_IDLE;
|
||||
end else begin
|
||||
rx_state <= RX_DATA;
|
||||
end
|
||||
|
||||
// always increment pointer
|
||||
rx_index <= rx_index + 1'b1;
|
||||
udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte
|
||||
end else begin
|
||||
rx_state <= RX_NOTREADY;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
RX_DATA: begin
|
||||
// hold valid until accepted
|
||||
if (!m_axis_rx_tready) begin
|
||||
// take a break while not ready
|
||||
m_axis_rx_tvalid <= m_axis_rx_tvalid;
|
||||
m_axis_rx_tlast <= m_axis_rx_tlast;
|
||||
rx_state <= RX_NOTREADY;
|
||||
// reset increment
|
||||
rx_index <= rx_index - 1'b1;
|
||||
udp_rec_ram_read_addr <= rx_index - 1'b1;
|
||||
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));
|
||||
if (rx_index == (rx_payload_len)) begin
|
||||
// last byte accepted immediately if ready=1,
|
||||
// otherwise valid/last remain asserted until ready
|
||||
rx_state <= RX_IDLE;
|
||||
end
|
||||
|
||||
|
||||
rx_index <= rx_index + 1'b1;
|
||||
udp_rec_ram_read_addr <= rx_index + 1'b1; // next byte
|
||||
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
|
||||
249
rtl/ethernet-udp/tests/test_axis_mac_rx.sv
Normal file
249
rtl/ethernet-udp/tests/test_axis_mac_rx.sv
Normal file
@ -0,0 +1,249 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module tb_mac_test;
|
||||
|
||||
reg rst_n = 1'b0;
|
||||
reg gmii_rx_clk = 1'b0;
|
||||
reg gmii_tx_clk = 1'b0;
|
||||
reg gmii_rx_dv = 1'b0;
|
||||
reg [7:0] gmii_rxd = 8'h00;
|
||||
|
||||
wire gmii_tx_en;
|
||||
wire [7:0] gmii_txd;
|
||||
|
||||
wire [7:0] m_axis_rx_tdata;
|
||||
wire m_axis_rx_tvalid;
|
||||
reg m_axis_rx_tready = 1'b0;
|
||||
wire m_axis_rx_tlast;
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DUT
|
||||
// ============================================================
|
||||
axis_mac dut (
|
||||
.rst_n(rst_n),
|
||||
.gmii_tx_clk(gmii_tx_clk),
|
||||
.gmii_rx_clk(gmii_rx_clk),
|
||||
.gmii_rx_dv(gmii_rx_dv),
|
||||
.gmii_rxd(gmii_rxd),
|
||||
.gmii_tx_en(gmii_tx_en),
|
||||
.gmii_txd(gmii_txd),
|
||||
.m_axis_rx_tdata(m_axis_rx_tdata),
|
||||
.m_axis_rx_tvalid(m_axis_rx_tvalid),
|
||||
.m_axis_rx_tready(m_axis_rx_tready),
|
||||
.m_axis_rx_tlast(m_axis_rx_tlast)
|
||||
);
|
||||
|
||||
// ============================================================
|
||||
// Clocks
|
||||
// ============================================================
|
||||
always #4 gmii_rx_clk = ~gmii_rx_clk;
|
||||
always #4 gmii_tx_clk = ~gmii_tx_clk;
|
||||
|
||||
// ============================================================
|
||||
// Helpers
|
||||
// ============================================================
|
||||
task automatic gmii_idle;
|
||||
input integer cycles;
|
||||
integer i;
|
||||
begin
|
||||
gmii_rx_dv <= 1'b0;
|
||||
gmii_rxd <= 8'h00;
|
||||
for (i = 0; i < cycles; i = i + 1) begin
|
||||
@(posedge gmii_rx_clk);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic send_gmii_byte;
|
||||
input [7:0] b;
|
||||
begin
|
||||
@(posedge gmii_rx_clk);
|
||||
gmii_rx_dv <= 1'b1;
|
||||
gmii_rxd <= b;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic end_gmii_frame;
|
||||
begin
|
||||
@(posedge gmii_rx_clk);
|
||||
gmii_rx_dv <= 1'b0;
|
||||
gmii_rxd <= 8'h00;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic send_gmii_frame;
|
||||
input integer frame_len;
|
||||
input [8*2048-1:0] frame_data_flat;
|
||||
integer i;
|
||||
reg [7:0] current_byte;
|
||||
begin
|
||||
gmii_idle(12);
|
||||
|
||||
for (i = 0; i < frame_len; i = i + 1) begin
|
||||
current_byte = frame_data_flat[i*8 +: 8];
|
||||
send_gmii_byte(current_byte);
|
||||
end
|
||||
|
||||
end_gmii_frame();
|
||||
|
||||
// inter-frame gap
|
||||
gmii_idle(12);
|
||||
end
|
||||
endtask
|
||||
|
||||
reg [7:0] frame_mem [0:2047];
|
||||
|
||||
task automatic send_gmii_frame_mem;
|
||||
input integer frame_len;
|
||||
integer i;
|
||||
begin
|
||||
gmii_idle(12);
|
||||
|
||||
for (i = 0; i < frame_len; i = i + 1) begin
|
||||
send_gmii_byte(frame_mem[i]);
|
||||
end
|
||||
|
||||
end_gmii_frame();
|
||||
gmii_idle(12);
|
||||
end
|
||||
endtask
|
||||
|
||||
// ============================================================
|
||||
// Monitor AXIS RX
|
||||
// ============================================================
|
||||
always @(posedge gmii_rx_clk) begin
|
||||
if (m_axis_rx_tvalid && m_axis_rx_tready) begin
|
||||
$write("%02x ", m_axis_rx_tdata);
|
||||
if (m_axis_rx_tlast)
|
||||
$write("<TLAST>");
|
||||
end
|
||||
cnt = cnt + 1;
|
||||
if (cnt % 8 < 6) begin
|
||||
m_axis_rx_tready = 1'b1;
|
||||
end else m_axis_rx_tready = 1'b0;
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// Test sequence
|
||||
// ============================================================
|
||||
integer i;
|
||||
|
||||
initial begin
|
||||
// init
|
||||
gmii_rx_dv = 1'b0;
|
||||
gmii_rxd = 8'h00;
|
||||
rst_n = 1'b0;
|
||||
|
||||
gmii_idle(200);
|
||||
rst_n = 1'b1;
|
||||
gmii_idle(200);
|
||||
|
||||
|
||||
frame_mem[0] = 8'h55;
|
||||
frame_mem[1] = 8'h55;
|
||||
frame_mem[2] = 8'h55;
|
||||
frame_mem[3] = 8'h55;
|
||||
frame_mem[4] = 8'h55;
|
||||
frame_mem[5] = 8'h55;
|
||||
frame_mem[6] = 8'h55;
|
||||
frame_mem[7] = 8'hd5;
|
||||
|
||||
frame_mem[8] = 8'h00;
|
||||
frame_mem[9] = 8'h0a;
|
||||
frame_mem[10] = 8'h35;
|
||||
frame_mem[11] = 8'h01;
|
||||
frame_mem[12] = 8'hfe;
|
||||
frame_mem[13] = 8'hc0;
|
||||
|
||||
frame_mem[14] = 8'h30;
|
||||
frame_mem[15] = 8'h56;
|
||||
frame_mem[16] = 8'h0f;
|
||||
frame_mem[17] = 8'ha0;
|
||||
frame_mem[18] = 8'h12;
|
||||
frame_mem[19] = 8'hec;
|
||||
|
||||
frame_mem[20] = 8'h08;
|
||||
frame_mem[21] = 8'h00;
|
||||
|
||||
frame_mem[22] = 8'h45;
|
||||
frame_mem[23] = 8'h00;
|
||||
frame_mem[24] = 8'h00;
|
||||
frame_mem[25] = 8'h23;
|
||||
frame_mem[26] = 8'h65;
|
||||
frame_mem[27] = 8'hfa;
|
||||
frame_mem[28] = 8'h40;
|
||||
frame_mem[29] = 8'h00;
|
||||
frame_mem[30] = 8'h40;
|
||||
frame_mem[31] = 8'h11;
|
||||
frame_mem[32] = 8'h53;
|
||||
frame_mem[33] = 8'h7a;
|
||||
|
||||
frame_mem[34] = 8'hc0;
|
||||
frame_mem[35] = 8'ha8;
|
||||
frame_mem[36] = 8'h00;
|
||||
frame_mem[37] = 8'h03;
|
||||
|
||||
frame_mem[38] = 8'hc0;
|
||||
frame_mem[39] = 8'ha8;
|
||||
frame_mem[40] = 8'h00;
|
||||
frame_mem[41] = 8'h02;
|
||||
|
||||
frame_mem[42] = 8'hc0;
|
||||
frame_mem[43] = 8'h31;
|
||||
frame_mem[44] = 8'h1f;
|
||||
frame_mem[45] = 8'h90;
|
||||
|
||||
frame_mem[46] = 8'h00;
|
||||
frame_mem[47] = 8'h0f;
|
||||
frame_mem[48] = 8'he4;
|
||||
frame_mem[49] = 8'h7f;
|
||||
|
||||
frame_mem[50] = 8'h6e;
|
||||
frame_mem[51] = 8'h65;
|
||||
frame_mem[52] = 8'h77;
|
||||
frame_mem[53] = 8'h5f;
|
||||
frame_mem[54] = 8'h6d;
|
||||
frame_mem[55] = 8'h73;
|
||||
frame_mem[56] = 8'h67;
|
||||
|
||||
// FCS
|
||||
frame_mem[57] = 8'h00;
|
||||
frame_mem[58] = 8'h00;
|
||||
frame_mem[59] = 8'h00;
|
||||
frame_mem[60] = 8'h00;
|
||||
|
||||
frame_mem[61] = 8'h00;
|
||||
frame_mem[62] = 8'h00;
|
||||
frame_mem[63] = 8'h00;
|
||||
frame_mem[64] = 8'h00;
|
||||
|
||||
frame_mem[65] = 8'h00;
|
||||
frame_mem[66] = 8'h00;
|
||||
frame_mem[67] = 8'h00;
|
||||
|
||||
frame_mem[68] = 8'h8c;
|
||||
frame_mem[69] = 8'ha2;
|
||||
frame_mem[70] = 8'h2e;
|
||||
frame_mem[71] = 8'h26;
|
||||
frame_mem[72] = 8'hdd;
|
||||
send_gmii_frame_mem(73);
|
||||
|
||||
gmii_idle(200);
|
||||
send_gmii_frame_mem(57);
|
||||
gmii_idle(20);
|
||||
send_gmii_frame_mem(57);
|
||||
gmii_idle(200);
|
||||
send_gmii_frame_mem(66);
|
||||
gmii_idle(200);
|
||||
send_gmii_frame_mem(66);
|
||||
send_gmii_frame_mem(66);
|
||||
gmii_idle(200);
|
||||
|
||||
$display("\nSimulation done");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user