Skip to content

Commit

Permalink
Merge pull request #4 from ronitnallagatla/ronit
Browse files Browse the repository at this point in the history
New Rule: implicit_case_default -- basic implementation
  • Loading branch information
5han7anu-S authored Feb 23, 2024
2 parents bdd3831 + 68f609b commit 1e9c772
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
83 changes: 83 additions & 0 deletions MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,89 @@ The most relevant clauses of IEEE1800-2017 are:



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `implicit_case_default`

### Hint

Signal driven in `case` statement does not have a default value.

### Reason

Default values ensure that signals are always driven.

### Pass Example (1 of 2)
```systemverilog
module M;
always_comb
y = 0;
case(x)
1: y = 1; // case default is implicit
endcase
endmodule
```

### Pass Example (2 of 2)
```systemverilog
module M;
always_comb
case(x)
1: y = 1;
default: y = 0;
endcase
endmodule
```

### Fail Example (1 of 2)
```systemverilog
module M;
always_comb
case (x)
1: a = 0; // No implicit or explicit case default
endcase
endmodule
```

### Fail Example (2 of 2)
```systemverilog
module M;
always_comb begin
a = 0;
case(x)
1: b = 0;
endcase
end
endmodule
```

### Explanation

This rule is an extension of the **case_default** rule that allows the case default to be implicitly defined.
Case statements without a `default` branch can cause signals to be undriven. Setting default values of signals at the top of an `always` procedures is good practice and ensures that signals are never metastable when a case match fails. For example,
```sv
always_comb begin
y = 0;
case(x)
1: y = 1;
endcase
end
```
If the case match fails, `y` wouldn't infer memory or be undriven because the default value is defined before the `case`.

See also:
- **case_default**
- **explicit_case_default**

The most relevant clauses of IEEE1800-2017 are:

- 12.5 Case statement




* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `inout_with_tri`
Expand Down
92 changes: 92 additions & 0 deletions src/syntaxrules/implicit_case_default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use crate::config::ConfigOption;
use crate::linter::{SyntaxRule, SyntaxRuleResult};
use sv_parser::{unwrap_node, Locate, NodeEvent, RefNode, SyntaxTree};

#[derive(Default)]
pub struct ImplicitCaseDefault {
under_always_construct: bool,
lhs_variables: Vec<String>,
}

impl SyntaxRule for ImplicitCaseDefault {
fn check(
&mut self,
syntax_tree: &SyntaxTree,
event: &NodeEvent,
_option: &ConfigOption,
) -> SyntaxRuleResult {
// println!("Syntax Tree: {}", syntax_tree);

let node = match event {
NodeEvent::Enter(x) => {
match x {
RefNode::AlwaysConstruct(_) => {
self.under_always_construct = true;
}

RefNode::BlockItemDeclaration(x) => {
let var = unwrap_node!(*x, VariableDeclAssignment).unwrap();
let id = get_identifier(var);
let id = syntax_tree.get_str(&id).unwrap();

self.lhs_variables.push(String::from(id));

println!("LHS Variables: {:?}", self.lhs_variables);
}

_ => (),
}
x
}
NodeEvent::Leave(x) => {
if let RefNode::AlwaysConstruct(_) = x {
self.under_always_construct = false;
}
return SyntaxRuleResult::Pass;
}
};
match (self.under_always_construct, node) {
(true, RefNode::CaseStatementNormal(x)) => {
let a = unwrap_node!(*x, CaseItemDefault);
if a.is_some() {
SyntaxRuleResult::Pass
} else {
// check if lvalues of case statement have an implicit definition
let var = unwrap_node!(*x, VariableLvalueIdentifier).unwrap();
let id = get_identifier(var);
let id = syntax_tree.get_str(&id).unwrap();

println!("Case variable: {id}");

// check if id is in lhs_variables
if self.lhs_variables.contains(&id.to_string()) {
SyntaxRuleResult::Pass
} else {
SyntaxRuleResult::Fail
}
}
}
_ => SyntaxRuleResult::Pass,
}
}

fn name(&self) -> String {
String::from("implicit_case_default")
}

fn hint(&self, _option: &ConfigOption) -> String {
String::from("Signal driven in `case` statement does not have a default value.")
}

fn reason(&self) -> String {
String::from("Default values ensure that signals are always driven.")
}
}

fn get_identifier(node: RefNode) -> Option<Locate> {
match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier) {
Some(RefNode::SimpleIdentifier(x)) => Some(x.nodes.0),
Some(RefNode::EscapedIdentifier(x)) => Some(x.nodes.0),
_ => None,
}
}

0 comments on commit 1e9c772

Please sign in to comment.