From dd14bb4d29008a0669f9ef900c1ccbfbf82444fb Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Wed, 22 May 2019 11:33:32 +0800 Subject: [PATCH] initial commit Signed-off-by: Sean Cross --- .gitmodules | 21 + bin/litex_read_verilog | 14 + bin/litex_server | 14 + bin/litex_sim | 14 + bin/litex_simple | 14 + bin/litex_term | 14 + bin/mkmscimg | 14 + deps/litedram | 1 + deps/litescope | 1 + deps/litex | 1 + deps/lxsocsupport | 1 + deps/migen | 1 + deps/pyserial | 1 + deps/valentyusb | 1 + factory-bitstream.py | 903 +++++++++++++++++++++++++++++++++++++++++ lxbuildenv.py | 2 +- 16 files changed, 1016 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100755 bin/litex_read_verilog create mode 100755 bin/litex_server create mode 100755 bin/litex_sim create mode 100755 bin/litex_simple create mode 100755 bin/litex_term create mode 100755 bin/mkmscimg create mode 160000 deps/litedram create mode 160000 deps/litescope create mode 160000 deps/litex create mode 160000 deps/lxsocsupport create mode 160000 deps/migen create mode 160000 deps/pyserial create mode 160000 deps/valentyusb create mode 100644 factory-bitstream.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a17a6e4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "deps/migen"] + path = deps/migen + url = https://github.com/m-labs/migen.git +[submodule "deps/litex"] + path = deps/litex + url = https://github.com/enjoy-digital/litex.git +[submodule "deps/litescope"] + path = deps/litescope + url = https://github.com/enjoy-digital/litescope.git +[submodule "deps/pyserial"] + path = deps/pyserial + url = https://github.com/pyserial/pyserial.git +[submodule "deps/litedram"] + path = deps/litedram + url = https://github.com/enjoy-digital/litedram.git +[submodule "deps/valentyusb"] + path = deps/valentyusb + url = https://github.com/im-tomu/valentyusb.git +[submodule "deps/lxsocsupport"] + path = deps/lxsocsupport + url = https://github.com/xobs/lxsocsupport.git diff --git a/bin/litex_read_verilog b/bin/litex_read_verilog new file mode 100755 index 0000000..fb8d178 --- /dev/null +++ b/bin/litex_read_verilog @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.tools.litex_read_verilog import main +main() diff --git a/bin/litex_server b/bin/litex_server new file mode 100755 index 0000000..9523fb5 --- /dev/null +++ b/bin/litex_server @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.tools.litex_server import main +main() diff --git a/bin/litex_sim b/bin/litex_sim new file mode 100755 index 0000000..4964d63 --- /dev/null +++ b/bin/litex_sim @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.tools.litex_sim import main +main() diff --git a/bin/litex_simple b/bin/litex_simple new file mode 100755 index 0000000..eea9a6a --- /dev/null +++ b/bin/litex_simple @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.boards.targets.simple import main +main() diff --git a/bin/litex_term b/bin/litex_term new file mode 100755 index 0000000..0e2590c --- /dev/null +++ b/bin/litex_term @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.tools.litex_term import main +main() diff --git a/bin/mkmscimg b/bin/mkmscimg new file mode 100755 index 0000000..456673c --- /dev/null +++ b/bin/mkmscimg @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys +import os + +# This script lives in the "bin" directory, but uses a helper script in the parent +# directory. Obtain the current path so we can get the absolute parent path. +script_path = os.path.dirname(os.path.realpath( + __file__)) + os.path.sep + os.path.pardir + os.path.sep +sys.path.insert(0, script_path) +import lxbuildenv + +from litex.soc.software.mkmscimg import main +main() diff --git a/deps/litedram b/deps/litedram new file mode 160000 index 0000000..da68e21 --- /dev/null +++ b/deps/litedram @@ -0,0 +1 @@ +Subproject commit da68e21badfb7c6b408d6857b41ba96d882c865d diff --git a/deps/litescope b/deps/litescope new file mode 160000 index 0000000..7f20aa4 --- /dev/null +++ b/deps/litescope @@ -0,0 +1 @@ +Subproject commit 7f20aa477fcf55aa2975097aa0025e72d5b60d10 diff --git a/deps/litex b/deps/litex new file mode 160000 index 0000000..3a72688 --- /dev/null +++ b/deps/litex @@ -0,0 +1 @@ +Subproject commit 3a72688b28cea67a34b53bf4bf3133d2b16e1f2a diff --git a/deps/lxsocsupport b/deps/lxsocsupport new file mode 160000 index 0000000..74f1333 --- /dev/null +++ b/deps/lxsocsupport @@ -0,0 +1 @@ +Subproject commit 74f133340144630e000f91e466ac5107f87a8d5a diff --git a/deps/migen b/deps/migen new file mode 160000 index 0000000..bc90344 --- /dev/null +++ b/deps/migen @@ -0,0 +1 @@ +Subproject commit bc903441a5a27cb36b6961c29529ff98cd94f1ec diff --git a/deps/pyserial b/deps/pyserial new file mode 160000 index 0000000..acab9d2 --- /dev/null +++ b/deps/pyserial @@ -0,0 +1 @@ +Subproject commit acab9d2c0efb63323faebfd5e3405d77cd4b5617 diff --git a/deps/valentyusb b/deps/valentyusb new file mode 160000 index 0000000..3052a16 --- /dev/null +++ b/deps/valentyusb @@ -0,0 +1 @@ +Subproject commit 3052a16d9f18360f407ce7a4d0cbd82e24cb4768 diff --git a/factory-bitstream.py b/factory-bitstream.py new file mode 100644 index 0000000..1fe9e2d --- /dev/null +++ b/factory-bitstream.py @@ -0,0 +1,903 @@ +#!/usr/bin/env python3 +# This variable defines all the external programs that this module +# relies on. lxbuildenv reads this variable in order to ensure +# the build will finish without exiting due to missing third-party +# programs. +LX_DEPENDENCIES = ["riscv", "icestorm", "yosys"] + +# Import lxbuildenv to integrate the deps/ directory +import lxbuildenv + +# Disable pylint's E1101, which breaks completely on migen +#pylint:disable=E1101 + +#from migen import * +from migen import Module, Signal, Instance, ClockDomain, If +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 litex.build.lattice.platform import LatticePlatform +from litex.build.generic_platform import Pins, IOStandard, Misc, Subsignal +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 + +from valentyusb import usbcore +from valentyusb.usbcore import io as usbio +from valentyusb.usbcore.cpu import epmem, unififo, epfifo +from valentyusb.usbcore.endpoint import EndpointType + +from lxsocsupport import up5kspram, spi_flash + +import argparse +import os + +_io_evt = [ + ("serial", 0, + Subsignal("rx", Pins("21")), + Subsignal("tx", Pins("13"), Misc("PULLUP")), + IOStandard("LVCMOS33") + ), + ("usb", 0, + Subsignal("d_p", Pins("34")), + Subsignal("d_n", Pins("37")), + Subsignal("pullup", Pins("35")), + Subsignal("pulldown", Pins("36")), + IOStandard("LVCMOS33") + ), + ("touch", 0, + Subsignal("t1", Pins("48"), IOStandard("LVCMOS33")), + Subsignal("t2", Pins("47"), IOStandard("LVCMOS33")), + Subsignal("t3", Pins("46"), IOStandard("LVCMOS33")), + Subsignal("t4", Pins("45"), IOStandard("LVCMOS33")), + ), + ("pmoda", 0, + Subsignal("p1", Pins("28"), IOStandard("LVCMOS33")), + Subsignal("p2", Pins("27"), IOStandard("LVCMOS33")), + Subsignal("p3", Pins("26"), IOStandard("LVCMOS33")), + Subsignal("p4", Pins("23"), IOStandard("LVCMOS33")), + ), + ("pmodb", 0, + Subsignal("p1", Pins("48"), IOStandard("LVCMOS33")), + Subsignal("p2", Pins("47"), IOStandard("LVCMOS33")), + Subsignal("p3", Pins("46"), IOStandard("LVCMOS33")), + Subsignal("p4", Pins("45"), IOStandard("LVCMOS33")), + ), + ("led", 0, + Subsignal("rgb0", Pins("39"), IOStandard("LVCMOS33")), + Subsignal("rgb1", Pins("40"), IOStandard("LVCMOS33")), + Subsignal("rgb2", Pins("41"), IOStandard("LVCMOS33")), + ), + ("spiflash", 0, + Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("15"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("17"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("14"), IOStandard("LVCMOS33")), + Subsignal("wp", Pins("18"), IOStandard("LVCMOS33")), + Subsignal("hold", Pins("19"), IOStandard("LVCMOS33")), + ), + ("spiflash4x", 0, + Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("15"), IOStandard("LVCMOS33")), + Subsignal("dq", Pins("14 17 19 18"), IOStandard("LVCMOS33")), + ), + ("clk48", 0, Pins("44"), IOStandard("LVCMOS33")) +] +_io_dvt = [ + ("serial", 0, + Subsignal("rx", Pins("C3")), + Subsignal("tx", Pins("B3"), Misc("PULLUP")), + IOStandard("LVCMOS33") + ), + ("usb", 0, + Subsignal("d_p", Pins("A1")), + Subsignal("d_n", Pins("A2")), + Subsignal("pullup", Pins("A4")), + IOStandard("LVCMOS33") + ), + ("touch", 0, + Subsignal("t1", Pins("E4"), IOStandard("LVCMOS33")), + Subsignal("t2", Pins("D5"), IOStandard("LVCMOS33")), + Subsignal("t3", Pins("E5"), IOStandard("LVCMOS33")), + Subsignal("t4", Pins("F5"), IOStandard("LVCMOS33")), + ), + ("led", 0, + Subsignal("rgb0", Pins("A5"), IOStandard("LVCMOS33")), + Subsignal("rgb1", Pins("B5"), IOStandard("LVCMOS33")), + Subsignal("rgb2", Pins("C5"), IOStandard("LVCMOS33")), + ), + ("spiflash", 0, + Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("E1"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("F1"), IOStandard("LVCMOS33")), + Subsignal("wp", Pins("F2"), IOStandard("LVCMOS33")), + Subsignal("hold", Pins("B1"), IOStandard("LVCMOS33")), + ), + ("spiflash4x", 0, + Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")), + Subsignal("dq", Pins("E1 F1 F2 B1"), IOStandard("LVCMOS33")), + ), + ("clk48", 0, Pins("F4"), IOStandard("LVCMOS33")) +] +_io_hacker = [ + ("serial", 0, + Subsignal("rx", Pins("C3")), + Subsignal("tx", Pins("B3"), Misc("PULLUP")), + IOStandard("LVCMOS33") + ), + ("usb", 0, + Subsignal("d_p", Pins("A4")), + Subsignal("d_n", Pins("A2")), + Subsignal("pullup", Pins("D5")), + IOStandard("LVCMOS33") + ), + ("touch", 0, + Subsignal("t1", Pins("F4"), IOStandard("LVCMOS33")), + Subsignal("t2", Pins("E5"), IOStandard("LVCMOS33")), + Subsignal("t3", Pins("E4"), IOStandard("LVCMOS33")), + Subsignal("t4", Pins("F2"), IOStandard("LVCMOS33")), + ), + ("led", 0, + Subsignal("rgb0", Pins("A5"), IOStandard("LVCMOS33")), + Subsignal("rgb1", Pins("B5"), IOStandard("LVCMOS33")), + Subsignal("rgb2", Pins("C5"), IOStandard("LVCMOS33")), + ), + ("spiflash", 0, + Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")), + Subsignal("miso", Pins("E1"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("F1"), IOStandard("LVCMOS33")), + Subsignal("wp", Pins("A1"), IOStandard("LVCMOS33")), + Subsignal("hold", Pins("B1"), IOStandard("LVCMOS33")), + ), + ("spiflash4x", 0, + Subsignal("cs_n", Pins("C1"), IOStandard("LVCMOS33")), + Subsignal("clk", Pins("D1"), IOStandard("LVCMOS33")), + Subsignal("dq", Pins("E1 F1 A1 B1"), IOStandard("LVCMOS33")), + ), + ("clk48", 0, Pins("F5"), IOStandard("LVCMOS33")) +] + +_connectors = [] + +class _CRG(Module): + def __init__(self, platform, use_pll): + clk48_raw = platform.request("clk48") + clk12_raw = Signal() + clk48 = Signal() + clk12 = Signal() + + reset_delay = Signal(13, reset=4095) + self.clock_domains.cd_por = ClockDomain() + self.reset = Signal() + + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_usb_12 = ClockDomain() + self.clock_domains.cd_usb_48 = ClockDomain() + + platform.add_period_constraint(self.cd_usb_48.clk, 1e9/48e6) + platform.add_period_constraint(self.cd_sys.clk, 1e9/12e6) + platform.add_period_constraint(self.cd_usb_12.clk, 1e9/12e6) + platform.add_period_constraint(clk48, 1e9/48e6) + platform.add_period_constraint(clk48_raw, 1e9/48e6) + platform.add_period_constraint(clk12_raw, 1e9/12e6) + + # POR reset logic- POR generated from sys clk, POR logic feeds sys clk + # reset. + self.comb += [ + self.cd_por.clk.eq(self.cd_sys.clk), + self.cd_sys.rst.eq(reset_delay != 0), + self.cd_usb_12.rst.eq(reset_delay != 0), + ] + + if use_pll: + + # Divide clk48 down to clk12, to ensure they're synchronized. + # By doing this, we avoid needing clock-domain crossing. + clk12_counter = Signal(2) + + self.clock_domains.cd_usb_48_raw = ClockDomain() + + platform.add_period_constraint(self.cd_usb_48_raw.clk, 1e9/48e6) + + # POR reset logic- POR generated from sys clk, POR logic feeds sys clk + # reset. + self.comb += [ + self.cd_usb_48.rst.eq(reset_delay != 0), + ] + + self.comb += self.cd_usb_48_raw.clk.eq(clk48_raw) + self.comb += self.cd_usb_48.clk.eq(clk48) + + self.sync.usb_48_raw += clk12_counter.eq(clk12_counter + 1) + + self.comb += clk12_raw.eq(clk12_counter[1]) + self.specials += Instance( + "SB_GB", + i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk12_raw, + o_GLOBAL_BUFFER_OUTPUT=clk12, + ) + + self.specials += Instance( + "SB_PLL40_CORE", + # Parameters + p_DIVR = 0, + p_DIVF = 3, + p_DIVQ = 2, + p_FILTER_RANGE = 1, + p_FEEDBACK_PATH = "PHASE_AND_DELAY", + p_DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED", + p_FDA_FEEDBACK = 15, + p_DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED", + p_FDA_RELATIVE = 0, + p_SHIFTREG_DIV_MODE = 1, + p_PLLOUT_SELECT = "SHIFTREG_0deg", + p_ENABLE_ICEGATE = 0, + # IO + i_REFERENCECLK = clk12, + # o_PLLOUTCORE = clk12, + o_PLLOUTGLOBAL = clk48, + #i_EXTFEEDBACK, + #i_DYNAMICDELAY, + #o_LOCK, + i_BYPASS = 0, + i_RESETB = 1, + #i_LATCHINPUTVALUE, + #o_SDO, + #i_SDI, + ) + else: + self.specials += Instance( + "SB_GB", + i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk48_raw, + o_GLOBAL_BUFFER_OUTPUT=clk48, + ) + self.comb += self.cd_usb_48.clk.eq(clk48) + + clk12_counter = Signal(2) + self.sync.usb_48 += clk12_counter.eq(clk12_counter + 1) + + self.comb += clk12_raw.eq(clk12_counter[1]) + self.specials += Instance( + "SB_GB", + i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk12_raw, + o_GLOBAL_BUFFER_OUTPUT=clk12, + ) + + self.comb += self.cd_sys.clk.eq(clk12) + self.comb += self.cd_usb_12.clk.eq(clk12) + + self.sync.por += \ + If(reset_delay != 0, + reset_delay.eq(reset_delay - 1) + ) + self.specials += AsyncResetSynchronizer(self.cd_por, self.reset) + +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 Platform(LatticePlatform): + def __init__(self, revision=None, toolchain="icestorm"): + if revision == "evt": + LatticePlatform.__init__(self, "ice40-up5k-sg48", _io_evt, _connectors, toolchain="icestorm") + elif revision == "dvt": + LatticePlatform.__init__(self, "ice40-up5k-uwg30", _io_dvt, _connectors, toolchain="icestorm") + elif revision == "hacker": + LatticePlatform.__init__(self, "ice40-up5k-uwg30", _io_hacker, _connectors, toolchain="icestorm") + else: + raise ValueError("Unrecognized reivsion: {}. Known values: evt, dvt, hacker".format(revision)) + + def create_programmer(self): + raise ValueError("programming is not supported") + +class SBLED(Module, AutoCSR): + def __init__(self, pads): + rgba_pwm = Signal(3) + + self.dat = CSRStorage(8) + self.addr = CSRStorage(4) + self.ctrl = CSRStorage(4) + + 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.rgb0, + o_RGB1 = pads.rgb1, + o_RGB2 = pads.rgb2, + p_CURRENT_MODE = "0b1", + p_RGB0_CURRENT = "0b000011", + p_RGB1_CURRENT = "0b000001", + 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 = rgba_pwm[0], + o_PWMOUT1 = rgba_pwm[1], + o_PWMOUT2 = rgba_pwm[2], + o_LEDDON = Signal(), + ) + + +class SBWarmBoot(Module, AutoCSR): + def __init__(self): + self.ctrl = CSRStorage(size=8) + self.addr = CSRStorage(size=32) + 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, + ) + +class TouchPads(Module, AutoCSR): + def __init__(self, pads): + 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(size=4) + self.oe = CSRStorage(size=4) + self.i = CSRStatus(size=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(size=8) + self.cfg2 = CSRStorage(size=8) + self.cfg3 = CSRStorage(size=8) + self.cfg4 = CSRStorage(size=8) + + self.stat1 = CSRStatus(size=8) + self.stat2 = CSRStatus(size=8) + self.stat3 = CSRStatus(size=8) + self.stat4 = CSRStatus(size=8) + + 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) + mem_bits = bits_for(size) + self.comb += flash_addr.eq(bus.adr[0:mem_bits-2] << 2), + + 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 BBSpi(Module, AutoCSR): + def __init__(self, platform, pads): + self.reset = Signal() + + mosi_pad = TSTriple() + miso_pad = TSTriple() + cs_n_pad = TSTriple() + clk_pad = TSTriple() + wp_pad = TSTriple() + hold_pad = TSTriple() + + self.do = CSRStorage(size=6) + self.oe = CSRStorage(size=6) + self.di = CSRStatus(size=6) + + 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) + + self.comb += [ + mosi_pad.oe.eq(self.oe.storage[0]), + miso_pad.oe.eq(self.oe.storage[1]), + wp_pad.oe.eq(self.oe.storage[2]), + hold_pad.oe.eq(self.oe.storage[3]), + clk_pad.oe.eq(self.oe.storage[4]), + cs_n_pad.oe.eq(self.oe.storage[5]), + + mosi_pad.o.eq(self.do.storage[0]), + miso_pad.o.eq(self.do.storage[1]), + wp_pad.o.eq(self.do.storage[2]), + hold_pad.o.eq(self.do.storage[3]), + clk_pad.o.eq(self.do.storage[4]), + cs_n_pad.o.eq(self.do.storage[5]), + + self.di.status.eq(Cat(mosi_pad.i, miso_pad.i, wp_pad.i, hold_pad.i, clk_pad.i, cs_n_pad.i)), + ] + +class Version(Module, AutoCSR): + def __init__(self): + def makeint(i, base=10): + try: + return int(i, base=base) + except: + return 0 + def get_gitver(): + import subprocess + 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 + raw_git_rev = git_stdout.decode().strip() + + dirty = False + if raw_git_rev[-1] == "+": + raw_git_rev = raw_git_rev[:-1] + dirty = True + + parts = raw_git_rev.split("-") + major = 0 + minor = 0 + rev = 0 + gitrev = 0 + gitextra = 0 + + 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) + + self.major = CSRStatus(8) + self.minor = CSRStatus(8) + self.revision = CSRStatus(8) + self.gitrev = CSRStatus(32) + self.gitextra = CSRStatus(10) + self.dirty = CSRStatus(1) + + (major, minor, rev, gitrev, gitextra, dirty) = get_gitver() + 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.status.eq(dirty), + ] + + +class BaseSoC(SoCCore): + SoCCore.csr_map = { + "ctrl": 0, # provided by default (optional) + "crg": 1, # user + "uart_phy": 2, # provided by default (optional) + "uart": 3, # provided by default (optional) + "identifier_mem": 4, # provided by default (optional) + "timer0": 5, # provided by default (optional) + "cpu_or_bridge": 8, + "usb": 9, + "picorvspi": 10, + "touch": 11, + "reboot": 12, + "rgb": 13, + "version": 14, + } + + mem_map = { + "spiflash": 0x20000000, # (default shadow @0xa0000000) + } + mem_map.update(SoCCore.mem_map) + + interrupt_map = { + "usb": 3, + } + interrupt_map.update(SoCCore.interrupt_map) + + def __init__(self, platform, boot_source="rand", + debug=None, bios_file=None, use_pll=True, + use_dsp=False, placer=None, output_dir="build", + **kwargs): + # Disable integrated RAM as we'll add it later + self.integrated_sram_size = 0 + + self.output_dir = output_dir + + clk_freq = int(12e6) + self.submodules.crg = _CRG(platform, use_pll=use_pll) + + SoCCore.__init__(self, platform, clk_freq, integrated_sram_size=0, with_uart=False, **kwargs) + + usb_debug = False + if debug is not None: + if debug == "uart": + from litex.soc.cores.uart import UARTWishboneBridge + self.submodules.uart_bridge = UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=115200) + self.add_wb_master(self.uart_bridge.wishbone) + elif debug == "usb": + usb_debug = True + if hasattr(self, "cpu"): + self.cpu.use_external_variant("rtl/2-stage-1024-cache-debug.v") + self.copy_memory_file("2-stage-1024-cache-debug.v_toplevel_RegFilePlugin_regFile.bin") + os.path.join(output_dir, "gateware") + self.register_mem("vexriscv_debug", 0xf00f0000, self.cpu.debug_bus, 0x10) + else: + if hasattr(self, "cpu"): + self.cpu.use_external_variant("rtl/2-stage-1024-cache.v") + self.copy_memory_file("2-stage-1024-cache.v_toplevel_RegFilePlugin_regFile.bin") + + # SPRAM- UP5K has single port RAM, might as well use it as SRAM to + # free up scarce block RAM. + spram_size = 128*1024 + self.submodules.spram = up5kspram.Up5kSPRAM(size=spram_size) + self.register_mem("sram", 0x10000000, self.spram.bus, spram_size) + + if boot_source == "rand": + kwargs['cpu_reset_address']=0 + bios_size = 0x2000 + self.submodules.random_rom = RandomFirmwareROM(bios_size) + self.add_constant("ROM_DISABLE", 1) + self.register_rom(self.random_rom.bus, bios_size) + elif boot_source == "bios": + kwargs['cpu_reset_address'] = 0 + if bios_file is None: + self.integrated_rom_size = bios_size = 0x2000 + self.submodules.rom = wishbone.SRAM(bios_size, read_only=True, init=[]) + self.register_rom(self.rom.bus, bios_size) + else: + bios_size = 0x2000 + self.submodules.firmware_rom = FirmwareROM(bios_size, bios_file) + self.add_constant("ROM_DISABLE", 1) + self.register_rom(self.firmware_rom.bus, bios_size) + + elif boot_source == "spi": + bios_size = 0x8000 + kwargs['cpu_reset_address']=self.mem_map["spiflash"]+platform.gateware_size + self.add_memory_region("rom", kwargs['cpu_reset_address'], bios_size) + self.add_constant("ROM_DISABLE", 1) + self.flash_boot_address = self.mem_map["spiflash"]+platform.gateware_size+bios_size + self.add_memory_region("user_flash", + self.flash_boot_address, + # Leave a grace area- possible one-by-off bug in add_memory_region? + # Possible fix: addr < origin + length - 1 + platform.spiflash_total_size - (self.flash_boot_address - self.mem_map["spiflash"]) - 0x100) + else: + raise ValueError("unrecognized boot_source: {}".format(boot_source)) + + # Add a simple bit-banged SPI Flash module + spi_pads = platform.request("spiflash") + self.submodules.picorvspi = PicoRVSpi(platform, spi_pads) + self.register_mem("spiflash", self.mem_map["spiflash"], + self.picorvspi.bus, size=self.picorvspi.size) + + self.submodules.reboot = SBWarmBoot() + self.cpu.cpu_params.update( + i_externalResetVector=self.reboot.addr.storage, + ) + + self.submodules.rgb = SBLED(platform.request("led")) + self.submodules.version = Version() + + # Add USB pads + usb_pads = platform.request("usb") + usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup) + self.submodules.usb = epfifo.PerEndpointFifoInterface(usb_iobuf, debug=usb_debug) + if usb_debug: + self.add_wb_master(self.usb.debug_bridge.wishbone) + # For the EVT board, ensure the pulldown pin is tristated as an input + if hasattr(usb_pads, "pulldown"): + pulldown = TSTriple() + self.specials += pulldown.get_tristate(usb_pads.pulldown) + self.comb += pulldown.oe.eq(0) + # self.submodules.usb = epmem.MemInterface(usb_iobuf) + # self.submodules.usb = unififo.UsbUniFifo(usb_iobuf) + + # Add GPIO pads for the touch buttons + self.submodules.touch = TouchPads(platform.request("touch")) + + # Add "-relut -dffe_min_ce_use 4" to the synth_ice40 command. + # The "-reult" adds an additional LUT pass to pack more stuff in, + # and the "-dffe_min_ce_use 4" flag prevents Yosys from generating a + # Clock Enable signal for a LUT that has fewer than 4 flip-flops. + # This increases density, and lets us use the FPGA more efficiently. + platform.toolchain.nextpnr_yosys_template[2] += " -relut -dffe_min_ce_use 5" + if use_dsp: + platform.toolchain.nextpnr_yosys_template[2] += " -dsp" + + # Disable final deep-sleep power down so firmware words are loaded + # onto softcore's address bus. + platform.toolchain.build_template[3] = "icepack -s {build_name}.txt {build_name}.bin" + platform.toolchain.nextpnr_build_template[2] = "icepack -s {build_name}.txt {build_name}.bin" + + if placer is not None: + platform.toolchain.nextpnr_build_template[1] += " --placer {}".format(placer) + + def copy_memory_file(self, src): + import os + from shutil import copyfile + if not os.path.exists(self.output_dir): + os.mkdir(self.output_dir) + if not os.path.exists(os.path.join(self.output_dir, "gateware")): + os.mkdir(os.path.join(self.output_dir, "gateware")) + copyfile(os.path.join("rtl", src), os.path.join(self.output_dir, "gateware", src)) + +def main(): + parser = argparse.ArgumentParser( + description="Build Fomu Main Gateware") + parser.add_argument( + "--boot-source", choices=["spi", "rand", "bios"], default="bios", + help="where to have the CPU obtain its executable code from" + ) + parser.add_argument( + "--revision", choices=["dvt", "evt", "hacker"], required=True, + help="build foboot for a particular hardware revision" + ) + parser.add_argument( + "--bios", help="use specified file as a BIOS, rather than building one" + ) + parser.add_argument( + "--with-debug", help="enable debug support", choices=["usb", "uart", None] + ) + parser.add_argument( + "--no-pll", help="disable pll -- this is easier to route, but may not work", action="store_true" + ) + parser.add_argument( + "--with-dsp", help="use dsp inference in yosys (not all yosys builds have -dsp)", action="store_true" + ) + parser.add_argument( + "--no-cpu", help="disable cpu generation for debugging purposes", action="store_true" + ) + parser.add_argument( + "--placer", choices=["sa", "heap"], help="which placer to use in nextpnr" + ) + parser.add_argument( + "--export-random-rom-file", help="Generate a random ROM file and save it to a file" + ) + args = parser.parse_args() + + output_dir = 'build' + + if args.export_random_rom_file is not None: + size = 0x2000 + 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 + seed = 1 + with open(args.export_random_rom_file, "w", newline="\n") as output: + for d in range(int(size / 4)): + seed = get_rand(seed) + print("{:08x}".format(seed), file=output) + return 0 + + compile_software = False + if args.boot_source == "bios" and args.bios is None: + compile_software = True + + cpu_type = "vexriscv" + cpu_variant = "min" + if args.with_debug: + cpu_variant = "debug" + + if args.no_cpu: + cpu_type = None + cpu_variant = None + + os.environ["LITEX"] = "1" # Give our Makefile something to look for + platform = Platform(revision=args.revision) + soc = BaseSoC(platform, cpu_type=cpu_type, cpu_variant=cpu_variant, + debug=args.with_debug, boot_source=args.boot_source, + bios_file=args.bios, use_pll=not args.no_pll, + use_dsp=args.with_dsp, placer=args.placer, + output_dir=output_dir) + builder = Builder(soc, output_dir=output_dir, csr_csv="test/csr.csv", compile_software=compile_software) + if compile_software: + builder.software_packages = [ + ("bios", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "sw"))) + ] + vns = builder.build() + soc.do_exit(vns) + + print( +"""Foboot build complete. Output files: + {}/gateware/top.bin Bitstream file. Load this onto the FPGA for testing. + {}/gateware/top.v Source Verilog file. Useful for debugging issues. + {}/software/include/generated/ Directory with header files for API access. + {}/software/bios/bios.elf ELF file for debugging bios. +""".format(output_dir, output_dir, output_dir, output_dir)) + +if __name__ == "__main__": + main() diff --git a/lxbuildenv.py b/lxbuildenv.py index 1b0b2b0..1726c1a 100644 --- a/lxbuildenv.py +++ b/lxbuildenv.py @@ -14,6 +14,7 @@ DEPS_DIR = "deps" DEFAULT_DEPS = { 'migen': 'https://github.com/m-labs/migen.git', 'litex': 'https://github.com/enjoy-digital/litex.git', + 'litedram': 'https://github.com/enjoy-digital/litedram.git', 'litescope': 'https://github.com/enjoy-digital/litescope.git', 'pyserial': 'https://github.com/pyserial/pyserial.git', } @@ -21,7 +22,6 @@ DEFAULT_DEPS = { OPTIONAL_DEPS = { 'liteeth': 'https://github.com/enjoy-digital/liteeth.git', 'liteusb': 'https://github.com/enjoy-digital/liteusb.git', - 'litedram': 'https://github.com/enjoy-digital/litedram.git', 'litepcie': 'https://github.com/enjoy-digital/litepcie.git', 'litesdcard': 'https://github.com/enjoy-digital/litesdcard.git', 'liteiclink': 'https://github.com/enjoy-digital/liteiclink.git',