Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICE40UP5K-B-EVN Support #90

Merged
merged 9 commits into from
Oct 25, 2018
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ jobs:
- stage: Targets
env: C=lm32.lite P=ice40_hx8k_b_evn F=stub T="base"

- stage: Targets
env: C=lm32.minimal P=ice40_up5k_b_evn F=stub T="base"
cr1901 marked this conversation as resolved.
Show resolved Hide resolved

- stage: Targets
env: C=lm32 P=mimasv2 T="base"

Expand Down
110 changes: 110 additions & 0 deletions gateware/up5kspram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from migen import *
from litex.soc.interconnect import wishbone

# ICE40 UltraPlus family-specific Wishbone interface to the Single Port RAM
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just force it to be multiples of the Single Port RAM size?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because each SPRAM has a width of 16 bits, there's only 2 valid configurations for a 32-bit softcore: Width-Cascade 2 SPRAMs to get 16k by 32bits SRAM, or Depth-Cascade and Width-Cascade 4 SPRAMs to get 32k by 32bits SRAM.

So multiples of SPRAM won't work, but perhaps a bool use_128k=False might. I'll see if I can make that work...

# (SPRAM) primitives. Because SPRAM is much more coarse grained than Block
# RAM resources, this RAM is only minimally configurable at present (64kB or
# 128kB). Because it is single port, this module is meant to be used as the
# CPU's RAM region, leaving block RAM free for other use.

class Up5kSPRAM(Module):
def __init__(self, width=32, size=64*1024):

# Right now, LiteX only supports 32-bit CPUs. To get a 32-bit data bus,
# we must width-cascade 2 16-bit SPRAMs. We've already used 2 out of 4
# SPRAMs for this, so the only other valid config is using all 4 SPRAMs
# by depth-cascading.
if width != 32:
raise ValueError("Width of Up5kSPRAM must be 32 bits")
if size != 64*1024 and size != 128*1024:
raise ValueError("Size of Up5kSPRAM must be 64kB or 128kB.")

self.bus = wishbone.Interface(width)

bytesels = []
for i in range(0, 2):
datain = Signal(16)
dataout = Signal(16)
maskwren = Signal(4)
wren = Signal(1)

# 64k vs 128k-specific routing signals.
datain0 = Signal(16)
dataout0 = Signal(16)
maskwren0 = Signal(4)

if size == 128 * 1024:
datain1 = Signal(16)
dataout1 = Signal(16)
maskwren1 = Signal(4)

# Generic routing common to all depths.
for j in range(16):
self.comb += [self.bus.dat_r[16*i + j].eq(dataout[j])]

self.comb += [
datain.eq(self.bus.dat_w[16*i:16*i+16]),
# MASKWREN is nibble-based, interestingly enough.
maskwren.eq(
Cat(
Replicate(self.bus.sel[2*i], 2),
Replicate(self.bus.sel[2*i + 1], 2),
)
),
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc)
]

# Signals which must be routed differently based on depth.
# 64kB
if size == 64*1024:
self.comb += [
datain0.eq(datain),
dataout.eq(dataout0),
maskwren0.eq(maskwren)
]
# 128kB
else:
self.comb += [
If(self.bus.adr[14],
datain1.eq(datain),
dataout.eq(dataout1),
maskwren1.eq(maskwren)
).Else(
datain0.eq(datain),
dataout.eq(dataout0),
maskwren0.eq(maskwren)
)
]

self.specials.spram = Instance("SB_SPRAM256KA",
i_ADDRESS=self.bus.adr[0:14], i_DATAIN=datain0,
i_MASKWREN=maskwren0,
i_WREN=wren,
i_CHIPSELECT=C(1,1),
i_CLOCK=ClockSignal("sys"),
i_STANDBY=C(0,1),
i_SLEEP=C(0,1),
i_POWEROFF=C(1,1),
o_DATAOUT=dataout0
)

# We need to depth cascade if using 128kB.
if size == 128*1024:
self.specials.spram = Instance("SB_SPRAM256KA",
i_ADDRESS=self.bus.adr[0:14], i_DATAIN=datain1,
i_MASKWREN=maskwren1,
i_WREN=wren,
i_CHIPSELECT=C(1,1),
i_CLOCK=ClockSignal("sys"),
i_STANDBY=C(0,1),
i_SLEEP=C(0,1),
i_POWEROFF=C(1,1),
o_DATAOUT=dataout1
)

self.sync += [
self.bus.ack.eq(0),
If(self.bus.stb & self.bus.cyc & ~self.bus.ack,
self.bus.ack.eq(1)
)
]
125 changes: 125 additions & 0 deletions platforms/ice40_up5k_b_evn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from litex.build.generic_platform import *
from litex.build.lattice import LatticePlatform
from litex.build.lattice.programmer import IceStormProgrammer


_io = [
("rgb_led", 0,
Subsignal("r", Pins("41")),
Subsignal("g", Pins("40")),
Subsignal("b", Pins("39")),
IOStandard("LVCMOS33")
),

("user_sw", 0, Pins("23"), IOStandard("LVCMOS33")),
("user_sw", 1, Pins("25"), IOStandard("LVCMOS33")),
("user_sw", 2, Pins("34"), IOStandard("LVCMOS33")),
("user_sw", 3, Pins("43"), IOStandard("LVCMOS33")),

("clk12", 0, Pins("35"), IOStandard("LVCMOS33"))
]

