162 lines
6.5 KiB
Rust
162 lines
6.5 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 wire_for_expr(expr: typed_ir::ExprId) -> rtlil::SigSpec {
|
|
rtlil::SigSpec::Wire(format!("$_expr_{}", expr.0))
|
|
}
|
|
|
|
fn lower_expression(
|
|
ctx: &Context,
|
|
module: &mut rtlil::Module,
|
|
body: &typed_ir::Body,
|
|
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!("$_expr_{}", 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| wire_for_expr(*expr))
|
|
.collect::<Vec<_>>();
|
|
|
|
let args: Vec<_> = call
|
|
.args
|
|
.iter()
|
|
.map(|expr_id| body.exprs.get(expr_id).unwrap())
|
|
.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(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", &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(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(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((
|
|
wire_for_expr(*pat),
|
|
rtlil::CaseRule {
|
|
assign: vec![(
|
|
rtlil::SigSpec::Wire(expr_wire_name.clone()),
|
|
wire_for_expr(*val),
|
|
)],
|
|
switches: vec![],
|
|
},
|
|
))
|
|
})
|
|
.collect::<Result<Vec<_>, CompileError>>()
|
|
.unwrap();
|
|
let root_switch = rtlil::SwitchRule {
|
|
signal: wire_for_expr(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.exprs.get(&block.expr).unwrap().typ)
|
|
.expect("signal has no size"),
|
|
Some(rtlil::PortOption::Output(block.signals.len() as i32)),
|
|
));
|
|
for expr in block.exprs.values() {
|
|
let expr_wire = lower_expression(ctx, module, block, expr)?;
|
|
module.add_connection(&wire_for_expr(expr.id), &expr_wire);
|
|
}
|
|
let out_sig = wire_for_expr(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<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())
|
|
}
|