sv_api/startup.rs
1/*
2 * File: startup.rs
3 * Brief: TODO
4 *
5 * Copyright (C) 2023 John Jekel
6 * See the LICENSE file at the root of the project for licensing info.
7 *
8 * TODO longer description
9 *
10*/
11
12/*!
13 * TODO rustdoc for this file here
14*/
15
16/* ------------------------------------------------------------------------------------------------
17 * Submodules
18 * --------------------------------------------------------------------------------------------- */
19
20//TODO (includes "mod ..." and "pub mod ...")
21
22/* ------------------------------------------------------------------------------------------------
23 * Uses
24 * --------------------------------------------------------------------------------------------- */
25
26use std::thread::{ThreadId};
27
28/* ------------------------------------------------------------------------------------------------
29 * Macros
30 * --------------------------------------------------------------------------------------------- */
31
32#[macro_export]
33macro_rules! vlog_startup_routines {
34 //($($arg:tt),*) => {
35 ($($arg:ident),*) => {//TODO support closures, functions in another module or part of a trait (with::), etc
36 #[doc(hidden)]
37 mod ___sv_api_vlog_startup_routines___ {
38 extern "C" fn ___sv_api_call_vlog_startup_routines___() {
39 //SAFETY: This is the only place we allow this function to be called without
40 //warning that the caller would violate safety by doing so.
41 unsafe { ::sv_api::startup::___startup_routines_started___(); }
42
43 $(
44 super::$arg();
45 )*
46
47 //SAFETY: This is the only place we allow this function to be called without
48 //warning that the caller would violate safety by doing so.
49 //This function allows the other dpi/pli/vpi abstraction functions to be called
50 //without panicing, so it must be called in order for user code to be able to
51 //use those functions. Those functions can only be used AFTER the startup routines
52 //have finished, so we only call this function after the above.
53 unsafe { ::sv_api::startup::___startup_routines_finished___(); }
54 }
55
56 //SAFETY: We must end vlog_startup_routines with a null pointer, so we do so with None
57 //Although there's no unsafe {} here, the simulator which will load the library
58 //and reference this symbol will expect this to be upheld
59 #[no_mangle]
60 #[used]
61 static vlog_startup_routines: [Option<extern "C" fn()>; 2usize] = [
62 Some(___sv_api_call_vlog_startup_routines___),
63 None
64 ];
65 }
66 };
67}
68
69macro_rules! this_fn_str {
70 () => {{
71 //Thanks https://stackoverflow.com/questions/38088067/equivalent-of-func-or-function-in-rust
72 fn ___subfunction___() {}
73 fn ___type_name_of___<T>(_: T) -> &'static str {
74 std::any::type_name::<T>()
75 }
76 ___type_name_of___(___subfunction___)
77 .strip_suffix("::___subfunction___")
78 .expect("Suffix should exist!")
79 }}
80}
81pub(crate) use this_fn_str;
82
83macro_rules! panic_if_in_startup_routine {
84 () => {{
85 if crate::startup::in_startup_routine() {
86 panic!("{}() cannot be called during a startup routine!", crate::startup::this_fn_str!());
87 }
88 }};
89}
90pub(crate) use panic_if_in_startup_routine;
91
92macro_rules! panic_if_not_main_thread {
93 () => {{
94 if !crate::startup::is_main_thread() {
95 panic!("{}() cannot be called by any thread other than the main thread!", crate::startup::this_fn_str!());
96 }
97 }};
98}
99pub(crate) use panic_if_not_main_thread;
100
101/* ------------------------------------------------------------------------------------------------
102 * Static Variables
103 * --------------------------------------------------------------------------------------------- */
104
105///Used to uphold invariants the C interface requires
106static INIT_FINISHED: std::sync::OnceLock<()> = std::sync::OnceLock::new();
107
108///Used to ensure thread safety since the C interface is not thread safe
109static MAIN_THREAD_ID: std::sync::OnceLock<ThreadId> = std::sync::OnceLock::new();
110
111/* ------------------------------------------------------------------------------------------------
112 * Types
113 * --------------------------------------------------------------------------------------------- */
114
115//TODO includes "type"-defs, structs, enums, unions, etc
116
117/* ------------------------------------------------------------------------------------------------
118 * Associated Functions and Methods
119 * --------------------------------------------------------------------------------------------- */
120
121//TODO
122
123/* ------------------------------------------------------------------------------------------------
124 * Traits And Default Implementations
125 * --------------------------------------------------------------------------------------------- */
126
127//TODO
128
129/* ------------------------------------------------------------------------------------------------
130 * Trait Implementations
131 * --------------------------------------------------------------------------------------------- */
132
133//TODO
134
135/* ------------------------------------------------------------------------------------------------
136 * Functions
137 * --------------------------------------------------------------------------------------------- */
138
139///Should only be called by the vlog_startup_routines! macro, any other use is undefined behaviour
140#[doc(hidden)]
141pub unsafe fn ___startup_routines_started___() {
142 let this_thread = std::thread::current();
143 let this_thread_id = this_thread.id();
144 MAIN_THREAD_ID.set(this_thread_id).expect("___startup_routines_started___() was called manually at some point!");
145}
146
147///Should only be called by the vlog_startup_routines! macro, any other use is undefined behaviour
148#[doc(hidden)]
149pub unsafe fn ___startup_routines_finished___() {
150 INIT_FINISHED.set(()).expect("___startup_routines_finished___() was called manually at some point!");
151}
152
153///Returns true if we are currently in a startup routine (and thus most SV interfaces are unavailable)
154pub fn in_startup_routine() -> bool {
155 INIT_FINISHED.get().is_none()
156}
157
158///Returns true if we are currently in the main thread
159///
160///Useful to double-check you aren't in another thread (otherwise no SV interfaces would be
161///available)
162pub fn is_main_thread() -> bool {
163 let this_thread = std::thread::current();
164 let this_thread_id = this_thread.id();
165 let main_thread_id = MAIN_THREAD_ID.get().expect("We should already know the main thread id");
166 this_thread_id == *main_thread_id
167}
168
169/* ------------------------------------------------------------------------------------------------
170 * Tests
171 * --------------------------------------------------------------------------------------------- */
172
173//TODO
174
175/* ------------------------------------------------------------------------------------------------
176 * Benchmarks
177 * --------------------------------------------------------------------------------------------- */
178
179//TODO