Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f4c6d6eb69a6
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: cd3f68ba7668
Choose a head ref
  • 2 commits
  • 2 files changed
  • 1 contributor

Commits on Nov 29, 2016

  1. Copy the full SHA
    cf342ec View commit details

Commits on Nov 30, 2016

  1. Copy the full SHA
    cd3f68b View commit details
Showing with 332 additions and 1 deletion.
  1. +331 −0 artiq/gateware/rtio/dma.py
  2. +1 −1 artiq/gateware/targets/kc705_drtio_master.py
331 changes: 331 additions & 0 deletions artiq/gateware/rtio/dma.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
from migen import *
from migen.genlib.record import Record, layout_len
from migen.genlib.fsm import FSM
from misoc.interconnect.csr import *
from misoc.interconnect import stream, wishbone

from artiq.gateware.rtio import cri


class WishboneReader(Module):
def __init__(self, bus=None):
if bus is None:
bus = wishbone.Interface
self.bus = bus

aw = len(bus.adr)
dw = len(bus.dat_w)
self.sink = stream.Endpoint(["address", aw])
self.source = stream.Endpoint(["data", dw])

# # #

bus_stb = Signal()
data_reg_loaded = Signal()

self.comb += [
bus_stb.eq(self.sink.stb & (~data_reg_loaded | self.source.ack)),
bus.cyc.eq(bus_stb),
bus.stb.eq(bus_stb),
bus.adr.eq(self.sink.address),
self.sink.ack.eq(bus.ack),
self.source.stb.eq(data_reg_loaded),
]
self.sync += [
If(self.source.ack, data_reg_loaded.eq(0)),
If(bus.ack,
data_reg_loaded.eq(1),
self.source.data.eq(bus.dat_r),
self.source.eop.eq(self.sink.eop)
)
]


