use std::collections::BTreeMap; use crate::parser; use crate::rtlil; use crate::builtin_cells::get_builtins; fn make_pubid(id: &str) -> String { "\\".to_owned() + id } #[derive(Debug)] pub enum CompileErrorKind { UndefinedReference(String) } #[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())) })?; 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 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 { 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()) }