serdes-transceiver/multi_coders.py

124 lines
4.0 KiB
Python
Raw Normal View History

2023-05-07 11:13:03 +08:00
from migen import *
from misoc.cores.code_8b10b import SingleEncoder, Decoder
class MultiEncoder(Module):
# For 4-channel EEM
# Thus, no need for words parameter
# It is always a 2-wds -> 4-ch conversion
# Implement using crossbar, so we don't need to do clock domain conversion
# while maintaining proper disparity
def __init__(self, lsb_first=False):
WORDS = 2
# Keep the link layer interface identical to standard encoders
self.d = [Signal(8) for _ in range(WORDS)]
self.k = [Signal() for _ in range(WORDS)]
# Output interface is simplified because we have custom physical layer
self.output = [Signal(10) for _ in range(WORDS)]
# Module start signal
self.start = Signal()
# lsb_first should not be set set
# But technically if the same setting is reflected on the decoder
# It shouldn't be an issue
if lsb_first:
raise ValueError("lsb_first must not be set")
# Phase of the encoder
# Alternate crossbar between encoder and SERDES every cycle
phase = Signal()
# Intermediate registers for output and disparity
# More significant bits are buffered due to channel geometry
# Disparity bit is delayed. The same encoder is shared by 2 SERDES
output_bufs = [Signal(5) for _ in range(WORDS)]
disp_bufs = [Signal() for _ in range(WORDS)]
encoders = [SingleEncoder(lsb_first) for _ in range(WORDS)]
self.submodules += encoders
for d, k, output, output_buf, disp_buf, encoder in \
zip(self.d, self.k, self.output, output_bufs, disp_bufs, encoders):
self.comb += [
encoder.d.eq(d),
encoder.k.eq(k),
encoder.disp_in.eq(disp_buf),
# Implementing switching crossbar
If(phase,
output.eq(Cat(encoder.output[0:5], output_buf))
).Else(
output.eq(Cat(output_buf, encoder.output[0:5]))
),
]
# Handle intermediate registers
self.sync += [
disp_buf.eq(encoder.disp_out),
output_buf.eq(encoder.output[5:10]),
]
aligned = Signal()
self.sync += [
# Phase switching
phase.eq(~phase),
If(~aligned & self.start,
aligned.eq(1),
# Later statements take precedent
phase.eq(0),
),
]
# Unlike the usual 8b10b decoder, it needs to know which SERDES to decode
class CrossbarDecoder(Module):
def __init__(self, lsb_first=False):
self.raw_input = Signal(10)
self.d = Signal(8)
self.k = Signal()
# Notifier signal when group alignmnet is completed
self.start = Signal()
aligned = Signal()
phase = Signal()
# Intermediate register for input
buffer = Signal(5)
self.submodules.decoder = Decoder(lsb_first)
# lsb_first should not be set set
# But technically if the same setting is reflected on the decoder
# It shouldn't be an issue
if lsb_first:
raise ValueError("lsb_first must not be set")
# Update phase & synchronous elements
self.sync += [
phase.eq(~phase),
If(~aligned & self.start,
aligned.eq(1),
phase.eq(0),
),
If(phase,
buffer.eq(self.raw_input[:5]),
).Else(
buffer.eq(self.raw_input[5:])
),
]
# Send appropriate input to decoder
self.comb += [
If(phase,
self.decoder.input.eq(Cat(buffer, self.raw_input[5:])),
).Else(
self.decoder.input.eq(Cat(buffer, self.raw_input[:5])),
),
]
self.comb += [
self.d.eq(self.decoder.d),
self.k.eq(self.decoder.k),
]