class DMAReader(Module, AutoCSR):
def __init__(self, membus, enable):
aw = len(membus.adr)
data_alignment = log2_int(len(membus.dat_w)//8)

self.submodules.wb_reader = WishboneReader(membus)
self.source = self.wb_reader.source

# All numbers in bytes
self.base_address = CSRStorage(aw + data_alignment,
alignment_bits=data_alignment)
self.last_address = CSRStorage(aw + data_alignment,
alignment_bits=data_alignment)

# # #

enable_r = Signal()
address = self.wb_reader.sink
self.sync += [
enable_r.eq(enable),
If(enable & ~enable_r,
address.address.eq(self.base_address.storage),
address.eop.eq(0),
address.stb.eq(1)
),
If(address.stb & address.ack,
If(address.eop,
address.stb.eq(0)
).Else(
address.address.eq(address.address + 1),
If(~enable | (address.address == self.last_address.storage),
address.eop.eq(1)
)
)
)
]


class RawSlicer(Module):
def __init__(self, in_size, out_size, granularity):
g = granularity

self.sink = stream.Endpoint([("data", in_size*g)])
self.source = Signal(out_size*g)
self.source_stb = Signal()
self.source_consume = Signal(max=out_size+1)

# # #

# worst-case buffer space required (when loading):
# <data being shifted out> <new incoming word> <EOP marker>
buf_size = out_size - 1 + in_size + 1
buf = Signal(buf_size*g)
self.comb += self.source.eq(buf[:out_size])

level = Signal(max=buf_size+1)
next_level = Signal(max=buf_size+1)
self.sync += level.eq(next_level)
self.comb += next_level.eq(level)

load_buf = Signal()
shift_buf = Signal()

self.sync += [
If(load_buf, Case(level,
# note how the MSBs of the buffer are set to 0
# (including the EOP marker position)
{i: buf[i*g:].eq(self.sink.data)
for i in range(out_size)})),
If(shift_buf, buf.eq(buf >> self.source_consume*g))
]

fsm = FSM(reset_state="FETCH")
self.submodules += fsm

fsm.act("FETCH",
self.sink.ack.eq(1),
load_buf.eq(1),
If(self.sink.stb,
If(self.sink.eop,
# insert <granularity> bits of 0 to mark EOP
next_level.eq(level + in_size + 1)
).Else(
next_level.eq(level + in_size)
)
),
If(next_level >= out_size, NextState("OUTPUT"))
)
fsm.act("OUTPUT",
self.source_stb.eq(1),
shift_buf.eq(1),
next_level.eq(level - self.source_consume),
If(next_level < out_size, NextState("FETCH"))
)


record_layout = [
("length", 8), # of whole record (header+data)
("channel", 24),
("timestamp", 64),
("address", 16),
("data", 512) # variable length
]


class RecordConverter(Module):
def __init__(self, stream_slicer):
self.source = stream.Endpoint(record_layout)

hdrlen = layout_len(header_layout) - 512
record_raw = Record(record_layout)
self.comb += [
record_raw.raw_bits().eq(stream_slicer.source),

record.channel.eq(record_raw.channel),
record.timestamp.eq(record_raw.timestamp),
record.address.eq(record_raw.address),
Case(record_raw.length,
{hdrlen+i*8: self.cri.o_data.eq(header.data[:])
for i in range(512//8)}),

self.source.stb.eq(stream_slicer.source_stb),
self.source.eop.eq(record_raw.length == 0),
If(self.source.ack,
If(record_raw.length == 0,
stream_slicer.source_consume.eq(1)
).Else(
stream_slicer.source_consume.eq(record_raw.length)
)
)
]


class RecordSlicer(Module):
def __init__(self, in_size):
self.submodules.raw_slicer = RawSlicer(
in_size, layout_len(record_layout)//8, 8)
self.submodules.record_converter = RecordConverter(self.raw_slicer)
self.sink = self.raw_slicer.sink
self.source = self.record_converter.source


class TimeOffset(Module, AutoCSR):
def __init__(self):
self.time_offset = CSRStorage(64)
self.source = stream.Endpoint(record_layout)
self.sink = stream.Endpoint(record_layout)

# # #

pipe_ce = Signal()
self.sync += \
If(pipe_ce,
self.source.payload.connect(self.sink.payload,
exclude={"timestamp"}),
self.source.payload.timestamp.eq(self.sink.payload.timestamp
+ self.time_offset.storage),
self.source.stb.eq(self.sink.stb)
)
self.comb += [
self.pipe_ce.eq(self.source.ack | ~self.source.stb),
self.sink.ack.eq(self.pipe_ce)
]


class CRIMaster(Module, AutoCSR):
def __init__(self):
self.arb_req = CSRStorage()
self.arb_gnt = CSRStatus()

self.error_status = CSRStatus(5) # same encoding as RTIO status
self.error_underflow_reset = CSR()
self.error_sequence_error_reset = CSR()
self.error_collision_reset = CSR()
self.error_busy_reset = CSR()

self.error_channel = CSRStatus(24)
self.error_timestamp = CSRStatus(64)
self.error_address = CSRStatus(16)

self.sink = stream.Endpoint(record_layout)
self.cri = cri.Interface()
self.busy = Signal()

# # #

self.comb += [
self.cri.arb_req.eq(self.arb_req.storage),
self.arb_gnt.status.eq(self.cri.arb_gnt)
]

error_set = Signal(4)
for i, rcsr in enumerate([self.error_underflow_reset, self.error_sequence_error_reset,
self.error_collision_reset, self.error_busy_reset]):
# bit 0 is RTIO wait and always 0 here
bit = i + 1
self.sync += [
If(error_set[i],
self.error_status[bit].eq(1),
self.error_channel.status.eq(self.sink.channel),
self.error_timestamp.status.eq(self.sink.timestamp),
self.error_address.status.eq(self.sink.address)
),
If(rcsr.re, self.error_status[bit].eq(0))
]

self.comb += [
self.cri.chan_sel.eq(self.sink.channel),
self.cri.o_timestamp.eq(self.sink.timestamp),
self.cri.o_address.eq(self.sink.address),
]

fsm = FSM(reset_state="IDLE")
self.submodules += fsm

fsm.act("IDLE",
If(self.error_status.status == 0,
If(self.sink.stb, NextState("WRITE"))
).Else(
# discard all data until errors are acked
self.sink.ack.eq(1)
)
)
fsm.act("WRITE",
self.busy.eq(1),
self.cri.cmd.eq(cri.commands["write"]),
NextState("CHECK_STATE")
)
fsm.act("CHECK_STATE",
self.busy.eq(1),
If(~self.cri.o_status,
self.sink.ack.eq(1),
NextState("IDLE")
),
If(self.cri.o_status[1], NextState("UNDERFLOW")),
If(self.cri.o_status[2], NextState("SEQUENCE_ERROR")),
If(self.cri.o_status[3], NextState("COLLISION")),
If(self.cri.o_status[4], NextState("BUSY"))
)
for n, name in enumerate(["UNDERFLOW", "SEQUENCE_ERROR",
"COLLISION", "BUSY"]):
fsm.act(name,
self.busy.eq(1),
error_set.eq(1 << n),
self.cri.cmd.eq(cri.commands["o_" + name.lower() + "_reset"]),
self.sink.ack.eq(1),
NextState("IDLE")
)


class DMA(Module):
def __init__(self, membus):
# shutdown procedure: set enable to 0, wait until busy=0
self.enable = CSRStorage()
self.busy = CSRStatus()

self.submodules.dma = DMAReader(membus, self.enable.storage)
self.submodules.slicer = RecordSlicer(len(membus.dat_w))
self.submodules.time_offset = TimeOffset()
self.submodules.cri_master = CRIMaster()

self.comb += [
self.dma.source.connect(self.slicer.sink),
self.slicer.source.connect(self.time_offset.sink),
self.time_offset.source.connect(self.cri_master.sink)
]

fsm = FSM(reset_state="IDLE")
self.submodules += fsm

fsm.act("IDLE",
If(self.enable.storage, NextState("FLOWING"))
)
fsm.act("FLOWING",
self.busy.status.eq(1),
If(self.cri_master.sink.stb & self.cri_master.sink.ack & self.cri_master.sink.eop,
NextState("WAIT_CRI_MASTER")
)
)
fsm.act("WAIT_CRI_MASTER",
self.busy.status.eq(1),
If(~self.cri_master.busy, NextState("IDLE"))
)

def get_csrs(self):
return ([self.enable, self.busy] +
self.dma.get_csrs() + self.time_offset.get_csrs() +
self.cri_master.get_csrs())
2 changes: 1 addition & 1 deletion artiq/gateware/targets/kc705_drtio_master.py
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ def __init__(self, **kwargs):
phy = ttl_simple.Inout(platform.request(sma))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.submodules.rtio_core = rtio.Core(rtio_channels, 2)
self.submodules.rtio_core = rtio.Core(rtio_channels, 3)

self.submodules.cridec = rtio.CRIDecoder([self.drtio.cri, self.rtio_core.cri])
self.submodules.rtio = rtio.KernelInitiator(self.cridec.master)