futilehdl/src/frontend/lowering.rs

147 lines
6.0 KiB
Rust

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<rtlil::SigSpec, CompileError> {
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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, 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::Block,
) -> 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::Block) -> Result<String, CompileError> {
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())
}