Skip to main content

libsurfer/
cxxrtl_container.rs

1use futures::executor::block_on;
2use std::{
3    collections::{HashMap, VecDeque},
4    sync::Arc,
5};
6use tokio::sync::mpsc;
7
8use eyre::Result;
9use num::{
10    BigUint,
11    bigint::{ToBigInt, ToBigUint},
12};
13use serde::Deserialize;
14use surfer_translation_types::VariableEncoding;
15use tracing::{error, info};
16
17use crate::wave_container::ScopeRefExt;
18use crate::{
19    channels::IngressReceiver,
20    cxxrtl::{
21        command::CxxrtlCommand,
22        cs_message::CSMessage,
23        query_container::QueryContainer,
24        sc_message::{
25            CommandResponse, CxxrtlSimulationStatus, Event, SCMessage, SimulationStatusType,
26        },
27        timestamp::CxxrtlTimestamp,
28    },
29    message::Message,
30    wave_container::{
31        QueryResult, ScopeId, ScopeRef, SimulationStatus, VarId, VariableMeta, VariableRef,
32        VariableRefExt,
33    },
34};
35
36const DEFAULT_REFERENCE: &str = "ALL_VARIABLES";
37
38type Callback = Box<dyn FnOnce(CommandResponse, &mut CxxrtlData) + Sync + Send>;
39
40#[derive(Deserialize, Debug, Clone)]
41pub(crate) struct CxxrtlScope {}
42
43#[derive(Deserialize, Debug, Clone)]
44pub struct CxxrtlItem {
45    pub width: u32,
46}
47
48/// A piece of data which we cache from Cxxrtl
49pub enum CachedData<T> {
50    /// The data cache is invalidated, the previously held data if it is still useful is
51    /// kept
52    Uncached { prev: Option<Arc<T>> },
53    /// The data cache is invalidated, and a request has been made for new data. However,
54    /// the new data has not been received yet. If the previous data is not useless, it
55    /// can be stored here
56    Waiting { prev: Option<Arc<T>> },
57    /// The cache is up-to-date
58    Filled(Arc<T>),
59}
60
61impl<T> CachedData<T> {
62    fn empty() -> Self {
63        Self::Uncached { prev: None }
64    }
65
66    fn make_uncached(&self) -> Self {
67        // Since the internals here are all Arc, clones are cheap
68        match &self {
69            CachedData::Uncached { prev } => CachedData::Uncached { prev: prev.clone() },
70            CachedData::Waiting { prev } => CachedData::Uncached { prev: prev.clone() },
71            CachedData::Filled(prev) => CachedData::Uncached {
72                prev: Some(prev.clone()),
73            },
74        }
75    }
76
77    pub fn filled(t: T) -> Self {
78        Self::Filled(Arc::new(t))
79    }
80
81    fn get(&self) -> Option<Arc<T>> {
82        match self {
83            CachedData::Uncached { prev } => prev.clone(),
84            CachedData::Waiting { prev } => prev.clone(),
85            CachedData::Filled(val) => Some(val.clone()),
86        }
87    }
88}
89
90impl<T> CachedData<T>
91where
92    T: Clone,
93{
94    /// Return the current value from the cache if it is there.
95    ///
96    /// If the cache is `Uncached` run `f` to fetch the new value.
97    /// The function must make sure that the cache is updated eventually.
98    /// The state is changed to `Waiting`
99    fn fetch_if_needed(&mut self, f: impl FnOnce()) -> Option<Arc<T>> {
100        if let CachedData::Uncached { .. } = self {
101            f();
102        }
103        match self {
104            CachedData::Uncached { prev } => {
105                let result = prev.clone();
106                *self = CachedData::Waiting { prev: prev.clone() };
107                result
108            }
109            CachedData::Waiting { prev } => prev.clone(),
110            CachedData::Filled(val) => Some(val.clone()),
111        }
112    }
113}
114
115pub struct CxxrtlData {
116    scopes_cache: CachedData<HashMap<ScopeRef, CxxrtlScope>>,
117    module_item_cache: HashMap<ScopeRef, CachedData<HashMap<VariableRef, CxxrtlItem>>>,
118    all_items_cache: CachedData<HashMap<VariableRef, CxxrtlItem>>,
119
120    /// We use the `CachedData` system to keep track of if we have sent a query request,
121    /// but the actual data is stored in the `interval_query_cache`.
122    ///
123    /// The held value in the query result is the end timestamp of the current current
124    /// `interval_query_cache`
125    query_result: CachedData<CxxrtlTimestamp>,
126    interval_query_cache: QueryContainer,
127
128    loaded_signals: Vec<VariableRef>,
129    signal_index_map: HashMap<VariableRef, usize>,
130
131    simulation_status: CachedData<CxxrtlSimulationStatus>,
132
133    msg_channel: std::sync::mpsc::Sender<Message>,
134}
135
136impl CxxrtlData {
137    pub fn trigger_redraw(&self) {
138        self.msg_channel
139            .send(Message::InvalidateDrawCommands)
140            .unwrap();
141        if let Some(ctx) = crate::EGUI_CONTEXT.read().unwrap().as_ref() {
142            ctx.request_repaint();
143        }
144    }
145
146    pub fn on_simulation_status_update(&mut self, status: CxxrtlSimulationStatus) {
147        self.simulation_status = CachedData::filled(status);
148        self.trigger_redraw();
149        self.invalidate_query_result();
150    }
151
152    pub fn invalidate_query_result(&mut self) {
153        self.query_result = self.query_result.make_uncached();
154        self.trigger_redraw();
155        // self.interval_query_cache.invalidate();
156    }
157}
158
159macro_rules! expect_response {
160    ($expected:pat, $response:expr) => {
161        let $expected = $response else {
162            error!(
163                "Got unexpected response. Got {:?} expected {}",
164                $response,
165                stringify!(expected)
166            );
167            return;
168        };
169    };
170}
171
172struct CSSender {
173    cs_messages: mpsc::Sender<String>,
174    callback_queue: VecDeque<Callback>,
175}
176
177impl CSSender {
178    fn run_command<F>(&mut self, command: CxxrtlCommand, f: F)
179    where
180        F: 'static + FnOnce(CommandResponse, &mut CxxrtlData) + Sync + Send,
181    {
182        self.callback_queue.push_back(Box::new(f));
183        let json = serde_json::to_string(&CSMessage::command(command))
184            .expect("Failed to encode cxxrtl command");
185        block_on(self.cs_messages.send(json)).unwrap();
186    }
187}
188
189pub struct CxxrtlContainer {
190    data: CxxrtlData,
191    sending: CSSender,
192    sc_messages: IngressReceiver<String>,
193    disconnected_reported: bool,
194}
195
196impl CxxrtlContainer {
197    async fn new(
198        msg_channel: std::sync::mpsc::Sender<Message>,
199        sending: CSSender,
200        sc_messages: IngressReceiver<String>,
201    ) -> Result<Self> {
202        info!("Sending cxxrtl greeting");
203        sending
204            .cs_messages
205            .send(serde_json::to_string(&CSMessage::greeting { version: 0 }).unwrap())
206            .await
207            .unwrap();
208
209        let data = CxxrtlData {
210            scopes_cache: CachedData::empty(),
211            module_item_cache: HashMap::new(),
212            all_items_cache: CachedData::empty(),
213            query_result: CachedData::empty(),
214            interval_query_cache: QueryContainer::empty(),
215            loaded_signals: vec![],
216            signal_index_map: HashMap::new(),
217            simulation_status: CachedData::empty(),
218            msg_channel: msg_channel.clone(),
219        };
220
221        let result = Self {
222            data,
223            sc_messages,
224            sending,
225            disconnected_reported: false,
226        };
227
228        info!("cxxrtl connected");
229
230        Ok(result)
231    }
232
233    #[cfg(not(target_arch = "wasm32"))]
234    pub async fn new_tcp(
235        addr: &str,
236        msg_channel: std::sync::mpsc::Sender<Message>,
237    ) -> Result<Self> {
238        use eyre::WrapErr as _;
239
240        use crate::channels::IngressSender;
241        use crate::cxxrtl::io_worker;
242
243        let stream = tokio::net::TcpStream::connect(addr)
244            .await
245            .with_context(|| format!("Failed to connect to {addr}"))?;
246
247        let (read, write) = tokio::io::split(stream);
248
249        let (cs_tx, cs_rx) = mpsc::channel(100);
250        let (sc_tx, sc_rx) = mpsc::channel(100);
251        tokio::spawn(
252            io_worker::CxxrtlWorker::new(write, read, IngressSender::new(sc_tx), cs_rx).start(),
253        );
254
255        Self::new(
256            msg_channel,
257            CSSender {
258                cs_messages: cs_tx,
259                callback_queue: VecDeque::new(),
260            },
261            IngressReceiver::new(sc_rx),
262        )
263        .await
264    }
265
266    #[cfg(target_arch = "wasm32")]
267    pub async fn new_wasm_mailbox(msg_channel: std::sync::mpsc::Sender<Message>) -> Result<Self> {
268        use eyre::anyhow;
269
270        use crate::wasm_api::{CXXRTL_CS_HANDLER, CXXRTL_SC_HANDLER};
271
272        let result = Self::new(
273            msg_channel,
274            CSSender {
275                cs_messages: CXXRTL_CS_HANDLER.tx.clone(),
276                callback_queue: VecDeque::new(),
277            },
278            CXXRTL_SC_HANDLER
279                .rx
280                .write()
281                .await
282                .take()
283                .ok_or_else(|| anyhow!("The wasm mailbox has already been consumed."))?,
284        )
285        .await;
286
287        result
288    }
289
290    pub fn tick(&mut self) {
291        loop {
292            match self.sc_messages.try_recv() {
293                Ok(s) => {
294                    info!("CXXRTL S>C: {s}");
295                    let msg = match serde_json::from_str::<SCMessage>(&s) {
296                        Ok(msg) => msg,
297                        Err(e) => {
298                            error!("Got an unrecognised message from the cxxrtl server {e}");
299                            continue;
300                        }
301                    };
302                    match msg {
303                        SCMessage::greeting { .. } => {
304                            info!("Received cxxrtl greeting");
305                        }
306                        SCMessage::response(response) => {
307                            if let Some(cb) = self.sending.callback_queue.pop_front() {
308                                cb(response, &mut self.data);
309                            } else {
310                                error!("Got a CXXRTL message with no corresponding callback");
311                            }
312                        }
313                        SCMessage::error(e) => {
314                            error!("CXXRTL error: '{}'", e.message);
315                            self.sending.callback_queue.pop_front();
316                        }
317                        SCMessage::event(event) => match event {
318                            Event::simulation_paused { time, cause: _ } => {
319                                self.data
320                                    .on_simulation_status_update(CxxrtlSimulationStatus {
321                                        status: SimulationStatusType::paused,
322                                        latest_time: time,
323                                    });
324                            }
325                            Event::simulation_finished { time } => {
326                                self.data
327                                    .on_simulation_status_update(CxxrtlSimulationStatus {
328                                        status: SimulationStatusType::finished,
329                                        latest_time: time,
330                                    });
331                            }
332                        },
333                    }
334                }
335                Err(mpsc::error::TryRecvError::Empty) => {
336                    break;
337                }
338                Err(mpsc::error::TryRecvError::Disconnected) => {
339                    if !self.disconnected_reported {
340                        error!("CXXRTL sender disconnected");
341                        self.disconnected_reported = true;
342                    }
343                    break;
344                }
345            }
346        }
347    }
348
349    fn get_scopes(&mut self) -> Arc<HashMap<ScopeRef, CxxrtlScope>> {
350        self.data
351            .scopes_cache
352            .fetch_if_needed(|| {
353                self.sending.run_command(
354                    CxxrtlCommand::list_scopes { scope: None },
355                    |response, data| {
356                        expect_response!(CommandResponse::list_scopes { scopes }, response);
357
358                        let scopes = scopes
359                            .into_iter()
360                            .map(|(name, s)| {
361                                (
362                                    ScopeRef {
363                                        strs: name.split(' ').map(str::to_string).collect(),
364                                        id: ScopeId::None,
365                                    },
366                                    s,
367                                )
368                            })
369                            .collect();
370
371                        data.scopes_cache = CachedData::filled(scopes);
372                    },
373                );
374            })
375            .unwrap_or_else(|| Arc::new(HashMap::new()))
376    }
377
378    /// Fetches the details on a specific item.
379    ///
380    /// For now, this fetches *all* items, but looks up the specific item before returning.
381    /// This is done in order to not have to return the whole Item list since
382    /// we need to lock the data structure to get that.
383    fn fetch_item(&mut self, var: &VariableRef) -> Option<CxxrtlItem> {
384        self.data
385            .all_items_cache
386            .fetch_if_needed(|| {
387                self.sending.run_command(
388                    CxxrtlCommand::list_items { scope: None },
389                    |response, data| {
390                        expect_response!(CommandResponse::list_items { items }, response);
391
392                        let items = Self::item_list_to_hash_map(items);
393
394                        data.all_items_cache = CachedData::filled(items);
395                    },
396                );
397            })
398            .and_then(|d| d.get(var).cloned())
399    }
400
401    fn fetch_all_items(&mut self) -> Option<Arc<HashMap<VariableRef, CxxrtlItem>>> {
402        self.data
403            .all_items_cache
404            .fetch_if_needed(|| {
405                self.sending.run_command(
406                    CxxrtlCommand::list_items { scope: None },
407                    |response, data| {
408                        expect_response!(CommandResponse::list_items { items }, response);
409
410                        let items = Self::item_list_to_hash_map(items);
411
412                        data.all_items_cache = CachedData::filled(items);
413                    },
414                );
415            })
416            .clone()
417    }
418
419    fn fetch_items_in_module(&mut self, scope: &ScopeRef) -> Arc<HashMap<VariableRef, CxxrtlItem>> {
420        let result = self
421            .data
422            .module_item_cache
423            .entry(scope.clone())
424            .or_insert(CachedData::empty())
425            .fetch_if_needed(|| {
426                let scope = scope.clone();
427                self.sending.run_command(
428                    CxxrtlCommand::list_items {
429                        scope: Some(scope.cxxrtl_repr()),
430                    },
431                    move |response, data| {
432                        expect_response!(CommandResponse::list_items { items }, response);
433
434                        let items = Self::item_list_to_hash_map(items);
435
436                        data.module_item_cache
437                            .insert(scope.clone(), CachedData::filled(items));
438                    },
439                );
440            });
441
442        result.unwrap_or_default()
443    }
444
445    fn item_list_to_hash_map(
446        items: HashMap<String, CxxrtlItem>,
447    ) -> HashMap<VariableRef, CxxrtlItem> {
448        items
449            .into_iter()
450            .filter_map(|(k, v)| {
451                let sp = k.split(' ').collect::<Vec<_>>();
452
453                if sp.is_empty() {
454                    error!("Found an empty variable name and scope");
455                    None
456                } else {
457                    Some((
458                        VariableRef {
459                            path: ScopeRef::from_strs(
460                                &sp[0..sp.len() - 1]
461                                    .iter()
462                                    .map(ToString::to_string)
463                                    .collect::<Vec<_>>(),
464                            ),
465                            name: (*sp.last().unwrap()).to_string(),
466                            id: VarId::None,
467                            index: None,
468                        },
469                        v,
470                    ))
471                }
472            })
473            .collect()
474    }
475
476    fn scopes(&mut self) -> Option<Arc<HashMap<ScopeRef, CxxrtlScope>>> {
477        Some(self.get_scopes())
478    }
479
480    pub fn modules(&mut self) -> Vec<ScopeRef> {
481        if let Some(scopes) = &self.scopes() {
482            scopes.keys().cloned().collect()
483        } else {
484            vec![]
485        }
486    }
487
488    pub fn root_modules(&mut self) -> Vec<ScopeRef> {
489        // In the cxxrtl protocol, the root scope is always ""
490        if self.scopes().is_some() {
491            vec![ScopeRef {
492                strs: vec![],
493                id: ScopeId::None,
494            }]
495        } else {
496            vec![]
497        }
498    }
499
500    pub fn module_exists(&mut self, module: &ScopeRef) -> bool {
501        self.scopes().is_some_and(|s| s.contains_key(module))
502    }
503
504    pub fn child_scopes(&mut self, parent: &ScopeRef) -> Vec<ScopeRef> {
505        self.scopes()
506            .map(|scopes| {
507                scopes
508                    .keys()
509                    .filter_map(|scope| {
510                        if scope.strs().len() == parent.strs().len() + 1 {
511                            if scope.strs()[0..parent.strs().len()]
512                                == parent.strs()[0..parent.strs().len()]
513                            {
514                                Some(scope.clone())
515                            } else {
516                                None
517                            }
518                        } else {
519                            None
520                        }
521                    })
522                    .collect()
523            })
524            .unwrap_or_default()
525    }
526
527    pub fn variables_in_module(&mut self, module: &ScopeRef) -> Vec<VariableRef> {
528        self.fetch_items_in_module(module).keys().cloned().collect()
529    }
530
531    pub fn no_variables_in_module(&mut self, module: &ScopeRef) -> bool {
532        self.fetch_items_in_module(module).is_empty()
533    }
534
535    pub fn variable_meta(&mut self, variable: &VariableRef) -> Result<VariableMeta> {
536        Ok(self.fetch_item(variable).map_or_else(
537            || VariableMeta {
538                var: variable.clone(),
539                num_bits: None,
540                variable_type: None,
541                variable_type_name: None,
542                index: None,
543                direction: None,
544                enum_map: Default::default(),
545                encoding: VariableEncoding::BitVector,
546            },
547            |item| VariableMeta {
548                var: variable.clone(),
549                num_bits: Some(item.width),
550                variable_type: None,
551                variable_type_name: None,
552                index: None,
553                direction: None,
554                enum_map: Default::default(),
555                encoding: VariableEncoding::BitVector,
556            },
557        ))
558    }
559
560    #[must_use]
561    pub fn max_displayed_timestamp(&self) -> Option<CxxrtlTimestamp> {
562        self.data.query_result.get().map(|t| (*t).clone())
563    }
564
565    pub fn max_timestamp(&mut self) -> Option<CxxrtlTimestamp> {
566        self.raw_simulation_status().map(|s| s.latest_time)
567    }
568
569    pub fn query_variable(
570        &mut self,
571        variable: &VariableRef,
572        time: &BigUint,
573    ) -> Option<QueryResult> {
574        // Before we can query any signals, we need some other data available. If we don't have
575        // that we'll early return with no value
576        let max_timestamp = self.max_timestamp()?;
577        let info = self.fetch_all_items()?;
578        let loaded_signals = self.data.loaded_signals.clone();
579
580        let res = self
581            .data
582            .query_result
583            .fetch_if_needed(|| {
584                info!("Running query variable");
585
586                self.sending.run_command(
587                    CxxrtlCommand::query_interval {
588                        interval: (CxxrtlTimestamp::zero(), max_timestamp.clone()),
589                        collapse: true,
590                        items: Some(DEFAULT_REFERENCE.to_string()),
591                        item_values_encoding: "base64(u32)",
592                        diagnostics: false,
593                    },
594                    move |response, data| {
595                        expect_response!(CommandResponse::query_interval { samples }, response);
596
597                        data.query_result = CachedData::filled(max_timestamp);
598                        data.interval_query_cache.populate(
599                            loaded_signals.clone(),
600                            info,
601                            samples,
602                            data.msg_channel.clone(),
603                        );
604                    },
605                );
606            })
607            .map(|_cached| {
608                // If we get here, the cache is valid and we we should look into the
609                // interval_query_cache for the query result
610                self.data
611                    .interval_query_cache
612                    .query(variable, &time.to_bigint().unwrap())
613            })
614            .unwrap_or_default();
615        Some(res)
616    }
617
618    pub fn load_variables<S: AsRef<VariableRef>, T: Iterator<Item = S>>(&mut self, variables: T) {
619        let data = &mut self.data;
620        for variable in variables {
621            let varref = variable.as_ref().clone();
622
623            if !data.signal_index_map.contains_key(&varref) {
624                let idx = data.loaded_signals.len();
625                data.signal_index_map.insert(varref.clone(), idx);
626                data.loaded_signals.push(varref.clone());
627            }
628        }
629
630        self.sending.run_command(
631            CxxrtlCommand::reference_items {
632                reference: DEFAULT_REFERENCE.to_string(),
633                items: data
634                    .loaded_signals
635                    .iter()
636                    .map(|s| vec![s.cxxrtl_repr()])
637                    .collect(),
638            },
639            |_response, data| {
640                info!("Item references updated");
641                data.invalidate_query_result();
642            },
643        );
644    }
645
646    fn raw_simulation_status(&mut self) -> Option<CxxrtlSimulationStatus> {
647        self.data
648            .simulation_status
649            .fetch_if_needed(|| {
650                self.sending
651                    .run_command(CxxrtlCommand::get_simulation_status, |response, data| {
652                        expect_response!(CommandResponse::get_simulation_status(status), response);
653
654                        data.on_simulation_status_update(status);
655                    });
656            })
657            .map(|s| s.as_ref().clone())
658    }
659
660    pub fn simulation_status(&mut self) -> Option<SimulationStatus> {
661        self.raw_simulation_status().map(|s| match s.status {
662            SimulationStatusType::running => SimulationStatus::Running,
663            SimulationStatusType::paused => SimulationStatus::Paused,
664            SimulationStatusType::finished => SimulationStatus::Finished,
665        })
666    }
667
668    pub fn unpause(&mut self) {
669        let duration = self.raw_simulation_status().map_or_else(
670            || CxxrtlTimestamp::from_femtoseconds(100_000_000u32.to_biguint().unwrap()),
671            |s| {
672                CxxrtlTimestamp::from_femtoseconds(
673                    s.latest_time.as_femtoseconds() + 100_000_000u32.to_biguint().unwrap(),
674                )
675            },
676        );
677
678        let cmd = CxxrtlCommand::run_simulation {
679            until_time: Some(duration),
680            until_diagnostics: vec![],
681            sample_item_values: true,
682        };
683
684        self.sending.run_command(cmd, |_, data| {
685            data.simulation_status = CachedData::filled(CxxrtlSimulationStatus {
686                status: SimulationStatusType::running,
687                latest_time: CxxrtlTimestamp::zero(),
688            });
689            info!("Unpausing simulation");
690        });
691    }
692
693    pub fn pause(&mut self) {
694        self.sending
695            .run_command(CxxrtlCommand::pause_simulation, |response, data| {
696                expect_response!(CommandResponse::pause_simulation { time }, response);
697
698                data.on_simulation_status_update(CxxrtlSimulationStatus {
699                    status: SimulationStatusType::paused,
700                    latest_time: time,
701                });
702            });
703    }
704}