Skip to main content

luwen_api/chip/blackhole/
message.rs

1use crate::{chip::AxiData, error::PlatformError};
2
3use super::Blackhole;
4
5#[derive(Debug, thiserror::Error)]
6pub enum MessageError {
7    #[error("Timed out in {phase} after {}s", .timeout.as_secs_f32())]
8    Timeout {
9        phase: String,
10        timeout: std::time::Duration,
11    },
12    #[error("Selected out of range queue ({index} > {queue_count})")]
13    QueueIndexOutOfRange { index: u32, queue_count: u32 },
14}
15
16#[derive(Clone)]
17pub struct MessageQueue<const N: usize> {
18    pub header_size: u32,
19    pub entry_size: u32,
20
21    pub queue_base: u64,
22    pub queue_count: u32,
23
24    pub queue_size: u32,
25
26    pub fw_int: AxiData,
27}
28
29impl<const N: usize> MessageQueue<N> {
30    fn get_base(&self, index: u8) -> u64 {
31        let msg_queue_size = 2 * self.queue_size * (self.entry_size * 4) + (self.header_size * 4);
32        self.queue_base + (index as u64 * msg_queue_size as u64)
33    }
34
35    fn qread32(
36        &self,
37        chip: &Blackhole,
38        index: u8,
39        offset: u32,
40    ) -> Result<u32, Box<dyn std::error::Error>> {
41        chip.arc_if
42            .axi_read32(&chip.chip_if, self.get_base(index) + (4 * offset as u64))
43    }
44
45    fn qwrite32(
46        &self,
47        chip: &Blackhole,
48        index: u8,
49        offset: u32,
50        value: u32,
51    ) -> Result<(), Box<dyn std::error::Error>> {
52        chip.arc_if.axi_write32(
53            &chip.chip_if,
54            self.get_base(index) + (4 * offset as u64),
55            value,
56        )
57    }
58
59    fn trigger_int(&self, chip: &Blackhole) -> Result<bool, PlatformError> {
60        let mut mvalue = vec![0u8; self.fw_int.size as usize];
61        let value =
62            crate::chip::HlCommsInterface::axi_read_field(&chip, &self.fw_int, &mut mvalue)?;
63
64        if value[0] & 1 != 0 {
65            return Ok(false);
66        }
67
68        mvalue[0] |= 1;
69
70        crate::chip::HlCommsInterface::axi_write_field(&chip, &self.fw_int, mvalue.as_slice())?;
71
72        Ok(true)
73    }
74
75    fn push_request(
76        &self,
77        chip: &Blackhole,
78        index: u8,
79        request: &[u32; N],
80        timeout: std::time::Duration,
81    ) -> Result<(), PlatformError> {
82        let request_queue_wptr = self.qread32(chip, index, 0)?;
83
84        let start_time = std::time::Instant::now();
85        loop {
86            let request_queue_rptr = self.qread32(chip, index, 4)?;
87
88            // Check if the queue is full
89            if request_queue_rptr.abs_diff(request_queue_wptr) % (2 * self.queue_size)
90                != self.queue_size
91            {
92                break;
93            }
94
95            let elapsed = start_time.elapsed();
96            if elapsed > timeout {
97                return Err(MessageError::Timeout {
98                    phase: "push".to_string(),
99                    timeout: elapsed,
100                })?;
101            }
102        }
103
104        let request_entry_offset =
105            self.header_size + (request_queue_wptr % self.queue_size) * N as u32;
106        for (i, &item) in request.iter().enumerate() {
107            self.qwrite32(chip, index, request_entry_offset + i as u32, item)?;
108        }
109
110        let request_queue_wptr = (request_queue_wptr + 1) % (2 * self.queue_size);
111        self.qwrite32(chip, index, 0, request_queue_wptr)?;
112
113        self.trigger_int(chip)?;
114
115        Ok(())
116    }
117
118    fn pop_response(
119        &self,
120        chip: &Blackhole,
121        index: u8,
122        result: &mut [u32; N],
123        timeout: std::time::Duration,
124    ) -> Result<(), PlatformError> {
125        let response_queue_rptr = self.qread32(chip, index, 1)?;
126
127        let start_time = std::time::Instant::now();
128        loop {
129            let response_queue_wptr = self.qread32(chip, index, 5)?;
130
131            // Break if there is some data in the queue
132            if response_queue_wptr != response_queue_rptr {
133                break;
134            }
135
136            let elapsed = start_time.elapsed();
137            if elapsed > timeout {
138                return Err(MessageError::Timeout {
139                    phase: "pop".to_string(),
140                    timeout: elapsed,
141                })?;
142            }
143        }
144
145        let response_entry_offset = self.header_size
146            + (self.queue_size + (response_queue_rptr % self.queue_size)) * N as u32;
147        for (i, item) in result.iter_mut().enumerate() {
148            *item = self.qread32(chip, index, response_entry_offset + i as u32)?;
149        }
150
151        let response_queue_rptr = (response_queue_rptr + 1) % (2 * self.queue_size);
152        self.qwrite32(chip, index, 1, response_queue_rptr)?;
153
154        Ok(())
155    }
156
157    pub fn send_message(
158        &self,
159        chip: &Blackhole,
160        index: u8,
161        mut request: [u32; N],
162        timeout: std::time::Duration,
163    ) -> Result<[u32; N], PlatformError> {
164        if index as u32 > self.queue_count {
165            return Err(MessageError::QueueIndexOutOfRange {
166                index: index as u32,
167                queue_count: self.queue_count,
168            })?;
169        }
170
171        self.push_request(chip, index, &request, timeout)?;
172        self.pop_response(chip, index, &mut request, timeout)?;
173
174        Ok(request)
175    }
176}
177
178#[derive(Debug)]
179pub struct QueueInfo {
180    pub req_rptr: u32,
181    pub req_wptr: u32,
182    pub resp_rptr: u32,
183    pub resp_wptr: u32,
184}
185
186impl<const N: usize> MessageQueue<N> {
187    pub fn get_queue_info(&self, chip: &Blackhole, index: u8) -> Result<QueueInfo, PlatformError> {
188        if index as u32 > self.queue_count {
189            return Err(MessageError::QueueIndexOutOfRange {
190                index: index as u32,
191                queue_count: self.queue_count,
192            })?;
193        }
194
195        Ok(QueueInfo {
196            req_rptr: self.qread32(chip, index, 4)?,
197            req_wptr: self.qread32(chip, index, 0)?,
198            resp_rptr: self.qread32(chip, index, 1)?,
199            resp_wptr: self.qread32(chip, index, 5)?,
200        })
201    }
202}