Add DP83223-based PMD

This adds the integrated PMD module to be used with the DP83223. It
contains NRZI en/decoding as well as the I/O interfaces. The rx I/O was
added a while back, and the tx is just the I/O cell.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2022-11-30 18:05:30 -05:00
parent f50f5b688f
commit 2eac757fd7
3 changed files with 157 additions and 0 deletions

View File

@ -102,6 +102,7 @@ MODULES += nrzi_encode
MODULES += pcs_rx
MODULES += pcs_tx
MODULES += phy_core
MODULES += pmd_dp83223
MODULES += pmd_dp83223_rx
MODULES += scramble

79
rtl/pmd_dp83223.v Normal file
View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
*/
`include "common.vh"
`include "io.vh"
`timescale 1ns/1ps
module pmd_dp83223 (
input clk_250,
input clk_125,
/* I/O */
input indicate_data,
input signal_detect,
output reg request_data,
/* "PMD" */
input tx_data,
output [1:0] rx_data,
output [1:0] rx_data_valid,
output reg signal_status,
/* Control */
input loopback
);
wire tx_nrzi;
nrzi_encode encoder (
.clk(clk_125),
.nrz(tx_data),
.nrzi(tx_nrzi)
);
`ifdef SYNTHESIS
SB_IO #(
.PIN_TYPE(`PIN_OUTPUT_ALWAYS | `PIN_OUTPUT_REGISTERED),
) tx_data_pin (
.PACKAGE_PIN(request_data),
.OUTPUT_CLK(clk_125),
.D_OUT_0(loopback ? 1'b0 : tx_nrzi)
);
`else
always @(posedge clk_125)
request_data <= loopback ? 1'b0 : tx_nrzi;
`endif
wire signal_status_nrzi;
wire [1:0] rx_nrzi, rx_nrzi_valid;
pmd_dp83223_rx rx (
.clk_125(clk_125),
.clk_250(clk_250),
.signal_detect(signal_detect),
.indicate_data(indicate_data),
.signal_status(signal_status_nrzi),
.rx_data(rx_nrzi),
.rx_data_valid(rx_nrzi_valid)
);
nrzi_decode decoder (
.clk(clk_125),
.rst(loopback ? 1'b0 : !signal_status_nrzi),
.nrzi(loopback ? { tx_nrzi, 1'bX } : rx_nrzi),
.nrzi_valid(loopback ? 2'b01 : rx_nrzi_valid),
.nrz(rx_data),
.nrz_valid(rx_data_valid)
);
initial signal_status = 0;
always @(posedge clk_125)
signal_status <= loopback ? 1'b1 : signal_status_nrzi;
endmodule

77
tb/pmd_dp83223.py Normal file
View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: AGPL-3.0-Only
# Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
import random
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import ClockCycles, Combine, FallingEdge, Join, RisingEdge, Timer
from cocotb.types import LogicArray
from .nrzi_encode import nrzi_encode
from .nrzi_decode import nrzi_decode
from .pmd_dp83223_rx import check_bits
from .util import alist, compare_lists, print_list_at
@cocotb.test(timeout_time=3, timeout_unit='us')
async def test_loopback(pmd):
rx_bits = [random.randrange(2) for _ in range(100)]
tx_bits = [random.randrange(2) for _ in range(100)]
pmd.loopback.value = 0
pmd.signal_detect.value = 0
pmd.tx_data.value = tx_bits[0]
await Timer(1)
await cocotb.start(Clock(pmd.clk_125, 8, units='ns').start())
await cocotb.start(Clock(pmd.clk_250, 4, units='ns').start())
async def send_rx():
for bit in nrzi_encode(rx_bits):
pmd.signal_detect.value = 1
pmd.indicate_data.value = bit
await Timer(8, units='ns')
pmd.signal_detect.value = 0
pmd.indicate_data.value = LogicArray('X')
async def send_tx():
for bit in tx_bits:
pmd.tx_data.value = bit
await FallingEdge(pmd.clk_125)
async def recv_tx(delay):
async def bits():
await ClockCycles(pmd.clk_125, delay)
for _ in range(len(tx_bits)):
await RisingEdge(pmd.clk_125)
yield pmd.request_data.value
compare_lists(tx_bits, await alist(nrzi_decode(bits())))
async def test_normal(delay=2):
await cocotb.start(send_rx())
await cocotb.start(send_tx())
rx_task = await cocotb.start(check_bits(pmd, rx_bits))
tx_task = await cocotb.start(recv_tx(delay))
await Combine(Join(rx_task), Join(tx_task))
await test_normal()
async def loopback_monitor():
await ClockCycles(pmd.clk_125, 2)
while pmd.loopback.value:
assert not pmd.request_data.value
await RisingEdge(pmd.clk_125)
tx_task = await cocotb.start(send_rx())
await cocotb.start(send_tx())
rx_task = await cocotb.start(check_bits(pmd, tx_bits))
loop_task = await cocotb.start(loopback_monitor())
pmd.loopback.value = 1
await Join(tx_task)
pmd.loopback.value = 0
await Join(rx_task)
loop_task.kill()
await test_normal(1)