1
0
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:
Alex Forencich 2019-03-28 19:18:03 -07:00
parent 0ca8c9a59b
commit 8e2d936884
10 changed files with 1849 additions and 1 deletions

166
rtl/eth_mac_mii.v Normal file
View 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
View 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
View 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

View File

@ -23,7 +23,8 @@
foreach mac_inst [get_cells -hier -filter {(ORIG_REF_NAME == eth_mac_1g_fifo || REF_NAME == eth_mac_1g_fifo || \ 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_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_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" 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"] 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
View 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
View 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
View 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
View 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
View 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
View 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