blob: 31e7ae4d861a02ce9c771161bb2a1df0e1a21fd4 [file]
/*++
Licensed under the Apache-2.0 license.
--*/
use std::collections::HashMap;
use std::path::PathBuf;
use crate::component_meta::PropertyMeta;
use crate::file_source::FileSource;
use crate::value::{AddressingType, ComponentType, InterruptType, PropertyType, ScopeType};
use crate::ParseError;
use crate::{
component_meta, token::Token, token_iter::TokenIter, Bits, FileParseError, RdlError, Result,
Value,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DynamicAssignment {
instance_path: Vec<String>,
prop_name: String,
value: Value,
}
impl DynamicAssignment {
fn parse<'a>(tokens: &mut TokenIter<'a>, scope: &Scope) -> Result<'a, Self> {
let mut instance_path = vec![];
loop {
instance_path.push(tokens.expect_identifier()?);
if *tokens.peek(0) != Token::Period {
break;
}
tokens.next();
}
let instance = scope.lookup_instance_by_path(instance_path.iter().cloned())?;
let ScopeType::Component(instance_ty) = instance.ty else {
return Err(RdlError::CantSetPropertyInRootScope);
};
tokens.expect(Token::Pointer)?;
let prop_name = tokens.expect_identifier()?;
let prop_meta = component_meta::property(instance_ty, prop_name)?;
let value = if *tokens.peek(0) == Token::Semicolon {
Value::Bool(true)
} else {
tokens.expect(Token::Equals)?;
prop_meta.ty.parse(tokens)?
};
Ok(Self {
instance_path: instance_path.into_iter().map(|s| s.to_string()).collect(),
prop_name: prop_name.into(),
value,
})
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Scope {
pub ty: ScopeType,
pub types: HashMap<String, Scope>,
pub instances: Vec<Instance>,
pub default_properties: HashMap<String, Value>,
pub properties: HashMap<String, Value>,
pub dynamic_assignments: Vec<DynamicAssignment>,
}
impl Scope {
fn new(ty: ScopeType) -> Self {
Scope {
ty,
types: Default::default(),
instances: Default::default(),
default_properties: Default::default(),
properties: Default::default(),
dynamic_assignments: Default::default(),
}
}
pub fn property_val<'a, T: TryFrom<Value, Error = RdlError<'static>>>(
&self,
name: &'a str,
) -> Result<'a, T> {
match self.property_val_opt(name)? {
Some(val) => Ok(val),
None => Err(RdlError::ExpectedPropertyNotFound(name)),
}
}
pub fn property_val_opt<T: TryFrom<Value, Error = RdlError<'static>>>(
&self,
name: &str,
) -> Result<'static, Option<T>> {
match self.properties.get(name) {
Some(val) => Ok(Some(T::try_from(val.clone())?)),
None => Ok(None),
}
}
pub fn lookup_instance_by_path<'a>(
&self,
mut path_iter: impl Iterator<Item = &'a str>,
) -> Result<'a, &Scope> {
if let Some(instance_name) = path_iter.next() {
if let Some(instance) = self.instances.iter().find(|e| e.name == instance_name) {
instance.scope.lookup_instance_by_path(path_iter)
} else {
Err(RdlError::UnknownInstanceName(instance_name))
}
} else {
Ok(self)
}
}
fn set_property_defaults(&mut self, parent: Option<&ParentScope<'_>>) {
if let ScopeType::Component(ty) = self.ty {
let mut next_parent = parent;
while let Some(parent_scope) = next_parent {
for (prop_name, val) in parent_scope.scope.default_properties.iter() {
if self.properties.contains_key(prop_name) {
continue;
}
if component_meta::property(ty, prop_name).is_err() {
continue;
}
self.properties.insert(prop_name.into(), val.clone());
}
next_parent = parent_scope.parent;
}
}
}
pub fn as_parent(&self) -> ParentScope {
ParentScope {
scope: self,
parent: None,
}
}
fn calculate_offsets(&mut self) -> Result<'static, ()> {
let addr_mode = AddressingType::Compact;
let default_alignment = self.property_val_opt::<u64>("alignment").ok().flatten();
// Set reg offsets
let mut next_offset = 0;
for instance in self.instances.iter_mut() {
if instance.scope.ty != ComponentType::Reg.into() {
continue;
}
let reg_width = instance
.scope
.property_val_opt("regwidth")
.ok()
.flatten()
.unwrap_or(32u64);
let access_width = instance
.scope
.property_val_opt("accesswidth")
.ok()
.flatten()
.unwrap_or(reg_width);
let align = if let Some(next_alignment) = instance.next_alignment {
next_alignment
} else if let Some(default_alignment) = default_alignment {
default_alignment
} else {
match addr_mode {
AddressingType::Compact => access_width / 8,
AddressingType::RegAlign => instance.element_size(),
AddressingType::FullAlign => instance.total_size()?,
}
};
if instance.offset.is_none() {
if next_offset % align != 0 {
next_offset = ((next_offset / align) + 1) * align;
}
instance.offset = Some(next_offset);
}
if let Some(offset) = instance.offset {
next_offset = offset + instance.total_size()?;
}
}
let mut next_offset = 0;
for instance in self.instances.iter_mut() {
if instance.scope.ty != ComponentType::Field.into() {
continue;
}
if instance.dimension_sizes.len() > 1 {
return Err(RdlError::MultidimensionalFieldsNotSupported);
}
let field_width = if instance.dimension_sizes.len() == 1 {
instance.dimension_sizes[0]
} else {
instance
.scope
.property_val_opt("fieldwidth")
.ok()
.flatten()
.unwrap_or(1u64)
};
if instance.offset.is_none() {
instance.offset = Some(next_offset);
}
if let Some(offset) = instance.offset {
next_offset = offset + field_width;
}
}
for ty in self.types.values_mut() {
ty.calculate_offsets()?;
}
for el in self.instances.iter_mut() {
el.scope.calculate_offsets()?;
}
Ok(())
}
fn parse<'a>(
&mut self,
tokens: &mut TokenIter<'a>,
parent: Option<&ParentScope<'_>>,
) -> Result<'a, ()> {
loop {
if self.ty == ScopeType::Root && *tokens.peek(0) == Token::EndOfFile {
break;
}
if *tokens.peek(0) == Token::BraceClose {
break;
}
if let Ok(component_type) = component_keyword(tokens.peek(0)) {
tokens.next();
let type_name = if *tokens.peek(0) == Token::BraceOpen {
None
} else {
Some(tokens.expect_identifier()?)
};
// TODO: implement support for component parameters
tokens.expect(Token::BraceOpen)?;
let mut ty_scope = Self::new(ScopeType::Component(component_type));
ty_scope.parse(
tokens,
Some(&ParentScope {
parent,
scope: self,
}),
)?;
ty_scope.set_property_defaults(Some(&ParentScope {
parent,
scope: self,
}));
tokens.expect(Token::BraceClose)?;
if let Some(type_name) = type_name {
if self.types.contains_key(type_name) {
return Err(RdlError::DuplicateTypeName(type_name));
}
self.types.insert(type_name.into(), ty_scope.clone());
}
if *tokens.peek(0) != Token::Semicolon {
loop {
let instance = Instance::parse(ty_scope.clone(), tokens)?;
if self.instances.iter().any(|e| e.name == instance.name) {
return Err(RdlError::DuplicateInstanceName(instance.name));
}
self.instances.push(instance);
if *tokens.peek(0) == Token::Comma {
tokens.next();
continue;
}
break;
}
}
tokens.expect(Token::Semicolon)?;
continue;
}
if *tokens.peek(0) == Token::Identifier("default") {
tokens.next();
if !self.instances.is_empty() || !self.types.is_empty() {
return Err(RdlError::DefaultPropertiesMustBeDefinedBeforeComponents);
}
let prop = PropertyAssignment::parse(tokens, |prop_name| {
component_meta::default_property(self.ty, prop_name)
})?;
self.default_properties
.insert(prop.prop_name.into(), prop.value);
continue;
}
if tokens.peek(0).is_identifier()
&& (*tokens.peek(1) == Token::Equals || *tokens.peek(1) == Token::Semicolon)
{
match self.ty {
ScopeType::Component(ComponentType::Enum) => {
let enum_variant_name = tokens.expect_identifier()?;
// Enums don't have properties
let mut variant_scope = Scope {
ty: ScopeType::Component(ComponentType::EnumVariant),
..Default::default()
};
let mut value = None;
if *tokens.peek(0) == Token::Equals {
tokens.next();
value = Some(tokens.expect_bits()?);
if *tokens.peek(0) == Token::BraceOpen {
tokens.next();
variant_scope =
Self::new(ScopeType::Component(ComponentType::EnumVariant));
variant_scope.parse(
tokens,
Some(&ParentScope {
parent,
scope: self,
}),
)?;
tokens.expect(Token::BraceClose)?;
}
}
self.instances.push(Instance {
name: enum_variant_name.into(),
reset: value,
scope: variant_scope,
..Default::default()
});
tokens.expect(Token::Semicolon)?;
continue;
}
_ => {
// This is a property
let ScopeType::Component(ty) = self.ty else {
return Err(RdlError::CantSetPropertyInRootScope);
};
let assignment = PropertyAssignment::parse(tokens, |prop_name| {
component_meta::property(ty, prop_name)
})?;
if self.properties.contains_key(assignment.prop_name) {
return Err(RdlError::DuplicatePropertyName(assignment.prop_name));
}
self.properties
.insert(assignment.prop_name.into(), assignment.value);
continue;
}
}
}
if tokens.peek(0).is_identifier() && *tokens.peek(1) == Token::Period
|| *tokens.peek(1) == Token::Pointer
{
let assignment = DynamicAssignment::parse(tokens, self)?;
tokens.expect(Token::Semicolon)?;
self.dynamic_assignments.push(assignment);
continue;
}
let type_name = tokens.expect_identifier()?;
// This is a template instantiation
let ty_scope = lookup_typedef(self, parent, type_name)?.clone();
let mut instance = Instance::parse(ty_scope, tokens)?;
instance.type_name = Some(type_name.into());
if self.instances.iter().any(|e| e.name == instance.name) {
return Err(RdlError::DuplicateInstanceName(instance.name));
}
self.instances.push(instance);
tokens.expect(Token::Semicolon)?;
}
Ok(())
}
pub fn parse_root<'a>(
file_source: &'a dyn FileSource,
input_files: &[PathBuf],
) -> std::result::Result<Self, FileParseError<'a>> {
let mut result = Self::parse_root_internal(file_source, input_files)?;
result
.calculate_offsets()
.map_err(|_| FileParseError::CouldNotCalculateOffsets)?;
Ok(result)
}
fn parse_root_internal<'a>(
file_source: &'a dyn FileSource,
input_files: &[PathBuf],
) -> std::result::Result<Self, FileParseError<'a>> {
let mut result = Self::new(ScopeType::Root);
for path in input_files.iter() {
let mut tokens = TokenIter::from_path(file_source, path).map_err(FileParseError::Io)?;
match result.parse(&mut tokens, None) {
Ok(()) => {}
Err(error) => {
return Err(FileParseError::Parse(ParseError::new(
tokens.current_file_path(),
tokens.current_file_contents(),
tokens.last_span().clone(),
error,
)))
}
}
}
Ok(result)
}
}
fn component_keyword<'a>(token: &Token<'a>) -> Result<'a, ComponentType> {
match token {
Token::Field => Ok(ComponentType::Field),
Token::Reg => Ok(ComponentType::Reg),
Token::RegFile => Ok(ComponentType::RegFile),
Token::AddrMap => Ok(ComponentType::AddrMap),
Token::Signal => Ok(ComponentType::Signal),
Token::Enum => Ok(ComponentType::Enum),
Token::Mem => Ok(ComponentType::Mem),
Token::Constraint => Ok(ComponentType::Constraint),
unexpected => Err(RdlError::UnexpectedToken(unexpected.clone())),
}
}
#[derive(Copy, Clone)]
pub struct ParentScope<'a> {
parent: Option<&'a ParentScope<'a>>,
pub scope: &'a Scope,
}
impl<'a> ParentScope<'a> {
pub fn instance_iter(&'a self) -> impl Iterator<Item = InstanceRef<'a>> {
self.scope.instances.iter().map(|i| InstanceRef {
instance: i,
scope: ParentScope {
parent: Some(self),
scope: &i.scope,
},
})
}
pub fn type_iter(&'a self) -> impl Iterator<Item = (&str, ParentScope<'a>)> {
self.scope.types.iter().map(|(name, scope)| {
(
name.as_str(),
ParentScope {
parent: Some(self),
scope,
},
)
})
}
pub fn lookup_typedef(&'a self, name: &'_ str) -> Option<ParentScope<'a>> {
let mut parent = self;
loop {
if let Some(result) = parent.scope.types.get(name) {
return Some(ParentScope {
scope: result,
parent: Some(parent),
});
}
if let Some(new_parent) = parent.parent {
parent = new_parent;
} else {
return None;
}
}
}
}
fn lookup_typedef<'a, 'b>(
mut scope: &'b Scope,
mut parent: Option<&'b ParentScope<'_>>,
name: &'a str,
) -> Result<'a, &'b Scope> {
loop {
if let Some(result) = scope.types.get(name) {
return Ok(result);
}
if let Some(parent_scope) = parent {
scope = parent_scope.scope;
parent = parent_scope.parent;
} else {
return Err(RdlError::UnknownTypeName(name));
}
}
}
#[derive(Copy, Clone)]
pub struct InstanceRef<'a> {
pub instance: &'a Instance,
pub scope: ParentScope<'a>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Instance {
pub name: String,
// If this instance was instantiated from a common type, this is the name of
// that type
pub type_name: Option<String>,
// [2][4] parses to vec![2, 4], which means there is an array of 2 elements,
// where each element is an array of 4 values. In rust notation this would be [[u32; 4]; 2].
pub dimension_sizes: Vec<u64>,
pub reset: Option<Bits>,
pub offset: Option<u64>,
pub stride: Option<u64>,
pub next_alignment: Option<u64>,
pub scope: Scope,
}
impl Instance {
pub fn element_size(&self) -> u64 {
let width = if let Ok(Some(w)) = self.scope.property_val_opt::<u64>("regwidth") {
w / 8
} else {
1
};
width
}
pub fn total_size(&self) -> Result<'static, u64> {
let stride = if let Some(stride) = self.stride {
stride
} else {
self.element_size()
};
if stride < self.element_size() {
return Err(RdlError::StrideIsLessThanElementSize);
}
let total_elements: u64 = self.dimension_sizes.iter().product();
Ok(total_elements * stride)
}
fn parse<'a>(scope: Scope, i: &mut TokenIter<'a>) -> Result<'a, Self> {
let ScopeType::Component(component_type) = scope.ty else {
return Err(RdlError::RootCantBeInstantiated);
};
let component_meta = component_meta::get_component_meta(component_type);
if !component_meta.can_instantiate {
return Err(RdlError::ComponentTypeCantBeInstantiated(component_type));
}
let mut result = Self {
name: i.expect_identifier()?.to_string(),
scope,
..Default::default()
};
if component_type == ComponentType::Field
&& *i.peek(0) == Token::BracketOpen
&& *i.peek(2) == Token::Colon
{
i.next();
let msb = i.expect_number()?;
i.expect(Token::Colon)?;
let lsb = i.expect_number()?;
if msb < lsb {
return Err(RdlError::MsbLessThanLsb);
}
result.dimension_sizes.push(msb - lsb + 1);
result.offset = Some(lsb);
i.expect(Token::BracketClose)?;
} else {
while *i.peek(0) == Token::BracketOpen {
i.next();
result.dimension_sizes.push(i.expect_number()?);
i.expect(Token::BracketClose)?;
}
}
if *i.peek(0) == Token::Equals {
i.next();
result.reset = match i.next() {
Token::Bits(bits) => Some(bits),
Token::Number(num) => Some(Bits::new(result.dimension_sizes.iter().product(), num)),
unexpected => return Err(RdlError::UnexpectedToken(unexpected)),
}
}
if result.scope.ty == ComponentType::Reg.into()
|| result.scope.ty == ComponentType::RegFile.into()
|| result.scope.ty == ComponentType::AddrMap.into()
|| result.scope.ty == ComponentType::Mem.into()
{
if *i.peek(0) == Token::At {
i.next();
result.offset = Some(i.expect_number()?);
}
if *i.peek(0) == Token::PlusEqual {
i.next();
result.stride = Some(i.expect_number()?);
}
if *i.peek(0) == Token::PercentEqual {
i.next();
result.next_alignment = Some(i.expect_number()?);
}
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use crate::{file_source::MemFileSource, value::AccessType, EnumReference};
use super::*;
#[test]
fn test_scope_def() {
let fs = MemFileSource::from_entries(&[(
"main.rdl".into(),
r#"
field {} some_field;
field a_field_ty {};
"#
.into(),
)]);
let root_scope = Scope::parse_root_internal(&fs, &["main.rdl".into()]).unwrap();
assert_eq!(
Scope {
ty: ScopeType::Root,
types: HashMap::from([(
"a_field_ty".into(),
Scope {
ty: ScopeType::Component(ComponentType::Field),
..Default::default()
}
),]),
instances: vec![Instance {
name: "some_field".into(),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
..Default::default()
},
..Default::default()
}],
..Default::default()
},
root_scope
);
}
#[test]
fn test_type_instantiation() {
let fs = MemFileSource::from_entries(&[(
"main.rdl".into(),
r#"
field a_field_ty {desc = "Hello";};
a_field_ty my_field;
"#
.into(),
)]);
let root_scope = Scope::parse_root_internal(&fs, &["main.rdl".into()]).unwrap();
assert_eq!(
Scope {
ty: ScopeType::Root,
types: HashMap::from([(
"a_field_ty".into(),
Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([("desc".into(), "Hello".into())]),
..Default::default()
}
),]),
instances: vec![Instance {
name: "my_field".into(),
type_name: Some("a_field_ty".into()),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([("desc".into(), "Hello".into())]),
..Default::default()
},
..Default::default()
}],
..Default::default()
},
root_scope
);
}
#[test]
fn test_stuff() {
let fs = MemFileSource::from_entries(&[(
"main.rdl".into(),
r#"
addrmap {
addressing = compact;
lsb0 = true;
default regwidth = 32;
reg {
name = "Status register";
desc = "Status of the peripheral";
field {sw = r; hw = w;} READY = 1'b0;
field {hwclr; sw = r; hw = w;} VALID = 1'b0;
field {sw = rw; hw = rw;} ID[23:16] = 0xd2;
} STATUS;
reg {
enum mode_t {
ALERT;
TIRED = 2'd1;
SLEEPING = 2'd2 {
desc = "Power consumption is minimal";
};
};
field {encode=mode_t;} MODE = 4'hf;
} MODE @0x1000;
} my_addrmap;
"#
.into(),
)]);
let root_scope = Scope::parse_root_internal(&fs, &["main.rdl".into()]).unwrap();
assert_eq!(
Scope {
ty: ScopeType::Root,
instances: vec![Instance {
name: "my_addrmap".into(),
scope: Scope {
ty: ScopeType::Component(ComponentType::AddrMap),
properties: HashMap::from([
("addressing".into(), AddressingType::Compact.into()),
("lsb0".into(), true.into()),
]),
default_properties: HashMap::from([("regwidth".into(), 32.into()),]),
instances: vec![
Instance {
name: "STATUS".into(),
scope: Scope {
ty: ScopeType::Component(ComponentType::Reg),
properties: HashMap::from([
("name".into(), "Status register".into()),
("desc".into(), "Status of the peripheral".into()),
("regwidth".into(), 32.into()),
]),
instances: vec![
Instance {
name: "READY".into(),
reset: Some(Bits::new(1, 0)),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([
("sw".into(), AccessType::R.into()),
("hw".into(), AccessType::W.into()),
]),
..Default::default()
},
..Default::default()
},
Instance {
name: "VALID".into(),
reset: Some(Bits::new(1, 0)),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([
("hwclr".into(), true.into()),
("sw".into(), AccessType::R.into()),
("hw".into(), AccessType::W.into()),
]),
..Default::default()
},
..Default::default()
},
Instance {
name: "ID".into(),
reset: Some(Bits::new(8, 0xd2)),
dimension_sizes: vec![8],
offset: Some(16),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([
("sw".into(), AccessType::Rw.into()),
("hw".into(), AccessType::Rw.into()),
]),
..Default::default()
},
..Default::default()
},
],
..Default::default()
},
..Default::default()
},
Instance {
name: "MODE".into(),
offset: Some(0x1000),
scope: Scope {
ty: ComponentType::Reg.into(),
properties: HashMap::from([("regwidth".into(), 32.into()),]),
types: HashMap::from([(
"mode_t".into(),
Scope {
ty: ScopeType::Component(ComponentType::Enum),
instances: vec![
Instance {
name: "ALERT".into(),
scope: Scope {
ty: ComponentType::EnumVariant.into(),
..Default::default()
},
..Default::default()
},
Instance {
name: "TIRED".into(),
reset: Some(Bits::new(2, 1)),
scope: Scope {
ty: ComponentType::EnumVariant.into(),
..Default::default()
},
..Default::default()
},
Instance {
name: "SLEEPING".into(),
reset: Some(Bits::new(2, 2)),
scope: Scope {
ty: ComponentType::EnumVariant.into(),
properties: HashMap::from([(
"desc".into(),
"Power consumption is minimal".into()
),]),
..Default::default()
},
..Default::default()
},
],
..Default::default()
}
)]),
instances: vec![Instance {
name: "MODE".into(),
reset: Some(Bits::new(4, 0xf)),
scope: Scope {
ty: ScopeType::Component(ComponentType::Field),
properties: HashMap::from([(
"encode".into(),
EnumReference("mode_t".into()).into()
),]),
..Default::default()
},
..Default::default()
},],
..Default::default()
},
..Default::default()
}
],
..Default::default()
},
..Default::default()
}],
..Default::default()
},
root_scope
);
}
}
fn is_intr_modifier(token: &Token) -> bool {
matches!(
*token,
Token::Identifier("posedge" | "negedge" | "bothedge" | "level" | "nonsticky")
)
}
struct PropertyAssignment<'a> {
prop_name: &'a str,
value: Value,
}
static INTR_BOOL_PROPERTY: PropertyMeta = PropertyMeta {
name: "intr",
ty: PropertyType::Boolean,
is_dynamic: true,
};
fn intr_bool_property<'a>(_name: &str) -> Result<'a, &'static PropertyMeta> {
Ok(&INTR_BOOL_PROPERTY)
}
impl<'a> PropertyAssignment<'a> {
fn parse(
tokens: &mut TokenIter<'a>,
meta_lookup_fn: impl Fn(&'a str) -> Result<'a, &'static PropertyMeta>,
) -> Result<'a, Self> {
if is_intr_modifier(tokens.peek(0)) && *tokens.peek(1) == Token::Identifier("intr") {
let intr_modifier = tokens.expect_identifier()?;
// skip the bool tokens...
PropertyAssignment::parse(tokens, intr_bool_property)?;
return Ok(Self {
prop_name: "intr",
value: match intr_modifier {
"posedge" => InterruptType::PosEdge.into(),
"negedge" => InterruptType::NegEdge.into(),
"bothedge" => InterruptType::BothEdge.into(),
"level" => InterruptType::Level.into(),
"nonsticky" => InterruptType::NonSticky.into(),
_ => InterruptType::Level.into(),
},
});
}
let prop_name = tokens.expect_identifier()?;
let prop_meta = meta_lookup_fn(prop_name)?;
let value = if *tokens.peek(0) == Token::Semicolon {
// This must be a boolean property set to true
if prop_meta.ty != PropertyType::Boolean
&& prop_meta.ty != PropertyType::BooleanOrReference
{
return Err(RdlError::UnexpectedPropertyType {
expected_type: prop_meta.ty,
value: true.into(),
});
}
true.into()
} else {
tokens.expect(Token::Equals)?;
prop_meta.ty.parse(tokens)?
};
tokens.expect(Token::Semicolon)?;
Ok(Self { prop_name, value })
}
}