r2u2_core/
lib.rs

1//! # Library for r2u2_core
2//!
3//!  R2U2: A stream-based runtime monitor written in `#![no_std]` that reasons over Mission-Time Linear
4//! Temporal Logic (MLTL) specifications compiled with the Configuration Compiler for Property Organization (C2PO).
5//!
6//! ## Optional Features:
7//! 
8//! - `aux_string_specs`: Enable Assume-Guarantee Contract status output and string auxiliary specification naming. This feature
9//! enables the following:
10//!     - **[r2u2_contract]** type
11//!     - **[r2u2_output::spec_str]** member of the **[r2u2_output]** type
12//!     - **[get_contract_buffer]** function
13//!     - **[AGC_INACTIVE]**, **[AGC_INVALID]**, and **[AGC_VERIFIED]** constants
14//! - `debug_print_std`: Debug printing to terminal (i.e., `println!`)
15//! - `debug_print_semihosting`: Debug printing utilizing GDB debugger of microcontroller (i.e., `hprintln!`)
16//! 
17//! ## Usage:
18//! 1. A monitor must be created utilizing **[get_monitor]**. The specification file from C2PO must be passed by reference as
19//! `&[u8]`. The specifications can later be updated with **[update_binary_file]**; this will also reset the monitor to its intial state.
20//! 2. Signals must be loaded in according to the mapping specified when compiling the specification file through **[load_bool_signal]**,
21//! **[load_float_signal]**, **[load_int_signal]**, and **[load_string_signal]**.
22//! 3. Run the monitor for a single timestep with **[monitor_step]**.
23//! 4. Get output data through **[get_output_buffer]** and **[get_contract_buffer]**.
24//! 5. Repeat steps 2-4. (Optionally check for overflow with the output of **[monitor_step]** or **[get_overflow_error]**.)
25//! 
26//! For details on compiling specifications with C2PO, refer to [README](https://crates.io/crates/r2u2_core).
27
28#![no_std]
29
30use internals::types::*;
31use internals::process_binary::process_binary_file;
32use engines::r2u2_engine_step;
33
34#[cfg(feature = "debug_print_semihosting")]
35use cortex_m_semihosting::hprintln;
36
37#[cfg(feature = "debug_print_std")]
38use libc_print::std_name::println;
39
40mod instructions;
41mod internals;
42mod engines;
43mod memory;
44
45pub use internals::types::{r2u2_output,r2u2_verdict};
46pub use memory::monitor::Monitor;
47#[cfg(feature = "aux_string_specs")]
48pub use internals::types::{r2u2_contract, AGC_INACTIVE, AGC_INVALID, AGC_VERIFIED};
49#[cfg(feature = "aux_string_specs")]
50pub use internals::bounds::{R2U2_MAX_OUTPUT_CONTRACTS, R2U2_AUX_MAX_FORMULAS, R2U2_AUX_MAX_CONTRACTS};
51pub use internals::bounds::{R2U2_MAX_OUTPUT_VERDICTS,R2U2_MAX_SIGNALS,R2U2_MAX_ATOMICS,R2U2_MAX_BZ_INSTRUCTIONS,R2U2_MAX_TL_INSTRUCTIONS,R2U2_MAX_QUEUE_SLOTS,R2U2_FLOAT_EPSILON};
52
53/// Get runtime monitor
54/// 
55/// # Arguments
56/// 
57/// * `spec_file` - A reference to a vector or array of u8 from the specification compiled by C2PO
58/// 
59/// # Returns
60/// 
61/// A new `Monitor` with the specifications loaded
62/// 
63/// # Examples
64/// 
65/// ```
66/// let spec_file: Vec<u8> = fs::read(spec_file_path).expect("Error opening specification file");
67/// let mut monitor = r2u2_core::get_monitor(&spec_file);
68/// ```
69/// 
70pub fn get_monitor(spec_file: &[u8]) -> Monitor{
71    let mut monitor = Monitor::default();
72    process_binary_file(spec_file, &mut monitor);
73    return monitor;
74}
75
76/// Update specification binary file (and reset monitor)
77/// 
78/// # Arguments
79/// 
80/// * `spec_file` - A reference to a vector or array of u8 from the specification compiled by C2PO
81/// * `monitor` - A reference to a monitor
82/// 
83pub fn update_binary_file(spec_file: &[u8], monitor: &mut Monitor){
84    monitor.reset();
85    process_binary_file(spec_file, monitor);
86}
87
88/// Take a step with runtime monitor
89/// 
90/// # Arguments
91/// 
92/// * `monitor` - A reference to a monitor
93/// 
94/// # Returns
95/// 
96/// A `bool` indicating if successful (true) or overflow occured (false)
97/// 
98pub fn monitor_step(monitor: &mut Monitor) -> r2u2_bool{
99    r2u2_engine_step(monitor)
100}
101
102/// Load boolean signal
103/// 
104/// # Arguments
105/// 
106/// * `monitor` - A reference to a monitor
107/// * `index` - Signal index\mapping as specified when compiled with C2PO
108/// * `value` - The boolean value to load
109/// 
110pub fn load_bool_signal(monitor: &mut Monitor, index: usize, value: r2u2_bool){
111    if monitor.bz_program_count.max_program_count == 0 {
112        monitor.atomic_buffer[index] = value;
113        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
114        internals::debug::debug_print!("Loaded atomic in directly at {}: {}", index, monitor.atomic_buffer[index]);
115    } else{
116        monitor.signal_buffer[index].i = value as r2u2_int;
117        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
118        internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].i != 0);
119    }
120}
121
122/// Load integer signal
123/// 
124/// # Arguments
125/// 
126/// * `monitor` - A reference to a monitor
127/// * `index` - Signal index\mapping as specified when compiled with C2PO
128/// * `value` - The integer value to load
129/// 
130pub fn load_int_signal(monitor: &mut Monitor, index: usize, value: r2u2_int){
131    if monitor.bz_program_count.max_program_count == 0 {
132        monitor.atomic_buffer[index] = value != 0;
133        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
134        internals::debug::debug_print!("Loaded atomic in directly at {}: {}", index, monitor.atomic_buffer[index]);
135    } else{
136        monitor.signal_buffer[index].i = value;
137        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
138        internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].i);
139    }
140}
141
142/// Load float signals
143/// 
144/// # Arguments
145/// 
146/// * `monitor` - A reference to a monitor
147/// * `index` - Signal index\mapping as specified when compiled with C2PO
148/// * `value` - The float value to load
149/// 
150pub fn load_float_signal(monitor: &mut Monitor, index: usize, value: r2u2_float){
151    if monitor.bz_program_count.max_program_count == 0 {
152        monitor.atomic_buffer[index] = value >= R2U2_FLOAT_EPSILON || value <= -R2U2_FLOAT_EPSILON;
153        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
154        internals::debug::debug_print!("Loaded atomic in directly at {}: {}", index, monitor.atomic_buffer[index]);
155    } else{
156        monitor.signal_buffer[index].f = value;
157        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
158        internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].f);
159
160    }
161}
162
163/// Load signal as string (decoded as integer, float, or boolean)
164/// 
165/// # Arguments
166/// 
167/// * `monitor` - A reference to a monitor
168/// * `index` - Signal index\mapping as specified when compiled with C2PO
169/// * `value` - The string value to load (gets encoded as float, integer, or boolean as applicable)
170/// 
171pub fn load_string_signal(monitor: &mut Monitor, index: usize, value: &str){
172    if monitor.bz_program_count.max_program_count == 0 {
173        monitor.atomic_buffer[index] = value.parse::<r2u2_int>().expect("Please provide a 0 or 1") != 0;
174        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
175        internals::debug::debug_print!("Loaded atomic in directly at {}: {}", index, monitor.atomic_buffer[index]);
176    } else{
177        match value.parse::<r2u2_int>() {
178            Ok(n) => {
179                monitor.signal_buffer[index].i = n;
180                #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
181                internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].i);
182                match value.parse::<r2u2_float>() {
183                    Ok(n) => {
184                        monitor.signal_buffer[index].f = n;
185                        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
186                        internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].f);
187                    },
188                    Err(_e) => ()
189                }
190            }
191            Err(_e) => 
192                match value.parse::<r2u2_float>() {
193                    Ok(n) => {
194                        monitor.signal_buffer[index].f = n;
195                        #[cfg(any(feature = "debug_print_semihosting", feature = "debug_print_std"))]
196                        internals::debug::debug_print!("Loading in signal {}: {}", index, monitor.signal_buffer[index].f);
197                    },
198                    Err(_e) => panic!("Please provide a valid number!")
199                }
200        }
201    }
202}
203
204/// Get output verdict buffer
205/// 
206/// # Arguments
207/// 
208/// * `monitor` - A reference to a monitor
209/// 
210/// # Returns
211/// 
212/// A reference to an array of r2u2_outputs from the last call of monitor_step()
213/// 
214/// # Examples
215/// 
216/// ```
217/// let mut monitor = r2u2_core::get_monitor(&spec_file);
218/// for out in r2u2_core::get_output_buffer(&monitor) {
219///     println!("{}:{},{}", out.spec_num, out.verdict.time, if out.verdict.truth {"T"} else {"F"} );
220/// }
221/// ```
222/// 
223pub fn get_output_buffer(monitor: &Monitor) -> &[r2u2_output]{
224    return &monitor.output_buffer[0..monitor.output_buffer_idx];
225}
226
227#[cfg(feature = "aux_string_specs")]
228/// Get Assume-Guarantee Contract (AGC) buffer
229/// 
230/// # Arguments
231/// 
232/// * `monitor` - A reference to a monitor
233/// 
234/// # Returns
235/// 
236/// A reference to an array of r2u2_contracts from the last call of monitor_step()
237/// 
238/// # Examples
239/// 
240/// ```
241/// let mut monitor = r2u2_core::get_monitor(&spec_file);
242/// for out in r2u2_core::get_contract_buffer(&monitor) {
243///     println!("Contract {} {} at {}", out.spec_str, if out.status == r2u2_core::AGC_VERIFIED {"verified"} else if out.status == r2u2_core::AGC_INVALID {"invalid"} else {"inactive"}, out.time);
244/// }
245/// ```
246/// 
247pub fn get_contract_buffer(monitor: &Monitor) -> &[r2u2_contract]{
248    return &monitor.contract_buffer[0..monitor.contract_buffer_idx];
249}
250
251/// Get overflow error flag
252/// 
253/// # Arguments
254/// 
255/// * `monitor` - A reference to a monitor
256/// 
257/// # Returns
258/// 
259/// A `bool` indicating if overflow (true) or no overflow occured (false)
260/// 
261pub fn get_overflow_error(monitor: &Monitor) -> r2u2_bool {
262    return monitor.overflow_error;
263}
264
265/// Reset overflow flag
266/// 
267/// # Arguments
268/// 
269/// * `monitor` - A reference to a monitor
270/// 
271pub fn reset_overflow_error(monitor: &mut Monitor){
272    monitor.overflow_error = false;
273}