Skip to main content

rhdl_ubft/
wire.rs

1//! The words on rhdl_ubft's streams, bit for bit as ubft_new has them.
2//!
3//! | Stream | Width | Content (LSB first) |
4//! |---|---|---|
5//! | `input` (from kvstore) | 64 | `value` 32, `syncronizationGroup` 32 (`ProposedValue`) |
6//! | `output`, `remoteUpdate` (to kvstore) | 256 | bits 0..32 `m`, 32..64 `k`, rest 0 |
7//! | `m_axis_tx_meta` (to RoCE) | 256 + `keep`/`last` | `op` 3, `qpn` 24, `laddr` 48, `raddr` 48, `len` 32 |
8//! | `m_axis_tx_data` (to RoCE) | 512 + `keep`/`last` | the bytes to write, from byte 0 |
9//! | `s_axis_tx_status` (from RoCE) | 512 | completion status (drained) |
10//!
11//! `op` is 1 for an RDMA write, 0 for a read. A write whose `laddr` is 0
12//! takes its bytes from `m_axis_tx_data`.
13
14/// RDMA write.
15pub const RDMA_WRITE: u32 = 1;
16/// RDMA read.
17pub const RDMA_READ: u32 = 0;
18
19/// The RoCE stack's request word (`m_axis_tx_meta`, `ubft_rdma.cpp`).
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub struct RdmaMeta {
22    /// 1 write, 0 read (3 bits).
23    pub op: u32,
24    /// Local queue pair (24 bits).
25    pub qpn: u32,
26    /// Local address (48 bits); 0 for a write means "data follows on the stream".
27    pub laddr: u64,
28    /// Remote byte address (48 bits).
29    pub raddr: u64,
30    /// Bytes (32 bits).
31    pub len: u32,
32}
33
34impl RdmaMeta {
35    /// The 256-bit word, as four little-endian 64-bit lanes.
36    pub fn pack(&self) -> [u64; 4] {
37        let mut w = [0u128; 2];
38        let mut put = |lo: u32, bits: u32, v: u64| {
39            let v = u128::from(v) & ((1u128 << bits) - 1);
40            let (i, s) = ((lo / 128) as usize, lo % 128);
41            w[i] |= v << s;
42            if s + bits > 128 {
43                w[i + 1] |= v >> (128 - s);
44            }
45        };
46        put(0, 3, u64::from(self.op));
47        put(3, 24, u64::from(self.qpn));
48        put(27, 48, self.laddr);
49        put(75, 48, self.raddr);
50        put(123, 32, u64::from(self.len));
51        [w[0] as u64, (w[0] >> 64) as u64, w[1] as u64, (w[1] >> 64) as u64]
52    }
53
54    /// The fields of a packed word.
55    pub fn unpack(lanes: [u64; 4]) -> RdmaMeta {
56        let w = [u128::from(lanes[0]) | (u128::from(lanes[1]) << 64), u128::from(lanes[2]) | (u128::from(lanes[3]) << 64)];
57        let get = |lo: u32, bits: u32| -> u64 {
58            let (i, s) = ((lo / 128) as usize, lo % 128);
59            let mut v = w[i] >> s;
60            if s + bits > 128 {
61                v |= w[i + 1] << (128 - s);
62            }
63            (v & ((1u128 << bits) - 1)) as u64
64        };
65        RdmaMeta { op: get(0, 3) as u32, qpn: get(3, 24) as u32, laddr: get(27, 48), raddr: get(75, 48), len: get(123, 32) as u32 }
66    }
67}
68
69/// A decision word (`output`, `remoteUpdate`): `m` in bits 0..32, `k` in
70/// 32..64.
71pub fn decision(k: u32, m: u32) -> u64 {
72    (u64::from(k) << 32) | u64::from(m)
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    /// The fields land where ubft_rdma.cpp's `range()` calls put them.
80    #[test]
81    fn meta_round_trips_at_upstream_bit_positions() {
82        let m = RdmaMeta { op: 1, qpn: 7, laddr: 0, raddr: 0x1234_5678_9a, len: 64 };
83        let p = m.pack();
84        assert_eq!(p[0] & 7, 1);
85        assert_eq!((p[0] >> 3) & 0xff_ffff, 7);
86        // raddr starts at bit 75: lane 1, bit 11.
87        assert_eq!((p[1] >> 11) & 0xffff_ffff_ffff, 0x1234_5678_9a);
88        // len starts at bit 123: lane 1 bit 59 (5 bits) then lane 2.
89        assert_eq!(((p[1] >> 59) | (p[2] << 5)) & 0xffff_ffff, 64);
90        assert_eq!(RdmaMeta::unpack(p), m);
91    }
92}