Add 'hw/' from commit 'd812378c4d61f7c957ac4bcba15a8344fb7fb458'

git-subtree-dir: hw
git-subtree-mainline: e4af98b4aa
git-subtree-split: d812378c4d
This commit is contained in:
Sean Cross 2019-03-05 09:05:50 +08:00
commit 8fe27d9371
25 changed files with 1168 additions and 0 deletions

3
hw/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
__pycache__/
.env

39
hw/.gitmodules vendored Normal file
View File

@ -0,0 +1,39 @@
[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/liteeth"]
path = deps/liteeth
url = https://github.com/enjoy-digital/liteeth.git
[submodule "deps/liteusb"]
path = deps/liteusb
url = https://github.com/enjoy-digital/liteusb.git
[submodule "deps/litedram"]
path = deps/litedram
url = https://github.com/enjoy-digital/litedram.git
[submodule "deps/litepcie"]
path = deps/litepcie
url = https://github.com/enjoy-digital/litepcie.git
[submodule "deps/litesdcard"]
path = deps/litesdcard
url = https://github.com/enjoy-digital/litesdcard.git
[submodule "deps/liteiclink"]
path = deps/liteiclink
url = https://github.com/enjoy-digital/liteiclink.git
[submodule "deps/litevideo"]
path = deps/litevideo
url = https://github.com/enjoy-digital/litevideo.git
[submodule "deps/lxsocsupport"]
path = deps/lxsocsupport
url = https://github.com/xobs/lxsocsupport.git
[submodule "deps/valentyusb"]
path = deps/valentyusb
url = https://github.com/xobs/valentyusb.git

14
hw/bin/litex_read_verilog Executable file
View File

@ -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.utils.litex_read_verilog import main
main()

14
hw/bin/litex_server Executable file
View File

@ -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.utils.litex_server import main
main()

14
hw/bin/litex_sim Executable file
View File

@ -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.utils.litex_sim import main
main()

14
hw/bin/litex_simple Executable file
View File

@ -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()

14
hw/bin/litex_term Executable file
View File

@ -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.utils.litex_term import main
main()

14
hw/bin/mkmscimg Executable file
View File

@ -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.tools.mkmscimg import main
main()

160
hw/crc5-test.c Normal file
View File

@ -0,0 +1,160 @@
#include <stdio.h>
#include <stdint.h>
const uint8_t crc5Table4[] =
{
0x00, 0x0E, 0x1C, 0x12, 0x11, 0x1F, 0x0D, 0x03,
0x0B, 0x05, 0x17, 0x19, 0x1A, 0x14, 0x06, 0x08};
const uint8_t crc5Table0[] =
{
0x00, 0x16, 0x05, 0x13, 0x0A, 0x1C, 0x0F, 0x19,
0x14, 0x02, 0x11, 0x07, 0x1E, 0x08, 0x1B, 0x0D};
//---------------
int crc5Check(const uint8_t *data)
//---------------
{
uint8_t b = data[0] ^ 0x1F;
uint8_t crc = crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F];
b = data[1] ^ crc;
return (crc5Table4[b & 0x0F] ^ crc5Table0[(b >> 4) & 0x0F]) == 0x06;
}
// crc5Check
int do_check(uint16_t pkt) {
uint8_t data[2] = {
pkt >> 8,
pkt,
};
return crc5Check(data);
}
#define INT_SIZE 32
unsigned CRC5(unsigned dwInput, int iBitcnt)
{
const uint32_t poly5 = (0x05 << (INT_SIZE-5));
uint32_t crc5 = (0x1f << (INT_SIZE-5));
uint32_t udata = (dwInput << (INT_SIZE-iBitcnt));
if ( (iBitcnt<1) || (iBitcnt>INT_SIZE) ) // Validate iBitcnt
return 0xffffffff;
while (iBitcnt--)
{
if ( (udata ^ crc5) & (0x1<<(INT_SIZE-1)) ) // bit4 != bit4?
{
crc5 <<= 1;
crc5 ^= poly5;
}
else
crc5 <<= 1;
udata <<= 1;
}
// Shift back into position
crc5 >>= (INT_SIZE-5);
// Invert contents to generate crc field
crc5 ^= 0x1f;
return crc5;
} //CRC5()
static uint32_t reverse_sof(uint32_t data) {
int i;
uint32_t data_flipped = 0;
for (i = 0; i < 11; i++)
if (data & (1 << i))
data_flipped |= 1 << (10 - i);
return data_flipped;
}
static uint8_t reverse_byte(uint8_t data) {
int i;
uint8_t data_flipped = 0;
for (i = 0; i < 8; i++)
if (data & (1 << i))
data_flipped |= 1 << (7 - i);
return data_flipped;
}
static uint8_t reverse_crc5(uint8_t data) {
int i;
uint8_t data_flipped = 0;
for (i = 0; i < 5; i++)
if (data & (1 << i))
data_flipped |= 1 << (4 - i);
return data_flipped;
}
static uint16_t make_token(uint16_t data) {
uint16_t val = 0;
data = reverse_sof(data);
val = data << 5;
val |= CRC5(data, 11);
return (reverse_byte(val >> 8) << 8) | reverse_byte(val);
}
int do_crc5(uint16_t pkt) {
uint8_t pkt_flipped[2] = {
reverse_byte(pkt >> 8),
reverse_byte(pkt),
};
uint32_t data = (pkt_flipped[1] >> 5) | (pkt_flipped[0] << 3);
uint32_t data_flipped;
uint8_t crc;
uint8_t found_crc = (pkt >> 3) & 0x1f;
data_flipped = reverse_sof(data);
crc = CRC5(data, 11);
crc = reverse_crc5(crc);
uint16_t reconstructed = make_token(data_flipped);
uint16_t wire = (reverse_byte(pkt >> 8) << 8) | reverse_byte(pkt);
printf("Packet: 0x%04x FCRC: %02x Data: 0x%04x "
"Flipped: 0x%04x CRC5: 0x%02x Pass? %d Reconstructed: 0x%04x Wire: %04x\n",
pkt, found_crc, data, data_flipped, crc, do_check(pkt),
reconstructed,
wire
);
return crc;
}
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
int main(int argc, char **argv)
{
uint32_t check_bytes[] = {
/*
0xff3c,
0x12c5,
0xe17e,
0x19f5,
0x0225,
0x0165,
0x009d,
0x102f,
make_token(1013),
make_token(1429),
make_token(100),
*/
0x82bc,
make_token(0x0483),//0x5fde,
0x843c,
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(check_bytes); i++)
do_crc5(check_bytes[i]);
return 0;
}

