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