from operator import attrgetter from migen import * from migen.genlib.cdc import MultiReg from migen_axi.interconnect import axi from misoc.interconnect.csr import * from artiq.gateware import rtio OUT_BURST_LEN = 10 IN_BURST_LEN = 4 class Engine(Module, AutoCSR): def __init__(self, bus, user): self.addr_base = CSRStorage(32) self.trig_count = CSRStatus(32) self.write_count = CSRStatus(32) self.trigger_stb = Signal() # Dout : Data received from CPU, output by DMA module # Din : Data driven into DMA module, written into CPU # When stb assert, index shows word being read/written, dout/din holds # data # # Cycle: # trigger_stb pulsed at start # Then out_burst_len words are strobed out of dout # Then, when din_ready is high, in_burst_len words are strobed in to din self.dout_stb = Signal() self.din_stb = Signal() self.dout_index = Signal(max=16) self.din_index = Signal(max=16) self.din_ready = Signal() self.dout = Signal(64) self.din = Signal(64) ### self.sync += If(self.trigger_stb, self.trig_count.status.eq(self.trig_count.status+1)) self.comb += [ user.aruser.eq(0x1f), user.awuser.eq(0x1f) ] ar, aw, w, r, b = attrgetter("ar", "aw", "w", "r", "b")(bus) ### Read self.comb += [ ar.addr.eq(self.addr_base.storage), self.dout.eq(r.data), r.ready.eq(1), ar.burst.eq(axi.Burst.incr.value), ar.len.eq(OUT_BURST_LEN-1), # Number of transfers in burst (0->1 transfer, 1->2 transfers...) ar.size.eq(3), # Width of burst: 3 = 8 bytes = 64 bits ar.cache.eq(0xf), ] # read control self.submodules.read_fsm = read_fsm = FSM(reset_state="IDLE") read_fsm.act("IDLE", If(self.trigger_stb, ar.valid.eq(1), If(ar.ready, NextState("READ") ).Else( NextState("READ_START") ) ) ) read_fsm.act("READ_START", ar.valid.eq(1), If(ar.ready, NextState("READ"), ) ) read_fsm.act("READ", ar.valid.eq(0), If(r.last & r.valid, NextState("IDLE") ) ) self.sync += [ If(read_fsm.ongoing("IDLE"), self.dout_index.eq(0) ).Else(If(r.valid & read_fsm.ongoing("READ"), self.dout_index.eq(self.dout_index+1) ) ) ] self.comb += self.dout_stb.eq(r.valid & r.ready) ### Write self.comb += [ w.data.eq(self.din), aw.addr.eq(self.addr_base.storage+96), w.strb.eq(0xff), aw.burst.eq(axi.Burst.incr.value), aw.len.eq(IN_BURST_LEN-1), # Number of transfers in burst minus 1 aw.size.eq(3), # Width of burst: 3 = 8 bytes = 64 bits aw.cache.eq(0xf), b.ready.eq(1), ] # write control self.submodules.write_fsm = write_fsm = FSM(reset_state="IDLE") write_fsm.act("IDLE", w.valid.eq(0), aw.valid.eq(0), If(self.trigger_stb, aw.valid.eq(1), If(aw.ready, # assumes aw.ready is not randomly deasserted NextState("DATA_WAIT") ).Else( NextState("AW_READY_WAIT") ) ) ) write_fsm.act("AW_READY_WAIT", aw.valid.eq(1), If(aw.ready, NextState("DATA_WAIT"), ) ) write_fsm.act("DATA_WAIT", aw.valid.eq(0), If(self.din_ready, w.valid.eq(1), NextState("WRITE") ) ) write_fsm.act("WRITE", w.valid.eq(1), If(w.ready & w.last, NextState("IDLE") ) ) self.sync += If(w.ready & w.valid, self.write_count.status.eq(self.write_count.status+1)) self.sync += [ If(write_fsm.ongoing("IDLE"), self.din_index.eq(0) ), If(w.ready & w.valid, self.din_index.eq(self.din_index+1)) ] self.comb += [ w.last.eq(0), If(self.din_index==aw.len, w.last.eq(1)) ] self.comb += self.din_stb.eq(w.valid & w.ready) class KernelInitiator(Module, AutoCSR): def __init__(self, tsc, bus, user, evento): # Core is disabled upon reset to avoid spurious triggering if evento toggles from e.g. boot code. self.enable = CSRStorage() self.counter = CSRStatus(64) self.counter_update = CSR() self.o_status = CSRStatus(3) self.i_status = CSRStatus(4) self.submodules.engine = Engine(bus, user) self.cri = rtio.cri.Interface() ### evento_stb = Signal() evento_latched = Signal() evento_latched_d = Signal() self.specials += MultiReg(evento, evento_latched) self.sync += [evento_latched_d.eq(evento_latched)] self.comb += self.engine.trigger_stb.eq(self.enable.storage & (evento_latched != evento_latched_d)) cri = self.cri cmd = Signal(8) cmd_write = Signal() cmd_read = Signal() self.comb += [ cmd_write.eq(cmd == 0), cmd_read.eq(cmd == 1) ] out_len = Signal(8) dout_cases = {} dout_cases[0] = [ cmd.eq(self.engine.dout[:8]), out_len.eq(self.engine.dout[8:16]), cri.chan_sel.eq(self.engine.dout[40:]), cri.o_address.eq(self.engine.dout[32:40]) ] for i in range(8): target = cri.o_data[i*64:(i+1)*64] dout_cases[0] += [If(i >= self.engine.dout[8:16], target.eq(0))] dout_cases[1] = [ cri.o_timestamp.eq(self.engine.dout) ] for i in range(8): target = cri.o_data[i*64:(i+1)*64] dout_cases[i+2] = [target.eq(self.engine.dout)] self.sync += [ cri.cmd.eq(rtio.cri.commands["nop"]), If(self.engine.dout_stb, Case(self.engine.dout_index, dout_cases), If(self.engine.dout_index == 2, If(cmd_write, cri.cmd.eq(rtio.cri.commands["write"])), If(cmd_read, cri.cmd.eq(rtio.cri.commands["read"])) ) ) ] # If input event, wait for response before allow input data to be # sampled # TODO: If output, wait for wait flag clear RTIO_I_STATUS_WAIT_STATUS = 4 RTIO_O_STATUS_WAIT = 1 self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", If(self.engine.trigger_stb, NextState("WAIT_OUT_CYCLE")) ) fsm.act("WAIT_OUT_CYCLE", self.engine.din_ready.eq(0), If(self.engine.dout_stb & (self.engine.dout_index == out_len + 2), NextState("WAIT_READY") ) ) fsm.act("WAIT_READY", If(cmd_read & (cri.i_status & RTIO_I_STATUS_WAIT_STATUS == 0) \ | cmd_write & ~(cri.o_status & RTIO_O_STATUS_WAIT), self.engine.din_ready.eq(1), NextState("IDLE") ) ) din_cases_cmdwrite = { 0: [self.engine.din.eq((1<<16) | cri.o_status)], 1: [self.engine.din.eq(0)], } din_cases_cmdread = { 0: [self.engine.din[:32].eq((1<<16) | cri.i_status), self.engine.din[32:].eq(cri.i_data)], 1: [self.engine.din.eq(cri.i_timestamp)] } self.comb += [ If(cmd_read, Case(self.engine.din_index, din_cases_cmdread)), If(cmd_write, Case(self.engine.din_index, din_cases_cmdwrite)), ] # CRI CSRs self.sync += If(self.counter_update.re, self.counter.status.eq(tsc.full_ts_cri)) self.comb += [ self.o_status.status.eq(self.cri.o_status), self.i_status.status.eq(self.cri.i_status), ]