From fb929c8599f2df03fcdbb5f69225d14dece40c95 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 26 Feb 2016 13:11:10 +0100 Subject: [PATCH 01/52] gateware/spi: stubs --- artiq/gateware/targets/pipistrello.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 288a7b3c8..dbb840aab 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -19,7 +19,7 @@ from misoc.targets.pipistrello import * from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1 -from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds, spi from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -152,12 +152,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512, ofifo_depth=4)) - # ttl2 can run on a 8x serdes if xtrig is not used - for i in range(15): + # the last five ttls are used for SPI and a ClockGen + for i in range(11): if i in (0, 1): phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i), self.rtio_crg.rtiox4_stb) - elif i in (2,): + elif i in (2,): # ttl2 can run on a 8x serdes if xtrig is not used phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i), self.rtio_crg.rtiox8_stb) else: @@ -192,6 +192,20 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd ofifo_depth=512, ififo_depth=4)) + spi_pins = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) + # cs_n can be multiple bits wide, one-hot + # absence of miso indicates bidirectional mosi + self.comb += [ + platform.request("ttl", 11).eq(spi_pins.cs_n), + platform.request("ttl", 12).eq(spi_pins.clk), + platform.request("ttl", 13).eq(spi_pins.mosi), + spi_pins.miso.eq(platform.request("ttl", 14)), + ] + phy = spi.SPIMaster(spi_pins) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=4, ififo_depth=4)) + self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.LogChannel()) From e7146cc999428bff820c1d1fd39998a51fcb0bfe Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Fri, 26 Feb 2016 17:03:08 +0100 Subject: [PATCH 02/52] gateware.spi: design sketch --- artiq/gateware/rtio/phy/spi.py | 13 ++++ artiq/gateware/spi.py | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 artiq/gateware/rtio/phy/spi.py create mode 100644 artiq/gateware/spi.py diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py new file mode 100644 index 000000000..e72f602c2 --- /dev/null +++ b/artiq/gateware/rtio/phy/spi.py @@ -0,0 +1,13 @@ +from migen import * + +from artiq.gateware.spi import SPIMaster as SPIMasterWB +from artiq.gateware.rtio.phy.wishbone import RT2WB + + +class SPIMaster(Module): + def __init__(self, pads, onehot=False, **kwargs): + self.submodules._ll = ClockDomainsRenamer("rio")( + SPIMasterWB(pads, **kwargs)) + self.submodules._rt2wb = RT2WB(2, self._ll.bus) + self.rtlink = self._rt2wb.rtlink + self.probes = [] diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py new file mode 100644 index 000000000..c3c6f14b2 --- /dev/null +++ b/artiq/gateware/spi.py @@ -0,0 +1,109 @@ +from migen import * +from migen.genlib.fsm import * +from migen.genlib.misc import WaitTimer +from misoc.interconnect import wishbone + + +class SPIMaster(Module): + """SPI Master. + + Notes: + * If there is a miso wire in pads, the input and output are done with + two signals (a.k.a. 4-wire SPI), else mosi is used for both output + and input (a.k.a. 3-wire SPI). + * Every transfer consists of a 0-32 bit write followed by a 0-32 + bit read. + * cs_n is always asserted at the beginning and deasserted + at the end of the tranfer. + * cs_n handling is agnostic to whether it is one-hot or decoded + somewhere downstream. If it is decoded, "cs_n all deasserted" + should be handled accordingly (no slave selected). + If it is one-hot, asserting multiple slaves should only be attempted + if miso is either not connected between slaves or open collector. + * If config.cs_polarity == 0 (cs active low, the default), + "cs_n all deasserted" means "all cs_n bits high". + * The first bit output on mosi is always the MSB/LSB (depending on + config.lsb_first) of the data register, independent of + xfer.write_len. The last bit input from miso always ends up in + the LSB/MSB (respectively) of the data register, independent of + read_len. + * For 4-wire SPI only the sum of read_len and write_len matters. The + behavior is the same no matter how the transfer length is divided + between the two. For 3-wire SPI, the direction of mosi/miso is + switched from output to input after write_len cycles, at the + "output" clk edge corresponding to bit write_len + 1 of the transfer. + * Data output on mosi in 4-wire SPI during the read cycles is + undefined. Data in the data register outside the + least/most (depending on config.lsb_first) significant read_len + bits is undefined. + * The transfer is complete when the wishbone transaction is ack-ed. + * Input data from the last transaction can be read from the data + register at any time. + + Transaction Sequence: + * if desired, write the xfer register to change lengths and cs_n. + * write the data register (also for zero-length writes), + writing triggers the transfer and the transfer is complete when the + write is complete. + * if desired, read the data register + + Register address and bit map: + + config (address 0): + 1 offline: all pins high-z (reset=1) + 1 cs_polarity: active level of chip select (reset=0) + 1 clk_polarity: idle level for clk (reset=0) + 1 clk_phase: first edge after cs assertion to sample data on (reset=0) + (0, 0): idle low, output on falling, input on rising + (0, 1): idle low, output on rising, input on falling + (1, 0): idle high, output on rising, input on falling + (1, 1): idle high, output on falling, input on rising + 1 lsb_first: LSB is the first bit on the wire (reset=0) + 11 undefined + 16 speed: divider from this module's clock to the SPI clk + (minimum=2, reset=4) + clk pulses are asymmetric if speed is odd, favoring longer setup + over hold times + + xfer (address 1): + 16 cs: active high bit mask of chip selects to assert + 6 write_len: 0-32 bits + 2 undefined + 6 read_len: 0-32 bits + 2 undefined + + data (address 2): + 32 write/read data + """ + def __init__(self, pads, bus=None): + if bus is None: + bus = wishbone.Interface(data_width=32) + self.bus = bus + + ### + + +def _test_gen(bus): + yield from bus.write(0, 0 | (5 << 16)) + yield + yield from bus.write(1, 1 | (24 << 16) | (16 << 24)) + yield + yield from bus.write(2, 0x12345678) + yield + r = (yield from bus.read(2)) + print(r) + yield + + +class _TestPads: + def __init__(self): + self.cs_n = Signal(3) + self.clk = Signal() + self.mosi = Signal() + self.miso = Signal() + + +if __name__ == "__main__": + pads = _TestPads() + dut = SPIMaster(pads) + run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From ade3eda19a8f0b0728226cab5555a6c875f78042 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 27 Feb 2016 11:29:40 +0100 Subject: [PATCH 03/52] gateware.pipistrello: use pmod for spi --- artiq/gateware/targets/pipistrello.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index dbb840aab..5967ccdd7 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -152,8 +152,8 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512, ofifo_depth=4)) - # the last five ttls are used for SPI and a ClockGen - for i in range(11): + # the last TTL is used for ClockGen + for i in range(15): if i in (0, 1): phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i), self.rtio_crg.rtiox4_stb) @@ -192,15 +192,12 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd ofifo_depth=512, ififo_depth=4)) - spi_pins = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) - # cs_n can be multiple bits wide, one-hot - # absence of miso indicates bidirectional mosi - self.comb += [ - platform.request("ttl", 11).eq(spi_pins.cs_n), - platform.request("ttl", 12).eq(spi_pins.clk), - platform.request("ttl", 13).eq(spi_pins.mosi), - spi_pins.miso.eq(platform.request("ttl", 14)), - ] + pmod = self.platform.request("pmod", 0) + spi_pins = Module() + spi_pins.clk = pmod.d[0] + spi_pins.mosi = pmod.d[1] + spi_pins.miso = pmod.d[2] + spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( From bd9ceb4e12d4fa2503cae096bbeda22700d3f3d9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 27 Feb 2016 22:47:16 +0100 Subject: [PATCH 04/52] gateware.spi: add complete spi master logic --- artiq/gateware/spi.py | 304 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 270 insertions(+), 34 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index c3c6f14b2..d0df75509 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -1,3 +1,5 @@ +from itertools import product + from migen import * from migen.genlib.fsm import * from migen.genlib.misc import WaitTimer @@ -8,18 +10,22 @@ class SPIMaster(Module): """SPI Master. Notes: - * If there is a miso wire in pads, the input and output are done with - two signals (a.k.a. 4-wire SPI), else mosi is used for both output - and input (a.k.a. 3-wire SPI). - * Every transfer consists of a 0-32 bit write followed by a 0-32 + * M = 32 is the data width (width of the data register, + maximum write bits, maximum read bits) + * If there is a miso wire in pads, the input and output can be done + with two signals (a.k.a. 4-wire SPI), else mosi must be used for + both output and input (a.k.a. 3-wire SPI) and config.half_duplex + needs to be set. + * Every transfer consists of a 0-M bit write followed by a 0-M bit read. * cs_n is always asserted at the beginning and deasserted - at the end of the tranfer. + at the end of the transfer. * cs_n handling is agnostic to whether it is one-hot or decoded somewhere downstream. If it is decoded, "cs_n all deasserted" should be handled accordingly (no slave selected). If it is one-hot, asserting multiple slaves should only be attempted if miso is either not connected between slaves or open collector. + cs can also be handled independently through other means. * If config.cs_polarity == 0 (cs active low, the default), "cs_n all deasserted" means "all cs_n bits high". * The first bit output on mosi is always the MSB/LSB (depending on @@ -32,24 +38,26 @@ class SPIMaster(Module): between the two. For 3-wire SPI, the direction of mosi/miso is switched from output to input after write_len cycles, at the "output" clk edge corresponding to bit write_len + 1 of the transfer. - * Data output on mosi in 4-wire SPI during the read cycles is - undefined. Data in the data register outside the - least/most (depending on config.lsb_first) significant read_len - bits is undefined. - * The transfer is complete when the wishbone transaction is ack-ed. + * Data output on mosi in 4-wire SPI during the read cycles is what + is found in the data register at the time. + Data in the data register outside the least/most (depending + on config.lsb_first) significant read_len bits is what is + seen on miso during the write cycles. + * When the transfer is complete the wishbone transaction is ack-ed. * Input data from the last transaction can be read from the data register at any time. Transaction Sequence: - * if desired, write the xfer register to change lengths and cs_n. - * write the data register (also for zero-length writes), - writing triggers the transfer and the transfer is complete when the - write is complete. - * if desired, read the data register + * If desired, write the config register to set up the core. + * If desired, write the xfer register to change lengths and cs_n. + * Write the data register (also for zero-length writes), + writing triggers the transfer and when the transfer is complete the + write is ack-ed. + * If desired, read the data register. Register address and bit map: - config (address 0): + config (address 2): 1 offline: all pins high-z (reset=1) 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level for clk (reset=0) @@ -59,40 +67,252 @@ class SPIMaster(Module): (1, 0): idle high, output on rising, input on falling (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) - 11 undefined - 16 speed: divider from this module's clock to the SPI clk - (minimum=2, reset=4) - clk pulses are asymmetric if speed is odd, favoring longer setup - over hold times + 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) + 10 undefined + 16 clk_load: clock load value to divide from this module's clock + to the SPI write clk clk pulses are asymmetric + if a divider is odd, favoring longer setup over hold times. + clk/spi_clk == clk_load + 2 (reset=0) xfer (address 1): 16 cs: active high bit mask of chip selects to assert - 6 write_len: 0-32 bits - 2 undefined - 6 read_len: 0-32 bits - 2 undefined + 8 write_len: 0-M bits + 8 read_len: 0-M bits - data (address 2): - 32 write/read data + data (address 0): + M write/read data """ - def __init__(self, pads, bus=None): + def __init__(self, pads, bus=None, data_width=32): if bus is None: - bus = wishbone.Interface(data_width=32) + bus = wishbone.Interface(data_width=data_width) self.bus = bus ### + # State machine + wb_we = Signal() + start = Signal() + active = Signal() + + fsm = FSM("IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(bus.cyc & bus.stb, + NextState("ACK"), + If(bus.we, + wb_we.eq(1), + If(bus.adr == 0, # data register + NextState("START"), + ) + ) + ) + ) + fsm.act("START", + start.eq(1), + NextState("ACTIVE"), + ) + fsm.act("ACTIVE", + If(~active, + bus.ack.eq(1), + NextState("IDLE"), + ) + ) + fsm.act("ACK", + bus.ack.eq(1), + NextState("IDLE"), + ) + + # Wishbone + config = Record([ + ("offline", 1), + ("cs_polarity", 1), + ("clk_polarity", 1), + ("clk_phase", 1), + ("lsb_first", 1), + ("half_duplex", 1), + ("padding", 10), + ("clk_load", 16), + ]) + config.offline.reset = 1 + assert len(config) <= len(bus.dat_w) + + xfer = Record([ + ("cs", 16), + ("write_length", 8), + ("read_length", 8), + ]) + assert len(xfer) <= len(bus.dat_w) + + data = Signal.like(bus.dat_w) + + wb_data = Array([data, xfer.raw_bits(), config.raw_bits()])[bus.adr] + self.comb += bus.dat_r.eq(wb_data) + self.sync += If(wb_we, wb_data.eq(bus.dat_w)) + + # SPI + write_count = Signal.like(xfer.write_length) + read_count = Signal.like(xfer.read_length) + clk_count = Signal.like(config.clk_load) + clk = Signal(reset=1) # idle high + phase = Signal() + edge = Signal() + write = Signal() + read = Signal() + miso = Signal() + miso_i = Signal() + mosi_o = Signal() + + self.comb += [ + phase.eq(clk ^ config.clk_phase), + edge.eq(active & (clk_count == 0)), + write.eq(write_count != 0), + read.eq(read_count != 0), + ] + + self.sync += [ + If(start, + write_count.eq(xfer.write_length), + read_count.eq(xfer.read_length), + active.eq(1), + ), + If(active, + clk_count.eq(clk_count - 1), + ), + If(start | edge, + # setup time passes during phase 0 + # use the lsb to bias that time to favor longer setup times + clk_count.eq(config.clk_load[1:] + + (config.clk_load[0] & phase)), + clk.eq(~clk), # idle high + If(phase, + data.eq(Mux(config.lsb_first, + Cat(data[1:], miso), + Cat(miso, data[:-1]))), + mosi_o.eq(Mux(config.lsb_first, data[0], data[-1])), + If(write, + write_count.eq(write_count - 1), + ), + ).Else( + miso.eq(miso_i), + If(~write & read, + read_count.eq(read_count - 1), + ), + ), + ), + If(~clk & edge & ~write & ~read, # always from low clk + active.eq(0), + ), + ] + + # I/O + cs_n_t = TSTriple(len(pads.cs_n)) + self.specials += cs_n_t.get_tristate(pads.cs_n) + clk_t = TSTriple() + self.specials += clk_t.get_tristate(pads.clk) + mosi_t = TSTriple() + self.specials += mosi_t.get_tristate(pads.mosi) + + self.comb += [ + cs_n_t.oe.eq(~config.offline), + clk_t.oe.eq(~config.offline), + mosi_t.oe.eq(~config.offline & (write | ~config.half_duplex)), + cs_n_t.o.eq((xfer.cs & Replicate(active, len(xfer.cs))) ^ + Replicate(~config.cs_polarity, len(xfer.cs))), + clk_t.o.eq((clk & active) ^ config.clk_polarity), + miso_i.eq(Mux(config.half_duplex, mosi_t.i, + getattr(pads, "miso", mosi_t.i))), + mosi_t.o.eq(mosi_o), + ] + + +SPI_CONFIG_ADDR = 2 +SPI_XFER_ADDR = 1 +SPI_DATA_ADDR = 0 +SPI_OFFLINE = 1 << 0 +SPI_CS_POLARITY = 1 << 1 +SPI_CLK_POLARITY = 1 << 2 +SPI_CLK_PHASE = 1 << 3 +SPI_LSB_FIRST = 1 << 4 +SPI_HALF_DUPLEX = 1 << 5 + + +def SPI_CLK_LOAD(i): + return i << 16 + + +def SPI_CS(i): + return i << 0 + + +def SPI_WRITE_LENGTH(i): + return i << 16 + + +def SPI_READ_LENGTH(i): + return i << 24 + def _test_gen(bus): - yield from bus.write(0, 0 | (5 << 16)) + yield from bus.write(SPI_CONFIG_ADDR, + 1*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | + 1*SPI_HALF_DUPLEX | SPI_CLK_LOAD(3)) yield - yield from bus.write(1, 1 | (24 << 16) | (16 << 24)) + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | + SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(0)) yield - yield from bus.write(2, 0x12345678) + yield from bus.write(SPI_DATA_ADDR, 0x90000000) yield - r = (yield from bus.read(2)) - print(r) + print(hex((yield from bus.read(SPI_DATA_ADDR)))) yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(4)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x81000000) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(0) | SPI_READ_LENGTH(4)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x90000000) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | + SPI_WRITE_LENGTH(32) | SPI_READ_LENGTH(0)) + yield + yield from bus.write(SPI_DATA_ADDR, 0x87654321) + yield + print(hex((yield from bus.read(SPI_DATA_ADDR)))) + yield + + return + for cpol, cpha, lsb, clk in product( + (0, 1), (0, 1), (0, 1), (0, 1)): + yield from bus.write(SPI_CONFIG_ADDR, + cpol*SPI_CLK_POLARITY | cpha*SPI_CLK_PHASE | + lsb*SPI_LSB_FIRST | SPI_CLK_LOAD(clk)) + for wlen, rlen, wdata in product((0, 8, 32), (0, 8, 32), + (0, 0xffffffff, 0xdeadbeef)): + yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | + SPI_WRITE_LENGTH(wlen) | + SPI_READ_LENGTH(rlen)) + yield from bus.write(SPI_DATA_ADDR, wdata) + rdata = yield from bus.read(SPI_DATA_ADDR) + len = (wlen + rlen) % 32 + mask = (1 << len) - 1 + if lsb: + shift = (wlen + rlen) % 32 + else: + shift = 0 + a = (wdata >> wshift) & wmask + b = (rdata >> rshift) & rmask + if a != b: + print("ERROR", end=" ") + print(cpol, cpha, lsb, clk, wlen, rlen, + hex(wdata), hex(rdata), hex(a), hex(b)) + class _TestPads: @@ -104,6 +324,22 @@ class _TestPads: if __name__ == "__main__": + from migen.fhdl.specials import Tristate + + class T(Module): + def __init__(self, t): + oe = Signal() + self.comb += [ + t.target.eq(t.o), + oe.eq(t.oe), + t.i.eq(t.o), + ] + Tristate.lower = staticmethod(lambda dr: T(dr)) + + from migen.fhdl.verilog import convert pads = _TestPads() dut = SPIMaster(pads) + dut.comb += pads.miso.eq(pads.mosi) + #print(convert(dut)) + run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From 3b6999ac06ecc09b9587e6b5c464a4fb880b6211 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 20:40:06 +0100 Subject: [PATCH 05/52] gateware.spi: refactor, sim verified --- artiq/gateware/spi.py | 454 ++++++++++++++++++++++++++---------------- 1 file changed, 283 insertions(+), 171 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index d0df75509..86ae26562 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -1,11 +1,158 @@ from itertools import product from migen import * -from migen.genlib.fsm import * -from migen.genlib.misc import WaitTimer +from migen.genlib.fsm import FSM, NextState from misoc.interconnect import wishbone +class SPIClockGen(Module): + def __init__(self, width): + self.load = Signal(width) + self.bias = Signal() # bias this clock phase to longer times + self.edge = Signal() + self.clk = Signal(reset=1) + + cnt = Signal.like(self.load) + self.comb += [ + self.edge.eq(cnt == 0), + ] + self.sync += [ + cnt.eq(cnt - 1), + If(self.edge, + cnt.eq(self.load[1:] + + (self.load[0] & self.bias)), + self.clk.eq(~self.clk), + ) + ] + + +class SPIRegister(Module): + def __init__(self, width): + self.data = Signal(width) + self.o = Signal() + self.i = Signal() + self.lsb = Signal() + self.shift = Signal() + self.sample = Signal() + + self.comb += [ + self.o.eq(Mux(self.lsb, self.data[0], self.data[-1])), + ] + self.sync += [ + If(self.shift, + If(self.lsb, + self.data[:-1].eq(self.data[1:]), + ).Else( + self.data[1:].eq(self.data[:-1]), + ) + ), + If(self.sample, + If(self.lsb, + self.data[-1].eq(self.i), + ).Else( + self.data[0].eq(self.i), + ) + ) + ] + + +class SPIBitCounter(Module): + def __init__(self, width): + self.n_read = Signal(width) + self.n_write = Signal(width) + self.read = Signal() + self.write = Signal() + self.done = Signal() + + self.comb += [ + self.write.eq(self.n_write != 0), + self.read.eq(self.n_read != 0), + self.done.eq(~(self.write | self.read)), + ] + self.sync += [ + If(self.write, + self.n_write.eq(self.n_write - 1), + ).Elif(self.read, + self.n_read.eq(self.n_read - 1), + ) + ] + + +class SPIMachine(Module): + def __init__(self, data_width, clock_width, bits_width): + ce = CEInserter() + self.submodules.cg = ce(SPIClockGen(clock_width)) + self.submodules.reg = ce(SPIRegister(data_width)) + self.submodules.bits = ce(SPIBitCounter(bits_width)) + self.div_write = Signal.like(self.cg.load) + self.div_read = Signal.like(self.cg.load) + self.clk_phase = Signal() + self.start = Signal() + self.cs = Signal() + self.oe = Signal() + self.done = Signal() + + # # # + + fsm = CEInserter()(FSM("IDLE")) + self.submodules += fsm + + fsm.act("IDLE", + If(self.start, + If(self.clk_phase, + NextState("WAIT"), + ).Else( + NextState("SETUP"), + ) + ) + ) + fsm.act("SETUP", + self.reg.sample.eq(1), + NextState("HOLD"), + ) + fsm.act("HOLD", + If(self.bits.done & ~self.start, + If(self.clk_phase, + NextState("IDLE"), + ).Else( + NextState("WAIT"), + ) + ).Else( + self.reg.shift.eq(1), + NextState("SETUP"), + ) + ) + fsm.act("WAIT", + If(self.bits.done, + NextState("IDLE"), + ).Else( + NextState("SETUP"), + ) + ) + + write0 = Signal() + self.sync += [ + If(self.cg.edge & self.reg.shift, + write0.eq(self.bits.write), + ) + ] + self.comb += [ + self.cg.ce.eq(self.start | self.cs | ~self.cg.edge), + If(self.bits.write | ~self.bits.read, + self.cg.load.eq(self.div_write), + ).Else( + self.cg.load.eq(self.div_read), + ), + self.cg.bias.eq(fsm.before_entering("SETUP")), + fsm.ce.eq(self.cg.edge), + self.cs.eq(~fsm.ongoing("IDLE")), + self.reg.ce.eq(self.cg.edge), + self.bits.ce.eq(self.cg.edge & self.reg.sample), + self.done.eq(self.cg.edge & self.bits.done & fsm.ongoing("HOLD")), + self.oe.eq(write0 | self.bits.write), + ] + + class SPIMaster(Module): """SPI Master. @@ -15,50 +162,61 @@ class SPIMaster(Module): * If there is a miso wire in pads, the input and output can be done with two signals (a.k.a. 4-wire SPI), else mosi must be used for both output and input (a.k.a. 3-wire SPI) and config.half_duplex - needs to be set. - * Every transfer consists of a 0-M bit write followed by a 0-M - bit read. - * cs_n is always asserted at the beginning and deasserted - at the end of the transfer. + must to be set when reading data is desired. + * Every transfer consists of a write_length 0-M bit write followed + by a read_length 0-M bit read. + * cs_n is asserted at the beginning and deasserted at the end of the + transfer if there is no other transfer pending. * cs_n handling is agnostic to whether it is one-hot or decoded somewhere downstream. If it is decoded, "cs_n all deasserted" should be handled accordingly (no slave selected). If it is one-hot, asserting multiple slaves should only be attempted - if miso is either not connected between slaves or open collector. - cs can also be handled independently through other means. + if miso is either not connected between slaves, or open collector, + or correctly multiplexed externally. * If config.cs_polarity == 0 (cs active low, the default), "cs_n all deasserted" means "all cs_n bits high". + * cs is not mandatory in pads. Framing and chip selection can also + be handled independently through other means. * The first bit output on mosi is always the MSB/LSB (depending on config.lsb_first) of the data register, independent of xfer.write_len. The last bit input from miso always ends up in the LSB/MSB (respectively) of the data register, independent of read_len. * For 4-wire SPI only the sum of read_len and write_len matters. The - behavior is the same no matter how the transfer length is divided - between the two. For 3-wire SPI, the direction of mosi/miso is - switched from output to input after write_len cycles, at the - "output" clk edge corresponding to bit write_len + 1 of the transfer. + behavior is the same no matter how the total transfer length is + divided between the two. For 3-wire SPI, the direction of mosi/miso + is switched from output to input after write_len cycles, at the + "shift_out" clk edge corresponding to bit write_length + 1 of the + transfer. * Data output on mosi in 4-wire SPI during the read cycles is what is found in the data register at the time. Data in the data register outside the least/most (depending - on config.lsb_first) significant read_len bits is what is + on config.lsb_first) significant read_length bits is what is seen on miso during the write cycles. - * When the transfer is complete the wishbone transaction is ack-ed. - * Input data from the last transaction can be read from the data - register at any time. + * The SPI data register is double-buffered: once a transfer completes, + the previous transfer's read data is available in the data register + and new write data can be written, queuing a new transfer. Transfers + submitted this way are chained and executed without deasserting cs. + * A wishbone transaction is ack-ed when the transfer has been written + to the intermediate buffer. It will be started when there are no + other transactions being executed. Transaction Sequence: * If desired, write the config register to set up the core. * If desired, write the xfer register to change lengths and cs_n. * Write the data register (also for zero-length writes), - writing triggers the transfer and when the transfer is complete the - write is ack-ed. + writing triggers the transfer and when the transfer is accepted to + the inermediate buffer, the write is ack-ed. * If desired, read the data register. + * If desired write xfer and data for the next, chained, transfer Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) + 1 active: cs/transfer active + 1 pending: transfer pending in intermediate buffer, bus writes will + block 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level for clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) @@ -68,11 +226,11 @@ class SPIMaster(Module): (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) - 10 undefined - 16 clk_load: clock load value to divide from this module's clock - to the SPI write clk clk pulses are asymmetric - if a divider is odd, favoring longer setup over hold times. - clk/spi_clk == clk_load + 2 (reset=0) + 12 div_write: counter load value to divide this module's clock + to the SPI write clk. clk pulses are asymmetric + if the value is odd, favoring longer setup over hold times. + f_clk/f_spi_write == div_write + 2 (reset=0) + 12 div_read: ditto for the read clock xfer (address 1): 16 cs: active high bit mask of chip selects to assert @@ -89,50 +247,18 @@ class SPIMaster(Module): ### - # State machine - wb_we = Signal() - start = Signal() - active = Signal() - - fsm = FSM("IDLE") - self.submodules += fsm - - fsm.act("IDLE", - If(bus.cyc & bus.stb, - NextState("ACK"), - If(bus.we, - wb_we.eq(1), - If(bus.adr == 0, # data register - NextState("START"), - ) - ) - ) - ) - fsm.act("START", - start.eq(1), - NextState("ACTIVE"), - ) - fsm.act("ACTIVE", - If(~active, - bus.ack.eq(1), - NextState("IDLE"), - ) - ) - fsm.act("ACK", - bus.ack.eq(1), - NextState("IDLE"), - ) - # Wishbone config = Record([ ("offline", 1), + ("active", 1), + ("pending", 1), ("cs_polarity", 1), ("clk_polarity", 1), ("clk_phase", 1), ("lsb_first", 1), ("half_duplex", 1), - ("padding", 10), - ("clk_load", 16), + ("div_write", 12), + ("div_read", 12), ]) config.offline.reset = 1 assert len(config) <= len(bus.dat_w) @@ -144,101 +270,96 @@ class SPIMaster(Module): ]) assert len(xfer) <= len(bus.dat_w) - data = Signal.like(bus.dat_w) - - wb_data = Array([data, xfer.raw_bits(), config.raw_bits()])[bus.adr] - self.comb += bus.dat_r.eq(wb_data) - self.sync += If(wb_we, wb_data.eq(bus.dat_w)) - # SPI - write_count = Signal.like(xfer.write_length) - read_count = Signal.like(xfer.read_length) - clk_count = Signal.like(config.clk_load) - clk = Signal(reset=1) # idle high - phase = Signal() - edge = Signal() - write = Signal() - read = Signal() - miso = Signal() - miso_i = Signal() - mosi_o = Signal() + spi = SPIMachine(data_width, clock_width=len(config.div_read), + bits_width=len(xfer.read_length)) + self.submodules += spi + + wb_we = Signal() + pending = Signal() + cs = Signal.like(xfer.cs) + data_read = Signal.like(spi.reg.data) + data_write = Signal.like(spi.reg.data) self.comb += [ - phase.eq(clk ^ config.clk_phase), - edge.eq(active & (clk_count == 0)), - write.eq(write_count != 0), - read.eq(read_count != 0), + wb_we.eq(bus.cyc & bus.stb & bus.we & (~pending | spi.done)), + bus.dat_r.eq( + Array([data_read, xfer.raw_bits(), config.raw_bits() + ])[bus.adr]), + spi.start.eq(pending & (~spi.cs | spi.done)), + spi.clk_phase.eq(config.clk_phase), + spi.reg.lsb.eq(config.lsb_first), + spi.div_write.eq(config.div_write), + spi.div_read.eq(config.div_read), ] - self.sync += [ - If(start, - write_count.eq(xfer.write_length), - read_count.eq(xfer.read_length), - active.eq(1), + bus.ack.eq(~bus.we | ~pending | spi.done), + If(wb_we, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w) ), - If(active, - clk_count.eq(clk_count - 1), + config.active.eq(spi.cs), + config.pending.eq(pending), + If(spi.done, + data_read.eq(spi.reg.data), ), - If(start | edge, - # setup time passes during phase 0 - # use the lsb to bias that time to favor longer setup times - clk_count.eq(config.clk_load[1:] + - (config.clk_load[0] & phase)), - clk.eq(~clk), # idle high - If(phase, - data.eq(Mux(config.lsb_first, - Cat(data[1:], miso), - Cat(miso, data[:-1]))), - mosi_o.eq(Mux(config.lsb_first, data[0], data[-1])), - If(write, - write_count.eq(write_count - 1), - ), - ).Else( - miso.eq(miso_i), - If(~write & read, - read_count.eq(read_count - 1), - ), - ), + If(spi.start, + cs.eq(xfer.cs), + spi.bits.n_write.eq(xfer.write_length), + spi.bits.n_read.eq(xfer.read_length), + spi.reg.data.eq(data_write), + pending.eq(0), ), - If(~clk & edge & ~write & ~read, # always from low clk - active.eq(0), + If(wb_we & (bus.adr == 0), # data register + pending.eq(1), ), ] # I/O - cs_n_t = TSTriple(len(pads.cs_n)) - self.specials += cs_n_t.get_tristate(pads.cs_n) + if hasattr(pads, "cs_n"): + cs_n_t = TSTriple(len(pads.cs_n)) + self.specials += cs_n_t.get_tristate(pads.cs_n) + self.comb += [ + cs_n_t.oe.eq(~config.offline), + cs_n_t.o.eq((cs & Replicate(spi.cs, len(cs))) ^ + Replicate(~config.cs_polarity, len(cs))), + ] + clk_t = TSTriple() self.specials += clk_t.get_tristate(pads.clk) mosi_t = TSTriple() self.specials += mosi_t.get_tristate(pads.mosi) self.comb += [ - cs_n_t.oe.eq(~config.offline), clk_t.oe.eq(~config.offline), - mosi_t.oe.eq(~config.offline & (write | ~config.half_duplex)), - cs_n_t.o.eq((xfer.cs & Replicate(active, len(xfer.cs))) ^ - Replicate(~config.cs_polarity, len(xfer.cs))), - clk_t.o.eq((clk & active) ^ config.clk_polarity), - miso_i.eq(Mux(config.half_duplex, mosi_t.i, - getattr(pads, "miso", mosi_t.i))), - mosi_t.o.eq(mosi_o), + mosi_t.oe.eq(~config.offline & spi.cs & + (spi.oe | ~config.half_duplex)), + clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i, + getattr(pads, "miso", mosi_t.i))), + mosi_t.o.eq(spi.reg.o), ] -SPI_CONFIG_ADDR = 2 -SPI_XFER_ADDR = 1 -SPI_DATA_ADDR = 0 -SPI_OFFLINE = 1 << 0 -SPI_CS_POLARITY = 1 << 1 -SPI_CLK_POLARITY = 1 << 2 -SPI_CLK_PHASE = 1 << 3 -SPI_LSB_FIRST = 1 << 4 -SPI_HALF_DUPLEX = 1 << 5 +SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) +( + SPI_OFFLINE, + SPI_ACTIVE, + SPI_PENDING, + SPI_CS_POLARITY, + SPI_CLK_POLARITY, + SPI_CLK_PHASE, + SPI_LSB_FIRST, + SPI_HALF_DUPLEX, +) = (1 << i for i in range(8)) -def SPI_CLK_LOAD(i): - return i << 16 +def SPI_DIV_WRITE(i): + return i << 8 + + +def SPI_DIV_READ(i): + return i << 20 def SPI_CS(i): @@ -253,53 +374,44 @@ def SPI_READ_LENGTH(i): return i << 24 -def _test_gen(bus): - yield from bus.write(SPI_CONFIG_ADDR, - 1*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | - 1*SPI_HALF_DUPLEX | SPI_CLK_LOAD(3)) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | - SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(0)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x90000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(4) | SPI_READ_LENGTH(4)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x81000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(0) | SPI_READ_LENGTH(4)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x90000000) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) - yield - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00010) | - SPI_WRITE_LENGTH(32) | SPI_READ_LENGTH(0)) - yield - yield from bus.write(SPI_DATA_ADDR, 0x87654321) - yield - print(hex((yield from bus.read(SPI_DATA_ADDR)))) +def _test_xfer(bus, cs, wlen, rlen, wdata): + yield from bus.write(SPI_XFER_ADDR, SPI_CS(cs) | + SPI_WRITE_LENGTH(wlen) | SPI_READ_LENGTH(rlen)) + yield from bus.write(SPI_DATA_ADDR, wdata) yield + +def _test_read(bus, sync=SPI_ACTIVE | SPI_PENDING): + while (yield from bus.read(SPI_CONFIG_ADDR)) & sync: + pass + return (yield from bus.read(SPI_DATA_ADDR)) + + +def _test_gen(bus): + yield from bus.write(SPI_CONFIG_ADDR, + 0*SPI_CLK_PHASE | 0*SPI_LSB_FIRST | + 1*SPI_HALF_DUPLEX | + SPI_DIV_WRITE(3) | SPI_DIV_READ(5)) + yield from _test_xfer(bus, 0b01, 4, 0, 0x90000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b10, 0, 4, 0x90000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b11, 4, 4, 0x81000000) + print(hex((yield from _test_read(bus)))) + yield from _test_xfer(bus, 0b01, 8, 32, 0x87654321) + yield from _test_xfer(bus, 0b01, 0, 32, 0x12345678) + print(hex((yield from _test_read(bus, SPI_PENDING)))) + print(hex((yield from _test_read(bus, SPI_ACTIVE)))) return for cpol, cpha, lsb, clk in product( (0, 1), (0, 1), (0, 1), (0, 1)): yield from bus.write(SPI_CONFIG_ADDR, cpol*SPI_CLK_POLARITY | cpha*SPI_CLK_PHASE | - lsb*SPI_LSB_FIRST | SPI_CLK_LOAD(clk)) + lsb*SPI_LSB_FIRST | SPI_DIV_WRITE(clk) | + SPI_DIV_READ(clk)) for wlen, rlen, wdata in product((0, 8, 32), (0, 8, 32), (0, 0xffffffff, 0xdeadbeef)): - yield from bus.write(SPI_XFER_ADDR, SPI_CS(0b00001) | - SPI_WRITE_LENGTH(wlen) | - SPI_READ_LENGTH(rlen)) - yield from bus.write(SPI_DATA_ADDR, wdata) - rdata = yield from bus.read(SPI_DATA_ADDR) + rdata = (yield from _test_xfer(bus, 0b1, wlen, rlen, wdata, True)) len = (wlen + rlen) % 32 mask = (1 << len) - 1 if lsb: @@ -336,10 +448,10 @@ if __name__ == "__main__": ] Tristate.lower = staticmethod(lambda dr: T(dr)) - from migen.fhdl.verilog import convert pads = _TestPads() dut = SPIMaster(pads) dut.comb += pads.miso.eq(pads.mosi) - #print(convert(dut)) + # from migen.fhdl.verilog import convert + # print(convert(dut)) run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From f8732acece7cd5d980a40b56768960e7e5a94b62 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 21:06:20 +0100 Subject: [PATCH 06/52] rtio.spi: drop unused argument --- artiq/gateware/rtio/phy/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/spi.py b/artiq/gateware/rtio/phy/spi.py index e72f602c2..92ab7a548 100644 --- a/artiq/gateware/rtio/phy/spi.py +++ b/artiq/gateware/rtio/phy/spi.py @@ -5,7 +5,7 @@ from artiq.gateware.rtio.phy.wishbone import RT2WB class SPIMaster(Module): - def __init__(self, pads, onehot=False, **kwargs): + def __init__(self, pads, **kwargs): self.submodules._ll = ClockDomainsRenamer("rio")( SPIMasterWB(pads, **kwargs)) self.submodules._rt2wb = RT2WB(2, self._ll.bus) From 312e09150e075a86c833c1fc49bb7f8406a192d0 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 21:17:53 +0100 Subject: [PATCH 07/52] kc705/clock: add spi bus for dac on ams101 --- artiq/gateware/targets/kc705.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 719876220..ba3751a27 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -19,7 +19,7 @@ from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1, nist_clock, nist_qc2 -from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi from artiq import __artiq_dir__ as artiq_dir from artiq import __version__ as artiq_version @@ -237,6 +237,22 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) + self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) + ldac_n = self.platform.request("xadc_gpio", 0) + phy = ttl_simple.Output(ldac_n) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + spi_pins = Module() + spi_pins.clk = self.platform.request("xadc_gpio", 1) + spi_pins.mosi = self.platform.request("xadc_gpio", 2) + spi_pins.cs_n = self.platform.request("xadc_gpio", 3) + phy = spi.SPIMaster(spi_pins) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=4, ififo_depth=4)) + self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 11 self.config["DDS_AD9914"] = True From d5893d15fbc383c8fbe7f743218f49d3efd227b9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sun, 28 Feb 2016 22:41:17 +0100 Subject: [PATCH 08/52] gateware.kc705: make xadc/ams an extension header --- artiq/gateware/targets/kc705.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index ba3751a27..3cc948aab 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -80,6 +80,18 @@ class _RTIOCRG(Module, AutoCSR): ] +_ams101_dac = [ + ("ams101_dac", 0, + + Subsignal("ldac", Pins("XADC:GPIO0")), + Subsignal("clk", Pins("XADC:GPIO1")), + Subsignal("mosi", Pins("XADC:GPIO2")), + Subsignal("cs_n", Pins("XADC:GPIO3")), + IOStandard("LVTTL") + ) +] + + class _NIST_Ions(MiniSoC, AMPSoC): csr_map = { "rtio": None, # mapped on Wishbone instead @@ -115,6 +127,8 @@ class _NIST_Ions(MiniSoC, AMPSoC): self.platform.request("user_led", 0), self.platform.request("user_led", 1))) + self.platform.add_extension(_ams101_dac) + def add_rtio(self, rtio_channels): self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) self.submodules.rtio = rtio.RTIO(rtio_channels) @@ -237,19 +251,15 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) - self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) - ldac_n = self.platform.request("xadc_gpio", 0) - phy = ttl_simple.Output(ldac_n) + spi_pins = self.platform.request("ams101_dac", 0) + phy = ttl_simple.Output(spi_pins.ldac) self.submodules += phy + self.config["RTIO_SPI_LDAC_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) - spi_pins = Module() - spi_pins.clk = self.platform.request("xadc_gpio", 1) - spi_pins.mosi = self.platform.request("xadc_gpio", 2) - spi_pins.cs_n = self.platform.request("xadc_gpio", 3) phy = spi.SPIMaster(spi_pins) self.submodules += phy + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) From 9a881aa430c18b141ceb9ee804f765d953465be0 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:36:18 +0100 Subject: [PATCH 09/52] gateware.spi: simpler clk bias --- artiq/gateware/spi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 86ae26562..ebfa9a8b0 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -20,7 +20,7 @@ class SPIClockGen(Module): cnt.eq(cnt - 1), If(self.edge, cnt.eq(self.load[1:] + - (self.load[0] & self.bias)), + (self.load[0] & (self.clk ^ self.bias))), self.clk.eq(~self.clk), ) ] @@ -143,7 +143,7 @@ class SPIMachine(Module): ).Else( self.cg.load.eq(self.div_read), ), - self.cg.bias.eq(fsm.before_entering("SETUP")), + self.cg.bias.eq(self.clk_phase), fsm.ce.eq(self.cg.edge), self.cs.eq(~fsm.ongoing("IDLE")), self.reg.ce.eq(self.cg.edge), From 8d7e92ebaebba8a756b0dc5c4af082e0069a1a13 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:37:00 +0100 Subject: [PATCH 10/52] pipistrello: set RTIO_SPI_CHANNEL --- artiq/gateware/targets/pipistrello.py | 1 + 1 file changed, 1 insertion(+) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index 5967ccdd7..d7e177517 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -200,6 +200,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy + self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) From d63a63531a079fc8be12023ac60f6ed1745c2ac1 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:38:36 +0100 Subject: [PATCH 11/52] spi: add runtime support --- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 4 ++++ artiq/runtime/spi.c | 50 ++++++++++++++++++++++++++++++++++++++++ artiq/runtime/spi.h | 20 ++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 artiq/runtime/spi.c create mode 100644 artiq/runtime/spi.h diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 6c13b631b..46e3aedb9 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o + bridge.o rtio.o ttl.o dds.o spi.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index d5825f746..bcdd7d028 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,6 +15,7 @@ #include "artiq_personality.h" #include "ttl.h" #include "dds.h" +#include "spi.h" #include "rtio.h" double round(double x); @@ -121,6 +122,9 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, + {"spi_write", &spi_write}, + {"spi_read", &spi_read}, + {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c new file mode 100644 index 000000000..a72e3a4f8 --- /dev/null +++ b/artiq/runtime/spi.c @@ -0,0 +1,50 @@ +#include +#include + +#include "artiq_personality.h" +#include "rtio.h" +#include "log.h" +#include "spi.h" + + +#define DURATION_WRITE (1 << CONFIG_RTIO_FINE_TS_WIDTH) + +void spi_write(long long int timestamp, int channel, int addr, + unsigned int data) +{ + rtio_chan_sel_write(CONFIG_RTIO_SPI_CHANNEL + channel); + rtio_o_address_write(addr); + rtio_o_data_write(data); + rtio_o_timestamp_write(timestamp); + rtio_write_and_process_status(timestamp, channel); +} + + +unsigned int spi_read(long long int timestamp, int channel, int addr) +{ + int status; + long long int time_limit = timestamp + DURATION_WRITE; + unsigned int r; + + spi_write(timestamp, channel, addr | SPI_WB_READ, 0); + + while((status = rtio_i_status_read())) { + if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO overflow at channel {0}", + channel, 0, 0); + } + if(rtio_get_counter() >= time_limit) { + /* check empty flag again to prevent race condition. + * now we are sure that the time limit has been exceeded. + */ + if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) + return -1; + } + /* input FIFO is empty - keep waiting */ + } + r = rtio_i_data_read(); + rtio_i_re_write(1); + return r; +} diff --git a/artiq/runtime/spi.h b/artiq/runtime/spi.h new file mode 100644 index 000000000..6a5ba08f1 --- /dev/null +++ b/artiq/runtime/spi.h @@ -0,0 +1,20 @@ +#ifndef __SPI_H +#define __SPI_H + +#include +#include +#include + +#define SPI_ADDR_DATA 0 +#define SPI_ADDR_XFER 1 +#define SPI_ADDR_CONFIG 2 +#define SPI_WB_READ (1 << 2) + +#define SPI_XFER_CS(x) (x) +#define SPI_XFER_WRITE_LENGTH(x) ((x) << 16) +#define SPI_XFER_READ_LENGTH(x) ((x) << 24) + +void spi_write(long long int timestamp, int channel, int address, unsigned int data); +unsigned int spi_read(long long int timestamp, int channel, int address); + +#endif /* __SPI_H */ From 8b2b27845761109cca45f0f18029e16db0199669 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 00:38:50 +0100 Subject: [PATCH 12/52] spi: add coredevice support --- artiq/coredevice/spi.py | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 artiq/coredevice/spi.py diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py new file mode 100644 index 000000000..6265ff3b6 --- /dev/null +++ b/artiq/coredevice/spi.py @@ -0,0 +1,88 @@ +from artiq.language.core import * +from artiq.language.types import * + + +@syscall +def spi_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def spi_read(time_mu: TInt64, channel: TInt32, addr: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") + + +SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) +( + SPI_OFFLINE, + SPI_ACTIVE, + SPI_PENDING, + SPI_CS_POLARITY, + SPI_CLK_POLARITY, + SPI_CLK_PHASE, + SPI_LSB_FIRST, + SPI_HALF_DUPLEX, +) = (1 << i for i in range(8)) + + +class SPIMaster: + """Core device Serial Peripheral Interface (SPI) bus master. + + :param ref_period: clock period of the SPI core. + :param channel: channel number of the SPI bus to control. + """ + def __init__(self, dmgr, ref_period, channel): + self.core = dmgr.get("core") + self.ref_period_mu = int(seconds_to_mu(ref_period, self.core), 64) + self.channel = channel + self.write_div = 0 + self.read_div = 0 + # a full transfer takes prep_mu + xfer_mu + self.prep_mu = int(0, 64) + # chaned transfers can happen every xfer_mu + self.xfer_mu = int(0, 64) + # The second transfer of a chain be written ref_period_mu + # after the first. Read data is available every xfer_mu starting + # a bit before prep_mu + xfer_mu. + + @portable + def predict_xfer_mu(self, write_length, read_length): + # this is only the intrinsic bit cycle duration + return self.ref_period_mu*( + write_length*self.write_div + + read_length*self.read_div) + + @portable + def predict_prep_mu(self, write_div): + return self.ref_period_mu*( + 2 + # intermediate transfers + # one write_div for the wait+idle cycle + self.write_div) + + @kernel + def set_config(self, flags=0, write_div=6, read_div=6): + self.write_div = write_div + self.read_div = read_div + self.prep_mu = self.predict_prep_mu(write_div) + spi_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 8) | ((read_div - 2) << 20)) + delay_mu(self.ref_period_mu) + + @kernel + def set_xfer(self, chip_select=0, write_length=0, read_length=0): + self.xfer_mu = self.predict_xfer_mu(write_length, read_length) + spi_write(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) + delay_mu(self.ref_period_mu) + + @kernel + def write(self, data): + spi_write(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(self.prep_mu + self.xfer_mu) + + @kernel + def read(self): + r = spi_read(now_mu(), self.channel, SPI_DATA_ADDR) + delay_mu(self.ref_period_mu) + return r From 9a1d6a51a49a05d8bf5bd0642bb19cb58cffecc1 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 01:51:33 +0100 Subject: [PATCH 13/52] gateware.spi: shorten counters --- artiq/gateware/spi.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index ebfa9a8b0..8a758d003 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -226,16 +226,18 @@ class SPIMaster(Module): (1, 1): idle high, output on falling, input on rising 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) - 12 div_write: counter load value to divide this module's clock - to the SPI write clk. clk pulses are asymmetric - if the value is odd, favoring longer setup over hold times. - f_clk/f_spi_write == div_write + 2 (reset=0) - 12 div_read: ditto for the read clock + 8 undefined + 8 div_write: counter load value to divide this module's clock + to the SPI write clk. + f_clk/f_spi_write == 2*(div_write + 1) (reset=0) + 8 div_read: ditto for the read clock xfer (address 1): 16 cs: active high bit mask of chip selects to assert - 8 write_len: 0-M bits - 8 read_len: 0-M bits + 6 write_len: 0-M bits + 2 undefined + 6 read_len: 0-M bits + 2 undefined data (address 0): M write/read data @@ -257,16 +259,19 @@ class SPIMaster(Module): ("clk_phase", 1), ("lsb_first", 1), ("half_duplex", 1), - ("div_write", 12), - ("div_read", 12), + ("padding", 8), + ("div_write", 8), + ("div_read", 8), ]) config.offline.reset = 1 assert len(config) <= len(bus.dat_w) xfer = Record([ ("cs", 16), - ("write_length", 8), - ("read_length", 8), + ("write_length", 6), + ("padding0", 2), + ("read_length", 6), + ("padding1", 2), ]) assert len(xfer) <= len(bus.dat_w) @@ -355,11 +360,11 @@ SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) def SPI_DIV_WRITE(i): - return i << 8 + return i << 16 def SPI_DIV_READ(i): - return i << 20 + return i << 24 def SPI_CS(i): From 5480099f1bbddb848c389d7a0395973ed6469013 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 02:28:19 +0100 Subject: [PATCH 14/52] gateware.spi: rewrite counter bias for timing --- artiq/gateware/spi.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 8a758d003..dabb89a40 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -13,14 +13,21 @@ class SPIClockGen(Module): self.clk = Signal(reset=1) cnt = Signal.like(self.load) + bias = Signal() + zero = Signal() self.comb += [ - self.edge.eq(cnt == 0), + zero.eq(cnt == 0), + self.edge.eq(zero & ~bias), ] self.sync += [ - cnt.eq(cnt - 1), + If(zero, + bias.eq(0), + ).Else( + cnt.eq(cnt - 1), + ), If(self.edge, - cnt.eq(self.load[1:] + - (self.load[0] & (self.clk ^ self.bias))), + cnt.eq(self.load[1:]), + bias.eq(self.load[0] & (self.clk ^ self.bias)), self.clk.eq(~self.clk), ) ] From ad34927b0a7b12e81ee33c757be5d44701f48c16 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:35:49 +0100 Subject: [PATCH 15/52] spi: RTIO_SPI_CHANNEL -> RTIO_FIRST_SPI_CHANNEL --- artiq/gateware/targets/kc705.py | 2 +- artiq/gateware/targets/pipistrello.py | 2 +- artiq/runtime/spi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 3cc948aab..42b5af981 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -259,7 +259,7 @@ class NIST_CLOCK(_NIST_Ions): phy = spi.SPIMaster(spi_pins) self.submodules += phy - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) diff --git a/artiq/gateware/targets/pipistrello.py b/artiq/gateware/targets/pipistrello.py index d7e177517..ceb39d83f 100755 --- a/artiq/gateware/targets/pipistrello.py +++ b/artiq/gateware/targets/pipistrello.py @@ -200,7 +200,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd spi_pins.cs_n = pmod.d[3:] phy = spi.SPIMaster(spi_pins) self.submodules += phy - self.config["RTIO_SPI_CHANNEL"] = len(rtio_channels) + self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c index a72e3a4f8..20d694454 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/spi.c @@ -12,7 +12,7 @@ void spi_write(long long int timestamp, int channel, int addr, unsigned int data) { - rtio_chan_sel_write(CONFIG_RTIO_SPI_CHANNEL + channel); + rtio_chan_sel_write(CONFIG_RTIO_FIRST_SPI_CHANNEL + channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(timestamp); From 948fefa69a3cc8348a4cb42763aac8edcd9e4400 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:48:29 +0100 Subject: [PATCH 16/52] gateware.spi: style --- artiq/gateware/spi.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index dabb89a40..f485d345a 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -282,10 +282,9 @@ class SPIMaster(Module): ]) assert len(xfer) <= len(bus.dat_w) - # SPI - spi = SPIMachine(data_width, clock_width=len(config.div_read), - bits_width=len(xfer.read_length)) - self.submodules += spi + self.submodules.spi = spi = SPIMachine( + data_width, clock_width=len(config.div_read), + bits_width=len(xfer.read_length)) wb_we = Signal() pending = Signal() @@ -344,12 +343,12 @@ class SPIMaster(Module): self.comb += [ clk_t.oe.eq(~config.offline), + clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), mosi_t.oe.eq(~config.offline & spi.cs & (spi.oe | ~config.half_duplex)), - clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + mosi_t.o.eq(spi.reg.o), spi.reg.i.eq(Mux(config.half_duplex, mosi_t.i, getattr(pads, "miso", mosi_t.i))), - mosi_t.o.eq(spi.reg.o), ] @@ -438,32 +437,31 @@ def _test_gen(bus): hex(wdata), hex(rdata), hex(a), hex(b)) - class _TestPads: def __init__(self): - self.cs_n = Signal(3) + self.cs_n = Signal(2) self.clk = Signal() self.mosi = Signal() self.miso = Signal() +class _TestTristate(Module): + def __init__(self, t): + oe = Signal() + self.comb += [ + t.target.eq(t.o), + oe.eq(t.oe), + t.i.eq(t.o), + ] + if __name__ == "__main__": from migen.fhdl.specials import Tristate - class T(Module): - def __init__(self, t): - oe = Signal() - self.comb += [ - t.target.eq(t.o), - oe.eq(t.oe), - t.i.eq(t.o), - ] - Tristate.lower = staticmethod(lambda dr: T(dr)) - pads = _TestPads() dut = SPIMaster(pads) dut.comb += pads.miso.eq(pads.mosi) # from migen.fhdl.verilog import convert # print(convert(dut)) + Tristate.lower = _TestTristate run_simulation(dut, _test_gen(dut.bus), vcd_name="spi_master.vcd") From aeae565d35c02d70c05a44a70c3aef41ccc16930 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 11:53:36 +0100 Subject: [PATCH 17/52] runtime/spi: don't apply channel offset --- artiq/runtime/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/spi.c b/artiq/runtime/spi.c index 20d694454..17e86dea7 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/spi.c @@ -12,7 +12,7 @@ void spi_write(long long int timestamp, int channel, int addr, unsigned int data) { - rtio_chan_sel_write(CONFIG_RTIO_FIRST_SPI_CHANNEL + channel); + rtio_chan_sel_write(channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(timestamp); From eb01b0bfee1454f26baf441fd0f24c65b6715378 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 12:41:30 +0100 Subject: [PATCH 18/52] gateware.spi: cleanup doc --- artiq/gateware/spi.py | 56 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index f485d345a..328419559 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -166,10 +166,6 @@ class SPIMaster(Module): Notes: * M = 32 is the data width (width of the data register, maximum write bits, maximum read bits) - * If there is a miso wire in pads, the input and output can be done - with two signals (a.k.a. 4-wire SPI), else mosi must be used for - both output and input (a.k.a. 3-wire SPI) and config.half_duplex - must to be set when reading data is desired. * Every transfer consists of a write_length 0-M bit write followed by a read_length 0-M bit read. * cs_n is asserted at the beginning and deasserted at the end of the @@ -184,29 +180,37 @@ class SPIMaster(Module): "cs_n all deasserted" means "all cs_n bits high". * cs is not mandatory in pads. Framing and chip selection can also be handled independently through other means. + * If there is a miso wire in pads, the input and output can be done + with two signals (a.k.a. 4-wire SPI), else mosi must be used for + both output and input (a.k.a. 3-wire SPI) and config.half_duplex + must to be set when reading data is desired. + * For 4-wire SPI only the sum of read_length and write_length matters. + The behavior is the same no matter how the total transfer length is + divided between the two. For 3-wire SPI, the direction of mosi/miso + is switched from output to input after write_len cycles, at the + "shift_out" clk edge corresponding to bit write_length + 1 of the + transfer. * The first bit output on mosi is always the MSB/LSB (depending on config.lsb_first) of the data register, independent of xfer.write_len. The last bit input from miso always ends up in the LSB/MSB (respectively) of the data register, independent of read_len. - * For 4-wire SPI only the sum of read_len and write_len matters. The - behavior is the same no matter how the total transfer length is - divided between the two. For 3-wire SPI, the direction of mosi/miso - is switched from output to input after write_len cycles, at the - "shift_out" clk edge corresponding to bit write_length + 1 of the - transfer. * Data output on mosi in 4-wire SPI during the read cycles is what is found in the data register at the time. Data in the data register outside the least/most (depending on config.lsb_first) significant read_length bits is what is seen on miso during the write cycles. - * The SPI data register is double-buffered: once a transfer completes, - the previous transfer's read data is available in the data register - and new write data can be written, queuing a new transfer. Transfers - submitted this way are chained and executed without deasserting cs. + * The SPI data register is double-buffered: Once a transfer has + started, new write data can be written, queuing a new transfer. + Transfers submitted this way are chained and executed without + deasserting cs. Once a transfer completes, the previous transfer's + read data is available in the data register. * A wishbone transaction is ack-ed when the transfer has been written to the intermediate buffer. It will be started when there are no - other transactions being executed. + other transactions being executed. Writes take one cycle when + there is either no transfer being executed, no data in the + intermediate buffer, or a transfer just completing. Reads always + finish in one cycle. Transaction Sequence: * If desired, write the config register to set up the core. @@ -215,28 +219,30 @@ class SPIMaster(Module): writing triggers the transfer and when the transfer is accepted to the inermediate buffer, the write is ack-ed. * If desired, read the data register. - * If desired write xfer and data for the next, chained, transfer + * If desired, write data for the next, chained, transfer. Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) - 1 active: cs/transfer active + 1 active: cs/transfer active (read-only) 1 pending: transfer pending in intermediate buffer, bus writes will - block + block (read-only) 1 cs_polarity: active level of chip select (reset=0) - 1 clk_polarity: idle level for clk (reset=0) + 1 clk_polarity: idle level of clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) + (clk_polarity, clk_phase) == (CPOL, CPHA) in Freescale language. (0, 0): idle low, output on falling, input on rising (0, 1): idle low, output on rising, input on falling (1, 0): idle high, output on rising, input on falling (1, 1): idle high, output on falling, input on rising + There is never a clk edge during a cs edge. 1 lsb_first: LSB is the first bit on the wire (reset=0) 1 half_duplex: 3-wire SPI, in/out on mosi (reset=0) 8 undefined 8 div_write: counter load value to divide this module's clock - to the SPI write clk. - f_clk/f_spi_write == 2*(div_write + 1) (reset=0) + to generate the SPI write clk (reset=0) + f_clk/f_spi_write == div_write + 2 8 div_read: ditto for the read clock xfer (address 1): @@ -338,12 +344,14 @@ class SPIMaster(Module): clk_t = TSTriple() self.specials += clk_t.get_tristate(pads.clk) - mosi_t = TSTriple() - self.specials += mosi_t.get_tristate(pads.mosi) - self.comb += [ clk_t.oe.eq(~config.offline), clk_t.o.eq((spi.cg.clk & spi.cs) ^ config.clk_polarity), + ] + + mosi_t = TSTriple() + self.specials += mosi_t.get_tristate(pads.mosi) + self.comb += [ mosi_t.oe.eq(~config.offline & spi.cs & (spi.oe | ~config.half_duplex)), mosi_t.o.eq(spi.reg.o), From df7d15d1fecb8e707f9618a28d344afb0e40bafb Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 13:54:36 +0100 Subject: [PATCH 19/52] runtime: refactor spi into rt2wb --- artiq/coredevice/rt2wb.py | 14 ++++++++++ artiq/coredevice/spi.py | 45 +++++++++++++------------------- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 6 ++--- artiq/runtime/{spi.c => rt2wb.c} | 30 ++++++++++----------- artiq/runtime/rt2wb.h | 10 +++++++ artiq/runtime/spi.h | 20 -------------- 7 files changed, 60 insertions(+), 67 deletions(-) create mode 100644 artiq/coredevice/rt2wb.py rename artiq/runtime/{spi.c => rt2wb.c} (56%) create mode 100644 artiq/runtime/rt2wb.h delete mode 100644 artiq/runtime/spi.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py new file mode 100644 index 000000000..150afd1fb --- /dev/null +++ b/artiq/coredevice/rt2wb.py @@ -0,0 +1,14 @@ +from artiq.language.core import * +from artiq.language.types import * + + +@syscall +def rt2wb_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rt2wb_read_sync(time_mu: TInt64, channel: TInt32, addr: TInt32, + duration_mu: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 6265ff3b6..4145a5513 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,16 +1,6 @@ from artiq.language.core import * from artiq.language.types import * - - -@syscall -def spi_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: - raise NotImplementedError("syscall not simulated") - - -@syscall -def spi_read(time_mu: TInt64, channel: TInt32, addr: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") +from artiq.coredevice.rt2wb import * SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -34,14 +24,14 @@ class SPIMaster: """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") - self.ref_period_mu = int(seconds_to_mu(ref_period, self.core), 64) + self.ref_period_mu = seconds_to_mu(ref_period, self.core) self.channel = channel self.write_div = 0 self.read_div = 0 # a full transfer takes prep_mu + xfer_mu - self.prep_mu = int(0, 64) - # chaned transfers can happen every xfer_mu - self.xfer_mu = int(0, 64) + self.prep_mu = 0 + # chained transfers can happen every xfer_mu + self.xfer_mu = 0 # The second transfer of a chain be written ref_period_mu # after the first. Read data is available every xfer_mu starting # a bit before prep_mu + xfer_mu. @@ -49,40 +39,41 @@ class SPIMaster: @portable def predict_xfer_mu(self, write_length, read_length): # this is only the intrinsic bit cycle duration - return self.ref_period_mu*( + return int(self.ref_period_mu*( write_length*self.write_div + - read_length*self.read_div) + read_length*self.read_div)) @portable def predict_prep_mu(self, write_div): - return self.ref_period_mu*( + return int(self.ref_period_mu*( 2 + # intermediate transfers # one write_div for the wait+idle cycle - self.write_div) + self.write_div)) @kernel def set_config(self, flags=0, write_div=6, read_div=6): self.write_div = write_div self.read_div = read_div self.prep_mu = self.predict_prep_mu(write_div) - spi_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 8) | ((read_div - 2) << 20)) + rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 8) | ((read_div - 2) << 20)) delay_mu(self.ref_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): self.xfer_mu = self.predict_xfer_mu(write_length, read_length) - spi_write(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) + rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) delay_mu(self.ref_period_mu) @kernel def write(self, data): - spi_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(self.prep_mu + self.xfer_mu) + rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(int(self.prep_mu + self.xfer_mu)) @kernel - def read(self): - r = spi_read(now_mu(), self.channel, SPI_DATA_ADDR) + def read_sync(self): + r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR, + int(self.ref_period_mu)) delay_mu(self.ref_period_mu) return r diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 46e3aedb9..045a162fe 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o spi.o + bridge.o rtio.o ttl.o dds.o rt2wb.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index bcdd7d028..892f66ccb 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,8 +15,8 @@ #include "artiq_personality.h" #include "ttl.h" #include "dds.h" -#include "spi.h" #include "rtio.h" +#include "rt2wb.h" double round(double x); @@ -122,8 +122,8 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"spi_write", &spi_write}, - {"spi_read", &spi_read}, + {"rt2wb_write", &rt2wb_write}, + {"rt2wb_read_sync", &rt2wb_read_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/spi.c b/artiq/runtime/rt2wb.c similarity index 56% rename from artiq/runtime/spi.c rename to artiq/runtime/rt2wb.c index 17e86dea7..0758bb6d5 100644 --- a/artiq/runtime/spi.c +++ b/artiq/runtime/rt2wb.c @@ -1,15 +1,11 @@ #include -#include #include "artiq_personality.h" #include "rtio.h" -#include "log.h" -#include "spi.h" +#include "rt2wb.h" -#define DURATION_WRITE (1 << CONFIG_RTIO_FINE_TS_WIDTH) - -void spi_write(long long int timestamp, int channel, int addr, +void rt2wb_write(long long int timestamp, int channel, int addr, unsigned int data) { rtio_chan_sel_write(channel); @@ -20,31 +16,33 @@ void spi_write(long long int timestamp, int channel, int addr, } -unsigned int spi_read(long long int timestamp, int channel, int addr) +unsigned int rt2wb_read_sync(long long int timestamp, int channel, + int addr, int duration) { int status; - long long int time_limit = timestamp + DURATION_WRITE; - unsigned int r; + unsigned int data; - spi_write(timestamp, channel, addr | SPI_WB_READ, 0); + rt2wb_write(timestamp, channel, addr, 0); while((status = rtio_i_status_read())) { - if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RTIO overflow at channel {0}", + "RTIO WB overflow on channel {0}", channel, 0, 0); } - if(rtio_get_counter() >= time_limit) { + if(rtio_get_counter() >= timestamp + duration) { /* check empty flag again to prevent race condition. * now we are sure that the time limit has been exceeded. */ if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - return -1; + artiq_raise_from_c("InternalError", + "RTIO WB read failed on channel {0}", + channel, 0, 0); } /* input FIFO is empty - keep waiting */ } - r = rtio_i_data_read(); + data = rtio_i_data_read(); rtio_i_re_write(1); - return r; + return data; } diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h new file mode 100644 index 000000000..f2cbe57d4 --- /dev/null +++ b/artiq/runtime/rt2wb.h @@ -0,0 +1,10 @@ +#ifndef __RT2WB_H +#define __RT2WB_H + +void rt2wb_write(long long int timestamp, int channel, int address, + unsigned int data); +unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, + int duration); + +#endif /* __RT2WB_H */ + diff --git a/artiq/runtime/spi.h b/artiq/runtime/spi.h deleted file mode 100644 index 6a5ba08f1..000000000 --- a/artiq/runtime/spi.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __SPI_H -#define __SPI_H - -#include -#include -#include - -#define SPI_ADDR_DATA 0 -#define SPI_ADDR_XFER 1 -#define SPI_ADDR_CONFIG 2 -#define SPI_WB_READ (1 << 2) - -#define SPI_XFER_CS(x) (x) -#define SPI_XFER_WRITE_LENGTH(x) ((x) << 16) -#define SPI_XFER_READ_LENGTH(x) ((x) << 24) - -void spi_write(long long int timestamp, int channel, int address, unsigned int data); -unsigned int spi_read(long long int timestamp, int channel, int address); - -#endif /* __SPI_H */ From c226aeb0d4f716b2fb4d3e6a73dde09e8e3e7baf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:55:29 +0100 Subject: [PATCH 20/52] coredevice/spi: read_sync read bit --- artiq/coredevice/spi.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 4145a5513..b75de123c 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -15,6 +15,8 @@ SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) SPI_HALF_DUPLEX, ) = (1 << i for i in range(8)) +SPI_RT2WB_READ = 1 << 2 + class SPIMaster: """Core device Serial Peripheral Interface (SPI) bus master. @@ -73,7 +75,7 @@ class SPIMaster: @kernel def read_sync(self): - r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR, - int(self.ref_period_mu)) + r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | + SPI_RT2WB_READ, int(self.ref_period_mu)) delay_mu(self.ref_period_mu) return r From 6903a1d88aacfc994a062fb1f8a9f3f00c4cbd7a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:56:04 +0100 Subject: [PATCH 21/52] runtime/rt2wb: accurate exception strings --- artiq/runtime/rt2wb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 0758bb6d5..e96ce3d3a 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -28,7 +28,7 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RTIO WB overflow on channel {0}", + "RT2WB overflow on channel {0}", channel, 0, 0); } if(rtio_get_counter() >= timestamp + duration) { @@ -37,7 +37,7 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, */ if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) artiq_raise_from_c("InternalError", - "RTIO WB read failed on channel {0}", + "RT2WB read failed on channel {0}", channel, 0, 0); } /* input FIFO is empty - keep waiting */ From 1b08e65fa1ffc616e2b74c0f875b6193ce7a01a3 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 14:56:29 +0100 Subject: [PATCH 22/52] gateware/rt2wb: only input when active --- artiq/gateware/rtio/phy/wishbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 1b58525f6..429e2c8ce 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -39,6 +39,6 @@ class RT2WB(Module): wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(wb.ack & ~wb.we), + self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From f73228f248fa2ce5d29a906dfc4973b913a1881a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 15:24:17 +0100 Subject: [PATCH 23/52] gateware/rt2wb: support combinatorial ack --- artiq/gateware/rtio/phy/wishbone.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 429e2c8ce..4eb69ceed 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -23,6 +23,9 @@ class RT2WB(Module): active = Signal() self.sync.rio += [ + If(active & wb.ack, + active.eq(0), + ), If(self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), @@ -30,15 +33,11 @@ class RT2WB(Module): wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**len(wb.sel) - 1) ), - If(wb.ack, - active.eq(0) - ) ] self.comb += [ - self.rtlink.o.busy.eq(active), + self.rtlink.o.busy.eq(active & ~wb.ack), wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From cb8815cc65cac4caa1e8108f81bc9590e53f63a8 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 16:44:04 +0100 Subject: [PATCH 24/52] Revert "gateware/rt2wb: support combinatorial ack" This reverts commit f73228f248fa2ce5d29a906dfc4973b913a1881a. --- artiq/gateware/rtio/phy/wishbone.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 4eb69ceed..429e2c8ce 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -23,9 +23,6 @@ class RT2WB(Module): active = Signal() self.sync.rio += [ - If(active & wb.ack, - active.eq(0), - ), If(self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), @@ -33,11 +30,15 @@ class RT2WB(Module): wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**len(wb.sel) - 1) ), + If(wb.ack, + active.eq(0) + ) ] self.comb += [ - self.rtlink.o.busy.eq(active & ~wb.ack), + self.rtlink.o.busy.eq(active), wb.cyc.eq(active), wb.stb.eq(active), + self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From a0083f450102264e99f717bf2533226bc5ed6782 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 16:44:11 +0100 Subject: [PATCH 25/52] Revert "gateware/rt2wb: only input when active" This reverts commit 1b08e65fa1ffc616e2b74c0f875b6193ce7a01a3. --- artiq/gateware/rtio/phy/wishbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/rtio/phy/wishbone.py b/artiq/gateware/rtio/phy/wishbone.py index 429e2c8ce..1b58525f6 100644 --- a/artiq/gateware/rtio/phy/wishbone.py +++ b/artiq/gateware/rtio/phy/wishbone.py @@ -39,6 +39,6 @@ class RT2WB(Module): wb.cyc.eq(active), wb.stb.eq(active), - self.rtlink.i.stb.eq(active & wb.ack & ~wb.we), + self.rtlink.i.stb.eq(wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ] From dd570720ac0d3aa27f621a36a692906b73379992 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:29:37 +0100 Subject: [PATCH 26/52] gateware.spi: ack only in cycles --- artiq/gateware/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 328419559..fd8f612c4 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -310,7 +310,7 @@ class SPIMaster(Module): spi.div_read.eq(config.div_read), ] self.sync += [ - bus.ack.eq(~bus.we | ~pending | spi.done), + bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), If(wb_we, Array([data_write, xfer.raw_bits(), config.raw_bits() ])[bus.adr].eq(bus.dat_w) From e11366869df33391a08174e8a78ab515965a4b91 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:54:42 +0100 Subject: [PATCH 27/52] coredevice/spi: clean up api --- artiq/coredevice/spi.py | 83 ++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index b75de123c..d6ebeb7db 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,6 +1,6 @@ from artiq.language.core import * -from artiq.language.types import * -from artiq.coredevice.rt2wb import * +from artiq.language.units import MHz +from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -26,56 +26,63 @@ class SPIMaster: """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") + self.ref_period = ref_period self.ref_period_mu = seconds_to_mu(ref_period, self.core) self.channel = channel - self.write_div = 0 - self.read_div = 0 - # a full transfer takes prep_mu + xfer_mu - self.prep_mu = 0 - # chained transfers can happen every xfer_mu - self.xfer_mu = 0 - # The second transfer of a chain be written ref_period_mu - # after the first. Read data is available every xfer_mu starting - # a bit before prep_mu + xfer_mu. - - @portable - def predict_xfer_mu(self, write_length, read_length): - # this is only the intrinsic bit cycle duration - return int(self.ref_period_mu*( - write_length*self.write_div + - read_length*self.read_div)) - - @portable - def predict_prep_mu(self, write_div): - return int(self.ref_period_mu*( - 2 + # intermediate transfers - # one write_div for the wait+idle cycle - self.write_div)) + self.write_period_mu = 0 + self.read_period_mu = 0 + self.xfer_period_mu = 0 + # A full transfer takes write_period_mu + xfer_period_mu. + # Chained transfers can happen every xfer_period_mu. + # The second transfer of a chain can be written 2*ref_period_mu + # after the first. Read data is available every xfer_period_mu starting + # a bit after xfer_period_mu (depending on clk_phase). + # To chain transfers together, new data must be written before + # pending transfer's read data becomes available. @kernel - def set_config(self, flags=0, write_div=6, read_div=6): - self.write_div = write_div - self.read_div = read_div - self.prep_mu = self.predict_prep_mu(write_div) + def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): + write_div = round(1/(write_freq*self.ref_period)) + read_div = round(1/(read_freq*self.ref_period)) + self.set_config_mu(flags, write_div, read_div) + + @kernel + def set_config_mu(self, flags=0, write_div=6, read_div=6): rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 8) | ((read_div - 2) << 20)) - delay_mu(self.ref_period_mu) + ((write_div - 2) << 16) | ((read_div - 2) << 24)) + self.write_period_mu = int(write_div*self.ref_period_mu) + self.read_period_mu = int(read_div*self.ref_period_mu) + delay_mu(2*self.ref_period_mu) + + @portable + def get_xfer_period_mu(self, write_length, read_length): + return int(write_length*self.write_period_mu + + read_length*self.read_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - self.xfer_mu = self.predict_xfer_mu(write_length, read_length) rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) - delay_mu(self.ref_period_mu) + self.xfer_period_mu = self.get_xfer_period_mu( + write_length, read_length) + delay_mu(int(2*self.ref_period_mu)) @kernel def write(self, data): rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(int(self.prep_mu + self.xfer_mu)) + delay_mu(int(self.write_period_mu + self.xfer_period_mu)) + + @kernel + def read_async(self): + rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + delay_mu(int(2*self.ref_period_mu)) @kernel def read_sync(self): - r = rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | - SPI_RT2WB_READ, int(self.ref_period_mu)) - delay_mu(self.ref_period_mu) - return r + return rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) + + @kernel + def get_config_sync(self): + return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) From d3c94827eb239e589393f974f107978d39ad6ccf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 17:58:54 +0100 Subject: [PATCH 28/52] runtime/ttl: simplify ttl_get() a bit --- artiq/runtime/ttl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index 577ab1eeb..db75207d4 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -38,7 +38,7 @@ long long int ttl_get(int channel, long long int time_limit) rtio_chan_sel_write(channel); while((status = rtio_i_status_read())) { - if(rtio_i_status_read() & RTIO_I_STATUS_OVERFLOW) { + if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", "RTIO overflow at channel {0}", From 5dae9f8aa8c2682e2a1b493dd63160d38516dddc Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:16:29 +0100 Subject: [PATCH 29/52] runtime: refactor rt2wb/dds --- artiq/runtime/Makefile | 2 +- artiq/runtime/dds.c | 19 ++++++------------- artiq/runtime/rt2wb.c | 34 +++++----------------------------- artiq/runtime/rt2wb.h | 2 ++ artiq/runtime/rtio.c | 38 ++++++++++++++++++++++++++++++++++++++ artiq/runtime/rtio.h | 3 +++ 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 045a162fe..7915f4d0e 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o dds.o rt2wb.o + bridge.o rtio.o ttl.o rt2wb.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index 396e652eb..fb9d34b05 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -2,7 +2,7 @@ #include #include "artiq_personality.h" -#include "rtio.h" +#include "rt2wb.h" #include "log.h" #include "dds.h" @@ -26,10 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rtio_o_address_write(addr); \ - rtio_o_data_write(data); \ - rtio_o_timestamp_write(now); \ - rtio_write_and_process_status(now, CONFIG_RTIO_DDS_CHANNEL); \ + rt2wb_write(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) @@ -37,8 +34,6 @@ void dds_init(long long int timestamp, int channel) { long long int now; - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); - now = timestamp - DURATION_INIT; #ifdef CONFIG_DDS_ONEHOT_SEL @@ -94,10 +89,10 @@ static void dds_set_one(long long int now, long long int ref_time, unsigned int { unsigned int channel_enc; - if(channel >= CONFIG_DDS_CHANNEL_COUNT) { - core_log("Attempted to set invalid DDS channel\n"); - return; - } + if(channel >= CONFIG_DDS_CHANNEL_COUNT) { + core_log("Attempted to set invalid DDS channel\n"); + return; + } #ifdef CONFIG_DDS_ONEHOT_SEL channel_enc = 1 << channel; #else @@ -190,7 +185,6 @@ void dds_batch_exit(void) if(!batch_mode) artiq_raise_from_c("DDSBatchError", "DDS batch error", 0, 0, 0); - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); /* + FUD time */ now = batch_ref_time - batch_count*(DURATION_PROGRAM + DURATION_WRITE); for(i=0;i= timestamp + duration) { - /* check empty flag again to prevent race condition. - * now we are sure that the time limit has been exceeded. - */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("InternalError", - "RT2WB read failed on channel {0}", - channel, 0, 0); - } - /* input FIFO is empty - keep waiting */ - } + rtio_output(timestamp, channel, addr, 0); + rtio_input_wait(timestamp + duration, channel); data = rtio_i_data_read(); rtio_i_re_write(1); return data; diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index f2cbe57d4..afae9a5a7 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -1,6 +1,8 @@ #ifndef __RT2WB_H #define __RT2WB_H +#include "rtio.h" + void rt2wb_write(long long int timestamp, int channel, int address, unsigned int data); unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 76514d19a..1680dbb6f 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -39,6 +39,44 @@ void rtio_process_exceptional_status(int status, long long int timestamp, int ch } } + +void rtio_output(long long int timestamp, int channel, unsigned int addr, + unsigned int data) +{ + rtio_chan_sel_write(channel); + rtio_o_timestamp_write(timestamp); + rtio_o_address_write(addr); + rtio_o_data_write(data); + rtio_write_and_process_status(timestamp, channel); +} + + +void rtio_input_wait(long long int timeout, int channel) +{ + int status; + + rtio_chan_sel_write(channel); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + } + if(rtio_get_counter() >= timeout) { + /* check empty flag again to prevent race condition. + * now we are sure that the time limit has been exceeded. + */ + if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("InternalError", + "RTIO input timeout on channel {0}", + channel, 0, 0); + } + /* input FIFO is empty - keep waiting */ + } +} + + void rtio_log_va(long long int timestamp, const char *fmt, va_list args) { // This executes on the kernel CPU's stack, which is specifically designed diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 87d8a9f37..1214b40c8 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -17,6 +17,9 @@ long long int rtio_get_counter(void); void rtio_process_exceptional_status(int status, long long int timestamp, int channel); void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); +void rtio_output(long long int timestamp, int channel, unsigned int address, + unsigned int data); +void rtio_input_wait(long long int timeout, int channel); static inline void rtio_write_and_process_status(long long int timestamp, int channel) { From ecedbbef4c735f342ab3ec2714638b9f70726a4f Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:20:07 +0100 Subject: [PATCH 30/52] runtime/ttl: use rtio_output and rtio_input_wait --- artiq/runtime/ttl.c | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index db75207d4..bdd6d82b3 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -6,53 +6,24 @@ void ttl_set_o(long long int timestamp, int channel, int value) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(0); - rtio_o_data_write(value); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 0, value); } void ttl_set_oe(long long int timestamp, int channel, int oe) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(1); - rtio_o_data_write(oe); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 1, oe); } void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_address_write(2); - rtio_o_data_write(sensitivity); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 2, sensitivity); } long long int ttl_get(int channel, long long int time_limit) { long long int r; - int status; - rtio_chan_sel_write(channel); - while((status = rtio_i_status_read())) { - if(status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RTIO overflow at channel {0}", - channel, 0, 0); - } - if(rtio_get_counter() >= time_limit) { - /* check empty flag again to prevent race condition. - * now we are sure that the time limit has been exceeded. - */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - return -1; - } - /* input FIFO is empty - keep waiting */ - } + rtio_input_wait(time_limit, channel); r = rtio_i_timestamp_read(); rtio_i_re_write(1); return r; @@ -60,8 +31,5 @@ long long int ttl_get(int channel, long long int time_limit) void ttl_clock_set(long long int timestamp, int channel, int ftw) { - rtio_chan_sel_write(channel); - rtio_o_timestamp_write(timestamp); - rtio_o_data_write(ftw); - rtio_write_and_process_status(timestamp, channel); + rtio_output(timestamp, channel, 0, ftw); } From 16537d347e094b5fd454df0d4c85187f81e80756 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:48:26 +0100 Subject: [PATCH 31/52] coredevice.spi: cleanup --- artiq/coredevice/spi.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index d6ebeb7db..330ddd168 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,4 +1,5 @@ -from artiq.language.core import * +from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, + delay_mu) from artiq.language.units import MHz from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync @@ -83,6 +84,11 @@ class SPIMaster: SPI_RT2WB_READ, int(2*self.ref_period_mu)) @kernel - def get_config_sync(self): + def _get_config_sync(self): return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, int(2*self.ref_period_mu)) + + @kernel + def _get_xfer_sync(self): + return rt2wb_read_sync(now_mu(), self.channel, SPI_XFER_ADDR | + SPI_RT2WB_READ, int(2*self.ref_period_mu)) From 6c899e6ba64ff83d9fb14ccbe048e14650d0e7c9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 19:49:15 +0100 Subject: [PATCH 32/52] runtime/rtio: fix rtio_input_wait(), add RTIOTimeout --- artiq/coredevice/__init__.py | 7 ++++--- artiq/coredevice/exceptions.py | 11 +++++++++++ artiq/runtime/rt2wb.c | 14 +++++++++++++- artiq/runtime/rtio.c | 14 ++++++-------- artiq/runtime/rtio.h | 2 +- artiq/runtime/ttl.c | 9 ++++++++- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index 76e782a3f..aa4ab5066 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,12 +1,13 @@ -from artiq.coredevice import exceptions, dds +from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, RTIOCollisionError, RTIOOverflow, - DDSBatchError, CacheError) + DDSBatchError, CacheError, + RTIOTimeout) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollisionError", - "RTIOOverflow", "DDSBatchError", "CacheError"] + "RTIOOverflow", "DDSBatchError", "CacheError", "RTIOTimeout"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 05d9d1c29..3b5dba547 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -108,6 +108,17 @@ class RTIOOverflow(Exception): """ artiq_builtin = True +class RTIOTimeout(Exception): + """Raised when an RTIO input operation does not complete within the expected + time. This is only raised by channels where a response is guaranteed, such + as RT2WB (DDS and SPI). + + This does not interrupt operations further than cancelling the current read + attempt. Reading can be reattempted after the exception is caught, and + events that have arrived in the meantime will be retrieved. + """ + artiq_builtin = True + class DDSBatchError(Exception): """Raised when attempting to start a DDS batch while already in a batch, or when too many commands are batched. diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 50f4fee55..30742ed24 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -16,8 +16,20 @@ unsigned int rt2wb_read_sync(long long int timestamp, int channel, int addr, int duration) { unsigned int data; + int status; + rtio_output(timestamp, channel, addr, 0); - rtio_input_wait(timestamp + duration, channel); + + status = rtio_input_wait(timestamp + duration, channel); + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RT2WB read overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("RTIOTimeout", + "RT2WB read timeout on channel {0}", + channel, 0, 0); + data = rtio_i_data_read(); rtio_i_re_write(1); return data; diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 1680dbb6f..00eecbc2f 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -51,7 +51,7 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, } -void rtio_input_wait(long long int timeout, int channel) +int rtio_input_wait(long long int timeout, int channel) { int status; @@ -59,21 +59,19 @@ void rtio_input_wait(long long int timeout, int channel) while((status = rtio_i_status_read())) { if(status & RTIO_I_STATUS_OVERFLOW) { rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RTIO input overflow on channel {0}", - channel, 0, 0); + break; } if(rtio_get_counter() >= timeout) { /* check empty flag again to prevent race condition. * now we are sure that the time limit has been exceeded. */ - if(rtio_i_status_read() & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("InternalError", - "RTIO input timeout on channel {0}", - channel, 0, 0); + status = rtio_i_status_read(); + if(status & RTIO_I_STATUS_EMPTY) + break; } /* input FIFO is empty - keep waiting */ } + return status; } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 1214b40c8..d311fbb55 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,7 +19,7 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); -void rtio_input_wait(long long int timeout, int channel); +int rtio_input_wait(long long int timeout, int channel); static inline void rtio_write_and_process_status(long long int timestamp, int channel) { diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c index bdd6d82b3..603eb2a8c 100644 --- a/artiq/runtime/ttl.c +++ b/artiq/runtime/ttl.c @@ -22,8 +22,15 @@ void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) long long int ttl_get(int channel, long long int time_limit) { long long int r; + int status = rtio_input_wait(time_limit, channel); + + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + return -1; - rtio_input_wait(time_limit, channel); r = rtio_i_timestamp_read(); rtio_i_re_write(1); return r; From 7ef21f03b98e5af196f080a19456500a75fc9aeb Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:19:39 +0100 Subject: [PATCH 33/52] nist_clock: add SPIMasters to spi buses --- artiq/gateware/targets/kc705.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 39a8ae3b2..5213adaae 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -250,18 +250,24 @@ class NIST_CLOCK(_NIST_Ions): self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - spi_pins = self.platform.request("ams101_dac", 0) - phy = ttl_simple.Output(spi_pins.ldac) + ams101_dac = self.platform.request("ams101_dac", 0) + phy = ttl_simple.Output(ams101_dac.ldac) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) - phy = spi.SPIMaster(spi_pins) + phy = spi.SPIMaster(ams101_dac) self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) + for i in range(3): + phy = spi.SPIMaster(self.platform.request("spi", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy( + phy, ofifo_depth=128, ififo_depth=128)) + phy = ttl_simple.ClockGen(platform.request("la32_p")) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) From 12252abc8f97765afe2b91709d1c36ef6cc10a42 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:21:18 +0100 Subject: [PATCH 34/52] nist_clock: rename spi*.ce to spi*.cs_n --- artiq/gateware/nist_clock.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/gateware/nist_clock.py b/artiq/gateware/nist_clock.py index 3b621ac41..808f5abb9 100644 --- a/artiq/gateware/nist_clock.py +++ b/artiq/gateware/nist_clock.py @@ -53,21 +53,21 @@ fmc_adapter_io = [ ("spi", 0, Subsignal("clk", Pins("LPC:LA13_N")), - Subsignal("ce", Pins("LPC:LA14_N")), + Subsignal("cs_n", Pins("LPC:LA14_N")), Subsignal("mosi", Pins("LPC:LA17_CC_P")), Subsignal("miso", Pins("LPC:LA17_CC_N")), IOStandard("LVTTL")), ("spi", 1, Subsignal("clk", Pins("LPC:LA23_N")), - Subsignal("ce", Pins("LPC:LA23_P")), + Subsignal("cs_n", Pins("LPC:LA23_P")), Subsignal("mosi", Pins("LPC:LA18_CC_N")), Subsignal("miso", Pins("LPC:LA18_CC_P")), IOStandard("LVTTL")), ("spi", 2, Subsignal("clk", Pins("LPC:LA27_P")), - Subsignal("ce", Pins("LPC:LA26_P")), + Subsignal("cs_n", Pins("LPC:LA26_P")), Subsignal("mosi", Pins("LPC:LA27_N")), Subsignal("miso", Pins("LPC:LA26_N")), IOStandard("LVTTL")), From da22ec73dfcd82e6d4b06235d1a7983c36a817b5 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:22:08 +0100 Subject: [PATCH 35/52] gateware.spi: rework wb bus sequence --- artiq/gateware/spi.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index fd8f612c4..5e2e5541a 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -292,14 +292,12 @@ class SPIMaster(Module): data_width, clock_width=len(config.div_read), bits_width=len(xfer.read_length)) - wb_we = Signal() pending = Signal() cs = Signal.like(xfer.cs) data_read = Signal.like(spi.reg.data) data_write = Signal.like(spi.reg.data) self.comb += [ - wb_we.eq(bus.cyc & bus.stb & bus.we & (~pending | spi.done)), bus.dat_r.eq( Array([data_read, xfer.raw_bits(), config.raw_bits() ])[bus.adr]), @@ -310,13 +308,6 @@ class SPIMaster(Module): spi.div_read.eq(config.div_read), ] self.sync += [ - bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), - If(wb_we, - Array([data_write, xfer.raw_bits(), config.raw_bits() - ])[bus.adr].eq(bus.dat_w) - ), - config.active.eq(spi.cs), - config.pending.eq(pending), If(spi.done, data_read.eq(spi.reg.data), ), @@ -327,9 +318,19 @@ class SPIMaster(Module): spi.reg.data.eq(data_write), pending.eq(0), ), - If(wb_we & (bus.adr == 0), # data register - pending.eq(1), + bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), + If(bus.ack, + bus.ack.eq(0), ), + If(bus.we & bus.ack, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w), + If(bus.adr == 0, # data register + pending.eq(1), + ), + ), + config.active.eq(spi.cs), + config.pending.eq(pending), ] # I/O From 764795a8fe7fbeab94d4688a7b1d43cbea71f944 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Mon, 29 Feb 2016 22:32:53 +0100 Subject: [PATCH 36/52] examples: update device_db for nist_clock spi --- examples/master/device_db.pyon | 47 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 4dbb92ab7..a78c74eba 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -74,14 +74,6 @@ "class": "TTLInOut", "arguments": {"channel": 18} }, - "ttl_clock_la32_p": { - "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLClockGen", - "arguments": {"channel": 20} - }, - - "led": { "type": "local", "module": "artiq.coredevice.ttl", @@ -89,6 +81,45 @@ "arguments": {"channel": 19} }, + "ams101_ldac": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 20} + }, + "ams101_spi": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 21} + }, + + "spi0": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 22} + }, + "spi1": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 23} + }, + "spi2": { + "type": "local", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", + "arguments": {"channel": 24} + }, + + "ttl_clock_la32_p": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", + "arguments": {"channel": 25} + }, + "dds_bus": { "type": "local", "module": "artiq.coredevice.dds", From 7d7a710a561b77618d7a321c64ca77ea5a868441 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 00:21:32 +0100 Subject: [PATCH 37/52] runtime/rt2wb: use input/output terminology and add (async) input --- artiq/coredevice/rt2wb.py | 12 ++++++--- artiq/coredevice/spi.py | 57 ++++++++++++++++++++++----------------- artiq/runtime/dds.c | 2 +- artiq/runtime/ksupport.c | 5 ++-- artiq/runtime/rt2wb.c | 39 ++++++++++++++++++++------- artiq/runtime/rt2wb.h | 6 ++--- 6 files changed, 77 insertions(+), 44 deletions(-) diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 150afd1fb..80d1cb041 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -3,12 +3,16 @@ from artiq.language.types import * @syscall -def rt2wb_write(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: +def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: raise NotImplementedError("syscall not simulated") @syscall -def rt2wb_read_sync(time_mu: TInt64, channel: TInt32, addr: TInt32, - duration_mu: TInt32) -> TInt32: +def rt2wb_input(channel: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rt2wb_input_sync(timeout_mu: TInt64, channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 330ddd168..c0daba932 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,7 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, - delay_mu) + delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_write, rt2wb_read_sync +from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input, rt2wb_input_sync SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -28,11 +28,11 @@ class SPIMaster: def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") self.ref_period = ref_period - self.ref_period_mu = seconds_to_mu(ref_period, self.core) + self.ref_period_mu = int(seconds_to_mu(ref_period, self.core)) self.channel = channel - self.write_period_mu = 0 - self.read_period_mu = 0 - self.xfer_period_mu = 0 + self.write_period_mu = int(0) + self.read_period_mu = int(0) + self.xfer_period_mu = int(0) # A full transfer takes write_period_mu + xfer_period_mu. # Chained transfers can happen every xfer_period_mu. # The second transfer of a chain can be written 2*ref_period_mu @@ -49,11 +49,11 @@ class SPIMaster: @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): - rt2wb_write(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 16) | ((read_div - 2) << 24)) + rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) self.read_period_mu = int(read_div*self.ref_period_mu) - delay_mu(2*self.ref_period_mu) + delay_mu(3*self.ref_period_mu) @portable def get_xfer_period_mu(self, write_length, read_length): @@ -62,33 +62,40 @@ class SPIMaster: @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - rt2wb_write(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) - self.xfer_period_mu = self.get_xfer_period_mu( - write_length, read_length) - delay_mu(int(2*self.ref_period_mu)) + rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) + self.xfer_period_mu = self.get_xfer_period_mu(write_length, + read_length) + delay_mu(3*self.ref_period_mu) @kernel def write(self, data): - rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(int(self.write_period_mu + self.xfer_period_mu)) + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR, data) + delay_mu(3*self.ref_period_mu) @kernel - def read_async(self): - rt2wb_write(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - delay_mu(int(2*self.ref_period_mu)) + def read(self): + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + delay_mu(3*self.ref_period_mu) + + @kernel + def input(self): + return rt2wb_input(self.channel) + + @kernel + def _rt2wb_read_sync(self, addr=0): + t = now_mu() + rt2wb_output(t, self.channel, addr | SPI_RT2WB_READ, 0) + return rt2wb_input_sync(t + 3*self.ref_period_mu, self.channel) @kernel def read_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_DATA_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_DATA_ADDR) @kernel def _get_config_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_CONFIG_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_CONFIG_ADDR) @kernel def _get_xfer_sync(self): - return rt2wb_read_sync(now_mu(), self.channel, SPI_XFER_ADDR | - SPI_RT2WB_READ, int(2*self.ref_period_mu)) + return self._rt2wb_read_sync(SPI_XFER_ADDR) diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index fb9d34b05..f76da5724 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -26,7 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rt2wb_write(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ + rt2wb_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index 892f66ccb..a95744450 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -122,8 +122,9 @@ static const struct symbol runtime_exports[] = { {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_write", &rt2wb_write}, - {"rt2wb_read_sync", &rt2wb_read_sync}, + {"rt2wb_output", &rt2wb_output}, + {"rt2wb_input", &rt2wb_input}, + {"rt2wb_input_sync", &rt2wb_input_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 30742ed24..3956028db 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -5,29 +5,50 @@ #include "rt2wb.h" -void rt2wb_write(long long int timestamp, int channel, int addr, +void rt2wb_output(long long int timestamp, int channel, int addr, unsigned int data) { rtio_output(timestamp, channel, addr, data); } -unsigned int rt2wb_read_sync(long long int timestamp, int channel, int addr, - int duration) +unsigned int rt2wb_input(int channel) { unsigned int data; int status; - rtio_output(timestamp, channel, addr, 0); - - status = rtio_input_wait(timestamp + duration, channel); - if (status & RTIO_I_STATUS_OVERFLOW) + rtio_chan_sel_write(channel); + status = rtio_i_status_read(); + if (status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); artiq_raise_from_c("RTIOOverflow", - "RT2WB read overflow on channel {0}", + "RT2WB input overflow on channel {0}", channel, 0, 0); + } if (status & RTIO_I_STATUS_EMPTY) artiq_raise_from_c("RTIOTimeout", - "RT2WB read timeout on channel {0}", + "RT2WB input timeout on channel {0}", + channel, 0, 0); + + data = rtio_i_data_read(); + rtio_i_re_write(1); + return data; +} + + +unsigned int rt2wb_input_sync(long long int timeout, int channel) +{ + unsigned int data; + int status; + + status = rtio_input_wait(timeout, channel); + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RT2WB input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + artiq_raise_from_c("RTIOTimeout", + "RT2WB input timeout on channel {0}", channel, 0, 0); data = rtio_i_data_read(); diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index afae9a5a7..d421397f4 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -3,10 +3,10 @@ #include "rtio.h" -void rt2wb_write(long long int timestamp, int channel, int address, +void rt2wb_output(long long int timestamp, int channel, int addr, unsigned int data); -unsigned int rt2wb_read_sync(long long int timestamp, int channel, int address, - int duration); +unsigned int rt2wb_input(int channel); +unsigned int rt2wb_input_sync(long long int timeout, int channel); #endif /* __RT2WB_H */ From f2ec8692c059f8581943c47fddaeeb8362445824 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 01:52:46 +0100 Subject: [PATCH 38/52] nist_clock: disable spi1/2 --- artiq/gateware/targets/kc705.py | 2 +- examples/master/device_db.pyon | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 5213adaae..5872e0f5f 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -262,7 +262,7 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) - for i in range(3): + for i in range(1): # spi1 and spi2 collide in pinout with ttl phy = spi.SPIMaster(self.platform.request("spi", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index a78c74eba..5debbc7d3 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -100,24 +100,12 @@ "class": "SPIMaster", "arguments": {"channel": 22} }, - "spi1": { - "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", - "arguments": {"channel": 23} - }, - "spi2": { - "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", - "arguments": {"channel": 24} - }, "ttl_clock_la32_p": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLClockGen", - "arguments": {"channel": 25} + "arguments": {"channel": 23} }, "dds_bus": { From c2fe9a08ae1dffa77a2ad9392c94de3bf78457e5 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 14:14:38 +0100 Subject: [PATCH 39/52] gateware.spi: delay only writes to data register, update doc --- artiq/gateware/spi.py | 59 +++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/spi.py b/artiq/gateware/spi.py index 5e2e5541a..e476adbb9 100644 --- a/artiq/gateware/spi.py +++ b/artiq/gateware/spi.py @@ -205,12 +205,15 @@ class SPIMaster(Module): Transfers submitted this way are chained and executed without deasserting cs. Once a transfer completes, the previous transfer's read data is available in the data register. - * A wishbone transaction is ack-ed when the transfer has been written - to the intermediate buffer. It will be started when there are no - other transactions being executed. Writes take one cycle when - there is either no transfer being executed, no data in the - intermediate buffer, or a transfer just completing. Reads always - finish in one cycle. + * Writes to the config register take effect immediately. Writes to xfer + and data are synchronized to the start of a transfer. + * A wishbone data register write is ack-ed when the transfer has + been written to the intermediate buffer. It will be started when + there are no other transactions being executed, either starting + a new SPI transfer of chained to an in-flight transfer. + Writes take two cycles unless the write is to the data register + and another chained transfer is pending and the transfer being + executed is not complete. Reads always finish in two cycles. Transaction Sequence: * If desired, write the config register to set up the core. @@ -218,16 +221,17 @@ class SPIMaster(Module): * Write the data register (also for zero-length writes), writing triggers the transfer and when the transfer is accepted to the inermediate buffer, the write is ack-ed. - * If desired, read the data register. - * If desired, write data for the next, chained, transfer. + * If desired, read the data register corresponding to the last + completed transfer. + * If desired, change xfer register for the next transfer. + * If desired, write data queuing the next (possibly chained) transfer. Register address and bit map: config (address 2): 1 offline: all pins high-z (reset=1) 1 active: cs/transfer active (read-only) - 1 pending: transfer pending in intermediate buffer, bus writes will - block (read-only) + 1 pending: transfer pending in intermediate buffer (read-only) 1 cs_polarity: active level of chip select (reset=0) 1 clk_polarity: idle level of clk (reset=0) 1 clk_phase: first edge after cs assertion to sample data on (reset=0) @@ -246,18 +250,18 @@ class SPIMaster(Module): 8 div_read: ditto for the read clock xfer (address 1): - 16 cs: active high bit mask of chip selects to assert - 6 write_len: 0-M bits + 16 cs: active high bit mask of chip selects to assert (reset=0) + 6 write_len: 0-M bits (reset=0) 2 undefined - 6 read_len: 0-M bits + 6 read_len: 0-M bits (reset=0) 2 undefined data (address 0): - M write/read data + M write/read data (reset=0) """ - def __init__(self, pads, bus=None, data_width=32): + def __init__(self, pads, bus=None): if bus is None: - bus = wishbone.Interface(data_width=data_width) + bus = wishbone.Interface(data_width=32) self.bus = bus ### @@ -289,7 +293,8 @@ class SPIMaster(Module): assert len(xfer) <= len(bus.dat_w) self.submodules.spi = spi = SPIMachine( - data_width, clock_width=len(config.div_read), + data_width=len(bus.dat_w), + clock_width=len(config.div_read), bits_width=len(xfer.read_length)) pending = Signal() @@ -318,15 +323,21 @@ class SPIMaster(Module): spi.reg.data.eq(data_write), pending.eq(0), ), - bus.ack.eq(bus.cyc & bus.stb & (~bus.we | ~pending | spi.done)), + # wb.ack a transaction if any of the following: + # a) reading, + # b) writing to non-data register + # c) writing to data register and no pending transfer + # d) writing to data register and pending and swapping buffers + bus.ack.eq(bus.cyc & bus.stb & + (~bus.we | (bus.adr != 0) | ~pending | spi.done)), If(bus.ack, bus.ack.eq(0), - ), - If(bus.we & bus.ack, - Array([data_write, xfer.raw_bits(), config.raw_bits() - ])[bus.adr].eq(bus.dat_w), - If(bus.adr == 0, # data register - pending.eq(1), + If(bus.we, + Array([data_write, xfer.raw_bits(), config.raw_bits() + ])[bus.adr].eq(bus.dat_w), + If(bus.adr == 0, # data register + pending.eq(1), + ), ), ), config.active.eq(spi.cs), From 324660ab407f6900cd7845b6967e18cb4ef1f739 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 14:44:07 +0100 Subject: [PATCH 40/52] rt2wb, exceptions: remove RTIOTimeout Assume that rt2wb transactions either collide and are then reported (https://github.com/m-labs/artiq/issues/308) or that they complete and the delay with which they complete does not matter. If a transaction is ack'ed with a delay because the WB core's downstream logic is busy, that may lead to a later collision with another WB transaction. --- artiq/coredevice/__init__.py | 5 ++--- artiq/coredevice/exceptions.py | 11 ---------- artiq/coredevice/rt2wb.py | 5 ----- artiq/coredevice/spi.py | 39 +++++++++++++++------------------- artiq/runtime/ksupport.c | 1 - artiq/runtime/rt2wb.c | 38 ++++++--------------------------- 6 files changed, 26 insertions(+), 73 deletions(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index aa4ab5066..88de8cf7c 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,13 +1,12 @@ from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, RTIOCollisionError, RTIOOverflow, - DDSBatchError, CacheError, - RTIOTimeout) + DDSBatchError, CacheError) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollisionError", - "RTIOOverflow", "DDSBatchError", "CacheError", "RTIOTimeout"] + "RTIOOverflow", "DDSBatchError", "CacheError"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 3b5dba547..05d9d1c29 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -108,17 +108,6 @@ class RTIOOverflow(Exception): """ artiq_builtin = True -class RTIOTimeout(Exception): - """Raised when an RTIO input operation does not complete within the expected - time. This is only raised by channels where a response is guaranteed, such - as RT2WB (DDS and SPI). - - This does not interrupt operations further than cancelling the current read - attempt. Reading can be reattempted after the exception is caught, and - events that have arrived in the meantime will be retrieved. - """ - artiq_builtin = True - class DDSBatchError(Exception): """Raised when attempting to start a DDS batch while already in a batch, or when too many commands are batched. diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 80d1cb041..5bc3e6f7a 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -11,8 +11,3 @@ def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 @syscall def rt2wb_input(channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") - - -@syscall -def rt2wb_input_sync(timeout_mu: TInt64, channel: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index c0daba932..c28cf828d 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,7 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input, rt2wb_input_sync +from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -55,17 +55,12 @@ class SPIMaster: self.read_period_mu = int(read_div*self.ref_period_mu) delay_mu(3*self.ref_period_mu) - @portable - def get_xfer_period_mu(self, write_length, read_length): - return int(write_length*self.write_period_mu + - read_length*self.read_period_mu) - @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) - self.xfer_period_mu = self.get_xfer_period_mu(write_length, - read_length) + self.xfer_period_mu = int(write_length*self.write_period_mu + + read_length*self.read_period_mu) delay_mu(3*self.ref_period_mu) @kernel @@ -74,28 +69,28 @@ class SPIMaster: delay_mu(3*self.ref_period_mu) @kernel - def read(self): + def read_async(self): + # every read_async() must be matched by an input_async() rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel - def input(self): + def input_async(self): + # matches the preeeding read_async() return rt2wb_input(self.channel) - @kernel - def _rt2wb_read_sync(self, addr=0): - t = now_mu() - rt2wb_output(t, self.channel, addr | SPI_RT2WB_READ, 0) - return rt2wb_input_sync(t + 3*self.ref_period_mu, self.channel) - @kernel def read_sync(self): - return self._rt2wb_read_sync(SPI_DATA_ADDR) - - @kernel - def _get_config_sync(self): - return self._rt2wb_read_sync(SPI_CONFIG_ADDR) + rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + return rt2wb_input(self.channel) @kernel def _get_xfer_sync(self): - return self._rt2wb_read_sync(SPI_XFER_ADDR) + rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) + return rt2wb_input(self.channel) + + @kernel + def _get_config_sync(self): + rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, + 0) + return rt2wb_input(self.channel) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index a95744450..e15352bb5 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -124,7 +124,6 @@ static const struct symbol runtime_exports[] = { {"rt2wb_output", &rt2wb_output}, {"rt2wb_input", &rt2wb_input}, - {"rt2wb_input_sync", &rt2wb_input_sync}, {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 3956028db..6760149c4 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -18,38 +18,14 @@ unsigned int rt2wb_input(int channel) int status; rtio_chan_sel_write(channel); - status = rtio_i_status_read(); - if (status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RT2WB input overflow on channel {0}", + channel, 0, 0); + } } - if (status & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("RTIOTimeout", - "RT2WB input timeout on channel {0}", - channel, 0, 0); - - data = rtio_i_data_read(); - rtio_i_re_write(1); - return data; -} - - -unsigned int rt2wb_input_sync(long long int timeout, int channel) -{ - unsigned int data; - int status; - - status = rtio_input_wait(timeout, channel); - if (status & RTIO_I_STATUS_OVERFLOW) - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); - if (status & RTIO_I_STATUS_EMPTY) - artiq_raise_from_c("RTIOTimeout", - "RT2WB input timeout on channel {0}", - channel, 0, 0); data = rtio_i_data_read(); rtio_i_re_write(1); From 29776fae3f171afa221fffffd36c20ecb933563a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 15:38:40 +0100 Subject: [PATCH 41/52] coredevice.spi: unused import --- artiq/coredevice/spi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index c28cf828d..5b0c6bb88 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,4 +1,4 @@ -from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, +from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input From aa10791ddf56fdcb36d3fb452a3b9e2008abc4c4 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 15:40:35 +0100 Subject: [PATCH 42/52] rtio: rm rtio_write_and_process_status --- artiq/runtime/rtio.c | 7 ++++++- artiq/runtime/rtio.h | 10 ---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 00eecbc2f..0670a4e2b 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -43,11 +43,16 @@ void rtio_process_exceptional_status(int status, long long int timestamp, int ch void rtio_output(long long int timestamp, int channel, unsigned int addr, unsigned int data) { + int status; + rtio_chan_sel_write(channel); rtio_o_timestamp_write(timestamp); rtio_o_address_write(addr); rtio_o_data_write(data); - rtio_write_and_process_status(timestamp, channel); + rtio_o_we_write(1); + status = rtio_o_status_read(); + if(status) + rtio_process_exceptional_status(status, timestamp, channel); } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index d311fbb55..9cebb6f2f 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -21,14 +21,4 @@ void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); int rtio_input_wait(long long int timeout, int channel); -static inline void rtio_write_and_process_status(long long int timestamp, int channel) -{ - int status; - - rtio_o_we_write(1); - status = rtio_o_status_read(); - if(status) - rtio_process_exceptional_status(status, timestamp, channel); -} - #endif /* __RTIO_H */ From 8adef1278133bcda56b55ac82bfd1d30d2dd3dd9 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 16:34:05 +0100 Subject: [PATCH 43/52] runtime: refactor ttl*() * remove rt2wb_output * remove ttl_*() ttl.c ttl.h * use rtio_output() and rtio_input_timestamp() * adapt coredevice/compiler layer * adapt bridge to not artiq_raise_from_c() --- artiq/coredevice/rt2wb.py | 6 ------ artiq/coredevice/rtio.py | 13 ++++++++++++ artiq/coredevice/spi.py | 3 ++- artiq/coredevice/ttl.py | 36 ++++++++------------------------- artiq/runtime/Makefile | 2 +- artiq/runtime/bridge.c | 14 ++++++++----- artiq/runtime/dds.c | 4 ++-- artiq/runtime/ksupport.c | 10 ++-------- artiq/runtime/rt2wb.c | 7 ------- artiq/runtime/rt2wb.h | 3 --- artiq/runtime/rtio.c | 15 ++++++++++++-- artiq/runtime/rtio.h | 2 +- artiq/runtime/ttl.c | 42 --------------------------------------- artiq/runtime/ttl.h | 10 ---------- 14 files changed, 51 insertions(+), 116 deletions(-) create mode 100644 artiq/coredevice/rtio.py delete mode 100644 artiq/runtime/ttl.c delete mode 100644 artiq/runtime/ttl.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py index 5bc3e6f7a..318d8bf0e 100644 --- a/artiq/coredevice/rt2wb.py +++ b/artiq/coredevice/rt2wb.py @@ -2,12 +2,6 @@ from artiq.language.core import * from artiq.language.types import * -@syscall -def rt2wb_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 - ) -> TNone: - raise NotImplementedError("syscall not simulated") - - @syscall def rt2wb_input(channel: TInt32) -> TInt32: raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py new file mode 100644 index 000000000..194818507 --- /dev/null +++ b/artiq/coredevice/rtio.py @@ -0,0 +1,13 @@ +from artiq.language.core import syscall +from artiq.language.types import TInt64, TInt32, TNone + + +@syscall +def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 + ) -> TNone: + raise NotImplementedError("syscall not simulated") + + +@syscall +def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 5b0c6bb88..5998997e1 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,7 +1,8 @@ from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rt2wb import rt2wb_output, rt2wb_input +from artiq.coredevice.rtio import rtio_output as rt2wb_output +from artiq.coredevice.rt2wb import rt2wb_input SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 91c8c5531..1b3a6eecd 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -1,26 +1,6 @@ from artiq.language.core import * from artiq.language.types import * - - -@syscall -def ttl_set_o(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_set_oe(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_set_sensitivity(time_mu: TInt64, channel: TInt32, sensitivity: TInt32) -> TNone: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_get(channel: TInt32, time_limit_mu: TInt64) -> TInt64: - raise NotImplementedError("syscall not simulated") - -@syscall -def ttl_clock_set(time_mu: TInt64, channel: TInt32, ftw: TInt32) -> TNone: - raise NotImplementedError("syscall not simulated") +from artiq.coredevice.rtio import rtio_output, rtio_input_timestamp class TTLOut: @@ -39,7 +19,7 @@ class TTLOut: @kernel def set_o(self, o): - ttl_set_o(now_mu(), self.channel, o) + rtio_output(now_mu(), self.channel, 0, 1 if o else 0) self.o_previous_timestamp = now_mu() @kernel @@ -108,7 +88,7 @@ class TTLInOut: @kernel def set_oe(self, oe): - ttl_set_oe(now_mu(), self.channel, oe) + rtio_output(now_mu(), self.channel, 1, 1 if oe else 0) @kernel def output(self): @@ -128,7 +108,7 @@ class TTLInOut: @kernel def set_o(self, o): - ttl_set_o(now_mu(), self.channel, o) + rtio_output(now_mu(), self.channel, 0, 1 if o else 0) self.o_previous_timestamp = now_mu() @kernel @@ -170,7 +150,7 @@ class TTLInOut: @kernel def _set_sensitivity(self, value): - ttl_set_sensitivity(now_mu(), self.channel, value) + rtio_output(now_mu(), self.channel, 2, 1 if value else 0) self.i_previous_timestamp = now_mu() @kernel @@ -226,7 +206,7 @@ class TTLInOut: """Poll the RTIO input during all the previously programmed gate openings, and returns the number of registered events.""" count = 0 - while ttl_get(self.channel, self.i_previous_timestamp) >= 0: + while rtio_input_timestamp(self.i_previous_timestamp, self.channel) >= 0: count += 1 return count @@ -237,7 +217,7 @@ class TTLInOut: If the gate is permanently closed, returns a negative value. """ - return ttl_get(self.channel, self.i_previous_timestamp) + return rtio_input_timestamp(self.i_previous_timestamp, self.channel) class TTLClockGen: @@ -288,7 +268,7 @@ class TTLClockGen: that are not powers of two cause jitter of one RTIO clock cycle at the output. """ - ttl_clock_set(now_mu(), self.channel, frequency) + rtio_output(now_mu(), self.channel, 0, frequency) self.previous_timestamp = now_mu() @kernel diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 7915f4d0e..4411790dc 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o ttl.o rt2wb.o dds.o + bridge.o rtio.o rt2wb.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 708e35473..02be42e4c 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -1,21 +1,25 @@ #include "mailbox.h" #include "messages.h" #include "rtio.h" -#include "ttl.h" #include "dds.h" #include "bridge.h" #define TIME_BUFFER (8000 << CONFIG_RTIO_FINE_TS_WIDTH) -static void dds_write(int addr, int data) +static void rtio_output_blind(int channel, int addr, int data) { - rtio_chan_sel_write(CONFIG_RTIO_DDS_CHANNEL); + rtio_chan_sel_write(channel); rtio_o_address_write(addr); rtio_o_data_write(data); rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER); rtio_o_we_write(1); } +static void dds_write(int addr, int data) +{ + rtio_output_blind(CONFIG_RTIO_DDS_CHANNEL, addr, data); +} + static int dds_read(int addr) { int r; @@ -54,7 +58,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_oe(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); + rtio_output_blind(msg->channel, 0, msg->value); mailbox_acknowledge(); break; } @@ -62,7 +66,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_o(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); + rtio_output_blind(msg->channel, 1, msg->value); mailbox_acknowledge(); break; } diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index f76da5724..3f8e728c1 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -2,7 +2,7 @@ #include #include "artiq_personality.h" -#include "rt2wb.h" +#include "rtio.h" #include "log.h" #include "dds.h" @@ -26,7 +26,7 @@ #endif #define DDS_WRITE(addr, data) do { \ - rt2wb_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ + rtio_output(now, CONFIG_RTIO_DDS_CHANNEL, addr, data); \ now += DURATION_WRITE; \ } while(0) diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index e15352bb5..add01a13a 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -13,7 +13,6 @@ #include "messages.h" #include "bridge.h" #include "artiq_personality.h" -#include "ttl.h" #include "dds.h" #include "rtio.h" #include "rt2wb.h" @@ -110,19 +109,14 @@ static const struct symbol runtime_exports[] = { /* direct syscalls */ {"rtio_get_counter", &rtio_get_counter}, {"rtio_log", &rtio_log}, - - {"ttl_set_o", &ttl_set_o}, - {"ttl_set_oe", &ttl_set_oe}, - {"ttl_set_sensitivity", &ttl_set_sensitivity}, - {"ttl_get", &ttl_get}, - {"ttl_clock_set", &ttl_clock_set}, + {"rtio_output", &rtio_output}, + {"rtio_input_timestamp", &rtio_input_timestamp}, {"dds_init", &dds_init}, {"dds_batch_enter", &dds_batch_enter}, {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_output", &rt2wb_output}, {"rt2wb_input", &rt2wb_input}, {"cache_get", &cache_get}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c index 6760149c4..ef71f3a91 100644 --- a/artiq/runtime/rt2wb.c +++ b/artiq/runtime/rt2wb.c @@ -5,13 +5,6 @@ #include "rt2wb.h" -void rt2wb_output(long long int timestamp, int channel, int addr, - unsigned int data) -{ - rtio_output(timestamp, channel, addr, data); -} - - unsigned int rt2wb_input(int channel) { unsigned int data; diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h index d421397f4..158a12d7c 100644 --- a/artiq/runtime/rt2wb.h +++ b/artiq/runtime/rt2wb.h @@ -3,10 +3,7 @@ #include "rtio.h" -void rt2wb_output(long long int timestamp, int channel, int addr, - unsigned int data); unsigned int rt2wb_input(int channel); -unsigned int rt2wb_input_sync(long long int timeout, int channel); #endif /* __RT2WB_H */ diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 0670a4e2b..a87bba88b 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -56,8 +56,9 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, } -int rtio_input_wait(long long int timeout, int channel) +long long int rtio_input_timestamp(long long int timeout, int channel) { + long long int r; int status; rtio_chan_sel_write(channel); @@ -76,7 +77,17 @@ int rtio_input_wait(long long int timeout, int channel) } /* input FIFO is empty - keep waiting */ } - return status; + + if (status & RTIO_I_STATUS_OVERFLOW) + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + if (status & RTIO_I_STATUS_EMPTY) + return -1; + + r = rtio_i_timestamp_read(); + rtio_i_re_write(1); + return r; } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 9cebb6f2f..1aef9f830 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,6 +19,6 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); -int rtio_input_wait(long long int timeout, int channel); +long long int rtio_input_timestamp(long long int timeout, int channel); #endif /* __RTIO_H */ diff --git a/artiq/runtime/ttl.c b/artiq/runtime/ttl.c deleted file mode 100644 index 603eb2a8c..000000000 --- a/artiq/runtime/ttl.c +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include "artiq_personality.h" -#include "rtio.h" -#include "ttl.h" - -void ttl_set_o(long long int timestamp, int channel, int value) -{ - rtio_output(timestamp, channel, 0, value); -} - -void ttl_set_oe(long long int timestamp, int channel, int oe) -{ - rtio_output(timestamp, channel, 1, oe); -} - -void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity) -{ - rtio_output(timestamp, channel, 2, sensitivity); -} - -long long int ttl_get(int channel, long long int time_limit) -{ - long long int r; - int status = rtio_input_wait(time_limit, channel); - - if (status & RTIO_I_STATUS_OVERFLOW) - artiq_raise_from_c("RTIOOverflow", - "RTIO input overflow on channel {0}", - channel, 0, 0); - if (status & RTIO_I_STATUS_EMPTY) - return -1; - - r = rtio_i_timestamp_read(); - rtio_i_re_write(1); - return r; -} - -void ttl_clock_set(long long int timestamp, int channel, int ftw) -{ - rtio_output(timestamp, channel, 0, ftw); -} diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h deleted file mode 100644 index 9d95a32f2..000000000 --- a/artiq/runtime/ttl.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __TTL_H -#define __TTL_H - -void ttl_set_o(long long int timestamp, int channel, int value); -void ttl_set_oe(long long int timestamp, int channel, int oe); -void ttl_set_sensitivity(long long int timestamp, int channel, int sensitivity); -long long int ttl_get(int channel, long long int time_limit); -void ttl_clock_set(long long int timestamp, int channel, int ftw); - -#endif /* __TTL_H */ From 6f9656dcbecb74295728bb6ccb2e135d32f75fc7 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:19:06 +0100 Subject: [PATCH 44/52] bridge: fix ttl o/oe addresses --- artiq/runtime/bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 02be42e4c..82fe3a16a 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -58,7 +58,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 0, msg->value); + rtio_output_blind(msg->channel, 1, msg->value); mailbox_acknowledge(); break; } @@ -66,7 +66,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 1, msg->value); + rtio_output_blind(msg->channel, 0, msg->value); mailbox_acknowledge(); break; } From 3aebbbdb61a7f231f8779a9731233578be1ffa87 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:22:03 +0100 Subject: [PATCH 45/52] coredevice.ttl: fix sensitivity --- artiq/coredevice/ttl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 1b3a6eecd..d5db21a67 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -150,7 +150,7 @@ class TTLInOut: @kernel def _set_sensitivity(self, value): - rtio_output(now_mu(), self.channel, 2, 1 if value else 0) + rtio_output(now_mu(), self.channel, 2, value) self.i_previous_timestamp = now_mu() @kernel From 135643e3a66db7bcfb2ab2ba4feb0bfe5b2e262e Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:22:42 +0100 Subject: [PATCH 46/52] runtime: define constants for ttl addresses --- artiq/runtime/bridge.c | 5 +++-- artiq/runtime/ttl.h | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 artiq/runtime/ttl.h diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 82fe3a16a..9c2e6d921 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -1,6 +1,7 @@ #include "mailbox.h" #include "messages.h" #include "rtio.h" +#include "ttl.h" #include "dds.h" #include "bridge.h" @@ -58,7 +59,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 1, msg->value); + rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); mailbox_acknowledge(); break; } @@ -66,7 +67,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, 0, msg->value); + rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); mailbox_acknowledge(); break; } diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h new file mode 100644 index 000000000..87fa74b7c --- /dev/null +++ b/artiq/runtime/ttl.h @@ -0,0 +1,8 @@ +#ifndef __TTL_H +#define __TTL_H + +#define TTL_O_ADDR 0 +#define TTL_OE_ADDR 1 +#define TTL_SENSITIVITY_ADDR 2 + +#endif /* __TTL_H */ From 81b35be5744044840d918fb4b9e4b35aba4353a6 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 18:49:04 +0100 Subject: [PATCH 47/52] bridge: really fix O/OE --- artiq/runtime/bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c index 9c2e6d921..d1157cf65 100644 --- a/artiq/runtime/bridge.c +++ b/artiq/runtime/bridge.c @@ -59,7 +59,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); + rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); mailbox_acknowledge(); break; } @@ -67,7 +67,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value); + rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value); mailbox_acknowledge(); break; } From baf7b0dcf2f572a6d1383b209714f4e2685492bf Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:04:03 +0100 Subject: [PATCH 48/52] examples/tdr: adapt to compiler changes --- .../master/repository/coredevice_examples/tdr.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/master/repository/coredevice_examples/tdr.py b/examples/master/repository/coredevice_examples/tdr.py index a71628645..d0c435753 100644 --- a/examples/master/repository/coredevice_examples/tdr.py +++ b/examples/master/repository/coredevice_examples/tdr.py @@ -39,6 +39,7 @@ class TDR(EnvExperiment): n = 1000 # repetitions latency = 50e-9 # calibrated latency without a transmission line pulse = 1e-6 # pulse length, larger than rtt + self.t = [0 for i in range(2)] try: self.many(n, seconds_to_mu(pulse, self.core)) except PulseNotReceivedError: @@ -53,21 +54,19 @@ class TDR(EnvExperiment): @kernel def many(self, n, p): - t = [0 for i in range(2)] self.core.break_realtime() for i in range(n): - self.one(t, p) - self.t = t + self.one(p) @kernel - def one(self, t, p): + def one(self, p): t0 = now_mu() with parallel: self.pmt0.gate_both_mu(2*p) self.ttl2.pulse_mu(p) - for i in range(len(t)): + for i in range(len(self.t)): ti = self.pmt0.timestamp_mu() if ti <= 0: - raise PulseNotReceivedError - t[i] += ti - t0 + raise PulseNotReceivedError() + self.t[i] = int(self.t[i] + ti - t0) self.pmt0.count() # flush From f30dc4b39e5beb9604cc45dbd858f118592df444 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:21:50 +0100 Subject: [PATCH 49/52] runtime: rt2wb_input -> rtio_input_data --- artiq/coredevice/rt2wb.py | 7 ------- artiq/coredevice/rtio.py | 5 +++++ artiq/coredevice/spi.py | 31 +++++++++++++++---------------- artiq/runtime/Makefile | 2 +- artiq/runtime/ksupport.c | 4 +--- artiq/runtime/rt2wb.c | 26 -------------------------- artiq/runtime/rt2wb.h | 9 --------- artiq/runtime/rtio.c | 21 +++++++++++++++++++++ artiq/runtime/rtio.h | 11 +++++++++++ 9 files changed, 54 insertions(+), 62 deletions(-) delete mode 100644 artiq/coredevice/rt2wb.py delete mode 100644 artiq/runtime/rt2wb.c delete mode 100644 artiq/runtime/rt2wb.h diff --git a/artiq/coredevice/rt2wb.py b/artiq/coredevice/rt2wb.py deleted file mode 100644 index 318d8bf0e..000000000 --- a/artiq/coredevice/rt2wb.py +++ /dev/null @@ -1,7 +0,0 @@ -from artiq.language.core import * -from artiq.language.types import * - - -@syscall -def rt2wb_input(channel: TInt32) -> TInt32: - raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py index 194818507..978d80fd1 100644 --- a/artiq/coredevice/rtio.py +++ b/artiq/coredevice/rtio.py @@ -11,3 +11,8 @@ def rtio_output(time_mu: TInt64, channel: TInt32, addr: TInt32, data: TInt32 @syscall def rtio_input_timestamp(timeout_mu: TInt64, channel: TInt32) -> TInt64: raise NotImplementedError("syscall not simulated") + + +@syscall +def rtio_input_data(channel: TInt32) -> TInt32: + raise NotImplementedError("syscall not simulated") diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 5998997e1..dcd1acc27 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,8 +1,7 @@ from artiq.language.core import (kernel, seconds_to_mu, now_mu, delay_mu, int) from artiq.language.units import MHz -from artiq.coredevice.rtio import rtio_output as rt2wb_output -from artiq.coredevice.rt2wb import rt2wb_input +from artiq.coredevice.rtio import rtio_output, rtio_input_data SPI_DATA_ADDR, SPI_XFER_ADDR, SPI_CONFIG_ADDR = range(3) @@ -50,48 +49,48 @@ class SPIMaster: @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): - rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | - ((write_div - 2) << 16) | ((read_div - 2) << 24)) + rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | + ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) self.read_period_mu = int(read_div*self.ref_period_mu) delay_mu(3*self.ref_period_mu) @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): - rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR, - chip_select | (write_length << 16) | (read_length << 24)) + rtio_output(now_mu(), self.channel, SPI_XFER_ADDR, + chip_select | (write_length << 16) | (read_length << 24)) self.xfer_period_mu = int(write_length*self.write_period_mu + read_length*self.read_period_mu) delay_mu(3*self.ref_period_mu) @kernel def write(self, data): - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR, data) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) delay_mu(3*self.ref_period_mu) @kernel def read_async(self): # every read_async() must be matched by an input_async() - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel def input_async(self): # matches the preeeding read_async() - return rt2wb_input(self.channel) + return rtio_input_data(self.channel) @kernel def read_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) + return rtio_input_data(self.channel) @kernel def _get_xfer_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_XFER_ADDR | SPI_RT2WB_READ, 0) + return rtio_input_data(self.channel) @kernel def _get_config_sync(self): - rt2wb_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, - 0) - return rt2wb_input(self.channel) + rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR | SPI_RT2WB_READ, + 0) + return rtio_input_data(self.channel) diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index 4411790dc..2a7b37e2e 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -7,7 +7,7 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ ksupport_data.o kloader.o test_mode.o main.o OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o rt2wb.o dds.o + bridge.o rtio.o dds.o CFLAGS += -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c index add01a13a..8ed50f131 100644 --- a/artiq/runtime/ksupport.c +++ b/artiq/runtime/ksupport.c @@ -15,7 +15,6 @@ #include "artiq_personality.h" #include "dds.h" #include "rtio.h" -#include "rt2wb.h" double round(double x); @@ -111,14 +110,13 @@ static const struct symbol runtime_exports[] = { {"rtio_log", &rtio_log}, {"rtio_output", &rtio_output}, {"rtio_input_timestamp", &rtio_input_timestamp}, + {"rtio_input_data", &rtio_input_data}, {"dds_init", &dds_init}, {"dds_batch_enter", &dds_batch_enter}, {"dds_batch_exit", &dds_batch_exit}, {"dds_set", &dds_set}, - {"rt2wb_input", &rt2wb_input}, - {"cache_get", &cache_get}, {"cache_put", &cache_put}, diff --git a/artiq/runtime/rt2wb.c b/artiq/runtime/rt2wb.c deleted file mode 100644 index ef71f3a91..000000000 --- a/artiq/runtime/rt2wb.c +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include "artiq_personality.h" -#include "rtio.h" -#include "rt2wb.h" - - -unsigned int rt2wb_input(int channel) -{ - unsigned int data; - int status; - - rtio_chan_sel_write(channel); - while((status = rtio_i_status_read())) { - if(status & RTIO_I_STATUS_OVERFLOW) { - rtio_i_overflow_reset_write(1); - artiq_raise_from_c("RTIOOverflow", - "RT2WB input overflow on channel {0}", - channel, 0, 0); - } - } - - data = rtio_i_data_read(); - rtio_i_re_write(1); - return data; -} diff --git a/artiq/runtime/rt2wb.h b/artiq/runtime/rt2wb.h deleted file mode 100644 index 158a12d7c..000000000 --- a/artiq/runtime/rt2wb.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __RT2WB_H -#define __RT2WB_H - -#include "rtio.h" - -unsigned int rt2wb_input(int channel); - -#endif /* __RT2WB_H */ - diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index a87bba88b..7a4faa78c 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -91,6 +91,27 @@ long long int rtio_input_timestamp(long long int timeout, int channel) } +unsigned int rtio_input_data(int channel) +{ + unsigned int data; + int status; + + rtio_chan_sel_write(channel); + while((status = rtio_i_status_read())) { + if(status & RTIO_I_STATUS_OVERFLOW) { + rtio_i_overflow_reset_write(1); + artiq_raise_from_c("RTIOOverflow", + "RTIO input overflow on channel {0}", + channel, 0, 0); + } + } + + data = rtio_i_data_read(); + rtio_i_re_write(1); + return data; +} + + void rtio_log_va(long long int timestamp, const char *fmt, va_list args) { // This executes on the kernel CPU's stack, which is specifically designed diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 1aef9f830..253a64fad 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -19,6 +19,17 @@ void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address, unsigned int data); + +/* + * Waits at least until timeout and returns the timestamp of the first + * input event on the chanel, -1 if there was no event. + */ long long int rtio_input_timestamp(long long int timeout, int channel); +/* + * Assumes that there is or will be an event in the channel and returns only + * its data. + */ +unsigned int rtio_input_data(int channel); + #endif /* __RTIO_H */ From 2cc1dfaee3d72566cb5b147c7b51cf6dab55c422 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 19:40:32 +0100 Subject: [PATCH 50/52] kc705: move ttl channels together again, update doc --- artiq/gateware/targets/kc705.py | 8 ++++---- doc/manual/core_device.rst | 8 ++++---- examples/master/device_db.pyon | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 5872e0f5f..3bc8db6f2 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -256,6 +256,10 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy(phy)) self.config["RTIO_REGULAR_TTL_COUNT"] = len(rtio_channels) + phy = ttl_simple.ClockGen(platform.request("la32_p")) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + phy = spi.SPIMaster(ams101_dac) self.submodules += phy self.config["RTIO_FIRST_SPI_CHANNEL"] = len(rtio_channels) @@ -268,10 +272,6 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=128, ififo_depth=128)) - phy = ttl_simple.ClockGen(platform.request("la32_p")) - self.submodules += phy - rtio_channels.append(rtio.Channel.from_phy(phy)) - self.config["RTIO_DDS_CHANNEL"] = len(rtio_channels) self.config["DDS_CHANNEL_COUNT"] = 11 self.config["DDS_AD9914"] = True diff --git a/doc/manual/core_device.rst b/doc/manual/core_device.rst index 8000a6770..c0369b184 100644 --- a/doc/manual/core_device.rst +++ b/doc/manual/core_device.rst @@ -64,7 +64,9 @@ With the CLOCK hardware, the TTL lines are mapped as follows: +--------------------+-----------------------+--------------+ | 19 | LED | Output | +--------------------+-----------------------+--------------+ -| 20 | LA32_P | Clock | +| 20 | AMS101_LDAC_B | Output | ++--------------------+-----------------------+--------------+ +| 21 | LA32_P | Clock | +--------------------+-----------------------+--------------+ @@ -101,6 +103,4 @@ When plugged to an adapter, the NIST QC1 hardware can be used. The TTL lines are The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Pipistrello board), the corresponding pins on the Pipistrello can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention. -The board can accept an external RTIO clock connected to PMT2. If the DDS box -does not drive the PMT2 pair, use XTRIG and patch the XTRIG transceiver output -on the adapter board onto C:15 disconnecting PMT2. +The board can accept an external RTIO clock connected to PMT2. If the DDS box does not drive the PMT2 pair, use XTRIG and patch the XTRIG transceiver output on the adapter board onto C:15 disconnecting PMT2. diff --git a/examples/master/device_db.pyon b/examples/master/device_db.pyon index 5debbc7d3..1644ed176 100644 --- a/examples/master/device_db.pyon +++ b/examples/master/device_db.pyon @@ -87,24 +87,24 @@ "class": "TTLOut", "arguments": {"channel": 20} }, - "ams101_spi": { + "ttl_clock_la32_p": { "type": "local", - "module": "artiq.coredevice.spi", - "class": "SPIMaster", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", "arguments": {"channel": 21} }, - "spi0": { + "ams101_spi": { "type": "local", "module": "artiq.coredevice.spi", "class": "SPIMaster", "arguments": {"channel": 22} }, - "ttl_clock_la32_p": { + "spi0": { "type": "local", - "module": "artiq.coredevice.ttl", - "class": "TTLClockGen", + "module": "artiq.coredevice.spi", + "class": "SPIMaster", "arguments": {"channel": 23} }, From 0456169558e915ba811c6bb7304983e039db5add Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 21:29:09 +0100 Subject: [PATCH 51/52] coredevice.spi, doc/manual: add spi --- artiq/coredevice/spi.py | 160 +++++++++++++++++++++++++- doc/manual/core_drivers_reference.rst | 9 +- 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index dcd1acc27..f1ee52f85 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -21,9 +21,25 @@ SPI_RT2WB_READ = 1 << 2 class SPIMaster: """Core device Serial Peripheral Interface (SPI) bus master. + Owns one SPI bus. + + **Transfer Sequence**: + + * If desired, write the ``config`` register (:meth:`set_config`) + to configure and activate the core. + * If desired, write the ``xfer`` register (:meth:`set_xfer`) + to set ``cs_n``, ``write_length``, and ``read_length``. + * :meth:`write` to the ``data`` register (also for transfers with + zero bits to be written). Writing starts the transfer. + * If desired, :meth:`read_sync` (or :meth:`read_async` followed by a + :meth:`input_async` later) the ``data`` register corresponding to + the last completed transfer. + * If desired, :meth:`set_xfer` for the next transfer. + * If desired, :meth:`write` ``data`` queuing the next + (possibly chained) transfer. :param ref_period: clock period of the SPI core. - :param channel: channel number of the SPI bus to control. + :param channel: RTIO channel number of the SPI bus to control. """ def __init__(self, dmgr, ref_period, channel): self.core = dmgr.get("core") @@ -43,12 +59,69 @@ class SPIMaster: @kernel def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): + """Set the configuration register. + + * If ``config.cs_polarity`` == 0 (```cs`` active low, the default), + "``cs_n`` all deasserted" means "all ``cs_n`` bits high". + * ``cs_n`` is not mandatory in the pads supplied to the gateware core. + Framing and chip selection can also be handled independently + through other means, e.g. ``TTLOut``. + * If there is a ``miso`` wire in the pads supplied in the gateware, + input and output may be two signals ("4-wire SPI"), + otherwise ``mosi`` must be used for both output and input + ("3-wire SPI") and ``config.half_duplex`` must to be set + when reading data is desired or when the slave drives the + ``mosi`` signal at any point. + * The first bit output on ``mosi`` is always the MSB/LSB (depending + on ``config.lsb_first``) of the ``data`` register, independent of + ``xfer.write_length``. The last bit input from ``miso`` always ends + up in the LSB/MSB (respectively) of the ``data`` register, + independent of ``xfer.read_length``. + * Writes to the ``config`` register take effect immediately. + + **Configuration flags**: + + * :const:`SPI_OFFLINE`: all pins high-z (reset=1) + * :const:`SPI_ACTIVE`: transfer in progress (read-only) + * :const:`SPI_PENDING`: transfer pending in intermediate buffer + (read-only) + * :const:`SPI_CS_POLARITY`: active level of ``cs_n`` (reset=0) + * :const:`SPI_CLK_POLARITY`: idle level of ``clk`` (reset=0) + * :const:`SPI_CLK_PHASE`: first edge after ``cs`` assertion to sample + data on (reset=0). In Motorola/Freescale SPI language + (:const:`SPI_CLK_POLARITY`, :const:`SPI_CLK_PHASE`) == (CPOL, CPHA): + + - (0, 0): idle low, output on falling, input on rising + - (0, 1): idle low, output on rising, input on falling + - (1, 0): idle high, output on rising, input on falling + - (1, 1): idle high, output on falling, input on rising + * :const:`SPI_LSB_FIRST`: LSB is the first bit on the wire (reset=0) + * :const:`SPI_HALF_DUPLEX`: 3-wire SPI, in/out on ``mosi`` (reset=0) + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + + :param flags: A bit map of `SPI_*` flags. + :param write_freq: Desired SPI clock frequency during write bits. + :param read_freq: Desired SPI clock frequency during read bits. + """ write_div = round(1/(write_freq*self.ref_period)) read_div = round(1/(read_freq*self.ref_period)) self.set_config_mu(flags, write_div, read_div) @kernel def set_config_mu(self, flags=0, write_div=6, read_div=6): + """Set the ``config`` register (in SPI bus machine units). + + .. seealso:: :meth:`set_config` + + :param write_div: Counter load value to divide the RTIO + clock by to generate the SPI write clk. (minimum=2, reset=2) + ``f_rtio_clk/f_spi_write == write_div``. If ``write_div`` is odd, + the setup phase of the SPI clock is biased to longer lengths + by one RTIO clock cycle. + :param read_div: Ditto for the read clock. + """ rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | ((write_div - 2) << 16) | ((read_div - 2) << 24)) self.write_period_mu = int(write_div*self.ref_period_mu) @@ -57,6 +130,43 @@ class SPIMaster: @kernel def set_xfer(self, chip_select=0, write_length=0, read_length=0): + """Set the ``xfer`` register. + + * Every transfer consists of a write of ``write_length`` bits + immediately followed by a read of ``read_length`` bits. + * ``cs_n`` is asserted at the beginning and deasserted at the end + of the transfer if there is no other transfer pending. + * ``cs_n`` handling is agnostic to whether it is one-hot or decoded + somewhere downstream. If it is decoded, "``cs_n`` all deasserted" + should be handled accordingly (no slave selected). + If it is one-hot, asserting multiple slaves should only be attempted + if ``miso`` is either not connected between slaves, or open + collector, or correctly multiplexed externally. + * For 4-wire SPI only the sum of ``read_length`` and ``write_length`` + matters. The behavior is the same (except for clock speeds) no matter + how the total transfer length is divided between the two. For + 3-wire SPI, the direction of ``mosi`` is switched from output to + input after ``write_length`` bits. + * Data output on ``mosi`` in 4-wire SPI during the read cycles is what + is found in the data register at the time. + Data in the ``data`` register outside the least/most (depending + on ``config.lsb_first``) significant ``read_length`` bits is what is + seen on ``miso`` (or ``mosi`` if ``config.half_duplex``) + during the write cycles. + * Writes to ``xfer`` are synchronized to the start of the next + (possibly chained) transfer. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + + :param chip_select: Bit mask of chip selects to assert. Or number of + the chip select to assert if ``cs`` is decoded downstream. + (reset=0) + :param write_length: Number of bits to write during the next transfer. + (reset=0) + :param read_length: Number of bits to read during the next transfer. + (reset=0) + """ rtio_output(now_mu(), self.channel, SPI_XFER_ADDR, chip_select | (write_length << 16) | (read_length << 24)) self.xfer_period_mu = int(write_length*self.write_period_mu + @@ -65,24 +175,64 @@ class SPIMaster: @kernel def write(self, data): + """Write data to data register. + + * The ``data`` register and the shift register are 32 bits wide. + If there are no writes to the register, ``miso`` data reappears on + ``mosi`` after 32 cycles. + * A wishbone data register write is acknowledged when the + transfer has been written to the intermediate buffer. + It will be started when there are no other transactions being + executed, either beginning a new SPI transfer of chained + to an in-flight transfer. + * Writes take three ``ref_period`` cycles unless another + chained transfer is pending and the transfer being + executed is not complete. + * The SPI ``data`` register is double-buffered: Once a transfer has + started, new write data can be written, queuing a new transfer. + Transfers submitted this way are chained and executed without + deasserting ``cs`` in between. Once a transfer completes, + the previous transfer's read data is available in the + ``data`` register. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) delay_mu(3*self.ref_period_mu) @kernel def read_async(self): - # every read_async() must be matched by an input_async() + """Trigger an asynchronous read from the data register. + + Reads always finish in two cycles. + + Every data register read triggered by a :meth:`read_async` + must be matched by a :meth:`input_async` to retrieve the data. + + This method advances the timeline by the duration of the + RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) delay_mu(3*self.ref_period_mu) @kernel def input_async(self): - # matches the preeeding read_async() + """Retrieves data written asynchronously. + + :meth:`input_async` must match a preeeding :meth:`read_async`. + """ return rtio_input_data(self.channel) @kernel def read_sync(self): - rtio_output(now_mu(), self.channel, SPI_DATA_ADDR | SPI_RT2WB_READ, 0) - return rtio_input_data(self.channel) + """Read the ``data`` register synchronously. + + This is a shortcut for :meth:`read_async` followed by + :meth:`input_async`. + """ + self.read_async() + return self.input_async() @kernel def _get_xfer_sync(self): diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index 604d823b5..e88e077e5 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -19,11 +19,18 @@ These drivers are for the core device and the peripherals closely integrated int ---------------------------------- .. autoclass:: artiq.coredevice.dds._DDSGeneric - :members: + :members: .. automodule:: artiq.coredevice.dds :members: +:mod:`artiq.coredevice.spi` module +---------------------------------- + +.. automodule:: artiq.coredevice.spi + :members: + + :mod:`artiq.coredevice.exceptions` module ----------------------------------------- From 5ba753425d0c33b7005d8fea8f81b728206ccc23 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 1 Mar 2016 21:38:51 +0100 Subject: [PATCH 52/52] runtime/rtio: rtio_process_exceptional_status() has only one user --- artiq/runtime/rtio.c | 5 +++-- artiq/runtime/rtio.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 7a4faa78c..6a136267c 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -15,7 +15,8 @@ long long int rtio_get_counter(void) return rtio_counter_read(); } -void rtio_process_exceptional_status(int status, long long int timestamp, int channel) +static void rtio_process_exceptional_status( + long long int timestamp, int channel, int status) { if(status & RTIO_O_STATUS_FULL) while(rtio_o_status_read() & RTIO_O_STATUS_FULL); @@ -52,7 +53,7 @@ void rtio_output(long long int timestamp, int channel, unsigned int addr, rtio_o_we_write(1); status = rtio_o_status_read(); if(status) - rtio_process_exceptional_status(status, timestamp, channel); + rtio_process_exceptional_status(timestamp, channel, status); } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index 253a64fad..709a85be3 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -14,7 +14,6 @@ void rtio_init(void); long long int rtio_get_counter(void); -void rtio_process_exceptional_status(int status, long long int timestamp, int channel); void rtio_log(long long int timestamp, const char *format, ...); void rtio_log_va(long long int timestamp, const char *format, va_list args); void rtio_output(long long int timestamp, int channel, unsigned int address,