switch more to sigspec, make processes work

main
NotAFile 2022-01-17 15:37:07 +01:00
parent b77fc1a1f9
commit 839287ec51
6 changed files with 120 additions and 32 deletions

View File

@ -4,9 +4,9 @@ module clockdiv_2 (
output wire out_clk output wire out_clk
) { ) {
proc (clk) { proc (clk) {
match (rst) { match (not(rst)) {
0 => out_clk = 0, 0 => out_clk = 0,
1 => out_clk = ~out_clk 1 => out_clk = not(out_clk)
} }
} }
} }

View File

@ -1,23 +1,30 @@
use crate::frontend::{CallArgument, Callable, Type}; use crate::frontend::{CallArgument, Callable, Type};
use crate::rtlil; 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); let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0"); cell.add_param("\\A_SIGNED", "0");
cell.add_param("\\A_WIDTH", "1"); cell.add_param("\\A_WIDTH", "1");
cell.add_param("\\Y_WIDTH", "1"); cell.add_param("\\Y_WIDTH", "1");
cell.add_connection("\\A", a); cell.add_connection("\\A", a);
cell.add_connection("\\Y", y); cell.add_connection("\\Y", ret);
cell 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 let a = args
.get(0) .get(0)
.expect("wrong argcount slipped through type check"); .expect("wrong argcount slipped through type check");
let b = args let b = args
.get(1) .get(1)
.expect("wrong argcount slipped through type check"); .expect("wrong argcount slipped through type check");
assert_eq!(args.len(), 2);
let mut cell = rtlil::Cell::new(id, celltype); let mut cell = rtlil::Cell::new(id, celltype);
cell.add_param("\\A_SIGNED", "0"); 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<Callable> { pub fn get_builtins() -> Vec<Callable> {
vec![ vec![
make_binop_callable("and", "$and"), make_binop_callable("and", "$and"),
make_binop_callable("or", "$or"), make_binop_callable("or", "$or"),
make_binop_callable("xor", "$xor"), make_binop_callable("xor", "$xor"),
make_binop_callable("xnor", "$xnor"), make_binop_callable("xnor", "$xnor"),
make_unnop_callable("not", "$not"),
] ]
} }

View File

@ -5,6 +5,9 @@ use crate::parser;
use crate::rtlil; use crate::rtlil;
use crate::rtlil::RtlilWrite; 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 { fn make_pubid(id: &str) -> String {
"\\".to_owned() + id "\\".to_owned() + id
} }
@ -52,31 +55,82 @@ pub struct Callable {
pub name: String, pub name: String,
pub args: Vec<CallArgument>, pub args: Vec<CallArgument>,
pub ret: Type, pub ret: Type,
pub instantiate: Box<dyn Fn(&str, &[String], &str) -> rtlil::Cell>, pub instantiate: Box<dyn Fn(&str, &[rtlil::SigSpec], &rtlil::SigSpec) -> rtlil::Cell>,
} }
struct Context { struct Context {
callables: BTreeMap<String, Callable>, callables: BTreeMap<String, Callable>,
} }
fn lower_process_statement(
ctx: &Context,
module: &mut rtlil::Module,
updates: &mut Vec<(rtlil::SigSpec, rtlil::SigSpec)>,
stmt: &parser::proc::ProcStatement,
) -> Result<rtlil::CaseRule, CompileError> {
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( fn lower_process(
ctx: &Context, ctx: &Context,
module: &mut rtlil::Module, module: &mut rtlil::Module,
process: &parser::proc::ProcBlock process: &parser::proc::ProcBlock
) -> Result<(), CompileError> { ) -> 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_cond = rtlil::SyncCond::Posedge((*process.net.fragment()).into());
let sync_rule = rtlil::SyncRule { let sync_rule = rtlil::SyncRule {
cond: sync_cond, cond: sync_cond,
assign: Vec::new() assign: updates
};
let root_case = rtlil::CaseRule {
assign: vec![],
switches: vec![],
}; };
if cases.len() != 1 {
panic!("only one expression per block, for now")
}
assert_eq!(cases.len(), 1);
let ir_proc = rtlil::Process { let ir_proc = rtlil::Process {
id: module.make_genid("proc"), id: module.make_genid("proc"),
root_case, root_case: cases.into_iter().next().unwrap(),
sync_rules: vec![sync_rule], sync_rules: vec![sync_rule],
}; };
module.add_process(ir_proc); module.add_process(ir_proc);
@ -87,12 +141,10 @@ fn lower_expression(
ctx: &Context, ctx: &Context,
module: &mut rtlil::Module, module: &mut rtlil::Module,
expr: &parser::Expression, expr: &parser::Expression,
) -> Result<String, CompileError> { ) -> Result<rtlil::SigSpec, CompileError> {
match expr { 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) => { 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 let args_resolved = call
.args .args
@ -118,12 +170,18 @@ fn lower_expression(
let cell_id = module.make_genid(&callable.name); 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); module.add_cell(cell);
Ok(output_gen_id) Ok(output_gen_wire)
} }
parser::Expression::Operation(_op) => todo!(), parser::Expression::Operation(_op) => todo!("operators not yet implemented"),
parser::Expression::Literal(_op) => todo!(), parser::Expression::Literal(lit) => {
Ok(rtlil::SigSpec::Const(*lit as i64, TODO_WIDTH))
},
} }
} }
@ -132,7 +190,7 @@ fn lower_assignment(
module: &mut rtlil::Module, module: &mut rtlil::Module,
assignment: parser::Assign, assignment: parser::Assign,
) -> Result<(), CompileError> { ) -> 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)?; 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(())

