forked from M-Labs/artiq-zynq
Based on work by Chris Ballance https://github.com/m-labs/artiq/issues/1167#issuecomment-427188287 M-Labs/artiq-zynq#55 Work-in-progress, only gateware part and build system, untested.core0-buffer
parent
c9bac028bf
commit
f8d4036451
@ -0,0 +1,260 @@ |
||||
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 = 4 |
||||
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+32), # Write to next cache line |
||||
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.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) |
||||
] |
||||
|
||||
dout_cases = {} |
||||
dout_lw = Signal(32) |
||||
dout_hw = Signal(32) |
||||
self.comb += [ |
||||
dout_lw.eq(self.engine.dout[:32]), |
||||
dout_hw.eq(self.engine.dout[32:]) |
||||
] |
||||
dout_cases[0] = [ |
||||
cmd.eq(dout_lw[24:]), |
||||
cri.chan_sel.eq(dout_lw[:24]), |
||||
cri.o_address.eq(dout_hw[:16]) |
||||
] |
||||
dout_cases[1] = [ |
||||
cri.o_timestamp.eq(self.engine.dout) |
||||
] |
||||
dout_cases[2] = [cri.o_data.eq(self.engine.dout)] # only lowest 64 bits |
||||
|
||||
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 == 3), |
||||
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)), |
||||
] |
||||
|
||||
# RTIO counter access |
||||
self.sync += If(self.counter_update.re, self.counter.status.eq(tsc.full_ts_cri)) |
@ -0,0 +1,81 @@ |
||||
use core::ptr::{self, read_volatile, write_volatile}; |
||||
use core::ffi::VaList; |
||||
use alloc::vec; |
||||
use cslice::CSlice; |
||||
use libc::{c_char, c_int, size_t}; |
||||
|
||||
use crate::artiq_raise; |
||||
|
||||
use crate::pl::csr; |
||||
|
||||
|
||||
#[repr(C)] |
||||
pub struct TimestampedData { |
||||
timestamp: i64, |
||||
data: i32, |
||||
} |
||||
|
||||
pub extern fn init() { |
||||
unsafe { |
||||
csr::rtio_core::reset_write(1); |
||||
} |
||||
} |
||||
|
||||
pub extern fn get_destination_status(destination: i32) -> bool { |
||||
// TODO
|
||||
destination == 0 |
||||
} |
||||
|
||||
pub extern fn get_counter() -> i64 { |
||||
unsafe { |
||||
csr::rtio::counter_update_write(1); |
||||
csr::rtio::counter_read() as i64 |
||||
} |
||||
} |
||||
|
||||
pub extern fn now_mu() -> i64 { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn at_mu(t: i64) { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn delay_mu(dt: i64) { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn output(target: i32, data: i32) { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn output_wide(target: i32, data: CSlice<i32>) { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn input_data(channel: i32) -> i32 { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedData { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
extern "C" { |
||||
fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int; |
||||
} |
||||
|
||||
fn write_rtio_log(data: &[i8]) { |
||||
unimplemented!(); |
||||
} |
||||
|
||||
pub unsafe extern fn log(fmt: *const c_char, mut args: ...) { |
||||
let size = vsnprintf_(ptr::null_mut(), 0, fmt, args.as_va_list()) as usize; |
||||
let mut buf = vec![0; size + 1]; |
||||
vsnprintf_(buf.as_mut_ptr(), size + 1, fmt, args.as_va_list()); |
||||
write_rtio_log(buf.as_slice()); |
||||
} |
Loading…
Reference in new issue