From 68306885d729b2f478994a6c51b0742cefa1f994 Mon Sep 17 00:00:00 2001 From: notafile Date: Tue, 4 Jan 2022 01:38:35 +0100 Subject: [PATCH] refactor rtlir backend --- Cargo.lock | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + identity.fut | 10 ++- src/main.rs | 1 + src/parser.rs | 33 +++++--- src/rtlil.rs | 162 ++++++++++++++++++++++++++++++++++---- 6 files changed, 386 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fed877d..def017e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,85 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "futilehdl" version = "0.1.0" dependencies = [ "nom", + "structopt", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + [[package]] name = "memchr" version = "2.4.1" @@ -32,8 +104,146 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 35a1bc0..1741f65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] nom = "7.1.0" +structopt = "0.3.25" diff --git a/identity.fut b/identity.fut index 8f35fd2..37ef9f1 100644 --- a/identity.fut +++ b/identity.fut @@ -1,4 +1,10 @@ -module top ( input wire x, output wire y ) +module halfadd ( + input wire a, + input wire b, + output wire sum, + output wire carry +) { - assign x = y; + assign sum = xor(a, b); + assign carry = and(a, b); } diff --git a/src/main.rs b/src/main.rs index 1aa9a6f..de13e38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod parser; mod rtlil; use nom::error::{convert_error, VerboseError}; +use structopt::StructOpt; // custom IResult type for verboseerror pub type IResult> = nom::IResult; diff --git a/src/parser.rs b/src/parser.rs index b735613..5322469 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,8 +3,7 @@ use nom::{ bytes::complete::tag, character::complete::{alpha1, alphanumeric1, char, multispace0, multispace1}, combinator::{map, opt, recognize}, - error::ParseError, - error::{context, convert_error, VerboseError}, + error::{context, ParseError}, multi::{many0, many1, separated_list0}, sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}, }; @@ -58,7 +57,7 @@ pub struct PortDecl { pub struct Module { pub name: String, pub ports: Vec, - pub statements: Vec, + pub statements: Vec, } #[derive(Debug)] @@ -80,8 +79,8 @@ pub enum Operation { #[derive(Debug)] pub struct Call { - name: String, - params: Vec, + pub name: String, + pub args: Vec, } #[derive(Debug)] @@ -142,10 +141,17 @@ fn call_item(input: &str) -> IResult<&str, Call> { map( tuple(( ws0(identifier), - delimited(char('('), ws0(separated_list0(char(','), expression)), char(')')) + delimited( + char('('), + ws0(separated_list0(char(','), expression)), + char(')'), + ), )), - |(name, params)| Call { name: name.into(), params } - )(input) + |(name, args)| Call { + name: name.into(), + args, + }, + )(input) } fn expression(input: &str) -> IResult<&str, Expression> { @@ -156,16 +162,18 @@ fn expression(input: &str) -> IResult<&str, Expression> { ))(input) } -fn assign_statement(input: &str) -> IResult<&str, Assign> { +fn assign_statement(input: &str) -> IResult<&str, Statement> { context( "assignment", delimited( ws0(terminated(tag("assign"), multispace1)), map( separated_pair(ws0(identifier), char('='), ws0(expression)), - |(lhs, expr)| Assign { - lhs: lhs.into(), - expr: expr.into(), + |(lhs, expr)| { + Statement::Assign(Assign { + lhs: lhs.into(), + expr: expr.into(), + }) }, ), ws0(char(';')), @@ -228,5 +236,6 @@ mod test { fn test_call() { call_item("thing ( )").unwrap(); call_item("thing ( a , b , c )").unwrap(); + call_item("thing(a,b,c)").unwrap(); } } diff --git a/src/rtlil.rs b/src/rtlil.rs index 6a46a22..9674e65 100644 --- a/src/rtlil.rs +++ b/src/rtlil.rs @@ -37,18 +37,91 @@ impl ILWriter { } // the proper way +#[derive(Debug)] pub enum WireOption { Input(i32), Output(i32), } +#[derive(Debug)] pub struct Wire { id: String, - options: Vec<()>, + options: Vec, } -struct Module {} +impl Wire { + fn new(id: impl Into) -> Self { + Wire { + id: id.into(), + options: Default::default(), + } + } + fn write_rtlil(&self, writer: &mut ILWriter) { + let mut line = String::from("wire "); + + for option in &self.options { + let option_str = match option { + WireOption::Input(num) => format!("input {} ", num), + WireOption::Output(num) => format!("output {} ", num), + }; + + line += &option_str; + } + + line += &self.id; + writer.write_line(&line); + } +} + +#[derive(Debug)] +struct Module { + name: String, + wires: Vec, + cells: Vec, + connections: Vec<(String, String)>, + gen_id: i32, +} + +impl Module { + fn new(name: impl Into) -> Self { + Module { + name: name.into(), + wires: Default::default(), + cells: Default::default(), + connections: Default::default(), + gen_id: 0, + } + } + + fn add_wire(&mut self, wire: Wire) { + self.wires.push(wire) + } + + fn write_rtlil(&self, writer: &mut ILWriter) { + writer.write_line(&format!("module {}", self.name)); + writer.indent(); + for wire in &self.wires { + wire.write_rtlil(writer); + } + for cell in &self.cells { + cell.write_rtlil(writer); + } + for conn in &self.connections { + writer.write_line(&format!("connect {} {}", conn.0, conn.1)) + } + writer.dedent(); + writer.write_line("end"); + } + + fn make_genid(&mut self, stem: &str) -> String { + let res = format!("${}${}", stem, self.gen_id); + self.gen_id += 1; + res + } +} + +#[derive(Debug)] struct Cell { id: String, celltype: String, @@ -88,6 +161,19 @@ impl Cell { } } +fn xor_cell(id: &str, a: &str, b: &str, y: &str) -> Cell { + let mut cell = Cell::new(id, "$xor"); + 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 adder_cell(id: &str, a: &str, b: &str, y: &str) -> Cell { let mut cell = Cell::new(id, "$and"); cell.add_param("\\A_SIGNED", "0"); @@ -103,24 +189,66 @@ fn adder_cell(id: &str, a: &str, b: &str, y: &str) -> Cell { // the hacky way -pub fn lower_module(module: parser::Module) -> String { +fn make_pubid(id: &str) -> String { + "\\".to_owned() + id +} + +fn lower_expression(module: &mut Module, expr: &parser::Expression) -> String { + match expr { + parser::Expression::Ident(ident) => make_pubid(&ident), + parser::Expression::Call(call) => { + let output_gen_id = module.make_genid("cell"); + module.add_wire(Wire::new(&output_gen_id)); + + let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr)); + + let arg_a = args_resolved.next().unwrap(); + let arg_b = args_resolved.next().unwrap(); + + let cell = match call.name.as_str() { + "and" => { + let cell_id = module.make_genid("and"); + adder_cell(&cell_id, &arg_a, &arg_b, &output_gen_id) + }, + "xor" => { + let cell_id = module.make_genid("xor"); + xor_cell(&cell_id, &arg_a, &arg_b, &output_gen_id) + }, + _ => todo!(), + }; + module.cells.push(cell); + output_gen_id + } + parser::Expression::Operation(op) => todo!(), + } +} + +fn lower_assignment(module: &mut Module, assignment: parser::Assign) { + let target_id = make_pubid(&assignment.lhs); + let return_wire = lower_expression(module, &assignment.expr); + module.connections.push((target_id, return_wire)) +} + +pub fn lower_module(pa_module: parser::Module) -> String { let mut writer = ILWriter::new(); + let mut ir_module = Module::new(format!("\\{}", pa_module.name)); writer.write_line("autoidx 1"); - writer.write_line(&format!("module \\{}", module.name)); - writer.indent(); - for (num, port) in module.ports.iter().enumerate() { - writer.write_line(&format!( - "wire {} {} \\{}", - "output", - (num + 1), - port.net.name - )) + for (idx, port) in pa_module.ports.iter().enumerate() { + let dir_option = match port.direction { + parser::PortDirection::Input => WireOption::Input(idx as i32 + 1), + parser::PortDirection::Output => WireOption::Output(idx as i32 + 1), + }; + let wire = Wire { + id: make_pubid(&port.net.name), + options: vec![dir_option], + }; + ir_module.wires.push(wire); } - for stmt in module.statements { - // writer.write_line(&format!("connect \\{} \\{}", stmt.lhs, stmt.expr)); + for stmt in pa_module.statements { + match stmt { + parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment), + } } - adder_cell("\\my_and", "\\a", "\\b", "\\y").write_rtlil(&mut writer); - writer.dedent(); - writer.write_line("end"); + ir_module.write_rtlil(&mut writer); writer.finish() }