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_rx
|
||||||
MODULES += pcs_tx
|
MODULES += pcs_tx
|
||||||
MODULES += phy_core
|
MODULES += phy_core
|
||||||
|
MODULES += pmd_dp83223
|
||||||
MODULES += pmd_dp83223_rx
|
MODULES += pmd_dp83223_rx
|
||||||
MODULES += scramble
|
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