| #!/usr/bin/env python3 | |
| import os | |
| import json | |
| import subprocess | |
| from migen import ClockDomain | |
| from litex.soc.cores.gpio import GPIOOut, GPIOIn | |
| from litex.soc.cores.spi import SPIMaster | |
| from litex.soc.cores.bitbang import I2CMaster | |
| from litex.soc.cores.xadc import XADC | |
| from litex.soc.cores.pwm import PWM | |
| from litex.soc.cores.icap import ICAPBitstream | |
| from litex.soc.cores.clock import S7MMCM | |
| from litex.build.generic_platform import Cat | |
| from litex.tools.litex_json2dts import generate_dts | |
| # Predefined values -------------------------------------------------------------------------------- | |
| video_resolutions = { | |
| "1920x1080_60Hz": | |
| { | |
| "pix_clk": 148.5e6, | |
| "h-active": 1920, | |
| "h-blanking": 280, | |
| "h-sync": 44, | |
| "h-front-porch": 148, | |
| "v-active": 1080, | |
| "v-blanking": 45, | |
| "v-sync": 5, | |
| "v-front-porch": 36, | |
| }, | |
| "1280x720_60Hz": | |
| { | |
| "pix_clk": 74.25e6, | |
| "h-active": 1280, | |
| "h-blanking": 370, | |
| "h-sync": 40, | |
| "h-front-porch": 220, | |
| "v-active": 720, | |
| "v-blanking": 30, | |
| "v-sync": 5, | |
| "v-front-porch": 20, | |
| }, | |
| "640x480_75Hz": | |
| { | |
| "pix_clk": 31.5e6, | |
| "h-active": 640, | |
| "h-blanking": 200, | |
| "h-sync": 64, | |
| "h-front-porch": 16, | |
| "v-active": 480, | |
| "v-blanking": 20, | |
| "v-sync": 3, | |
| "v-front-porch": 1, | |
| } | |
| } | |
| # Helpers ------------------------------------------------------------------------------------------ | |
| def platform_request_all(platform, name): | |
| from litex.build.generic_platform import ConstraintError | |
| r = [] | |
| while True: | |
| try: | |
| r += [platform.request(name, len(r))] | |
| except ConstraintError: | |
| break | |
| if r == []: | |
| raise ValueError | |
| return r | |
| # SoCLinux ----------------------------------------------------------------------------------------- | |
| def SoCLinux(soc_cls, **kwargs): | |
| class _SoCLinux(soc_cls): | |
| csr_map = { | |
| **soc_cls.csr_map, | |
| **{ | |
| "ctrl": 0, | |
| "uart": 2, | |
| "timer0": 3, | |
| } | |
| } | |
| interrupt_map = { | |
| **soc_cls.interrupt_map, | |
| **{ | |
| "uart": 0, | |
| "timer0": 1, | |
| } | |
| } | |
| mem_map = { | |
| **soc_cls.mem_map, | |
| **{ | |
| "ethmac": 0xb0000000, # len: 0x2000 | |
| "sdread": 0xb0002000, # len: 0x200 | |
| "sdwrite": 0xb0002200, # len: 0x200 | |
| "spiflash": 0xd0000000, | |
| "csr": 0xf0000000, | |
| } | |
| } | |
| def __init__(self, cpu_variant="linux", uart_baudrate=1e6, **kwargs): | |
| # SoC ---------------------------------------------------------------------------------- | |
| soc_cls.__init__( | |
| self, | |
| cpu_type="vexriscv", | |
| cpu_variant=cpu_variant, | |
| uart_baudrate=uart_baudrate, | |
| max_sdram_size=0x40000000, # Limit mapped SDRAM to 1GB. | |
| **kwargs | |
| ) | |
| # Add linker region for machine mode emulator | |
| self.add_memory_region( | |
| "emulator", | |
| self.mem_map["main_ram"] + 0x01100000, | |
| 0x4000, | |
| type="cached+linker" | |
| ) | |
| # Leds ------------------------------------------------------------------------------------- | |
| def add_leds(self): | |
| self.submodules.leds = GPIOOut( | |
| Cat(platform_request_all(self.platform, "user_led")) | |
| ) | |
| self.add_csr("leds") | |
| # RGB Led ---------------------------------------------------------------------------------- | |
| def add_rgb_led(self): | |
| rgb_led_pads = self.platform.request("rgb_led", 0) | |
| for n in "rgb": | |
| setattr( | |
| self.submodules, "rgb_led_{}0".format(n), | |
| PWM(getattr(rgb_led_pads, n)) | |
| ) | |
| self.add_csr("rgb_led_{}0".format(n)) | |
| # Switches --------------------------------------------------------------------------------- | |
| def add_switches(self): | |
| self.submodules.switches = GPIOIn( | |
| Cat(platform_request_all(self.platform, "user_sw")) | |
| ) | |
| self.add_csr("switches") | |
| # SPI -------------------------------------------------------------------------------------- | |
| def add_spi(self, data_width, clk_freq): | |
| spi_pads = self.platform.request("spi") | |
| self.submodules.spi = SPIMaster( | |
| spi_pads, data_width, self.clk_freq, clk_freq | |
| ) | |
| self.add_csr("spi") | |
| # I2C -------------------------------------------------------------------------------------- | |
| def add_i2c(self): | |
| self.submodules.i2c0 = I2CMaster(self.platform.request("i2c", 0)) | |
| self.add_csr("i2c0") | |
| # XADC (Xilinx only) ----------------------------------------------------------------------- | |
| def add_xadc(self): | |
| self.submodules.xadc = XADC() | |
| self.add_csr("xadc") | |
| # ICAP Bitstream (Xilinx only) ------------------------------------------------------------- | |
| def add_icap_bitstream(self): | |
| self.submodules.icap_bit = ICAPBitstream() | |
| self.add_csr("icap_bit") | |
| # MMCM (Xilinx only) ----------------------------------------------------------------------- | |
| def add_mmcm(self, nclkout): | |
| if (nclkout > 7): | |
| raise ValueError("nclkout cannot be above 7!") | |
| self.cd_mmcm_clkout = [] | |
| self.submodules.mmcm = S7MMCM(speedgrade=-1) | |
| self.mmcm.register_clkin(self.crg.cd_sys.clk, self.clk_freq) | |
| for n in range(nclkout): | |
| self.cd_mmcm_clkout += [ | |
| ClockDomain(name="cd_mmcm_clkout{}".format(n)) | |
| ] | |
| self.mmcm.create_clkout(self.cd_mmcm_clkout[n], self.clk_freq) | |
| self.mmcm.clock_domains.cd_mmcm_clkout = self.cd_mmcm_clkout | |
| self.add_constant("clkout_def_freq", int(self.clk_freq)) | |
| self.add_constant("clkout_def_phase", int(0)) | |
| self.add_constant("clkout_def_duty_num", int(50)) | |
| self.add_constant("clkout_def_duty_den", int(100)) | |
| # We need to write exponent of clkout_margin to allow the driver for smaller inaccuracy | |
| from math import log10 | |
| exp = log10(self.mmcm.clkouts[0][3]) | |
| if exp < 0: | |
| self.add_constant("clkout_margin_exp", int(abs(exp))) | |
| self.add_constant( | |
| "clkout_margin", | |
| int(self.mmcm.clkouts[0][3] * 10**abs(exp)) | |
| ) | |
| else: | |
| self.add_constant( | |
| "clkout_margin", int(self.mmcm.clkouts[0][3]) | |
| ) | |
| self.add_constant("clkout_margin_exp", int(0)) | |
| self.add_constant("nclkout", int(nclkout)) | |
| self.add_constant("mmcm_lock_timeout", int(10)) | |
| self.add_constant("mmcm_drdy_timeout", int(10)) | |
| self.add_constant("vco_margin", int(self.mmcm.vco_margin)) | |
| self.add_constant( | |
| "vco_freq_range_min", int(self.mmcm.vco_freq_range[0]) | |
| ) | |
| self.add_constant( | |
| "vco_freq_range_max", int(self.mmcm.vco_freq_range[1]) | |
| ) | |
| self.add_constant( | |
| "clkfbout_mult_frange_min", | |
| int(self.mmcm.clkfbout_mult_frange[0]) | |
| ) | |
| self.add_constant( | |
| "clkfbout_mult_frange_max", | |
| int(self.mmcm.clkfbout_mult_frange[1]) | |
| ) | |
| self.add_constant( | |
| "divclk_divide_range_min", | |
| int(self.mmcm.divclk_divide_range[0]) | |
| ) | |
| self.add_constant( | |
| "divclk_divide_range_max", | |
| int(self.mmcm.divclk_divide_range[1]) | |
| ) | |
| self.add_constant( | |
| "clkout_divide_range_min", | |
| int(self.mmcm.clkout_divide_range[0]) | |
| ) | |
| self.add_constant( | |
| "clkout_divide_range_max", | |
| int(self.mmcm.clkout_divide_range[1]) | |
| ) | |
| self.mmcm.expose_drp() | |
| self.add_csr("mmcm") | |
| self.comb += self.mmcm.reset.eq(self.mmcm.drp_reset.re) | |
| # Ethernet configuration ------------------------------------------------------------------- | |
| def configure_ethernet(self, local_ip, remote_ip): | |
| local_ip = local_ip.split(".") | |
| remote_ip = remote_ip.split(".") | |
| self.add_constant("LOCALIP1", int(local_ip[0])) | |
| self.add_constant("LOCALIP2", int(local_ip[1])) | |
| self.add_constant("LOCALIP3", int(local_ip[2])) | |
| self.add_constant("LOCALIP4", int(local_ip[3])) | |
| self.add_constant("REMOTEIP1", int(remote_ip[0])) | |
| self.add_constant("REMOTEIP2", int(remote_ip[1])) | |
| self.add_constant("REMOTEIP3", int(remote_ip[2])) | |
| self.add_constant("REMOTEIP4", int(remote_ip[3])) | |
| # Boot configuration ----------------------------------------------------------------------- | |
| def configure_boot(self): | |
| if hasattr(self, "spiflash"): | |
| self.add_constant( | |
| "FLASH_BOOT_ADDRESS", self.mem_map["spiflash"] | |
| ) | |
| # DTS generation --------------------------------------------------------------------------- | |
| def generate_dts(self, build_dir, board_name): | |
| json_src = os.path.join(build_dir, "csr.json") | |
| dts = os.path.join(build_dir, "{}.dts".format(board_name)) | |
| with open(json_src) as json_file, open(dts, "w") as dts_file: | |
| dts_content = generate_dts(json.load(json_file)) | |
| dts_file.write(dts_content) | |
| # DTS compilation -------------------------------------------------------------------------- | |
| def compile_dts(self, build_dir, board_name): | |
| dts = os.path.join(build_dir, "{}.dts".format(board_name)) | |
| dtb = os.path.join("buildroot", "rv32.dtb") | |
| subprocess.check_call( | |
| "dtc -O dtb -o {} {}".format(dtb, dts), shell=True | |
| ) | |
| # Emulator compilation --------------------------------------------------------------------- | |
| def compile_emulator(self, build_dir): | |
| os.environ["BUILD_DIR"] = build_dir | |
| subprocess.check_call("cd emulator && make", shell=True) | |
| # Documentation generation ----------------------------------------------------------------- | |
| def generate_doc(self, board_name): | |
| from litex.soc.doc import generate_docs | |
| doc_dir = os.path.join("build", board_name, "doc") | |
| generate_docs(self, doc_dir) | |
| os.system( | |
| "sphinx-build -M html {}/ {}/_build".format(doc_dir, doc_dir) | |
| ) | |
| return _SoCLinux(**kwargs) |