From b52f253dbdc3bf80eb0aa6343f018c2527da683e Mon Sep 17 00:00:00 2001 From: occheung <54844539+occheung@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:02:49 -0700 Subject: [PATCH] Simplify OOB reset by clock division (#2217) * oob: simply logic by dividing into clk100 * replace clk100 clk ctrl with clk200 async reset * fix comment (singular/plural) * oob reset: invoke platform commands locally * cleanup * oob reset: add async reset import * fix duplicated comment --- .../gateware/drtio/transceiver/eem_serdes.py | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/artiq/gateware/drtio/transceiver/eem_serdes.py b/artiq/gateware/drtio/transceiver/eem_serdes.py index 4092cb202..75b4f3389 100644 --- a/artiq/gateware/drtio/transceiver/eem_serdes.py +++ b/artiq/gateware/drtio/transceiver/eem_serdes.py @@ -1,4 +1,5 @@ from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer from misoc.interconnect.csr import * from misoc.cores.code_8b10b import SingleEncoder, Decoder from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface @@ -396,53 +397,78 @@ class SerdesSingle(Module): class OOBReset(Module): - def __init__(self, iserdes_o): - ce_counter = Signal(13) - activity_ce = Signal() - transition_ce = Signal() + def __init__(self, platform, iserdes_o): + self.clock_domains.cd_clk100 = ClockDomain() + self.specials += [ + Instance("BUFR", + i_I=ClockSignal("clk200"), + o_O=ClockSignal("clk100"), + p_BUFR_DIVIDE="2"), + AsyncResetSynchronizer(self.cd_clk100, ResetSignal("clk200")), + ] - self.sync.clk200 += Cat(ce_counter, activity_ce).eq(ce_counter + 1) - self.comb += transition_ce.eq(ce_counter[0]) - - idle_low_meta = Signal() - idle_high_meta = Signal() idle_low = Signal() idle_high = Signal() - idle = Signal() self.rst = Signal(reset=1) - # Detect the lack of transitions (idle) within 2 clk200 cycles - self.specials += [ - Instance("FDCE", p_INIT=1, i_D=1, i_CLR=iserdes_o, - i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low_meta, - attr={"async_reg", "ars_ff1"}), - Instance("FDCE", p_INIT=1, i_D=idle_low_meta, i_CLR=0, - i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_low, - attr={"async_reg", "ars_ff2"}), + # Detect the lack of transitions (idle) within a clk100 cycle + for idle, source in [ + (idle_low, iserdes_o), (idle_high, ~iserdes_o)]: + idle_meta = Signal() + ff_pair = [ff1, ff2] = [ + Instance("FDCE", p_INIT=1, i_D=1, i_CLR=source, + i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle_meta, + attr={"async_reg"}), + Instance("FDCE", p_INIT=1, i_D=idle_meta, i_CLR=0, + i_CE=1, i_C=ClockSignal("clk100"), o_Q=idle, + attr={"async_reg"}), + ] + self.specials += ff_pair - Instance("FDCE", p_INIT=1, i_D=1, i_CLR=~iserdes_o, - i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high_meta, - attr={"async_reg", "ars_ff1"}), - Instance("FDCE", p_INIT=1, i_D=idle_high_meta, i_CLR=0, - i_CE=transition_ce, i_C=ClockSignal("clk200"), o_Q=idle_high, - attr={"async_reg", "ars_ff2"}), - ] + platform.add_platform_command( + "set_false_path -quiet -to {ff1}/CLR", ff1=ff1) + # Capture transition detected by FF1/Q in FF2/D + platform.add_platform_command( + "set_max_delay 2 -quiet " + "-from {ff1}/Q -to {ff2}/D", ff1=ff1, ff2=ff2) - # Detect activity for the last 2**13 clk200 cycles - # The 2**13 cycles are fully partitioned into 2**12 time segments of 2 - # cycles in duration. If there exists 2-cycle time segment without - # signal level transition, rst is asserted. - self.sync.clk200 += [ - If(activity_ce, - idle.eq(0), - self.rst.eq(idle), - ), + # Detect activity for the last 2**15 clk100 cycles + self.submodules.fsm = fsm = ClockDomainsRenamer("clk100")( + FSM(reset_state="WAIT_TRANSITION")) + counter = Signal(15, reset=0x7FFF) + + # Keep sysclk reset asserted until transition is detected for a + # continuous 2**15 clk100 cycles + fsm.act("WAIT_TRANSITION", + self.rst.eq(1), If(idle_low | idle_high, - idle.eq(1), - self.rst.eq(1), - ), - ] + NextValue(counter, 0x7FFF), + ).Else( + If(counter == 0, + NextState("WAIT_NO_TRANSITION"), + NextValue(counter, 0x7FFF), + ).Else( + NextValue(counter, counter - 1), + ) + ) + ) + + # Reassert sysclk reset if there are no transition for the last 2**15 + # clk100 cycles. + fsm.act("WAIT_NO_TRANSITION", + self.rst.eq(0), + If(idle_low | idle_high, + If(counter == 0, + NextState("WAIT_TRANSITION"), + NextValue(counter, 0x7FFF), + ).Else( + NextValue(counter, counter - 1), + ) + ).Else( + NextValue(counter, 0x7FFF), + ) + ) class EEMSerdes(Module, TransceiverInterface, AutoCSR): @@ -526,7 +552,7 @@ class EEMSerdes(Module, TransceiverInterface, AutoCSR): self.submodules += serdes_list - self.submodules.oob_reset = OOBReset(serdes_list[0].rx_serdes.o[0]) + self.submodules.oob_reset = OOBReset(platform, serdes_list[0].rx_serdes.o[0]) self.rst = self.oob_reset.rst self.rst.attr.add("no_retiming")