rtl: break foboot modules into their own files

These are all independent modules, so break each one out into its own
file.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2019-11-23 10:00:22 +08:00
parent c8e632dd7e
commit 45e3b5b617
8 changed files with 566 additions and 546 deletions

View File

@ -13,27 +13,20 @@ import lxbuildenv
#from migen import *
from migen import Module, Signal, Instance, ClockDomain, If
from migen.genlib import fifo
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.fhdl.specials import TSTriple
from migen.fhdl.bitcontainer import bits_for
from migen.fhdl.structure import ClockSignal, ResetSignal, Replicate, Cat
from migen.fhdl.decorators import ClockDomainsRenamer
from litex.build.lattice.platform import LatticePlatform
from litex.build.generic_platform import Pins, IOStandard, Misc, Subsignal
from litex.build.generic_platform import Pins, Subsignal
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from litex.soc.integration import SoCCore
from litex.soc.integration.builder import Builder
from litex.soc.integration.soc_core import csr_map_update
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
from litex.soc.cores import up5kspram, spi_flash
from litex_boards.partner.targets.fomu import _CRG
from valentyusb import usbcore
from valentyusb.usbcore import io as usbio
from valentyusb.usbcore.cpu import epmem, unififo, epfifo, dummyusb, eptri
from valentyusb.usbcore.endpoint import EndpointType
@ -43,49 +36,13 @@ import spibone
import argparse
import os
import subprocess
class RandomFirmwareROM(wishbone.SRAM):
"""
Seed the random data with a fixed number, so different bitstreams
can all share firmware.
"""
def __init__(self, size, seed=2373):
def xorshift32(x):
x = x ^ (x << 13) & 0xffffffff
x = x ^ (x >> 17) & 0xffffffff
x = x ^ (x << 5) & 0xffffffff
return x & 0xffffffff
def get_rand(x):
out = 0
for i in range(32):
x = xorshift32(x)
if (x & 1) == 1:
out = out | (1 << i)
return out & 0xffffffff
data = []
seed = 1
for d in range(int(size / 4)):
seed = get_rand(seed)
data.append(seed)
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
class FirmwareROM(wishbone.SRAM):
def __init__(self, size, filename):
data = []
with open(filename, 'rb') as inp:
data = inp.read()
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
class JumpToAddressROM(wishbone.SRAM):
def __init__(self, size, addr):
data = [
0x00000537 | ((addr & 0xfffff000) << 0 ), # lui a0,%hi(addr)
0x00052503 | ((addr & 0x00000fff) << 20), # lw a0,%lo(addr)(a0)
0x000500e7, # jalr a0
]
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
from rtl.version import Version
from rtl.romgen import RandomFirmwareROM, FirmwareROM
from rtl.fomutouch import TouchPads
from rtl.sbled import SBLED
from rtl.sbwarmboot import SBWarmBoot
from rtl.messible import Messible
class Platform(LatticePlatform):
def __init__(self, revision=None, toolchain="icestorm"):
@ -108,501 +65,6 @@ class Platform(LatticePlatform):
def create_programmer(self):
raise ValueError("programming is not supported")
class SBLED(Module, AutoCSR):
def __init__(self, revision, pads):
rgba_pwm = Signal(3)
self.intro = ModuleDoc("""RGB LED Controller
The ICE40 contains two different RGB LED control devices. The first is a
constant-current LED source, which is fixed to deliver 4 mA to each of the
three LEDs. This block is called ``SB_RGBA_DRV``.
The other is used for creating interesting fading effects, particularly
for "breathing" effects used to indicate a given state. This block is called
``SB_LEDDA_IP``. This block feeds directly into ``SB_RGBA_DRV``.
The RGB LED controller available on this device allows for control of these
two LED control devices. Additionally, it is possible to disable ``SB_LEDDA_IP``
and directly control the individual LEDs.
""")
self.dat = CSRStorage(8, description="""
This is the value for the ``SB_LEDDA_IP.DAT`` register. It is directly
written into the ``SB_LEDDA_IP`` hardware block, so you should
refer to http://www.latticesemi.com/view_document?document_id=50668.
The contents of this register are written to the address specified in
``ADDR`` immediately upon writing this register.""")
self.addr = CSRStorage(4, description="""
This register is directly connected to ``SB_LEDDA_IP.ADDR``. This
register controls the address that is updated whenever ``DAT`` is
written. Writing to this register has no immediate effect -- data
isn't written until the ``DAT`` register is written.""")
self.ctrl = CSRStorage(fields=[
CSRField("exe", description="Connected to ``SB_LEDDA_IP.LEDDEXE``. Set this to ``1`` to enable the fading pattern."),
CSRField("curren", description="Connected to ``SB_RGBA_DRV.CURREN``. Set this to ``1`` to enable the current source."),
CSRField("rgbleden", description="Connected to ``SB_RGBA_DRV.RGBLEDEN``. Set this to ``1`` to enable the RGB PWM control logic."),
CSRField("rraw", description="Set this to ``1`` to enable raw control of the red LED via the ``RAW.R`` register."),
CSRField("graw", description="Set this to ``1`` to enable raw control of the green LED via the ``RAW.G`` register."),
CSRField("braw", description="Set this to ``1`` to enable raw control of the blue LED via the ``RAW.B`` register."),
], description="Control logic for the RGB LED and LEDDA hardware PWM LED block.")
self.raw = CSRStorage(fields=[
CSRField("r", description="Raw value for the red LED when ``CTRL.RRAW`` is ``1``."),
CSRField("g", description="Raw value for the green LED when ``CTRL.GRAW`` is ``1``."),
CSRField("b", description="Raw value for the blue LED when ``CTRL.BRAW`` is ``1``."),
], description="""
Normally the hardware ``SB_LEDDA_IP`` block controls the brightness of the LED,
creating a gentle fading pattern. However, by setting the appropriate bit in ``CTRL``,
it is possible to manually control the three individual LEDs.""")
ledd_value = Signal(3)
if revision == "pvt" or revision == "evt" or revision == "dvt":
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[1].eq(self.raw.storage[0])).Else(rgba_pwm[1].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[0].eq(self.raw.storage[1])).Else(rgba_pwm[0].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[2].eq(self.raw.storage[2])).Else(rgba_pwm[2].eq(ledd_value[2])),
]
elif revision == "hacker":
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[2].eq(self.raw.storage[0])).Else(rgba_pwm[2].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[1].eq(self.raw.storage[1])).Else(rgba_pwm[1].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[0].eq(self.raw.storage[2])).Else(rgba_pwm[0].eq(ledd_value[2])),
]
else:
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[0].eq(self.raw.storage[0])).Else(rgba_pwm[0].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[1].eq(self.raw.storage[1])).Else(rgba_pwm[1].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[2].eq(self.raw.storage[2])).Else(rgba_pwm[2].eq(ledd_value[2])),
]
self.specials += Instance("SB_RGBA_DRV",
i_CURREN = self.ctrl.storage[1],
i_RGBLEDEN = self.ctrl.storage[2],
i_RGB0PWM = rgba_pwm[0],
i_RGB1PWM = rgba_pwm[1],
i_RGB2PWM = rgba_pwm[2],
o_RGB0 = pads.r,
o_RGB1 = pads.g,
o_RGB2 = pads.b,
p_CURRENT_MODE = "0b1",
p_RGB0_CURRENT = "0b000011",
p_RGB1_CURRENT = "0b000011",
p_RGB2_CURRENT = "0b000011",
)
self.specials += Instance("SB_LEDDA_IP",
i_LEDDCS = self.dat.re,
i_LEDDCLK = ClockSignal(),
i_LEDDDAT7 = self.dat.storage[7],
i_LEDDDAT6 = self.dat.storage[6],
i_LEDDDAT5 = self.dat.storage[5],
i_LEDDDAT4 = self.dat.storage[4],
i_LEDDDAT3 = self.dat.storage[3],
i_LEDDDAT2 = self.dat.storage[2],
i_LEDDDAT1 = self.dat.storage[1],
i_LEDDDAT0 = self.dat.storage[0],
i_LEDDADDR3 = self.addr.storage[3],
i_LEDDADDR2 = self.addr.storage[2],
i_LEDDADDR1 = self.addr.storage[1],
i_LEDDADDR0 = self.addr.storage[0],
i_LEDDDEN = self.dat.re,
i_LEDDEXE = self.ctrl.storage[0],
# o_LEDDON = led_is_on, # Indicates whether LED is on or not
# i_LEDDRST = ResetSignal(), # This port doesn't actually exist
o_PWMOUT0 = ledd_value[0],
o_PWMOUT1 = ledd_value[1],
o_PWMOUT2 = ledd_value[2],
o_LEDDON = Signal(),
)
class SBWarmBoot(Module, AutoCSR):
def __init__(self, parent, offsets=None):
table = ""
if offsets is not None:
arr = [["Image", "Offset"]]
for i,offset in enumerate(offsets):
arr.append([str(i), str(offset)])
table = "\nYou can use this block to reboot into one of these four addresses:\n\n" \
+ lxsocdoc.rst.make_table(arr)
self.intro = ModuleDoc("""FPGA Reboot Interface
This module provides the ability to reboot the FPGA. It is based on the
``SB_WARMBOOT`` primitive built in to the FPGA.
When power is applied to the FPGA, it reads configuration data from the
onboard flash chip. This contains reboot offsets for four images. It then
booted from the first image, but kept note of the other addresses.
{}""".format(table))
self.ctrl = CSRStorage(fields=[
CSRField("image", size=2, description="""
Which image to reboot to. ``SB_WARMBOOT`` supports four images that
are configured at FPGA startup. The bootloader is image 0, so set
these bits to 0 to reboot back into the bootloader.
"""),
CSRField("key", size=6, description="""
A reboot key used to prevent accidental reboots when writing to random
areas of memory. To initiate a reboot, set this to ``0b101011``.""")
], description="""
Provides support for rebooting the FPGA. You can select which of the four images
to reboot to, just be sure to OR the image number with ``0xac``. For example,
to reboot to the bootloader (image 0), write ``0xac``` to this register."""
)
self.addr = CSRStorage(size=32, description="""
This sets the reset vector for the VexRiscv. This address will be used whenever
the CPU is reset, for example through a debug bridge. You should update this
address whenever you load a new program, to enable the debugger to run ``mon reset``
"""
)
do_reset = Signal()
self.comb += [
# "Reset Key" is 0xac (0b101011xx)
do_reset.eq(self.ctrl.storage[2] & self.ctrl.storage[3] & ~self.ctrl.storage[4]
& self.ctrl.storage[5] & ~self.ctrl.storage[6] & self.ctrl.storage[7])
]
self.specials += Instance("SB_WARMBOOT",
i_S0 = self.ctrl.storage[0],
i_S1 = self.ctrl.storage[1],
i_BOOT = do_reset,
)
parent.config["BITSTREAM_SYNC_HEADER1"] = 0x7e99aa7e
parent.config["BITSTREAM_SYNC_HEADER2"] = 0x7eaa997e
class TouchPads(Module, AutoCSR):
touch_device = [
("touch_pads", 0,
Subsignal("t1", Pins("touch_pins:0")),
Subsignal("t2", Pins("touch_pins:1")),
Subsignal("t3", Pins("touch_pins:2")),
Subsignal("t4", Pins("touch_pins:3")),
)
]
def __init__(self, pads):
self.intro = ModuleDoc("""Fomu Touchpads
Fomu has four single-ended exposed pads on its side. These pads are designed
to be connected to some captouch block, or driven in a resistive touch mode
in order to get simple touchpad support.
This block simply provides CPU-controlled GPIO support for this block. It has
three registers which control the In, Out, and Output Enable functionality of
each of these pins.
""")
touch1 = TSTriple()
touch2 = TSTriple()
touch3 = TSTriple()
touch4 = TSTriple()
self.specials += touch1.get_tristate(pads.t1)
self.specials += touch2.get_tristate(pads.t2)
self.specials += touch3.get_tristate(pads.t3)
self.specials += touch4.get_tristate(pads.t4)
self.o = CSRStorage(4, description="Output values for pads 1-4")
self.oe = CSRStorage(4, description="Output enable control for pads 1-4")
self.i = CSRStatus(4, description="Input value for pads 1-4")
self.comb += [
touch1.o.eq(self.o.storage[0]),
touch2.o.eq(self.o.storage[1]),
touch3.o.eq(self.o.storage[2]),
touch4.o.eq(self.o.storage[3]),
touch1.oe.eq(self.oe.storage[0]),
touch2.oe.eq(self.oe.storage[1]),
touch3.oe.eq(self.oe.storage[2]),
touch4.oe.eq(self.oe.storage[3]),
self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i))
]
class PicoRVSpi(Module, AutoCSR):
def __init__(self, platform, pads, size=2*1024*1024):
self.size = size
self.bus = bus = wishbone.Interface()
self.reset = Signal()
self.cfg1 = CSRStorage(fields=[
CSRField("bb_out", size=4, description="Output bits in bit-bang mode"),
CSRField("bb_clk", description="Serial clock line in bit-bang mode"),
CSRField("bb_cs", description="Chip select line in bit-bang mode"),
])
self.cfg2 = CSRStorage(fields=[
CSRField("bb_oe", size=4, description="Output Enable bits in bit-bang mode"),
])
self.cfg3 = CSRStorage(fields=[
CSRField("rlat", size=4, description="Read latency/dummy cycle count"),
CSRField("crm", description="Continuous Read Mode enable bit"),
CSRField("qspi", description="Quad-SPI enable bit"),
CSRField("ddr", description="Double Data Rate enable bit"),
])
self.cfg4 = CSRStorage(fields=[
CSRField("memio", offset=7, reset=1, description="Enable memory-mapped mode (set to 0 to enable bit-bang mode)")
])
self.stat1 = CSRStatus(fields=[
CSRField("bb_in", size=4, description="Input bits in bit-bang mode"),
])
self.stat2 = CSRStatus(1, fields=[], description="Reserved")
self.stat3 = CSRStatus(1, fields=[], description="Reserved")
self.stat4 = CSRStatus(1, fields=[], description="Reserved")
cfg = Signal(32)
cfg_we = Signal(4)
cfg_out = Signal(32)
self.comb += [
cfg.eq(Cat(self.cfg1.storage, self.cfg2.storage, self.cfg3.storage, self.cfg4.storage)),
cfg_we.eq(Cat(self.cfg1.re, self.cfg2.re, self.cfg3.re, self.cfg4.re)),
self.stat1.status.eq(cfg_out[0:8]),
self.stat2.status.eq(cfg_out[8:16]),
self.stat3.status.eq(cfg_out[16:24]),
self.stat4.status.eq(cfg_out[24:32]),
]
mosi_pad = TSTriple()
miso_pad = TSTriple()
cs_n_pad = TSTriple()
clk_pad = TSTriple()
wp_pad = TSTriple()
hold_pad = TSTriple()
self.specials += mosi_pad.get_tristate(pads.mosi)
self.specials += miso_pad.get_tristate(pads.miso)
self.specials += cs_n_pad.get_tristate(pads.cs_n)
self.specials += clk_pad.get_tristate(pads.clk)
self.specials += wp_pad.get_tristate(pads.wp)
self.specials += hold_pad.get_tristate(pads.hold)
reset = Signal()
self.comb += [
reset.eq(ResetSignal() | self.reset),
cs_n_pad.oe.eq(~reset),
clk_pad.oe.eq(~reset),
]
flash_addr = Signal(24)
# size/4 because data bus is 32 bits wide, -1 for base 0
mem_bits = bits_for(int(size/4)-1)
pad = Signal(2)
self.comb += flash_addr.eq(Cat(pad, bus.adr[0:mem_bits-1]))
read_active = Signal()
spi_ready = Signal()
self.sync += [
If(bus.stb & bus.cyc & ~read_active,
read_active.eq(1),
bus.ack.eq(0),
)
.Elif(read_active & spi_ready,
read_active.eq(0),
bus.ack.eq(1),
)
.Else(
bus.ack.eq(0),
read_active.eq(0),
)
]
o_rdata = Signal(32)
self.comb += bus.dat_r.eq(o_rdata)
self.specials += Instance("spimemio",
o_flash_io0_oe = mosi_pad.oe,
o_flash_io1_oe = miso_pad.oe,
o_flash_io2_oe = wp_pad.oe,
o_flash_io3_oe = hold_pad.oe,
o_flash_io0_do = mosi_pad.o,
o_flash_io1_do = miso_pad.o,
o_flash_io2_do = wp_pad.o,
o_flash_io3_do = hold_pad.o,
o_flash_csb = cs_n_pad.o,
o_flash_clk = clk_pad.o,
i_flash_io0_di = mosi_pad.i,
i_flash_io1_di = miso_pad.i,
i_flash_io2_di = wp_pad.i,
i_flash_io3_di = hold_pad.i,
i_resetn = ~reset,
i_clk = ClockSignal(),
i_valid = bus.stb & bus.cyc,
o_ready = spi_ready,
i_addr = flash_addr,
o_rdata = o_rdata,
i_cfgreg_we = cfg_we,
i_cfgreg_di = cfg,
o_cfgreg_do = cfg_out,
)
platform.add_source("rtl/spimemio.v")
class Messible(Module, AutoCSR, AutoDoc):
"""Messaging-style Ansible"""
def __init__(self):
self.submodules.fifo = f = fifo.SyncFIFOBuffered(width=8, depth=64)
in_reg = CSRStorage(8, name="in", description="""
Write half of the FIFO to send data out the Messible.
Writing to this register advances the write pointer automatically.""")
out_reg = CSRStatus(8, name="out", description="""
Read half of the FIFO to receive data on the Messible.
Reading from this register advances the read pointer automatically.""")
self.__setattr__("in", in_reg)
self.__setattr__("out", out_reg)
self.status = status = CSRStatus(fields=[
CSRField("full", description="``0`` if more data can fit into the IN FIFO."),
CSRField("have", description="``1`` if data can be read from the OUT FIFO."),
])
self.intro = ModuleDoc("""
Messible: An Ansible for Messages
An Ansible is a system for instant communication across vast distances, from
a small portable device to a huge terminal far away. A Messible is a message-
passing system from embedded devices to a host system. You can use it to get
very simple printf()-style support over a debug channel.
The Messible assumes the host has a way to peek into the device's memory space.
This is the case with the Wishbone bridge, which allows both the device and
the host to access the same memory.
At its core, a Messible is a FIFO. As long as the ``STATUS.FULL`` bit is ``0``, the
device can write data into the Messible by writing into the ``IN``. However, if this
value is ``1``, you need to decide if you want to wait for it to empty (if the other
side is just slow), or if you want to drop the message.
From the host side, you need to read ``STATUS.HAVE`` to see if there is data
in the FIFO. If there is, read ``OUT`` to get the most recent byte, which automatically
advances the ``READ`` pointer.
""")
self.comb += [
f.din.eq(in_reg.storage),
f.we.eq(in_reg.re),
out_reg.status.eq(f.dout),
f.re.eq(out_reg.we),
status.fields.full.eq(~f.writable),
status.fields.have.eq(f.readable),
]
class Version(Module, AutoCSR, AutoDoc):
def __init__(self, model, seed):
self.intro = ModuleDoc("""SoC Version Information
This block contains various information about the state of the source code
repository when this SoC was built.
""")
def makeint(i, base=10):
try:
return int(i, base=base)
except:
return 0
def get_gitver():
major = 0
minor = 0
rev = 0
gitrev = 0
gitextra = 0
dirty = 0
def decode_version(v):
version = v.split(".")
major = 0
minor = 0
rev = 0
if len(version) >= 3:
rev = makeint(version[2])
if len(version) >= 2:
minor = makeint(version[1])
if len(version) >= 1:
major = makeint(version[0])
return (major, minor, rev)
git_rev_cmd = subprocess.Popen(["git", "describe", "--tags", "--dirty=+"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(git_stdout, _) = git_rev_cmd.communicate()
if git_rev_cmd.wait() != 0:
print('unable to get git version')
return (major, minor, rev, gitrev, gitextra, dirty)
raw_git_rev = git_stdout.decode().strip()
parts = raw_git_rev.split("-")
if raw_git_rev[-1] == "+":
raw_git_rev = raw_git_rev[:-1]
dirty = 1
if len(parts) >= 3:
if parts[0].startswith("v"):
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
gitextra = makeint(parts[1])
if parts[2].startswith("g"):
gitrev = makeint(parts[2][1:], base=16)
elif len(parts) >= 2:
if parts[1].startswith("g"):
gitrev = makeint(parts[1][1:], base=16)
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
elif len(parts) >= 1:
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
return (major, minor, rev, gitrev, gitextra, dirty)
model_val = 0x3f # '?'
if model == "evt":
model_val = 0x45 # 'E'
elif model == "dvt":
model_val = 0x44 # 'D'
elif model == "pvt":
model_val = 0x50 # 'P'
elif model == "hacker":
model_val = 0x48 # 'H'
(major, minor, rev, gitrev, gitextra, dirty) = get_gitver()
self.major = CSRStatus(8, reset=major, description="Major git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, major))
self.minor = CSRStatus(8, reset=minor, description="Minor git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, minor))
self.revision = CSRStatus(8, reset=rev, description="Revision git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, rev))
self.gitrev = CSRStatus(32, reset=gitrev, description="First 32-bits of the git revision. This documentation was built from git rev ``{:08x}``, so this value is {}, which should be enough to check out the exact git version used to build this firmware.".format(gitrev, gitrev))
self.gitextra = CSRStatus(10, reset=gitextra, description="The number of additional commits beyond the git tag. For example, if this value is ``1``, then the repository this was built from has one additional commit beyond the tag indicated in `MAJOR`, `MINOR`, and `REVISION`.")
self.dirty = CSRStatus(fields=[
CSRField("dirty", reset=dirty, description="Set to ``1`` if this device was built from a git repo with uncommitted modifications.")
])
self.model = CSRStatus(fields=[
CSRField("model", reset=model_val, size=8, description="Contains information on which model device this was built for.", values=[
("0x45", "E", "Fomu EVT"),
("0x44", "D", "Fomu DVT"),
("0x50", "P", "Fomu PVT (production)"),
("0x48", "H", "Fomu Hacker"),
("0x3f", "?", "Unknown model"),
])
])
self.seed = CSRStatus(32, reset=seed, description="32-bit seed used for the place-and-route.")
self.comb += [
self.major.status.eq(major),
self.minor.status.eq(minor),
self.revision.status.eq(rev),
self.gitrev.status.eq(gitrev),
self.gitextra.status.eq(gitextra),
self.dirty.fields.dirty.eq(dirty),
self.model.fields.model.eq(model_val),
self.seed.status.eq(seed),
]
class BaseSoC(SoCCore):
SoCCore.csr_map = {
@ -742,7 +204,13 @@ class BaseSoC(SoCCore):
)
self.submodules.rgb = SBLED(platform.revision, platform.request("rgb_led"))
self.submodules.version = Version(platform.revision, pnr_seed)
self.submodules.version = Version(platform.revision, pnr_seed, models=[
("0x45", "E", "Fomu EVT"),
("0x44", "D", "Fomu DVT"),
("0x50", "P", "Fomu PVT (production)"),
("0x48", "H", "Fomu Hacker"),
("0x3f", "?", "Unknown model"),
])
# Add USB pads
usb_pads = platform.request("usb")

51
hw/rtl/fomutouch.py Normal file
View File

@ -0,0 +1,51 @@
from migen import Module, TSTriple, Cat
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
from litex.soc.integration.doc import ModuleDoc
from litex.build.generic_platform import Pins, Subsignal
class TouchPads(Module, AutoCSR):
touch_device = [
("touch_pads", 0,
Subsignal("t1", Pins("touch_pins:0")),
Subsignal("t2", Pins("touch_pins:1")),
Subsignal("t3", Pins("touch_pins:2")),
Subsignal("t4", Pins("touch_pins:3")),
)
]
def __init__(self, pads):
self.intro = ModuleDoc("""Fomu Touchpads
Fomu has four single-ended exposed pads on its side. These pads are designed
to be connected to some captouch block, or driven in a resistive touch mode
in order to get simple touchpad support.
This block simply provides CPU-controlled GPIO support for this block. It has
three registers which control the In, Out, and Output Enable functionality of
each of these pins.
""")
touch1 = TSTriple()
touch2 = TSTriple()
touch3 = TSTriple()
touch4 = TSTriple()
self.specials += touch1.get_tristate(pads.t1)
self.specials += touch2.get_tristate(pads.t2)
self.specials += touch3.get_tristate(pads.t3)
self.specials += touch4.get_tristate(pads.t4)
self.o = CSRStorage(4, description="Output values for pads 1-4")
self.oe = CSRStorage(4, description="Output enable control for pads 1-4")
self.i = CSRStatus(4, description="Input value for pads 1-4")
self.comb += [
touch1.o.eq(self.o.storage[0]),
touch2.o.eq(self.o.storage[1]),
touch3.o.eq(self.o.storage[2]),
touch4.o.eq(self.o.storage[3]),
touch1.oe.eq(self.oe.storage[0]),
touch2.oe.eq(self.oe.storage[1]),
touch3.oe.eq(self.oe.storage[2]),
touch4.oe.eq(self.oe.storage[3]),
self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i))
]

53
hw/rtl/messible.py Normal file
View File

@ -0,0 +1,53 @@
from migen import Module
from migen.genlib import fifo
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
class Messible(Module, AutoCSR, AutoDoc):
"""Messaging-style Ansible"""
def __init__(self):
self.submodules.fifo = f = fifo.SyncFIFOBuffered(width=8, depth=64)
in_reg = CSRStorage(8, name="in", description="""
Write half of the FIFO to send data out the Messible.
Writing to this register advances the write pointer automatically.""")
out_reg = CSRStatus(8, name="out", description="""
Read half of the FIFO to receive data on the Messible.
Reading from this register advances the read pointer automatically.""")
self.__setattr__("in", in_reg)
self.__setattr__("out", out_reg)
self.status = status = CSRStatus(fields=[
CSRField("full", description="``0`` if more data can fit into the IN FIFO."),
CSRField("have", description="``1`` if data can be read from the OUT FIFO."),
])
self.intro = ModuleDoc("""
Messible: An Ansible for Messages
An Ansible is a system for instant communication across vast distances, from
a small portable device to a huge terminal far away. A Messible is a message-
passing system from embedded devices to a host system. You can use it to get
very simple printf()-style support over a debug channel.
The Messible assumes the host has a way to peek into the device's memory space.
This is the case with the Wishbone bridge, which allows both the device and
the host to access the same memory.
At its core, a Messible is a FIFO. As long as the ``STATUS.FULL`` bit is ``0``, the
device can write data into the Messible by writing into the ``IN``. However, if this
value is ``1``, you need to decide if you want to wait for it to empty (if the other
side is just slow), or if you want to drop the message.
From the host side, you need to read ``STATUS.HAVE`` to see if there is data
in the FIFO. If there is, read ``OUT`` to get the most recent byte, which automatically
advances the ``READ`` pointer.
""")
self.comb += [
f.din.eq(in_reg.storage),
f.we.eq(in_reg.re),
out_reg.status.eq(f.dout),
f.re.eq(out_reg.we),
status.fields.full.eq(~f.writable),
status.fields.have.eq(f.readable),
]

126
hw/rtl/picorvspi.py Normal file
View File

@ -0,0 +1,126 @@
from migen import Module, Signal, Cat, TSTriple, bits_for, ResetSignal, If, ClockSignal, Instance
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
from litex.soc.interconnect import wishbone
class PicoRVSpi(Module, AutoCSR):
def __init__(self, platform, pads, size=2*1024*1024):
self.size = size
self.bus = bus = wishbone.Interface()
self.reset = Signal()
self.cfg1 = CSRStorage(fields=[
CSRField("bb_out", size=4, description="Output bits in bit-bang mode"),
CSRField("bb_clk", description="Serial clock line in bit-bang mode"),
CSRField("bb_cs", description="Chip select line in bit-bang mode"),
])
self.cfg2 = CSRStorage(fields=[
CSRField("bb_oe", size=4, description="Output Enable bits in bit-bang mode"),
])
self.cfg3 = CSRStorage(fields=[
CSRField("rlat", size=4, description="Read latency/dummy cycle count"),
CSRField("crm", description="Continuous Read Mode enable bit"),
CSRField("qspi", description="Quad-SPI enable bit"),
CSRField("ddr", description="Double Data Rate enable bit"),
])
self.cfg4 = CSRStorage(fields=[
CSRField("memio", offset=7, reset=1, description="Enable memory-mapped mode (set to 0 to enable bit-bang mode)")
])
self.stat1 = CSRStatus(fields=[
CSRField("bb_in", size=4, description="Input bits in bit-bang mode"),
])
self.stat2 = CSRStatus(1, fields=[], description="Reserved")
self.stat3 = CSRStatus(1, fields=[], description="Reserved")
self.stat4 = CSRStatus(1, fields=[], description="Reserved")
cfg = Signal(32)
cfg_we = Signal(4)
cfg_out = Signal(32)
self.comb += [
cfg.eq(Cat(self.cfg1.storage, self.cfg2.storage, self.cfg3.storage, self.cfg4.storage)),
cfg_we.eq(Cat(self.cfg1.re, self.cfg2.re, self.cfg3.re, self.cfg4.re)),
self.stat1.status.eq(cfg_out[0:8]),
self.stat2.status.eq(cfg_out[8:16]),
self.stat3.status.eq(cfg_out[16:24]),
self.stat4.status.eq(cfg_out[24:32]),
]
mosi_pad = TSTriple()
miso_pad = TSTriple()
cs_n_pad = TSTriple()
clk_pad = TSTriple()
wp_pad = TSTriple()
hold_pad = TSTriple()
self.specials += mosi_pad.get_tristate(pads.mosi)
self.specials += miso_pad.get_tristate(pads.miso)
self.specials += cs_n_pad.get_tristate(pads.cs_n)
self.specials += clk_pad.get_tristate(pads.clk)
self.specials += wp_pad.get_tristate(pads.wp)
self.specials += hold_pad.get_tristate(pads.hold)
reset = Signal()
self.comb += [
reset.eq(ResetSignal() | self.reset),
cs_n_pad.oe.eq(~reset),
clk_pad.oe.eq(~reset),
]
flash_addr = Signal(24)
# size/4 because data bus is 32 bits wide, -1 for base 0
mem_bits = bits_for(int(size/4)-1)
pad = Signal(2)
self.comb += flash_addr.eq(Cat(pad, bus.adr[0:mem_bits-1]))
read_active = Signal()
spi_ready = Signal()
self.sync += [
If(bus.stb & bus.cyc & ~read_active,
read_active.eq(1),
bus.ack.eq(0),
)
.Elif(read_active & spi_ready,
read_active.eq(0),
bus.ack.eq(1),
)
.Else(
bus.ack.eq(0),
read_active.eq(0),
)
]
o_rdata = Signal(32)
self.comb += bus.dat_r.eq(o_rdata)
self.specials += Instance("spimemio",
o_flash_io0_oe = mosi_pad.oe,
o_flash_io1_oe = miso_pad.oe,
o_flash_io2_oe = wp_pad.oe,
o_flash_io3_oe = hold_pad.oe,
o_flash_io0_do = mosi_pad.o,
o_flash_io1_do = miso_pad.o,
o_flash_io2_do = wp_pad.o,
o_flash_io3_do = hold_pad.o,
o_flash_csb = cs_n_pad.o,
o_flash_clk = clk_pad.o,
i_flash_io0_di = mosi_pad.i,
i_flash_io1_di = miso_pad.i,
i_flash_io2_di = wp_pad.i,
i_flash_io3_di = hold_pad.i,
i_resetn = ~reset,
i_clk = ClockSignal(),
i_valid = bus.stb & bus.cyc,
o_ready = spi_ready,
i_addr = flash_addr,
o_rdata = o_rdata,
i_cfgreg_we = cfg_we,
i_cfgreg_di = cfg,
o_cfgreg_do = cfg_out,
)
platform.add_source("rtl/spimemio.v")

43
hw/rtl/romgen.py Normal file
View File

@ -0,0 +1,43 @@
from litex.soc.interconnect import wishbone
class RandomFirmwareROM(wishbone.SRAM):
"""
Seed the random data with a fixed number, so different bitstreams
can all share firmware.
"""
def __init__(self, size, seed=2373):
def xorshift32(x):
x = x ^ (x << 13) & 0xffffffff
x = x ^ (x >> 17) & 0xffffffff
x = x ^ (x << 5) & 0xffffffff
return x & 0xffffffff
def get_rand(x):
out = 0
for i in range(32):
x = xorshift32(x)
if (x & 1) == 1:
out = out | (1 << i)
return out & 0xffffffff
data = []
seed = 1
for d in range(int(size / 4)):
seed = get_rand(seed)
data.append(seed)
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
class FirmwareROM(wishbone.SRAM):
def __init__(self, size, filename):
data = []
with open(filename, 'rb') as inp:
data = inp.read()
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
class JumpToAddressROM(wishbone.SRAM):
def __init__(self, size, addr):
data = [
0x00000537 | ((addr & 0xfffff000) << 0 ), # lui a0,%hi(addr)
0x00052503 | ((addr & 0x00000fff) << 20), # lw a0,%lo(addr)(a0)
0x000500e7, # jalr a0
]
wishbone.SRAM.__init__(self, size, read_only=True, init=data)

110
hw/rtl/sbled.py Normal file
View File

@ -0,0 +1,110 @@
from migen import Module, Signal, If, Instance, ClockSignal
from litex.soc.integration.doc import ModuleDoc
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
class SBLED(Module, AutoCSR):
def __init__(self, revision, pads):
rgba_pwm = Signal(3)
self.intro = ModuleDoc("""RGB LED Controller
The ICE40 contains two different RGB LED control devices. The first is a
constant-current LED source, which is fixed to deliver 4 mA to each of the
three LEDs. This block is called ``SB_RGBA_DRV``.
The other is used for creating interesting fading effects, particularly
for "breathing" effects used to indicate a given state. This block is called
``SB_LEDDA_IP``. This block feeds directly into ``SB_RGBA_DRV``.
The RGB LED controller available on this device allows for control of these
two LED control devices. Additionally, it is possible to disable ``SB_LEDDA_IP``
and directly control the individual LEDs.
""")
self.dat = CSRStorage(8, description="""
This is the value for the ``SB_LEDDA_IP.DAT`` register. It is directly
written into the ``SB_LEDDA_IP`` hardware block, so you should
refer to http://www.latticesemi.com/view_document?document_id=50668.
The contents of this register are written to the address specified in
``ADDR`` immediately upon writing this register.""")
self.addr = CSRStorage(4, description="""
This register is directly connected to ``SB_LEDDA_IP.ADDR``. This
register controls the address that is updated whenever ``DAT`` is
written. Writing to this register has no immediate effect -- data
isn't written until the ``DAT`` register is written.""")
self.ctrl = CSRStorage(fields=[
CSRField("exe", description="Connected to ``SB_LEDDA_IP.LEDDEXE``. Set this to ``1`` to enable the fading pattern."),
CSRField("curren", description="Connected to ``SB_RGBA_DRV.CURREN``. Set this to ``1`` to enable the current source."),
CSRField("rgbleden", description="Connected to ``SB_RGBA_DRV.RGBLEDEN``. Set this to ``1`` to enable the RGB PWM control logic."),
CSRField("rraw", description="Set this to ``1`` to enable raw control of the red LED via the ``RAW.R`` register."),
CSRField("graw", description="Set this to ``1`` to enable raw control of the green LED via the ``RAW.G`` register."),
CSRField("braw", description="Set this to ``1`` to enable raw control of the blue LED via the ``RAW.B`` register."),
], description="Control logic for the RGB LED and LEDDA hardware PWM LED block.")
self.raw = CSRStorage(fields=[
CSRField("r", description="Raw value for the red LED when ``CTRL.RRAW`` is ``1``."),
CSRField("g", description="Raw value for the green LED when ``CTRL.GRAW`` is ``1``."),
CSRField("b", description="Raw value for the blue LED when ``CTRL.BRAW`` is ``1``."),
], description="""
Normally the hardware ``SB_LEDDA_IP`` block controls the brightness of the LED,
creating a gentle fading pattern. However, by setting the appropriate bit in ``CTRL``,
it is possible to manually control the three individual LEDs.""")
ledd_value = Signal(3)
if revision == "pvt" or revision == "evt" or revision == "dvt":
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[1].eq(self.raw.storage[0])).Else(rgba_pwm[1].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[0].eq(self.raw.storage[1])).Else(rgba_pwm[0].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[2].eq(self.raw.storage[2])).Else(rgba_pwm[2].eq(ledd_value[2])),
]
elif revision == "hacker":
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[2].eq(self.raw.storage[0])).Else(rgba_pwm[2].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[1].eq(self.raw.storage[1])).Else(rgba_pwm[1].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[0].eq(self.raw.storage[2])).Else(rgba_pwm[0].eq(ledd_value[2])),
]
else:
self.comb += [
If(self.ctrl.storage[3], rgba_pwm[0].eq(self.raw.storage[0])).Else(rgba_pwm[0].eq(ledd_value[0])),
If(self.ctrl.storage[4], rgba_pwm[1].eq(self.raw.storage[1])).Else(rgba_pwm[1].eq(ledd_value[1])),
If(self.ctrl.storage[5], rgba_pwm[2].eq(self.raw.storage[2])).Else(rgba_pwm[2].eq(ledd_value[2])),
]
self.specials += Instance("SB_RGBA_DRV",
i_CURREN = self.ctrl.storage[1],
i_RGBLEDEN = self.ctrl.storage[2],
i_RGB0PWM = rgba_pwm[0],
i_RGB1PWM = rgba_pwm[1],
i_RGB2PWM = rgba_pwm[2],
o_RGB0 = pads.r,
o_RGB1 = pads.g,
o_RGB2 = pads.b,
p_CURRENT_MODE = "0b1",
p_RGB0_CURRENT = "0b000011",
p_RGB1_CURRENT = "0b000011",
p_RGB2_CURRENT = "0b000011",
)
self.specials += Instance("SB_LEDDA_IP",
i_LEDDCS = self.dat.re,
i_LEDDCLK = ClockSignal(),
i_LEDDDAT7 = self.dat.storage[7],
i_LEDDDAT6 = self.dat.storage[6],
i_LEDDDAT5 = self.dat.storage[5],
i_LEDDDAT4 = self.dat.storage[4],
i_LEDDDAT3 = self.dat.storage[3],
i_LEDDDAT2 = self.dat.storage[2],
i_LEDDDAT1 = self.dat.storage[1],
i_LEDDDAT0 = self.dat.storage[0],
i_LEDDADDR3 = self.addr.storage[3],
i_LEDDADDR2 = self.addr.storage[2],
i_LEDDADDR1 = self.addr.storage[1],
i_LEDDADDR0 = self.addr.storage[0],
i_LEDDDEN = self.dat.re,
i_LEDDEXE = self.ctrl.storage[0],
# o_LEDDON = led_is_on, # Indicates whether LED is on or not
# i_LEDDRST = ResetSignal(), # This port doesn't actually exist
o_PWMOUT0 = ledd_value[0],
o_PWMOUT1 = ledd_value[1],
o_PWMOUT2 = ledd_value[2],
o_LEDDON = Signal(),
)