spiflash = [
# Only usable in PROG FLASH mode and J7 attached (see PCB silkscreen).
("spiflash", 0,
Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")),
Subsignal("clk", Pins("15"), IOStandard("LVCMOS33")),
Subsignal("mosi", Pins("14"), IOStandard("LVCMOS33")),
Subsignal("miso", Pins("17"), IOStandard("LVCMOS33")),
),
]

# The ICE40UP5K-B-EVN does not use the provided FT2232H chip to provide a
# UART port. One must use their own USB-to-serial cable instead to get a UART.
# We have chosen to use 37A and 36B for "tx" and "rx" respectively on Header B
# to implement UART connections. The board comes unpopulated and will need to
# have headers soldered.
serial = [
cr1901 marked this conversation as resolved.
Show resolved Hide resolved
("serial", 0,
Subsignal("tx", Pins("J2:0")),
Subsignal("rx", Pins("J2:1")),
IOStandard("LVCMOS33")
)
]


_connectors = [
# Many pins on the AARDVARK, PMOD, J52/HEADER A, and J2/HEADER B connectors
# are multiplexed with other I/O or connector pins. For completeness, all
# pins are exposed here except Vdd, NC, and GND. Pin order is as specified
# on the schematic (except for PMOD, which uses Digilent's numbering).

# AARDVARK connector omitted- although sysCONFIG pins are exposed on this
# header (which can be used as GPIO), it is meant for flashing using an
# external programmer rather than as an I/O port.

# PMOD connector shares pins with sysCONFIG- make sure to remove jumper
# J7 if using the PMOD. TODO: Perhaps it would be better to split into two
# single 6-pin PMODs.
#
# PMOD pinout (using ICE40 pin names):
# 1, 2, 3, 4- SPI_SS, SPI_SI, SPI_SO, SPI_SCK
# 5, 6, 7, 8- Free
("PMOD", "16 17 14 15 27 26 32 31"),

# J52 exposes LEDs and sysCONFIG pins (yet again). Make sure to remove
# jumper J7 if using the PMOD. Pin order is as follows (right to left):
# 12 10 8 6 4 2
# 11 9 7 5 3 1
#
# J52's pinout (using ICE40 pin names for SPI flash):
# 1, 2- Vdd
# 3, 4- rgb_led.b, SPI_SI
# 5, 6- rgb_led.g, SPI_SO
# 7, 8- GND, SPI_SCK
# 9, 10- rgb_led.r, SPI_SS
# 11, 12- GND
# 3 4 5 6 8 9 10
("J52", "39 17 40 14 15 41 16"),

# Pin order of J2, and J3 are as follows (left to right/top to bottom):
# 2 4 6 8 10 12 14 16 18 20
# 1 3 5 7 9 11 13 15 17 19
#
# J2's pinout is shared by many things. Entire pinout listed follows:
# 1, 2- Vdd
# 3, 4- user_sw0, NC
# 5, 6- user_sw1, NC
# 7, 8- PMOD D5, Free
# 9, 10- PMOD D4, Free
# 11, 12- PMOD D6, Free
# 13, 14- PMOD D7, Free
# 15, 16- Free, 12.00 clock
# 17, 18- user_sw2, GND
# 19, 20- user_sw3, GND
# 3 5 7 8 9 10 11 12 13 14 15 16 17 19
("J2", "23 25 26 36 27 42 32 38 31 28 37 35 34 43"),

# Bank2: 4 3 48 45 47 44 46 2
# Bank1: 12 21 13 20 19 18 11 10 9 6
# J3's pinout is all Free, except 1 (Vdd) and 19 (GND).
# 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 20
("J3", "12 4 21 3 13 48 20 45 19 47 18 44 11 46 10 2 9 6"),
]


class Platform(LatticePlatform):
default_clk_name = "clk12"
default_clk_period = 83.333

gateware_size = 0x20000

# FIXME: Create a "spi flash module" object in the same way we have SDRAM
spiflash_model = "n25q32"
spiflash_read_dummy_bits = 8
spiflash_clock_div = 2
spiflash_total_size = int((32/8)*1024*1024) # 32Mbit
spiflash_page_size = 256
spiflash_sector_size = 0x10000

def __init__(self):
LatticePlatform.__init__(self, "ice40-up5k-sg48", _io, _connectors,
toolchain="icestorm")

def create_programmer(self):
return IceStormProgrammer()
66 changes: 66 additions & 0 deletions targets/ice40_up5k_b_evn/Makefile.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ice40_hx8k_b_evn targets

ifneq ($(PLATFORM),ice40_up5k_b_evn)
$(error "Platform should be ice40_up5k_b_evn 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 "ICE40UP5K-B-EVN 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
cr1901 marked this conversation as resolved.
Show resolved Hide resolved
# 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 "ICE40UP5K-B-EVN 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
Loading