When implementing Verilog tasks in modules, the best approach is to group tasks that have the same output signals into separate modules. If different modules control the same signal, then explicit arbitration logic is required to specify which module is controlling the signal at a given time. To put tasks in a separate module, you will require start and completion handshaking signals.
Behavioural:
task update_a_and_b;
begin
a = y;
wait (z != 0) b = z;
end
endtask
...
y = x + w;
update_a_and_b; // calls the task
x = a + b;
@(posedge clock);
x = b;
if (y == x) update_a_and_b;
@(posedge clock);
command1;
Care must be taken when translating this into synthesizable Verilog, to preserve correct timing:
module update_a_and_b(do_update_a_and_b, clock, reset, a, z, done_update_a_and_b, b_temp, x_temp);
input do_update_a_and_b; // this signal should go high for only one clock cycle
input clock;
input reset; // reset this module when reset goes high
input [7:0] a;
input [7:0] z;
output done_update_a_and_b;
output [7:0] b_temp;
output [7:0] x_temp;
reg done_update_a_and_b;
reg [7:0] b_temp;
reg [7:0] x_temp;
reg state;
always @(posedge refclk or posedge reset)
if (reset)
begin
state <= 0;
done_update_a_and_b = 0;
b_temp = 0;
x_temp = 0;
end
else if (do_update_a_and_b && (state == 0))
begin
done_update_a_and_b = 0;
if (z != 0)
begin
b_temp = z;
x_temp = a+b_temp; // x value is update inside this module, as it must occur immediately when z != 0
done_update_a_and_b = 1;
// stay in state 0, we've finished
end
else state <= 1;
end
else
begin
case (state)
0 : done_update_a_and_b = 0; // do nothing, this is the idle state
1 : if (z != 0)
begin
b_temp = z;
x_temp = a+b_temp; // x value is update inside this module, as it must occur immediately when z != 0
done_update_a_and_b = 1;
state <= 0;
// stay in state 0, we've finished
end
endcase
end
endmodule
...
reg [2:0] top_state;
...
case (top_state)
0 : begin
y = x + w;
a = y; // this must happen immediately
if (z != 0) // we have to update x and b immediately if z != 0
begin
b = z;
x = a + b;
top_state <= 2;
end
else
begin
do_update_a_and_b = 1; // call the task
top_state <= 1;
end
end
1 : begin
do_update_a_and_b = 0; // stays high for only one cycle
if (done_update_a_and_b)
begin
// we assume the values of x and b weren't needed on the previous cycle, otherwise additional circuitry is needed
// or x_temp and b_temp values need to be used on that cycle - it's very difficult to coordinate this correctly
// in the general case
x = x_temp;
b = b_temp;
top_state <= 2;
end
end
2 : begin
x = b;
if (y == x)
begin
a = y; // this must happen immediately
if (z != 0) // we have to update x and b immediately if z != 0
begin
b = z;
x = a + b;
top_state <= 4;
end
else
begin
do_update_a_and_b = 1; // call the task
top_state <= 3;
end
end
else top_state <= 4;
end
3 : begin
do_update_a_and_b = 0; // stays high for only one cycle
if (done_update_a_and_b)
begin
// we assume the values of x and b weren't needed on the previous cycle, otherwise additional circuitry is needed
// or x_temp and b_temp values need to be used on that cycle - it's very difficult to coordinate this correctly
// in the general case
x = x_temp;
b = b_temp;
top_state <= 4;
end
end
4: command1;
endcase
Now if we didn't care about having a couple of additional cycle delays between updates (i.e. assuming nothing depends on the variable values immediately, and nothing else is changing variable values), we could implement this in a far simpler fashion:
module update_a_and_b(do_update_a_and_b, clock, reset, a, z, done_update_and_b, b_temp, x_temp);
input do_update_a_and_b; // this signal should go high for only one clock cycle
input clock;
input reset; // reset this module when reset goes high
input [7:0] a;
input [7:0] z;
output done_update_and_b;
output [7:0] b_temp;
output [7:0] x_temp;
reg done_update_and_b;
reg [7:0] b_temp;
reg [7:0] x_temp;
reg state;
always @(posedge refclk or posedge reset)
if (reset)
begin
state <= 0;
done_update_and_b = 0;
b_temp = 0;
x_temp = 0;
end
else if (do_update_a_and_b && (state == 0))
begin
state <= 1;
done_update_a_and_b = 0;
end
else
begin
case (state)
0 : done_update_and_b = 0; // do nothing, this is the idle state
1 : if (z != 0)
begin
b_temp = z;
x_temp = a+b_temp; // x value is update inside this module, as it must occur immediately when z != 0
done_update_and_b = 1;
state <= 0;
// stay in state 0, we've finished
end
endcase
end
endmodule
...
reg [2:0] top_state;
...
case (top_state)
0 : begin
y = x + w;
do_update_a_and_b = 1; // call the task
top_state <= 1;
end
1 : begin
do_update_a_and_b = 0; // stays high for only one cycle
if (done_update_and_b)
begin
x = x_temp;
b = b_temp;
top_state <= 2;
end
end
2 : begin
x = b;
if (y == x)
begin
do_update_a_and_b = 1; // call the task
top_state <= 3;
end
else top_state <= 4;
end
3 : begin
do_update_a_and_b = 0; // stays high for only one cycle
if (done_update_and_b)
begin
x = x_temp;
b = b_temp;
top_state <= 4;
end
end
4: command1;
endcase
Note: in general commandi refers to a block of commands. It is assumed there is an
appropriate clock for the case statement state machines.
Care is required in setting appropriate reset states, initialization, and completion of use of a
state machine:
o Is there a signal to tell the state machine to begin?
o Does a done signal go high, signalling the state machine has finished?
o When it is not in operation, does the state machine idle correctly? Does it change signal values shared with other code? Does it set outputs from it to appropriate idling values?
o Is the state machine reset to the idle state by a reset signal?
o Ensure that you initialize all registers.
o Ensure that your state register has the correct bit width - if it is too small, assigning a larger state value will just return it to an earlier state.
Synthesizable Verilog from behavioral constructs - 4
Written by
Tuesday, December 13, 2005
0
Tags:
Your comments will be moderated before it can appear here. Win prizes for being an engaged reader.