Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Rule: Implicit case default #281

Merged
merged 29 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
47c3517
added explanation
ronitnallagatla Jan 31, 2024
bdd3831
added pass/fail examples
ronitnallagatla Feb 8, 2024
1c0a7d0
started making changes to implicit_case_default.rs
ShreChinno Feb 22, 2024
546873f
New Rule: implicit_case_default -- basic implementation
ronitnallagatla Feb 23, 2024
68f609b
Commit from GitHub Actions (Run mdgen)
ronitnallagatla Feb 23, 2024
1e9c772
Merge pull request #4 from ronitnallagatla/ronit
5han7anu-S Feb 23, 2024
5073475
implicit_case_default works for most implicit declarations, still nee…
ShreChinno Mar 15, 2024
f445e1f
simple case implicit_case_default DONE
ShreChinno Mar 19, 2024
f665f23
Commit from GitHub Actions (Run mdgen)
ShreChinno Mar 19, 2024
ec897a6
added another fail testcase
ShreChinno Mar 20, 2024
b12a61f
Merge remote-tracking branch 'origin/shreyas' into shreyas
ShreChinno Mar 20, 2024
8907da0
Commit from GitHub Actions (Run mdgen)
ShreChinno Mar 20, 2024
977e4c3
removed debug prints
ShreChinno Mar 20, 2024
78df364
small changes
ShreChinno Mar 21, 2024
1a96bf0
Commit from GitHub Actions (Run mdgen)
ShreChinno Mar 21, 2024
3d48239
changed hint
ShreChinno Mar 21, 2024
611c74f
Commit from GitHub Actions (Run mdgen)
ShreChinno Mar 21, 2024
b3baa66
Merge pull request #5 from ronitnallagatla/shreyas
ronitnallagatla Mar 21, 2024
57e4d5a
add locate on fail
ronitnallagatla Mar 21, 2024
fc18e12
add missing begin/end
ronitnallagatla Mar 21, 2024
38f0769
Commit from GitHub Actions (Run mdgen)
ronitnallagatla Mar 22, 2024
4bdb2c3
added more testcases
ronitnallagatla Mar 22, 2024
67f6a4a
Commit from GitHub Actions (Run mdgen)
ronitnallagatla Mar 22, 2024
7b753af
some cleanup/renaming
ronitnallagatla Mar 22, 2024
b3442e9
Merge pull request #6 from ronitnallagatla/ronit
ShreChinno Mar 22, 2024
321c530
fixed line width in explanation
ronitnallagatla Mar 22, 2024
cf1215c
Commit from GitHub Actions (Run mdgen)
ronitnallagatla Mar 22, 2024
9865291
implicit_case_default: updated explanation
ronitnallagatla Apr 10, 2024
ade2a24
Commit from GitHub Actions (Run mdgen)
ronitnallagatla Apr 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,169 @@ 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 never metastable.

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

### Pass Example (2 of 5)
```systemverilog
module M;
always_comb begin
y = 0;
z = 0;
w = 0;
case (x)
1: y = 1;
2: begin
z = 1;
w = 1;
end
endcase
end
endmodule
```

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

### Pass Example (4 of 5)
```systemverilog
module M;
always_comb
case (x)
1: p = 1;
2: q = 0;
default: begin
p = 0;
q = 0;
end
endcase
endmodule
```

### Pass Example (5 of 5)
```systemverilog
module M;
always_comb begin
p = 0; // p -> implicit default
q = 0; // q -> implicit default
case (x)
1: p = 1;
2: q = 1;
3: r = 1;
default: r = 1; // r -> explicit default
endcase
end
endmodule
```

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

### Fail Example (2 of 3)
```systemverilog
module M;
always_comb begin
y = 0;
case (x)
1: y = 1;
2: begin
z = 1;
w = 1;
end
endcase
end
endmodule
```

