Skip to main content

rhdl_ubft/
writer.rs

1//! Writing one word into this replica's own HBM.
2//!
3//! The RoCE stack has no connection to the replica itself (N − 1 peers per
4//! replica, docs/rhdl_ubft-roce.md), so whatever a replica tells its peers
5//! it also records in its own HBM: its votes, the leader's prepare, view
6//! records and installs. Polling then reads the same mailbox for everyone,
7//! the replica included. [`Writer`] writes up to four consecutive 32-bit
8//! words, one strobed AXI4 beat each (ubft_new's base is 60 mod 64, so four
9//! words may straddle a line).
10
11use rhdl::prelude::*;
12
13use crate::ports::{AxiWrite, AxiWriteResp, idle_write};
14
15/// A write request.
16#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
17pub struct WriteReq {
18    /// Start this cycle (ignored while busy).
19    pub start: bool,
20    /// Buffer base (byte address).
21    pub base: b64,
22    /// First word index.
23    pub word: b32,
24    /// The values.
25    pub values: [b32; 4],
26    /// Words to write, 1..=4.
27    pub count: b3,
28}
29
30/// No write.
31#[kernel]
32pub fn idle_write_req() -> WriteReq {
33    WriteReq { start: false, base: b64(0), word: b32(0), values: [b32(0); 4], count: b3(0) }
34}
35
36/// Inputs of [`Writer`].
37#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
38pub struct WriteIn {
39    /// The request.
40    pub req: WriteReq,
41    /// The memory's answers.
42    pub axi: AxiWriteResp,
43}
44
45/// Outputs of [`Writer`].
46#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
47pub struct WriteOut {
48    /// Writing.
49    pub busy: bool,
50    /// The write was acknowledged this cycle.
51    pub done: bool,
52    /// Requests to the memory.
53    pub axi: AxiWrite,
54}
55
56/// The writer's registers.
57#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
58pub struct WriteState {
59    /// 0 idle, 3 set up a word, 1 address and data, 2 response.
60    pub phase: b2,
61    /// Aligned byte address.
62    pub addr: b64,
63    /// The beat.
64    pub data: [b64; 8],
65    /// Its strobes.
66    pub strb: b64,
67    /// The request.
68    pub req: WriteReq,
69    /// Words written.
70    pub done_words: b3,
71    /// The address beat was accepted.
72    pub aw_done: bool,
73    /// The data beat was accepted.
74    pub w_done: bool,
75}
76
77/// One-word HBM writer.
78#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
79pub struct Writer {
80    state: rhdl_fpga::core::dff::DFF<WriteState>,
81}
82
83impl Default for Writer {
84    fn default() -> Self {
85        Self { state: rhdl_fpga::core::dff::DFF::new(WriteState::default()) }
86    }
87}
88
89impl SynchronousIO for Writer {
90    type I = WriteIn;
91    type O = WriteOut;
92    type Kernel = write_kernel;
93}
94
95/// The writer's logic.
96#[kernel]
97pub fn write_kernel(_cr: ClockReset, i: WriteIn, q: WriterQ) -> (WriteOut, WriterD) {
98    let r = q.state;
99    let mut s = r;
100    let mut o = WriteOut { busy: false, done: false, axi: idle_write() };
101    o.busy = r.phase != b2(0);
102    if r.phase == b2(0) && i.req.start {
103        s.req = i.req;
104        s.done_words = b3(0);
105        s.phase = b2(3);
106    }
107    // Phase 3: set up the beat for word `done_words`.
108    if r.phase == b2(3) {
109        let word = r.req.word + r.done_words.resize::<32>();
110        let mut value = r.req.values[0];
111        for k in 0..4 {
112            if r.done_words == b3(k as u128) {
113                value = r.req.values[k];
114            }
115        }
116        let byte = r.req.base + (word.resize::<64>() << b64(2));
117        let pos: b4 = (byte >> b64(2)).resize();
118        s.addr = byte & !b64(63);
119        s.strb = b64(0xf) << (pos.resize::<64>() << b64(2));
120        let mut d = [b64(0); 8];
121        for k in 0..8 {
122            if (pos >> b4(1)) == b4(k as u128) {
123                d[k] = if (pos & b4(1)) == b4(1) { value.resize::<64>() << b64(32) } else { value.resize::<64>() };
124            }
125        }
126        s.data = d;
127        s.aw_done = false;
128        s.w_done = false;
129        s.phase = b2(1);
130    }
131    if r.phase == b2(1) {
132        o.axi.aw_valid = !r.aw_done;
133        o.axi.aw_addr = r.addr;
134        o.axi.w_valid = !r.w_done;
135        o.axi.w_data = r.data;
136        o.axi.w_strb = r.strb;
137        let aw = r.aw_done || i.axi.aw_ready;
138        let w = r.w_done || i.axi.w_ready;
139        s.aw_done = aw;
140        s.w_done = w;
141        if aw && w {
142            s.phase = b2(2);
143        }
144    }
145    if r.phase == b2(2) {
146        o.axi.b_ready = true;
147        if i.axi.b_valid {
148            s.done_words = r.done_words + b3(1);
149            if r.done_words + b3(1) == r.req.count {
150                o.done = true;
151                s.phase = b2(0);
152            } else {
153                s.phase = b2(3);
154            }
155        }
156    }
157    (o, WriterD { state: s })
158}