From 839287ec5113495f21672ecf51525d3aa5c0dc68 Mon Sep 17 00:00:00 2001 From: NotAFile Date: Mon, 17 Jan 2022 15:37:07 +0100 Subject: [PATCH] switch more to sigspec, make processes work --- doc/examples/clockdiv.fut | 4 +- src/builtin_cells.rs | 29 +++++++++++-- src/frontend.rs | 90 ++++++++++++++++++++++++++++++++------- src/parser/proc.rs | 4 +- src/rtlil.rs | 12 +++--- src/rtlil/sync.rs | 13 ++++-- 6 files changed, 120 insertions(+), 32 deletions(-) diff --git a/doc/examples/clockdiv.fut b/doc/examples/clockdiv.fut index 236ea8b..e400a75 100644 --- a/doc/examples/clockdiv.fut +++ b/doc/examples/clockdiv.fut @@ -4,9 +4,9 @@ module clockdiv_2 ( output wire out_clk ) { proc (clk) { - match (rst) { + match (not(rst)) { 0 => out_clk = 0, - 1 => out_clk = ~out_clk + 1 => out_clk = not(out_clk) } } } diff --git a/src/builtin_cells.rs b/src/builtin_cells.rs index d8de9ae..ed5e72e 100644 --- a/src/builtin_cells.rs +++ b/src/builtin_cells.rs @@ -1,23 +1,30 @@ use crate::frontend::{CallArgument, Callable, Type}; use crate::rtlil; +use crate::rtlil::SigSpec; + +fn instantiate_unop(celltype: &str, id: &str, args: &[SigSpec], ret: &SigSpec) -> rtlil::Cell { + let a = args + .get(0) + .expect("wrong argcount slipped through type check"); + assert_eq!(args.len(), 1); -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.add_connection("\\Y", ret); cell } -fn instantiate_binop(celltype: &str, id: &str, args: &[String], ret: &str) -> rtlil::Cell { +fn instantiate_binop(celltype: &str, id: &str, args: &[SigSpec], ret: &SigSpec) -> 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"); + assert_eq!(args.len(), 2); let mut cell = rtlil::Cell::new(id, celltype); cell.add_param("\\A_SIGNED", "0"); @@ -50,11 +57,27 @@ fn make_binop_callable(name: &str, celltype: &'static str) -> Callable { } } +fn make_unnop_callable(name: &str, celltype: &'static str) -> Callable { + let args = vec![ + CallArgument { + name: "A".to_owned(), + atype: Type::wire(), + }, + ]; + Callable { + name: name.to_owned(), + args, + ret: Type::wire(), + instantiate: Box::new(move |id, args, ret| instantiate_unop(celltype, id, args, ret)), + } +} + pub fn get_builtins() -> Vec { vec![ make_binop_callable("and", "$and"), make_binop_callable("or", "$or"), make_binop_callable("xor", "$xor"), make_binop_callable("xnor", "$xnor"), + make_unnop_callable("not", "$not"), ] } diff --git a/src/frontend.rs b/src/frontend.rs index a02b8ae..b190c26 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -5,6 +5,9 @@ use crate::parser; use crate::rtlil; use crate::rtlil::RtlilWrite; +/// lots of code is still not width-aware, this constant keeps track of that +const TODO_WIDTH: u32 = 1; + fn make_pubid(id: &str) -> String { "\\".to_owned() + id } @@ -52,31 +55,82 @@ pub struct Callable { pub name: String, pub args: Vec, pub ret: Type, - pub instantiate: Box rtlil::Cell>, + pub instantiate: Box rtlil::Cell>, } struct Context { callables: BTreeMap, } +fn lower_process_statement( + ctx: &Context, + module: &mut rtlil::Module, + updates: &mut Vec<(rtlil::SigSpec, rtlil::SigSpec)>, + stmt: &parser::proc::ProcStatement, +) -> Result { + let rule = match stmt { + parser::proc::ProcStatement::IfElse(_) => todo!("if/else unimplemented"), + parser::proc::ProcStatement::Assign(assig) => { + // FIXME: actually store this + let next_gen_id = format!("${}$next", assig.lhs); + module.add_wire(rtlil::Wire::new(&next_gen_id, TODO_WIDTH, None)); + + let next_wire = rtlil::SigSpec::Wire(next_gen_id.clone()); + updates.push((rtlil::SigSpec::Wire(assig.lhs.to_owned()), next_wire.clone())); + + let next_expr_wire = lower_expression(ctx, module, &assig.expr)?; + + rtlil::CaseRule { + assign: vec![(next_wire, next_expr_wire)], + switches: vec![] + } + }, + parser::proc::ProcStatement::Match(match_block) => { + let match_sig = lower_expression(ctx, module, &match_block.expr)?; + let mut cases = vec![]; + for arm in &match_block.arms { + let case = lower_process_statement(ctx, module, updates, &arm.1)?; + let compare_sig = lower_expression(ctx, module, &arm.0)?; + cases.push((compare_sig, case)); + }; + let switch_rule = rtlil::SwitchRule { + signal: match_sig, + cases, + }; + rtlil::CaseRule { + assign: vec![], + switches: vec![switch_rule], + } + }, + parser::proc::ProcStatement::Block(_) => todo!("blocks unimplemented"), + }; + Ok(rule) +} fn lower_process( ctx: &Context, module: &mut rtlil::Module, process: &parser::proc::ProcBlock ) -> Result<(), CompileError> { + let mut updates = vec![]; + let mut cases = vec![]; + for stmt in &process.items { + let case = lower_process_statement(ctx, module, &mut updates, &stmt)?; + cases.push(case); + } + let sync_cond = rtlil::SyncCond::Posedge((*process.net.fragment()).into()); let sync_rule = rtlil::SyncRule { cond: sync_cond, - assign: Vec::new() - }; - let root_case = rtlil::CaseRule { - assign: vec![], - switches: vec![], + assign: updates }; + if cases.len() != 1 { + panic!("only one expression per block, for now") + } + assert_eq!(cases.len(), 1); let ir_proc = rtlil::Process { id: module.make_genid("proc"), - root_case, + root_case: cases.into_iter().next().unwrap(), sync_rules: vec![sync_rule], }; module.add_process(ir_proc); @@ -87,12 +141,10 @@ fn lower_expression( ctx: &Context, module: &mut rtlil::Module, expr: &parser::Expression, -) -> Result { +) -> Result { match expr { - parser::Expression::Ident(ident) => Ok(make_pubid(ident)), + parser::Expression::Ident(ident) => Ok(rtlil::SigSpec::Wire(make_pubid(ident))), parser::Expression::Call(call) => { - let output_gen_id = module.make_genid("cell"); - module.add_wire(rtlil::Wire::new(&output_gen_id, 1, None)); let args_resolved = call .args @@ -118,12 +170,18 @@ fn lower_expression( let cell_id = module.make_genid(&callable.name); - let cell = (*callable.instantiate)(&cell_id, args_resolved.as_slice(), &output_gen_id); + let output_gen_id = format!("{}$out", &cell_id); + module.add_wire(rtlil::Wire::new(&output_gen_id, TODO_WIDTH, None)); + let output_gen_wire = rtlil::SigSpec::Wire(output_gen_id); + + let cell = (*callable.instantiate)(&cell_id, args_resolved.as_slice(), &output_gen_wire); module.add_cell(cell); - Ok(output_gen_id) + Ok(output_gen_wire) } - parser::Expression::Operation(_op) => todo!(), - parser::Expression::Literal(_op) => todo!(), + parser::Expression::Operation(_op) => todo!("operators not yet implemented"), + parser::Expression::Literal(lit) => { + Ok(rtlil::SigSpec::Const(*lit as i64, TODO_WIDTH)) + }, } } @@ -132,7 +190,7 @@ fn lower_assignment( module: &mut rtlil::Module, assignment: parser::Assign, ) -> Result<(), CompileError> { - let target_id = make_pubid(assignment.lhs); + let target_id = rtlil::SigSpec::Wire(make_pubid(assignment.lhs)); let return_wire = lower_expression(ctx, module, &assignment.expr)?; module.add_connection(&target_id, &return_wire); Ok(()) diff --git a/src/parser/proc.rs b/src/parser/proc.rs index 8ec34e4..eae81ab 100644 --- a/src/parser/proc.rs +++ b/src/parser/proc.rs @@ -30,8 +30,8 @@ pub struct IfElseBlock {} #[derive(Debug)] pub struct MatchBlock<'a> { - expr: Expression<'a>, - arms: Vec<(Expression<'a>, ProcStatement<'a>)>, + pub expr: Expression<'a>, + pub arms: Vec<(Expression<'a>, ProcStatement<'a>)>, } fn match_arm(input: Span) -> IResult { diff --git a/src/rtlil.rs b/src/rtlil.rs index 4121b84..e4badaa 100644 --- a/src/rtlil.rs +++ b/src/rtlil.rs @@ -48,9 +48,9 @@ pub trait RtlilWrite { fn write_rtlil(&self, writer: &mut ILWriter); } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum SigSpec { - Const(i32, u32), + Const(i64, u32), Wire(String), } @@ -143,9 +143,9 @@ impl Module { self.wires.push(wire) } - pub fn add_connection(&mut self, target: &str, source: &str) { + pub fn add_connection(&mut self, target: &SigSpec, source: &SigSpec) { self.connections - .push((SigSpec::wire(target), SigSpec::wire(source))) + .push((target.clone(), source.clone())) } pub fn add_cell(&mut self, cell: Cell) { @@ -200,9 +200,9 @@ impl Cell { self.parameters.push((name.into(), value.into())) } - pub fn add_connection(&mut self, from: &str, to: &str) { + pub fn add_connection(&mut self, from: &str, to: &SigSpec) { self.connections - .push((SigSpec::wire(from), SigSpec::wire(to))) + .push((SigSpec::wire(from), to.clone())) } } diff --git a/src/rtlil/sync.rs b/src/rtlil/sync.rs index d380b06..2b71139 100644 --- a/src/rtlil/sync.rs +++ b/src/rtlil/sync.rs @@ -15,8 +15,8 @@ pub struct CaseRule { #[derive(Debug)] pub struct SwitchRule { - signal: String, - cases: Vec, + pub signal: SigSpec, + pub cases: Vec<(SigSpec, CaseRule)>, } #[derive(Debug)] @@ -28,6 +28,7 @@ pub struct SyncRule { #[derive(Debug)] pub enum SyncCond { Always, + Init, Posedge(String), Negedge(String), } @@ -56,7 +57,12 @@ impl RtlilWrite for SwitchRule { fn write_rtlil(&self, writer: &mut super::ILWriter) { writer.write_line(&format!("switch {}", self.signal)); writer.indent(); - writer.write_iter(&self.cases); + for case in &self.cases { + writer.write_line(&format!("case {}", case.0)); + writer.indent(); + case.1.write_rtlil(writer); + writer.dedent(); + } writer.dedent(); } } @@ -65,6 +71,7 @@ impl RtlilWrite for SyncRule { fn write_rtlil(&self, writer: &mut super::ILWriter) { let sync_expr = match &self.cond { SyncCond::Always => "always".to_owned(), + SyncCond::Init => "always".to_owned(), SyncCond::Posedge(sig) => format!("posedge {}", sig), SyncCond::Negedge(sig) => format!("negedge {}", sig), };