diff --git a/src/builtin_cells.rs b/src/builtin_cells.rs new file mode 100644 index 0000000..037cdc7 --- /dev/null +++ b/src/builtin_cells.rs @@ -0,0 +1,54 @@ +use crate::frontend::{Callable, CallArgument, Type}; +use crate::rtlil; + +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 +} + +fn instantiate_binop(celltype: &str, id: &str, args: &[String], ret: &str) -> rtlil::Cell { + let a = args.get(0).expect("wrong argcount slipped through type check"); + let b = args.get(1).expect("wrong argcount slipped through type check"); + + 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", ret); + cell +} + +fn make_binop_callable(name: &str, celltype: &'static str) -> Callable { + let args = vec![ + CallArgument { + name: "A".to_owned(), + atype: Type::wire(), + }, + CallArgument { + name: "B".to_owned(), + atype: Type::wire(), + }, + ]; + Callable { + name: name.to_owned(), + args, + ret: Type::wire(), + instantiate: Box::new(move |id, args, ret| instantiate_binop(celltype, id, args, ret)) + } +} + +pub fn get_builtins() -> Vec { + vec![ + make_binop_callable("and", "$and"), + make_binop_callable("xor", "$xor"), + ] +} diff --git a/src/frontend.rs b/src/frontend.rs index 8aeb19b..46fc152 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1,30 +1,8 @@ +use std::collections::BTreeMap; + 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 +use crate::builtin_cells::get_builtins; fn make_pubid(id: &str) -> String { "\\".to_owned() + id @@ -58,45 +36,46 @@ pub enum Type { Wire(GenericParam) } -// module that can be instantiated like a function -pub struct Callable { +impl Type { + pub fn wire() -> Self { + Self::Wire(GenericParam::Unsolved) + } } -fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Result { +pub struct CallArgument { + pub name: String, + pub atype: Type, +} + +// module that can be instantiated like a function +pub struct Callable { + pub name: String, + pub args: Vec, + pub ret: Type, + pub instantiate: Box rtlil::Cell> +} + +struct Context { + callables: BTreeMap +} + +fn lower_expression(ctx: &Context, 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)); + let args_resolved = call.args.iter() + .map(|expr| lower_expression(ctx, module, expr)).collect::, _>>()?; - // TODO: make this sensible - let cell = match *call.name.fragment() { - "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) - } - name => return Err(CompileError::new(CompileErrorKind::UndefinedReference(name.to_owned()))), - }; + let callable = ctx.callables.get(call.name.fragment() as &str).ok_or_else(|| { + CompileError::new(CompileErrorKind::UndefinedReference(call.name.fragment().to_string())) + })?; + + let cell_id = module.make_genid(&callable.name); + + let cell = (*callable.instantiate)(&cell_id, args_resolved.as_slice(), &output_gen_id); module.add_cell(cell); Ok(output_gen_id) } @@ -104,9 +83,9 @@ fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Re } } -fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> { +fn lower_assignment(ctx: &Context, module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> { let target_id = make_pubid(&assignment.lhs); - let return_wire = lower_expression(module, &assignment.expr)?; + let return_wire = lower_expression(ctx, module, &assignment.expr)?; module.add_connection(target_id, return_wire); Ok(()) } @@ -114,6 +93,7 @@ fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> R 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)); + let mut context = Context { callables: get_builtins().into_iter().map(|clb| (clb.name.to_owned(), clb)).collect() }; writer.write_line("autoidx 1"); for (idx, port) in pa_module.ports.iter().enumerate() { let dir_option = match port.direction { @@ -129,7 +109,7 @@ pub fn lower_module(pa_module: parser::Module) -> Result { } for stmt in pa_module.statements { match stmt { - parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment)?, + parser::Statement::Assign(assignment) => lower_assignment(&context, &mut ir_module, assignment)?, } } ir_module.write_rtlil(&mut writer);