diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fe7d14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index 9a06edf..ac2943d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,39 +1,16 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "anyhow" version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" -[[package]] -name = "bitvec" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" -dependencies = [ - "either", - "radium", -] - -[[package]] -name = "either" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" - -[[package]] -name = "example" -version = "0.1.0" -dependencies = [ - "lvbitfile2rust-macros", - "ni-fpga", - "ni-fpga-macros", -] - [[package]] name = "lvbitfile2rust" -version = "0.1.0" +version = "0.2.0" dependencies = [ "proc-macro2", "quote", @@ -44,7 +21,7 @@ dependencies = [ [[package]] name = "lvbitfile2rust-cli" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "lvbitfile2rust", @@ -52,40 +29,13 @@ dependencies = [ [[package]] name = "lvbitfile2rust-macros" -version = "0.1.0" +version = "0.2.0" dependencies = [ "lvbitfile2rust", "quote", "syn", ] -[[package]] -name = "ni-fpga" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5d78540f253a540cc1455011cb950f5b09ad9e22d16c87d0bdeab5f98f39f8" -dependencies = [ - "bitvec", - "ni-fpga-sys", - "thiserror", -] - -[[package]] -name = "ni-fpga-macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63db566ab7c85356f297c918a92c6a8d873bedee049f19e34a51e791faa3bb6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "ni-fpga-sys" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe22ec861b584675eb2cbbd4c150c4eeb9111527ed3ef64cc75ca69adb7f0051" - [[package]] name = "proc-macro2" version = "1.0.19" @@ -104,12 +54,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - [[package]] name = "roxmltree" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index 1282eae..7a4af10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "example", "lvbitfile2rust", "lvbitfile2rust-cli", "lvbitfile2rust-macros", diff --git a/lvbitfile2rust-cli/Cargo.toml b/lvbitfile2rust-cli/Cargo.toml index bc3a625..a11858f 100644 --- a/lvbitfile2rust-cli/Cargo.toml +++ b/lvbitfile2rust-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lvbitfile2rust-cli" -version = "0.1.1" +version = "0.2.0" authors = ["Connor Worley "] edition = "2018" license-file = "LICENSE.md" @@ -10,4 +10,4 @@ repository = "https://github.com/first-rust-competition/lvbitfile2rust" [dependencies] anyhow = "1.0.32" -lvbitfile2rust = { version = "0.1.0", path = "../lvbitfile2rust" } +lvbitfile2rust = { version = "0.2.0", path = "../lvbitfile2rust" } diff --git a/lvbitfile2rust-cli/src/main.rs b/lvbitfile2rust-cli/src/main.rs index 6b27ff8..5aef0e1 100644 --- a/lvbitfile2rust-cli/src/main.rs +++ b/lvbitfile2rust-cli/src/main.rs @@ -3,6 +3,6 @@ use anyhow::Result; fn main() -> Result<()> { let mut args: Vec = std::env::args().collect(); assert_eq!(args.len(), 2, "usage: lvbitfile2rust-cli "); - println!("{}", lvbitfile2rust::codegen(args.remove(1))?); + println!("{}", lvbitfile2rust::codegen(args.remove(1), false)?); Ok(()) } diff --git a/lvbitfile2rust-macros/Cargo.toml b/lvbitfile2rust-macros/Cargo.toml index d16a83f..e9ac28f 100644 --- a/lvbitfile2rust-macros/Cargo.toml +++ b/lvbitfile2rust-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lvbitfile2rust-macros" -version = "0.1.1" +version = "0.2.0" authors = ["Connor Worley "] edition = "2018" license-file = "LICENSE.md" @@ -12,6 +12,6 @@ repository = "https://github.com/first-rust-competition/lvbitfile2rust" proc-macro = true [dependencies] -lvbitfile2rust = { version = "0.1.0", path = "../lvbitfile2rust" } +lvbitfile2rust = { version = "0.2.0", path = "../lvbitfile2rust" } quote = "1.0.7" syn = "1.0.23" diff --git a/lvbitfile2rust-macros/src/lib.rs b/lvbitfile2rust-macros/src/lib.rs index 9abcd64..516b4b6 100644 --- a/lvbitfile2rust-macros/src/lib.rs +++ b/lvbitfile2rust-macros/src/lib.rs @@ -6,7 +6,7 @@ use quote::quote; #[proc_macro] pub fn lvbitfile2rust(item: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(item as syn::LitStr); - match lvbitfile2rust::codegen(input.value()) { + match lvbitfile2rust::codegen(input.value(), true) { Ok(tokens) => tokens, Err(e) => { let err_string = e.to_string(); diff --git a/lvbitfile2rust/Cargo.toml b/lvbitfile2rust/Cargo.toml index 9898a2e..395bf64 100644 --- a/lvbitfile2rust/Cargo.toml +++ b/lvbitfile2rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lvbitfile2rust" -version = "0.1.1" +version = "0.2.0" authors = ["Connor Worley "] edition = "2018" license-file = "LICENSE.md" @@ -13,4 +13,4 @@ proc-macro2 = "1.0.19" quote = "1.0.7" roxmltree = "0.13.0" syn = { version = "1.0.23", features = ["full"] } -thiserror = "1.0.20" \ No newline at end of file +thiserror = "1.0.20" diff --git a/lvbitfile2rust/src/lib.rs b/lvbitfile2rust/src/lib.rs index e03b562..4ffff63 100644 --- a/lvbitfile2rust/src/lib.rs +++ b/lvbitfile2rust/src/lib.rs @@ -53,19 +53,18 @@ impl ToTokens for HashableTokenStream { } fn sanitize_ident(ident: &str) -> String { - ident.replace(".", "_").replace(" ", "_") + ident.replace(['.', ' '], "_") } fn first_child_by_tag<'a, 'b>( node: &'a roxmltree::Node<'b, 'b>, child_tag: &'a str, ) -> Result, CodegenError> { - Ok(node - .children() + node.children() .find(|child| child.tag_name().name() == child_tag) .ok_or_else(|| { CodegenError::MissingChild(node.tag_name().name().to_owned(), child_tag.to_owned()) - })?) + }) } fn node_text<'a>(node: &'a roxmltree::Node) -> Result<&'a str, CodegenError> { @@ -73,6 +72,20 @@ fn node_text<'a>(node: &'a roxmltree::Node) -> Result<&'a str, CodegenError> { .ok_or_else(|| CodegenError::NoText(node.tag_name().name().to_owned())) } +pub fn find_cluster_name(type_node: &roxmltree::Node) -> Result { + Ok(match node_text(&first_child_by_tag(type_node, "Name")?) { + Ok(name) => name.into(), + Err(_) => { + // Navigate up 2 parents, see if we find an Array + let parent_block = type_node + .parent() + .and_then(|f| f.parent()) + .ok_or_else(|| CodegenError::NoText(type_node.tag_name().name().to_owned()))?; + node_text(&first_child_by_tag(&parent_block, "Name")?)?.into() + } + }) +} + fn type_node_to_rust_typedecl( type_node: &roxmltree::Node, ) -> Result { @@ -96,12 +109,22 @@ fn type_node_to_rust_typedecl( let inner_type_node = first_child_by_tag(type_node, "Type")? .first_element_child() .ok_or_else(|| CodegenError::NoChildren("Type".to_owned()))?; + let inner_typedecl = type_node_to_rust_typedecl(&inner_type_node)?; Ok(quote! { [#inner_typedecl; #size] }) } - "Cluster" | "EnumU8" | "EnumU16" | "EnumU32" | "EnumU64" => { + "Cluster" => { + let ident = syn::Ident::new( + &sanitize_ident(&find_cluster_name(type_node)?), + proc_macro2::Span::call_site(), + ); + Ok(quote! { + types::#ident + }) + } + "EnumU8" | "EnumU16" | "EnumU32" | "EnumU64" => { let ident = syn::Ident::new( &sanitize_ident(node_text(&first_child_by_tag(type_node, "Name")?)?), proc_macro2::Span::call_site(), @@ -123,8 +146,14 @@ fn type_node_to_rust_typedecl( node_text(&first_child_by_tag(type_node, "IntegerWordLength")?)?, proc_macro2::Span::call_site(), ); + let include_overflow = syn::LitBool { + value: node_text(&first_child_by_tag(type_node, "IncludeOverflowStatus")?)? + == "true", + span: proc_macro2::Span::call_site(), + }; + Ok(quote! { - ni_fpga::fxp::FXP<#word_length, #integer_word_length, #signed> + ni_fpga::fxp::FXP<#word_length, #integer_word_length, #signed, #include_overflow> }) } _ => Err(CodegenError::UnknownBitfileType( @@ -133,7 +162,10 @@ fn type_node_to_rust_typedecl( } } -pub fn codegen(bitfile_path: String) -> Result { +pub fn codegen( + bitfile_path: String, + embed_bitfile: bool, +) -> Result { let bitfile_contents = match std::fs::read_to_string(&bitfile_path) { Ok(contents) => contents, Err(e) => return Err(CodegenError::BitfileRead(bitfile_path, e)), @@ -142,18 +174,27 @@ pub fn codegen(bitfile_path: String) -> Result doc, Err(e) => return Err(CodegenError::BitfileParse(bitfile_path, e)), }; + let bitfile_literal = if embed_bitfile { + Some(syn::LitStr::new( + &bitfile_contents, + proc_macro2::Span::call_site(), + )) + } else { + None + }; let mut typedefs = std::collections::HashSet::::new(); let mut register_defs = Vec::::new(); - let mut register_fields = Vec::::new(); + let mut hmb_fields = Vec::::new(); let mut register_inits = Vec::::new(); + let mut hmb_inits = Vec::::new(); let mut vi_signature = String::new(); for node in bitfile.root().descendants() { match node.tag_name().name() { "Cluster" => { let ident = syn::Ident::new( - &sanitize_ident(node_text(&first_child_by_tag(&node, "Name")?)?), + &sanitize_ident(&find_cluster_name(&node)?), proc_macro2::Span::call_site(), ); let mut fields = Vec::::new(); @@ -202,50 +243,84 @@ pub fn codegen(bitfile_path: String) -> Result { - if node_text(&first_child_by_tag(&node, "Hidden")?)? == "false" { + if node_text(&first_child_by_tag(&node, "Hidden")?)? == "false" + && node_text(&first_child_by_tag(&node, "Internal")?)? == "false" + { let ident = syn::Ident::new( &sanitize_ident(node_text(&first_child_by_tag(&node, "Name")?)?), proc_macro2::Span::call_site(), ); - let offset = syn::LitInt::new( - &sanitize_ident(node_text(&first_child_by_tag(&node, "Offset")?)?), + let str_ident = syn::LitStr::new( + node_text(&first_child_by_tag(&node, "Name")?)?, proc_macro2::Span::call_site(), ); let type_node = first_child_by_tag(&node, "Datatype")? .first_element_child() .ok_or_else(|| CodegenError::NoChildren("Datatype".to_owned()))?; let typedecl = type_node_to_rust_typedecl(&type_node)?; - let write_fn = match node_text(&first_child_by_tag(&node, "Indicator")?)? { - "false" => quote! { - pub fn write(&self, value: &#typedecl) -> Result<(), ni_fpga::Error> { - unsafe { SESSION.as_ref() }.unwrap().write(#offset, value) - } - }, - _ => quote! {}, + let readable = true; + let writeable = match node_text(&first_child_by_tag(&node, "Indicator")?)? { + "false" => true, + "true" => false, + _ => todo!(), }; - register_defs.push(quote! { - pub struct #ident { - _marker: PhantomData<*const ()>, - } - impl #ident { - pub fn read(&self) -> Result<#typedecl, ni_fpga::Error> { - unsafe { SESSION.as_ref() }.unwrap().read(#offset) - } - #write_fn + let access_type = if readable { + if writeable { + "ReadWrite" + } else { + "ReadOnly" } - }); - register_fields.push(quote! { - pub #ident: #ident + } else if writeable { + "WriteOnly" + } else { + // Error + todo!(); + }; + + let access = syn::Ident::new(access_type, proc_macro2::Span::call_site()); + + register_defs.push(quote! { + pub #ident: Option> }); register_inits.push(quote! { - #ident: #ident{ _marker: PhantomData } + #ident: Some(unsafe { ni_fpga::Register::new(session.find_offset(#str_ident)?) }) }); } } "SignatureRegister" => { vi_signature = node_text(&node)?.to_owned(); } + "HMB" => { + let blocks = first_child_by_tag(&node, "MemoryBlockList")?; + for block in blocks + .children() + .filter(|child| child.is_element() && child.tag_name().name() == "MemoryBlock") + { + let ident = syn::Ident::new( + &sanitize_ident(node_text(&first_child_by_tag(&block, "Name")?)?), + proc_macro2::Span::call_site(), + ); + let ident_raw = syn::LitStr::new( + node_text(&first_child_by_tag(&block, "Name")?)?, + proc_macro2::Span::call_site(), + ); + let stride = syn::LitInt::new( + &sanitize_ident(node_text(&first_child_by_tag(&block, "Stride")?)?), + proc_macro2::Span::call_site(), + ); + let elements = syn::LitInt::new( + &sanitize_ident(node_text(&first_child_by_tag(&block, "Elements")?)?), + proc_macro2::Span::call_site(), + ); + hmb_fields.push(quote! { + pub #ident: ni_fpga::HmbDefinition + }); + hmb_inits.push(quote! { + #ident: ni_fpga::HmbDefinition { name: #ident_raw, stride: #stride, elements: #elements } + }); + } + } _ => {} } } @@ -254,49 +329,51 @@ pub fn codegen(bitfile_path: String) -> Result, - #(#register_fields),* + pub struct FpgaBitfile { + #(#register_defs),*, + hmb_definitions: FpgaBitfileHmbDefs, } - impl Peripherals { - pub fn take(resource: &str) -> Result> { - if unsafe{ !SESSION.is_null() } { - Err("Peripherals already in use")? - } else { - let session = ni_fpga::Session::open( - #bitfile_path, - #vi_signature, - resource, - )?; - unsafe { SESSION = &session as *const ni_fpga::Session; } - std::mem::forget(session); - Ok( - Self{ - _marker: PhantomData, - #(#register_inits),* - }, - ) + impl FpgaBitfile { + pub fn take(session: &impl ni_fpga::SessionAccess) -> Result { + static REGISTERS: std::sync::Mutex> = std::sync::Mutex::new(Some(())); + let mut lock = REGISTERS.lock().unwrap(); + let contents = lock.take(); + drop(lock); + + if contents.is_none() { + return Err(ni_fpga::Error::ResourceAlreadyTaken); } + + Ok(Self { + #(#register_inits),*, + + hmb_definitions: FpgaBitfileHmbDefs { + #(#hmb_inits),* + } + }) + } + + pub const fn contents() -> &'static str { + #bitfile_literal + } + + pub const fn signature() -> &'static str { + #vi_signature } - } - impl Drop for Peripherals { - fn drop(&mut self) { - std::mem::drop(unsafe { SESSION.as_ref() }.unwrap()) + pub fn session_builder(resource: impl AsRef) -> Result { + ni_fpga::SessionBuilder::new().bitfile_contents(Self::contents())?.signature(Self::signature())?.resource(resource) } } })