diff --git a/gluelogic/default.nix b/gluelogic/default.nix index e1d58c7..9f5489b 100644 --- a/gluelogic/default.nix +++ b/gluelogic/default.nix @@ -16,11 +16,11 @@ let echo file binary-dist $out/urukul.jed >> $out/nix-support/hydra-build-products ''; }; - buildMirnyCpld = {version, src}: pkgs.stdenv.mkDerivation { + buildMirnyCpld = {version, patchPhase ? "", src}: pkgs.stdenv.mkDerivation { pname = "mirny-cpld"; - inherit src version; + inherit src version patchPhase; buildInputs = [(pkgs.python3.withPackages(ps: [fpgatools.migen]))] ++ (builtins.attrValues fpgatools.ise); - phases = ["buildPhase" "installPhase"]; + phases = ["unpackPhase" "patchPhase" "buildPhase" "installPhase"]; buildPhase = "python $src/mirny_impl.py"; installPhase = '' @@ -66,6 +66,26 @@ in sha256 = "sha256-u1iXcbGX6JkVgfpnCbkyTOVoMYnYcSufLBb6OBAeu8c="; }; }; + mirny-cpld-legacy-almazny = buildMirnyCpld rec { + version = "0.2.4"; + src = pkgs.fetchFromGitHub { + owner = "quartiq"; + repo = "mirny"; + rev = "v${version}"; + sha256 = "sha256-/O1AE0JOXALC8I7NPhOd8h18oX8Qu7lj+6ToAMMD3zs="; + }; + patchPhase = "patch -p1 < ${./mirny-legacy-almazny.diff}"; + }; + mirny-cpld-almazny = buildMirnyCpld rec { + version = "0.3"; + src = pkgs.fetchFromGitHub { + owner = "quartiq"; + repo = "mirny"; + rev = "v${version}"; + sha256 = "sha256-u1iXcbGX6JkVgfpnCbkyTOVoMYnYcSufLBb6OBAeu8c="; + }; + patchPhase = "patch -p1 < ${./mirny-almazny.diff}"; + }; fastino-fpga = pkgs.stdenv.mkDerivation { name = "fastino-fpga"; src = ; diff --git a/gluelogic/mirny-almazny.diff b/gluelogic/mirny-almazny.diff new file mode 100644 index 0000000..9067a49 --- /dev/null +++ b/gluelogic/mirny-almazny.diff @@ -0,0 +1,40 @@ +diff --git a/mirny.py b/mirny.py +index 6c041de..73991b1 100644 +--- a/mirny.py ++++ b/mirny.py +@@ -135,7 +135,7 @@ class SR(Module): + ) + ] + +- def connect_ext(self, ext, adr, mask): ++ def connect_ext(self, ext, adr, mask, sdi_passthrough=False): + adr &= mask + self._check_intersection(adr, mask) + self._slaves.append((ext, adr, mask)) +@@ -146,12 +146,16 @@ class SR(Module): + stb.ce.eq(self.bus.re), + # don't glitch with &stb.o + ext.sck.eq(self.ext.sck), +- ext.sdi.eq(self.ext.sdi & stb.o), + ext.cs.eq(stb.o), + If(stb.o, + self.ext.sdo.eq(ext.sdo), + ), + ] ++ # Almazny shares one SDI with 4 devices, it cannot be masked by stb ++ if sdi_passthrough: ++ self.comb += ext.sdi.eq(self.ext.sdi) ++ else: ++ self.comb += ext.sdi.eq(self.ext.sdi & stb.o), + + + def intersection(a, b): +@@ -360,7 +364,7 @@ class Mirny(Module): + ] + + ext = Record(ext_layout) +- self.sr.connect_ext(ext, adr=i + 12, mask=mask) ++ self.sr.connect_ext(ext, adr=i + 12, mask=mask, sdi_passthrough=True) + self.comb += [ + mezz[i + 3].oe.eq(1), + mezz[i + 3].o.eq(~ext.cs), # Almazny REG_LATCH diff --git a/gluelogic/mirny-legacy-almazny.diff b/gluelogic/mirny-legacy-almazny.diff new file mode 100644 index 0000000..628bcc0 --- /dev/null +++ b/gluelogic/mirny-legacy-almazny.diff @@ -0,0 +1,313 @@ +diff --git a/Makefile b/Makefile +index 667b8e7..ed97303 100644 +--- a/Makefile ++++ b/Makefile +@@ -8,9 +8,15 @@ test: + .PHONY: build + build: build/mirny.vm6 + ++.PHONY: legacy_almazny ++legacy_almazny: build/mirny_legacy_almazny.vm6 ++ + build/mirny.vm6: mirny.py mirny_cpld.py + python mirny_impl.py + ++build/mirny_legacy_almazny.vm6: mirny.py mirny_cpld.py ++ python mirny_impl.py --legacy-almazny ++ + REV:=$(shell git describe --always --abbrev=8 --dirty) + + .PHONY: release +diff --git a/README.md b/README.md +index 1bea35a..5e93809 100644 +--- a/README.md ++++ b/README.md +@@ -1,23 +1,39 @@ +-# Mirny CPLD code ++# Mirny CPLD gateware + +-[Mirny overview](https://github.com/sinara-hw/mirny/wiki) ++## Hardware ++ ++[![Hardware](https://github.com/sinara-hw/mirny/wiki/Mirny_v1.0_top_small.jpg)](https://github.com/sinara-hw/mirny/wiki) + + [Mirny Schematics](https://github.com/sinara-hw/mirny/releases) + + ## Building + +-Needs migen and ISE. ++Needs [migen](https://github.com/m-labs/migen) and [Xilinx ISE](https://www.xilinx.com/products/design-tools/ise-design-suite.html). Assumes ISE is installed in ``/opt/Xilinx``. + + ``` + make +-# and then look at/use flash.sh or make flash +- +-# or use fxload and xc3sprog: +-/sbin/fxload -t fx2 -I /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/xusb_xp2.hex -D /dev/bus/usb/001/*`cat /sys/bus/usb/devices/1-3/devnum` && sleep 10 && \ +-xc3sprog -c xpc -m /opt/Xilinx/14.7/ISE_DS/ISE/xbr/data -v build/mirny.jed:w +-# look for "Verify: Success" + ``` + ++## Flashing ++ ++With Digilent [JTAG HS2](https://store.digilentinc.com/jtag-hs2-programming-cable/) cable: ++ ++ - download firmware to dongle. Manually (adjust USB bus as needed): ++ ``` ++ /sbin/fxload -t fx2 -I /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/xusb_xp2.hex -D /dev/bus/usb/001/*`cat /sys/bus/usb/devices/1-3/devnum` ++ ``` ++ or automatically via the ``udev`` rule: ++ ``` ++ SUBSYSTEM=="usb", ACTION="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", ATTR{manufacturer}=="Digilent", RUN+="/usr/bin/fxload -v -t fx2 -I /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/xusb_xp2.hex -D $tempnode" ++ ``` ++ ++ - install [xc3sprog](http://xc3sprog.sourceforge.net/) ++ ++ - ``flash_xc3.sh jtaghs2`` ++ ++ - look for ``Verify: Success`` ++ ++ + # License + + GPLv3+ +diff --git a/flash_xc3.sh b/flash_xc3.sh +index 4c8a94c..c84b4d6 100755 +--- a/flash_xc3.sh ++++ b/flash_xc3.sh +@@ -1,8 +1,9 @@ + #!/bin/bash + + set -e +-set -x + +-/sbin/fxload -t fx2 -I /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/xusb_xp2.hex -D /dev/bus/usb/001/*`cat /sys/bus/usb/devices/1-7/devnum` +-sleep 7 +-../xc3sprog/build/xc3sprog -c xpc -m /opt/Xilinx/14.7/ISE_DS/ISE/xbr/data -v build/mirny.jed:w ++XC3SPROG=xc3sprog ++CABLE=${1-xpc} ++ ++set -x ++$XC3SPROG -c $CABLE -m /opt/Xilinx/14.7/ISE_DS/ISE/xbr/data -v build/mirny.jed:w +diff --git a/mirny.py b/mirny.py +index 82edca2..6dc2612 100644 +--- a/mirny.py ++++ b/mirny.py +@@ -153,7 +153,6 @@ class SR(Module): + ), + ] + +- + def intersection(a, b): + (aa, am), (ba, bm) = a, b + # TODO +@@ -182,26 +181,26 @@ class Mirny(Module): + SPI + --- + +- SPI xfer is ADR(7), WE(1), DAT(REG: 16, ATT: 8, PLL: 32) +- +- | ADR | TARGET | +- |--------+--------| +- | 0 | REG0 | +- | 1 | REG1 | +- | 2 | REG2 | +- | 3 | REG3 | +- | 4 | PLL0 | +- | 5 | PLL1 | +- | 6 | PLL2 | +- | 7 | PLL3 | +- | 8 | ATT0 | +- | 9 | ATT1 | +- | a | ATT2 | +- | b | ATT3 | +- | c | reserved | +- | d | reserved | +- | e | reserved | +- | f | reserved | ++ SPI xfer is ADR(7), WE(1), DAT(REG: 16, ATT: 8, PLL: 32, SR: 8) ++ ++ | ADR | TARGET | ++ |-----+----------------------| ++ | 0 | REG0 | ++ | 1 | REG1 | ++ | 2 | REG2 | ++ | 3 | REG3 | ++ | 4 | PLL0 | ++ | 5 | PLL1 | ++ | 6 | PLL2 | ++ | 7 | PLL3 | ++ | 8 | ATT0 | ++ | 9 | ATT1 | ++ | a | ATT2 | ++ | b | ATT3 | ++ | c | (Legacy Almazny) SR1 | ++ | d | (Legacy Almazny) SR2 | ++ | e | (Legacy Almazny) SR3 | ++ | f | (Legacy Almazny) SR4 | + + The SPI interface is CPOL=0, CPHA=0, SPI mode 0, 4-wire, full fuplex. + +@@ -223,8 +222,8 @@ class Mirny(Module): + | Name | Width | Function | + |-----------+-------+------------------------------------| + | CE_N | 4 | PLL chip enable (bar) | +- | CLK_SEL | 2 | Selects CLK source: 0 OSC, 1 MMCX, | +- | | | 2 reserved, 3 SMA | ++ | CLK_SEL | 2 | Selects CLK source: | ++ | | | 0 OSC, 1 reserved, 2 MMCX, 3 SMA | + | DIV | 2 | Clock divider configuration: | + | | | 0: divide-by-one, | + | | | 1: reserved, | +@@ -234,6 +233,7 @@ class Mirny(Module): + | FSEN_N | 1 | LVDS fail safe, Type 2 (bar) | + | MUXOUT_EEM| 1 | route MUXOUT to EEM[4:8] | + | EEM_MEZZIO| 1 | route EEM[4:8] to MEZZ_IO[0:4] | ++ | ALMAZNY_OE| 1 | Almazny OE in legacy Almazny mode | + + | Name | Width | Function | + |-----------+-------+------------------------------------| +@@ -250,7 +250,7 @@ class Mirny(Module): + The test points expose miscellaneous signals for debugging and are not part + of the protocol revision. + """ +- def __init__(self, platform): ++ def __init__(self, platform, legacy_almazny=False): + self.eem = eem = [] + for i in range(8): + tsi = TSTriple() +@@ -292,7 +292,7 @@ class Mirny(Module): + self.sr.ext.cs.eq(eem[3].i), + ] + +- regs = [REG(), REG(width=12), REG(width=4), REG()] ++ regs = [REG(), REG(width=13), REG(width=4), REG()] + self.submodules += regs + for i, reg in enumerate(regs): + self.sr.connect(reg.bus, adr=i, mask=mask) +@@ -310,23 +310,47 @@ class Mirny(Module): + clk = platform.request("clk") + clk_div = TSTriple() + self.specials += clk_div.get_tristate(clk.div) +- # in_sel: 00: XO, 01: MMCX, 10: n/a (SMA+XO), 11: SMA ++ # in_sel: 00: XO, 01: n/a (SMA+XO), 10: MMCX, 11: SMA + # dividers: 00(z): 1, 01(z): 1, 10(low): 2, 11(high) 4 + self.comb += [ + Cat(clk.in_sel, clk_div.o, clk_div.oe).eq(regs[1].write[4:8]), + platform.request("fsen").eq(~regs[1].write[9]), + ] + +- for i, m in enumerate(platform.request("mezz_io")): +- tsi = TSTriple() +- self.specials += tsi.get_tristate(m) ++ if legacy_almazny: ++ almazny_io = platform.request("legacy_almazny_common") ++ almazny_adr = 0b1100 # 1100 - and then 1101, 1110, 1111 for sr 1-4 ++ ext = Record(ext_layout) ++ self.sr.connect_ext(ext, almazny_adr, almazny_adr) ++ latches = AsyncRst(width=4, reset=0xF) ++ self.submodules += latches ++ + self.comb += [ +- tsi.o.eq(regs[3].write[i] | (0 if i >= 4 else +- (regs[1].write[11] & eem[i + 4].i))), +- regs[3].read[i].eq(tsi.i), +- tsi.oe.eq(regs[3].write[i + 8]), +- regs[3].read[i + 8].eq(tsi.oe), ++ latches.ce.eq(ext.cs), ++ almazny_io.clk.eq(ext.sck), ++ almazny_io.mosi.eq(ext.sdi), ++ almazny_io.srclr.eq(1) + ] ++ ++ for i in range(4): ++ almazny = platform.request("legacy_almazny", i) ++ self.sync += latches.i[i].eq(self.sr.bus.adr[:2] != i) ++ self.comb += [ ++ almazny.latch.eq(latches.o[i]), ++ almazny.noe.eq(~regs[1].write[12]) ++ ] ++ ++ else: ++ for i, m in enumerate(platform.request("mezz_io")): ++ tsi = TSTriple() ++ self.specials += tsi.get_tristate(m) ++ self.comb += [ ++ tsi.o.eq(regs[3].write[i] | (0 if i >= 4 else ++ (regs[1].write[11] & eem[i + 4].i))), ++ regs[3].read[i].eq(tsi.i), ++ tsi.oe.eq(regs[3].write[i + 8]), ++ regs[3].read[i + 8].eq(tsi.oe), ++ ] + + for i in range(4): + rf_sw = platform.request("rf_sw", i) +diff --git a/mirny_cpld.py b/mirny_cpld.py +index 70fc164..a688d89 100644 +--- a/mirny_cpld.py ++++ b/mirny_cpld.py +@@ -16,9 +16,33 @@ _io = [ + # fail save LVDS enable, LVDS mode selection + # high: type 2 receiver, failsafe low + ("fsen", 0, Pins("P80")), +- ++ ++ # IO from 0 to 7 + ("mezz_io", 0, Pins("P57 P58 P59 P60 P61 P64 P68 P69")), + ++ # legacy (v1.0-1.1) Almazny pins ++ ("legacy_almazny_common", 0, ++ Subsignal("mosi", Pins("P94")), ++ Subsignal("clk", Pins("P97")), ++ Subsignal("srclr", Pins("P60")), ++ ), ++ ("legacy_almazny", 0, ++ Subsignal("latch", Pins("P96")), ++ Subsignal("noe", Pins("P95")), ++ ), ++ ("legacy_almazny", 1, ++ Subsignal("latch", Pins("P100")), ++ Subsignal("noe", Pins("P98")), ++ ), ++ ("legacy_almazny", 2, ++ Subsignal("latch", Pins("P92")), ++ Subsignal("noe", Pins("P101")), ++ ), ++ ("legacy_almazny", 3, ++ Subsignal("latch", Pins("P57")), ++ Subsignal("noe", Pins("P58")), ++ ), ++ + ("clk", 0, + Subsignal("div", Pins("P53")), + Subsignal("in_sel", Pins("P54 P56")), +diff --git a/mirny_impl.py b/mirny_impl.py +index 0c42b0b..d7022dc 100644 +--- a/mirny_impl.py ++++ b/mirny_impl.py +@@ -1,10 +1,23 @@ ++import argparse ++ ++def get_argparser(): ++ parser = argparse.ArgumentParser( ++ description="Mirny CPLD firmware" ++ ) ++ parser.add_argument("--legacy-almazny", action="store_true", default=False) ++ ++ return parser ++ + def main(): + from mirny_cpld import Platform + from mirny import Mirny + ++ args = get_argparser().parse_args() ++ + p = Platform() +- mirny = Mirny(p) +- p.build(mirny, build_name="mirny", mode="cpld") ++ mirny = Mirny(p, args.legacy_almazny) ++ build_name = "mirny" if not args.legacy_almazny else "mirny_legacy_almazny" ++ p.build(mirny, build_name=build_name, mode="cpld") + + + if __name__ == "__main__":