Merge pull request #67 from mubes/ecp5_usb

Addition of USB ACM for ECP5
This commit is contained in:
enjoy-digital 2020-04-14 15:56:22 +02:00 committed by GitHub
commit 4b4f2f9eb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 266 additions and 16 deletions

View File

@ -172,6 +172,13 @@ _io_v7_0 = [ # Documented by @miek
Subsignal("tx_data", Pins("T14 R12 R13 R14")),
IOStandard("LVCMOS33")
),
("usb", 0,
Subsignal("d_p", Pins("M8")),
Subsignal("d_n", Pins("R2")),
Subsignal("pullup", Pins("P4")),
IOStandard("LVCMOS33")
),
]
# from https://github.com/miek/chubby75/blob/5a-75b-v7_pinout/5a-75b/hardware_V6.1.md

View File

@ -84,6 +84,13 @@ _io = [
Subsignal("n", Pins("C10")),
IOStandard("LVCMOS33")
),
("usb", 0,
Subsignal("d_p", Pins("D15")),
Subsignal("d_n", Pins("E15")),
Subsignal("pullup", Pins("B12 C12")),
IOStandard("LVCMOS33")
),
]
# Platform -----------------------------------------------------------------------------------------

View File

@ -0,0 +1,26 @@
USB
===
USB support has been integrated for the V7.0 this board. Supporting other
versions should be trivial and just need pinning changes. To build with
usb support just do;
./colourlight_5a_75b.py --uart-name=usb_cdc
To install onto the board;
./colourlight_5a_75b.py --load
The USB Serial connection will appear as /dev/ttyACMx, or equivalent on your OS.
Pinning for V7.0;
* Replace U23 with a SN74CBT3245APWR, or remove U23 and place jumper wires.
(You're basically making the ports Bi-directional).
* Place a 15K resistor between J4 pin 2 and J4 pin 4.
* Place a 15K resistor between J4 pin 3 and J4 pin 4.
* Place a 1.5K resistor between J4 pin 1 and J4 pin 3.
* Connect USB DP (Green) to J4 pin 3, USB DN (White) to J4 pin 2.

View File

@ -0,0 +1,11 @@
USB
===
USB support has been integrated and tested for V3.0.3 this board. Revisions
higher than this should be supported. The USB connection is on US2.
To build with usb support just do;
./ulx3s.py --uart-name=usb_cdc
The USB Serial connection will appear as /dev/ttyACMx, or equivalent on your OS.

View File

@ -0,0 +1,149 @@
#!/usr/bin/env python3
import sys
import textwrap
# Very basic bitstream to SVF converter, tested with the ULX3S WiFi interface
flash_page_size = 256
erase_block_size = 64*1024
def bitreverse(x):
y = 0
for i in range(8):
if (x >> (7 - i)) & 1 == 1:
y |= (1 << i)
return y
with open(sys.argv[1], 'rb') as bitf:
bs = bitf.read()
# Autodetect IDCODE from bitstream
idcode_cmd = bytes([0xE2, 0x00, 0x00, 0x00])
idcode = None
for i in range(len(bs) - 4):
if bs[i:i+4] == idcode_cmd:
idcode = bs[i+4] << 24
idcode |= bs[i+5] << 16
idcode |= bs[i+6] << 8
idcode |= bs[i+7]
break
if idcode is None:
print("Failed to find IDCODE in bitstream, check bitstream is valid")
sys.exit(1)
bitf.seek(0)
address = 0
last_page = -1
with open(sys.argv[2], 'w') as svf:
print("""
STATE RESET;
HDR 0;
HIR 0;
TDR 0;
TIR 0;
ENDDR DRPAUSE;
ENDIR IRPAUSE;
STATE IDLE;
""", file=svf)
print("""
SIR 8 TDI (E0);
SDR 32 TDI (00000000)
TDO ({:08X})
MASK (FFFFFFFF);
""".format(idcode), file=svf)
print("""
SIR 8 TDI (1C);
SDR 510 TDI (3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
// Enter Programming mode
SIR 8 TDI (C6);
SDR 8 TDI (00);
RUNTEST IDLE 2 TCK 1.00E-02 SEC;
// Erase
SIR 8 TDI (0E);
SDR 8 TDI (01);
RUNTEST IDLE 2 TCK 2.0E-1 SEC;
// Read STATUS
SIR 8 TDI (3C);
SDR 32 TDI (00000000)
TDO (00000000)
MASK (0000B000);
// Exit Programming mode
SIR 8 TDI (26);
RUNTEST IDLE 2 TCK 1.00E-02 SEC;
// BYPASS
SIR 8 TDI (FF);
STATE IDLE;
RUNTEST 32 TCK;
RUNTEST 2.00E-2 SEC;
// Enter SPI mode
SIR 8 TDI (3A);
SDR 16 TDI (68FE);
STATE IDLE;
RUNTEST 32 TCK;
RUNTEST 2.00E-2 SEC;
// SPI IO
SDR 8 TDI (D5);
RUNTEST 2.00E-0 SEC;
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (68FFFFFF)
MASK (FF000000);
SDR 8 TDI(60);
SDR 16 TDI(0080);
RUNTEST 1.00E-0 SEC;
""", file=svf)
while True:
if((address // 0x10000) != last_page):
last_page = (address // 0x10000)
print("""SDR 8 TDI (60);
""", file=svf)
address_flipped = [bitreverse(x) for x in [0xd8,int(address // 0x10000),0x00,0x00]]
hex_address= ["{:02X}".format(x) for x in reversed(address_flipped)]
print("\n".join(textwrap.wrap("SDR {} TDI ({});".format(8*len(hex_address), "".join(hex_address)), 100)), file=svf)
print("""RUNTEST 3.00 SEC;
""", file=svf)
chunk = bitf.read(flash_page_size)
if not chunk:
break
# Convert chunk to bit-reversed hex
br_chunk = [bitreverse(x) for x in bytes([0x02, int(address / 0x10000 % 0x100),int(address / 0x100 % 0x100),int(address % 0x100)]) + chunk]
address += len(chunk)
hex_chunk = ["{:02X}".format(x) for x in reversed(br_chunk)]
print("""
SDR 8 TDI (60);
""", file=svf)
print("\n".join(textwrap.wrap("SDR {} TDI ({});".format(8*len(br_chunk), "".join(hex_chunk)), 100)), file=svf)
print("""
RUNTEST 2.50E-2 SEC;
""", file=svf)
print("""
// BYPASS
SIR 8 TDI (FF);
//REFRESH
SIR 8 TDI(79);
SDR 24 TDI(000000);
STATE IDLE;
RUNTEST 32 TCK;
RUNTEST 2.00E-2 SEC;
STATE RESET;
""", file=svf)

View File

@ -48,7 +48,7 @@ from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
def __init__(self, platform, sys_clk_freq, with_rst=True):
def __init__(self, platform, sys_clk_freq, with_usb_pll=False, with_rst=True):
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_sys_ps = ClockDomain()
@ -67,22 +67,34 @@ class _CRG(Module):
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll.locked | ~rst_n)
# USB PLL
if with_usb_pll:
self.submodules.usb_pll = usb_pll = ECP5PLL()
usb_pll.register_clkin(clk25, 25e6)
self.clock_domains.cd_usb_12 = ClockDomain()
self.clock_domains.cd_usb_48 = ClockDomain()
usb_pll.create_clkout(self.cd_usb_12, 12e6, margin=0)
usb_pll.create_clkout(self.cd_usb_48, 48e6, margin=0)
#self.comb += self.cd_usb_48.clk.eq(self.cd_sys.clk)
# SDRAM clock
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), ClockSignal("sys_ps"))
# BaseSoC ------------------------------------------------------------------------------------------
class BaseSoC(SoCCore):
def __init__(self, revision, with_ethernet=False, with_etherbone=False, **kwargs):
def __init__(self, revision, with_ethernet=False, with_etherbone=False, sys_clk_freq=60e6, **kwargs):
platform = colorlight_5a_75b.Platform(revision=revision)
sys_clk_freq = int(125e6) if with_etherbone else int(60e6)
if (with_etherbone):
sys_clk_freq = int(125e6)
# SoCCore ----------------------------------------------------------------------------------
SoCCore.__init__(self, platform, clk_freq=sys_clk_freq, **kwargs)
# CRG --------------------------------------------------------------------------------------
with_rst = kwargs["uart_name"] not in ["serial", "bridge"] # serial_rx shared with user_btn_n.
self.submodules.crg = _CRG(platform, sys_clk_freq, with_rst=with_rst)
with_usb_pll = kwargs.get("uart_name", None) == "usb_cdc"
self.submodules.crg = _CRG(platform, sys_clk_freq, with_usb_pll=with_usb_pll,with_rst=with_rst)
# SDR SDRAM --------------------------------------------------------------------------------
if not self.integrated_main_ram_size:
@ -115,23 +127,44 @@ class BaseSoC(SoCCore):
# Load ---------------------------------------------------------------------------------------------
def load():
def openocd_run_svf(filename, iface="ftdi"):
import os
f = open("openocd.cfg", "w")
f.write(
"""
interface ftdi
if (iface == "ftdi"):
f.write(
"""adapter driver ftdi
ftdi_vid_pid 0x0403 0x6011
ftdi_channel 0
ftdi_layout_init 0x0098 0x008b
reset_config none
adapter_khz 25000
adapter speed 25000
jtag newtap ecp5 tap -irlen 8 -expected-id 0x41111043
""")
elif (iface=="jlink"):
f.write("""adapter driver jlink
transport select jtag
reset_config none
telnet_port 4444
adapter speed 10000
jtag newtap lfe5u25 tap -irlen 8 -irmask 0xFF -ircapture 0x5 -expected-id 0x41111043
""")
else:
print("Unrecognised jtag interface")
exit()
f.close()
os.system("openocd -f openocd.cfg -c \"transport select jtag; init; svf soc_basesoc_colorlight_5a_75b/gateware/top.svf; exit\"")
os.system("openocd -d0 -f openocd.cfg -c \"transport select jtag; init; svf -tap lfe5u25.tap {} -quiet -progress; exit\"".format(filename))
os.system("rm openocd.cfg")
exit()
def load(iface="ftdi"):
openocd_run_svf("soc_basesoc_colorlight_5a_75b/gateware/top.svf",iface=iface)
def flash(iface="ftdi"):
import os
os.system("./bit_to_flash.py soc_basesoc_colorlight_5a_75b/gateware/top.bit soc_basesoc_colorlight_5a_75b/gateware/top.svf.flash")
openocd_run_svf("soc_basesoc_colorlight_5a_75b/gateware/top.svf.flash",iface=iface)
# Build --------------------------------------------------------------------------------------------
def main():
@ -144,15 +177,24 @@ def main():
parser.add_argument("--with-etherbone", action="store_true", help="enable Etherbone support")
parser.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY 0 or 1 (default=0)")
parser.add_argument("--load", action="store_true", help="load bitstream")
parser.add_argument("--flash", action="store_true", help="flash bitstream")
parser.add_argument("--iface", default="ftdi", help="loading jtag interface")
parser.add_argument("--sys-clk-freq", default=60e6,
help="system clock frequency (default=60MHz)")
args = parser.parse_args()
if args.load:
load()
load(iface=args.iface)
if args.flash:
flash(iface=args.iface)
assert not (args.with_ethernet and args.with_etherbone)
soc = BaseSoC(revision=args.revision,
with_ethernet = args.with_ethernet,
with_etherbone = args.with_etherbone,
sys_clk_freq = args.sys_clk_freq,
**soc_core_argdict(args))
builder = Builder(soc, **builder_argdict(args))
builder.build(**trellis_argdict(args))

View File

@ -27,7 +27,7 @@ from litedram.phy import GENSDRPHY
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
def __init__(self, platform, sys_clk_freq):
def __init__(self, platform, sys_clk_freq, with_usb_pll=False):
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True)
@ -42,10 +42,17 @@ class _CRG(Module):
self.submodules.pll = pll = ECP5PLL()
self.comb += pll.reset.eq(rst)
pll.register_clkin(clk25, 25e6)
pll.create_clkout(self.cd_sys, sys_clk_freq)
pll.create_clkout(self.cd_sys, sys_clk_freq, margin=0)
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=90)
self.specials += AsyncResetSynchronizer(self.cd_sys, ~pll.locked | rst)
# USB PLL
if with_usb_pll:
self.clock_domains.cd_usb_12 = ClockDomain()
self.clock_domains.cd_usb_48 = ClockDomain()
pll.create_clkout(self.cd_usb_12, 12e6, margin=0)
self.comb += self.cd_usb_48.clk.eq(self.cd_sys.clk)
# SDRAM clock
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), ClockSignal("sys_ps"))
@ -64,7 +71,8 @@ class BaseSoC(SoCCore):
SoCCore.__init__(self, platform, clk_freq=sys_clk_freq, **kwargs)
# CRG --------------------------------------------------------------------------------------
self.submodules.crg = _CRG(platform, sys_clk_freq)
with_usb_pll = kwargs.get("uart_name", None) == "usb_cdc"
self.submodules.crg = _CRG(platform, sys_clk_freq, with_usb_pll)
# SDR SDRAM --------------------------------------------------------------------------------
if not self.integrated_main_ram_size:
@ -87,8 +95,8 @@ def main():
help="gateware toolchain to use, trellis (default) or diamond")
parser.add_argument("--device", dest="device", default="LFE5U-45F",
help="FPGA device, ULX3S can be populated with LFE5U-45F (default) or LFE5U-85F")
parser.add_argument("--sys-clk-freq", default=50e6,
help="system clock frequency (default=50MHz)")
parser.add_argument("--sys-clk-freq", default=48e6,
help="system clock frequency (default=48MHz)")
parser.add_argument("--sdram-module", default="MT48LC16M16",
help="SDRAM module: MT48LC16M16, AS4C32M16 or AS4C16M16 (default=MT48LC16M16)")
builder_args(parser)