Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: whitequark/Yumewatari
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7e72d2fe0fdd
Choose a base ref
...
head repository: whitequark/Yumewatari
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fd7e9b4c5b65
Choose a head ref
  • 1 commit
  • 3 files changed
  • 1 contributor

Commits on Nov 11, 2018

  1. Copy the full SHA
    fd7e9b4 View commit details
Showing with 80 additions and 56 deletions.
  1. +26 −13 yumewatari/gateware/platform/lattice_ecp5.py
  2. +23 −15 yumewatari/gateware/serdes.py
  3. +31 −28 yumewatari/testbench/serdes.py
39 changes: 26 additions & 13 deletions yumewatari/gateware/platform/lattice_ecp5.py
Original file line number Diff line number Diff line change
@@ -10,22 +10,22 @@
class LatticeECP5PCIeSERDES(Module):
"""
Lattice ECP5 DCU configured in PCIe mode. Assumes 100 MHz reference clock on SERDES clock
input pair. Only provides a single lane.
input pair. Uses 1:2 gearing. Only provides a single lane.
Parameters
----------
ref_clk : Signal
100 MHz SERDES reference clock.
rx_clk_o : Signal
250 MHz clock recovered from received data.
125 MHz clock recovered from received data.
rx_clk_i : Signal
250 MHz clock for the receive FIFO.
125 MHz clock for the receive FIFO.
tx_clk_o : Signal
250 MHz clock generated by transmit PLL.
125 MHz clock generated by transmit PLL.
tx_clk_i : Signal
250 MHz clock for the transmit FIFO.
125 MHz clock for the transmit FIFO.
"""

def __init__(self, pins):
@@ -74,22 +74,27 @@ def __init__(self, pins):
MultiReg(tx_lol, tx_lol_s, odomain="tx")
]

self.lane = lane = PCIeSERDESInterface()
self.lane = lane = PCIeSERDESInterface(ratio=2)
self.comb += [
rx_inv.eq(lane.rx_invert),
rx_det.eq(lane.rx_align),
lane.rx_present.eq(~rx_los_s),
lane.rx_locked .eq(~rx_lol_s),
lane.rx_aligned.eq(rx_lsm_s),
lane.rx_symbol.eq(self.rx_bus),
lane.rx_symbol.eq(Cat(self.rx_bus[ 0: 9],
self.rx_bus[12:21])),
# In theory, ``rx_bus[9:11]`` has disparity error and coding violation status
# signals, but in practice, they appear to be stuck at 1 and 0 respectively.
# However, the 8b10b decoder replaces errors with a "K14.7", which is not a legal
# point in 8b10b coding space, so we can use that as an indication.
lane.rx_valid.eq(self.rx_bus[:9] != 0x1EE),
lane.rx_valid.eq(Cat(self.rx_bus[ 0: 9] != 0x1EE,
self.rx_bus[12:21] != 0x1EE)),
]
self.comb += [
self.tx_bus.eq(Cat(lane.tx_symbol, lane.tx_set_disp, lane.tx_disp)),
self.tx_bus.eq(Cat(lane.tx_symbol[0: 9],
lane.tx_set_disp[0], lane.tx_disp[0], lane.tx_e_idle[0],
lane.tx_symbol[9:18],
lane.tx_set_disp[1], lane.tx_disp[1], lane.tx_e_idle[1])),
]