76
hw/csr-test.py Normal file
View File

@ -0,0 +1,76 @@
#!/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 Module, Signal, Instance, ClockDomain, If, run_simulation
from migen.genlib.resetsync import AsyncResetSynchronizer
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 CSR, CSRStorage, AutoCSR
def csr_test(dut):
for i in range(20):
counter = yield dut.counter
# test_value = yield dut.test_value.w
(test_value) = yield from dut.test_value.read()
print("CSR value: {} / {}".format(counter, test_value))
# yield from CSRStorage.update_csrs()
yield
class TestCSR(Module, AutoCSR):
def __init__(self):
# self.counter = Signal(8)
# self.test_value = CSR(8)
# self.result = Signal(8)
# self.read_result = Signal()
# self.sync += [
# self.counter.eq(self.counter+1),
# self.test_value.w.eq(self.counter),
# self.result.eq(self.test_value.r),
# self.read_result.eq(self.test_value.re),
# ]
self.counter = Signal(8)
self.test_value = CSRStorage(8, write_from_dev=True)
self.result = Signal(8)
self.result_re = Signal()
self.sync += [
self.counter.eq(self.counter+1),
self.test_value.we.eq(1),
self.test_value.dat_w.eq(self.counter),
self.result.eq(self.test_value.storage),
self.result_re.eq(self.test_value.re),
]
def main():
dut = TestCSR()
for x in dir(dut):
print("x: {}".format(x))
for csr in dut.get_csrs():
print("csr: {}".format(csr))
if isinstance(csr, CSRStorage) and hasattr(csr, "dat_w"):
print("Adding CSRStorage patch")
dut.sync += [
If(csr.we,
csr.storage.eq(csr.dat_w),
csr.re.eq(1),
).Else(
csr.re.eq(0),
)
]
run_simulation(dut, csr_test(dut), vcd_name="csr-test.vcd")
if __name__ == "__main__":
main()

