From 5b6485d7d30dd7859983bb86211ec38060a0b50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Sowi=C5=84ski?= Date: Fri, 24 Mar 2023 15:26:12 +0100 Subject: [PATCH 1/4] Modified SPI flash core to support 1x non-IO SPI flash --- misoc/cores/spi_flash.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/misoc/cores/spi_flash.py b/misoc/cores/spi_flash.py index d9f6e002a..72ea94dd5 100644 --- a/misoc/cores/spi_flash.py +++ b/misoc/cores/spi_flash.py @@ -40,7 +40,10 @@ def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big", d """ adr_width = 32-log2_int(dw//8) self.bus = bus = wishbone.Interface(data_width=dw, adr_width=adr_width) - spi_width = len(pads.dq) + if hasattr(pads, "dq"): + spi_width = len(pads.dq) + else: + spi_width = 1 if with_bitbang: self.bitbang = CSRStorage(4) self.miso = CSRStatus() @@ -62,8 +65,16 @@ def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big", d pads.cs_n.reset = 1 - dq = TSTriple(spi_width) - self.specials.dq = dq.get_tristate(pads.dq) + if spi_width > 1: + dq = TSTriple(spi_width) + self.specials.dq = dq.get_tristate(pads.dq) + else: + class TripleMock: + def __init__(self): + self.i = pads.miso + self.o = pads.mosi + self.oe = Signal() + dq = TripleMock() sr = Signal(max(cmd_width, addr_width, dw)) if endianness == "big": @@ -88,7 +99,8 @@ def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big", d dq.oe.eq(1) ), If(self.bitbang.storage[1], - self.miso.status.eq(dq.i[1]) + self.miso.status.eq(dq.i) if spi_width == 1 \ + else self.miso.status.eq(dq.i[1]) ) ] if spi_width > 1: From 4d180bf4dd1c8b745c86461cbb3d0bc24bc84b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Sowi=C5=84ski?= Date: Fri, 24 Mar 2023 15:26:55 +0100 Subject: [PATCH 2/4] Ported Xilinx S7 RGMII from LiteEth --- misoc/cores/liteeth_mini/phy/s7rgmii.py | 258 ++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 misoc/cores/liteeth_mini/phy/s7rgmii.py diff --git a/misoc/cores/liteeth_mini/phy/s7rgmii.py b/misoc/cores/liteeth_mini/phy/s7rgmii.py new file mode 100644 index 000000000..5a645e27a --- /dev/null +++ b/misoc/cores/liteeth_mini/phy/s7rgmii.py @@ -0,0 +1,258 @@ +# +# This file is part of LiteEth, backported to MiSoC. +# +# Copyright (c) 2015-2020 Florent Kermarrec +# Copyright (c) 2022-2022 Mikolaj Sowinski +# SPDX-License-Identifier: BSD-2-Clause + +# RGMII PHY for 7-Series Xilinx FPGA + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from misoc.interconnect.csr import * +from misoc.interconnect import stream +from misoc.cores.liteeth_mini.common import * + + +class LiteEthPHYHWReset(Module): + def __init__(self, cycles=256): + self.reset = Signal() + + # # # + + counter = Signal(max=cycles + 1) + counter_done = Signal() + counter_ce = Signal() + self.sync += If(counter_ce, counter.eq(counter + 1)) + self.comb += [ + counter_done.eq(counter == cycles), + counter_ce.eq(~counter_done), + self.reset.eq(~counter_done) + ] + + +class LiteEthPHYRGMIITX(Module): + def __init__(self, pads): + self.sink = sink = stream.Endpoint(eth_phy_layout(8)) + + # # # + + tx_ctl_obuf = Signal() + tx_data_obuf = Signal(4) + + self.specials += [ + Instance("ODDR", + p_DDR_CLK_EDGE = "SAME_EDGE", + i_C = ClockSignal("eth_tx"), + i_CE = 1, + i_S = 0, + i_R = 0, + i_D1 = sink.stb, + i_D2 = sink.stb, + o_Q = tx_ctl_obuf, + ), + Instance("OBUF", + i_I = tx_ctl_obuf, + o_O = pads.tx_ctl, + ), + ] + for i in range(4): + self.specials += [ + Instance("ODDR", + p_DDR_CLK_EDGE = "SAME_EDGE", + i_C = ClockSignal("eth_tx"), + i_CE = 1, + i_S = 0, + i_R = 0, + i_D1 = sink.data[i], + i_D2 = sink.data[4+i], + o_Q = tx_data_obuf[i], + ), + Instance("OBUF", + i_I = tx_data_obuf[i], + o_O = pads.tx_data[i], + ) + ] + self.comb += sink.ack.eq(1) + + +class LiteEthPHYRGMIIRX(Module): + def __init__(self, pads, rx_delay=2e-9, iodelay_clk_freq=200e6): + self.source = source = stream.Endpoint(eth_phy_layout(8)) + + # # # + + assert iodelay_clk_freq in [200e6, 300e6, 400e6] + iodelay_tap_average = 1 / (2*32 * iodelay_clk_freq) + rx_delay_taps = round(rx_delay / iodelay_tap_average) + assert rx_delay_taps < 32, "Exceeded ODELAYE2 max value: {} >= 32".format(rx_delay_taps) + + rx_ctl_ibuf = Signal() + rx_ctl_idelay = Signal() + rx_ctl = Signal() + rx_data_ibuf = Signal(4) + rx_data_idelay = Signal(4) + rx_data = Signal(8) + + self.specials += [ + Instance("IBUF", i_I=pads.rx_ctl, o_O=rx_ctl_ibuf), + Instance("IDELAYE2", + p_IDELAY_TYPE = "FIXED", + p_IDELAY_VALUE = rx_delay_taps, + p_REFCLK_FREQUENCY = iodelay_clk_freq/1e6, + i_C = 0, + i_LD = 0, + i_CE = 0, + i_LDPIPEEN = 0, + i_INC = 0, + i_IDATAIN = rx_ctl_ibuf, + o_DATAOUT = rx_ctl_idelay, + ), + Instance("IDDR", + p_DDR_CLK_EDGE = "SAME_EDGE_PIPELINED", + i_C = ClockSignal("eth_rx"), + i_CE = 1, + i_S = 0, + i_R = 0, + i_D = rx_ctl_idelay, + o_Q1 = rx_ctl, + o_Q2 = Signal(), + ) + ] + for i in range(4): + self.specials += [ + Instance("IBUF", + i_I = pads.rx_data[i], + o_O = rx_data_ibuf[i], + ), + Instance("IDELAYE2", + p_IDELAY_TYPE = "FIXED", + p_IDELAY_VALUE = rx_delay_taps, + p_REFCLK_FREQUENCY = iodelay_clk_freq/1e6, + i_C = 0, + i_LD = 0, + i_CE = 0, + i_LDPIPEEN = 0, + i_INC = 0, + i_IDATAIN = rx_data_ibuf[i], + o_DATAOUT = rx_data_idelay[i], + ), + Instance("IDDR", + p_DDR_CLK_EDGE = "SAME_EDGE_PIPELINED", + i_C = ClockSignal("eth_rx"), + i_CE = 1, + i_S = 0, + i_R = 0, + i_D = rx_data_idelay[i], + o_Q1 = rx_data[i], + o_Q2 = rx_data[i+4], + ) + ] + + rx_ctl_d = Signal() + self.sync += rx_ctl_d.eq(rx_ctl) + + last = Signal() + self.comb += last.eq(~rx_ctl & rx_ctl_d) + self.sync += [ + source.stb.eq(rx_ctl), + source.data.eq(rx_data) + ] + self.comb += source.eop.eq(last) + + +class LiteEthPHYRGMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset, tx_delay=2e-9, hw_reset_cycles=256): + self._reset = CSRStorage() + + # # # + + # RX clock + self.clock_domains.cd_eth_rx = ClockDomain() + eth_rx_clk_ibuf = Signal() + self.specials += [ + Instance("IBUF", + i_I = clock_pads.rx, + o_O = eth_rx_clk_ibuf, + ), + Instance("BUFG", + i_I = eth_rx_clk_ibuf, + o_O = self.cd_eth_rx.clk, + ), + ] + + # TX clock + self.clock_domains.cd_eth_tx = ClockDomain() + self.clock_domains.cd_eth_tx_delayed = ClockDomain(reset_less=True) + tx_phase = 125e6*tx_delay*360 + assert tx_phase < 360 + pll_fb = Signal() + eth_tx_clk = Signal() + eth_tx_delayed_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=Signal(), + + # VCO @ 1GHz + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, + p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1, + i_CLKIN1=ClockSignal("eth_rx"), i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, + + # 125MHz + p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=eth_tx_clk, + + # 500MHz + p_CLKOUT1_DIVIDE=8, p_CLKOUT1_PHASE=tx_phase, o_CLKOUT1=eth_tx_delayed_clk, + + p_CLKOUT2_DIVIDE=5, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=, + p_CLKOUT3_DIVIDE=5, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, + p_CLKOUT4_DIVIDE=5, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= + ), + Instance("BUFG", i_I=eth_tx_clk, o_O=self.cd_eth_tx.clk), + Instance("BUFG", i_I=eth_tx_delayed_clk, o_O=self.cd_eth_tx_delayed.clk), + ] + + eth_tx_clk_obuf = Signal() + self.specials += [ + Instance("ODDR", + p_DDR_CLK_EDGE = "SAME_EDGE", + i_C = ClockSignal("eth_tx_delayed"), + i_CE = 1, + i_S = 0, + i_R = 0, + i_D1 = 1, + i_D2 = 0, + o_Q = eth_tx_clk_obuf, + ), + Instance("OBUF", + i_I = eth_tx_clk_obuf, + o_O = clock_pads.tx, + ) + ] + + # Reset + self.reset = reset = Signal() + if with_hw_init_reset: + self.submodules.hw_reset = LiteEthPHYHWReset(cycles=hw_reset_cycles) + self.comb += reset.eq(self._reset.storage | self.hw_reset.reset) + else: + self.comb += reset.eq(self._reset.storage) + if hasattr(pads, "rst_n"): + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + + +class LiteEthPHYRGMII(Module, AutoCSR): + dw = 8 + tx_clk_freq = 125e6 + rx_clk_freq = 125e6 + def __init__(self, clock_pads, pads, with_hw_init_reset=True, tx_delay=2e-9, rx_delay=2e-9, + iodelay_clk_freq=200e6, hw_reset_cycles=256): + self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset, tx_delay, hw_reset_cycles) + self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads)) + self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads, rx_delay, iodelay_clk_freq)) + self.sink, self.source = self.tx.sink, self.rx.source From 1a584116da463ff3b91bdfda3db448b081b3024c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Sowi=C5=84ski?= Date: Fri, 24 Mar 2023 15:27:18 +0100 Subject: [PATCH 3/4] Added Digilent Genesys2 target --- misoc/targets/digilent_genesys2.py | 164 +++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 misoc/targets/digilent_genesys2.py diff --git a/misoc/targets/digilent_genesys2.py b/misoc/targets/digilent_genesys2.py new file mode 100644 index 000000000..0ba6d6cd6 --- /dev/null +++ b/misoc/targets/digilent_genesys2.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 + +# +# This file is based on LiteX-Boards Digilent Genesys 2 target. +# +# Copyright (c) 2019 Florent Kermarrec +# Copyright (c) 2023 Mikolaj Sowinski +# SPDX-License-Identifier: BSD-2-Clause + +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.build.platforms import digilent_genesys2 +from migen.genlib.cdc import MultiReg + +from misoc.cores.sdram_settings import MT41J256M16 +from misoc.cores.sdram_phy import k7ddrphy +from misoc.cores import spi_flash, icap +from misoc.cores.liteeth_mini.phy.s7rgmii import LiteEthPHYRGMII +from misoc.cores.liteeth_mini.mac import LiteEthMAC +from misoc.integration.soc_sdram import * +from misoc.integration.builder import * +from misoc.interconnect.csr import * + + +class _SysCRG(Module): + def __init__(self, platform): + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_sys4x = ClockDomain(reset_less=True) + self.clock_domains.cd_clk200 = ClockDomain() + + clk200 = platform.request("clk200") + clk200_se = Signal() + self.specials += Instance("IBUFDS", i_I=clk200.p, i_IB=clk200.n, o_O=clk200_se) + + rst_n = platform.request("cpu_reset_n") + + pll_locked = Signal() + pll_fb = Signal() + pll_sys = Signal() + pll_sys4x = Signal() + pll_clk200 = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1GHz + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, + p_CLKFBOUT_MULT=5, p_DIVCLK_DIVIDE=1, + i_CLKIN1=clk200_se, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, + + # 125MHz + p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, + + # 500MHz + p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=pll_sys4x, + + # 200MHz + p_CLKOUT2_DIVIDE=5, p_CLKOUT2_PHASE=0.0, o_CLKOUT2=pll_clk200, + + p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, + + p_CLKOUT4_DIVIDE=4, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= + ), + Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), + Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk), + Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), + AsyncResetSynchronizer(self.cd_sys, ~pll_locked | ~rst_n), + AsyncResetSynchronizer(self.cd_clk200, ~pll_locked | ~rst_n), + ] + + reset_counter = Signal(4, reset=15) + ic_reset = Signal(reset=1) + self.sync.clk200 += \ + If(reset_counter != 0, + reset_counter.eq(reset_counter - 1) + ).Else( + ic_reset.eq(0) + ) + self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) + + +class BaseSoC(SoCSDRAM): + def __init__(self, sdram_controller_type="minicon", clk_freq=125e6, **kwargs): + platform = digilent_genesys2.Platform() + SoCSDRAM.__init__(self, platform, + clk_freq=clk_freq, cpu_reset_address=0xaf0000, + **kwargs) + + self.submodules.crg = _SysCRG(platform) + + self.submodules.ddrphy = k7ddrphy.K7DDRPHY(platform.request("ddram")) + self.config["DDRPHY_WLEVEL"] = None + sdram_module = MT41J256M16(self.clk_freq, "1:4") + self.register_sdram(self.ddrphy, sdram_controller_type, + sdram_module.geom_settings, sdram_module.timing_settings) + self.csr_devices.append("ddrphy") + + if not self.integrated_rom_size: + spiflash_pads = platform.request("spiflash") + spiflash_pads.clk = Signal() + self.specials += Instance("STARTUPE2", + i_CLK=0, i_GSR=0, i_GTS=0, i_KEYCLEARB=0, i_PACK=0, + i_USRCCLKO=spiflash_pads.clk, i_USRCCLKTS=0, i_USRDONEO=1, i_USRDONETS=1) + self.submodules.spiflash = spi_flash.SpiFlash( + spiflash_pads, dummy=8, div=4, + endianness=self.cpu.endianness, dw=self.cpu_dw) + self.config["SPIFLASH_PAGE_SIZE"] = 256 + self.config["SPIFLASH_SECTOR_SIZE"] = 0x10000 + self.flash_boot_address = 0xb40000 + self.register_rom(self.spiflash.bus, 16*1024*1024) + self.csr_devices.append("spiflash") + self.submodules.icap = icap.ICAP("7series") + self.csr_devices.append("icap") + + +class MiniSoC(BaseSoC): + mem_map = { + "ethmac": 0x30000000, # (shadow @0xb0000000) + } + mem_map.update(BaseSoC.mem_map) + + def __init__(self, *args, ethmac_nrxslots=2, ethmac_ntxslots=2, **kwargs): + BaseSoC.__init__(self, *args, **kwargs) + + self.csr_devices += ["ethphy", "ethmac"] + self.interrupt_devices.append("ethmac") + + eth_clocks = self.platform.request("eth_clocks") + eth_pads = self.platform.request("eth") + self.comb += eth_pads.rst_n.eq(1) + self.submodules.ethphy = LiteEthPHYRGMII(eth_clocks, eth_pads) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 8.) + self.submodules.ethmac = LiteEthMAC(phy=self.ethphy, dw=self.cpu_dw, interface="wishbone", + endianness=self.cpu.endianness, + nrxslots=ethmac_nrxslots, ntxslots=ethmac_ntxslots) + ethmac_len = (ethmac_nrxslots + ethmac_ntxslots) * 0x800 + self.add_wb_slave(self.mem_map["ethmac"], ethmac_len, self.ethmac.bus) + self.add_memory_region("ethmac", self.mem_map["ethmac"] | self.shadow_base, + ethmac_len) + + self.platform.add_false_path_constraints( + self.crg.cd_sys.clk, + self.ethphy.crg.cd_eth_tx.clk, eth_clocks.rx) + + +def main(): + parser = argparse.ArgumentParser(description="MiSoC port to the Digilent Genesys2") + builder_args(parser) + soc_sdram_args(parser) + parser.add_argument("--with-ethernet", action="store_true", + help="enable Ethernet support") + args = parser.parse_args() + + cls = MiniSoC if args.with_ethernet else BaseSoC + soc = cls(**soc_sdram_argdict(args)(args)) + builder = Builder(soc, **builder_argdict(args)) + builder.build() + + +if __name__ == "__main__": + main() From 196aa747692fb945d6e7c9019b54a41ae537bb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Sowi=C5=84ski?= Date: Thu, 30 Mar 2023 12:12:45 +0200 Subject: [PATCH 4/4] targets: Genesys2: added FMC VADJ parameter, removed redundant import --- misoc/targets/digilent_genesys2.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misoc/targets/digilent_genesys2.py b/misoc/targets/digilent_genesys2.py index 0ba6d6cd6..705ce76ed 100644 --- a/misoc/targets/digilent_genesys2.py +++ b/misoc/targets/digilent_genesys2.py @@ -12,7 +12,6 @@ from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer from migen.build.platforms import digilent_genesys2 -from migen.genlib.cdc import MultiReg from misoc.cores.sdram_settings import MT41J256M16 from misoc.cores.sdram_phy import k7ddrphy @@ -82,8 +81,8 @@ def __init__(self, platform): class BaseSoC(SoCSDRAM): - def __init__(self, sdram_controller_type="minicon", clk_freq=125e6, **kwargs): - platform = digilent_genesys2.Platform() + def __init__(self, fmc1_vadj, sdram_controller_type="minicon", clk_freq=125e6, **kwargs): + platform = digilent_genesys2.Platform(fmc1_vadj) SoCSDRAM.__init__(self, platform, clk_freq=clk_freq, cpu_reset_address=0xaf0000, **kwargs)