switch more to sigspec, make processes work

This commit is contained in:
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
) {
proc (clk) {
match (rst) {
match (not(rst)) {
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::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<Callable> {
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"),
]
}

View File

@ -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<CallArgument>,
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 {
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(
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<String, CompileError> {
) -> Result<rtlil::SigSpec, CompileError> {
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(())

View File

@ -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<Span, (Expression, ProcStatement)> {

View File

@ -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()))
}
}

View File

@ -15,8 +15,8 @@ pub struct CaseRule {
#[derive(Debug)]
pub struct SwitchRule {
signal: String,
cases: Vec<CaseRule>,
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),
};