1
hw/deps/litedram Submodule

@ -0,0 +1 @@
Subproject commit fd3e9afbcd0872b645badc9b75aacab6dbf7459e

1
hw/deps/liteeth Submodule

@ -0,0 +1 @@
Subproject commit 77fa4bfb1e452adb1fa34c1b0baede68c056763d

1
hw/deps/liteiclink Submodule

@ -0,0 +1 @@
Subproject commit ee9ae87641d98a1b9ad88e28042394797bfb9e00

1
hw/deps/litepcie Submodule

@ -0,0 +1 @@
Subproject commit 3804c4947adedc6c720e3041e518627b0bf57f78

1
hw/deps/litescope Submodule

@ -0,0 +1 @@
Subproject commit c1d8bdf6f23b1070b8bd2dd277a4708863474148

1
hw/deps/litesdcard Submodule

@ -0,0 +1 @@
Subproject commit c07165ff9f9e368c8147158b88fe90cea683e51c

1
hw/deps/liteusb Submodule

@ -0,0 +1 @@
Subproject commit 0a9110f901182a1233cc4e64b6e39175f6784621

1
hw/deps/litevideo Submodule

@ -0,0 +1 @@
Subproject commit 98e145fba8c25394e9958bad67e2a457d145127e

1
hw/deps/litex Submodule

@ -0,0 +1 @@
Subproject commit ff155a474d12b62fb84db0ca07d2577687c5c141

1
hw/deps/lxsocsupport Submodule

@ -0,0 +1 @@
Subproject commit 74f133340144630e000f91e466ac5107f87a8d5a

1
hw/deps/migen Submodule

@ -0,0 +1 @@
Subproject commit ae421054f1e3621621bcc397df171caf3681763c

1
hw/deps/pyserial Submodule

@ -0,0 +1 @@
Subproject commit a4d8f27bf636ee598c4368d7e488f78a226bf778

1
hw/deps/valentyusb Submodule

@ -0,0 +1 @@
Subproject commit ac9a522847a116c842c8f7f795e587ff3d0d0dbb

266
hw/foboot-bitstream.py Executable file
View File

