Add NRZI support

This adds support for encoding and decoding nrzi data.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2022-08-24 12:29:09 -04:00
parent f18acfc0b0
commit c6f95ce26f
5 changed files with 148 additions and 1 deletions

View File

@ -63,7 +63,7 @@ endef
%.post.fst: rtl/%.post.vvp tb/%.py FORCE
$(run-vvp)
MODULES := pcs pmd
MODULES := pcs pmd nrzi_encode nrzi_decode
.PHONY: test
test: $(addsuffix .fst,$(MODULES)) $(addsuffix .post.fst,$(MODULES))

39
rtl/nrzi_decode.v Normal file
View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
*/
`include "common.vh"
module nrzi_decode (
input clk,
input [1:0] nrzi,
input [1:0] nrzi_valid,
output reg [1:0] nrz,
output reg [1:0] nrz_valid
);
reg [1:0] nrz_next;
reg nrzi_last;
reg nrzi_last_next;
always @(*) begin
nrz_next[0] = nrzi[1] ^ nrzi[0];
nrz_next[1] = nrzi[1] ^ nrzi_last;
nrzi_last_next = nrzi_last;
if (nrzi_valid != 0)
nrzi_last_next = nrzi[1];
if (nrzi_valid & 2)
nrzi_last_next = nrzi[0];
end
always @(posedge clk) begin
nrzi_last <= nrzi_last_next;
nrz_valid <= nrzi_valid;
nrz <= nrz_next;
end
`DUMP(0)
endmodule

25
rtl/nrzi_encode.v Normal file
View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2022 Sean Anderson <seanga2@gmail.com>
*/
`include "common.vh"
module nrzi_encode (
input clk,
input nrz,
output reg nrzi
);
reg nrzi_next;
initial nrzi = 1;
always @(*)
nrzi_next = nrz ^ nrzi;
always @(posedge clk)
nrzi <= nrzi_next;
`DUMP(0)
endmodule

45
tb/nrzi_decode.py Normal file
View File

@ -0,0 +1,45 @@
# 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.regression import TestFactory
from cocotb.triggers import FallingEdge, RisingEdge, Timer
from .util import compare_lists, timeout, send_recovered_bits, with_valids
def nrzi_decode(bits):
last = 1
for bit in bits:
yield bit ^ last
last = bit
@timeout(10, 'us')
async def test_rx(decoder, valids):
decoder.nrzi_valid.value = 0
await Timer(1)
await cocotb.start(Clock(decoder.clk, 8, units='ns').start())
ins = [random.randrange(2) for _ in range(1000)]
await cocotb.start(send_recovered_bits(decoder.clk, decoder.nrzi,
decoder.nrzi_valid, ins, valids))
outs = []
await RisingEdge(decoder.clk)
for _ in ins:
await RisingEdge(decoder.clk)
valid = decoder.nrz_valid.value
if valid == 0:
pass
elif valid == 1:
outs.append(decoder.nrz[1].value)
else:
outs.append(decoder.nrz[1].value)
outs.append(decoder.nrz[0].value)
# Ignore the first bit, since it is influenced by the initial value
compare_lists(list(nrzi_decode(ins))[1:], outs[1:])
with_valids(globals(), test_rx)

38
tb/nrzi_encode.py Normal file
View File

@ -0,0 +1,38 @@
# 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 FallingEdge, RisingEdge, Timer
from .util import compare_lists
def nrzi_encode(bits):
last = 1
for bit in bits:
yield (last := last ^ bit)
@cocotb.test(timeout_time=100, timeout_unit='us')
async def text_encode(encoder):
ins = [random.randrange(2) for _ in range(1000)]
encoder.nrz.value = ins[0]
await Timer(1)
await cocotb.start(Clock(encoder.clk, 8, units='ns').start())
async def send_nrz():
for bit in ins:
await FallingEdge(encoder.clk)
encoder.nrz.value = bit
await cocotb.start(send_nrz())
outs = []
await RisingEdge(encoder.clk)
await RisingEdge(encoder.clk)
for _ in ins:
await RisingEdge(encoder.clk)
outs.append(encoder.nrzi.value)
# Ignore the first bit, since it is influenced by the initial value
compare_lists(list(nrzi_encode(ins[1:])), outs[1:])