mirror of
https://github.com/corundum/corundum.git
synced 2025-01-30 08:32:52 +08:00
Add MII PHY interface, MAC wrappers, and testbenches
This commit is contained in:
parent
0ca8c9a59b
commit
8e2d936884
166
rtl/eth_mac_mii.v
Normal file
166
rtl/eth_mac_mii.v
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* 10M/100M Ethernet MAC with MII interface
|
||||
*/
|
||||
module eth_mac_mii #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64
|
||||
)
|
||||
(
|
||||
input wire rst,
|
||||
output wire rx_clk,
|
||||
output wire rx_rst,
|
||||
output wire tx_clk,
|
||||
output wire tx_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] tx_axis_tdata,
|
||||
input wire tx_axis_tvalid,
|
||||
output wire tx_axis_tready,
|
||||
input wire tx_axis_tlast,
|
||||
input wire tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser,
|
||||
|
||||
/*
|
||||
* MII interface
|
||||
*/
|
||||
input wire mii_rx_clk,
|
||||
input wire [3:0] mii_rxd,
|
||||
input wire mii_rx_dv,
|
||||
input wire mii_rx_er,
|
||||
input wire mii_tx_clk,
|
||||
output wire [3:0] mii_txd,
|
||||
output wire mii_tx_en,
|
||||
output wire mii_tx_er,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire tx_start_packet,
|
||||
output wire tx_error_underflow,
|
||||
output wire rx_start_packet,
|
||||
output wire rx_error_bad_frame,
|
||||
output wire rx_error_bad_fcs,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
wire [3:0] mac_mii_rxd;
|
||||
wire mac_mii_rx_dv;
|
||||
wire mac_mii_rx_er;
|
||||
wire [3:0] mac_mii_txd;
|
||||
wire mac_mii_tx_en;
|
||||
wire mac_mii_tx_er;
|
||||
|
||||
mii_phy_if #(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE)
|
||||
)
|
||||
mii_phy_if_inst (
|
||||
.rst(rst),
|
||||
|
||||
.mac_mii_rx_clk(rx_clk),
|
||||
.mac_mii_rx_rst(rx_rst),
|
||||
.mac_mii_rxd(mac_mii_rxd),
|
||||
.mac_mii_rx_dv(mac_mii_rx_dv),
|
||||
.mac_mii_rx_er(mac_mii_rx_er),
|
||||
.mac_mii_tx_clk(tx_clk),
|
||||
.mac_mii_tx_rst(tx_rst),
|
||||
.mac_mii_txd(mac_mii_txd),
|
||||
.mac_mii_tx_en(mac_mii_tx_en),
|
||||
.mac_mii_tx_er(mac_mii_tx_er),
|
||||
|
||||
.phy_mii_rx_clk(mii_rx_clk),
|
||||
.phy_mii_rxd(mii_rxd),
|
||||
.phy_mii_rx_dv(mii_rx_dv),
|
||||
.phy_mii_rx_er(mii_rx_er),
|
||||
.phy_mii_tx_clk(mii_tx_clk),
|
||||
.phy_mii_txd(mii_txd),
|
||||
.phy_mii_tx_en(mii_tx_en),
|
||||
.phy_mii_tx_er(mii_tx_er)
|
||||
);
|
||||
|
||||
eth_mac_1g #(
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
eth_mac_1g_inst (
|
||||
.tx_clk(tx_clk),
|
||||
.tx_rst(tx_rst),
|
||||
.rx_clk(rx_clk),
|
||||
.rx_rst(rx_rst),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
.gmii_rxd(mac_mii_rxd),
|
||||
.gmii_rx_dv(mac_mii_rx_dv),
|
||||
.gmii_rx_er(mac_mii_rx_er),
|
||||
.gmii_txd(mac_mii_txd),
|
||||
.gmii_tx_en(mac_mii_tx_en),
|
||||
.gmii_tx_er(mac_mii_tx_er),
|
||||
.rx_clk_enable(1'b1),
|
||||
.tx_clk_enable(1'b1),
|
||||
.rx_mii_select(1'b1),
|
||||
.tx_mii_select(1'b1),
|
||||
.tx_start_packet(tx_start_packet),
|
||||
.tx_error_underflow(tx_error_underflow),
|
||||
.rx_start_packet(rx_start_packet),
|
||||
.rx_error_bad_frame(rx_error_bad_frame),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
312
rtl/eth_mac_mii_fifo.v
Normal file
312
rtl/eth_mac_mii_fifo.v
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* 10M/100M Ethernet MAC with MII interface and TX and RX FIFOs
|
||||
*/
|
||||
module eth_mac_mii_fifo #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64,
|
||||
parameter TX_FIFO_ADDR_WIDTH = 12,
|
||||
parameter TX_FRAME_FIFO = 1,
|
||||
parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO,
|
||||
parameter TX_DROP_WHEN_FULL = 0,
|
||||
parameter RX_FIFO_ADDR_WIDTH = 12,
|
||||
parameter RX_FRAME_FIFO = 1,
|
||||
parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO,
|
||||
parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO
|
||||
)
|
||||
(
|
||||
input wire rst,
|
||||
input wire logic_clk,
|
||||
input wire logic_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] tx_axis_tdata,
|
||||
input wire tx_axis_tvalid,
|
||||
output wire tx_axis_tready,
|
||||
input wire tx_axis_tlast,
|
||||
input wire tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
input wire rx_axis_tready,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser,
|
||||
|
||||
/*
|
||||
* MII interface
|
||||
*/
|
||||
input wire mii_rx_clk,
|
||||
input wire [3:0] mii_rxd,
|
||||
input wire mii_rx_dv,
|
||||
input wire mii_rx_er,
|
||||
input wire mii_tx_clk,
|
||||
output wire [3:0] mii_txd,
|
||||
output wire mii_tx_en,
|
||||
output wire mii_tx_er,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire tx_error_underflow,
|
||||
output wire tx_fifo_overflow,
|
||||
output wire tx_fifo_bad_frame,
|
||||
output wire tx_fifo_good_frame,
|
||||
output wire rx_error_bad_frame,
|
||||
output wire rx_error_bad_fcs,
|
||||
output wire rx_fifo_overflow,
|
||||
output wire rx_fifo_bad_frame,
|
||||
output wire rx_fifo_good_frame,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
wire tx_clk;
|
||||
wire rx_clk;
|
||||
wire tx_rst;
|
||||
wire rx_rst;
|
||||
|
||||
wire [7:0] tx_fifo_axis_tdata;
|
||||
wire tx_fifo_axis_tvalid;
|
||||
wire tx_fifo_axis_tready;
|
||||
wire tx_fifo_axis_tlast;
|
||||
wire tx_fifo_axis_tuser;
|
||||
|
||||
wire [7:0] rx_fifo_axis_tdata;
|
||||
wire rx_fifo_axis_tvalid;
|
||||
wire rx_fifo_axis_tlast;
|
||||
wire rx_fifo_axis_tuser;
|
||||
|
||||
// synchronize MAC status signals into logic clock domain
|
||||
wire tx_error_underflow_int;
|
||||
|
||||
reg [0:0] tx_sync_reg_1 = 1'b0;
|
||||
reg [0:0] tx_sync_reg_2 = 1'b0;
|
||||
reg [0:0] tx_sync_reg_3 = 1'b0;
|
||||
reg [0:0] tx_sync_reg_4 = 1'b0;
|
||||
|
||||
assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
|
||||
|
||||
always @(posedge tx_clk or posedge tx_rst) begin
|
||||
if (tx_rst) begin
|
||||
tx_sync_reg_1 <= 1'b0;
|
||||
end else begin
|
||||
tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge logic_clk or posedge logic_rst) begin
|
||||
if (logic_rst) begin
|
||||
tx_sync_reg_2 <= 1'b0;
|
||||
tx_sync_reg_3 <= 1'b0;
|
||||
tx_sync_reg_4 <= 1'b0;
|
||||
end else begin
|
||||
tx_sync_reg_2 <= tx_sync_reg_1;
|
||||
tx_sync_reg_3 <= tx_sync_reg_2;
|
||||
tx_sync_reg_4 <= tx_sync_reg_3;
|
||||
end
|
||||
end
|
||||
|
||||
wire rx_error_bad_frame_int;
|
||||
wire rx_error_bad_fcs_int;
|
||||
|
||||
reg [1:0] rx_sync_reg_1 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_2 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_3 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_4 = 2'd0;
|
||||
|
||||
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
|
||||
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
|
||||
|
||||
always @(posedge rx_clk or posedge rx_rst) begin
|
||||
if (rx_rst) begin
|
||||
rx_sync_reg_1 <= 2'd0;
|
||||
end else begin
|
||||
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int};
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge logic_clk or posedge logic_rst) begin
|
||||
if (logic_rst) begin
|
||||
rx_sync_reg_2 <= 2'd0;
|
||||
rx_sync_reg_3 <= 2'd0;
|
||||
rx_sync_reg_4 <= 2'd0;
|
||||
end else begin
|
||||
rx_sync_reg_2 <= rx_sync_reg_1;
|
||||
rx_sync_reg_3 <= rx_sync_reg_2;
|
||||
rx_sync_reg_4 <= rx_sync_reg_3;
|
||||
end
|
||||
end
|
||||
|
||||
eth_mac_mii #(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
eth_mac_1g_mii_inst (
|
||||
.rst(rst),
|
||||
.tx_clk(tx_clk),
|
||||
.tx_rst(tx_rst),
|
||||
.rx_clk(rx_clk),
|
||||
.rx_rst(rx_rst),
|
||||
.tx_axis_tdata(tx_fifo_axis_tdata),
|
||||
.tx_axis_tvalid(tx_fifo_axis_tvalid),
|
||||
.tx_axis_tready(tx_fifo_axis_tready),
|
||||
.tx_axis_tlast(tx_fifo_axis_tlast),
|
||||
.tx_axis_tuser(tx_fifo_axis_tuser),
|
||||
.rx_axis_tdata(rx_fifo_axis_tdata),
|
||||
.rx_axis_tvalid(rx_fifo_axis_tvalid),
|
||||
.rx_axis_tlast(rx_fifo_axis_tlast),
|
||||
.rx_axis_tuser(rx_fifo_axis_tuser),
|
||||
.mii_rx_clk(mii_rx_clk),
|
||||
.mii_rxd(mii_rxd),
|
||||
.mii_rx_dv(mii_rx_dv),
|
||||
.mii_rx_er(mii_rx_er),
|
||||
.mii_tx_clk(mii_tx_clk),
|
||||
.mii_txd(mii_txd),
|
||||
.mii_tx_en(mii_tx_en),
|
||||
.mii_tx_er(mii_tx_er),
|
||||
.tx_error_underflow(tx_error_underflow_int),
|
||||
.rx_error_bad_frame(rx_error_bad_frame_int),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs_int),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
axis_async_fifo #(
|
||||
.ADDR_WIDTH(TX_FIFO_ADDR_WIDTH),
|
||||
.DATA_WIDTH(8),
|
||||
.KEEP_ENABLE(0),
|
||||
.LAST_ENABLE(1),
|
||||
.ID_ENABLE(0),
|
||||
.DEST_ENABLE(0),
|
||||
.USER_ENABLE(1),
|
||||
.USER_WIDTH(1),
|
||||
.FRAME_FIFO(TX_FRAME_FIFO),
|
||||
.USER_BAD_FRAME_VALUE(1'b1),
|
||||
.USER_BAD_FRAME_MASK(1'b1),
|
||||
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
|
||||
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
|
||||
)
|
||||
tx_fifo (
|
||||
// Common reset
|
||||
.async_rst(logic_rst | tx_rst),
|
||||
// AXI input
|
||||
.s_clk(logic_clk),
|
||||
.s_axis_tdata(tx_axis_tdata),
|
||||
.s_axis_tkeep(0),
|
||||
.s_axis_tvalid(tx_axis_tvalid),
|
||||
.s_axis_tready(tx_axis_tready),
|
||||
.s_axis_tlast(tx_axis_tlast),
|
||||
.s_axis_tid(0),
|
||||
.s_axis_tdest(0),
|
||||
.s_axis_tuser(tx_axis_tuser),
|
||||
// AXI output
|
||||
.m_clk(tx_clk),
|
||||
.m_axis_tdata(tx_fifo_axis_tdata),
|
||||
.m_axis_tkeep(),
|
||||
.m_axis_tvalid(tx_fifo_axis_tvalid),
|
||||
.m_axis_tready(tx_fifo_axis_tready),
|
||||
.m_axis_tlast(tx_fifo_axis_tlast),
|
||||
.m_axis_tid(),
|
||||
.m_axis_tdest(),
|
||||
.m_axis_tuser(tx_fifo_axis_tuser),
|
||||
// Status
|
||||
.s_status_overflow(tx_fifo_overflow),
|
||||
.s_status_bad_frame(tx_fifo_bad_frame),
|
||||
.s_status_good_frame(tx_fifo_good_frame),
|
||||
.m_status_overflow(),
|
||||
.m_status_bad_frame(),
|
||||
.m_status_good_frame()
|
||||
);
|
||||
|
||||
axis_async_fifo #(
|
||||
.ADDR_WIDTH(RX_FIFO_ADDR_WIDTH),
|
||||
.DATA_WIDTH(8),
|
||||
.KEEP_ENABLE(0),
|
||||
.LAST_ENABLE(1),
|
||||
.ID_ENABLE(0),
|
||||
.DEST_ENABLE(0),
|
||||
.USER_ENABLE(1),
|
||||
.USER_WIDTH(1),
|
||||
.FRAME_FIFO(TX_FRAME_FIFO),
|
||||
.USER_BAD_FRAME_VALUE(1'b1),
|
||||
.USER_BAD_FRAME_MASK(1'b1),
|
||||
.DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
|
||||
.DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
|
||||
)
|
||||
rx_fifo (
|
||||
// Common reset
|
||||
.async_rst(rx_rst | logic_rst),
|
||||
// AXI input
|
||||
.s_clk(rx_clk),
|
||||
.s_axis_tdata(rx_fifo_axis_tdata),
|
||||
.s_axis_tkeep(0),
|
||||
.s_axis_tvalid(rx_fifo_axis_tvalid),
|
||||
.s_axis_tready(),
|
||||
.s_axis_tlast(rx_fifo_axis_tlast),
|
||||
.s_axis_tid(0),
|
||||
.s_axis_tdest(0),
|
||||
.s_axis_tuser(rx_fifo_axis_tuser),
|
||||
// AXI output
|
||||
.m_clk(logic_clk),
|
||||
.m_axis_tdata(rx_axis_tdata),
|
||||
.m_axis_tkeep(),
|
||||
.m_axis_tvalid(rx_axis_tvalid),
|
||||
.m_axis_tready(rx_axis_tready),
|
||||
.m_axis_tlast(rx_axis_tlast),
|
||||
.m_axis_tid(),
|
||||
.m_axis_tdest(),
|
||||
.m_axis_tuser(rx_axis_tuser),
|
||||
// Status
|
||||
.s_status_overflow(),
|
||||
.s_status_bad_frame(),
|
||||
.s_status_good_frame(),
|
||||
.m_status_overflow(rx_fifo_overflow),
|
||||
.m_status_bad_frame(rx_fifo_bad_frame),
|
||||
.m_status_good_frame(rx_fifo_good_frame)
|
||||
);
|
||||
|
||||
endmodule
|
137
rtl/mii_phy_if.v
Normal file
137
rtl/mii_phy_if.v
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* MII PHY interface
|
||||
*/
|
||||
module mii_phy_if #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2"
|
||||
)
|
||||
(
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* MII interface to MAC
|
||||
*/
|
||||
output wire mac_mii_rx_clk,
|
||||
output wire mac_mii_rx_rst,
|
||||
output wire [3:0] mac_mii_rxd,
|
||||
output wire mac_mii_rx_dv,
|
||||
output wire mac_mii_rx_er,
|
||||
output wire mac_mii_tx_clk,
|
||||
output wire mac_mii_tx_rst,
|
||||
input wire [3:0] mac_mii_txd,
|
||||
input wire mac_mii_tx_en,
|
||||
input wire mac_mii_tx_er,
|
||||
|
||||
/*
|
||||
* MII interface to PHY
|
||||
*/
|
||||
input wire phy_mii_rx_clk,
|
||||
input wire [3:0] phy_mii_rxd,
|
||||
input wire phy_mii_rx_dv,
|
||||
input wire phy_mii_rx_er,
|
||||
input wire phy_mii_tx_clk,
|
||||
output wire [3:0] phy_mii_txd,
|
||||
output wire phy_mii_tx_en,
|
||||
output wire phy_mii_tx_er
|
||||
);
|
||||
|
||||
ssio_sdr_in #
|
||||
(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.WIDTH(6)
|
||||
)
|
||||
rx_ssio_sdr_inst (
|
||||
.input_clk(phy_mii_rx_clk),
|
||||
.input_d({phy_mii_rxd, phy_mii_rx_dv, phy_mii_rx_er}),
|
||||
.output_clk(mac_mii_rx_clk),
|
||||
.output_q({mac_mii_rxd, mac_mii_rx_dv, mac_mii_rx_er})
|
||||
);
|
||||
|
||||
(* IOB = "TRUE" *)
|
||||
reg [3:0] phy_mii_txd_reg = 4'd0;
|
||||
(* IOB = "TRUE" *)
|
||||
reg phy_mii_tx_en_reg = 1'b0, phy_mii_tx_er_reg = 1'b0;
|
||||
|
||||
assign phy_mii_txd = phy_mii_txd_reg;
|
||||
assign phy_mii_tx_en = phy_mii_tx_en_reg;
|
||||
assign phy_mii_tx_er = phy_mii_tx_er_reg;
|
||||
|
||||
always @(posedge mac_mii_tx_clk) begin
|
||||
phy_mii_txd_reg <= mac_mii_txd;
|
||||
phy_mii_tx_en_reg <= mac_mii_tx_en;
|
||||
phy_mii_tx_er_reg <= mac_mii_tx_er;
|
||||
end
|
||||
|
||||
generate
|
||||
|
||||
if (TARGET == "XILINX") begin
|
||||
BUFG
|
||||
mii_bufg_inst (
|
||||
.I(phy_mii_tx_clk),
|
||||
.O(mac_mii_tx_clk)
|
||||
);
|
||||
end else begin
|
||||
assign mac_mii_tx_clk = phy_mii_tx_clk;
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
// reset sync
|
||||
reg [3:0] tx_rst_reg = 4'hf;
|
||||
assign mac_mii_tx_rst = tx_rst_reg[0];
|
||||
|
||||
always @(posedge mac_mii_tx_clk or posedge rst) begin
|
||||
if (rst) begin
|
||||
tx_rst_reg <= 4'hf;
|
||||
end else begin
|
||||
tx_rst_reg <= {1'b0, tx_rst_reg[3:1]};
|
||||
end
|
||||
end
|
||||
|
||||
reg [3:0] rx_rst_reg = 4'hf;
|
||||
assign mac_mii_rx_rst = rx_rst_reg[0];
|
||||
|
||||
always @(posedge mac_mii_rx_clk or posedge rst) begin
|
||||
if (rst) begin
|
||||
rx_rst_reg <= 4'hf;
|
||||
end else begin
|
||||
rx_rst_reg <= {1'b0, rx_rst_reg[3:1]};
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
@ -23,7 +23,8 @@
|
||||
foreach mac_inst [get_cells -hier -filter {(ORIG_REF_NAME == eth_mac_1g_fifo || REF_NAME == eth_mac_1g_fifo || \
|
||||
ORIG_REF_NAME == eth_mac_10g_fifo || REF_NAME == eth_mac_10g_fifo || \
|
||||
ORIG_REF_NAME == eth_mac_1g_gmii_fifo || REF_NAME == eth_mac_1g_gmii_fifo || \
|
||||
ORIG_REF_NAME == eth_mac_1g_rgmii_fifo || REF_NAME == eth_mac_1g_rgmii_fifo)}] {
|
||||
ORIG_REF_NAME == eth_mac_1g_rgmii_fifo || REF_NAME == eth_mac_1g_rgmii_fifo || \
|
||||
ORIG_REF_NAME == eth_mac_mii_fifo || REF_NAME == eth_mac_mii_fifo)}] {
|
||||
puts "Inserting timing constraints for ethernet MAC with FIFO instance $mac_inst"
|
||||
|
||||
set sync_ffs [get_cells -hier -regexp ".*/rx_sync_reg_\[1234\]_reg\\\[\\d+\\\]" -filter "PARENT == $mac_inst"]
|
||||
|
31
syn/mii_phy_if.tcl
Normal file
31
syn/mii_phy_if.tcl
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2019 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# MII PHY IF timing constraints
|
||||
|
||||
foreach if_inst [get_cells -hier -filter {(ORIG_REF_NAME == mii_phy_if || REF_NAME == mii_phy_if)}] {
|
||||
puts "Inserting timing constraints for mii_phy_if instance $if_inst"
|
||||
|
||||
# reset synchronization
|
||||
set reset_ffs [get_cells -hier -regexp ".*/(rx|tx)_rst_reg_reg\\\[\\d\\\]" -filter "PARENT == $if_inst"]
|
||||
|
||||
set_property ASYNC_REG TRUE $reset_ffs
|
||||
set_false_path -to [get_pins -of_objects $reset_ffs -filter {IS_PRESET || IS_RESET}]
|
||||
}
|
262
tb/mii_ep.py
Normal file
262
tb/mii_ep.py
Normal file
@ -0,0 +1,262 @@
|
||||
"""
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
|
||||
class MIIFrame(object):
|
||||
def __init__(self, data=b'', error=None):
|
||||
self.data = b''
|
||||
self.error = None
|
||||
|
||||
if type(data) is MIIFrame:
|
||||
self.data = data.data
|
||||
self.error = data.error
|
||||
else:
|
||||
self.data = bytearray(data)
|
||||
|
||||
def build(self):
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
f = list(self.data)
|
||||
d = []
|
||||
er = []
|
||||
i = 0
|
||||
|
||||
assert_er = False
|
||||
if (type(self.error) is int or type(self.error) is bool) and self.error:
|
||||
assert_er = True
|
||||
self.error = None
|
||||
|
||||
while len(f) > 0:
|
||||
db = f.pop(0)
|
||||
d.append(db & 0x0f)
|
||||
d.append(db >> 4)
|
||||
if self.error is None:
|
||||
er.append(0)
|
||||
er.append(0)
|
||||
else:
|
||||
er.append(self.error[i])
|
||||
er.append(self.error[i])
|
||||
i += 1
|
||||
|
||||
if assert_er:
|
||||
er[-1] = 1
|
||||
self.error = 1
|
||||
|
||||
return d, er
|
||||
|
||||
def parse(self, d, er):
|
||||
if d is None or er is None:
|
||||
return
|
||||
|
||||
# sync and repack data
|
||||
odd = True
|
||||
sync = False
|
||||
b = 0
|
||||
be = 0
|
||||
d2 = []
|
||||
er2 = []
|
||||
for n, e in zip(d, er):
|
||||
odd = not odd
|
||||
b = (n & 0x0F) << 4 | b >> 4
|
||||
be |= e
|
||||
if not sync and b == 0xD5:
|
||||
odd = True
|
||||
sync = True
|
||||
if odd:
|
||||
d2.append(b)
|
||||
er2.append(be)
|
||||
be = False
|
||||
|
||||
self.data = bytearray(d2)
|
||||
self.error = er2
|
||||
|
||||
def __eq__(self, other):
|
||||
if type(other) is MIIFrame:
|
||||
return self.data == other.data
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return 'MIIFrame(data=%s, error=%s)' % (repr(self.data), repr(self.error))
|
||||
|
||||
def __iter__(self):
|
||||
return self.data.__iter__()
|
||||
|
||||
|
||||
class MIISource(object):
|
||||
def __init__(self):
|
||||
self.has_logic = False
|
||||
self.queue = []
|
||||
|
||||
def send(self, frame):
|
||||
self.queue.append(MIIFrame(frame))
|
||||
|
||||
def count(self):
|
||||
return len(self.queue)
|
||||
|
||||
def empty(self):
|
||||
return not self.queue
|
||||
|
||||
def create_logic(self,
|
||||
clk,
|
||||
rst,
|
||||
txd,
|
||||
tx_en,
|
||||
tx_er,
|
||||
clk_enable=True,
|
||||
name=None
|
||||
):
|
||||
|
||||
assert not self.has_logic
|
||||
|
||||
self.has_logic = True
|
||||
|
||||
assert len(txd) == 4
|
||||
|
||||
@instance
|
||||
def logic():
|
||||
frame = None
|
||||
d = []
|
||||
er = []
|
||||
ifg_cnt = 0
|
||||
|
||||
while True:
|
||||
yield clk.posedge, rst.posedge
|
||||
|
||||
if rst:
|
||||
frame = None
|
||||
txd.next = 0
|
||||
tx_en.next = 0
|
||||
tx_er.next = 0
|
||||
d = []
|
||||
er = []
|
||||
ifg_cnt = 0
|
||||
else:
|
||||
if not clk_enable:
|
||||
pass
|
||||
elif ifg_cnt > 0:
|
||||
ifg_cnt -= 1
|
||||
txd.next = 0
|
||||
tx_er.next = 0
|
||||
tx_en.next = 0
|
||||
elif len(d) > 0:
|
||||
txd.next = d.pop(0)
|
||||
tx_er.next = er.pop(0)
|
||||
tx_en.next = 1
|
||||
if len(d) == 0:
|
||||
ifg_cnt = 12*2
|
||||
elif self.queue:
|
||||
frame = MIIFrame(self.queue.pop(0))
|
||||
d, er = frame.build()
|
||||
if name is not None:
|
||||
print("[%s] Sending frame %s" % (name, repr(frame)))
|
||||
txd.next = d.pop(0)
|
||||
tx_er.next = er.pop(0)
|
||||
tx_en.next = 1
|
||||
else:
|
||||
txd.next = 0
|
||||
tx_er.next = 0
|
||||
tx_en.next = 0
|
||||
|
||||
return logic
|
||||
|
||||
|
||||
class MIISink(object):
|
||||
def __init__(self):
|
||||
self.has_logic = False
|
||||
self.queue = []
|
||||
self.sync = Signal(intbv(0))
|
||||
|
||||
def recv(self):
|
||||
if self.queue:
|
||||
return self.queue.pop(0)
|
||||
return None
|
||||
|
||||
def count(self):
|
||||
return len(self.queue)
|
||||
|
||||
def empty(self):
|
||||
return not self.queue
|
||||
|
||||
def wait(self, timeout=0):
|
||||
if self.queue:
|
||||
return
|
||||
if timeout:
|
||||
yield self.sync, delay(timeout)
|
||||
else:
|
||||
yield self.sync
|
||||
|
||||
def create_logic(self,
|
||||
clk,
|
||||
rst,
|
||||
rxd,
|
||||
rx_dv,
|
||||
rx_er,
|
||||
clk_enable=True,
|
||||
name=None
|
||||
):
|
||||
|
||||
assert not self.has_logic
|
||||
|
||||
self.has_logic = True
|
||||
|
||||
assert len(rxd) == 4
|
||||
|
||||
@instance
|
||||
def logic():
|
||||
frame = None
|
||||
d = []
|
||||
er = []
|
||||
|
||||
while True:
|
||||
yield clk.posedge, rst.posedge
|
||||
|
||||
if rst:
|
||||
frame = None
|
||||
d = []
|
||||
er = []
|
||||
else:
|
||||
if not clk_enable:
|
||||
pass
|
||||
elif rx_dv:
|
||||
if frame is None:
|
||||
frame = MIIFrame()
|
||||
d = []
|
||||
er = []
|
||||
d.append(int(rxd))
|
||||
er.append(int(rx_er))
|
||||
elif frame is not None:
|
||||
if len(d) > 0:
|
||||
frame.parse(d, er)
|
||||
self.queue.append(frame)
|
||||
self.sync.next = not self.sync
|
||||
if name is not None:
|
||||
print("[%s] Got frame %s" % (name, repr(frame)))
|
||||
frame = None
|
||||
d = []
|
||||
er = []
|
||||
|
||||
return logic
|
||||
|
301
tb/test_eth_mac_mii.py
Executable file
301
tb/test_eth_mac_mii.py
Executable file
@ -0,0 +1,301 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
import os
|
||||
|
||||
import axis_ep
|
||||
import eth_ep
|
||||
import mii_ep
|
||||
|
||||
module = 'eth_mac_mii'
|
||||
testbench = 'test_%s' % module
|
||||
|
||||
srcs = []
|
||||
|
||||
srcs.append("../rtl/%s.v" % module)
|
||||
srcs.append("../rtl/lfsr.v")
|
||||
srcs.append("../rtl/axis_gmii_rx.v")
|
||||
srcs.append("../rtl/axis_gmii_tx.v")
|
||||
srcs.append("../rtl/eth_mac_1g.v")
|
||||
srcs.append("../rtl/mii_phy_if.v")
|
||||
srcs.append("../rtl/ssio_sdr_in.v")
|
||||
srcs.append("%s.v" % testbench)
|
||||
|
||||
src = ' '.join(srcs)
|
||||
|
||||
build_cmd = "iverilog -o %s.vvp %s" % (testbench, src)
|
||||
|
||||
def bench():
|
||||
|
||||
# Parameters
|
||||
TARGET = "SIM"
|
||||
CLOCK_INPUT_STYLE = "BUFIO2"
|
||||
ENABLE_PADDING = 1
|
||||
MIN_FRAME_LENGTH = 64
|
||||
|
||||
# Inputs
|
||||
clk = Signal(bool(0))
|
||||
rst = Signal(bool(0))
|
||||
current_test = Signal(intbv(0)[8:])
|
||||
|
||||
tx_axis_tdata = Signal(intbv(0)[8:])
|
||||
tx_axis_tvalid = Signal(bool(0))
|
||||
tx_axis_tlast = Signal(bool(0))
|
||||
tx_axis_tuser = Signal(bool(0))
|
||||
mii_rx_clk = Signal(bool(0))
|
||||
mii_rxd = Signal(intbv(0)[4:])
|
||||
mii_rx_dv = Signal(bool(0))
|
||||
mii_rx_er = Signal(bool(0))
|
||||
mii_tx_clk = Signal(bool(0))
|
||||
ifg_delay = Signal(intbv(0)[8:])
|
||||
|
||||
# Outputs
|
||||
rx_clk = Signal(bool(0))
|
||||
rx_rst = Signal(bool(0))
|
||||
tx_clk = Signal(bool(0))
|
||||
tx_rst = Signal(bool(0))
|
||||
tx_axis_tready = Signal(bool(0))
|
||||
rx_axis_tdata = Signal(intbv(0)[8:])
|
||||
rx_axis_tvalid = Signal(bool(0))
|
||||
rx_axis_tlast = Signal(bool(0))
|
||||
rx_axis_tuser = Signal(bool(0))
|
||||
mii_txd = Signal(intbv(0)[4:])
|
||||
mii_tx_en = Signal(bool(0))
|
||||
mii_tx_er = Signal(bool(0))
|
||||
tx_error_underflow = Signal(bool(0))
|
||||
rx_error_bad_frame = Signal(bool(0))
|
||||
rx_error_bad_fcs = Signal(bool(0))
|
||||
|
||||
# sources and sinks
|
||||
axis_source_pause = Signal(bool(0))
|
||||
|
||||
|
||||
mii_source = mii_ep.MIISource()
|
||||
|
||||
mii_source_logic = mii_source.create_logic(
|
||||
mii_rx_clk,
|
||||
rst,
|
||||
txd=mii_rxd,
|
||||
tx_en=mii_rx_dv,
|
||||
tx_er=mii_rx_er,
|
||||
name='mii_source'
|
||||
)
|
||||
|
||||
mii_sink = mii_ep.MIISink()
|
||||
|
||||
mii_sink_logic = mii_sink.create_logic(
|
||||
mii_tx_clk,
|
||||
rst,
|
||||
rxd=mii_txd,
|
||||
rx_dv=mii_tx_en,
|
||||
rx_er=mii_tx_er,
|
||||
name='mii_sink'
|
||||
)
|
||||
|
||||
axis_source = axis_ep.AXIStreamSource()
|
||||
|
||||
axis_source_logic = axis_source.create_logic(
|
||||
mii_rx_clk, #tx_clk,
|
||||
tx_rst,
|
||||
tdata=tx_axis_tdata,
|
||||
tvalid=tx_axis_tvalid,
|
||||
tready=tx_axis_tready,
|
||||
tlast=tx_axis_tlast,
|
||||
tuser=tx_axis_tuser,
|
||||
pause=axis_source_pause,
|
||||
name='axis_source'
|
||||
)
|
||||
|
||||
axis_sink = axis_ep.AXIStreamSink()
|
||||
|
||||
axis_sink_logic = axis_sink.create_logic(
|
||||
mii_rx_clk,
|
||||
rx_rst,
|
||||
tdata=rx_axis_tdata,
|
||||
tvalid=rx_axis_tvalid,
|
||||
tlast=rx_axis_tlast,
|
||||
tuser=rx_axis_tuser,
|
||||
name='axis_sink'
|
||||
)
|
||||
|
||||
# DUT
|
||||
if os.system(build_cmd):
|
||||
raise Exception("Error running build command")
|
||||
|
||||
dut = Cosimulation(
|
||||
"vvp -m myhdl %s.vvp -lxt2" % testbench,
|
||||
clk=clk,
|
||||
rst=rst,
|
||||
current_test=current_test,
|
||||
|
||||
rx_clk=rx_clk,
|
||||
rx_rst=rx_rst,
|
||||
tx_clk=tx_clk,
|
||||
tx_rst=tx_rst,
|
||||
|
||||
tx_axis_tdata=tx_axis_tdata,
|
||||
tx_axis_tvalid=tx_axis_tvalid,
|
||||
tx_axis_tready=tx_axis_tready,
|
||||
tx_axis_tlast=tx_axis_tlast,
|
||||
tx_axis_tuser=tx_axis_tuser,
|
||||
|
||||
rx_axis_tdata=rx_axis_tdata,
|
||||
rx_axis_tvalid=rx_axis_tvalid,
|
||||
rx_axis_tlast=rx_axis_tlast,
|
||||
rx_axis_tuser=rx_axis_tuser,
|
||||
|
||||
mii_rx_clk=mii_rx_clk,
|
||||
mii_rxd=mii_rxd,
|
||||
mii_rx_dv=mii_rx_dv,
|
||||
mii_rx_er=mii_rx_er,
|
||||
|
||||
mii_tx_clk=mii_tx_clk,
|
||||
mii_txd=mii_txd,
|
||||
mii_tx_en=mii_tx_en,
|
||||
mii_tx_er=mii_tx_er,
|
||||
|
||||
tx_error_underflow=tx_error_underflow,
|
||||
rx_error_bad_frame=rx_error_bad_frame,
|
||||
rx_error_bad_fcs=rx_error_bad_fcs,
|
||||
|
||||
ifg_delay=ifg_delay
|
||||
)
|
||||
|
||||
@always(delay(4))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
phy_clk_hp = Signal(int(20))
|
||||
|
||||
@instance
|
||||
def phy_clk_gen():
|
||||
while True:
|
||||
yield delay(int(phy_clk_hp))
|
||||
mii_rx_clk.next = not mii_rx_clk
|
||||
mii_tx_clk.next = not mii_rx_clk
|
||||
|
||||
rx_error_bad_frame_asserted = Signal(bool(0))
|
||||
rx_error_bad_fcs_asserted = Signal(bool(0))
|
||||
|
||||
@always(clk.posedge)
|
||||
def monitor():
|
||||
if (rx_error_bad_frame):
|
||||
rx_error_bad_frame_asserted.next = 1
|
||||
if (rx_error_bad_fcs):
|
||||
rx_error_bad_fcs_asserted.next = 1
|
||||
|
||||
@instance
|
||||
def check():
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
rst.next = 1
|
||||
yield clk.posedge
|
||||
rst.next = 0
|
||||
yield clk.posedge
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
|
||||
ifg_delay.next = 12
|
||||
|
||||
# testbench stimulus
|
||||
|
||||
for rate in [20, 200]:
|
||||
phy_clk_hp.next = rate
|
||||
|
||||
yield delay(1000)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 1: test rx packet")
|
||||
current_test.next = 1
|
||||
|
||||
test_frame = eth_ep.EthFrame()
|
||||
test_frame.eth_dest_mac = 0xDAD1D2D3D4D5
|
||||
test_frame.eth_src_mac = 0x5A5152535455
|
||||
test_frame.eth_type = 0x8000
|
||||
test_frame.payload = bytearray(range(32))
|
||||
test_frame.update_fcs()
|
||||
|
||||
axis_frame = test_frame.build_axis_fcs()
|
||||
|
||||
mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame))
|
||||
|
||||
yield axis_sink.wait()
|
||||
rx_frame = axis_sink.recv()
|
||||
|
||||
eth_frame = eth_ep.EthFrame()
|
||||
eth_frame.parse_axis(rx_frame)
|
||||
eth_frame.update_fcs()
|
||||
|
||||
assert eth_frame == test_frame
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 2: test tx packet")
|
||||
current_test.next = 2
|
||||
|
||||
test_frame = eth_ep.EthFrame()
|
||||
test_frame.eth_dest_mac = 0xDAD1D2D3D4D5
|
||||
test_frame.eth_src_mac = 0x5A5152535455
|
||||
test_frame.eth_type = 0x8000
|
||||
test_frame.payload = bytearray(range(32))
|
||||
test_frame.update_fcs()
|
||||
|
||||
axis_frame = test_frame.build_axis()
|
||||
|
||||
axis_source.send(axis_frame)
|
||||
|
||||
yield mii_sink.wait()
|
||||
rx_frame = mii_sink.recv()
|
||||
|
||||
assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5')
|
||||
|
||||
eth_frame = eth_ep.EthFrame()
|
||||
eth_frame.parse_axis_fcs(rx_frame.data[8:])
|
||||
|
||||
print(hex(eth_frame.eth_fcs))
|
||||
print(hex(eth_frame.calc_fcs()))
|
||||
|
||||
assert len(eth_frame.payload.data) == 46
|
||||
assert eth_frame.eth_fcs == eth_frame.calc_fcs()
|
||||
assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac
|
||||
assert eth_frame.eth_src_mac == test_frame.eth_src_mac
|
||||
assert eth_frame.eth_type == test_frame.eth_type
|
||||
assert eth_frame.payload.data.index(test_frame.payload.data) == 0
|
||||
|
||||
yield delay(100)
|
||||
|
||||
raise StopSimulation
|
||||
|
||||
return instances()
|
||||
|
||||
def test_bench():
|
||||
sim = Simulation(bench())
|
||||
sim.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running test...")
|
||||
test_bench()
|
148
tb/test_eth_mac_mii.v
Normal file
148
tb/test_eth_mac_mii.v
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Testbench for eth_mac_mii
|
||||
*/
|
||||
module test_eth_mac_mii;
|
||||
|
||||
// Parameters
|
||||
parameter TARGET = "SIM";
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2";
|
||||
parameter ENABLE_PADDING = 1;
|
||||
parameter MIN_FRAME_LENGTH = 64;
|
||||
|
||||
// Inputs
|
||||
reg clk = 0;
|
||||
reg rst = 0;
|
||||
reg [7:0] current_test = 0;
|
||||
|
||||
reg [7:0] tx_axis_tdata = 0;
|
||||
reg tx_axis_tvalid = 0;
|
||||
reg tx_axis_tlast = 0;
|
||||
reg tx_axis_tuser = 0;
|
||||
reg mii_rx_clk = 0;
|
||||
reg [3:0] mii_rxd = 0;
|
||||
reg mii_rx_dv = 0;
|
||||
reg mii_rx_er = 0;
|
||||
reg mii_tx_clk = 0;
|
||||
reg [7:0] ifg_delay = 0;
|
||||
|
||||
// Outputs
|
||||
wire rx_clk;
|
||||
wire rx_rst;
|
||||
wire tx_clk;
|
||||
wire tx_rst;
|
||||
wire tx_axis_tready;
|
||||
wire [7:0] rx_axis_tdata;
|
||||
wire rx_axis_tvalid;
|
||||
wire rx_axis_tlast;
|
||||
wire rx_axis_tuser;
|
||||
wire [3:0] mii_txd;
|
||||
wire mii_tx_en;
|
||||
wire mii_tx_er;
|
||||
wire tx_error_underflow;
|
||||
wire rx_error_bad_frame;
|
||||
wire rx_error_bad_fcs;
|
||||
|
||||
initial begin
|
||||
// myhdl integration
|
||||
$from_myhdl(
|
||||
clk,
|
||||
rst,
|
||||
current_test,
|
||||
tx_axis_tdata,
|
||||
tx_axis_tvalid,
|
||||
tx_axis_tlast,
|
||||
tx_axis_tuser,
|
||||
mii_rx_clk,
|
||||
mii_rxd,
|
||||
mii_rx_dv,
|
||||
mii_rx_er,
|
||||
mii_tx_clk,
|
||||
ifg_delay
|
||||
);
|
||||
$to_myhdl(
|
||||
rx_clk,
|
||||
rx_rst,
|
||||
tx_clk,
|
||||
tx_rst,
|
||||
tx_axis_tready,
|
||||
rx_axis_tdata,
|
||||
rx_axis_tvalid,
|
||||
rx_axis_tlast,
|
||||
rx_axis_tuser,
|
||||
mii_txd,
|
||||
mii_tx_en,
|
||||
mii_tx_er,
|
||||
tx_error_underflow,
|
||||
rx_error_bad_frame,
|
||||
rx_error_bad_fcs
|
||||
);
|
||||
|
||||
// dump file
|
||||
$dumpfile("test_eth_mac_mii.lxt");
|
||||
$dumpvars(0, test_eth_mac_mii);
|
||||
end
|
||||
|
||||
eth_mac_mii #(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
UUT (
|
||||
.rst(rst),
|
||||
.rx_clk(rx_clk),
|
||||
.rx_rst(rx_rst),
|
||||
.tx_clk(tx_clk),
|
||||
.tx_rst(tx_rst),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
.mii_rx_clk(mii_rx_clk),
|
||||
.mii_rxd(mii_rxd),
|
||||
.mii_rx_dv(mii_rx_dv),
|
||||
.mii_rx_er(mii_rx_er),
|
||||
.mii_tx_clk(mii_tx_clk),
|
||||
.mii_txd(mii_txd),
|
||||
.mii_tx_en(mii_tx_en),
|
||||
.mii_tx_er(mii_tx_er),
|
||||
.tx_error_underflow(tx_error_underflow),
|
||||
.rx_error_bad_frame(rx_error_bad_frame),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
323
tb/test_eth_mac_mii_fifo.py
Executable file
323
tb/test_eth_mac_mii_fifo.py
Executable file
@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
import os
|
||||
|
||||
import axis_ep
|
||||
import eth_ep
|
||||
import mii_ep
|
||||
|
||||
module = 'eth_mac_mii_fifo'
|
||||
testbench = 'test_%s' % module
|
||||
|
||||
srcs = []
|
||||
|
||||
srcs.append("../rtl/%s.v" % module)
|
||||
srcs.append("../rtl/lfsr.v")
|
||||
srcs.append("../rtl/axis_gmii_rx.v")
|
||||
srcs.append("../rtl/axis_gmii_tx.v")
|
||||
srcs.append("../rtl/eth_mac_1g.v")
|
||||
srcs.append("../rtl/eth_mac_mii.v")
|
||||
srcs.append("../rtl/mii_phy_if.v")
|
||||
srcs.append("../rtl/ssio_sdr_in.v")
|
||||
srcs.append("../lib/axis/rtl/axis_async_fifo.v")
|
||||
srcs.append("%s.v" % testbench)
|
||||
|
||||
src = ' '.join(srcs)
|
||||
|
||||
build_cmd = "iverilog -o %s.vvp %s" % (testbench, src)
|
||||
|
||||
def bench():
|
||||
|
||||
# Parameters
|
||||
TARGET = "SIM"
|
||||
CLOCK_INPUT_STYLE = "BUFIO2"
|
||||
ENABLE_PADDING = 1
|
||||
MIN_FRAME_LENGTH = 64
|
||||
TX_FIFO_ADDR_WIDTH = 9
|
||||
RX_FIFO_ADDR_WIDTH = 9
|
||||
|
||||
# Inputs
|
||||
clk = Signal(bool(0))
|
||||
rst = Signal(bool(0))
|
||||
current_test = Signal(intbv(0)[8:])
|
||||
|
||||
logic_clk = Signal(bool(0))
|
||||
logic_rst = Signal(bool(0))
|
||||
tx_axis_tdata = Signal(intbv(0)[8:])
|
||||
tx_axis_tvalid = Signal(bool(0))
|
||||
tx_axis_tlast = Signal(bool(0))
|
||||
tx_axis_tuser = Signal(bool(0))
|
||||
rx_axis_tready = Signal(bool(0))
|
||||
mii_rx_clk = Signal(bool(0))
|
||||
mii_rxd = Signal(intbv(0)[4:])
|
||||
mii_rx_dv = Signal(bool(0))
|
||||
mii_rx_er = Signal(bool(0))
|
||||
mii_tx_clk = Signal(bool(0))
|
||||
ifg_delay = Signal(intbv(0)[8:])
|
||||
|
||||
# Outputs
|
||||
tx_axis_tready = Signal(bool(0))
|
||||
rx_axis_tdata = Signal(intbv(0)[8:])
|
||||
rx_axis_tvalid = Signal(bool(0))
|
||||
rx_axis_tlast = Signal(bool(0))
|
||||
rx_axis_tuser = Signal(bool(0))
|
||||
mii_txd = Signal(intbv(0)[4:])
|
||||
mii_tx_en = Signal(bool(0))
|
||||
mii_tx_er = Signal(bool(0))
|
||||
tx_error_underflow = Signal(bool(0))
|
||||
tx_fifo_overflow = Signal(bool(0))
|
||||
tx_fifo_bad_frame = Signal(bool(0))
|
||||
tx_fifo_good_frame = Signal(bool(0))
|
||||
rx_error_bad_frame = Signal(bool(0))
|
||||
rx_error_bad_fcs = Signal(bool(0))
|
||||
rx_fifo_overflow = Signal(bool(0))
|
||||
rx_fifo_bad_frame = Signal(bool(0))
|
||||
rx_fifo_good_frame = Signal(bool(0))
|
||||
|
||||
# sources and sinks
|
||||
axis_source_pause = Signal(bool(0))
|
||||
axis_sink_pause = Signal(bool(0))
|
||||
|
||||
mii_source = mii_ep.MIISource()
|
||||
|
||||
mii_source_logic = mii_source.create_logic(
|
||||
mii_rx_clk,
|
||||
rst,
|
||||
txd=mii_rxd,
|
||||
tx_en=mii_rx_dv,
|
||||
tx_er=mii_rx_er,
|
||||
name='mii_source'
|
||||
)
|
||||
|
||||
mii_sink = mii_ep.MIISink()
|
||||
|
||||
mii_sink_logic = mii_sink.create_logic(
|
||||
mii_tx_clk,
|
||||
rst,
|
||||
rxd=mii_txd,
|
||||
rx_dv=mii_tx_en,
|
||||
rx_er=mii_tx_er,
|
||||
name='mii_sink'
|
||||
)
|
||||
|
||||
axis_source = axis_ep.AXIStreamSource()
|
||||
|
||||
axis_source_logic = axis_source.create_logic(
|
||||
logic_clk,
|
||||
logic_rst,
|
||||
tdata=tx_axis_tdata,
|
||||
tvalid=tx_axis_tvalid,
|
||||
tready=tx_axis_tready,
|
||||
tlast=tx_axis_tlast,
|
||||
tuser=tx_axis_tuser,
|
||||
pause=axis_source_pause,
|
||||
name='axis_source'
|
||||
)
|
||||
|
||||
axis_sink = axis_ep.AXIStreamSink()
|
||||
|
||||
axis_sink_logic = axis_sink.create_logic(
|
||||
logic_clk,
|
||||
logic_rst,
|
||||
tdata=rx_axis_tdata,
|
||||
tvalid=rx_axis_tvalid,
|
||||
tready=rx_axis_tready,
|
||||
tlast=rx_axis_tlast,
|
||||
tuser=rx_axis_tuser,
|
||||
pause=axis_sink_pause,
|
||||
name='axis_sink'
|
||||
)
|
||||
|
||||
# DUT
|
||||
if os.system(build_cmd):
|
||||
raise Exception("Error running build command")
|
||||
|
||||
dut = Cosimulation(
|
||||
"vvp -m myhdl %s.vvp -lxt2" % testbench,
|
||||
clk=clk,
|
||||
rst=rst,
|
||||
current_test=current_test,
|
||||
|
||||
logic_clk=logic_clk,
|
||||
logic_rst=logic_rst,
|
||||
|
||||
tx_axis_tdata=tx_axis_tdata,
|
||||
tx_axis_tvalid=tx_axis_tvalid,
|
||||
tx_axis_tready=tx_axis_tready,
|
||||
tx_axis_tlast=tx_axis_tlast,
|
||||
tx_axis_tuser=tx_axis_tuser,
|
||||
|
||||
rx_axis_tdata=rx_axis_tdata,
|
||||
rx_axis_tready=rx_axis_tready,
|
||||
rx_axis_tvalid=rx_axis_tvalid,
|
||||
rx_axis_tlast=rx_axis_tlast,
|
||||
rx_axis_tuser=rx_axis_tuser,
|
||||
|
||||
mii_rx_clk=mii_rx_clk,
|
||||
mii_rxd=mii_rxd,
|
||||
mii_rx_dv=mii_rx_dv,
|
||||
mii_rx_er=mii_rx_er,
|
||||
|
||||
mii_tx_clk=mii_tx_clk,
|
||||
mii_txd=mii_txd,
|
||||
mii_tx_en=mii_tx_en,
|
||||
mii_tx_er=mii_tx_er,
|
||||
|
||||
tx_error_underflow=tx_error_underflow,
|
||||
tx_fifo_overflow=tx_fifo_overflow,
|
||||
tx_fifo_bad_frame=tx_fifo_bad_frame,
|
||||
tx_fifo_good_frame=tx_fifo_good_frame,
|
||||
rx_error_bad_frame=rx_error_bad_frame,
|
||||
rx_error_bad_fcs=rx_error_bad_fcs,
|
||||
rx_fifo_overflow=rx_fifo_overflow,
|
||||
rx_fifo_bad_frame=rx_fifo_bad_frame,
|
||||
rx_fifo_good_frame=rx_fifo_good_frame,
|
||||
|
||||
ifg_delay=ifg_delay
|
||||
)
|
||||
|
||||
@always(delay(4))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
logic_clk.next = not clk
|
||||
|
||||
phy_clk_hp = Signal(int(20))
|
||||
|
||||
@instance
|
||||
def phy_clk_gen():
|
||||
while True:
|
||||
yield delay(int(phy_clk_hp))
|
||||
mii_rx_clk.next = not mii_rx_clk
|
||||
mii_tx_clk.next = not mii_rx_clk
|
||||
|
||||
rx_error_bad_frame_asserted = Signal(bool(0))
|
||||
rx_error_bad_fcs_asserted = Signal(bool(0))
|
||||
|
||||
@always(clk.posedge)
|
||||
def monitor():
|
||||
if (rx_error_bad_frame):
|
||||
rx_error_bad_frame_asserted.next = 1
|
||||
if (rx_error_bad_fcs):
|
||||
rx_error_bad_fcs_asserted.next = 1
|
||||
|
||||
clk_enable_rate = Signal(int(0))
|
||||
clk_enable_div = Signal(int(0))
|
||||
|
||||
@instance
|
||||
def check():
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
rst.next = 1
|
||||
logic_rst.next = 1
|
||||
yield clk.posedge
|
||||
rst.next = 0
|
||||
logic_rst.next = 0
|
||||
yield clk.posedge
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
|
||||
ifg_delay.next = 12
|
||||
|
||||
# testbench stimulus
|
||||
|
||||
for rate in [20, 200]:
|
||||
phy_clk_hp.next = rate
|
||||
|
||||
yield delay(1000)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 1: test rx packet")
|
||||
current_test.next = 1
|
||||
|
||||
test_frame = eth_ep.EthFrame()
|
||||
test_frame.eth_dest_mac = 0xDAD1D2D3D4D5
|
||||
test_frame.eth_src_mac = 0x5A5152535455
|
||||
test_frame.eth_type = 0x8000
|
||||
test_frame.payload = bytearray(range(32))
|
||||
test_frame.update_fcs()
|
||||
|
||||
axis_frame = test_frame.build_axis_fcs()
|
||||
|
||||
mii_source.send(b'\x55\x55\x55\x55\x55\x55\x55\xD5'+bytearray(axis_frame))
|
||||
|
||||
yield axis_sink.wait()
|
||||
rx_frame = axis_sink.recv()
|
||||
|
||||
eth_frame = eth_ep.EthFrame()
|
||||
eth_frame.parse_axis(rx_frame)
|
||||
eth_frame.update_fcs()
|
||||
|
||||
assert eth_frame == test_frame
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 2: test tx packet")
|
||||
current_test.next = 2
|
||||
|
||||
test_frame = eth_ep.EthFrame()
|
||||
test_frame.eth_dest_mac = 0xDAD1D2D3D4D5
|
||||
test_frame.eth_src_mac = 0x5A5152535455
|
||||
test_frame.eth_type = 0x8000
|
||||
test_frame.payload = bytearray(range(32))
|
||||
test_frame.update_fcs()
|
||||
|
||||
axis_frame = test_frame.build_axis()
|
||||
|
||||
axis_source.send(axis_frame)
|
||||
|
||||
yield mii_sink.wait()
|
||||
rx_frame = mii_sink.recv()
|
||||
|
||||
assert rx_frame.data[0:8] == bytearray(b'\x55\x55\x55\x55\x55\x55\x55\xD5')
|
||||
|
||||
eth_frame = eth_ep.EthFrame()
|
||||
eth_frame.parse_axis_fcs(rx_frame.data[8:])
|
||||
|
||||
print(hex(eth_frame.eth_fcs))
|
||||
print(hex(eth_frame.calc_fcs()))
|
||||
|
||||
assert len(eth_frame.payload.data) == 46
|
||||
assert eth_frame.eth_fcs == eth_frame.calc_fcs()
|
||||
assert eth_frame.eth_dest_mac == test_frame.eth_dest_mac
|
||||
assert eth_frame.eth_src_mac == test_frame.eth_src_mac
|
||||
assert eth_frame.eth_type == test_frame.eth_type
|
||||
assert eth_frame.payload.data.index(test_frame.payload.data) == 0
|
||||
|
||||
yield delay(100)
|
||||
|
||||
raise StopSimulation
|
||||
|
||||
return instances()
|
||||
|
||||
def test_bench():
|
||||
sim = Simulation(bench())
|
||||
sim.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running test...")
|
||||
test_bench()
|
167
tb/test_eth_mac_mii_fifo.v
Normal file
167
tb/test_eth_mac_mii_fifo.v
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2019 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Testbench for eth_mac_mii_fifo
|
||||
*/
|
||||
module test_eth_mac_mii_fifo;
|
||||
|
||||
// Parameters
|
||||
parameter TARGET = "SIM";
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2";
|
||||
parameter ENABLE_PADDING = 1;
|
||||
parameter MIN_FRAME_LENGTH = 64;
|
||||
parameter TX_FIFO_ADDR_WIDTH = 9;
|
||||
parameter RX_FIFO_ADDR_WIDTH = 9;
|
||||
|
||||
// Inputs
|
||||
reg clk = 0;
|
||||
reg rst = 0;
|
||||
reg [7:0] current_test = 0;
|
||||
|
||||
reg logic_clk = 0;
|
||||
reg logic_rst = 0;
|
||||
reg [7:0] tx_axis_tdata = 0;
|
||||
reg tx_axis_tvalid = 0;
|
||||
reg tx_axis_tlast = 0;
|
||||
reg tx_axis_tuser = 0;
|
||||
reg rx_axis_tready = 0;
|
||||
reg mii_rx_clk = 0;
|
||||
reg [3:0] mii_rxd = 0;
|
||||
reg mii_rx_dv = 0;
|
||||
reg mii_rx_er = 0;
|
||||
reg mii_tx_clk = 0;
|
||||
reg [7:0] ifg_delay = 0;
|
||||
|
||||
// Outputs
|
||||
wire tx_axis_tready;
|
||||
wire [7:0] rx_axis_tdata;
|
||||
wire rx_axis_tvalid;
|
||||
wire rx_axis_tlast;
|
||||
wire rx_axis_tuser;
|
||||
wire [3:0] mii_txd;
|
||||
wire mii_tx_en;
|
||||
wire mii_tx_er;
|
||||
wire tx_error_underflow;
|
||||
wire tx_fifo_overflow;
|
||||
wire tx_fifo_bad_frame;
|
||||
wire tx_fifo_good_frame;
|
||||
wire rx_error_bad_frame;
|
||||
wire rx_error_bad_fcs;
|
||||
wire rx_fifo_overflow;
|
||||
wire rx_fifo_bad_frame;
|
||||
wire rx_fifo_good_frame;
|
||||
|
||||
initial begin
|
||||
// myhdl integration
|
||||
$from_myhdl(
|
||||
clk,
|
||||
rst,
|
||||
current_test,
|
||||
logic_clk,
|
||||
logic_rst,
|
||||
tx_axis_tdata,
|
||||
tx_axis_tvalid,
|
||||
tx_axis_tlast,
|
||||
tx_axis_tuser,
|
||||
rx_axis_tready,
|
||||
mii_rx_clk,
|
||||
mii_rxd,
|
||||
mii_rx_dv,
|
||||
mii_rx_er,
|
||||
mii_tx_clk,
|
||||
ifg_delay
|
||||
);
|
||||
$to_myhdl(
|
||||
tx_axis_tready,
|
||||
rx_axis_tdata,
|
||||
rx_axis_tvalid,
|
||||
rx_axis_tlast,
|
||||
rx_axis_tuser,
|
||||
mii_txd,
|
||||
mii_tx_en,
|
||||
mii_tx_er,
|
||||
tx_error_underflow,
|
||||
tx_fifo_overflow,
|
||||
tx_fifo_bad_frame,
|
||||
tx_fifo_good_frame,
|
||||
rx_error_bad_frame,
|
||||
rx_error_bad_fcs,
|
||||
rx_fifo_overflow,
|
||||
rx_fifo_bad_frame,
|
||||
rx_fifo_good_frame
|
||||
);
|
||||
|
||||
// dump file
|
||||
$dumpfile("test_eth_mac_mii_fifo.lxt");
|
||||
$dumpvars(0, test_eth_mac_mii_fifo);
|
||||
end
|
||||
|
||||
eth_mac_mii_fifo #(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH),
|
||||
.TX_FIFO_ADDR_WIDTH(TX_FIFO_ADDR_WIDTH),
|
||||
.RX_FIFO_ADDR_WIDTH(RX_FIFO_ADDR_WIDTH)
|
||||
)
|
||||
UUT (
|
||||
.rst(rst),
|
||||
.logic_clk(logic_clk),
|
||||
.logic_rst(logic_rst),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tready(rx_axis_tready),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
.mii_rx_clk(mii_rx_clk),
|
||||
.mii_rxd(mii_rxd),
|
||||
.mii_rx_dv(mii_rx_dv),
|
||||
.mii_rx_er(mii_rx_er),
|
||||
.mii_tx_clk(mii_tx_clk),
|
||||
.mii_txd(mii_txd),
|
||||
.mii_tx_en(mii_tx_en),
|
||||
.mii_tx_er(mii_tx_er),
|
||||
.tx_error_underflow(tx_error_underflow),
|
||||
.tx_fifo_overflow(tx_fifo_overflow),
|
||||
.tx_fifo_bad_frame(tx_fifo_bad_frame),
|
||||
.tx_fifo_good_frame(tx_fifo_good_frame),
|
||||
.rx_error_bad_frame(rx_error_bad_frame),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs),
|
||||
.rx_fifo_overflow(rx_fifo_overflow),
|
||||
.rx_fifo_bad_frame(rx_fifo_bad_frame),
|
||||
.rx_fifo_good_frame(rx_fifo_good_frame),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
Loading…
x
Reference in New Issue
Block a user