@ -0,0 +1,266 @@
#!/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 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 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
_io = [
("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")),
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"))
]
_connectors = []
class _CRG(Module):
def __init__(self, platform):
clk12 = Signal()
# # "0b00" Sets 48MHz HFOSC output
# # "0b01" Sets 24MHz HFOSC output.
# # "0b10" Sets 12MHz HFOSC output.
# # "0b11" Sets 6MHz HFOSC output
# self.specials += Instance(
# "SB_HFOSC",
# i_CLKHFEN=1,
# i_CLKHFPU=1,
# o_CLKHF=clk12,
# p_CLKHF_DIV="0b10", # 12MHz
# )
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_usb_12 = ClockDomain()
self.reset = Signal()
# FIXME: Use PLL, increase system clock to 32 MHz, pending nextpnr
# fixes.
self.comb += self.cd_sys.clk.eq(clk12)
self.comb += self.cd_usb_12.clk.eq(clk12)
# POR reset logic- POR generated from sys clk, POR logic feeds sys clk
# reset.
self.clock_domains.cd_por = ClockDomain()
reset_delay = Signal(12, reset=4095)
self.comb += [
self.cd_por.clk.eq(self.cd_sys.clk),
self.cd_sys.rst.eq(reset_delay != 0),
self.cd_usb_12.rst.eq(reset_delay != 0)
]
# Divide clk48 down to clk12, to ensure they're synchronized.
clk12_counter = Signal(2)
self.sync.usb_48 += [
clk12_counter.eq(clk12_counter + 1),
]
self.specials += Instance(
"SB_GB",
i_USER_SIGNAL_TO_GLOBAL_BUFFER=clk12_counter[1],
o_GLOBAL_BUFFER_OUTPUT=clk12,
)
self.sync.por += \
If(reset_delay != 0,
reset_delay.eq(reset_delay - 1)
)
self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
self.clock_domains.cd_usb_48 = ClockDomain()
platform.add_period_constraint(self.cd_usb_48.clk, 1e9/48e6)
self.comb += [
self.cd_usb_48.clk.eq(platform.request("clk48")),
]
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)
print("Firmware {} bytes of random data".format(size))
wishbone.SRAM.__init__(self, size, read_only=True, init=data)
class Platform(LatticePlatform):
default_clk_name = "clk48"
default_clk_period = 20.833
gateware_size = 0x20000
def __init__(self, toolchain="icestorm"):
LatticePlatform.__init__(self, "ice40-up5k-sg48", _io, _connectors, toolchain="icestorm")
def create_programmer(self):
raise ValueError("programming is not supported")
# def do_finalize(self, fragment):
# LatticePlatform.do_finalize(self, fragment)
class BaseSoC(SoCCore):
csr_peripherals = [
"cpu_or_bridge",
"usb",
"usb_obuf",
"usb_ibuf",
]
csr_map_update(SoCCore.csr_map, csr_peripherals)
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="random_rom", **kwargs):
# Disable integrated RAM as we'll add it later
self.integrated_sram_size = 0
clk_freq = int(12e6)
self.submodules.crg = _CRG(platform)
platform.add_period_constraint(self.crg.cd_sys.clk, 1e9/clk_freq)
platform.add_period_constraint(self.crg.cd_usb_12.clk, 1e9/clk_freq)
SoCCore.__init__(self, platform, clk_freq, integrated_sram_size=0, **kwargs)
# 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 == "random_rom":
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_rom":
kwargs['cpu_reset_address']=0
bios_size = 0x2000
self.add_memory_region("rom", kwargs['cpu_reset_address'], bios_size)
elif boot_source == "spi_rom":
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 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, endpoints=[EndpointType.BIDIR])
# self.submodules.usb = epmem.MemInterface(usb_iobuf)
# self.submodules.usb = unififo.UsbUniFifo(usb_iobuf)
# 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"
def main():
platform = Platform()
parser = argparse.ArgumentParser(
description="Build Fomu Main Gateware",
add_help=False)
parser.add_argument(
"--bios", help="use bios as boot source", action="store_true"
)
parser.add_argument(
"--rand", help="use random data as boot source", action="store_false"
)
parser.add_argument(
"--spi", help="boot from spi", action="store_true"
)
(args, rest) = parser.parse_known_args()
if args.rand:
boot_source="random_rom"
compile_software=False
elif args.bios:
boot_source="bios_rom"
compile_software=True
elif args.spi:
boot_source = "spi_rom"
compile_software = False
soc = BaseSoC(platform, cpu_type="vexriscv", cpu_variant="min", boot_source=boot_source)
builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv", compile_software=compile_software)
vns = builder.build()
soc.do_exit(vns)
if __name__ == "__main__":
main()

527
hw/lxbuildenv.py Normal file
View File