self.specials.dcu0 = Instance("DCUA",
@@ -165,13 +170,16 @@ def __init__(self, pins):
o_CH0_FF_RX_PCLK = self.rx_clk_o,
i_CH0_FF_RXI_CLK = self.rx_clk_i,

p_CH0_AUTO_FACQ_EN = "0b1", # undocumented (wizard value used)
p_CH0_AUTO_CALIB_EN = "0b1", # undocumented (wizard value used)
p_CH0_CDR_MAX_RATE = "2.5", # 2.5 Gbps
p_CH0_RX_DCO_CK_DIV = "0b000", # DIV/1
p_CH0_PDEN_SEL = "0b1", # phase detector disabled on LOS
p_CH0_RX_GEAR_MODE = "0b1", # 1:2 gearbox
p_CH0_FF_RX_H_CLK_EN = "0b1", # enable DIV/2 output clock
p_CH0_FF_RX_F_CLK_DIS = "0b1", # disable DIV/1 output clock
p_CH0_SEL_SD_RX_CLK = "0b1", # FIFO driven by recovered clock
p_CH0_CTC_BYPASS = "0b1", # bypass CTC FIFO

p_CH0_AUTO_FACQ_EN = "0b1", # undocumented (wizard value used)
p_CH0_AUTO_CALIB_EN = "0b1", # undocumented (wizard value used)
p_CH0_PDEN_SEL = "0b1", # phase detector disabled on LOS

p_CH0_DCOATDCFG = "0b00", # begin undocumented (PCIe sample code used)
p_CH0_DCOATDDLY = "0b00",
@@ -208,6 +216,7 @@ def __init__(self, pins):
p_CH0_UDF_COMMA_A = "0x283", # K28.5 inverted
p_CH0_UDF_COMMA_B = "0x17C", # K28.5

p_CH0_CTC_BYPASS = "0b1", # bypass CTC FIFO
p_CH0_MIN_IPG_CNT = "0b11", # minimum interpacket gap of 4
p_CH0_MATCH_4_ENABLE = "0b1", # 4 character skip matching
p_CH0_CC_MATCH_1 = "0x1BC", # K28.5
@@ -250,6 +259,10 @@ def __init__(self, pins):
o_CH0_FF_TX_PCLK = self.tx_clk_o,
i_CH0_FF_TXI_CLK = self.tx_clk_i,

p_CH0_TX_GEAR_MODE = "0b1", # 1:2 gearbox
p_CH0_FF_TX_H_CLK_EN = "0b1", # disable DIV/1 output clock
p_CH0_FF_TX_F_CLK_DIS = "0b1", # enable DIV/2 output clock

# CH0 TX — data
**{"o_CH0_FF_TX_D_%d" % n: self.tx_bus[n] for n in range(self.tx_bus.nbits)},
)
38 changes: 23 additions & 15 deletions yumewatari/gateware/serdes.py
Original file line number Diff line number Diff line change
@@ -6,51 +6,59 @@

class PCIeSERDESInterface(Module):
"""
Interface of a single PCIe SERDES pair, connected to a single lane. Assumes 1:1 gearing,
i.e. one symbol is transmitted each clock cycle.
Interface of a single PCIe SERDES pair, connected to a single lane. Uses 1:**ratio** gearing
for configurable **ratio**, i.e. **ratio** symbols are transmitted per clock cycle.
Parameters
----------
ratio : int
Gearbox ratio.
rx_invert : Signal
Assert to invert the received bits before 8b10b decoder.
rx_align : Signal
Assert to enable comma alignment state machine, deassert to lock bit alignment.
Assert to enable comma alignment state machine, deassert to lock alignment.
rx_present : Signal
Asserted if the receiver has detected signal.
rx_locked : Signal
Asserted if the receiver has recovered a valid clock.
rx_aligned : Signal
Asserted if the receiver has aligned to the comma symbol.
rx_symbol : Signal(8)
8b10b-decoded received symbol, with 9th bit indicating a control symbol.
rx_valid : Signal
rx_symbol : Signal(9 * ratio)
Two 8b10b-decoded received symbols, with 9th bit indicating a control symbol.
rx_valid : Signal(ratio)
Asserted if the received symbol has no coding errors. If not asserted, ``rx_data`` and
``rx_control`` must be ignored, and may contain symbols that do not exist in 8b10b coding
space.
tx_locked : Signal
Asserted if the transmitter is generating a valid clock.
tx_symbol : Signal(8)
tx_symbol : Signal(9 * ratio)
Symbol to 8b10b-encode and transmit, with 9th bit indicating a control symbol.
tx_set_disp : Signal
tx_set_disp : Signal(ratio)
Assert to indicate that the 8b10b encoder should choose an encoding with a specific
running disparity instead of using its state, specified by ``tx_disp``.
tx_disp : Signal
tx_disp : Signal(ratio)
Assert to transmit a symbol with positive running disparity, deassert for negative
running disparity.
tx_e_idle : Signal(ratio)
Assert to transmit Electrical Idle for that symbol.
"""
def __init__(self):
def __init__(self, ratio=1):
self.ratio = ratio

self.rx_invert = Signal()
self.rx_align = Signal()
self.rx_present = Signal()
self.rx_locked = Signal()
self.rx_aligned = Signal()

self.rx_symbol = Signal(9)
self.rx_valid = Signal()
self.rx_symbol = Signal(ratio * 9)
self.rx_valid = Signal(ratio)

