From 5b4f3785265f9aabdd32a71627c96babb3dee5c3 Mon Sep 17 00:00:00 2001 From: NotAFile Date: Tue, 4 Jan 2022 23:05:25 +0100 Subject: [PATCH] extract lowering into frontend --- src/frontend.rs | 108 +++++++++++++++++++++++++++++++ src/main.rs | 8 ++- src/rtlil.rs | 168 +++++++++++------------------------------------- 3 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 src/frontend.rs diff --git a/src/frontend.rs b/src/frontend.rs new file mode 100644 index 0000000..e1f20d4 --- /dev/null +++ b/src/frontend.rs @@ -0,0 +1,108 @@ +use crate::parser; +use crate::rtlil; + +fn builtin_binop_cell(celltype: &str, id: &str, a: &str, b: &str, y: &str) -> rtlil::Cell { + let mut cell = rtlil::Cell::new(id, celltype); + cell.add_param("\\A_SIGNED", "0"); + cell.add_param("\\A_WIDTH", "1"); + cell.add_param("\\B_SIGNED", "0"); + cell.add_param("\\B_WIDTH", "1"); + cell.add_param("\\Y_WIDTH", "1"); + cell.add_connection("\\A", a); + cell.add_connection("\\B", b); + cell.add_connection("\\Y", y); + cell +} + +fn builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> rtlil::Cell { + let mut cell = rtlil::Cell::new(id, celltype); + cell.add_param("\\A_SIGNED", "0"); + cell.add_param("\\A_WIDTH", "1"); + cell.add_param("\\Y_WIDTH", "1"); + cell.add_connection("\\A", a); + cell.add_connection("\\Y", y); + cell +} + +// the hacky way + +fn make_pubid(id: &str) -> String { + "\\".to_owned() + id +} + +#[derive(Debug)] +pub struct CompileError; + +fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Result { + match expr { + parser::Expression::Ident(ident) => Ok(make_pubid(&ident)), + parser::Expression::Call(call) => { + let output_gen_id = module.make_genid("cell"); + module.add_wire(rtlil::Wire::new(&output_gen_id, 1, None)); + + let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr)); + + // TODO: make this sensible + let cell = match call.name.as_str() { + "and" => { + let arg_a = args_resolved.next().unwrap()?; + let arg_b = args_resolved.next().unwrap()?; + let cell_id = module.make_genid("and"); + builtin_binop_cell("$and", &cell_id, &arg_a, &arg_b, &output_gen_id) + } + "xor" => { + let arg_a = args_resolved.next().unwrap()?; + let arg_b = args_resolved.next().unwrap()?; + let cell_id = module.make_genid("xor"); + builtin_binop_cell("$xor", &cell_id, &arg_a, &arg_b, &output_gen_id) + } + "not" => { + let arg_a = args_resolved.next().unwrap()?; + let cell_id = module.make_genid("not"); + builtin_unop_cell("$not", &cell_id, &arg_a, &output_gen_id) + } + "reduce_or" => { + let arg_a = args_resolved.next().unwrap()?; + let cell_id = module.make_genid("reduce_or"); + builtin_unop_cell("$reduce_or", &cell_id, &arg_a, &output_gen_id) + } + _ => return Err(CompileError {}), + }; + module.add_cell(cell); + Ok(output_gen_id) + } + parser::Expression::Operation(op) => todo!(), + } +} + +fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> { + let target_id = make_pubid(&assignment.lhs); + let return_wire = lower_expression(module, &assignment.expr)?; + module.add_connection(target_id, return_wire); + Ok(()) +} + +pub fn lower_module(pa_module: parser::Module) -> Result { + let mut writer = rtlil::ILWriter::new(); + let mut ir_module = rtlil::Module::new(make_pubid(&pa_module.name)); + writer.write_line("autoidx 1"); + for (idx, port) in pa_module.ports.iter().enumerate() { + let dir_option = match port.direction { + parser::PortDirection::Input => rtlil::PortOption::Input(idx as i32 + 1), + parser::PortDirection::Output => rtlil::PortOption::Output(idx as i32 + 1), + }; + let wire = rtlil::Wire::new( + make_pubid(&port.net.name), + port.net.width.unwrap_or(1) as u32, + Some(dir_option) + ); + ir_module.add_wire(wire); + } + for stmt in pa_module.statements { + match stmt { + parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment)?, + } + } + ir_module.write_rtlil(&mut writer); + Ok(writer.finish()) +} diff --git a/src/main.rs b/src/main.rs index d62d640..03067b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod literals; mod parser; mod rtlil; +mod frontend; use nom::error::convert_error; use std::fs::File; @@ -34,8 +35,11 @@ fn main() { Err(_) => (), Ok(res) => { println!("{:#?}", res); - let lowered = crate::rtlil::lower_module(res.1); - println!("{}", lowered); + let lowered = crate::frontend::lower_module(res.1); + match lowered { + Ok(res) => println!("{}", res), + Err(err) => println!("{:#?}", err), + } } } } diff --git a/src/rtlil.rs b/src/rtlil.rs index 2dc180c..64dbdbc 100644 --- a/src/rtlil.rs +++ b/src/rtlil.rs @@ -1,6 +1,3 @@ -use crate::parser; -use std::collections::HashMap; - #[derive(Debug, Default)] pub struct ILWriter { data: String, @@ -10,65 +7,71 @@ pub struct ILWriter { // this would be much nicer if indent gave you a new writer // which would indent things impl ILWriter { - fn new() -> Self { + pub fn new() -> Self { Default::default() } - fn write_line(&mut self, line: &str) { + pub fn write_line(&mut self, line: &str) { self.data += &"\t".repeat(self.indent); self.data += line; self.data += "\n"; } - fn indent(&mut self) { + pub fn indent(&mut self) { self.indent += 1 } - fn dedent(&mut self) { + pub fn dedent(&mut self) { self.indent = self .indent .checked_sub(1) .expect("tried to dedent negative amount") } - fn finish(self) -> String { + pub fn finish(self) -> String { self.data } } // the proper way #[derive(Debug)] -pub enum WireOption { +pub enum PortOption { Input(i32), Output(i32), - Width(i32), } #[derive(Debug)] pub struct Wire { + /// rtlil ID id: String, - options: Vec, + /// width in bits + width: u32, + /// Port info if this is a port + port_info: Option } impl Wire { - fn new(id: impl Into) -> Self { + pub fn new(id: impl Into, width: u32, port_info: Option) -> Self { Wire { id: id.into(), - options: Default::default(), + width, + port_info } } fn write_rtlil(&self, writer: &mut ILWriter) { let mut line = String::from("wire "); - for option in &self.options { - let option_str = match option { - WireOption::Input(num) => format!("input {} ", num), - WireOption::Output(num) => format!("output {} ", num), - WireOption::Width(num) => format!("width {} ", num), + if let Some(option) = &self.port_info { + let port_str = match option { + PortOption::Input(num) => format!("input {} ", num), + PortOption::Output(num) => format!("output {} ", num), }; + line += &port_str; + }; - line += &option_str; + if self.width > 1 { + line += &format!("width {} ", self.width); } line += &self.id; @@ -77,7 +80,7 @@ impl Wire { } #[derive(Debug)] -struct Module { +pub struct Module { name: String, wires: Vec, cells: Vec, @@ -86,7 +89,7 @@ struct Module { } impl Module { - fn new(name: impl Into) -> Self { + pub fn new(name: impl Into) -> Self { Module { name: name.into(), wires: Default::default(), @@ -96,11 +99,19 @@ impl Module { } } - fn add_wire(&mut self, wire: Wire) { + pub fn add_wire(&mut self, wire: Wire) { self.wires.push(wire) } - fn write_rtlil(&self, writer: &mut ILWriter) { + pub fn add_connection(&mut self, target: String, source: String) { + self.connections.push((target, source)) + } + + pub fn add_cell(&mut self, cell: Cell) { + self.cells.push(cell) + } + + pub fn write_rtlil(&self, writer: &mut ILWriter) { writer.write_line(&format!("module {}", self.name)); writer.indent(); for wire in &self.wires { @@ -116,7 +127,7 @@ impl Module { writer.write_line("end"); } - fn make_genid(&mut self, stem: &str) -> String { + pub fn make_genid(&mut self, stem: &str) -> String { let res = format!("${}${}", stem, self.gen_id); self.gen_id += 1; res @@ -124,7 +135,7 @@ impl Module { } #[derive(Debug)] -struct Cell { +pub struct Cell { id: String, celltype: String, parameters: Vec<(String, String)>, @@ -132,7 +143,7 @@ struct Cell { } impl Cell { - fn new(id: &str, celltype: &str) -> Self { + pub fn new(id: &str, celltype: &str) -> Self { Cell { id: id.into(), celltype: celltype.into(), @@ -141,11 +152,11 @@ impl Cell { } } - fn add_param(&mut self, name: &str, value: &str) { + pub fn add_param(&mut self, name: &str, value: &str) { self.parameters.push((name.into(), value.into())) } - fn add_connection(&mut self, from: &str, to: &str) { + pub fn add_connection(&mut self, from: &str, to: &str) { self.connections.push((from.into(), to.into())) } @@ -162,104 +173,3 @@ impl Cell { writer.write_line("end"); } } - -fn builtin_binop_cell(celltype: &str, id: &str, a: &str, b: &str, y: &str) -> Cell { - let mut cell = Cell::new(id, celltype); - cell.add_param("\\A_SIGNED", "0"); - cell.add_param("\\A_WIDTH", "1"); - cell.add_param("\\B_SIGNED", "0"); - cell.add_param("\\B_WIDTH", "1"); - cell.add_param("\\Y_WIDTH", "1"); - cell.add_connection("\\A", a); - cell.add_connection("\\B", b); - cell.add_connection("\\Y", y); - cell -} - -fn builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> Cell { - let mut cell = Cell::new(id, celltype); - cell.add_param("\\A_SIGNED", "0"); - cell.add_param("\\A_WIDTH", "1"); - cell.add_param("\\Y_WIDTH", "1"); - cell.add_connection("\\A", a); - cell.add_connection("\\Y", y); - cell -} - -// the hacky way - -fn make_pubid(id: &str) -> String { - "\\".to_owned() + id -} - -fn lower_expression(module: &mut Module, expr: &parser::Expression) -> String { - match expr { - parser::Expression::Ident(ident) => make_pubid(&ident), - parser::Expression::Call(call) => { - let output_gen_id = module.make_genid("cell"); - module.add_wire(Wire::new(&output_gen_id)); - - let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr)); - - // TODO: make this sensible - let cell = match call.name.as_str() { - "and" => { - let arg_a = args_resolved.next().unwrap(); - let arg_b = args_resolved.next().unwrap(); - let cell_id = module.make_genid("and"); - builtin_binop_cell("$and", &cell_id, &arg_a, &arg_b, &output_gen_id) - } - "xor" => { - let arg_a = args_resolved.next().unwrap(); - let arg_b = args_resolved.next().unwrap(); - let cell_id = module.make_genid("xor"); - builtin_binop_cell("$xor", &cell_id, &arg_a, &arg_b, &output_gen_id) - } - "not" => { - let arg_a = args_resolved.next().unwrap(); - let cell_id = module.make_genid("not"); - builtin_unop_cell("$not", &cell_id, &arg_a, &output_gen_id) - } - "reduce_or" => { - let arg_a = args_resolved.next().unwrap(); - let cell_id = module.make_genid("reduce_or"); - builtin_unop_cell("$reduce_or", &cell_id, &arg_a, &output_gen_id) - } - _ => todo!("unknown function"), - }; - module.cells.push(cell); - output_gen_id - } - parser::Expression::Operation(op) => todo!(), - } -} - -fn lower_assignment(module: &mut Module, assignment: parser::Assign) { - let target_id = make_pubid(&assignment.lhs); - let return_wire = lower_expression(module, &assignment.expr); - module.connections.push((target_id, return_wire)) -} - -pub fn lower_module(pa_module: parser::Module) -> String { - let mut writer = ILWriter::new(); - let mut ir_module = Module::new(format!("\\{}", pa_module.name)); - writer.write_line("autoidx 1"); - for (idx, port) in pa_module.ports.iter().enumerate() { - let dir_option = match port.direction { - parser::PortDirection::Input => WireOption::Input(idx as i32 + 1), - parser::PortDirection::Output => WireOption::Output(idx as i32 + 1), - }; - let wire = Wire { - id: make_pubid(&port.net.name), - options: vec![dir_option], - }; - ir_module.wires.push(wire); - } - for stmt in pa_module.statements { - match stmt { - parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment), - } - } - ir_module.write_rtlil(&mut writer); - writer.finish() -}