diff --git a/litex_boards/community/platforms/pipistrello.py b/litex_boards/community/platforms/pipistrello.py new file mode 100644 index 0000000..02cad22 --- /dev/null +++ b/litex_boards/community/platforms/pipistrello.py @@ -0,0 +1,170 @@ +from litex.build.generic_platform import * +from litex.build.xilinx import XilinxPlatform +from litex.build.xilinx.programmer import XC3SProg +from litex.build.openocd import OpenOCD + +_io = [ + ("user_led", 0, Pins("V16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # green at hdmi + ("user_led", 1, Pins("U16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at hdmi + ("user_led", 2, Pins("A16"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # green at msd + ("user_led", 3, Pins("A15"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at msd + ("user_led", 4, Pins("A12"), IOStandard("LVTTL"), Drive(8), Misc("SLEW=QUIETIO")), # red at usb + + ("user_btn", 0, Pins("N14"), IOStandard("LVTTL"), Misc("PULLDOWN")), + + ("clk50", 0, Pins("H17"), IOStandard("LVTTL")), + + ("serial", 0, + Subsignal("tx", Pins("A10")), + Subsignal("rx", Pins("A11"), Misc("PULLUP")), + Subsignal("cts", Pins("C10"), Misc("PULLUP")), + Subsignal("rts", Pins("A9"), Misc("PULLUP")), + IOStandard("LVTTL"), + ), + + ("usb_fifo", 0, + Subsignal("data", Pins("A11 A10 C10 A9 B9 A8 B8 A7")), + Subsignal("rxf_n", Pins("C7")), + Subsignal("txe_n", Pins("A6")), + Subsignal("rd_n", Pins("B6")), + Subsignal("wr_n", Pins("A5")), + Subsignal("siwua", Pins("C5")), + IOStandard("LVTTL"), + ), + + ("hdmi", 0, + Subsignal("clk_p", Pins("U5"), IOStandard("TMDS_33")), + Subsignal("clk_n", Pins("V5"), IOStandard("TMDS_33")), + Subsignal("data0_p", Pins("T6"), IOStandard("TMDS_33")), + Subsignal("data0_n", Pins("V6"), IOStandard("TMDS_33")), + Subsignal("data1_p", Pins("U7"), IOStandard("TMDS_33")), + Subsignal("data1_n", Pins("V7"), IOStandard("TMDS_33")), + Subsignal("data2_p", Pins("U8"), IOStandard("TMDS_33")), + Subsignal("data2_n", Pins("V8"), IOStandard("TMDS_33")), + Subsignal("scl", Pins("V9"), IOStandard("I2C")), + Subsignal("sda", Pins("T9"), IOStandard("I2C")), + Subsignal("hpd_notif", Pins("R8"), IOStandard("LVTTL")), + ), + + ("spiflash", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("mosi", Pins("T13")), + Subsignal("miso", Pins("R13"), Misc("PULLUP")), + Subsignal("wp", Pins("T14")), + Subsignal("hold", Pins("V14")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("spiflash2x", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("dq", Pins("T13 R13"), Misc("PULLUP")), + Subsignal("wp", Pins("T14")), + Subsignal("hold", Pins("V14")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("spiflash4x", 0, + Subsignal("cs_n", Pins("V3")), + Subsignal("clk", Pins("R15")), + Subsignal("dq", Pins("T13 R13 T14 V14"), Misc("PULLUP")), + IOStandard("LVTTL"), Misc("SLEW=FAST") + ), + + ("mmc", 0, + Subsignal("clk", Pins("A3")), + Subsignal("cmd", Pins("B3"), Misc("PULLUP")), + Subsignal("dat", Pins("B4 A4 B2 A2"), Misc("PULLUP")), + IOStandard("SDIO") + ), + + ("mmc_spi", 0, + Subsignal("cs_n", Pins("A2"), Misc("PULLUP")), + Subsignal("clk", Pins("A3")), + Subsignal("mosi", Pins("B3")), + Subsignal("miso", Pins("B4"), Misc("PULLUP")), + IOStandard("SDIO") + ), + + ("audio", 0, + Subsignal("l", Pins("R7"), Misc("SLEW=SLOW")), + Subsignal("r", Pins("T7"), Misc("SLEW=SLOW")), + IOStandard("LVTTL"), + ), + + ("pmod", 0, + Subsignal("d", Pins("D9 C8 D6 C4 B11 C9 D8 C6")), + IOStandard("LVTTL") + ), + + ("ddram_clock", 0, + Subsignal("p", Pins("G3")), + Subsignal("n", Pins("G1")), + IOStandard("MOBILE_DDR") + ), + + ("ddram", 0, + Subsignal("a", Pins("J7 J6 H5 L7 F3 H4 H3 H6 D2 D1 F4 D3 G6")), + Subsignal("ba", Pins("F2 F1")), + Subsignal("cke", Pins("H7")), + Subsignal("ras_n", Pins("L5")), + Subsignal("cas_n", Pins("K5")), + Subsignal("we_n", Pins("E3")), + Subsignal("dq", Pins("L2 L1 K2 K1 H2 H1 J3 J1 M3 M1 N2 N1 T2 T1 U2 U1")), + Subsignal("dqs", Pins("L4 P2")), + Subsignal("dm", Pins("K3 K4")), + IOStandard("MOBILE_DDR") + ) +] + +_connectors = [ + ("A", "U18 T17 P17 P16 N16 N17 M16 L15 L17 K15 K17 J16 H15 H18 F18 D18"), + ("B", "C18 E18 G18 H16 J18 K18 K16 L18 L16 M18 N18 N15 P15 P18 T18 U17"), + ("C", "F17 F16 E16 G16 F15 G14 F14 H14 H13 J13 G13 H12 K14 K13 K12 L12"), +] + + +_hdmi_infos = { + "HDMI_OUT0_MNEMONIC": "J4", + "HDMI_OUT0_DESCRIPTION" : ( + " Type A connector, marked as J4.\\r\\n" + ) +} + + +class Platform(XilinxPlatform): + name = "pipistrello" + identifier = 0x5049 + default_clk_name = "clk50" + default_clk_period = 20 + hdmi_infos = _hdmi_infos + + # Micron N25Q128 (ID 0x0018ba20) + # FIXME: Create a "spi flash module" object in the same way we have SDRAM + # module objects. + spiflash_model = "n25q128" + spiflash_read_dummy_bits = 10 + spiflash_clock_div = 4 + spiflash_total_size = int((128/8)*1024*1024) # 128Mbit + spiflash_page_size = 256 + spiflash_sector_size = 0x10000 + + # The Pipistrello has a XC6SLX45 which bitstream takes up ~12Mbit (1484472 bytes) + # 0x200000 offset (16Mbit) gives plenty of space + gateware_size = 0x200000 + + def __init__(self, programmer="openocd"): + XilinxPlatform.__init__(self, "xc6slx45-csg324-3", _io, _connectors) + self.toolchain.bitgen_opt += " -g Compress -g ConfigRate:6" + self.programmer = programmer + + def create_programmer(self): + proxy="bscan_spi_{}.bit".format(self.device.split('-')[0]) + if self.programmer == "openocd": + return OpenOCD(config="board/pipistrello.cfg", flash_proxy_basename=proxy) + # Alternative programmers - not regularly tested. + elif self.programmer == "xc3sprog": + return XC3SProg("papilio", "bscan_spi_lx45_csg324.bit") + else: + raise ValueError("{} programmer is not supported".format(self.programmer)) diff --git a/litex_boards/community/targets/pipistrello.py b/litex_boards/community/targets/pipistrello.py new file mode 100755 index 0000000..348f256 --- /dev/null +++ b/litex_boards/community/targets/pipistrello.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +from fractions import Fraction + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex_boards.platforms import pipistrello + +from litex.soc.integration.soc_sdram import * +from litex.soc.integration.builder import * + +from litedram.modules import MT46H32M16 +from litedram.phy import s6ddrphy +from litedram.core import ControllerSettings + + +import argparse + + +class _CRG(Module): + def __init__(self, platform, clk_freq): + # Clock domains for the system (soft CPU and related components run at). + self.clock_domains.cd_sys = ClockDomain() + # Clock domains for the DDR interface. + self.clock_domains.cd_sdram_half = ClockDomain() + self.clock_domains.cd_sdram_full_wr = ClockDomain() + self.clock_domains.cd_sdram_full_rd = ClockDomain() + # Clock domain for peripherals (such as HDMI output). + self.clock_domains.cd_base50 = ClockDomain() + + self.reset = Signal() + + # Input 50MHz clock + f0 = 50*1000000 + clk50 = platform.request("clk50") + clk50a = Signal() + # Input 50MHz clock (buffered) + #self.specials += Instance("IBUFG", i_I=clk50, o_O=clk50a) + clk50b = Signal() + self.specials += Instance( + "BUFIO2", p_DIVIDE=1, + p_DIVIDE_BYPASS="TRUE", p_I_INVERT="FALSE", + i_I=clk50, o_DIVCLK=clk50b) + + p = 12 + f = Fraction(clk_freq*p, f0) + n, d = f.numerator, f.denominator + assert 19e6 <= f0/d <= 500e6 # pfd + assert 400e6 <= f0*n/d <= 1080e6 # vco + + # Unbuffered output signals from the PLL. They need to be buffered + # before feeding into the fabric. + unbuf_sdram_full = Signal() + unbuf_sdram_half_a = Signal() + unbuf_sdram_half_b = Signal() + unbuf_unused = Signal() + unbuf_sys = Signal() + unbuf_periph = Signal() + + # PLL signals + pll_lckd = Signal() + pll_fb = Signal() + self.specials.pll = Instance( + "PLL_ADV", + name="crg_pll_adv", + p_SIM_DEVICE="SPARTAN6", p_BANDWIDTH="OPTIMIZED", p_COMPENSATION="INTERNAL", + p_REF_JITTER=.01, + i_DADDR=0, i_DCLK=0, i_DEN=0, i_DI=0, i_DWE=0, i_RST=0, i_REL=0, + p_DIVCLK_DIVIDE=d, + # Input Clocks (50MHz) + i_CLKIN1=clk50b, + p_CLKIN1_PERIOD=1e9/f0, + i_CLKIN2=0, + p_CLKIN2_PERIOD=0., + i_CLKINSEL=1, + # Feedback + i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, o_LOCKED=pll_lckd, + p_CLK_FEEDBACK="CLKFBOUT", + p_CLKFBOUT_MULT=n, p_CLKFBOUT_PHASE=0., + # (333MHz) sdram wr rd + o_CLKOUT0=unbuf_sdram_full, p_CLKOUT0_DUTY_CYCLE=.5, + p_CLKOUT0_PHASE=0., p_CLKOUT0_DIVIDE=p//4, + # unused? + o_CLKOUT1=unbuf_unused, p_CLKOUT1_DUTY_CYCLE=.5, + p_CLKOUT1_PHASE=0., p_CLKOUT1_DIVIDE=15, + # (166MHz) sdram_half - sdram dqs adr ctrl + o_CLKOUT2=unbuf_sdram_half_a, p_CLKOUT2_DUTY_CYCLE=.5, + p_CLKOUT2_PHASE=270., p_CLKOUT2_DIVIDE=p//2, + # (166MHz) off-chip ddr + o_CLKOUT3=unbuf_sdram_half_b, p_CLKOUT3_DUTY_CYCLE=.5, + p_CLKOUT3_PHASE=250., p_CLKOUT3_DIVIDE=p//2, + # ( 50MHz) periph + o_CLKOUT4=unbuf_periph, p_CLKOUT4_DUTY_CYCLE=.5, + p_CLKOUT4_PHASE=0., p_CLKOUT4_DIVIDE=20, + # ( 83MHz) sysclk + o_CLKOUT5=unbuf_sys, p_CLKOUT5_DUTY_CYCLE=.5, + p_CLKOUT5_PHASE=0., p_CLKOUT5_DIVIDE=p//1, + ) + + + # power on reset? + reset = platform.request("user_btn") | self.reset + self.clock_domains.cd_por = ClockDomain() + por = Signal(max=1 << 11, reset=(1 << 11) - 1) + self.sync.por += If(por != 0, por.eq(por - 1)) + self.specials += AsyncResetSynchronizer(self.cd_por, reset) + + # System clock - 83MHz + self.specials += Instance("BUFG", i_I=unbuf_sys, o_O=self.cd_sys.clk) + self.comb += self.cd_por.clk.eq(self.cd_sys.clk) + self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll_lckd | (por > 0)) + + + # SDRAM clocks + # ------------------------------------------------------------------------------ + self.clk4x_wr_strb = Signal() + self.clk4x_rd_strb = Signal() + + # sdram_full + self.specials += Instance("BUFPLL", name="sdram_full_bufpll", + p_DIVIDE=4, + i_PLLIN=unbuf_sdram_full, i_GCLK=self.cd_sys.clk, + i_LOCKED=pll_lckd, + o_IOCLK=self.cd_sdram_full_wr.clk, + o_SERDESSTROBE=self.clk4x_wr_strb) + self.comb += [ + self.cd_sdram_full_rd.clk.eq(self.cd_sdram_full_wr.clk), + self.clk4x_rd_strb.eq(self.clk4x_wr_strb), + ] + # sdram_half + self.specials += Instance("BUFG", name="sdram_half_a_bufpll", i_I=unbuf_sdram_half_a, o_O=self.cd_sdram_half.clk) + clk_sdram_half_shifted = Signal() + self.specials += Instance("BUFG", name="sdram_half_b_bufpll", i_I=unbuf_sdram_half_b, o_O=clk_sdram_half_shifted) + clk = platform.request("ddram_clock") + self.specials += Instance("ODDR2", p_DDR_ALIGNMENT="NONE", + p_INIT=0, p_SRTYPE="SYNC", + i_D0=1, i_D1=0, i_S=0, i_R=0, i_CE=1, + i_C0=clk_sdram_half_shifted, + i_C1=~clk_sdram_half_shifted, + o_Q=clk.p) + self.specials += Instance("ODDR2", p_DDR_ALIGNMENT="NONE", + p_INIT=0, p_SRTYPE="SYNC", + i_D0=0, i_D1=1, i_S=0, i_R=0, i_CE=1, + i_C0=clk_sdram_half_shifted, i_C1=~clk_sdram_half_shifted, + o_Q=clk.n) + + # Peripheral clock - 50MHz + # ------------------------------------------------------------------------------ + # The peripheral clock is kept separate from the system clock to allow + # the system clock to be increased in the future. + dcm_base50_locked = Signal() + self.specials += [ + Instance("DCM_CLKGEN", name="crg_periph_dcm_clkgen", + p_CLKIN_PERIOD=20.0, + p_CLKFX_MULTIPLY=2, + p_CLKFX_DIVIDE=2, + p_CLKFX_MD_MAX=1.0, # CLKFX_MULTIPLY/CLKFX_DIVIDE + p_CLKFXDV_DIVIDE=2, + p_SPREAD_SPECTRUM="NONE", + p_STARTUP_WAIT="FALSE", + + i_CLKIN=clk50a, + o_CLKFX=self.cd_base50.clk, + o_LOCKED=dcm_base50_locked, + i_FREEZEDCM=0, + i_RST=ResetSignal(), + ), + AsyncResetSynchronizer(self.cd_base50, + self.cd_sys.rst | ~dcm_base50_locked) + ] + platform.add_period_constraint(self.cd_base50.clk, 20) + + +class BaseSoC(SoCSDRAM): + + mem_map = { + "spiflash": 0x20000000, # (default shadow @0xa0000000) + } + mem_map.update(SoCSDRAM.mem_map) + + + def __init__(self, **kwargs): + if 'integrated_rom_size' not in kwargs: + kwargs['integrated_rom_size']=0x8000 + if 'integrated_sram_size' not in kwargs: + kwargs['integrated_sram_size']=0x8000 + + clk_freq = (83 + Fraction(1, 3))*1000*1000 + + platform = pipistrello.Platform() + + SoCSDRAM.__init__(self, platform, clk_freq, **kwargs) + + self.submodules.crg = _CRG(platform, clk_freq) + self.platform.add_period_constraint(self.crg.cd_sys.clk, 1e9/clk_freq) + + + bios_size = 0x8000 + # sdram + sdram_module = MT46H32M16(self.clk_freq, "1:2") + self.submodules.ddrphy = s6ddrphy.S6HalfRateDDRPHY( + platform.request("ddram"), + sdram_module.memtype, + rd_bitslip=1, + wr_bitslip=3, + dqs_ddr_alignment="C1") + self.add_csr("ddrphy") + controller_settings = ControllerSettings(with_bandwidth=True) + self.register_sdram(self.ddrphy, + sdram_module.geom_settings, + sdram_module.timing_settings, + controller_settings=controller_settings) + self.comb += [ + self.ddrphy.clk4x_wr_strb.eq(self.crg.clk4x_wr_strb), + self.ddrphy.clk4x_rd_strb.eq(self.crg.clk4x_rd_strb), + ] + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on Pipistrello") + builder_args(parser) + soc_sdram_args(parser) + args = parser.parse_args() + + soc = BaseSoC(**soc_sdram_argdict(args)) + builder = Builder(soc, **builder_argdict(args)) + builder.build() + + +if __name__ == "__main__": + main()