use super::typed_ir; use super::typed_ir::ExprKind; use super::{make_pubid, CompileError, Context}; use crate::rtlil; use crate::rtlil::RtlilWrite; fn lower_expression( ctx: &Context, module: &mut rtlil::Module, expr: &typed_ir::Expr, ) -> Result { let expr_width = ctx.types.get_width(expr.typ).expect("signal needs width"); let expr_wire_name = format!("$_sig_{}", expr.id.0); let expr_wire = rtlil::Wire::new(expr_wire_name.clone(), expr_width, None); module.add_wire(expr_wire); match &expr.kind { ExprKind::Path(def) => Ok(rtlil::SigSpec::Wire(format!("\\$sig_{}", def.0))), ExprKind::Call(call) => { let args_resolved = call .args .iter() .map(|expr| lower_expression(ctx, module, expr)) .collect::, _>>()?; let callable = ctx.callables.get(call.called); let cell_id = module.make_genid(callable.name()); if call.called == ctx.callables.builtins.xor { let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); let b_width = ctx.types.get_width(call.args[1].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$xor"); cell.add_param("\\A_SIGNED", "0"); cell.add_param("\\A_WIDTH", &a_width.to_string()); cell.add_param("\\B_SIGNED", "0"); cell.add_param("\\B_WIDTH", &b_width.to_string()); cell.add_param("\\Y_WIDTH", &y_width.to_string()); cell.add_connection("\\A", &args_resolved[0]); cell.add_connection("\\B", &args_resolved[1]); cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone())); module.add_cell(cell); } else if call.called == ctx.callables.builtins.reduce_or { let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$reduce_or"); cell.add_param("\\A_SIGNED", "0"); cell.add_param("\\A_WIDTH", &a_width.to_string()); cell.add_param("\\Y_WIDTH", &y_width.to_string()); cell.add_connection("\\A", &args_resolved[0]); cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone())); module.add_cell(cell); } else if call.called == ctx.callables.builtins.bitnot { let a_width = ctx.types.get_width(call.args[0].typ).unwrap(); let y_width = ctx.types.get_width(expr.typ).unwrap(); let mut cell = rtlil::Cell::new(&cell_id, "$not"); cell.add_param("\\A_SIGNED", "0"); cell.add_param("\\A_WIDTH", &a_width.to_string()); cell.add_param("\\Y_WIDTH", &y_width.to_string()); cell.add_connection("\\A", &args_resolved[0]); cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone())); module.add_cell(cell); } // TODO: insert builtin cells here Ok(rtlil::SigSpec::Wire(expr_wire_name)) } ExprKind::Literal(lit) => Ok(rtlil::SigSpec::Const( ctx.types .elab_as_u32(lit) .expect("const does not fit in 32 bits") as i64, ctx.types.get_width(expr.typ).expect("signal has no size"), )), ExprKind::Match(match_) => { let cases = match_ .arms .iter() .map(|(pat, val)| { Ok(( lower_expression(ctx, module, pat)?, rtlil::CaseRule { assign: vec![( rtlil::SigSpec::Wire(expr_wire_name.clone()), lower_expression(ctx, module, val)?, )], switches: vec![], }, )) }) .collect::, CompileError>>() .unwrap(); let root_switch = rtlil::SwitchRule { signal: lower_expression(ctx, module, &match_.expr)?, cases, }; let root_case = rtlil::CaseRule { assign: vec![], switches: vec![root_switch], }; let proc = rtlil::Process { id: module.make_genid("match"), root_case, sync_rules: vec![], }; module.add_process(proc); Ok(rtlil::SigSpec::Wire(expr_wire_name)) } } } fn lower_comb( ctx: &mut Context, module: &mut rtlil::Module, block: &typed_ir::Body, ) -> Result<(), CompileError> { for (num, sig) in block.signals.iter().enumerate() { let sig_id = format!("\\$sig_{}", sig.id.0); let port_width = ctx.types.get_width(sig.typ).expect("signal has no size"); module.add_wire(rtlil::Wire::new( sig_id.clone(), port_width, Some(rtlil::PortOption::Input((num + 1) as i32)), )); } let ret_id = make_pubid("ret"); module.add_wire(rtlil::Wire::new( ret_id.clone(), ctx.types .get_width(block.expr.typ) .expect("signal has no size"), Some(rtlil::PortOption::Output(block.signals.len() as i32)), )); let out_sig = lower_expression(ctx, module, &block.expr)?; module.add_connection(&rtlil::SigSpec::Wire(ret_id), &out_sig); Ok(()) } pub fn lower_block(context: &mut Context, block: &typed_ir::Body) -> Result { let mut writer = rtlil::ILWriter::new(); let mut ir_module = rtlil::Module::new(make_pubid("test")); lower_comb(context, &mut ir_module, block)?; writer.write_line("autoidx 1"); ir_module.write_rtlil(&mut writer); Ok(writer.finish()) }