mdio_regs: Add support for counters
This adds support for counters for interesting conditions (disconnects, PMD phase wraparounds, errors, etc). All the counters are 15 bits instead of 16, because 16-bit counters have an fmax of 110MHz or so. All the counters live behind a condition because they ~double the number of resources used. Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
parent
ec08287853
commit
7d35e07401
109
rtl/mdio_regs.v
109
rtl/mdio_regs.v
|
@ -16,6 +16,10 @@ module mdio_regs (
|
|||
|
||||
/* Control signals */
|
||||
input link_status,
|
||||
input positive_wraparound,
|
||||
input negative_wraparound,
|
||||
input false_carrier,
|
||||
input symbol_error,
|
||||
output reg loopback,
|
||||
output reg pdown,
|
||||
output reg isolate,
|
||||
|
@ -28,19 +32,39 @@ module mdio_regs (
|
|||
parameter [3:0] REVISION = 0;
|
||||
/*
|
||||
* Normally, this module will assert err when read/writing to an
|
||||
* unknown register. The master will detect this and won't drive MDIO
|
||||
* line. However, this might be undesirable if there is no external
|
||||
* MDIO bus. Setting this parameter to 0 will cause it to ack all
|
||||
* transactions. Writes to unknown registers will be ignored, and
|
||||
* unknown register. The master will detect this and won't drive the
|
||||
* MDIO line. However, this might be undesirable if there is no
|
||||
* external MDIO bus. Setting this parameter to 0 will cause it to ack
|
||||
* all transactions. Writes to unknown registers will be ignored, and
|
||||
* reads from unknown registers will yield 16'hffff, emulating
|
||||
* a pull-up on MDIO.
|
||||
*/
|
||||
parameter EMULATE_PULLUP = 0;
|
||||
/* Enable counter registers */
|
||||
parameter ENABLE_COUNTERS = 1;
|
||||
/*
|
||||
* Number of bits in counters; we can't meet timing with 16 bits, so
|
||||
* use a smaller default.
|
||||
*/
|
||||
parameter COUNTER_WIDTH = 15;
|
||||
|
||||
/* c22 Basic Mode Control Register */
|
||||
localparam BMCR = 0;
|
||||
/* c22 Basic Mode Status Register */
|
||||
localparam BMSR = 1;
|
||||
/* c22 Phy Identifier */
|
||||
localparam ID1 = 2;
|
||||
localparam ID2 = 3;
|
||||
/* Negative Wraparound Counter Register */
|
||||
localparam NWCR = 16;
|
||||
/* Positive Wraparound Counter Register */
|
||||
localparam PWCR = 17;
|
||||
/* Disconnect Counter Register */
|
||||
localparam DCR = 18;
|
||||
/* False Carrier Counter Register */
|
||||
localparam FCCR = 19;
|
||||
/* Symbol Error Counter Register */
|
||||
localparam SECR = 21;
|
||||
|
||||
localparam BMCR_RESET = 15;
|
||||
localparam BMCR_LOOPBACK = 14;
|
||||
|
@ -57,10 +81,13 @@ module mdio_regs (
|
|||
localparam BMSR_EXTCAP = 0;
|
||||
|
||||
integer i;
|
||||
reg duplex, link_status_latched;
|
||||
reg duplex, false_carrier_last, false_carrier_event;
|
||||
reg link_status_latched, link_status_latched_next, link_status_last, disconnect;
|
||||
reg loopback_next, pdown_next, isolate_next, duplex_next, coltest_next;
|
||||
reg link_status_latched_next;
|
||||
reg [15:0] data_read_next;
|
||||
/* Can't meet timing at 16 bits wide */
|
||||
reg [COUNTER_WIDTH-1:0] nwc, pwc, dc, fcc, sec;
|
||||
reg [COUNTER_WIDTH-1:0] nwc_next, pwc_next, dc_next, fcc_next, sec_next;
|
||||
|
||||
initial begin
|
||||
loopback = 0;
|
||||
|
@ -69,6 +96,14 @@ module mdio_regs (
|
|||
duplex = 0;
|
||||
coltest = 0;
|
||||
link_status_latched = 0;
|
||||
link_status_last = 0;
|
||||
if (ENABLE_COUNTERS) begin
|
||||
nwc = 0;
|
||||
pwc = 0;
|
||||
dc = 0;
|
||||
fcc = 0;
|
||||
sec = 0;
|
||||
end
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
|
@ -78,6 +113,22 @@ module mdio_regs (
|
|||
duplex_next = duplex;
|
||||
coltest_next = coltest;
|
||||
link_status_latched_next = link_status_latched && link_status;
|
||||
disconnect = link_status_last && !link_status;
|
||||
false_carrier_event = false_carrier && !false_carrier_last;
|
||||
|
||||
if (ENABLE_COUNTERS) begin
|
||||
nwc_next = nwc;
|
||||
pwc_next = pwc;
|
||||
dc_next = dc;
|
||||
fcc_next = fcc;
|
||||
sec_next = sec;
|
||||
|
||||
if (!(&nwc)) nwc_next = nwc + negative_wraparound;
|
||||
if (!(&pwc)) pwc_next = pwc + positive_wraparound;
|
||||
if (!(&dc)) dc_next = dc + disconnect;
|
||||
if (!(&fcc)) fcc_next = fcc + false_carrier_event;
|
||||
if (!(&sec)) sec_next = sec + symbol_error;
|
||||
end
|
||||
|
||||
data_read_next = 0;
|
||||
ack = cyc && stb;
|
||||
|
@ -105,6 +156,13 @@ module mdio_regs (
|
|||
duplex_next = 0;
|
||||
coltest_next = 0;
|
||||
link_status_latched_next = link_status;
|
||||
if (ENABLE_COUNTERS) begin
|
||||
nwc_next = negative_wraparound;
|
||||
pwc_next = positive_wraparound;
|
||||
dc_next = disconnect;
|
||||
fcc_next = false_carrier_event;
|
||||
sec_next = symbol_error;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -127,6 +185,36 @@ module mdio_regs (
|
|||
for (i = 0; i < 6; i = i + 1)
|
||||
data_read_next[i + 4] = OUI[23 - i];
|
||||
end
|
||||
NWCR: if (ENABLE_COUNTERS) begin
|
||||
data_read_next = nwc;
|
||||
|
||||
if (cyc && stb)
|
||||
nwc_next = we ? data_write : negative_wraparound;
|
||||
end
|
||||
PWCR: if (ENABLE_COUNTERS) begin
|
||||
data_read_next = pwc;
|
||||
|
||||
if (cyc && stb)
|
||||
pwc_next = we ? data_write : positive_wraparound;
|
||||
end
|
||||
DCR: if (ENABLE_COUNTERS) begin
|
||||
data_read_next = dc;
|
||||
|
||||
if (cyc && stb)
|
||||
dc_next = we ? data_write : disconnect;
|
||||
end
|
||||
FCCR: if (ENABLE_COUNTERS) begin
|
||||
data_read_next = fcc;
|
||||
|
||||
if (cyc && stb)
|
||||
fcc_next = we ? data_write : false_carrier_event;
|
||||
end
|
||||
SECR: if (ENABLE_COUNTERS) begin
|
||||
data_read_next = sec;
|
||||
|
||||
if (cyc && stb)
|
||||
sec_next = we ? data_write : symbol_error;
|
||||
end
|
||||
default: begin
|
||||
if (EMULATE_PULLUP) begin
|
||||
data_read_next = 16'hFFFF;
|
||||
|
@ -146,7 +234,16 @@ module mdio_regs (
|
|||
duplex <= duplex_next;
|
||||
coltest <= coltest_next;
|
||||
link_status_latched <= link_status_latched_next;
|
||||
link_status_last <= link_status;
|
||||
false_carrier_last <= false_carrier;
|
||||
data_read <= data_read_next;
|
||||
if (ENABLE_COUNTERS) begin
|
||||
nwc <= nwc_next;
|
||||
pwc <= pwc_next;
|
||||
dc <= dc_next;
|
||||
fcc <= fcc_next;
|
||||
sec <= sec_next;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -14,6 +14,11 @@ BMSR = 1
|
|||
PHYID1 = 2
|
||||
PHYID2 = 3
|
||||
EXTSTATUS = 15
|
||||
NWCR = 16
|
||||
PWCR = 17
|
||||
DCR = 18
|
||||
FCCR = 19
|
||||
SECR = 21
|
||||
|
||||
BMCR_RESET = BIT(15)
|
||||
BMCR_LOOPBACK = BIT(14)
|
||||
|
@ -34,6 +39,10 @@ async def test_mdio(regs):
|
|||
regs.cyc.value = 1
|
||||
regs.stb.value = 0
|
||||
regs.link_status.value = 1
|
||||
regs.positive_wraparound.value = 0
|
||||
regs.negative_wraparound.value = 0
|
||||
regs.false_carrier.value = 0
|
||||
regs.symbol_error.value = 0
|
||||
await Timer(1)
|
||||
await cocotb.start(Clock(regs.clk, 8, units='ns').start())
|
||||
|
||||
|
@ -93,3 +102,24 @@ async def test_mdio(regs):
|
|||
# I'm pretty sure this register will never be implemented
|
||||
assert await xfer(EXTSTATUS) is None
|
||||
assert await xfer(EXTSTATUS, 0) is None
|
||||
|
||||
async def counter_test(reg, signal, edge_triggered=False, active_high=True):
|
||||
signal.value = 1 if active_high else 0
|
||||
assert await xfer(reg) == 1
|
||||
await xfer(reg, 0xfffe)
|
||||
if edge_triggered:
|
||||
signal.value = 0 if active_high else 1
|
||||
await FallingEdge(regs.clk)
|
||||
if edge_triggered:
|
||||
signal.value = 1 if active_high else 0
|
||||
await FallingEdge(regs.clk)
|
||||
signal.value = 0 if active_high else 1
|
||||
assert await xfer(reg) == 0x7fff
|
||||
assert await xfer(reg) == 0
|
||||
|
||||
await counter_test(NWCR, regs.negative_wraparound)
|
||||
await counter_test(PWCR, regs.positive_wraparound)
|
||||
await xfer(DCR) # Clear DCR from the BMSR testing
|
||||
await counter_test(DCR, regs.link_status, True, False)
|
||||
await counter_test(FCCR, regs.false_carrier, True)
|
||||
await counter_test(SECR, regs.symbol_error)
|
||||
|
|
Loading…
Reference in New Issue