124 lines
4.0 KiB
Python
124 lines
4.0 KiB
Python
|
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),
|
||
|
]
|