a653rs_macros/lib.rs
1#![deny(rustdoc::broken_intra_doc_links)]
2use partition::Partition;
3use proc_macro::TokenStream;
4use syn::{parse_macro_input, ItemMod, TypePath};
5
6mod generate;
7mod parse;
8mod partition;
9
10/// Convenience macro for simpler partition development with less pitfalls
11///
12/// For using this macro a module is annotated with the [`partition()`] attribute.
13/// Inside of this module, start functions, processes, as well as channels can be defined using attributes.
14///
15/// [`partition()`]: macro@partition#attribute-partition
16///
17/// # Example
18/// ```no_run
19/// use a653rs::prelude::PartitionExt;
20/// use a653rs_macros::partition;
21///
22/// # // TODO include example/partition.rs
23/// # #[path = "../../examples/deps/dummy.rs"]
24/// mod dummy;
25///
26/// fn main() {
27/// example::Partition.run();
28/// }
29///
30/// #[partition(crate::dummy::DummyHypervisor)]
31/// mod example {
32/// #[sampling_out(name = "Ch1", msg_size = "10KB")]
33/// struct Channel1;
34///
35/// #[sampling_in(refresh_period = "500ms")]
36/// #[sampling_in(name = "[ChannelTwo]", msg_size = "25KB")]
37/// struct ChannelTwo;
38///
39/// #[queuing_out(msg_count = 20, msg_size = "12KB", discipline = "FIFO")]
40/// struct Channel3;
41///
42/// #[start(cold)]
43/// fn cold_start(ctx: start::Context) {
44/// warm_start(ctx);
45/// }
46///
47/// #[start(warm)]
48/// fn warm_start(mut ctx: start::Context) {
49/// ctx.create_aperiodic2().unwrap().start().unwrap();
50/// ctx.create_periodic3().unwrap().start().unwrap();
51/// ctx.create_channel_1().unwrap();
52/// ctx.create_channel_two().unwrap();
53///
54/// // Maybe we do not always want to initialize channel3
55/// // ctx.create_channel_3().unwrap();
56/// }
57///
58/// #[aperiodic(
59/// name = "ap2",
60/// time_capacity = "2ms",
61/// stack_size = "10KB",
62/// base_priority = 1,
63/// deadline = "Soft"
64/// )]
65/// fn aperiodic2(ctx: aperiodic2::Context) {
66/// ctx.get_time();
67/// }
68///
69/// #[periodic(
70/// period = "10ms",
71/// time_capacity = "5ms",
72/// stack_size = "10KB",
73/// base_priority = 1,
74/// deadline = "Hard"
75/// )]
76/// fn periodic3(ctx: periodic3::Context) {}
77/// }
78/// ```
79///
80/// # Attribute `#[partition()]`
81///
82/// The [`partition()`] attribute marks the entry point of this macro.
83/// It is meant to be used on a module containing the partition.
84///
85/// When the attribute is used correctly, inside of the module a `Partition` struct is made available.
86/// This `Partition` struct can then be used in i.e the `main` function for running the partition.
87///
88/// ## Requirements
89///
90/// #### #[partition(HYPERVISOR)]
91///
92/// - *HYPERVISOR*: the full path to the used hypervisor
93///
94/// #### Module
95/// - [`start(cold)`] and [`start(cold)`]
96/// - For calling `run()` on the `Partition` struct, HYPERVISOR must implement `a653rs::prelude::PartitionExt`
97///
98/// [`start(cold)`]: macro@partition#attributes-startcold-and-startwarm
99/// [`start(warm)`]: macro@partition#attributes-startcold-and-startwarm
100///
101/// ## Flexibility
102///
103/// - The module name can be anything
104///
105/// ## Example
106/// ```no_run
107/// use a653rs::prelude::PartitionExt;
108/// use a653rs_macros::partition;
109/// # #[path = "../../examples/deps/dummy.rs"]
110/// # mod dummy;
111///
112/// fn main() {
113/// example::Partition.run();
114/// }
115///
116/// #[partition(crate::dummy::DummyHypervisor)]
117/// mod example {
118/// #[start(cold)]
119/// fn cold_start(ctx: start::Context) { }
120///
121/// #[start(warm)]
122/// fn warm_start(ctx: start::Context) { }
123/// }
124/// ```
125///
126/// ## Attributes `start(cold)` and `start(warm)`
127///
128/// [`start(cold)`] and [`start(warm)`] are used for the start functions of the partition.
129/// Inside these functions, the `start::Context` provides simple functions
130/// for initializing processes, channels and using apex functionalities of the provided hypervisor.
131///
132/// ## Requirements
133///
134/// - the start functions must require solely the `Context` parameter
135///
136/// ## Flexibility
137///
138/// - The identifier of the functions can be anything
139/// - The identifier of the `start::Context` can be anything
140///
141/// ## Example
142/// ```no_run
143/// # use a653rs::prelude::PartitionExt;
144/// # use a653rs_macros::partition;
145/// # #[path = "../../examples/deps/dummy.rs"]
146/// # mod dummy;
147/// # fn main() {
148/// # example::Partition.run();
149/// # }
150/// # #[partition(crate::dummy::DummyHypervisor)]
151/// # mod example {
152/// #[start(cold)]
153/// fn cold_start(ctx: start::Context) {
154/// let status = ctx.get_partition_status();
155/// }
156///
157/// #[start(warm)]
158/// fn warm_start(ctx: start::Context) {
159/// cold_start(ctx);
160/// }
161/// # }
162/// ```
163///
164/// # Attributes `periodic()` and `aperiodic()`
165///
166/// Two types of processes are available: periodic and aperiodic processes.
167///
168/// Functions with either the [`periodic()`] or [`aperiodic()`] attribute use a `Context` parameter for interacting with the rest of the partition.
169/// This `Context` contains fields for all defined channels and processes as well as functions provided by the used hypervisor.
170///
171/// When a process is defined, a `create_NAME()` function is made available on the `start::Context` struct in [`start(cold)`] and [`start(warm)`].
172/// This function must be called in order to initialize the process.
173/// Also, these create functions return a reference to the process on success.
174/// For the process to be scheduled, the `start()` function must be called on this reference.
175///
176/// [`periodic()`]: macro@partition#attributes-periodic-and-aperiodic
177/// [`aperiodic()`]: macro@partition#attributes-periodic-and-aperiodic
178///
179/// ## Requirements
180///
181/// - the functions must require solely the `Context` parameter
182/// - the module path of the `Context` is the name of the function
183///
184/// #### #[periodic(NAME, PERIOD, TIME_CAPACITY, STACK_SIZE, BASE_PRIORITY, DEADLINE)]
185///
186/// - **NAME**: name used for internal apex calls (optional)
187/// - **PERIOD**: time like ["10ms", "16s", "18m", ...](https://crates.io/crates/humantime)
188/// - Suggested value for P4: equal to the partition period
189/// - **TIME_CAPACITY**: either "Infinite" or a time like ["10ms", "16s", "18m", ...](https://crates.io/crates/humantime)
190/// - Suggested value for P4: equal to the partition duration
191/// - **STACK_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
192/// - **BASE_PRIORITY**: [i32]
193/// - Suggested value for P4: lower than the base priority of the aperiodic process
194/// - **DEADLINE**: either "Hard" or "Soft"
195///
196/// #### #[aperiodic(NAME, TIME_CAPACITY, STACK_SIZE, BASE_PRIORITY, DEADLINE)]
197///
198/// - **NAME**: name used for internal apex calls (optional)
199/// - **TIME_CAPACITY**: either "Infinite" or a time like ["10ms", "16s", "18m", ...](https://crates.io/crates/humantime)
200/// - Suggested value for P4: equal to the partition duration
201/// - **STACK_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
202/// - **BASE_PRIORITY**: [i32]
203/// - Suggested value for P4: higher than the base priority of the periodic process
204/// - **DEADLINE**: either "Hard" or "Soft"
205///
206/// ## Flexibility
207///
208/// - The identifier of the functions can be anything
209/// - The identifier of the `Context` can be anything
210///
211/// ## Example
212/// ```no_run
213/// # use a653rs::prelude::PartitionExt;
214/// # use a653rs_macros::partition;
215/// # #[path = "../../examples/deps/dummy.rs"]
216/// # mod dummy;
217/// # fn main() {
218/// # example::Partition.run();
219/// # }
220/// # #[partition(crate::dummy::DummyHypervisor)]
221/// # mod example {
222/// #[start(cold)]
223/// fn cold_start(ctx: start::Context) {
224/// warm_start(ctx);
225/// }
226///
227/// #[start(warm)]
228/// fn warm_start(mut ctx: start::Context) {
229/// ctx.create_aperiodic2().unwrap().start().unwrap();
230/// ctx.create_periodic3().unwrap().start().unwrap();
231/// }
232///
233/// #[aperiodic(
234/// name = "ap2",
235/// time_capacity = "Infinite",
236/// stack_size = "10KB",
237/// base_priority = 1,
238/// deadline = "Soft"
239/// )]
240/// fn aperiodic2(ctx: aperiodic2::Context) {
241/// ctx.get_time();
242/// ctx.periodic3.unwrap().stop();
243/// }
244///
245/// #[periodic(
246/// period = "10ms",
247/// time_capacity = "Infinite",
248/// stack_size = "10KB",
249/// base_priority = 1,
250/// deadline = "Hard"
251/// )]
252/// fn periodic3(ctx: periodic3::Context) {
253/// let status = ctx.proc_self.status();
254/// ctx.report_application_message(b"Hello World").unwrap()
255/// }
256/// # }
257/// ```
258///
259/// # Attributes `sampling_out()`, `sampling_in()`, `queuing_out()` and `queuing_in()`
260///
261/// Two types of channel are available: sampling and queuing ports.
262///
263/// Structs with [`sampling_out()`], [`sampling_in()`], [`queuing_out()`] and [`queuing_in()`] attribute define channel.
264///
265/// When a channel is defined, a `create_NAME()` function is made available on the `start::Context` struct in [`start(cold)`] and [`start(warm)`].
266/// This function must be called in order to initialize the channel.
267/// Also a field for each created channel is made available on the `Context` of each [`periodic()`] and [`aperiodic()`] process.
268///
269/// [`sampling_out()`]: macro@partition#attributes-sampling_out-sampling_in-queuing_out-and-queuing_in
270/// [`sampling_in()`]: macro@partition#attributes-sampling_out-sampling_in-queuing_out-and-queuing_in
271/// [`queuing_out()`]: macro@partition#attributes-sampling_out-sampling_in-queuing_out-and-queuing_in
272/// [`queuing_in()`]: macro@partition#attributes-sampling_out-sampling_in-queuing_out-and-queuing_in
273///
274/// ## Requirements
275///
276/// #### #[sampling_out(NAME, MSG_SIZE)]
277///
278/// - **NAME**: name used for internal apex calls (optional)
279/// - **MSG_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
280///
281/// #### #[sampling_in(NAME, MSG_SIZE, REFRESH_PERIOD)]
282///
283/// - **NAME**: name used for internal apex calls (optional)
284/// - **MSG_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
285/// - **REFRESH_PERIOD**: time like ["10ms", "16s", "18m", ...](https://crates.io/crates/humantime)
286///
287/// #### #[queuing_out(NAME, MSG_COUNT, MSG_SIZE, DISCIPLINE)]
288///
289/// - **NAME**: name used for internal apex calls (optional)
290/// - **MSG_COUNT**: [u32]
291/// - **MSG_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
292/// - **DISCIPLINE**: either "FIFO" or "Priority"
293///
294/// #### #[queuing_in(NAME, MSG_COUNT, MSG_SIZE, DISCIPLINE)]
295///
296/// - **NAME**: name used for internal apex calls (optional)
297/// - **MSG_COUNT**: [u32]
298/// - **MSG_SIZE**: size like ["10KB", "16kiB", "12Mb", ...](https://crates.io/crates/bytesize)
299/// - **DISCIPLINE**: either "FIFO" or "Priority"
300///
301/// ## Flexibility
302///
303/// - The identifier of the struct can be anything
304///
305/// ## Example
306/// ```no_run
307/// # use a653rs::prelude::PartitionExt;
308/// # use a653rs_macros::partition;
309/// # #[path = "../../examples/deps/dummy.rs"]
310/// # mod dummy;
311/// # fn main() {
312/// # example::Partition.run();
313/// # }
314/// # #[partition(crate::dummy::DummyHypervisor)]
315/// # mod example {
316/// #[sampling_out(name = "Ch1", msg_size = "10KB")]
317/// struct Channel1;
318///
319/// #[sampling_in(refresh_period = "500ms")]
320/// #[sampling_in(msg_size = "25KB")]
321/// struct ChannelTwo;
322///
323/// #[queuing_out(msg_count = 20, msg_size = "12KB", discipline = "FIFO")]
324/// struct Channel3;
325///
326/// #[queuing_in(name = "ch_3", msg_count = 20, msg_size = "12KB", discipline = "Priority")]
327/// struct LastChannel;
328///
329/// #[start(cold)]
330/// fn cold_start(ctx: start::Context) {
331/// warm_start(ctx);
332/// }
333///
334/// #[start(warm)]
335/// fn warm_start(mut ctx: start::Context) {
336/// ctx.create_channel_1().unwrap();
337/// ctx.create_channel_two().unwrap();
338/// ctx.create_channel_3().unwrap();
339/// ctx.create_last_channel().unwrap();
340/// }
341/// # }
342/// ```
343///
344///
345///
346#[proc_macro_attribute]
347pub fn partition(args: TokenStream, input: TokenStream) -> TokenStream {
348 let input = parse_macro_input!(input as ItemMod);
349 // Right now we only expect the Identifier of the used Hypervisor here
350 let args = parse_macro_input!(args as TypePath);
351
352 // TODO allow only for a single partition per project
353
354 Partition::expand_partition(input, args)
355 .unwrap_or_else(syn::Error::into_compile_error)
356 .into()
357}