diff --git a/gateware/.gitignore b/gateware/.gitignore new file mode 100644 index 0000000..9d68e3c --- /dev/null +++ b/gateware/.gitignore @@ -0,0 +1,2 @@ +build/ +*.json \ No newline at end of file diff --git a/gateware/env.bash b/gateware/env.bash new file mode 100644 index 0000000..cf773c4 --- /dev/null +++ b/gateware/env.bash @@ -0,0 +1,5 @@ +# RV32 toolchain +export PATH="/home/david/install/litex/riscv64-unknown-elf-gcc-10.1.0-2020.08.2-x86_64-linux-ubuntu14/bin:$PATH" + +# Yosys/Nextpnr +export PATH="/home/david/install/oss-cad-suite/bin:$PATH" diff --git a/gateware/main.py b/gateware/main.py new file mode 100755 index 0000000..d3b861c --- /dev/null +++ b/gateware/main.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +# General imports +from migen import * + +from litex.build.io import DDROutput + +from litex_boards.platforms import colorlight_i5 + +from litex.build.lattice.trellis import trellis_args, trellis_argdict + +from litex.soc.cores.clock import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * + +from litex.soc.interconnect.csr import * + +from litedram.modules import M12L64322A +from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY + +from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII + +# Module to configure clocks and resets +class _CRG(Module): + def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_video_pll=False, sdram_rate="1:1"): + self.rst = Signal() + self.clock_domains.cd_sys = ClockDomain() + if sdram_rate == "1:2": + self.clock_domains.cd_sys2x = ClockDomain() + self.clock_domains.cd_sys2x_ps = ClockDomain() + else: + self.clock_domains.cd_sys_ps = ClockDomain() + + # # # + + # Clk / Rst + if not use_internal_osc: + clk = platform.request("clk25") + clk_freq = 25e6 + else: + clk = Signal() + div = 5 + self.specials += Instance("OSCG", + p_DIV = div, + o_OSC = clk + ) + clk_freq = 310e6/div + + rst_n = platform.request("cpu_reset_n") + + # PLL + self.submodules.pll = pll = ECP5PLL() + self.comb += pll.reset.eq(~rst_n | self.rst) + pll.register_clkin(clk, clk_freq) + pll.create_clkout(self.cd_sys, sys_clk_freq) + if sdram_rate == "1:2": + pll.create_clkout(self.cd_sys2x, 2*sys_clk_freq) + pll.create_clkout(self.cd_sys2x_ps, 2*sys_clk_freq, phase=180) # Idealy 90° but needs to be increased. + else: + pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90° but needs to be increased. + + # USB PLL + if with_usb_pll: + self.submodules.usb_pll = usb_pll = ECP5PLL() + self.comb += usb_pll.reset.eq(~rst_n | self.rst) + usb_pll.register_clkin(clk, clk_freq) + self.clock_domains.cd_usb_12 = ClockDomain() + self.clock_domains.cd_usb_48 = ClockDomain() + usb_pll.create_clkout(self.cd_usb_12, 12e6, margin=0) + usb_pll.create_clkout(self.cd_usb_48, 48e6, margin=0) + + # Video PLL + if with_video_pll: + self.submodules.video_pll = video_pll = ECP5PLL() + self.comb += video_pll.reset.eq(~rst_n | self.rst) + video_pll.register_clkin(clk, clk_freq) + self.clock_domains.cd_hdmi = ClockDomain() + self.clock_domains.cd_hdmi5x = ClockDomain() + video_pll.create_clkout(self.cd_hdmi, 40e6, margin=0) + video_pll.create_clkout(self.cd_hdmi5x, 200e6, margin=0) + + # SDRAM clock + sdram_clk = ClockSignal("sys2x_ps" if sdram_rate == "1:2" else "sys_ps") + self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk) + + +# SoC definition - this basically instantiates hardware +class SoC(SoCCore): + # While there are more configurations in what I'm basing this off of, I'm reducing it to + # one supported config. + def __init__(self, **kwargs): + platform = colorlight_i5.Platform(board="i9", revision = "7.2", toolchain="trellis") + + sys_clk_freq = 50e6 + + self.submodules.crg = _CRG(platform, sys_clk_freq, sdram_rate="1:1") + + # Initialize base SoC core stuff, with given system clock + SoCCore.__init__(self, platform, int(sys_clk_freq), ident = "Sonar SoC on Colorlight i9", **kwargs) + + # Set SPI flash with correct configuration + from litespi.modules import W25Q64 as SpiFlashModule + from litespi.opcodes import SpiNorFlashOpCodes as Codes + # 1x SPI interface (as opposed to QSPI or something), and use the simplest READ timing commands + self.add_spi_flash(mode="1x", module=SpiFlashModule(Codes.READ_1_1_1)) + + # Set up SDRAM + sdrphy_cls = GENSDRPHY + self.submodules.sdrphy = sdrphy_cls(platform.request("sdram")) + self.add_sdram("sdram", + phy = self.sdrphy, + module = M12L64322A(sys_clk_freq, "1:1"), + l2_cache_size = 8192, + ) + + # TODO ethernet + + +def main(): + from litex.soc.integration.soc import LiteXSoCArgumentParser + parser = LiteXSoCArgumentParser(description="LiteX SoC for FPGA sonar") + target_group = parser.add_argument_group(title="Target options") + target_group.add_argument("--build", action="store_true", help="Build design") + target_group.add_argument("--load", action="store_true", help="Load design onto board") + builder_args(parser) + soc_core_args(parser) + trellis_args(parser) + args = parser.parse_args() + + soc = SoC(**soc_core_argdict(args)) + + builder = Builder(soc, **builder_argdict(args)) + builder_kargs = trellis_argdict(args) + if args.build: + builder.build(**builder_kargs) + + if args.load: + prog = soc.platform.create_programmer() + prog.load_bitstream(builder.get_bitstream_filename(mode="sram")) + + +if __name__ == "__main__": + main()