Monday, April 28, 2008

Synthesizable Verilog from behavioral constructs - 4

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.

No comments:

Archive