View File

@ -30,8 +30,8 @@ pub struct IfElseBlock {}
#[derive(Debug)] #[derive(Debug)]
pub struct MatchBlock<'a> { pub struct MatchBlock<'a> {
expr: Expression<'a>, pub expr: Expression<'a>,
arms: Vec<(Expression<'a>, ProcStatement<'a>)>, pub arms: Vec<(Expression<'a>, ProcStatement<'a>)>,
} }
fn match_arm(input: Span) -> IResult<Span, (Expression, ProcStatement)> { fn match_arm(input: Span) -> IResult<Span, (Expression, ProcStatement)> {

View File

@ -48,9 +48,9 @@ pub trait RtlilWrite {
fn write_rtlil(&self, writer: &mut ILWriter); fn write_rtlil(&self, writer: &mut ILWriter);
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum SigSpec { pub enum SigSpec {
Const(i32, u32), Const(i64, u32),
Wire(String), Wire(String),
} }
@ -143,9 +143,9 @@ impl Module {
self.wires.push(wire) 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 self.connections
.push((SigSpec::wire(target), SigSpec::wire(source))) .push((target.clone(), source.clone()))
} }
pub fn add_cell(&mut self, cell: Cell) { pub fn add_cell(&mut self, cell: Cell) {
@ -200,9 +200,9 @@ impl Cell {
self.parameters.push((name.into(), value.into())) 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 self.connections
.push((SigSpec::wire(from), SigSpec::wire(to))) .push((SigSpec::wire(from), to.clone()))
} }
} }

View File

@ -15,8 +15,8 @@ pub struct CaseRule {
#[derive(Debug)] #[derive(Debug)]
pub struct SwitchRule { pub struct SwitchRule {
signal: String, pub signal: SigSpec,
cases: Vec<CaseRule>, pub cases: Vec<(SigSpec, CaseRule)>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -28,6 +28,7 @@ pub struct SyncRule {
#[derive(Debug)] #[derive(Debug)]
pub enum SyncCond { pub enum SyncCond {
Always, Always,
Init,
Posedge(String), Posedge(String),
Negedge(String), Negedge(String),
} }
@ -56,7 +57,12 @@ impl RtlilWrite for SwitchRule {
fn write_rtlil(&self, writer: &mut super::ILWriter) { fn write_rtlil(&self, writer: &mut super::ILWriter) {
writer.write_line(&format!("switch {}", self.signal)); writer.write_line(&format!("switch {}", self.signal));
writer.indent(); 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(); writer.dedent();
} }
} }
@ -65,6 +71,7 @@ impl RtlilWrite for SyncRule {
fn write_rtlil(&self, writer: &mut super::ILWriter) { fn write_rtlil(&self, writer: &mut super::ILWriter) {
let sync_expr = match &self.cond { let sync_expr = match &self.cond {
SyncCond::Always => "always".to_owned(), SyncCond::Always => "always".to_owned(),
SyncCond::Init => "always".to_owned(),
SyncCond::Posedge(sig) => format!("posedge {}", sig), SyncCond::Posedge(sig) => format!("posedge {}", sig),
SyncCond::Negedge(sig) => format!("negedge {}", sig), SyncCond::Negedge(sig) => format!("negedge {}", sig),
}; };