57
hw/rtl/sbwarmboot.py Normal file
View File

@ -0,0 +1,57 @@
from migen import Module, Signal, If, Instance
from litex.soc.integration.doc import ModuleDoc
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
import lxsocdoc
class SBWarmBoot(Module, AutoCSR):
def __init__(self, parent, offsets=None):
table = ""
if offsets is not None:
arr = [["Image", "Offset"]]
for i,offset in enumerate(offsets):
arr.append([str(i), str(offset)])
table = "\nYou can use this block to reboot into one of these four addresses:\n\n" \
+ lxsocdoc.rst.make_table(arr)
self.intro = ModuleDoc("""FPGA Reboot Interface
This module provides the ability to reboot the FPGA. It is based on the
``SB_WARMBOOT`` primitive built in to the FPGA.
When power is applied to the FPGA, it reads configuration data from the
onboard flash chip. This contains reboot offsets for four images. It then
booted from the first image, but kept note of the other addresses.
{}""".format(table))
self.ctrl = CSRStorage(fields=[
CSRField("image", size=2, description="""
Which image to reboot to. ``SB_WARMBOOT`` supports four images that
are configured at FPGA startup. The bootloader is image 0, so set
these bits to 0 to reboot back into the bootloader.
"""),
CSRField("key", size=6, description="""
A reboot key used to prevent accidental reboots when writing to random
areas of memory. To initiate a reboot, set this to ``0b101011``.""")
], description="""
Provides support for rebooting the FPGA. You can select which of the four images
to reboot to, just be sure to OR the image number with ``0xac``. For example,
to reboot to the bootloader (image 0), write ``0xac``` to this register."""
)
self.addr = CSRStorage(size=32, description="""
This sets the reset vector for the VexRiscv. This address will be used whenever
the CPU is reset, for example through a debug bridge. You should update this
address whenever you load a new program, to enable the debugger to run ``mon reset``
"""
)
do_reset = Signal()
self.comb += [
# "Reset Key" is 0xac (0b101011xx)
do_reset.eq(self.ctrl.storage[2] & self.ctrl.storage[3] & ~self.ctrl.storage[4]
& self.ctrl.storage[5] & ~self.ctrl.storage[6] & self.ctrl.storage[7])
]
self.specials += Instance("SB_WARMBOOT",
i_S0 = self.ctrl.storage[0],
i_S1 = self.ctrl.storage[1],
i_BOOT = do_reset,
)
parent.config["BITSTREAM_SYNC_HEADER1"] = 0x7e99aa7e
parent.config["BITSTREAM_SYNC_HEADER2"] = 0x7eaa997e

