chirrtl_parser/
lib.rs

1pub mod ast;
2pub mod lexer;
3
4use crate::ast::{Circuit, Module};
5use crate::firrtl::*;
6use crate::lexer::{FIRRTLLexer, Token, LexicalError};
7use ast::CircuitModule;
8use lalrpop_util::{lalrpop_mod, ParseError};
9
10lalrpop_mod!(pub firrtl);
11
12pub type FIRRTLParserError = ParseError<usize, Token, LexicalError>;
13
14/// Given a path to a FIRRTL file, parse it and return a `Circuit` which represents the FIRRTL AST
15pub fn parse_circuit(source: &str) -> Result<Circuit, FIRRTLParserError> {
16    let lexer = FIRRTLLexer::new(source);
17    let parser = CircuitParser::new();
18    parser.parse(lexer)
19}
20
21#[cfg(test)]
22mod lexer_test {
23    use crate::lexer::*;
24
25    fn run(source: &str) {
26        let mut lex = FIRRTLLexer::new(source);
27        while let Some(ts) = lex.next_token() {
28            println!("{:?}", ts);
29            match ts.token {
30                Token::Error => {
31                    println!("{:?}", ts);
32                    panic!("Got a error token");
33                }
34                _ => { }
35            }
36        }
37    }
38
39    #[test]
40    fn gcd() {
41        let source =
42r#"FIRRTL version 3.3.0
43circuit GCD :
44  module GCD : @[src/main/scala/gcd/GCD.scala 15:7]
45    input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
46    input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
47    output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
48
49    reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
50    reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
51    node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
52    when _T : @[src/main/scala/gcd/GCD.scala 27:15]
53      node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
54      node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
55      connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
56    else :
57      node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
58      node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
59      connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
60    when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
61      connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
62      connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
63    connect io.outputGCD, x @[src/main/scala/gcd/GCD.scala 35:16]
64    node _io_outputValid_T = eq(y, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 36:23]
65    connect io.outputValid, _io_outputValid_T @[src/main/scala/gcd/GCD.scala 36:18]
66"#;
67
68        run(source);
69    }
70
71    #[test]
72    fn one_read_one_write_sram() {
73        let source =
74r#"FIRRTL version 3.3.0
75circuit OneReadOneWritePortSRAM :
76  module OneReadOneWritePortSRAM : @[src/main/scala/gcd/SRAM.scala 10:7]
77    input clock : Clock @[src/main/scala/gcd/SRAM.scala 10:7]
78    input reset : UInt<1> @[src/main/scala/gcd/SRAM.scala 10:7]
79    output io : { flip ren : UInt<1>, flip raddr : UInt<3>, rdata : UInt<2>[4], flip wen : UInt<1>, flip waddr : UInt<3>, flip wdata : UInt<2>[4], flip wmask : UInt<1>[4]} @[src/main/scala/gcd/SRAM.scala 11:14]
80
81    smem mem : UInt<2>[4] [8] @[src/main/scala/gcd/SRAM.scala 22:24]
82    when io.wen : @[src/main/scala/gcd/SRAM.scala 23:17]
83      write mport MPORT = mem[io.waddr], clock @[src/main/scala/gcd/SRAM.scala 24:14]
84      when io.wmask[0] : @[src/main/scala/gcd/SRAM.scala 24:14]
85        connect MPORT[0], io.wdata[0] @[src/main/scala/gcd/SRAM.scala 24:14]
86      when io.wmask[1] : @[src/main/scala/gcd/SRAM.scala 24:14]
87        connect MPORT[1], io.wdata[1] @[src/main/scala/gcd/SRAM.scala 24:14]
88      when io.wmask[2] : @[src/main/scala/gcd/SRAM.scala 24:14]
89        connect MPORT[2], io.wdata[2] @[src/main/scala/gcd/SRAM.scala 24:14]
90      when io.wmask[3] : @[src/main/scala/gcd/SRAM.scala 24:14]
91        connect MPORT[3], io.wdata[3] @[src/main/scala/gcd/SRAM.scala 24:14]
92    wire _WIRE : UInt<3> @[src/main/scala/gcd/SRAM.scala 26:23]
93    invalidate _WIRE @[src/main/scala/gcd/SRAM.scala 26:23]
94    when io.ren : @[src/main/scala/gcd/SRAM.scala 26:23]
95      connect _WIRE, io.raddr @[src/main/scala/gcd/SRAM.scala 26:23]
96      read mport MPORT_1 = mem[_WIRE], clock @[src/main/scala/gcd/SRAM.scala 26:23]
97    connect io.rdata, MPORT_1 @[src/main/scala/gcd/SRAM.scala 26:12]
98    "#;
99
100        run(source);
101    }
102
103    #[test]
104    fn ports_2() {
105        let source = r#"output io : { flip a : UInt<2>, flip b : UInt<2>, flip c : UInt<2>, flip sel : UInt<2>, output : UInt<2>}"#;
106        run(source);
107    }
108
109    #[test]
110    fn extmodule() {
111        let source =
112r#"extmodule plusarg_reader : @[generators/rocket-chip/src/main/scala/util/PlusArg.scala 45:7]
113     output out : UInt<32>
114     defname = plusarg_reader
115     parameter DEFAULT = 0
116     parameter FORMAT = "tilelink_timeout=%d"
117     parameter WIDTH = 32"#;
118        run(source);
119    }
120
121    #[test]
122    fn stmts() {
123        let source =
124r#"reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
125reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
126node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
127when _T : @[src/main/scala/gcd/GCD.scala 27:15]
128  node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
129  node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
130  connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
131else :
132  node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
133  node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
134  connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
135when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
136  connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
137  connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
138"#;
139        run(source);
140    }
141
142    #[test]
143    fn circuit_annos() {
144        let source =
145r#"FIRRTL version 3.3.0
146circuit GCD :%[[
147  {
148    "class":"firrtl.transforms.DedupGroupAnnotation",
149    "target":"~TestHarness|IntXbar_i1_o1",
150    "group":"IntXbar_i1_o1"
151  }
152]]
153  module GCD : @[src/main/scala/gcd/GCD.scala 15:7]
154    input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
155    input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
156    output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
157
158    reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
159    reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
160    node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
161    when _T : @[src/main/scala/gcd/GCD.scala 27:15]
162      node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
163      node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
164      connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
165    else :
166      node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
167      node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
168      connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
169    when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
170      connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
171      connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
172    connect io.outputGCD, x @[src/main/scala/gcd/GCD.scala 35:16]
173    node _io_outputValid_T = eq(y, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 36:23]
174    connect io.outputValid, _io_outputValid_T @[src/main/scala/gcd/GCD.scala 36:18]
175"#;
176        run(&source);
177    }
178
179    #[test]
180    fn rocketconfig() -> Result<(), std::io::Error> {
181        let source = std::fs::read_to_string("./test-inputs/chipyard.harness.TestHarness.RocketConfig.fir")?;
182        run(&source);
183        Ok(())
184    }
185
186    #[test]
187    fn largeboomconfig() -> Result<(), std::io::Error> {
188        let source = std::fs::read_to_string("./test-inputs/chipyard.harness.TestHarness.LargeBoomV3Config.fir")?;
189        run(&source);
190        Ok(())
191    }
192
193}
194
195#[cfg(test)]
196mod parser_test {
197    use crate::lexer::*;
198    use crate::firrtl::*;
199
200    #[test]
201    fn stmts() {
202        let source =
203r#"reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
204reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
205node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
206when _T : @[src/main/scala/gcd/GCD.scala 27:15]
207  node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
208  node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
209  connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
210else :
211  node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
212  node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
213  connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
214when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
215  connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
216  connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
217"#;
218        let lexer = FIRRTLLexer::new(source);
219        let parser = StmtsParser::new();
220        let ast = parser.parse(lexer).unwrap();
221
222        for stmt in ast.iter() {
223            stmt.traverse();
224        }
225    }
226
227    #[test]
228    fn ports() {
229        let source =
230r#"
231input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
232input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
233output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
234"#;
235        let lexer = FIRRTLLexer::new(source);
236        let parser = PortsParser::new();
237        let ast = parser.parse(lexer).unwrap();
238        println!("{:?}", ast);
239    }
240
241    #[test]
242    fn ports_2() {
243        let source = r#"output io : { flip a : UInt<2>, flip b : UInt<2>, flip c : UInt<2>, flip sel : UInt<2>, output : UInt<2>}"#;
244        let lexer = FIRRTLLexer::new(source);
245        let parser = PortsParser::new();
246        let ast = parser.parse(lexer).unwrap();
247        println!("{:?}", ast);
248    }
249
250    #[test]
251    fn ports_3() {
252        let source = r#"output io : { flip in : { a : { ready : UInt<1>, valid : UInt<1>, bits : { opcode : UInt<3>, param : UInt<3>, size : UInt<4>, source : UInt<5>, address : UInt<32>, user : { }, echo : { }, mask : UInt<8>, data : UInt<64>, corrupt : UInt<1>}}, d : { ready : UInt<1>, valid : UInt<1>, bits : { opcode : UInt<3>, param : UInt<2>, size : UInt<4>, source : UInt<5>, sink : UInt<3>, denied : UInt<1>, user : { }, echo : { }, data : UInt<64>, corrupt : UInt<1>}}}} @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 20:14]"#;
253        let lexer = FIRRTLLexer::new(source);
254        let parser = PortsParser::new();
255        let ast = parser.parse(lexer).unwrap();
256        println!("{:?}", ast);
257    }
258
259    #[test]
260    fn when() {
261        let source =
262r#"
263when io.in.a.valid : @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 372:27]
264  node _T = leq(io.in.a.bits.opcode, UInt<3>(0h7)) @[generators/rocket-chip/src/main/scala/tilelink/Bundles.scala 42:24]
265  node _T_1 = asUInt(reset) @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
266  node _T_2 = eq(_T_1, UInt<1>(0h0)) @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
267  when _T_2 : @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
268    node _T_3 = eq(_T, UInt<1>(0h0)) @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
269    when _T_3 : @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
270      printf(clock, UInt<1>(0h1), "Assertion failed: 'A' channel has invalid opcode (connected at generators/rocket-chip/src/main/scala/subsystem/SystemBus.scala:48:55)\n    at Monitor.scala:45 assert(cond, message)\n") : printf @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
271    assert(clock, _T, UInt<1>(0h1), "") : assert @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]
272"#;
273
274        let lexer = FIRRTLLexer::new(source);
275        let parser = StmtsParser::new();
276        let ast = parser.parse(lexer).unwrap();
277        println!("{:?}", ast);
278
279    }
280
281    #[test]
282    fn module() {
283        let source =
284r#"
285module GCD : @[src/main/scala/gcd/GCD.scala 15:7]
286  input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
287  input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
288  output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
289
290  reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
291  reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
292  node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
293  when _T : @[src/main/scala/gcd/GCD.scala 27:15]
294    node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
295    node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
296    connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
297  else :
298    node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
299    node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
300    connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
301  when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
302    connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
303    connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
304  connect io.outputGCD, x @[src/main/scala/gcd/GCD.scala 35:16]
305  node _io_outputValid_T = eq(y, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 36:23]
306  connect io.outputValid, _io_outputValid_T @[src/main/scala/gcd/GCD.scala 36:18]
307"#;
308        let lexer = FIRRTLLexer::new(source);
309        let parser = ModuleParser::new();
310        let ast = parser.parse(lexer).unwrap();
311        println!("{:?}", ast);
312    }
313    #[test]
314    fn version() {
315        let source = r#"FIRRTL version 3.3.0"#;
316        let lexer = FIRRTLLexer::new(source);
317        let parser = VersionParser::new();
318        let ast = parser.parse(lexer).unwrap();
319        println!("{:?}", ast);
320    }
321
322    #[test]
323    fn circuit() {
324        let source =
325r#"FIRRTL version 3.3.0
326circuit GCD :
327  module GCD : @[src/main/scala/gcd/GCD.scala 15:7]
328    input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
329    input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
330    output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
331
332    reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
333    reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
334    node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
335    when _T : @[src/main/scala/gcd/GCD.scala 27:15]
336      node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
337      node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
338      connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
339    else :
340      node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
341      node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
342      connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
343    when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
344      connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
345      connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
346    connect io.outputGCD, x @[src/main/scala/gcd/GCD.scala 35:16]
347    node _io_outputValid_T = eq(y, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 36:23]
348    connect io.outputValid, _io_outputValid_T @[src/main/scala/gcd/GCD.scala 36:18]
349"#;
350        let lexer = FIRRTLLexer::new(source);
351        let parser = CircuitParser::new();
352        let ast = parser.parse(lexer).unwrap();
353        println!("{:?}", ast);
354    }
355
356    #[test]
357    fn circuit_annos() {
358        let source =
359r#"FIRRTL version 3.3.0
360circuit GCD :%[[
361  {
362    "class":"firrtl.transforms.DedupGroupAnnotation",
363    "target":"~TestHarness|IntXbar_i1_o1",
364    "group":"IntXbar_i1_o1"
365  }
366]]
367  module GCD : @[src/main/scala/gcd/GCD.scala 15:7]
368    input clock : Clock @[src/main/scala/gcd/GCD.scala 15:7]
369    input reset : UInt<1> @[src/main/scala/gcd/GCD.scala 15:7]
370    output io : { flip value1 : UInt<16>, flip value2 : UInt<16>, flip loadingValues : UInt<1>, outputGCD : UInt<16>, outputValid : UInt<1>} @[src/main/scala/gcd/GCD.scala 16:14]
371
372    reg x : UInt, clock @[src/main/scala/gcd/GCD.scala 24:15]
373    reg y : UInt, clock @[src/main/scala/gcd/GCD.scala 25:15]
374    node _T = gt(x, y) @[src/main/scala/gcd/GCD.scala 27:10]
375    when _T : @[src/main/scala/gcd/GCD.scala 27:15]
376      node _x_T = sub(x, y) @[src/main/scala/gcd/GCD.scala 27:24]
377      node _x_T_1 = tail(_x_T, 1) @[src/main/scala/gcd/GCD.scala 27:24]
378      connect x, _x_T_1 @[src/main/scala/gcd/GCD.scala 27:19]
379    else :
380      node _y_T = sub(y, x) @[src/main/scala/gcd/GCD.scala 28:25]
381      node _y_T_1 = tail(_y_T, 1) @[src/main/scala/gcd/GCD.scala 28:25]
382      connect y, _y_T_1 @[src/main/scala/gcd/GCD.scala 28:20]
383    when io.loadingValues : @[src/main/scala/gcd/GCD.scala 30:26]
384      connect x, io.value1 @[src/main/scala/gcd/GCD.scala 31:7]
385      connect y, io.value2 @[src/main/scala/gcd/GCD.scala 32:7]
386    connect io.outputGCD, x @[src/main/scala/gcd/GCD.scala 35:16]
387    node _io_outputValid_T = eq(y, UInt<1>(0h0)) @[src/main/scala/gcd/GCD.scala 36:23]
388    connect io.outputValid, _io_outputValid_T @[src/main/scala/gcd/GCD.scala 36:18]
389"#;
390        let lexer = FIRRTLLexer::new(source);
391        let parser = CircuitParser::new();
392        let ast = parser.parse(lexer).unwrap();
393        println!("{:?}", ast);
394    }
395
396    #[test]
397    fn nested_whens() {
398        let source =
399r#"FIRRTL version 3.3.0
400circuit NestedWhen :
401  module NestedWhen : @[src/main/scala/gcd/NestedWhen.scala 8:7]
402    input clock : Clock @[src/main/scala/gcd/NestedWhen.scala 8:7]
403    input reset : UInt<1> @[src/main/scala/gcd/NestedWhen.scala 8:7]
404    output io : { flip a : UInt<2>, flip b : UInt<2>, flip c : UInt<2>, flip sel : UInt<2>, output : UInt<2>} @[src/main/scala/gcd/NestedWhen.scala 9:14]
405
406    node _T = eq(io.output, UInt<1>(0h0)) @[src/main/scala/gcd/NestedWhen.scala 17:19]
407    when _T : @[src/main/scala/gcd/NestedWhen.scala 17:28]
408      connect io.output, io.a @[src/main/scala/gcd/NestedWhen.scala 18:15]
409    else :
410      node _T_1 = eq(io.output, UInt<1>(0h1)) @[src/main/scala/gcd/NestedWhen.scala 19:26]
411      when _T_1 : @[src/main/scala/gcd/NestedWhen.scala 19:35]
412        connect io.output, io.b @[src/main/scala/gcd/NestedWhen.scala 20:15]
413      else :
414        connect io.output, io.c @[src/main/scala/gcd/NestedWhen.scala 22:15]
415
416"#;
417        let lexer = FIRRTLLexer::new(source);
418        let parser = CircuitParser::new();
419        let ast = parser.parse(lexer).unwrap();
420        println!("{:?}", ast);
421    }
422
423    #[test]
424    fn one_read_one_write_sram() {
425        let source =
426r#"FIRRTL version 3.3.0
427circuit OneReadOneWritePortSRAM :
428  module OneReadOneWritePortSRAM : @[src/main/scala/gcd/SRAM.scala 10:7]
429    input clock : Clock @[src/main/scala/gcd/SRAM.scala 10:7]
430    input reset : UInt<1> @[src/main/scala/gcd/SRAM.scala 10:7]
431    output io : { flip ren : UInt<1>, flip raddr : UInt<3>, rdata : UInt<2>[4], flip wen : UInt<1>, flip waddr : UInt<3>, flip wdata : UInt<2>[4], flip wmask : UInt<1>[4]} @[src/main/scala/gcd/SRAM.scala 11:14]
432
433    smem mem : UInt<2>[4] [8] @[src/main/scala/gcd/SRAM.scala 22:24]
434    when io.wen : @[src/main/scala/gcd/SRAM.scala 23:17]
435      write mport MPORT = mem[io.waddr], clock @[src/main/scala/gcd/SRAM.scala 24:14]
436      when io.wmask[0] : @[src/main/scala/gcd/SRAM.scala 24:14]
437        connect MPORT[0], io.wdata[0] @[src/main/scala/gcd/SRAM.scala 24:14]
438      when io.wmask[1] : @[src/main/scala/gcd/SRAM.scala 24:14]
439        connect MPORT[1], io.wdata[1] @[src/main/scala/gcd/SRAM.scala 24:14]
440      when io.wmask[2] : @[src/main/scala/gcd/SRAM.scala 24:14]
441        connect MPORT[2], io.wdata[2] @[src/main/scala/gcd/SRAM.scala 24:14]
442      when io.wmask[3] : @[src/main/scala/gcd/SRAM.scala 24:14]
443        connect MPORT[3], io.wdata[3] @[src/main/scala/gcd/SRAM.scala 24:14]
444    wire _WIRE : UInt<3> @[src/main/scala/gcd/SRAM.scala 26:23]
445    invalidate _WIRE @[src/main/scala/gcd/SRAM.scala 26:23]
446    when io.ren : @[src/main/scala/gcd/SRAM.scala 26:23]
447      connect _WIRE, io.raddr @[src/main/scala/gcd/SRAM.scala 26:23]
448      read mport MPORT_1 = mem[_WIRE], clock @[src/main/scala/gcd/SRAM.scala 26:23]
449    connect io.rdata, MPORT_1 @[src/main/scala/gcd/SRAM.scala 26:12]
450    "#;
451
452        let lexer = FIRRTLLexer::new(source);
453        let parser = CircuitParser::new();
454        let ast = parser.parse(lexer).unwrap();
455        println!("{:?}", ast);
456    }
457
458    #[test]
459    fn as_clock_stmt() {
460        let source = "node _childClock_T = asClock(UInt<1>(0h0)) @[generators/diplomacy/diplomacy/src/diplomacy/lazymodule/LazyModuleImp.scala 160:25]";
461        let lexer = FIRRTLLexer::new(source);
462        let parser = StmtParser::new();
463        let ast = parser.parse(lexer).unwrap();
464        println!("{:?}", ast);
465    }
466
467    #[test]
468    fn printf_stmt() {
469        let source = r#"printf(clock, UInt<1>(0h1), "Assertion failed: 'A' channel has invalid opcode (connected at generators/rocket-chip/src/main/scala/subsystem/SystemBus.scala:48:55)\n    at Monitor.scala:45 assert(cond, message)\n") : printf @[generators/rocket-chip/src/main/scala/tilelink/Monitor.scala 45:11]"#;
470        let lexer = FIRRTLLexer::new(source);
471        let parser = StmtParser::new();
472        let ast = parser.parse(lexer).unwrap();
473        println!("{:?}", ast);
474    }
475
476    #[test]
477    fn empty_type() {
478        let source = "output auto : { } @[generators/diplomacy/diplomacy/src/diplomacy/lazymodule/LazyModuleImp.scala 107:25]";
479        let lexer = FIRRTLLexer::new(source);
480        let parser = PortParser::new();
481        let ast = parser.parse(lexer).unwrap();
482        println!("{:?}", ast);
483    }
484
485    #[test]
486    fn extmodule() {
487        let source =
488r#"extmodule plusarg_reader : @[generators/rocket-chip/src/main/scala/util/PlusArg.scala 45:7]
489     output out : UInt<32>
490     defname = plusarg_reader
491     parameter DEFAULT = 0
492     parameter FORMAT = "tilelink_timeout=%d"
493     parameter WIDTH = 32"#;
494
495        let lexer = FIRRTLLexer::new(source);
496        let parser = CircuitModuleParser::new();
497        let ast = parser.parse(lexer).unwrap();
498        println!("{:?}", ast);
499    }
500
501    #[test] fn primop_name_overlaps_with_variable() {
502        let source =
503r#"
504node pad = or(bootAddrReg, UInt<64>(0h0)) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 150:19]
505node _oldBytes_T = bits(pad, 7, 0) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
506node _oldBytes_T_1 = bits(pad, 15, 8) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
507node _oldBytes_T_2 = bits(pad, 23, 16) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
508node _oldBytes_T_3 = bits(pad, 31, 24) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
509node _oldBytes_T_4 = bits(pad, 39, 32) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
510node _oldBytes_T_5 = bits(pad, 47, 40) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
511node _oldBytes_T_6 = bits(pad, 55, 48) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
512node _oldBytes_T_7 = bits(pad, 63, 56) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 151:57]
513"#;
514        let lexer = FIRRTLLexer::new(&source);
515        let parser = StmtsParser::new();
516        let ast = parser.parse(lexer).expect("FAILED");
517        println!("{:?}", ast);
518    }
519
520    #[test] fn empty_skip() {
521        let source =
522r#"
523when do_deq : @[src/main/scala/chisel3/util/Decoupled.scala 273:16]
524  skip
525"#;
526        let lexer = FIRRTLLexer::new(&source);
527        let parser = StmtsParser::new();
528        let ast = parser.parse(lexer).expect("FAILED");
529        println!("{:?}", ast);
530    }
531
532    #[test]
533    fn primop_name_overlaps_with_variable_2() {
534        let source =
535r#"node pad = or(bootAddrReg, UInt<64>(0h0)) @[generators/rocket-chip/src/main/scala/regmapper/RegField.scala 150:19]"#;
536
537        let lexer = FIRRTLLexer::new(&source);
538        let parser = StmtsParser::new();
539        let ast = parser.parse(lexer).expect("FAILED");
540        println!("{:?}", ast);
541    }
542
543    #[test]
544    fn primop_name_overlaps_with_variable_3() {
545        let source =
546r#"
547cmem head : UInt<6> [40] @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 48:18]
548cmem tail : UInt<6> [40] @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 49:18]
549read mport push_tail = tail[io.push.bits.index], clock @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 62:28]
550when _T : @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 66:23]
551  node valid_set_shiftAmount = bits(io.push.bits.index, 5, 0) @[src/main/scala/chisel3/util/OneHot.scala 64:49]
552  node _valid_set_T = dshl(UInt<1>(0h1), valid_set_shiftAmount) @[src/main/scala/chisel3/util/OneHot.scala 65:12]
553  node _valid_set_T_1 = bits(_valid_set_T, 39, 0) @[src/main/scala/chisel3/util/OneHot.scala 65:27]
554  connect valid_set, _valid_set_T_1 @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 67:15]
555  connect used_set, freeOH @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 68:14]
556  write mport MPORT = data[freeIdx], clock @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 69:15]
557  connect MPORT, io.push.bits.data @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 69:15]
558  when push_valid : @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 70:23]
559    write mport MPORT_1 = next[push_tail], clock @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 71:17]
560    connect MPORT_1, freeIdx @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 71:17]
561  else :
562    write mport MPORT_2 = head[io.push.bits.index], clock @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 73:17]
563    connect MPORT_2, freeIdx @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 73:17]
564  write mport MPORT_3 = tail[io.push.bits.index], clock @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 75:15]
565  connect MPORT_3, freeIdx @[generators/rocket-chip-inclusive-cache/design/craft/inclusivecache/src/ListBuffer.scala 75:15]
566"#;
567        let lexer = FIRRTLLexer::new(&source);
568        let parser = StmtsParser::new();
569        let ast = parser.parse(lexer).expect("FAILED");
570        println!("{:?}", ast);
571    }
572
573    #[test]
574    fn extmodule_without_param() {
575        let source = 
576r#"
577extmodule GenericDigitalOutIOCell : @[generators/chipyard/src/main/scala/iocell/IOCell.scala 151:7]
578  output pad : UInt<1>
579  input o : UInt<1>
580  input oe : UInt<1>
581  defname = GenericDigitalOutIOCell
582"#;
583        let lexer = FIRRTLLexer::new(&source);
584        let parser = CircuitModuleParser::new();
585        let ast = parser.parse(lexer).expect("FAILED");
586        println!("{:?}", ast);
587    }
588
589    #[test]
590    fn bug() -> Result<(), std::io::Error> {
591        let source = r#"node _T_567 = asSInt(_T_566) @[generators/rocket-chip/src/main/scala/diplomacy/Parameters.scala 137:46]"#;
592        let lexer = FIRRTLLexer::new(&source);
593        let parser = StmtParser::new();
594        let ast = parser.parse(lexer).expect("FAILED");
595        Ok(())
596    }
597
598    #[test]
599    fn rocketconfig() -> Result<(), std::io::Error> {
600        let source = std::fs::read_to_string("./test-inputs/chipyard.harness.TestHarness.RocketConfig.fir")?;
601        let lexer = FIRRTLLexer::new(&source);
602        let parser = CircuitParser::new();
603        let ast = parser.parse(lexer).expect("FAILED");
604        Ok(())
605    }
606
607    #[test]
608    fn boomconfig() -> Result<(), std::io::Error> {
609        let source = std::fs::read_to_string("./test-inputs/chipyard.harness.TestHarness.LargeBoomV3Config.fir")?;
610        let lexer = FIRRTLLexer::new(&source);
611        let parser = CircuitParser::new();
612        let ast = parser.parse(lexer).expect("FAILED");
613        Ok(())
614    }
615
616    #[test]
617    fn double_indexing() -> Result<(), std::io::Error> {
618        let source = r#"connect io_debug_fetch_pc_0_REG, pcs[io.debug_ftq_idx[0]] @[generators/boom/src/main/scala/v3/ifu/fetch-target-queue.scala 363:36]"#;
619        let lexer = FIRRTLLexer::new(&source);
620        let parser = StmtParser::new();
621        let ast = parser.parse(lexer).expect("FAILED");
622        println!("{:?}", ast);
623        Ok(())
624    }
625
626    #[test]
627    fn rocket_modules() -> Result<(), std::io::Error> {
628        for entry in std::fs::read_dir("./test-inputs/rocket-modules/")? {
629            let entry = entry?;
630            let path = entry.path();
631
632            // Check if it's a file (not a directory)
633            if path.is_file() {
634                match std::fs::read_to_string(&path) {
635                    Ok(source) => {
636                        let lexer = FIRRTLLexer::new(&source);
637                        let parser = CircuitModuleParser::new();
638
639                        println!("Parsing file: {:?}", path);
640                        let ast = parser.parse(lexer).expect("TOWORK");
641                    }
642                    Err(e) => {
643                        eprintln!("Could not read file {}: {}", path.display(), e);
644                    }
645                }
646            }
647        }
648        Ok(())
649    }
650}