diff --git a/litex_boards/platforms/colorlight_5a_75b.py b/litex_boards/platforms/colorlight_5a_75b.py index 944338d..27afe03 100644 --- a/litex_boards/platforms/colorlight_5a_75b.py +++ b/litex_boards/platforms/colorlight_5a_75b.py @@ -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 diff --git a/litex_boards/platforms/ulx3s.py b/litex_boards/platforms/ulx3s.py index 20640fc..87feeb3 100644 --- a/litex_boards/platforms/ulx3s.py +++ b/litex_boards/platforms/ulx3s.py @@ -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 ----------------------------------------------------------------------------------------- diff --git a/litex_boards/targets/README.colorlight_5a_75b b/litex_boards/targets/README.colorlight_5a_75b new file mode 100644 index 0000000..8663810 --- /dev/null +++ b/litex_boards/targets/README.colorlight_5a_75b @@ -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. diff --git a/litex_boards/targets/README.ulx3s b/litex_boards/targets/README.ulx3s new file mode 100644 index 0000000..d565499 --- /dev/null +++ b/litex_boards/targets/README.ulx3s @@ -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. + diff --git a/litex_boards/targets/bit_to_flash.py b/litex_boards/targets/bit_to_flash.py new file mode 100755 index 0000000..aeba5e6 --- /dev/null +++ b/litex_boards/targets/bit_to_flash.py @@ -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) diff --git a/litex_boards/targets/colorlight_5a_75b.py b/litex_boards/targets/colorlight_5a_75b.py index da89d8f..cafe99d 100755 --- a/litex_boards/targets/colorlight_5a_75b.py +++ b/litex_boards/targets/colorlight_5a_75b.py @@ -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)) diff --git a/litex_boards/targets/ulx3s.py b/litex_boards/targets/ulx3s.py index fa75121..eec7670 100755 --- a/litex_boards/targets/ulx3s.py +++ b/litex_boards/targets/ulx3s.py @@ -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)