@ -0,0 +1,527 @@
#!/usr/bin/env python3
# This script enables easy, cross-platform building without the need
# to install third-party Python modules.
import sys
import os
import subprocess
import argparse
DEPS_DIR = "deps"
DEFAULT_DEPS = {
'migen': 'https://github.com/m-labs/migen.git',
'litex': 'https://github.com/enjoy-digital/litex.git',
'litescope': 'https://github.com/enjoy-digital/litescope.git',
'pyserial': 'https://github.com/pyserial/pyserial.git',
'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',
'litevideo': 'https://github.com/enjoy-digital/litevideo.git',
}
# Obtain the path to this script, plus a trailing separator. This will
# be used later on to construct various environment variables for paths
# to a variety of support directories.
script_path = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
# Look through the specified file for known variables to get the dependency list
def get_required_dependencies(filename):
import ast
# Always check the Python version
dependencies = {
'python': 1
}
main_src = ""
try:
with open(sys.argv[0], 'r') as f:
main_src = f.read()
main_ast = ast.parse(main_src, filename=filename)
except:
return list(dependencies.keys())
# Iterate through the top-level nodes looking for variables named
# LX_DEPENDENCIES or LX_DEPENDENCY and get the values that are
# assigned to them.
for node in ast.iter_child_nodes(main_ast):
if isinstance(node, ast.Assign):
value = node.value
for target in node.targets:
if isinstance(target, ast.Name):
if target.id == "LX_DEPENDENCIES" or target.id == "LX_DEPENDENCY":
if isinstance(value, (ast.List, ast.Tuple)):
for elt in value.elts:
if isinstance(elt, ast.Str):
dependencies[elt.s] = 1
elif isinstance(value, ast.Str):
dependencies[value.s] = 1
# Set up sub-dependencies
if 'riscv' in dependencies:
dependencies['make'] = 1
return list(dependencies.keys())
def get_python_path(script_path, args):
# Python has no concept of a local dependency path, such as the C `-I``
# switch, or the nodejs `node_modules` path, or the rust cargo registry.
# Instead, it relies on an environment variable to append to the search
# path.
# Construct this variable by adding each subdirectory under the `deps/`
# directory to the PYTHONPATH environment variable.
python_path = []
if os.path.isdir(script_path + DEPS_DIR):
for dep in os.listdir(script_path + DEPS_DIR):
dep = script_path + DEPS_DIR + os.path.sep + dep
if os.path.isdir(dep):
python_path.append(dep)
return python_path
def fixup_env(script_path, args):
os.environ["PYTHONPATH"] = os.pathsep.join(get_python_path(script_path, 0))
# Set the "LXBUILDENV_REEXEC" variable to prevent the script from continuously
# reinvoking itself.
os.environ["LXBUILDENV_REEXEC"] = "1"
# Python randomizes the order in which it traverses hashes, and Migen uses
# hashes an awful lot when bringing together modules. As such, the order
# in which Migen generates its output Verilog will change with every run,
# and the addresses for various modules will change.
# Make builds deterministic so that the generated Verilog code won't change
# across runs.
os.environ["PYTHONHASHSEED"] = "1"
# Some Makefiles are invoked as part of the build process, and those Makefiles
# occasionally have calls to Python. Ensure those Makefiles use the same
# interpreter that this script is using.
os.environ["PYTHON"] = sys.executable
# Set the environment variable "V" to 1. This causes Makefiles to print
# the commands they run, which makes them easier to debug.
if "lx_verbose" in args and args.lx_verbose:
os.environ["V"] = "1"
# If the user just wanted to print the environment variables, do that and quit.
if args.lx_print_env:
print("PYTHONPATH={}".format(os.environ["PYTHONPATH"]))
print("PYTHONHASHSEED={}".format(os.environ["PYTHONHASHSEED"]))
print("PYTHON={}".format(sys.executable))
print("LXBUILDENV_REEXEC={}".format(os.environ["LXBUILDENV_REEXEC"]))
sys.exit(0)
# Equivalent to the powershell Get-Command, and kinda like `which`
def get_command(cmd):
if os.name == 'nt':
path_ext = os.environ["PATHEXT"].split(os.pathsep)
else:
path_ext = [""]
for ext in path_ext:
for path in os.environ["PATH"].split(os.pathsep):
if os.path.exists(path + os.path.sep + cmd + ext):
return path + os.path.sep + cmd + ext
return None
def check_python_version(args):
import platform
# Litex / Migen require Python 3.5 or newer. Ensure we're running
# under a compatible version of Python.
if sys.version_info[:3] < (3, 5):
return (False,
"python: You need Python 3.5+ (version {} found)".format(sys.version_info[:3]))
return (True, "python 3.5+: ok (Python {} found)".format(platform.python_version()))
def check_vivado(args):
vivado_path = get_command("vivado")
if vivado_path == None:
# Look for the default Vivado install directory
if os.name == 'nt':
base_dir = r"C:\Xilinx\Vivado"
else:
base_dir = "/opt/Xilinx/Vivado"
if os.path.exists(base_dir):
for file in os.listdir(base_dir):
bin_dir = base_dir + os.path.sep + file + os.path.sep + "bin"
if os.path.exists(bin_dir + os.path.sep + "vivado"):
os.environ["PATH"] += os.pathsep + bin_dir
vivado_path = bin_dir
break
if vivado_path == None:
return (False, "toolchain not found in your PATH", "download it from https://www.xilinx.com/support/download.html")
return (True, "found at {}".format(vivado_path))
def check_cmd(args, cmd, name=None, fix=None):
if name is None:
name = cmd
path = get_command(cmd)
if path == None:
return (False, name + " not found in your PATH", fix)
return (True, "found at {}".format(path))
def check_make(args):
return check_cmd(args, "make", "GNU Make")
def check_riscv(args):
riscv64 = check_cmd(args, "riscv64-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/")
if riscv64[0] == True:
return riscv64
riscv32 = check_cmd(args, "riscv32-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/")
if riscv32[0] == True:
return riscv32
return riscv64
def check_yosys(args):
return check_cmd(args, "yosys")
def check_arachne(args):
return check_cmd(args, "arachne-pnr")
def check_icestorm(args):
return check_cmd(args, "icepack") and check_cmd(args, "nextpnr-ice40")
dependency_checkers = {
'python': check_python_version,
'vivado': check_vivado,
'make': check_make,
'riscv': check_riscv,
'yosys': check_yosys,
'arachne-pnr': check_arachne,
'icestorm': check_icestorm,
}
# Validate that the required dependencies (Vivado, compilers, etc.)
# have been installed.
def check_dependencies(args, dependency_list):
dependency_errors = 0
for dependency_name in dependency_list:
if not dependency_name in dependency_checkers:
print('WARNING: Unrecognized dependency "{}"'.format(dependency_name))
continue
result = dependency_checkers[dependency_name](args)
if result[0] == False:
if len(result) > 2:
print('{}: {} -- {}'.format(dependency_name, result[1], result[2]))
else:
print('{}: {}'.format(dependency_name, result[1]))
dependency_errors = dependency_errors + 1
elif args.lx_check_deps or args.lx_verbose:
print('dependency: {}: {}'.format(dependency_name, result[1]))
if dependency_errors > 0:
if args.lx_ignore_deps:
print('{} missing dependencies were found but continuing anyway'.format(dependency_errors))
else:
raise SystemExit(str(dependency_errors) +
" missing dependencies were found")
if args.lx_check_deps:
sys.exit(0)
# Return True if the given tree needs to be initialized
def check_module_recursive(root_path, depth, verbose=False):
if verbose:
print('git-dep: checking if "{}" requires updating...'.format(root_path))
# If the directory isn't a valid git repo, initialization is required
if not os.path.exists(root_path + os.path.sep + '.git'):
return True
# If there are no submodules, no initialization needs to be done
if not os.path.isfile(root_path + os.path.sep + '.gitmodules'):
return False
# Loop through the gitmodules to check all submodules
gitmodules = open(root_path + os.path.sep + '.gitmodules', 'r')
for line in gitmodules:
parts = line.split("=", 2)
if parts[0].strip() == "path":
path = parts[1].strip()
if check_module_recursive(root_path + os.path.sep + path, depth + 1, verbose=verbose):
return True
return False
# Determine whether we need to invoke "git submodules init --recurse"
def check_submodules(script_path, args):
if check_module_recursive(script_path, 0, verbose=args.lx_verbose):
print("Missing submodules -- updating")
subprocess.Popen(["git", "submodule", "update",
"--init", "--recursive"], cwd=script_path).wait()
elif args.lx_verbose:
print("Submodule check: Submodules found")
def lx_git(cmd, *args):
import subprocess
git_cmd = ["git", cmd]
if args is not None:
for arg in args:
git_cmd = git_cmd + [arg]
subprocess.call(git_cmd)
def lx_print_deps():
print('Known dependencies:')
for dep in dependency_checkers.keys():
print(' {}'.format(dep))
print('To define a dependency, add a variable inside {} at the top level called LX_DEPENDENCIES and assign it a list or tuple.'.format(sys.argv[0]))
print('For example:')
print('LX_DEPENDENCIES = ("riscv", "vivado")')
def lx_main(args):
if args.lx_print_env:
fixup_env(script_path, args)
elif args.lx_print_deps:
lx_print_deps()
elif args.lx_run is not None:
script_name=args.lx_run[0]
get_required_dependencies(script_name)
fixup_env(script_path, args)
check_submodules(script_path, args)
try:
sys.exit(subprocess.Popen(
[sys.executable] + [script_name] + args.lx_run[1:]).wait())
except:
sys.exit(1)
elif args.init:
if args.main is None:
main_name = os.getcwd().split(os.path.sep)[-1] + '.py'
new_main_name = input('What would you like your main program to be called? [' + main_name + '] ')
if new_main_name is not None and new_main_name != "":
main_name = new_main_name
else:
main_name = args.main
if not main_name.endswith('.py'):
main_name = main_name + '.py'
if args.no_git:
print("skipping git initialization")
else:
if not os.path.exists(DEPS_DIR):
os.mkdir(DEPS_DIR)
if not os.path.exists(".git"):
print("initializing git repository")
lx_git('init')
else:
print("using existing git repository")
lx_git('add', str(__file__))
for dep_name, dep_url in DEFAULT_DEPS.items():
dest_path = '{}{}{}'.format(DEPS_DIR, '/', dep_name)
if not os.path.exists(dest_path):
lx_git('submodule', 'add', dep_url, dest_path)
lx_git('add', dest_path)
lx_git('submodule', 'update', '--init', '--recursive')
if args.no_bin:
print("skipping bin/ initialization")
elif os.path.exists("bin"):
print("bin/ directory exists -- remove bin/ directory to re-initialize")
else:
bin_tools = {
'mkmscimg': 'litex.soc.tools.mkmscimg',
'litex_term': 'litex.utils.litex_term',
'litex_server': 'litex.utils.litex_server',
'litex_sim': 'litex.utils.litex_sim',
'litex_read_verilog': 'litex.utils.litex_read_verilog',
'litex_simple': 'litex.boards.targets.simple',
}
bin_template = """#!/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
"""
print("Creating binaries")
os.mkdir("bin")
for bin_name, python_module in bin_tools.items():
with open('bin' + os.path.sep + bin_name, 'w') as new_bin:
new_bin.write(bin_template)
new_bin.write('from ' + python_module + ' import main\n')
new_bin.write('main()\n')
import stat
os.chmod('bin' + os.path.sep + bin_name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
if not args.no_git:
lx_git('add', '--chmod=+x', 'bin' + os.path.sep + bin_name)
if os.path.exists(main_name):
print("skipping creation of {}: file exists".format(main_name))
else:
print("creating main program {}".format(main_name))
with open(main_name, 'w') as m:
program_template = """#!/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", "vivado"]
# 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 litex.build.xilinx import VivadoProgrammer, XilinxPlatform
from litex.build.generic_platform import Pins, IOStandard
from litex.soc.integration import SoCSDRAM
from litex.soc.integration.builder import Builder
from litex.soc.integration.soc_core import csr_map_update
_io = [
("clk50", 0, Pins("J19"), IOStandard("LVCMOS33")),
]
class Platform(XilinxPlatform):
def __init__(self, toolchain="vivado", programmer="vivado", part="35"):
part = "xc7a" + part + "t-fgg484-2"
def create_programmer(self):
if self.programmer == "vivado":
return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4")
else:
raise ValueError("{} programmer is not supported"
.format(self.programmer))
def do_finalize(self, fragment):
XilinxPlatform.do_finalize(self, fragment)
class BaseSoC(SoCSDRAM):
csr_peripherals = [
"ddrphy",
# "dna",
"xadc",
"cpu_or_bridge",
]
csr_map_update(SoCSDRAM.csr_map, csr_peripherals)
def __init__(self, platform, **kwargs):
clk_freq = int(100e6)
def main():
platform = Platform()
soc = BaseSoC(platform)
builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv")
vns = builder.build()
soc.do_exit(vns)
if __name__ == "__main__":
main()
"""
m.write(program_template)
if not args.no_git:
lx_git("add", main_name)
else:
return False
return True
# For the main command, parse args and hand it off to main()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Wrap Python code to enable quickstart",
add_help=False)
parser.add_argument(
"-h", "--help", '--lx-help', help="show this help message and exit", action="help"
)
parser.add_argument(
'-i', '--init', '--lx-init', help='initialize a new project', action="store_true"
)
parser.add_argument(
'-m', '--main', '--lx-main', help='name of main project'
)
parser.add_argument(
'--no-bin', '--lx-no-bin', help="don't create a bin/ directory"
)
parser.add_argument(
'--no-git', '--lx-no-git', help="Don't create a git repository"
)
parser.add_argument(
'-e', '--print-env', '--lx-print-env', dest="lx_print_env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true"
)
parser.add_argument(
'-d', '--print-deps', '--lx-print-deps', dest="lx_print_deps", help="print all possible dependencies and then exit", action="store_true"
)
parser.add_argument(
"--lx-verbose", help="increase verboseness of some processes", action="store_true"
)
parser.add_argument(
'-r', '--run', '--lx-run', dest='lx_run', help="run the given script under lxbuildenv", nargs=argparse.REMAINDER
)
args = parser.parse_args()
if not lx_main(args):
parser.print_help()
elif not os.path.isfile(sys.argv[0]):
print("lxbuildenv doesn't operate while in interactive mode")
elif "LXBUILDENV_REEXEC" not in os.environ:
parser = argparse.ArgumentParser(
description="Wrap Python code to enable quickstart",
add_help=False)
parser.add_argument(
"--lx-verbose", help="increase verboseness of some processes", action="store_true"
)
parser.add_argument(
"--lx-print-env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true"
)
parser.add_argument(
"--lx-check-deps", help="check build environment for dependencies such as compiler and fpga tools and then exit", action="store_true"
)
parser.add_argument(
"--lx-print-deps", help="print all possible dependencies and then exit", action="store_true"
)
parser.add_argument(
"--lx-help", action="help"
)
parser.add_argument(
"--lx-ignore-deps", help="try building even if dependencies are missing", action="store_true"
)
(args, rest) = parser.parse_known_args()
if args.lx_print_deps:
lx_print_deps()
sys.exit(0)
deps = get_required_dependencies(sys.argv[0])
fixup_env(script_path, args)
check_dependencies(args, deps)
check_submodules(script_path, args)
try:
sys.exit(subprocess.Popen(
[sys.executable] + [sys.argv[0]] + rest).wait())
except:
sys.exit(1)
else:
# Overwrite the deps directory.
# Because we're running with a predefined PYTHONPATH, you'd think that
# the DEPS_DIR would be first.
# Unfortunately, setuptools causes the sitewide packages to take precedence
# over the PYTHONPATH variable.
# Work around this bug by inserting paths into the first index.
for path in get_python_path(script_path, None):
sys.path.insert(0, path)