move builting functions to own file

This commit is contained in:
NotAFile 2022-01-05 02:08:25 +01:00
parent 33149eb5aa
commit 6abbd30792
2 changed files with 92 additions and 58 deletions

54
src/builtin_cells.rs Normal file
View File

@ -0,0 +1,54 @@
use crate::frontend::{Callable, CallArgument, Type};
use crate::rtlil;
fn builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> rtlil::Cell {
let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0");
cell.add_param("\\A_WIDTH", "1");
cell.add_param("\\Y_WIDTH", "1");
cell.add_connection("\\A", a);
cell.add_connection("\\Y", y);
cell
}
fn instantiate_binop(celltype: &str, id: &str, args: &[String], ret: &str) -> rtlil::Cell {
let a = args.get(0).expect("wrong argcount slipped through type check");
let b = args.get(1).expect("wrong argcount slipped through type check");
let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0");
cell.add_param("\\A_WIDTH", "1");
cell.add_param("\\B_SIGNED", "0");
cell.add_param("\\B_WIDTH", "1");
cell.add_param("\\Y_WIDTH", "1");
cell.add_connection("\\A", a);
cell.add_connection("\\B", b);
cell.add_connection("\\Y", ret);
cell
}
fn make_binop_callable(name: &str, celltype: &'static str) -> Callable {
let args = vec![
CallArgument {
name: "A".to_owned(),
atype: Type::wire(),
},
CallArgument {
name: "B".to_owned(),
atype: Type::wire(),
},
];
Callable {
name: name.to_owned(),
args,
ret: Type::wire(),
instantiate: Box::new(move |id, args, ret| instantiate_binop(celltype, id, args, ret))
}
}
pub fn get_builtins() -> Vec<Callable> {
vec![
make_binop_callable("and", "$and"),
make_binop_callable("xor", "$xor"),
]
}

View File

@ -1,30 +1,8 @@
use std::collections::BTreeMap;
use crate::parser; use crate::parser;
use crate::rtlil; use crate::rtlil;
use crate::builtin_cells::get_builtins;
fn builtin_binop_cell(celltype: &str, id: &str, a: &str, b: &str, y: &str) -> rtlil::Cell {
let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0");
cell.add_param("\\A_WIDTH", "1");
cell.add_param("\\B_SIGNED", "0");
cell.add_param("\\B_WIDTH", "1");
cell.add_param("\\Y_WIDTH", "1");
cell.add_connection("\\A", a);
cell.add_connection("\\B", b);
cell.add_connection("\\Y", y);
cell
}
fn builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> rtlil::Cell {
let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0");
cell.add_param("\\A_WIDTH", "1");
cell.add_param("\\Y_WIDTH", "1");
cell.add_connection("\\A", a);
cell.add_connection("\\Y", y);
cell
}
// the hacky way
fn make_pubid(id: &str) -> String { fn make_pubid(id: &str) -> String {
"\\".to_owned() + id "\\".to_owned() + id
@ -58,45 +36,46 @@ pub enum Type {
Wire(GenericParam<u32>) Wire(GenericParam<u32>)
} }
// module that can be instantiated like a function impl Type {
pub struct Callable { pub fn wire() -> Self {
Self::Wire(GenericParam::Unsolved)
}
} }
fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Result<String, CompileError> { 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<CallArgument>,
pub ret: Type,
pub instantiate: Box<dyn Fn(&str, &[String], &str) -> rtlil::Cell>
}
struct Context {
callables: BTreeMap<String, Callable>
}
fn lower_expression(ctx: &Context, module: &mut rtlil::Module, expr: &parser::Expression) -> Result<String, CompileError> {
match expr { match expr {
parser::Expression::Ident(ident) => Ok(make_pubid(&ident)), parser::Expression::Ident(ident) => Ok(make_pubid(&ident)),
parser::Expression::Call(call) => { parser::Expression::Call(call) => {
let output_gen_id = module.make_genid("cell"); let output_gen_id = module.make_genid("cell");
module.add_wire(rtlil::Wire::new(&output_gen_id, 1, None)); module.add_wire(rtlil::Wire::new(&output_gen_id, 1, None));
let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr)); let args_resolved = call.args.iter()
.map(|expr| lower_expression(ctx, module, expr)).collect::<Result<Vec<_>, _>>()?;
// TODO: make this sensible let callable = ctx.callables.get(call.name.fragment() as &str).ok_or_else(|| {
let cell = match *call.name.fragment() { CompileError::new(CompileErrorKind::UndefinedReference(call.name.fragment().to_string()))
"and" => { })?;
let arg_a = args_resolved.next().unwrap()?;
let arg_b = args_resolved.next().unwrap()?; let cell_id = module.make_genid(&callable.name);
let cell_id = module.make_genid("and");
builtin_binop_cell("$and", &cell_id, &arg_a, &arg_b, &output_gen_id) let cell = (*callable.instantiate)(&cell_id, args_resolved.as_slice(), &output_gen_id);
}
"xor" => {
let arg_a = args_resolved.next().unwrap()?;
let arg_b = args_resolved.next().unwrap()?;
let cell_id = module.make_genid("xor");
builtin_binop_cell("$xor", &cell_id, &arg_a, &arg_b, &output_gen_id)
}
"not" => {
let arg_a = args_resolved.next().unwrap()?;
let cell_id = module.make_genid("not");
builtin_unop_cell("$not", &cell_id, &arg_a, &output_gen_id)
}
"reduce_or" => {
let arg_a = args_resolved.next().unwrap()?;
let cell_id = module.make_genid("reduce_or");
builtin_unop_cell("$reduce_or", &cell_id, &arg_a, &output_gen_id)
}
name => return Err(CompileError::new(CompileErrorKind::UndefinedReference(name.to_owned()))),
};
module.add_cell(cell); module.add_cell(cell);
Ok(output_gen_id) Ok(output_gen_id)
} }
@ -104,9 +83,9 @@ fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Re
} }
} }
fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> { fn lower_assignment(ctx: &Context, module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> {
let target_id = make_pubid(&assignment.lhs); let target_id = make_pubid(&assignment.lhs);
let return_wire = lower_expression(module, &assignment.expr)?; let return_wire = lower_expression(ctx, module, &assignment.expr)?;
module.add_connection(target_id, return_wire); module.add_connection(target_id, return_wire);
Ok(()) Ok(())
} }
@ -114,6 +93,7 @@ fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> R
pub fn lower_module(pa_module: parser::Module) -> Result<String, CompileError> { pub fn lower_module(pa_module: parser::Module) -> Result<String, CompileError> {
let mut writer = rtlil::ILWriter::new(); let mut writer = rtlil::ILWriter::new();
let mut ir_module = rtlil::Module::new(make_pubid(&pa_module.name)); 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"); writer.write_line("autoidx 1");
for (idx, port) in pa_module.ports.iter().enumerate() { for (idx, port) in pa_module.ports.iter().enumerate() {
let dir_option = match port.direction { let dir_option = match port.direction {
@ -129,7 +109,7 @@ pub fn lower_module(pa_module: parser::Module) -> Result<String, CompileError> {
} }
for stmt in pa_module.statements { for stmt in pa_module.statements {
match stmt { match stmt {
parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment)?, parser::Statement::Assign(assignment) => lower_assignment(&context, &mut ir_module, assignment)?,
} }
} }
ir_module.write_rtlil(&mut writer); ir_module.write_rtlil(&mut writer);