112
hw/rtl/version.py Normal file
View File

@ -0,0 +1,112 @@
import subprocess
from migen import Module
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
class Version(Module, AutoCSR, AutoDoc):
def __init__(self, model, seed=0, models=[]):
self.intro = ModuleDoc("""SoC Version Information
This block contains various information about the state of the source code
repository when this SoC was built.
""")
def makeint(i, base=10):
try:
return int(i, base=base)
except:
return 0
def get_gitver():
major = 0
minor = 0
rev = 0
gitrev = 0
gitextra = 0
dirty = 0
def decode_version(v):
version = v.split(".")
major = 0
minor = 0
rev = 0
if len(version) >= 3:
rev = makeint(version[2])
if len(version) >= 2:
minor = makeint(version[1])
if len(version) >= 1:
major = makeint(version[0])
return (major, minor, rev)
git_rev_cmd = subprocess.Popen(["git", "describe", "--tags", "--dirty=+"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(git_stdout, _) = git_rev_cmd.communicate()
if git_rev_cmd.wait() != 0:
print('unable to get git version')
return (major, minor, rev, gitrev, gitextra, dirty)
raw_git_rev = git_stdout.decode().strip()
parts = raw_git_rev.split("-")
if raw_git_rev[-1] == "+":
raw_git_rev = raw_git_rev[:-1]
dirty = 1
if len(parts) >= 3:
if parts[0].startswith("v"):
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
gitextra = makeint(parts[1])
if parts[2].startswith("g"):
gitrev = makeint(parts[2][1:], base=16)
elif len(parts) >= 2:
if parts[1].startswith("g"):
gitrev = makeint(parts[1][1:], base=16)
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
elif len(parts) >= 1:
version = parts[0]
if version.startswith("v"):
version = parts[0][1:]
(major, minor, rev) = decode_version(version)
return (major, minor, rev, gitrev, gitextra, dirty)
model_val = 0x3f # '?'
if model == "evt":
model_val = 0x45 # 'E'
elif model == "dvt":
model_val = 0x44 # 'D'
elif model == "pvt":
model_val = 0x50 # 'P'
elif model == "hacker":
model_val = 0x48 # 'H'
(major, minor, rev, gitrev, gitextra, dirty) = get_gitver()
self.major = CSRStatus(8, reset=major, description="Major git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, major))
self.minor = CSRStatus(8, reset=minor, description="Minor git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, minor))
self.revision = CSRStatus(8, reset=rev, description="Revision git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``.".format(major, minor, rev, rev))
self.gitrev = CSRStatus(32, reset=gitrev, description="First 32-bits of the git revision. This documentation was built from git rev ``{:08x}``, so this value is {}, which should be enough to check out the exact git version used to build this firmware.".format(gitrev, gitrev))
self.gitextra = CSRStatus(10, reset=gitextra, description="The number of additional commits beyond the git tag. For example, if this value is ``1``, then the repository this was built from has one additional commit beyond the tag indicated in `MAJOR`, `MINOR`, and `REVISION`.")
self.dirty = CSRStatus(fields=[
CSRField("dirty", reset=dirty, description="Set to ``1`` if this device was built from a git repo with uncommitted modifications.")
])
self.model = CSRStatus(fields=[
CSRField("model", reset=model_val, size=8, description="Contains information on which model device this was built for.", values=models)
])
self.seed = CSRStatus(32, reset=seed, description="32-bit seed used for the place-and-route.")
self.comb += [
self.major.status.eq(major),
self.minor.status.eq(minor),
self.revision.status.eq(rev),
self.gitrev.status.eq(gitrev),
self.gitextra.status.eq(gitextra),
self.dirty.fields.dirty.eq(dirty),
self.model.fields.model.eq(model_val),
self.seed.status.eq(seed),
]