sv_api/
print.rs

1/*
2 * File:    print.rs
3 * Brief:   Utilities to print to the simulator's standard output.
4 *
5 * Copyright (C) 2023 John Jekel
6 * See the LICENSE file at the root of the project for licensing info.
7 *
8 * Uses vpi_printf() from IEEE 1800 to print to the simulator's standard output.
9 *
10*/
11
12/*!
13 *
14 * Utilities to print to the simulator's standard output.
15 *
16 * [`sim_print!()`](crate::sim_print) and [`sim_println!()`](crate::sim_println) are analogous to the
17 * standard [`print!()`] and [`println!()`] macros but print to the simulator's standard output instead
18 * of the regular Rust standard output (though these are often the same).
19 *
20 * There is also a [`SimulatorPrinter`] similar to the standard [`Stdout`](std::io::Stdout) that
21 * is useful if you need a [`Writer`] corresponding to the simulator's output.
22 *
23 * Since these use `vpi_printf()` internally, and according to the IEEE 1800-2017 standard, __it is
24 * not safe to call it from a startup routine__, if you do, a panic will occur or you'll
25 * get an [Err] depending on exactly what you're working with. This will also happen if you call
26 * printing functions / invoke printing macros from a thread other than the main one, which also
27 * isn't permissible.
28 *
29*/
30
31/* ------------------------------------------------------------------------------------------------
32 * Uses
33 * --------------------------------------------------------------------------------------------- */
34
35use std::ffi::CString;
36use std::fmt::{Error, Write};
37use std::fmt::Result as FmtResult;
38
39use crate::startup::in_startup_routine;
40use crate::startup::is_main_thread;
41
42/* ------------------------------------------------------------------------------------------------
43 * Macros
44 * --------------------------------------------------------------------------------------------- */
45
46///Prints to the simulator output.
47///
48///This may or not be the same as printing to the regular Rust standard output.
49///It is preferable if you want, for example, ensure you are logging to the same log file that the
50///simulator itself is writing to.
51///
52///Equivalent to the [`sim_println!()`](crate::sim_println) macro except that a newline is not
53///printed at the end of the message.
54///
55///This will panic if it is called from a thread other than the main thread or during a startup
56///routine
57///
58///Analagous to [`print!()`](std::print).
59#[macro_export]
60macro_rules! sim_print {
61    ($($arg:tt)*) => {{
62        use std::fmt::Write as _;
63        write!(::sv_api::print::SimulatorPrinter::new(), $($arg)*)
64            .expect("Failure writing to simulator output with sim_print!(), are you in a startup routine or not in the main thread?");
65    }};
66}
67
68///Prints to the simulator output, with a newline.
69///
70///This may or not be the same as printing to the regular Rust standard output.
71///It is preferable if you want, for example, ensure you are logging to the same log file that the
72///simulator itself is writing to.
73///
74///This will panic if it is called from a thread other than the main thread or during a startup
75///routine
76///
77///Analagous to [`println!()`](std::println).
78#[macro_export]
79macro_rules! sim_println {
80    ($($arg:tt)*) => {{
81        use std::fmt::Write as _;
82        writeln!(::sv_api::print::SimulatorPrinter::new(), $($arg)*)
83            .expect("Failure writing to simulator output with sim_println!(), are you in a startup routine or not in the main thread?");
84    }};
85}
86
87/* ------------------------------------------------------------------------------------------------
88 * Types
89 * --------------------------------------------------------------------------------------------- */
90
91///A handle to the simulator's output.
92///
93///Useful for if you need a Writer struct corresponding to the simulator's output.
94///
95#[derive(Debug)]
96pub struct SimulatorPrinter {}
97
98/* ------------------------------------------------------------------------------------------------
99 * Associated Functions and Methods
100 * --------------------------------------------------------------------------------------------- */
101
102impl SimulatorPrinter {
103    pub fn new() -> Self {
104        Self {}
105    }
106
107    //TODO perhaps return a VpiError? using vpi_chk_error()?
108    pub fn flush(&mut self) -> Result<(), ()> {
109        //We can only call vpi_flush() after startup routines have finished, and if we are in the
110        //main thread
111        if in_startup_routine() || !is_main_thread() {
112            return Err(());
113        }
114
115        //SAFETY: We're calling vpi_flush() from the main thread and after startup routines have finished
116        if unsafe { sv_bindings::vpi_flush() } == 0 {
117            Ok(())
118        } else {
119            Err(())
120        }
121    }
122}
123
124/* ------------------------------------------------------------------------------------------------
125 * Trait Implementations
126 * --------------------------------------------------------------------------------------------- */
127
128impl Write for SimulatorPrinter {
129    ///Write formatted textual data to the simulator's output
130    ///Since the backing `vpi_printf()` function used is not thread safe, write_str will return an
131    ///[`Err`] if you attempt to call [`write_str()`] from a thread other than the main one.
132    ///
133    ///It will also [`Err`] out if you attempt to call it during a start routine since
134    ///`vpi_printf()` also doesn't support this.
135    fn write_str(&mut self, string: &str) -> FmtResult {
136        //We can only call vpi_printf() after startup routines have finished, and if we are in the
137        //main thread
138        if in_startup_routine() || !is_main_thread() {
139            return Err(Error);
140        }
141
142        //We can only print up to i32::MAX bytes since that's all we can check
143        //to ensure that the string was printed successfully
144        let num_bytes: i32 = string.len().try_into().map_err(|_| Error)?;
145
146        //Need a null terminated string to actually print
147        let cstring = CString::new(string).map_err(|_| Error)?;
148
149        //SAFETY: It is safe to cast to *mut PLI_BYTE8 because vpi_printf does not modify the string.
150        //We are also guaranteed that the string is null terminated because the pointer is from a
151        //CString. We only actually print if we weren't in a startup routine, and finally
152        //we only print if we are in the main thread.
153        let num_bytes_written = unsafe {
154            sv_bindings::vpi_printf(cstring.as_ptr() as *mut sv_bindings::PLI_BYTE8)
155        };
156
157        if num_bytes_written == num_bytes {
158            Ok(())
159        } else {//EOF or more or less bytes written than expected
160            Err(Error)
161        }
162    }
163}
164
165/* ------------------------------------------------------------------------------------------------
166 * Tests
167 * --------------------------------------------------------------------------------------------- */
168
169//TODO
170
171/* ------------------------------------------------------------------------------------------------
172 * Benchmarks
173 * --------------------------------------------------------------------------------------------- */
174
175//TODO