From c6f95ce26f5abca18e6cb77eaac0c061df598e73 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Wed, 24 Aug 2022 12:29:09 -0400 Subject: [PATCH] Add NRZI support This adds support for encoding and decoding nrzi data. Signed-off-by: Sean Anderson --- Makefile | 2 +- rtl/nrzi_decode.v | 39 +++++++++++++++++++++++++++++++++++++++ rtl/nrzi_encode.v | 25 +++++++++++++++++++++++++ tb/nrzi_decode.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ tb/nrzi_encode.py | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 rtl/nrzi_decode.v create mode 100644 rtl/nrzi_encode.v create mode 100644 tb/nrzi_decode.py create mode 100644 tb/nrzi_encode.py diff --git a/Makefile b/Makefile index 714276f..b6276a3 100644 --- a/Makefile +++ b/Makefile @@ -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)) diff --git a/rtl/nrzi_decode.v b/rtl/nrzi_decode.v new file mode 100644 index 0000000..adae854 --- /dev/null +++ b/rtl/nrzi_decode.v @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: AGPL-3.0-Only +/* + * Copyright (C) 2022 Sean Anderson + */ + +`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 diff --git a/rtl/nrzi_encode.v b/rtl/nrzi_encode.v new file mode 100644 index 0000000..5f9ae4c --- /dev/null +++ b/rtl/nrzi_encode.v @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: AGPL-3.0-Only +/* + * Copyright (C) 2022 Sean Anderson + */ + +`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 diff --git a/tb/nrzi_decode.py b/tb/nrzi_decode.py new file mode 100644 index 0000000..fb06f7c --- /dev/null +++ b/tb/nrzi_decode.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: AGPL-3.0-Only +# Copyright (C) 2022 Sean Anderson + +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) diff --git a/tb/nrzi_encode.py b/tb/nrzi_encode.py new file mode 100644 index 0000000..e56f86a --- /dev/null +++ b/tb/nrzi_encode.py @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: AGPL-3.0-Only +# Copyright (C) 2022 Sean Anderson + +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:])