### Fail Example (3 of 3)
```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 on `x` fails, `y` would not infer memory or be undriven
because the default value is defined before the `case`.

This rule is a more lenient version of `case_default`. It adapts to a specific
coding style of setting default values to signals at the top of a procedural
block to ensure that signals have a default value regardless of the logic in the
procedural block. As such, this rule will only consider values set
**unconditionally** at the top of the procedural block as a default and will
disregard assignments made in conditional blocks like `if`/`else`, etc. If this
coding style is not preferred, it is strongly suggested to use the rules
mentioned below as they offer stricter guarantees.

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
36 changes: 36 additions & 0 deletions md/syntaxrules-explanation-implicit_case_default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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 on `x` fails, `y` would not infer memory or be undriven
because the default value is defined before the `case`.

This rule is a more lenient version of `case_default`. It adapts to a specific
coding style of setting default values to signals at the top of a procedural
block to ensure that signals have a default value regardless of the logic in the
procedural block. As such, this rule will only consider values set
**unconditionally** at the top of the procedural block as a default and will
disregard assignments made in conditional blocks like `if`/`else`, etc. If this
coding style is not preferred, it is strongly suggested to use the rules
mentioned below as they offer stricter guarantees.
Comment on lines +20 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaveMcEwan, thanks for your feedback. I have updated the explanation to indicate that this rule is not as strict as the alternatives. Is there anything else you might want to add or perhaps rephrase?


See also:

- **case_default**
- **explicit_case_default**

The most relevant clauses of IEEE1800-2017 are:

- 12.5 Case statement
125 changes: 125 additions & 0 deletions src/syntaxrules/implicit_case_default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use crate::config::ConfigOption;
use crate::linter::{SyntaxRule, SyntaxRuleResult};
use sv_parser::{unwrap_locate, unwrap_node, NodeEvent, RefNode, SyntaxTree};

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

impl SyntaxRule for ImplicitCaseDefault {
fn check(
&mut self,
syntax_tree: &SyntaxTree,
event: &NodeEvent,
_option: &ConfigOption,
) -> SyntaxRuleResult {
let node = match event {
NodeEvent::Enter(x) => {
match x {
RefNode::AlwaysConstruct(_) => {
self.under_always_construct = true;
self.has_default = false;
}

RefNode::CaseItemNondefault(_) => {
self.under_case_item = true;
}

_ => (),
}
x
}

NodeEvent::Leave(x) => {
match x {
RefNode::AlwaysConstruct(_) => {
self.under_always_construct = false;
self.has_default = false;
self.lhs_variables.clear();
}

RefNode::CaseItemNondefault(_) => {
self.under_case_item = false;
}

_ => (),
}
return SyntaxRuleResult::Pass;
}
};

// match implicit declarations
if let (true, false, RefNode::BlockItemDeclaration(x)) =
(self.under_always_construct, self.under_case_item, node)
{
let var = unwrap_node!(*x, VariableDeclAssignment).unwrap();
let id = get_identifier(var, syntax_tree);
self.lhs_variables.push(id);
}

// check if default
if let (true, RefNode::CaseStatementNormal(x)) = (self.under_always_construct, node) {
let a = unwrap_node!(*x, CaseItemDefault);
if a.is_some() {
self.has_default = true;
}
}

// match case statement declarations
match (self.under_always_construct, self.under_case_item, node) {
(true, true, RefNode::BlockingAssignment(x)) => {
let var = unwrap_node!(*x, VariableLvalueIdentifier).unwrap();
let loc = unwrap_locate!(var.clone()).unwrap();
let id = get_identifier(var, syntax_tree);

if self.lhs_variables.contains(&id.to_string()) || self.has_default {
return SyntaxRuleResult::Pass;
} else {
return SyntaxRuleResult::FailLocate(*loc);
}
}

(true, true, RefNode::BlockItemDeclaration(x)) => {
let var = unwrap_node!(*x, VariableDeclAssignment).unwrap();
let loc = unwrap_locate!(var.clone()).unwrap();
let id = get_identifier(var, syntax_tree);

if self.lhs_variables.contains(&id.to_string()) || self.has_default {
return SyntaxRuleResult::Pass;
} else {
return SyntaxRuleResult::FailLocate(*loc);
}
}

_ => (),
}

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 never metastable.")
}
}

fn get_identifier(node: RefNode, syntax_tree: &SyntaxTree) -> String {
let id = match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier) {
Some(RefNode::SimpleIdentifier(x)) => Some(x.nodes.0),
Some(RefNode::EscapedIdentifier(x)) => Some(x.nodes.0),
_ => None,
};

String::from(syntax_tree.get_str(&id).unwrap())
}
28 changes: 28 additions & 0 deletions testcases/syntaxrules/fail/implicit_case_default.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module M;
always_comb
case (x)
1: a = 0; // No implicit or explicit case default
endcase
endmodule
////////////////////////////////////////////////////////////////////////////////
module M;
always_comb begin
y = 0;
case (x)
1: y = 1;
2: begin
z = 1;
w = 1;
end
endcase
end
endmodule
////////////////////////////////////////////////////////////////////////////////
module M;
always_comb begin
a = 0;
case (x)
1: b = 0;
endcase
end
endmodule
Loading