---
title: '`task` in Verilog'
...

# About the `task` Keyword

A `task` is a group of statements that would usually appear in an `always` block.
If we need to repeat the same statements multiple times, it makes sense to define them
as a `task`. Common examples include simple data format conversions, such as reversing
the bit order in a vector. Say we have an 8-bit vector `v`. In some specifications, 
the Least Significant Bit (LSB) is on the "right", i.e. v[0]. In other specifications,
the LSB is on the "left", i.e. v[7]. 

For some project, we may need to reverse the bit order of several vectors. This particularly 
occurs with filesystem interfaces and data communication channels. Do do this, we could use
`for` loops, as in this code snippet:

```
input      [7:0] a, b;
output reg [7:0] q, w;

integer idx; // Loop Index

always @(a,b) begin
   for (idx=0; idx<8; idx=idx+1) begin
      q[7-idx] = a[idx];
      w[7-idx] = b[idx];
   end   
end
```

This seems simple enough, but if we need to do this for many different signals, in
multiple modules, it would make sense to group these lines into a simpler reusable
statement. To accomplish this, we put the loop into a `task` like this:

```
task automatic reverse_bits_task;
   input   [7:0]  in;
   output  [7:0]  out;

   integer          idx;
   
   begin
      for (idx=0; idx<8; idx=idx+1) begin
         out[7-idx] = in[idx];
      end
   end
endtask
```

Then to use the `task`, we reference it within an `always` block like this:

```
always @(a,b) begin
   reverse_bits_task(.in(a),.out(q));
   reverse_bits_task(.in(b),.out(w));
end
```

The `task` definition has to appear inside of the module. The `automatic`
keyword is usually required; it instructs the simulator to allocate fresh
memory every time the task is evaluated. The default non-`automatic` 
behavior has fewer applications and can create subtle simulation bugs.

So almost always **your tasks should be automatic**. 

# Assigned Tasks

## Tasks in Testbenches

Tasks probably get the most use in testbenches. We'll start with a simple
example that simulates the `reverse_bits_task` shown above. In a text editor,
open the file `src/testbench.v`. At the bottom of the testbench module, 
place the definition for `reverse_bits_task`. Find the `always` block that
assigns `q` and `w`, and change it from `for` loops to task invocations. 
You should remove or comment out the declaration of `idx` since it is now
defined within the task.

Type `make` to run the simulation. You should see several lines of output
reporting a->q and b->w. Verify that the bits are correctly reversed in 
each case.


## Tasks in Include Files

A task can be reused in a variety of different modules and testbenches.
To facilitate this, Verilog has a ``include` directive that imports
lines from external files. You can use this to organize `reverse_bits_task`
in its own file. 

Move the `reverse_bits_task` definition into a file called `inc/reverse_bits_task.v`
(remove or comment the task definition in `testbench.v`). Then, in the testbench, 
add the include directive from inside the module:

```
`include "inc/reverse_bits_task.v"
```

Run the test again, you should get the same result.


## Tasks in Modules

Next, remove or comment the ``include` line in the `testbench.v`.
Create a new module file named `src/reverse_bits_module.v`, to
act as a "wrapper" for the task:

```
module reverse_bits_module
  (
   input [7:0]      a,
   input [7:0]      b,
   output reg [7:0] q,
   output reg [7:0] w
   );

`include "inc/reverse_bits_task.v"
   
   always @(a,b) begin
      reverse_bits_task(a,q);
      reverse_bits_task(b,w);
   end
   
endmodule
```

In the testbench, remove or comment the lines that reference the
`reverse_bits_task`, and make an instance of `reverse_bits_module`.
You will need to change `q` and `w` to `wire` types.


## Implement `reverse_bits_module`

A task is **synthesizable** if it

1. Contains only synthesizeable statements, and  
2. Is invoked within a synthesizeable module.  

Examples of **non-synthesizeable** statements include:

* Asynchronous time delays using `#N` syntax  
* System tasks like `$display` or `$random`  
* Complex math like division or trigonometric functions  
* Real-valued variables and operations  

To be synthesizeable, a module needs inputs and outputs, and 
should be fully defined by synthesizeable statements; 
non-synthesizeable lines can exist for simulation purposes,
but they are ignored during synthesis.

Fortunately for our `task`, a `for` loop is synthesizeable.
With `reverse_bits_module` serving as a top module, we can synthesize
and verify the design on the Basys3 board. Open `reverse_bits.xdc`
and examine the constraints.  Input vector `a` is mapped
to switches 0--7, and input vector `b` is mapped to switches 8--15.
Output vector `q` is mapped to LEDs 0--7, and `w` is mapped to 8--15.
Also notice that there is no clock in this design.

Run `make implement` to build the design. Since there is no clock or
other timing constraints in the design, the timing report is meaningless.
The utilization report is more interesting. Open it and notice that there
is no logic utilization -- zero. The `reverse_bits_task` is implemented
entirely in **routing**, i.e. switch configurations. 

If successful, the build process should create `reverse_bits.bit`. 
Program the Basys3 board with this bitstream and verify that the
left/right 8-bit groups are reversed. Verify and photograph the 
following cases:

* Case 1: `a=00010011`, `b=01010101`
* Case 2: `a=11101100`, `b=01101001`

Place photos of the two cases in your working directory, and
name them `case1` and `case2` (with the appropriate graphics file 
extension).


## Turn in Your Work

To complete this assignment, commit and push your results to
the repository, and indicate on Canvas that it is complete:

```bash
git add *.bit *.rpt *.txt inc/ src/*
git commit . -m "Complete."
git push origin main
```


