mirror of
https://github.com/im-tomu/foboot.git
synced 2024-09-20 02:40:09 +00:00
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:
parent
c8e632dd7e
commit
45e3b5b617
@ -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
51
hw/rtl/fomutouch.py
Normal 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
53
hw/rtl/messible.py
Normal 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
126
hw/rtl/picorvspi.py
Normal 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
43
hw/rtl/romgen.py
Normal 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
110
hw/rtl/sbled.py
Normal 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
57
hw/rtl/sbwarmboot.py
Normal 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
112
hw/rtl/version.py
Normal 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),
|
||||
]
|
Loading…
Reference in New Issue
Block a user