diff --git a/examples/nic.rsf b/examples/nic.rsf index 9d1f827..66ddeb6 100644 --- a/examples/nic.rsf +++ b/examples/nic.rsf @@ -13,6 +13,8 @@ block Main { } /// Phy registers. +#[attr(foo = "this is an attribute")] +#[attr(bar = "embedded quota\" attr")] block Phy { /// Configuration register. config: PhyConfig @ 0x200, diff --git a/lib/src/ast.rs b/lib/src/ast.rs index 74d3057..8096fe4 100644 --- a/lib/src/ast.rs +++ b/lib/src/ast.rs @@ -77,6 +77,9 @@ impl Emit for Register { for x in &self.doc { writeln!(f, "///{x}")?; } + for x in &self.attrs { + writeln!(f, "{x}")?; + } writeln!( f, "{}register<{}> {}{} {{", @@ -104,6 +107,9 @@ impl Emit for Field { for x in &self.doc { writeln!(f, " ///{x}")?; } + for x in &self.attrs { + writeln!(f, " {x}")?; + } write!( f, " {}: {} {}", @@ -142,6 +148,9 @@ impl Emit for Block { for x in &self.doc { writeln!(f, "///{x}")?; } + for x in &self.attrs { + writeln!(f, "{x}")?; + } writeln!( f, "{}block {} {{", @@ -164,6 +173,9 @@ impl Emit for BlockElement { for x in &self.doc { writeln!(f, " ///{x}")?; } + for x in &self.attrs { + writeln!(f, " {x}")?; + } self.component.emit(f)?; write!(f, " @ {},", self.offset.to_code())?; writeln!(f) diff --git a/lib/src/common.rs b/lib/src/common.rs index 24d4d1b..3deb15d 100644 --- a/lib/src/common.rs +++ b/lib/src/common.rs @@ -3,12 +3,30 @@ use std::{fmt::Display, ops::Range}; use crate::ast::Emit; +#[derive(Debug, Clone, PartialEq)] +pub struct Attribute { + pub id: Identifier, + pub value: String, +} + +impl Display for Attribute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "#[attr({name} = \"{value}\")]", + name = self.id.name, + value = self.value, + ) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct Enum { pub doc: Vec, pub id: Identifier, pub width: Number, pub alternatives: Vec, + pub attrs: Vec, } impl Emit for Enum { @@ -16,7 +34,15 @@ impl Emit for Enum { for x in &self.doc { writeln!(f, "///{x}")?; } - writeln!(f, "enum<{}> {} {{", self.width.to_code(), self.id.name)?; + for x in &self.attrs { + writeln!(f, "{x}")?; + } + writeln!( + f, + "enum<{width}> {name} {{", + width = self.width.to_code(), + name = self.id.name + )?; for x in &self.alternatives { x.emit(f)?; } @@ -90,6 +116,7 @@ pub struct Field { pub mode: FieldMode, pub typ: T, pub offset: Number, + pub attrs: Vec, } #[derive(Debug, Clone, PartialEq)] @@ -100,6 +127,7 @@ pub struct Register { pub reset_value: Option, pub sram: bool, pub fields: Vec>, + pub attrs: Vec, } #[derive(Debug, Clone, PartialEq)] @@ -108,6 +136,7 @@ pub struct Block { pub id: Identifier, pub sram: bool, pub elements: Vec>, + pub attrs: Vec, } #[derive(Debug, Clone, PartialEq)] @@ -115,6 +144,7 @@ pub struct BlockElement { pub doc: Vec, pub component: Component, pub offset: Number, + pub attrs: Vec, } #[derive(Debug, PartialEq, Clone)] diff --git a/lib/src/model.rs b/lib/src/model.rs index 168c3b2..df31366 100644 --- a/lib/src/model.rs +++ b/lib/src/model.rs @@ -1,3 +1,4 @@ +pub use crate::common::Attribute; pub use crate::common::Enum; use crate::{ @@ -76,6 +77,7 @@ pub struct Model { pub enums: Vec>, pub registers: Vec>, pub blocks: Vec>, + pub attrs: Vec>, } #[derive(Debug, Clone, PartialEq)] @@ -211,6 +213,9 @@ impl Model { impl Display for Model { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for x in &self.attrs { + writeln!(f, "{x}")?; + } let name = if self.id.is_empty() { "root" } else { @@ -235,13 +240,19 @@ impl Display for Register { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!( f, - "{}\n{} {}{}{}{}", + "{}\n{}{} {}{}{}{}", self.doc .iter() .map(|x| x.trim()) .collect::>() .join("\n ") .dimmed(), + self.attrs + .iter() + .map(|attr| format!("{attr}\n")) + .collect::>() + .join("") + .dimmed(), "register".blue(), self.id.name.cyan(), "<".dimmed(), @@ -263,13 +274,19 @@ impl Display for Field { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}\n {}{} {} {}", + "{}\n {}\n {}{} {} {}", self.doc .iter() .map(|x| x.trim()) .collect::>() .join("\n ") .dimmed(), + self.attrs + .iter() + .map(|attr| format!("{attr}\n ")) + .collect::>() + .join("") + .dimmed(), self.id.name, ":".dimmed(), self.mode.to_string().blue(), @@ -305,6 +322,9 @@ impl Display for Enum { impl Display for Block { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for x in &self.attrs { + writeln!(f, "{}", format!("{x}").dimmed())?; + } writeln!(f, "{} {}", "block".blue(), self.id.name.cyan())?; for x in &self.elements { writeln!(f, " {x}")?; @@ -475,6 +495,7 @@ impl ModelModules { enums: vec![], registers: vec![], blocks: vec![], + attrs: vec![], }, }; @@ -497,6 +518,7 @@ impl ModelModules { mode: f.mode.clone(), typ: mm.resolve_field_type(&f.typ)?, offset: f.offset.clone(), + attrs: f.attrs.clone(), }); } mm.root.registers.push(Arc::new(Register { @@ -506,6 +528,7 @@ impl ModelModules { sram: r.sram, reset_value: r.reset_value.clone(), fields, + attrs: r.attrs.clone(), })); } @@ -539,6 +562,7 @@ impl ModelModules { }, }, offset: e.offset.clone(), + attrs: e.attrs.clone(), }) } mm.root.blocks.push(Arc::new(Block { @@ -546,6 +570,7 @@ impl ModelModules { id: b.id.clone(), sram: b.sram, elements, + attrs: b.attrs.clone(), })); } diff --git a/lib/src/parser.rs b/lib/src/parser.rs index 4ab76a5..1b53e41 100644 --- a/lib/src/parser.rs +++ b/lib/src/parser.rs @@ -7,7 +7,7 @@ use crate::{ Ast, AstModules, Block, BlockElement, Component, Enum, Field, FieldType, Identifier, Number, QualifiedType, Register, Use, }, - common::{Alternative, FieldMode, NumberFormat}, + common::{Alternative, Attribute, FieldMode, NumberFormat}, }; use anyhow::{Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; @@ -21,6 +21,7 @@ use winnow::{ alt, cut_err, delimited, fail, not, repeat, separated, trace, }, error::{ContextError, ErrMode, StrContext, StrContextValue}, + token::take_till, }; pub type Input<'i> = LocatingSlice<&'i str>; @@ -116,6 +117,14 @@ macro_rules! tr { }; } +macro_rules! contextual { + ($name:ident, $label:literal, [$($descr:literal),+]) => { + $name + .context(StrContext::Label($label)) + $(.context(StrContext::Expected(StrContextValue::Description($descr))))+ + } +} + pub fn parse_top(input: &mut Input) -> ModalResult> { let result = cut_err(repeat( 1.., @@ -125,13 +134,14 @@ pub fn parse_top(input: &mut Input) -> ModalResult> { tr!(parse_reg_top), tr!(parse_block_top), tr!(multi_disc_parser_top), - fail.context(StrContext::Label("top level element")) - .context(StrContext::Expected(StrContextValue::Description( + contextual!( + fail, + "top level element", + [ "use statement", - ))) - .context(StrContext::Expected(StrContextValue::Description( - "register/block with a documentation comment", - ))), + "register/block with a documentation comment" + ] + ), )), )) .parse_next(input)?; @@ -149,23 +159,47 @@ pub fn parse_use(input: &mut Input) -> ModalResult { Ok(Use { module }) } +pub fn attributes_parser(input: &mut Input) -> ModalResult> { + let attrs: Vec = separated(1.., attribute_line_parser, newline) + .parse_next(input) + .unwrap_or_else(|_| vec![]); + Ok(attrs) +} + +pub fn attribute_line_parser(input: &mut Input) -> ModalResult { + let _ = multispace0.parse_next(input)?; + let attr = delimited("#[attr(", parse_attribute, ")]").parse_next(input)?; + Ok(attr) +} + +pub fn parse_attribute(input: &mut Input) -> ModalResult { + let _ = multispace0.parse_next(input)?; + let id = identifier_parser.parse_next(input)?; + let _ = multispace0.parse_next(input)?; + token("=").parse_next(input)?; + let _ = multispace0.parse_next(input)?; + let value = string_parser.parse_next(input)?; + let _ = multispace0.parse_next(input)?; + Ok(Attribute { id, value }) +} + pub fn parse_enum_top(input: &mut Input) -> ModalResult { Ok(Top::Enum(parse_enum.parse_next(input)?)) } pub fn parse_enum(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("enum")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) + let doc = contextual!(doc_comment_parser, "enum", ["doc comment"]) + .parse_next(input)?; + let attrs = contextual!(attributes_parser, "enum", ["attributes"]) .parse_next(input)?; token("enum").parse_next(input)?; let mut e = cut_err(parse_enum_cut).parse_next(input)?; e.doc = doc; + e.attrs = attrs; Ok(e) } pub fn parse_enum_cut(input: &mut Input) -> ModalResult { + let attrs = vec![]; let width = delimited("<", number_parser, ">").parse_next(input)?; let id = identifier_parser.parse_next(input)?; token("{").parse_next(input)?; @@ -179,16 +213,14 @@ pub fn parse_enum_cut(input: &mut Input) -> ModalResult { id, width, alternatives, + attrs, }) } pub fn parse_enum_alt(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("enum alternate")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) - .parse_next(input)?; + let doc = + contextual!(doc_comment_parser, "enum alternative", ["doc comment"]) + .parse_next(input)?; let id = identifier_parser.parse_next(input)?; token("=").parse_next(input)?; let value = number_parser.parse_next(input)?; @@ -200,21 +232,21 @@ pub fn parse_reg_top(input: &mut Input) -> ModalResult { } pub fn parse_reg(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("register")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) + let doc = contextual!(doc_comment_parser, "register", ["doc comment"]) + .parse_next(input)?; + let attrs = contextual!(attributes_parser, "register", ["attributes"]) .parse_next(input)?; let sram = token("sram").parse_next(input).is_ok(); token("register").parse_next(input)?; let mut reg = cut_err(parse_reg_cut).parse_next(input)?; reg.doc = doc; reg.sram = sram; + reg.attrs = attrs; Ok(reg) } pub fn parse_reg_cut(input: &mut Input) -> ModalResult { + let attrs = vec![]; let width = delimited("<", number_parser, ">").parse_next(input)?; let id = identifier_parser.parse_next(input)?; let reset_value = match token("reset").parse_next(input) { @@ -240,6 +272,7 @@ pub fn parse_reg_cut(input: &mut Input) -> ModalResult { sram: false, fields, reset_value, + attrs, }); } fields.push(cut_err(parse_field).parse_next(input)?); @@ -249,11 +282,9 @@ pub fn parse_reg_cut(input: &mut Input) -> ModalResult { } pub fn parse_field(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("field")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) + let doc = contextual!(doc_comment_parser, "field", ["doc comment"]) + .parse_next(input)?; + let attrs = contextual!(attributes_parser, "field", ["attributes"]) .parse_next(input)?; let id = identifier_parser.parse_next(input)?; token(":").parse_next(input)?; @@ -266,6 +297,7 @@ pub fn parse_field(input: &mut Input) -> ModalResult { mode, typ, offset, + attrs, }) } @@ -346,17 +378,16 @@ pub fn parse_block_top(input: &mut Input) -> ModalResult { } pub fn parse_block(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("block")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) + let doc = contextual!(doc_comment_parser, "block", ["doc comment"]) + .parse_next(input)?; + let attrs = contextual!(attributes_parser, "block", ["attributes"]) .parse_next(input)?; let sram = token("sram").parse_next(input).is_ok(); token("block").parse_next(input)?; let mut blk = cut_err(parse_block_cut).parse_next(input)?; blk.doc = doc; blk.sram = sram; + blk.attrs = attrs; Ok(blk) } @@ -380,6 +411,7 @@ pub fn parse_block_cut(input: &mut Input) -> ModalResult { id, sram: false, elements, + attrs: Vec::default(), }); } elements.push(cut_err(block_element_parser).parse_next(input)?); @@ -389,11 +421,9 @@ pub fn parse_block_cut(input: &mut Input) -> ModalResult { } pub fn block_element_parser(input: &mut Input) -> ModalResult { - let doc = doc_comment_parser - .context(StrContext::Label("block element")) - .context(StrContext::Expected(StrContextValue::Description( - "doc comment", - ))) + let doc = contextual!(doc_comment_parser, "block element", ["doc comment"]) + .parse_next(input)?; + let attrs = contextual!(attributes_parser, "block element", ["attributes"]) .parse_next(input)?; let component = component_parser.parse_next(input)?; let offset = component_offset_parser.parse_next(input)?; @@ -401,6 +431,7 @@ pub fn block_element_parser(input: &mut Input) -> ModalResult { doc, component, offset, + attrs, }) } @@ -424,11 +455,7 @@ pub fn component_parser(input: &mut Input) -> ModalResult { pub fn component_array_parser( input: &mut Input, ) -> ModalResult<(Number, Number)> { - let length = number_parser - .context(StrContext::Label("array size")) - .context(StrContext::Expected(StrContextValue::Description( - "positive integer", - ))) + let length = contextual!(number_parser, "array size", ["positive integer"]) .parse_next(input)?; token(";").parse_next(input)?; let spacing = number_parser.parse_next(input)?; @@ -511,6 +538,29 @@ pub fn line_comments_parser(input: &mut Input) -> ModalResult> { repeat(0.., token(line_comment_parser)).parse_next(input) } +/// Parse a delimited string, with embedded escapes. +pub fn string_parser<'s>(input: &mut Input<'s>) -> ModalResult { + trace("string_parser", move |input: &mut Input<'s>| { + delimited("\"", parse_string, "\"").parse_next(input) + }) + .parse_next(input) +} + +fn parse_string<'s>(input: &mut Input<'s>) -> ModalResult { + trace("parse_string", move |input: &mut Input<'s>| { + let s: String = repeat(0.., parse_string_parts).parse_next(input)?; + Ok(s) + }) + .parse_next(input) +} + +fn parse_string_parts<'s>(input: &mut Input<'s>) -> ModalResult<&'s str> { + trace("parse_string_parts", move |input: &mut Input<'s>| { + alt(("\\\"", take_till(1, |c| c == '"'))).parse_next(input) + }) + .parse_next(input) +} + /// Parse an identifier. pub fn identifier_parser<'s>(input: &mut Input<'s>) -> ModalResult { trace("identifier_parser", move |input: &mut Input<'s>| { @@ -571,7 +621,7 @@ mod test { let ast = match parse_ast.parse(s) { Ok(ast) => ast, Err(ref e) => { - panic!("parsing failed: {e}"); + panic!("parsing failed: {e:?}"); } }; assert_eq!(ast.use_statements.len(), 3); diff --git a/lib/src/rust_codegen.rs b/lib/src/rust_codegen.rs index 373b8b9..6e6b128 100644 --- a/lib/src/rust_codegen.rs +++ b/lib/src/rust_codegen.rs @@ -1,15 +1,15 @@ //! Rust code generation use crate::ast::Number; -use crate::common::{FieldMode, NumberFormat, Typename}; +use crate::common::{Attribute, FieldMode, NumberFormat, Typename}; use crate::model::{Block, Component, FieldType, FieldUserType, Register}; use crate::model::{ModelModules, Visitor}; use anyhow::{Result, anyhow}; use camino::Utf8Path; use camino_tempfile::NamedUtf8TempFile; use convert_case::{Case, Casing}; -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use proc_macro2::{Punct, Spacing, TokenStream}; +use quote::{ToTokens, TokenStreamExt, format_ident, quote}; use std::collections::BTreeMap; use std::io::Write; use std::str::FromStr; @@ -61,6 +61,7 @@ impl CodegenVisitor { format_ident!("{}Instance", reg.id.name.to_case(Case::Pascal)); let doc = reg.doc.join("\n"); + let attrs = attrs_accessor(&format_ident!("attrs"), ®.attrs); let instance_doc = format!("Instance of a [`{}`]", reg.id.name); self.register_definitions.extend(quote! { @@ -69,6 +70,10 @@ impl CodegenVisitor { #[doc = #doc] pub struct #name([u8; #width]); + impl #name { + #attrs + } + #[doc = #instance_doc] pub struct #instance_name { pub msel_id: u32, @@ -89,9 +94,10 @@ impl Visitor for CodegenVisitor { for f in ®.fields { let doc = f.doc.join("\n"); - let getter = format_ident!("get_{}", f.id.name); - let setter = format_ident!("set_{}", f.id.name); let fname_str = &f.id.name; + let getter = format_ident!("get_{fname_str}"); + let setter = format_ident!("set_{fname_str}"); + let attrs_getter = format_ident!("{fname_str}_attrs"); let offset = proc_macro2::Literal::u128_unsuffixed(f.offset.value); @@ -206,10 +212,11 @@ impl Visitor for CodegenVisitor { pub fn #setter(&mut self, data__: #typename) { self.0.set_field::<#width, #offset>(data__.into()); } - }) + }); } } } + fields.extend(attrs_accessor(&attrs_getter, &f.attrs)); } let addr_type: TokenStream = self.addr_type.into(); let value_type: TokenStream = self.value_type.into(); @@ -358,6 +365,7 @@ impl Visitor for CodegenVisitor { format_ident!("f") }; + let attrs = attrs_accessor(&format_ident!("attrs"), ®.attrs); self.register_definitions.extend(quote! { #[derive(Default, Debug)] @@ -372,6 +380,7 @@ impl Visitor for CodegenVisitor { pub fn reset(&mut self) { #reset } + #attrs } #to_from_value @@ -403,6 +412,7 @@ impl Visitor for CodegenVisitor { _ => panic!("enums cannot be more than 128 bits wide"), }; let doc = e.doc.join("\n"); + let attrs = attrs_accessor(&format_ident!("attrs"), &e.attrs); let mut alts = TokenStream::default(); for a in &e.alternatives { @@ -456,6 +466,9 @@ impl Visitor for CodegenVisitor { } } + impl #name { + #attrs + } }); } @@ -476,7 +489,7 @@ impl Visitor for CodegenVisitor { self.block_definitions.extend(quote! { #[doc = #doc] #[derive(Default, Debug)] - pub struct #block_name{ + pub struct #block_name { pub addr: #addr_type } }); @@ -507,12 +520,16 @@ impl Visitor for CodegenVisitor { } }; + let (id_name, typ) = match &element.component { + Component::Single { id, typ, .. } => (&id.name, typ), + Component::Array { id, typ, .. } => (&id.name, typ), + }; + let name = id_name.to_case(Case::Snake); + let method_name = format_ident!("{name}"); + let attrs_name = format_ident!("{name}_attrs"); + let type_name = typename_to_qualified_ident(typ, "Instance"); match &element.component { - Component::Single { id, typ } => { - let method_name = - format_ident!("{}", id.name.to_case(Case::Snake)); - let type_name = - typename_to_qualified_ident(typ, "Instance"); + Component::Single { .. } => { if block.sram { tokens.extend(quote! { #[doc = #doc] @@ -532,18 +549,10 @@ impl Visitor for CodegenVisitor { } }); } - self.block_methods.insert(current_block.clone(), tokens); } Component::Array { - id, - typ, - length, - spacing, + length, spacing, .. } => { - let method_name = - format_ident!("{}", id.name.to_case(Case::Snake)); - let type_name = - typename_to_qualified_ident(typ, "Instance"); let spacing = proc_macro2::Literal::from_str(&format!( "0x{:x}", spacing.value @@ -574,10 +583,18 @@ impl Visitor for CodegenVisitor { } }); } - self.block_methods.insert(current_block.clone(), tokens); } } + tokens.extend(attrs_accessor(&attrs_name, &element.attrs)); + self.block_methods.insert(current_block.clone(), tokens); } + let mut tokens = self + .block_methods + .get(¤t_block) + .cloned() + .unwrap_or_default(); + tokens.extend(attrs_accessor(&format_ident!("attrs"), &block.attrs)); + self.block_methods.insert(current_block.clone(), tokens); } fn block_component( @@ -788,3 +805,36 @@ fn number_to_token(n: &Number) -> proc_macro2::Literal { } .unwrap() } + +impl ToTokens for Attribute { + fn to_tokens(&self, tokens: &mut TokenStream) { + let name = &self.id.name; + let value = &self.value; + tokens.extend(quote! {(#name, #value)}); + } +} + +struct AttrSlice<'a>(&'a [Attribute]); + +impl ToTokens for AttrSlice<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + for (k, attr) in self.0.iter().enumerate() { + if k > 0 { + tokens.append(Punct::new(',', Spacing::Alone)); + } + attr.to_tokens(tokens); + } + } +} + +fn attrs_accessor( + name: &proc_macro2::Ident, + attrs: &[Attribute], +) -> TokenStream { + let attrs = AttrSlice(attrs); + quote! { + pub fn #name(&self) -> &'static [(&'static str, &'static str)] { + &[#attrs] + } + } +} diff --git a/lib/test_data/nic_rpi.rs b/lib/test_data/nic_rpi.rs index 35f9d3b..0d4f04c 100644 --- a/lib/test_data/nic_rpi.rs +++ b/lib/test_data/nic_rpi.rs @@ -14,6 +14,9 @@ impl PhyConfig { pub fn set_speed(&mut self, data__: ethernet::DataRate) { self.0.set_field::<2, 0>(data__.into()); } + pub fn speed_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Signal reach the phy is configured for. pub fn get_reach(&self) -> Result { self.0.get_field::<3, 8>().try_into() @@ -22,6 +25,9 @@ impl PhyConfig { pub fn set_reach(&mut self, data__: ethernet::Reach) { self.0.set_field::<3, 8>(data__.into()); } + pub fn reach_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Number of lanes the phy is using. pub fn get_lanes(&self) -> Result { self.0.get_field::<3, 16>().try_into() @@ -30,6 +36,9 @@ impl PhyConfig { pub fn set_lanes(&mut self, data__: Lanes) { self.0.set_field::<3, 16>(data__.into()); } + pub fn lanes_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Type of forward error correction to use. pub fn get_fec(&self) -> Result { self.0.get_field::<2, 20>().try_into() @@ -38,6 +47,9 @@ impl PhyConfig { pub fn set_fec(&mut self, data__: ethernet::Fec) { self.0.set_field::<2, 20>(data__.into()); } + pub fn fec_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Type of modulation used on the wire. pub fn get_modulation(&self) -> Result { self.0.get_field::<1, 24>().try_into() @@ -46,12 +58,18 @@ impl PhyConfig { pub fn set_modulation(&mut self, data__: cei::Modulation) { self.0.set_field::<1, 24>(data__.into()); } + pub fn modulation_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } pub fn value(&self) -> BitSet<32> { self.0 } pub fn reset(&mut self) { self.0 = BitSet::<32>::ZERO; } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl From for PhyConfig { fn from(value: u32) -> Self { @@ -141,20 +159,32 @@ impl PhyStatus { pub fn get_carrier(&self) -> bool { bool::from(self.0.get_field::<1, 0>()) } + pub fn carrier_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Indicates if a signal error has been recieved by the MAU. pub fn get_signal_error(&self) -> bool { bool::from(self.0.get_field::<1, 1>()) } + pub fn signal_error_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Indicates that data in the signal received from the MAU is valid. pub fn get_data_valid(&self) -> bool { bool::from(self.0.get_field::<1, 2>()) } + pub fn data_valid_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } pub fn value(&self) -> BitSet<32> { self.0 } pub fn reset(&mut self) { self.0 = BitSet::<32>::ZERO; } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl From for PhyStatus { fn from(value: u32) -> Self { @@ -237,6 +267,11 @@ impl core::fmt::Display for PhyStatus { #[derive(Debug, Default)] /// Metadata value pub struct Metadata([u8; 32]); +impl Metadata { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } +} ///Instance of a [`Metadata`] pub struct MetadataInstance { pub msel_id: u32, @@ -244,6 +279,11 @@ pub struct MetadataInstance { #[derive(Debug, Default)] /// Firmware instruction pub struct FirmwareInstruction([u8; 32]); +impl FirmwareInstruction { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } +} ///Instance of a [`FirmwareInstruction`] pub struct FirmwareInstructionInstance { pub msel_id: u32, @@ -260,12 +300,18 @@ impl Debug { pub fn set_value(&mut self, data__: BitSet<32>) { self.0.set_field::<32, 0>(data__); } + pub fn value_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } pub fn value(&self) -> BitSet<32> { self.0 } pub fn reset(&mut self) { bitset_macro::bitset!(32, 4294967295); } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl From for Debug { fn from(value: u32) -> Self { @@ -379,6 +425,11 @@ impl TryFrom> for Lanes { .map_err(|_| rust_rpi::OutOfRange::EnumValueOutOfRange) } } +impl Lanes { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } +} /// Phy registers. #[derive(Default, Debug)] pub struct PhyInstance { @@ -401,6 +452,9 @@ impl Client { addr: self.addr + 0x100, } } + pub fn version_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// A block for each of the four phys. pub fn phys(&self, index: u32) -> Result { if index > 4 { @@ -410,24 +464,42 @@ impl Client { addr: self.addr + 0x6000 + (index * 0x1000), }) } + pub fn phys_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// The NIC's firmware. pub fn firmware(&self) -> FirmwareInstance { FirmwareInstance { addr: self.addr + 0x10000, } } + pub fn firmware_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl FirmwareInstance { /// Metadata section of firmware pub fn metadata(&self) -> MetadataInstance { MetadataInstance { msel_id: 0 } } + pub fn metadata_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Instruction section of firmware pub fn instructions(&self) -> FirmwareInstructionInstance { FirmwareInstructionInstance { msel_id: 1, } } + pub fn instructions_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl PhyInstance { /// The Phy's version info @@ -436,24 +508,39 @@ impl PhyInstance { addr: self.addr, } } + pub fn version_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// test register. pub fn debug(&self) -> DebugInstance { DebugInstance { addr: self.addr + 0x10, } } + pub fn debug_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Configuration register. pub fn config(&self) -> PhyConfigInstance { PhyConfigInstance { addr: self.addr + 0x200, } } + pub fn config_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } /// Status register. pub fn status(&self) -> PhyStatusInstance { PhyStatusInstance { addr: self.addr + 0x400, } } + pub fn status_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[("foo", "this is an attribute"), ("bar", "embedded quota\\\" attr")] + } } pub mod cei { use bitset::BitSet; @@ -482,6 +569,11 @@ pub mod cei { .map_err(|_| rust_rpi::OutOfRange::EnumValueOutOfRange) } } + impl Modulation { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + } } pub mod ethernet { use super::version; @@ -526,6 +618,11 @@ pub mod ethernet { .map_err(|_| rust_rpi::OutOfRange::EnumValueOutOfRange) } } + impl Reach { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + } /// Forward error correction mode. #[derive(num_enum::TryFromPrimitive, PartialEq, Debug)] #[repr(u8)] @@ -553,6 +650,11 @@ pub mod ethernet { .map_err(|_| rust_rpi::OutOfRange::EnumValueOutOfRange) } } + impl Fec { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + } /// Data rate specification. #[derive(num_enum::TryFromPrimitive, PartialEq, Debug)] #[repr(u8)] @@ -583,6 +685,11 @@ pub mod ethernet { .map_err(|_| rust_rpi::OutOfRange::EnumValueOutOfRange) } } + impl DataRate { + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + } /** Test block This block isn't referenced anywhere. It exists simply to exercise the @@ -598,6 +705,12 @@ pub mod ethernet { addr: self.addr, } } + pub fn version_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } } pub mod version { @@ -611,12 +724,18 @@ pub mod version { pub fn get_value(&self) -> BitSet<32> { self.0.get_field::<32, 0>() } + pub fn value_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } pub fn value(&self) -> BitSet<32> { self.0 } pub fn reset(&mut self) { self.0 = BitSet::<32>::ZERO; } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } impl From for Version { fn from(value: u32) -> Self { @@ -707,5 +826,11 @@ pub mod version { pub fn version(&self) -> VersionInstance { VersionInstance { addr: self.addr } } + pub fn version_attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } + pub fn attrs(&self) -> &'static [(&'static str, &'static str)] { + &[] + } } }