futilehdl/src/frontend/lowering.rs

128 lines
5.1 KiB
Rust

use std::collections::BTreeMap;
use super::typed_ir;
use super::typed_ir::ExprKind;
use super::{make_pubid, CompileError, Context, TODO_WIDTH};
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 {
called,
args,
genargs,
} => {
let args_resolved = args
.iter()
.map(|expr| lower_expression(ctx, module, expr))
.collect::<Result<Vec<_>, _>>()?;
let callable = ctx.callables.get(*called);
let cell_id = module.make_genid(callable.name());
if *called == ctx.callables.builtins.xor {
let a_width = ctx.types.get_width(args[0].typ).unwrap();
let b_width = ctx.types.get_width(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",
&rtlil::SigSpec::Wire(format!("$sig_{}", args[0].id.0)),
);
cell.add_connection(
"\\B",
&rtlil::SigSpec::Wire(format!("$sig_{}", args[1].id.0)),
);
cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone()));
module.add_cell(cell);
} else if *called == ctx.callables.builtins.reduce_or {
let a_width = ctx.types.get_width(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",
&rtlil::SigSpec::Wire(format!("$sig_{}", args[0].id.0)),
);
cell.add_connection("\\Y", &rtlil::SigSpec::Wire(expr_wire_name.clone()));
module.add_cell(cell);
} else if *called == ctx.callables.builtins.bitnot {
let a_width = ctx.types.get_width(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",
&rtlil::SigSpec::Wire(format!("$sig_{}", args[0].id.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(
todo!(),
ctx.types.get_width(expr.typ).expect("signal has no size"),
)),
}
}
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 = module.make_genid("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(99)),
));
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())
}