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:
parent
f50f5b688f
commit
2eac757fd7
1
Makefile
1
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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)
|
Loading…
Reference in New Issue