diff --git a/platforms/ice40_feather.py b/platforms/icefeather.py similarity index 97% rename from platforms/ice40_feather.py rename to platforms/icefeather.py index f7c416052..1ec9bb26c 100644 --- a/platforms/ice40_feather.py +++ b/platforms/icefeather.py @@ -9,6 +9,7 @@ ("user_ledr_n", 0, Pins("41"), IOStandard("LVCMOS33")), ("user_ledg_n", 0, Pins("40"), IOStandard("LVCMOS33")), ("user_ledb_n", 0, Pins("39"), IOStandard("LVCMOS33")), + ("user_btn_n", 0, Pins("2"), IOStandard("LVCMOS33")), ("serial", 0, Subsignal("rx", Pins("23")), diff --git a/scripts/download-env.sh b/scripts/download-env.sh index bb77de0ac..752910cbf 100755 --- a/scripts/download-env.sh +++ b/scripts/download-env.sh @@ -438,7 +438,7 @@ if [ "$PLATFORM" = "tinyfpga_bx" ]; then fi # iceprog compatible platforms -if [ "$PLATFORM" = "icebreaker" -o "$PLATFORM" = "ice40_hx8k_b_evn" -o "$PLATFORM" = "ice40_up5k_b_evn" ]; then +if [ "$PLATFORM" = "icebreaker" -o "$PLATFORM" = "icefeather" -o "$PLATFORM" = "ice40_hx8k_b_evn" -o "$PLATFORM" = "ice40_up5k_b_evn" ]; then echo echo "Installing iceprog (tool for FTDI)" conda install iceprog diff --git a/scripts/enter-env.sh b/scripts/enter-env.sh index 17d41c9a9..88bddf2a4 100755 --- a/scripts/enter-env.sh +++ b/scripts/enter-env.sh @@ -361,7 +361,7 @@ if [ "$PLATFORM" = "tinyfpga_bx" ]; then fi # iceprog compatible platforms -if [ "$PLATFORM" = "icebreaker" -o "$PLATFORM" = "ice40_hx8k_b_evn" -o "$PLATFORM" = "ice40_up5k_b_evn" ]; then +if [ "$PLATFORM" = "icebreaker" -o "$PLATFORM" = "icefeather" -o "$PLATFORM" = "ice40_hx8k_b_evn" -o "$PLATFORM" = "ice40_up5k_b_evn" ]; then diff --git a/targets/icefeather/Makefile.mk b/targets/icefeather/Makefile.mk new file mode 100644 index 000000000..19873b780 --- /dev/null +++ b/targets/icefeather/Makefile.mk @@ -0,0 +1,66 @@ +# ice40_up5k targets + +ifneq ($(PLATFORM),icefeather) + $(error "Platform should be icefeather when using this file!?") +endif + +# Settings +DEFAULT_TARGET = base +TARGET ?= $(DEFAULT_TARGET) +BAUD ?= 115200 + +# Image +image-flash-$(PLATFORM): + iceprog $(IMAGE_FILE) + +# Gateware +gateware-load-$(PLATFORM): + @echo "icefeather doesn't support loading, use the flash target instead." + @echo "make gateware-flash" + @false + +# As with Mimasv2, if the user asks to flash the gateware only, the BIOS must +# be sent as well (because the BIOS is too big to fit into the bitstream). +GATEWARE_BIOS_FILE = $(TARGET_BUILD_DIR)/image-gateware+bios+none.bin + +gateware-flash-$(PLATFORM): $(GATEWARE_BIOS_FILE) + iceprog $(GATEWARE_BIOS_FILE) + +# To avoid duplicating the mkimage.py call here, if the user has not +# already built a image-gateware+bios+none.bin, we call make recursively +# to build one here, with the FIRMWARE=none override. +# +ifneq ($(GATEWARE_BIOS_FILE),$(IMAGE_FILE)) +$(GATEWARE_BIOS_FILE): $(GATEWARE_FILEBASE).bin $(BIOS_FILE) mkimage.py + FIRMWARE=none make image +endif + +# Firmware +firmware-load-$(PLATFORM): + @echo "Unsupported." + @false + +firmware-flash-$(PLATFORM): + @echo "icefeather doesn't support just flashing firmware, use image target instead." + @echo "make image-flash" + @false + +firmware-connect-$(PLATFORM): + flterm --port=$(COMM_PORT) --speed=$(BAUD) + +firmware-clear-$(PLATFORM): + @echo "FIXME: Unsupported?." + @false + +# Bios +bios-flash-$(PLATFORM): + @echo "Unsupported." + @false + +# Extra commands +help-$(PLATFORM): + @true + +reset-$(PLATFORM): + @echo "Unsupported." + @false diff --git a/targets/icefeather/base.py b/targets/icefeather/base.py new file mode 100644 index 000000000..bdb7c91dc --- /dev/null +++ b/targets/icefeather/base.py @@ -0,0 +1,118 @@ +import sys +import struct +import os.path +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.build.generic_platform import Pins, Subsignal, IOStandard +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage + +from gateware import ice40 +from gateware import cas +from gateware import spi_flash + +from targets.utils import define_flash_constants +from platforms import icefeather + +class _CRG(Module): + def __init__(self, platform): + clk12 = platform.request("clk12") + + self.clock_domains.cd_sys = ClockDomain() + self.reset = Signal() + + # FIXME: Use PLL, increase system clock to 32 MHz, pending nextpnr + # fixes. + self.comb += self.cd_sys.clk.eq(clk12) + + # POR reset logic- POR generated from sys clk, POR logic feeds sys clk + # reset. + reset = self.reset | ~platform.request("user_btn_n") + self.clock_domains.cd_por = ClockDomain() + reset_delay = Signal(12, reset=4095) + self.comb += [ + self.cd_por.clk.eq(self.cd_sys.clk), + self.cd_sys.rst.eq(reset_delay != 0) + ] + self.sync.por += \ + If(reset_delay != 0, + reset_delay.eq(reset_delay - 1) + ) + self.specials += AsyncResetSynchronizer(self.cd_por, reset) + + +class BaseSoC(SoCCore): + mem_map = {**SoCCore.mem_map, **{ + "spiflash": 0xa0000000, + }} + + def __init__(self, platform, **kwargs): + # disable SRAM, it'll be added later + kwargs['integrated_sram_size'] = 0x0 + + # disable ROM, it'll be added later + kwargs['integrated_rom_size'] = 0x0 + + # FIXME: Force either lite or minimal variants of CPUs; full is too big. + + # Assume user still has LEDs/Buttons still attached to the PCB or as + # a PMOD; pinout is identical either way. + clk_freq = int(12e6) + + kwargs['cpu_reset_address']=self.mem_map["spiflash"]+platform.gateware_size + SoCCore.__init__(self, platform, clk_freq, **kwargs) + + self.submodules.crg = _CRG(platform) + self.platform.add_period_constraint(self.crg.cd_sys.clk, 1e9/clk_freq) + + # Control and Status + self.submodules.cas = cas.ControlAndStatus(platform, clk_freq) + self.add_csr("cas") + + # SPI flash peripheral + # TODO: Inferred tristate not currently supported by nextpnr; upgrade + # to spiflash4x when possible. + self.submodules.spiflash = spi_flash.SpiFlashSingle( + platform.request("spiflash"), + dummy=platform.spiflash_read_dummy_bits, + div=platform.spiflash_clock_div, + endianness=self.cpu.endianness) + self.add_csr("spiflash") + self.add_constant("SPIFLASH_PAGE_SIZE", platform.spiflash_page_size) + self.add_constant("SPIFLASH_SECTOR_SIZE", platform.spiflash_sector_size) + self.register_mem("spiflash", self.mem_map["spiflash"], + self.spiflash.bus, size=platform.spiflash_total_size) + + # rgb led connector + platform.add_extension(icefeather.rgb_led) + self.submodules.rgbled = ice40.LED(platform.request("rgbled", 0)) + self.add_csr("rgbled") + + bios_size = 0x8000 + self.add_constant("ROM_DISABLE", 1) + self.add_memory_region( + "rom", kwargs['cpu_reset_address'], bios_size, + type="cached+linker") + self.flash_boot_address = self.mem_map["spiflash"]+platform.gateware_size+bios_size + define_flash_constants(self) + + # SPRAM- UP5K has single port RAM, might as well use it as SRAM to + # free up scarce block RAM. + self.submodules.spram = ice40.SPRAM(size=128*1024) + self.register_mem("sram", self.mem_map["sram"], self.spram.bus, 0x20000) + + # We don't have a DRAM, so use the remaining SPI flash for user + # program. + self.add_memory_region("user_flash", + self.flash_boot_address, + # Leave a grace area- possible one-by-off bug in add_memory_region? + # Possible fix: addr < origin + length - 1 + platform.spiflash_total_size - (self.flash_boot_address - self.mem_map["spiflash"]) - 0x100, + type="cached+linker") + + +SoC = BaseSoC