rtlicious/
process.rs

1//! Declares a process, with zero or more attributes, with the given identifier
2//! in the enclosing module. The body of a process consists of zero or more
3//! assignments, exactly one switch, and zero or more syncs.
4//! ```text
5//! <process>       ::= <attr-stmt>* <proc-stmt> <process-body> <proc-end-stmt>
6//! <proc-stmt>     ::= process <id> <eol>
7//! <process-body>  ::= <assign-stmt>* <switch>? <assign-stmt>* <sync>*
8//! <assign-stmt>   ::= assign <dest-sigspec> <src-sigspec> <eol>
9//! <dest-sigspec>  ::= <sigspec>
10//! <src-sigspec>   ::= <sigspec>
11//! <proc-end-stmt> ::= end <eol>
12//! ```
13
14use crate::*;
15use nom::{bytes::complete::tag, multi::many0, IResult};
16use nom_tracable::tracable_parser;
17
18#[tracable_parser]
19pub(crate) fn process(input: Span) -> IResult<Span, (String, Process)> {
20    let (input, _) = many0(characters::sep)(input)?;
21    let (input, attributes) = many0(attribute::attr_stmt)(input)?;
22    let (input, id) = process_stmt(input)?;
23    let (input, assignments) = many0(assign_stmt)(input)?;
24    let (input, switches) = many0(switch::switch)(input)?;
25    let (input, syncs) = many0(crate::sync::sync)(input)?;
26    let (input, _) = process_end_stmt(input)?;
27    Ok((
28        input,
29        (
30            id,
31            Process {
32                attributes: attributes.into_iter().collect(),
33                assignments,
34                switches,
35                syncs,
36            },
37        ),
38    ))
39}
40
41/// `<proc-stmt>     ::= process <id> <eol>`
42pub(crate) fn process_stmt(input: Span) -> IResult<Span, String> {
43    let (input, _) = tag("process")(input)?;
44    let (input, _) = characters::sep(input)?;
45    let (input, id) = identifier::id(input)?;
46    let (input, _) = characters::eol(input)?;
47    Ok((input, id.erease()))
48}
49/// `<proc-end-stmt> ::= end <eol>`
50pub(crate) fn process_end_stmt(input: Span) -> IResult<Span, &str> {
51    let (input, _) = tag("end")(input)?;
52    let (input, _) = characters::eol(input)?;
53    Ok((input, ""))
54}
55
56/// `<assign-stmt>   ::= assign <dest-sigspec> <src-sigspec> <eol>`
57pub(crate) fn assign_stmt(input: Span) -> IResult<Span, (SigSpec, SigSpec)> {
58    let (input, _) = tag("assign")(input)?;
59    let (input, _) = characters::sep(input)?;
60    let (input, dest) = sigspec::sigspec(input)?;
61    let (input, _) = characters::sep(input)?;
62    let (input, src) = sigspec::sigspec(input)?;
63    let (input, _) = characters::eol(input)?;
64    Ok((input, (dest, src)))
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use indoc::indoc;
71    use pretty_assertions::assert_eq;
72    #[test]
73    fn test_process() {
74        let input = indoc! {r#"
75        process $flatten\ctrl.$proc$serv_ctrl.v:0$702
76            switch 1'0
77                case 
78            end
79            sync always
80            sync init
81        end
82        "#};
83        let (_input, process) = process(Span::new_extra(input, Default::default())).unwrap();
84        assert_eq!(
85            process,
86            (
87                "flatten\\ctrl.$proc$serv_ctrl.v:0$702".to_string(),
88                Process {
89                    attributes: HashMap::new(),
90                    assignments: vec![],
91                    switches: vec![Switch {
92                        attributes: HashMap::new(),
93                        switch_on_sigspec: SigSpec::Constant(Constant::Value(vec!['0'])),
94                        cases: vec![Case {
95                            attributes: HashMap::new(),
96                            compare_against: None,
97                            case_bodies: vec![],
98                        }]
99                    }],
100                    syncs: vec![
101                        sync::sync(Span::new_extra("sync always\n", Default::default()))
102                            .unwrap()
103                            .1,
104                        sync::sync(Span::new_extra("sync init\n", Default::default()))
105                            .unwrap()
106                            .1
107                    ]
108                }
109            )
110        );
111    }
112
113    #[test]
114    fn test_process_multiple_switch_in_process() {
115        let input = indoc! {r#"
116            attribute \src "serv_bufreg.v:35.4-44.7"
117            process $flatten\bufreg.$proc$serv_bufreg.v:35$710
118              assign { } { }
119              assign $flatten\bufreg.$0\data[29:0] \bufreg.data
120              assign $flatten\bufreg.$0\lsb[1:0] \bufreg.lsb
121              assign $flatten\bufreg.$0\c_r[0:0] $flatten\bufreg.$and$serv_bufreg.v:37$711_Y
122              attribute \src "serv_bufreg.v:39.7-40.62"
123              switch \bufreg.i_en
124                case 1'1
125                  assign $flatten\bufreg.$0\data[29:0] { $flatten\bufreg.$ternary$serv_bufreg.v:40$713_Y \bufreg.data [29:1] }
126                case 
127              end
128              attribute \src "serv_bufreg.v:42.7-43.39"
129              switch $flatten\bufreg.$ternary$serv_bufreg.v:42$715_Y
130                case 1'1
131                  assign $flatten\bufreg.$0\lsb[1:0] { $flatten\bufreg.$ternary$serv_bufreg.v:43$716_Y \bufreg.lsb [1] }
132                case 
133              end
134              sync posedge \bufreg.i_clk
135                update \bufreg.c_r $flatten\bufreg.$0\c_r[0:0]
136                update \bufreg.data $flatten\bufreg.$0\data[29:0]
137                update \bufreg.lsb $flatten\bufreg.$0\lsb[1:0]
138            end
139            "#};
140        let (_input, process) = process(Span::new_extra(input, Default::default())).unwrap();
141        assert_eq!(process.0, "flatten\\bufreg.$proc$serv_bufreg.v:35$710");
142        assert_eq!(process.1.attributes.len(), 1);
143    }
144    #[test]
145    fn test_proc_stmt() {
146        let vectors = vec![
147            ("process \\dynports\n", "dynports"),
148            ("process \\top\n", "top"),
149            ("process \\src\n", "src"),
150        ];
151        for (input, expected) in vectors {
152            let span = Span::new_extra(input, Default::default());
153            let ret = process_stmt(span).unwrap();
154            assert_eq!(ret.1, expected);
155        }
156    }
157    #[test]
158    fn test_proc_end_stmt() {
159        let vectors = vec![("end\n", "")];
160        for input in vectors {
161            let span = Span::new_extra(input.0, Default::default());
162            let ret = process_end_stmt(span).unwrap();
163            assert_eq!(ret.1, input.1);
164        }
165    }
166
167    #[test]
168    fn test_assign_stmt() {
169        let vectors = vec![(
170            indoc! {r#"
171                assign $flatten\bufreg2.$0\dat[31:0] $flatten\bufreg2.$ternary$serv_bufreg2.v:62$80_Y
172                "#},
173            (
174                SigSpec::WireId("flatten\\bufreg2.$0\\dat[31:0]".to_string()),
175                SigSpec::WireId("flatten\\bufreg2.$ternary$serv_bufreg2.v:62$80_Y".to_string()),
176            ),
177        )];
178        for (input, expected) in vectors {
179            let span = Span::new_extra(input, Default::default());
180            let ret = assign_stmt(span).unwrap();
181            assert_eq!(ret.1, expected);
182        }
183    }
184}