self.tx_symbol = Signal(9)
self.tx_set_disp = Signal()
self.tx_disp = Signal()
self.tx_symbol = Signal(ratio * 9)
self.tx_set_disp = Signal(ratio)
self.tx_disp = Signal(ratio)
self.tx_e_idle = Signal(ratio)
59 changes: 31 additions & 28 deletions yumewatari/testbench/serdes.py
Original file line number Diff line number Diff line change
@@ -35,9 +35,9 @@ def __init__(self, capture_depth, **kwargs):
self.cd_tx.clk.eq(serdes.tx_clk_i),
]

# self.platform.add_platform_command("""FREQUENCY NET "ref_clk" 100 MHz;""")
# self.platform.add_platform_command("""FREQUENCY NET "rx_clk" 250 MHz;""")
# self.platform.add_platform_command("""FREQUENCY NET "tx_clk" 250 MHz;""")
self.platform.add_platform_command("""FREQUENCY NET "ref_clk" 100 MHz;""")
self.platform.add_platform_command("""FREQUENCY NET "rx_clk" 250 MHz;""")
self.platform.add_platform_command("""FREQUENCY NET "tx_clk" 250 MHz;""")

refclkcounter = Signal(32)
self.sync.ref += refclkcounter.eq(refclkcounter + 1)
@@ -85,11 +85,10 @@ def __init__(self, capture_depth, **kwargs):
self.submodules.symbols = symbols = ClockDomainsRenamer({
"write": "rx", "read": "ref"
})(
AsyncFIFO(width=16, depth=capture_depth)
AsyncFIFO(width=18, depth=capture_depth)
)
self.comb += [
symbols.din.eq(Cat(serdes.lane.rx_symbol,
serdes.lane.rx_aligned)),
symbols.din.eq(Cat(serdes.lane.rx_symbol)),
symbols.we.eq(capture)
]
self.sync.rx += [
@@ -128,29 +127,36 @@ def __init__(self, capture_depth, **kwargs):
If(uart.tx_rdy,
uart.tx_ack.eq(1),
uart.tx_data.eq(0xff),
NextState("HIGH")
NextState("BYTE-0")
)
)
self.fsm.act("HIGH",
self.fsm.act("BYTE-0",
If(symbols.readable & uart.tx_rdy,
uart.tx_ack.eq(1),
uart.tx_data.eq(symbols.dout[8:]),
NextState("LOW")
uart.tx_data.eq(symbols.dout[16:]),
NextState("BYTE-1")
).Elif(~symbols.readable,
NextState("WAIT")
)
)
self.fsm.act("LOW",
self.fsm.act("BYTE-1",
If(symbols.readable & uart.tx_rdy,
uart.tx_ack.eq(1),
uart.tx_data.eq(symbols.dout[8:]),
NextState("BYTE-2")
)
)
self.fsm.act("BYTE-2",
If(symbols.readable & uart.tx_rdy,
uart.tx_ack.eq(1),
uart.tx_data.eq(symbols.dout[:8]),
uart.tx_data.eq(symbols.dout[0:]),
symbols.re.eq(1),
NextState("HIGH")
NextState("BYTE-0")
)
)

tp0 = self.platform.request("tp0")
self.comb += tp0.eq(capture)
self.comb += tp0.eq(serdes.rx_clk_o)

# -------------------------------------------------------------------------------------------------

@@ -191,18 +197,15 @@ def __init__(self, capture_depth, **kwargs):
if port.read(1) == b"\xff": break

for x in range(CAPTURE_DEPTH):
hi, lo = port.read(2)
word = (hi << 8) | lo
if word & 0x1ff == 0x1ee:
print("{}KEEEEEEEE".format(
"L" if word & (1 << 9) else " ",
), end=" ")
else:
print("{}{}{:08b}".format(
"L" if word & (1 << 9) else " ",
"K" if word & (1 << 8) else " ",
word & 0xff,
), end=" ")
# print("".join(reversed("{:010b}".format(word & 3ff)), end=" ")
if x % 8 == 7:
b2, b1, b0 = port.read(3)
dword = (b2 << 16) | (b1 << 8) | b0
for word in (((dword >> 0) & 0x1ff), ((dword >> 9) & 0x1ff)):
if word & 0x1ff == 0x1ee:
print("KEEEEEEEE", end=" ")
else:
print("{}{:08b}".format(
"K" if word & (1 << 8) else " ",
word & 0xff,
), end=" ")
if x % 4 == 3:
print()