Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into pages
Browse files Browse the repository at this point in the history
  • Loading branch information
williaml33moore committed Apr 9, 2024
2 parents f1c0098 + 735eda4 commit 83f4be7
Show file tree
Hide file tree
Showing 17 changed files with 5,468 additions and 17 deletions.
49 changes: 36 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
# Bathtub

BDD Gherkin implementation in native SystemVerilog, based on UVM.
![Bathtub_Logo](docs/assets/Bathtub_Logo.png)

Bathtub is an open-source
SystemVerilog package
based on UVM
that brings Agile Behavior-Driven Development (BDD)
and Gherkin
to the design and verification of integrated circuits,
enabling executable specifications and true living documentation.

This repo contains the following:

[Source code](https://github.com/williaml33moore/bathtub)
: The Bathtub code is available open-source under the [M.I.T. license](https://github.com/williaml33moore/bathtub/blob/main/LICENSE).
The repo is available for cloning and forking, but I don't expect to entertain pull requests until it has reached a more complete release milestone. Bathtub is written in SystemVerilog and requires a full-featured SystemVerilog simulator with UVM to run.

[Pages](https://williaml33moore.github.io/bathtub/)
: Web pages, deployed and served from the [`pages`](https://github.com/williaml33moore/bathtub/tree/pages) branch to [https://williaml33moore.github.io/bathtub/](https://williaml33moore.github.io/bathtub/).

These easy-to-remember bookmarkable URLs all redirect you to the home page:
* [bathtubBDD.dev](https://bathtubbdd.dev)
* [bathtubBDD.com](http://bathtubbdd.com)
* [bathtubBDD.org](http://bathtubbdd.org)

but you will note from the true URL in your web browser that these pages are served from GitHub.

[Issues](https://github.com/williaml33moore/bathtub/issues)
: These are for tracking tasks and reporting bugs.

[Discussions](https://github.com/williaml33moore/bathtub/discussions)
: This is currently the preferred forum for community conversation about Bathtub. It requires a free GitHub account. You can sign up [here](https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fdiscussions%2Findex&source=header-repo&source_repo=williaml33moore%2Fbathtub_).

[Wiki](https://github.com/williaml33moore/bathtub/wiki)
: For technical documentation and user guides.

Bathtub is a new work-in-progress so everything is incomplete today, but continuously improving.

B.A.T.H.T.U.B.:
- BDD
Expand All @@ -11,15 +46,3 @@ B.A.T.H.T.U.B.:
- Understand
- Behavior

Hello and welcome!

Bathtub was introduced in my paper "Gherkin Implementation In SystemVerilog Brings Agile Behavior-Driven Development To UVM" at [DVCon U.S. 2024](https://2024.dvcon.org).
The source code is all here and it works.
However this repo is not very user friendly at this time.
I plan to keep developing it to complete a few features that I intentionally broke when I rewrote the parser, to add user documentation, and to provide more examples.

In the meantime, feel free to have a look at [bathtub_test_paper](https://edaplayground.com/x/Prp5) on EDA Playground.
Its purpose is to validate the code samples in the paper.

I promise, there is more to come!
Stay tuned and stay in touch.
6 changes: 3 additions & 3 deletions bathtub_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

`include "uvm_macros.svh"
`include "bathtub_macros.sv"

`define push_onto_parser_stack(o) parser_stack.push_front(o);
Expand All @@ -45,7 +46,6 @@ package bathtub_pkg;
// ===================================================================

import uvm_pkg::*;
import meta_pkg::*;


typedef enum {Given, When, Then, And, But, \* } step_keyword_t;
Expand All @@ -61,7 +61,7 @@ package bathtub_pkg;
// ===================================================================
static function bit split_string;
// ===================================================================
`FUNCTION_METADATA('{
/* `FUNCTION_METADATA('{
description:
"Take a string containing tokens separated by white space, split the tokens, and return them in the supplied SystemVerilog queue.",
Expand All @@ -74,7 +74,7 @@ package bathtub_pkg;
string: ""
})

*/
// Parameters:

input string str; // Incoming string of white space-separated tokans
Expand Down
20 changes: 20 additions & 0 deletions examples/alu_division/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
https://edaplayground.com/x/Prp5

Bathtub is described in my paper "Gherkin Implementation in SystemVerilog Brings Agile Behavior-Driven Development to UVM" at DVCon U.S. 2024.
The purpose of this playground is to validate the code samples included in the paper.
It's a very minimal test bench for a simple ALU.
The test runs and passes.

File `testbench.sv` is just that, the test bench.
It contains:
* Test class `bathtub_test` as included in the paper
* Environment class `alu_env` as briefly described in the paper
* Virtual sequencer `alu_sequencer` as mentioned in the paper
* Virtual sequence base class `alu_base_vsequence` as mentioned in the paper

File `alu_division.feature` is the Gherkin feature file featured in the paper.

File `alu_step_definition.svh` contains all four step definitions required for the feature file, two of which are included in the paper.

---
https://github.com/williaml33moore/bathtub/commit/c666c807ca38988f1370ca9f4591161104a126a0
17 changes: 17 additions & 0 deletions examples/alu_division/result/alu_division.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This Gherkin feature file's name is alu_division.feature

Feature: Arithmetic Logic Unit division operations

The arithmetic logic unit performs integer division.

Scenario: In integer division, the remainder is discarded
Given operand A is 15 and operand B is 4
When the ALU performs the division operation
Then the result should be 3
And the DIV_BY_ZERO flag should be clear

Scenario: Attempting to divide by zero results in an error
Given operand A is 10 and operand B is 0
When the ALU performs the division operation
Then the DIV_BY_ZERO flag should be raised

86 changes: 86 additions & 0 deletions examples/alu_division/result/alu_step_definition.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
`ifndef ALU_STEP_DEFINITION_SVH
`define ALU_STEP_DEFINITION_SVH

import bathtub_pkg::*;

class set_operand_A_and_B_vseq extends alu_base_vsequence implements bathtub_pkg::step_definition_interface;
`Given("operand A is %d and operand B is %d")
int operand_A, operand_B;
`uvm_object_utils(set_operand_A_and_B_vseq)
function new (string name="set_operand_A_and_B_vseq");
super.new(name);
endfunction : new
virtual task body();
// Extract the parameters
`step_parameter_get_args_begin()
operand_A = `step_parameter_get_next_arg_as(int);
operand_B = `step_parameter_get_next_arg_as(int);
`step_parameter_get_args_end
// Do the actual work using the API in the base sequence
super.set_operand_A(operand_A);
super.set_operand_B(operand_B);
endtask : body
endclass : set_operand_A_and_B_vseq



class check_DIV_BY_ZERO_flag_vseq extends alu_base_vsequence implements bathtub_pkg::step_definition_interface;
`Then("the DIV_BY_ZERO flag should be %s")
string flag_arg;
bit expected_flag;
bit actual_flag;
`uvm_object_utils(check_DIV_BY_ZERO_flag_vseq)
function new (string name="check_DIV_BY_ZERO_flag_vseq");
super.new(name);
endfunction : new
virtual task body();
// Extract the parameter
`step_parameter_get_args_begin()
flag_arg = `step_parameter_get_next_arg_as(string);
`step_parameter_get_args_end
case (flag_arg) // convert the string to a bit value
"raised", "asserted" : expected_flag = 1;
"clear", "deasserted" : expected_flag = 0;
default: `uvm_error("UNEXPECTED ARG", flag_arg)
endcase
super.get_div_by_zero_flag(actual_flag);
check_DIV_BY_ZERO_flag : assert (expected_flag === actual_flag) else
`uvm_error("MISMATCH", $sformatf("expected %b; actual %b", expected_flag, actual_flag))
endtask : body
endclass : check_DIV_BY_ZERO_flag_vseq


class do_division_operation_vseq extends alu_base_vsequence implements bathtub_pkg::step_definition_interface;
`When("the ALU performs the division operation")
int operation;
`uvm_object_utils(do_division_operation_vseq)
function new (string name="do_division_operation_vseq");
super.new(name);
endfunction : new
virtual task body();
operation = DIVIDE;
super.do_operation(operation);
endtask : body
endclass : do_division_operation_vseq


class check_result_vseq extends alu_base_vsequence implements bathtub_pkg::step_definition_interface;
`Then("the result should be %d")
int expected_result;
int actual_result;
`uvm_object_utils(check_result_vseq)
function new (string name="check_DIV_BY_ZERO_flag_vseq");
super.new(name);
endfunction : new
virtual task body();
// Extract the parameter
`step_parameter_get_args_begin()
expected_result = `step_parameter_get_next_arg_as(int);
`step_parameter_get_args_end
super.get_result(actual_result);
check_result : assert (expected_result === actual_result) else
`uvm_error("MISMATCH", $sformatf("expected %0d; actual %0d", expected_result, actual_result))
endtask : body
endclass : check_result_vseq

`endif // ALU_STEP_DEFINITION_SVH
100 changes: 100 additions & 0 deletions examples/alu_division/result/bathtub_macros.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

`ifndef BATHTUB_MACROS_SV
`define BATHTUB_MACROS_SV

`ifdef EDAPG
`undef BATHTUB__MULTILINE_MACRO_IS_OK
`else
`define BATHTUB__MULTILINE_MACRO_IS_OK
`endif

`define Given(e) `__register_step_def(bathtub_pkg::Given, e)

`define When(e) `__register_step_def(bathtub_pkg::When, e)

`define Then(e) `__register_step_def(bathtub_pkg::Then, e)

`ifdef BATHTUB__MULTILINE_MACRO_IS_OK

`define __register_step_def(k, e) static bathtub_pkg::step_static_attributes_interface __step_static_attributes = bathtub_pkg::step_nature::register_step(k, e, get_type());\
bathtub_pkg::step_attributes_interface __step_attributes;\
virtual function bathtub_pkg::step_static_attributes_interface get_step_static_attributes();\
return __step_static_attributes;\
endfunction : get_step_static_attributes\
virtual function bathtub_pkg::step_attributes_interface get_step_attributes();\
return __step_attributes;\
endfunction : get_step_attributes\
virtual function void set_step_attributes(bathtub_pkg::step_attributes_interface step_attributes);\
this.__step_attributes = step_attributes;\
endfunction : set_step_attributes\
virtual function bathtub_pkg::feature_sequence_interface get_current_feature_sequence();\
return this.__step_attributes.get_current_feature_sequence();\
endfunction : get_current_feature_sequence\
virtual function void set_current_feature_sequence(bathtub_pkg::feature_sequence_interface seq);\
this.__step_attributes.set_current_feature_sequence(seq);\
endfunction : set_current_feature_sequence\
virtual function bathtub_pkg::scenario_sequence_interface get_current_scenario_sequence();\
return this.__step_attributes.get_current_scenario_sequence();\
endfunction : get_current_scenario_sequence\
virtual function void set_current_scenario_sequence(bathtub_pkg::scenario_sequence_interface seq);\
this.__step_attributes.set_current_scenario_sequence(seq);\
endfunction : set_current_scenario_sequence

`else // BATHTUB__MULTILINE_MACRO_IS_OK
`define __register_step_def(k, e) static bathtub_pkg::step_static_attributes_interface __step_static_attributes = bathtub_pkg::step_nature::register_step(k, e, get_type()); bathtub_pkg::step_attributes_interface __step_attributes; virtual function bathtub_pkg::step_static_attributes_interface get_step_static_attributes(); return __step_static_attributes; endfunction : get_step_static_attributes virtual function bathtub_pkg::step_attributes_interface get_step_attributes(); return __step_attributes; endfunction : get_step_attributes virtual function void set_step_attributes(bathtub_pkg::step_attributes_interface step_attributes); this.__step_attributes = step_attributes; endfunction : set_step_attributes virtual function bathtub_pkg::feature_sequence_interface get_current_feature_sequence(); return this.__step_attributes.get_current_feature_sequence(); endfunction : get_current_feature_sequence virtual function void set_current_feature_sequence(bathtub_pkg::feature_sequence_interface seq); this.__step_attributes.set_current_feature_sequence(seq); endfunction : set_current_feature_sequence virtual function bathtub_pkg::scenario_sequence_interface get_current_scenario_sequence(); return this.__step_attributes.get_current_scenario_sequence(); endfunction : get_current_scenario_sequence virtual function void set_current_scenario_sequence(bathtub_pkg::scenario_sequence_interface seq); this.__step_attributes.set_current_scenario_sequence(seq); endfunction : set_current_scenario_sequence
`endif // BATHTUB__MULTILINE_MACRO_IS_OK

`ifdef BATHTUB__MULTILINE_MACRO_IS_OK

`define virtual_step_definition(e) virtual function bathtub_pkg::step_static_attributes_interface get_step_static_attributes();\
return null;\
endfunction : get_step_static_attributes\
virtual function bathtub_pkg::step_attributes_interface get_step_attributes();\
return null;\
endfunction : get_step_attributes\
virtual function void set_step_attributes(bathtub_pkg::step_attributes_interface step_attributes);\
endfunction : set_step_attributes\
virtual function bathtub_pkg::feature_sequence_interface get_current_feature_sequence();\
return null;\
endfunction : get_current_feature_sequence\
virtual function void set_current_feature_sequence(bathtub_pkg::feature_sequence_interface seq);\
endfunction : set_current_feature_sequence\
virtual function bathtub_pkg::scenario_sequence_interface get_current_scenario_sequence();\
return null;\
endfunction : get_current_scenario_sequence\
virtual function void set_current_scenario_sequence(bathtub_pkg::scenario_sequence_interface seq);\
endfunction : set_current_scenario_sequence

`else // BATHTUB__MULTILINE_MACRO_IS_OK
`define virtual_step_definition(e) virtual function bathtub_pkg::step_static_attributes_interface get_step_static_attributes(); return null; endfunction : get_step_static_attributes virtual function bathtub_pkg::step_attributes_interface get_step_attributes(); return null; endfunction : get_step_attributes virtual function void set_step_attributes(bathtub_pkg::step_attributes_interface step_attributes); endfunction : set_step_attributes virtual function bathtub_pkg::feature_sequence_interface get_current_feature_sequence(); return null; endfunction : get_current_feature_sequence virtual function void set_current_feature_sequence(bathtub_pkg::feature_sequence_interface seq); endfunction : set_current_feature_sequence virtual function bathtub_pkg::scenario_sequence_interface get_current_scenario_sequence(); return null; endfunction : get_current_scenario_sequence virtual function void set_current_scenario_sequence(bathtub_pkg::scenario_sequence_interface seq); endfunction : set_current_scenario_sequence
`endif // BATHTUB__MULTILINE_MACRO_IS_OK



`ifdef BATHTUB__MULTILINE_MACRO_IS_OK

`define step_parameter_get_args_begin(f=get_step_attributes().get_expression())\
begin : step_parameter_get_args\
step_parameters __step_params;\
int __next = 0;\
__step_params = step_parameters::create_new("__step_params", get_step_attributes().get_text(), f);

`else // BATHTUB__MULTILINE_MACRO_IS_OK
`define step_parameter_get_args_begin(f=get_step_attributes().get_expression()) begin : step_parameter_get_args step_parameters __step_params; int __next = 0; __step_params = step_parameters::create_new("__step_params", get_step_attributes().get_text(), f);
`endif // BATHTUB__MULTILINE_MACRO_IS_OK

`define step_parameter_get_arg_object(i) __step_params.get_arg(i)

`define step_parameter_get_arg_as(i, t) `step_parameter_get_arg_object(i).as_``t()

`define step_parameter_get_next_arg_object `step_parameter_get_arg_object(__next++)

`define step_parameter_get_next_arg_as(t) `step_parameter_get_next_arg_object.as_``t()

`define step_parameter_num_args __step_params.num_args()

`define step_parameter_get_args_end end : step_parameter_get_args

`define get_scope_name(d) ($sformatf("%m")) // TODO - Extract the last segment

`endif // BATHTUB_MACROS_SV
Loading

0 comments on commit 83f4be7

Please sign in to comment.