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
48pub enum CachedData<T> {
50 Uncached { prev: Option<Arc<T>> },
53 Waiting { prev: Option<Arc<T>> },
57 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 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 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 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 }
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 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 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 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 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}