From fb751eb7fbff402fdaa4020dd2e4526c1ce5a146 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Sun, 28 Aug 2022 18:43:23 -0400 Subject: [PATCH] mii_io: Add isolation support The specification requires that the MII be isolated before the STA clears the BMCR.ISOLATE bit. Add support for this to the MII I/O modules. Signed-off-by: Sean Anderson --- rtl/mii_io_rx.v | 31 ++++++++++++++++++++++--------- rtl/mii_io_tx.v | 24 ++++++++++++++++-------- tb/mii_io_rx.py | 10 +++++++++- tb/mii_io_tx.py | 8 ++++++++ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/rtl/mii_io_rx.v b/rtl/mii_io_rx.v index 9037f9b..c3d27e2 100644 --- a/rtl/mii_io_rx.v +++ b/rtl/mii_io_rx.v @@ -7,8 +7,10 @@ `include "io.vh" module mii_io_rx ( - /* On-chip */ input clk, + input isolate, + + /* On-chip */ input ce, input valid, input err, @@ -56,28 +58,31 @@ module mii_io_rx ( `ifdef SYNTHESIS SB_IO #( - .PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_DDR) + .PIN_TYPE(`PIN_OUTPUT_ENABLE | `PIN_OUTPUT_DDR) ) rx_clk_pin ( .PACKAGE_PIN(rx_clk), .OUTPUT_CLK(clk), + .OUTPUT_ENABLE(!isolate), .D_OUT_0(rx_clk_p_next), .D_OUT_1(rx_clk_n) ); SB_IO #( - .PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_REGISTERED) + .PIN_TYPE(`PIN_OUTPUT_ENABLE | `PIN_OUTPUT_REGISTERED) ) rx_dv_pin ( .PACKAGE_PIN(rx_dv), .CLOCK_ENABLE(ce), + .OUTPUT_ENABLE(!isolate), .OUTPUT_CLK(clk), .D_OUT_0(valid) ); SB_IO #( - .PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_REGISTERED) + .PIN_TYPE(`PIN_OUTPUT_ENABLE | `PIN_OUTPUT_REGISTERED) ) rx_er_pin ( .PACKAGE_PIN(rx_er), .CLOCK_ENABLE(ce), + .OUTPUT_ENABLE(!isolate), .OUTPUT_CLK(clk), .D_OUT_0(err) ); @@ -85,10 +90,11 @@ module mii_io_rx ( genvar i; generate for (i = 0; i < 4; i = i + 1) begin SB_IO #( - .PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_REGISTERED) + .PIN_TYPE(`PIN_OUTPUT_ENABLE | `PIN_OUTPUT_REGISTERED) ) rxd_pin ( .PACKAGE_PIN(rxd[i]), .CLOCK_ENABLE(ce), + .OUTPUT_ENABLE(!isolate), .OUTPUT_CLK(clk), .D_OUT_0(data[i]) ); @@ -96,16 +102,23 @@ module mii_io_rx ( endgenerate `else always @(posedge clk) begin - rx_clk <= rx_clk_p_next; - if (ce) begin + if (isolate) begin + rx_dv <= 1'bz; + rx_er <= 1'bz; + rxd <= 4'bz; + end else if (ce) begin rx_dv <= valid; rx_er <= err; rxd <= data; end end - always @(negedge clk) - rx_clk <= rx_clk_n; + always @(posedge clk, negedge clk) begin + if (isolate) + rx_clk <= 1'bz; + else + rx_clk <= clk ? rx_clk_p_next : rx_clk_n; + end `endif `DUMP(0) diff --git a/rtl/mii_io_tx.v b/rtl/mii_io_tx.v index 79eaa99..2fa0d4f 100644 --- a/rtl/mii_io_tx.v +++ b/rtl/mii_io_tx.v @@ -7,8 +7,10 @@ `include "io.vh" module mii_io_tx ( - /* On-chip */ input clk, + input isolate, + + /* On-chip */ output reg ce, output reg enable, output reg err, @@ -21,7 +23,7 @@ module mii_io_tx ( input [3:0] txd ); - reg ce_next; + reg ce_next, raw_enable; reg tx_clk_p_next, tx_clk_n, tx_clk_n_next; reg [2:0] counter, counter_next; /* I have no idea why we need to use initial... */ @@ -44,6 +46,8 @@ module mii_io_tx ( counter_next = 4; end endcase + + enable = raw_enable && !isolate; end always @(posedge clk) begin @@ -54,10 +58,11 @@ module mii_io_tx ( `ifdef SYNTHESIS SB_IO #( - .PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_DDR) + .PIN_TYPE(`PIN_OUTPUT_ENABLE | `PIN_OUTPUT_DDR) ) tx_clk_pin ( .PACKAGE_PIN(tx_clk), .OUTPUT_CLK(clk), + .OUTPUT_ENABLE(!isolate), .D_OUT_0(tx_clk_p_next), .D_OUT_1(tx_clk_n) ); @@ -68,7 +73,7 @@ module mii_io_tx ( .PACKAGE_PIN(tx_en), .CLOCK_ENABLE(ce_next), .INPUT_CLK(clk), - .D_IN_0(enable) + .D_IN_0(raw_enable) ); SB_IO #( @@ -94,16 +99,19 @@ module mii_io_tx ( endgenerate `else always @(posedge clk) begin - tx_clk <= tx_clk_p_next; if (ce_next) begin - enable <= tx_en; + raw_enable <= tx_en; err <= tx_er; data <= txd; end end - always @(negedge clk) - tx_clk <= tx_clk_n; + always @(posedge clk, negedge clk) begin + if (isolate) + tx_clk <= 1'bz; + else + tx_clk <= clk ? tx_clk_p_next : tx_clk_n; + end `endif `DUMP(0) diff --git a/tb/mii_io_rx.py b/tb/mii_io_rx.py index 1a2eae3..0d02e0e 100644 --- a/tb/mii_io_rx.py +++ b/tb/mii_io_rx.py @@ -12,10 +12,11 @@ from .util import ClockEnable @cocotb.test(timeout_time=500, timeout_unit='ns') async def test_io(io): + io.isolate.value = 0 + io.ce.value = 0 io.valid.value = LogicArray('X') io.err.value = LogicArray('X') io.data.value = LogicArray('X' * 4) - io.ce.value = 0 await Timer(1) await cocotb.start(Clock(io.clk, 8, units='ns').start()) await ClockCycles(io.clk, 1) @@ -78,3 +79,10 @@ async def test_io(io): await recv_datum(0, 1, 8) await recv_datum(1, 0, 9) await recv_datum(0, 1, 10) + + io.isolate.value = 1 + await FallingEdge(io.clk) + assert io.rx_clk.value.binstr == 'z' + assert io.rx_dv.value.binstr == 'z' + assert io.rx_er.value.binstr == 'z' + assert io.rxd.value.binstr == 'zzzz' diff --git a/tb/mii_io_tx.py b/tb/mii_io_tx.py index 2fef5d8..d20c255 100644 --- a/tb/mii_io_tx.py +++ b/tb/mii_io_tx.py @@ -12,6 +12,7 @@ from .util import ClockEnable @cocotb.test(timeout_time=500, timeout_unit='ns') async def test_io(io): + io.isolate.value = 0 io.tx_en.value = LogicArray('X') io.tx_er.value = LogicArray('X') io.txd.value = LogicArray('X' * 4) @@ -49,3 +50,10 @@ async def test_io(io): await recv_datum(0, 1, 3) await recv_datum(1, 0, 4) await recv_datum(0, 1, 5) + + io.isolate.value = 1 + io.tx_en.value = 1 + await RisingEdge(io.clk) + assert io.tx_clk.value.binstr == 'z' + await RisingEdge(io.ce) + assert not io.enable.value