use std::collections::BTreeMap; use crate::builtin_cells::get_builtins; use crate::parser; use crate::rtlil; fn make_pubid(id: &str) -> String { "\\".to_owned() + id } #[derive(Debug)] pub enum CompileErrorKind { UndefinedReference(String), BadArgCount{received: usize, expected: usize}, } #[derive(Debug)] pub struct CompileError { kind: CompileErrorKind, } impl CompileError { fn new(kind: CompileErrorKind) -> Self { Self { kind } } } pub enum GenericParam { Unsolved, Solved(T), } pub enum Type { /// a wire of some width Wire(GenericParam), } impl Type { pub fn wire() -> Self { Self::Wire(GenericParam::Unsolved) } } 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 args_resolved = call .args .iter() .map(|expr| lower_expression(ctx, module, expr)) .collect::, _>>()?; let callable = ctx .callables .get(call.name.fragment() as &str) .ok_or_else(|| { CompileError::new(CompileErrorKind::UndefinedReference( call.name.fragment().to_string(), )) })?; if args_resolved.len() != callable.args.len() { return Err(CompileError::new(CompileErrorKind::BadArgCount{ expected: callable.args.len(), received: args_resolved.len() })) } 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) } parser::Expression::Operation(_op) => todo!(), } } 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(ctx, 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)); let 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 { 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(&context, &mut ir_module, assignment)? } } } ir_module.write_rtlil(&mut writer); Ok(writer.finish()) }