ni_fpga_interface/session/
mod.rs

1//! Holds session management functions for the FPGA.
2//!
3mod data_interfaces;
4pub mod fifo_control;
5use std::sync::atomic::AtomicBool;
6use std::sync::atomic::Ordering;
7use std::sync::Arc;
8
9use crate::error::{to_fpga_result, FPGAError};
10use crate::nifpga_sys::*;
11pub use data_interfaces::*;
12
13static CONTEXT_ACTIVE: AtomicBool = AtomicBool::new(false);
14
15/// An NI FPGA context must be initialized and active for the FPGA
16/// functions to work.
17///
18/// We wrap this type in an Arc so that it can be shared between sessions
19/// and automatically destruct when no more sessions are active or it is out of scope.
20pub struct NiFpgaContext();
21
22impl NiFpgaContext {
23    /// Create a new NI FPGA context which is required to open a session.
24    ///
25    /// This can only be called once per application and will return an error
26    /// if you call it more than once.
27    pub fn new() -> Result<Arc<Self>, FPGAError> {
28        // Use an atomic to prevent multiple contexts from being active at once.
29        if CONTEXT_ACTIVE
30            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
31            .is_err()
32        {
33            return Err(FPGAError::ContextAlreadyActive);
34        }
35
36        let status = unsafe { NiFpga_Initialize() };
37        to_fpga_result(Arc::new(Self {}), status)
38        //Ok(Arc::new(Self {}))
39    }
40}
41
42impl Drop for NiFpgaContext {
43    fn drop(&mut self) {
44        unsafe {
45            NiFpga_Finalize();
46        }
47        CONTEXT_ACTIVE.store(false, std::sync::atomic::Ordering::SeqCst);
48    }
49}
50
51/// Options for the session.
52pub struct SessionOptions {
53    /// Reset the FPGA on close (default: True)
54    pub reset_on_close: bool,
55    /// Run the FPGA when you open the session (default: True)
56    pub run_on_open: bool,
57}
58
59impl SessionOptions {
60    fn open_attribute(&self) -> u32 {
61        let mut attribute = 0;
62        if !self.run_on_open {
63            attribute |= 1;
64        }
65        attribute
66    }
67
68    fn close_attribute(&self) -> u32 {
69        let mut attribute = 0;
70        if !self.reset_on_close {
71            attribute |= 1;
72        }
73        attribute
74    }
75}
76
77impl Default for SessionOptions {
78    fn default() -> Self {
79        Self {
80            reset_on_close: true,
81            run_on_open: true,
82        }
83    }
84}
85
86pub struct Session {
87    pub handle: SessionHandle,
88    close_attribute: u32,
89    _context: Arc<NiFpgaContext>,
90}
91
92impl Session {
93    /// Create a new session for the specified bitfile and resource.
94    ///
95    /// You must have an open context to construct the session.
96    ///
97    /// The bitfile is the compiled FPGA VI and can be provided as a relative or absolute path.
98    ///
99    /// The session options specify run and reset behaviour for the session.
100    /// You can use default to run on start and reset on end.
101    ///
102    /// # Example
103    /// ```no_run
104    /// use ni_fpga_interface::session::{NiFpgaContext, Session};
105    ///
106    /// let fpga_context = NiFpgaContext::new().unwrap();
107    /// let session = Session::new(
108    ///    &fpga_context,
109    ///   "./NiFpga_Main.lvbitx",
110    ///  "signature",
111    /// "RIO0",
112    /// &Default::default(),
113    /// ).unwrap();
114    /// ```
115    pub fn new(
116        context: &Arc<NiFpgaContext>,
117        bitfile: &str,
118        signature: &str,
119        resource: &str,
120        options: &SessionOptions,
121    ) -> Result<Self, crate::error::FPGAError> {
122        let mut handle: SessionHandle = 0;
123        let bitfile = std::ffi::CString::new(bitfile).unwrap();
124        let signature = std::ffi::CString::new(signature).unwrap();
125        let resource = std::ffi::CString::new(resource).unwrap();
126        let result = unsafe {
127            NiFpga_Open(
128                bitfile.as_ptr(),
129                signature.as_ptr(),
130                resource.as_ptr(),
131                options.open_attribute(),
132                &mut handle,
133            )
134        };
135        to_fpga_result(
136            Self {
137                handle,
138                _context: context.clone(),
139                close_attribute: options.close_attribute(),
140            },
141            result,
142        )
143    }
144
145    /// Update the session options if you want to change the close behaviour.
146    ///
147    /// # Example
148    /// ```no_run
149    /// use ni_fpga_interface::session::{NiFpgaContext, Session, SessionOptions};
150    ///
151    ///#  let fpga_context = NiFpgaContext::new().unwrap();
152    ///#  let mut session = Session::new(
153    ///#   &fpga_context,
154    ///#  "./NiFpga_Main.lvbitx",
155    ///# "signature",
156    ///# "RIO0",
157    ///# &Default::default(),
158    ///# ).unwrap();
159    ///
160    /// session.set_options(&SessionOptions { reset_on_close: false, ..Default::default() });
161    /// ```
162    pub fn set_options(&mut self, options: &SessionOptions) {
163        self.close_attribute = options.close_attribute();
164    }
165
166    /// Reset the FPGA back to it's initial state.
167    pub fn reset(&mut self) -> Result<(), crate::error::FPGAError> {
168        let result = unsafe { NiFpga_Reset(self.handle) };
169
170        to_fpga_result((), result)
171    }
172
173    /// Runs the FPGA on the target.
174    /// If `wait_until_done` is true this function will block until the FPGA is done running.
175    pub fn run(&mut self, wait_until_done: bool) -> Result<(), crate::error::FPGAError> {
176        let attributes = if wait_until_done { 1 } else { 0 };
177
178        let result = unsafe { NiFpga_Run(self.handle, attributes) };
179
180        to_fpga_result((), result)
181    }
182
183    /// Abort the FPGA VI.
184    pub fn abort(&mut self) -> Result<(), crate::error::FPGAError> {
185        let result = unsafe { NiFpga_Abort(self.handle) };
186
187        to_fpga_result((), result)
188    }
189
190    /// Re-download the bitfile to the FPGA.
191    pub fn download(&mut self) -> Result<(), crate::error::FPGAError> {
192        let result = unsafe { NiFpga_Download(self.handle) };
193
194        to_fpga_result((), result)
195    }
196
197    /// Close the session to the FPGA and resets it if set for the session.
198    pub fn close(self) -> Result<(), crate::error::FPGAError> {
199        let result = unsafe { NiFpga_Close(self.handle, self.close_attribute) };
200
201        to_fpga_result((), result)
202    }
203}
204
205impl Drop for Session {
206    fn drop(&mut self) {
207        unsafe {
208            NiFpga_Close(self.handle, self.close_attribute);
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215
216    #[test]
217    fn test_session_options_default() {
218        let options = super::SessionOptions::default();
219        assert_eq!(options.run_on_open, true);
220        assert_eq!(options.reset_on_close, true);
221    }
222
223    #[test]
224    fn test_session_options_get_open_attribute_run() {
225        let mut options = super::SessionOptions::default();
226        options.run_on_open = true;
227        assert_eq!(options.open_attribute(), 0);
228    }
229
230    #[test]
231    fn test_session_options_get_open_attribute_no_run() {
232        let mut options = super::SessionOptions::default();
233        options.run_on_open = false;
234        assert_eq!(options.open_attribute(), 1);
235    }
236
237    #[test]
238    fn test_session_options_get_close_attribute_reset() {
239        let mut options = super::SessionOptions::default();
240        options.reset_on_close = true;
241        assert_eq!(options.close_attribute(), 0);
242    }
243
244    #[test]
245    fn test_session_options_get_close_attribute_no_reset() {
246        let mut options = super::SessionOptions::default();
247        options.reset_on_close = false;
248        assert_eq!(options.close_attribute(), 1);
249    }
250}