pklink
Newbie level 4
Hi everyone,
I'm currently working on a project for my studies in which I have to implement an elevator control. The modules and it's connections between each other are given and we only have to implement the functionality of the modules.
In the lectures, we were told that we should use non-blocking assignments (for example: a <= 3'b0 within always blocks if we want to write code for a FSM. While I was writing the first two modules (they are used for the motor steering), I recognized that I would not have any clue how to implement the modules without using blocking assignments in the always block while the Module is still a FSM. So I wanted to ask whether this rule to use non-blocking assignments for FSMs is just not completely true or if I should alter my module. Beneath is the code for one module (just to clarify: I'm not asking to verify the code or anything else The code should just give you a little overview about the model):
Thanks,
Pascal
I'm currently working on a project for my studies in which I have to implement an elevator control. The modules and it's connections between each other are given and we only have to implement the functionality of the modules.
In the lectures, we were told that we should use non-blocking assignments (for example: a <= 3'b0 within always blocks if we want to write code for a FSM. While I was writing the first two modules (they are used for the motor steering), I recognized that I would not have any clue how to implement the modules without using blocking assignments in the always block while the Module is still a FSM. So I wanted to ask whether this rule to use non-blocking assignments for FSMs is just not completely true or if I should alter my module. Beneath is the code for one module (just to clarify: I'm not asking to verify the code or anything else The code should just give you a little overview about the model):
Code:
[syntax=verilog]
`timescale 1ns / 1ns
module motor_if
#(parameter INITIAL_POSITION = 0, // inital position is at floor 0
MOTOR_STEP = 50, // [m] (a single motor step is 50m = 0,05mm)
CONTROL_STEP = 10, // [mm] (a control step is 10mm = 1cm)
DISTANCE_BITS = 14, // max distance 12m = 12.000 mm
CONTROL_STEP_BITS = 11, // 12m <> 1.200 control steps
MOTOR_STEP_BITS = 18, // max distance 12m = 240.000 * 50m
MOTOR_STEP_BITS2 = 18, // max distance 12m = 240.000 * 50m
MAX_POSITION_R = 1200, // [control steps] (1.200 control steps = 240.000 motor steps = 12 m)
MAX_POSITION_L = 0, // [control steps] (0 control steps = 0 motor steps = 0 m)
DELAY_COUNT_BITS = 17, // 17 bits required to count up to C_0 = 100.000
C_0 = 100000, // initial counter value <> 100 Hz @ 10 MHz clock
C_MIN = 250) // minimum counter value <> 40 kHz @ 10 MHz clock
(input wire CLK,
input wire RESET,
input wire [DISTANCE_BITS-1 :0] DISTANCE, // levels [mm] / door_width [mm]
input wire ROTATE_LEFT, // open or down
input wire ROTATE_RIGHT, // close or up
output reg CTRL_STEP_DONE, // 10mm distance is a step
output reg DONE, // motor request executed
output wire A, // motor coil a output
output wire B, // motor coil a output
output wire C, // motor coil a output
output wire D); // motor coil a output
/* =============================INSERT CODE HERE======================================*/
//Test related regs and wires
reg [DELAY_COUNT_BITS -1:0] actual_delay_rounded;
//The number of motor steps which make one control step
parameter steps_per_ctrl = CONTROL_STEP * 1000 / MOTOR_STEP;
//Register to save motor steps needed for the current command
reg [DISTANCE_BITS + 9:0] motor_steps_command;
//Register to save motor steps needed for the acceleration and deceleration
reg [DISTANCE_BITS + 8:0] motor_steps_accel, motor_steps_decel;
/*
* Register for storing the current number of clocks to wait before making a motor step.
* The value is stored in a DELAY_COUNT_BITS.16 fixed point format
*/
reg [(DELAY_COUNT_BITS + 16) - 1:0] actual_delay;
/*
* Register for storing the calculation result of the next delay. The calculation result
* is 2 * (DELAY_COUNT_BITS + 16) bit wide, that's why the result can't be directly
* assigned to actual_delay
*/
reg [(DELAY_COUNT_BITS + 16) + (16 + 16) - 1:0] actual_delay_tmp;
/*
* Register for storing the number of clock waited since the last calculation of
* actual_delay
*/
reg [DELAY_COUNT_BITS - 1:0] delay_count;
/*
* Registers storing the direction (LEFT, RIGHT) of a command, the emergency halt signal.
* The remaining registers are helpers for converting wires to registers
*/
reg direction, halt, next_root, previous_root, a_tmp, b_tmp, c_tmp, d_tmp;
//Wires for controlling the sqrt module (instantiated below)
wire next_root_tmp, previous_root_tmp;
//Wire for the done signal of the sqrt module
wire root_finished;
//Wire for the result of the sqrt module
wire [31:0] root_res;
/*
* Register for storing the current root before calculating the next root, since both roots
* are needed for the calculation of the next delay
*/
reg [31:0] interval_root;
/*
* Registers for storing the motor steps done in one command and the total
* motor steps done since the reset (the total motor steps consider the direction of a
* command). The total motor steps are used to actually steer the motor (using outputs
* A, B, C and D)
*/
reg [MOTOR_STEP_BITS - 1:0] total_motor_steps, current_motor_steps;
/*
* Register for storing the position of the elevator or the door (is used to check whether
* a command exceeds the maximum or minimum position of the elevator or door)
*/
reg [CONTROL_STEP_BITS - 1:0] position;
//Register for storing the intern state of the module
reg [1:0] state;
//Conversion between wires and regs
assign next_root_tmp = next_root;
assign previous_root_tmp = previous_root;
assign A = a_tmp;
assign B = b_tmp;
assign C = c_tmp;
assign D = d_tmp;
//Instantiation of the sqrt module used to calculates the square roots
sqrt root(CLK, RESET, next_root_tmp, previous_root_tmp, root_finished, root_res);
//State and combinational logic
always @ (posedge CLK) begin
if(CTRL_STEP_DONE) CTRL_STEP_DONE = 0;
if(next_root) next_root = 0;
if(previous_root) previous_root = 0;
//Reset the whole module
if(RESET) begin
CTRL_STEP_DONE = 0;
DONE = 1;
motor_steps_command = 0;
motor_steps_accel = 0;
motor_steps_decel = 0;
delay_count = 0;
actual_delay = {C_0, 16'b0};
direction = 0;
halt = 0;
next_root = 0;
previous_root = 0;
interval_root = root_res;
total_motor_steps = INITIAL_POSITION * steps_per_ctrl;
current_motor_steps = 0;
position = INITIAL_POSITION;
state = 0;
end
//Emergency halt the motor
else if(ROTATE_LEFT & ROTATE_RIGHT) begin
halt = 1;
end
else begin
//Check whether the module is currently within a command (!DONE) or not (DONE)
if(DONE) begin
//If new commando is given, take the given values and start
if(ROTATE_LEFT | ROTATE_RIGHT) begin
if(ROTATE_LEFT ? position - DISTANCE / CONTROL_STEP >= MAX_POSITION_L : position + DISTANCE / CONTROL_STEP <= MAX_POSITION_R) begin
delay_count = 0;
actual_delay = {C_0, 16'b0};
direction = ROTATE_LEFT ? 0 : 1;
current_motor_steps = 0;
motor_steps_command = (DISTANCE * 1000) / MOTOR_STEP;
if(motor_steps_command[0]) begin
motor_steps_accel = (motor_steps_command - 1) / 2;
end
else begin
motor_steps_accel = motor_steps_command / 2;
end
state = 0;
DONE = 0;
end
end
//If no new commando is given, do nothing
end
else begin
/*
* Check if distance has to be updated (can only be updated along the current
* direction). It's not a problem that this block is not only run through during
* updates, since the distance cannot change is a single clock? Maybe Issue
*/
if((ROTATE_LEFT & !direction) | (ROTATE_RIGHT & direction)) begin
motor_steps_command = (DISTANCE * 1000) / MOTOR_STEP;
if(motor_steps_command[0]) begin
motor_steps_accel = (motor_steps_command - 1) / 2;
end
else begin
motor_steps_accel = motor_steps_command / 2;
end
end
//Do calculations (only if motor is not halted)
if(!halt) begin
//Check in which state the module is currently
case (state)
/*
* State 0: Check if target distance if greater than 0, if yes, change to
* state 1 and pre-calculate next root
*/
0: begin
if(motor_steps_accel > 0) begin
state = 1;
/*interval_root = root_res;
next_root = 1;*/
end
else begin
state = 3;
end
end
/*
* State 1: Accelerate motor -> check if enough clocks have been waited,
* if yes, calculate new interval, store current root, calculate
* next root, do a motor step and update the total motor steps, if
* not, the increase delay count
* If the half of target distance is travelled, change to state 3
* and calculate the previous root
* If the actual delay is smaller the C_MIN change to state 2 and
* calculate the previous root
*/
1: begin
if(delay_count == actual_delay[DELAY_COUNT_BITS + 15:16]) begin
delay_count = 1;
//Do motor step (current and total)
current_motor_steps = current_motor_steps + 1;
total_motor_steps = direction ? total_motor_steps + 1 : total_motor_steps - 1;
if(actual_delay <= C_MIN) begin
motor_steps_decel = current_motor_steps;
state = 2;
end
else if(current_motor_steps == motor_steps_accel) begin
if(!motor_steps_command[0]) begin
interval_root = root_res;
previous_root = 1;
state = 3;
end
end
else if(current_motor_steps == motor_steps_accel + 1) begin
interval_root = root_res;
previous_root = 1;
state = 3;
end
else begin
interval_root = root_res;
next_root = 1;
end
end
else begin
delay_count = delay_count + 1;
end
end
/*
* State 2: Travel with full speed -> check if enough clocks have been
* waited, if yes, do a motor step and update the total motor
* steps
* If the acceleration distance is left of target distance,
* calculate the new delay and the previous root
*/
2: begin
if(delay_count == C_MIN) begin
delay_count = 1;
//Do motor step (current and total)
current_motor_steps = current_motor_steps + 1;
total_motor_steps = direction ? total_motor_steps + 1 : total_motor_steps - 1;
end
if(motor_steps_command - current_motor_steps == motor_steps_decel) begin
interval_root = root_res;
previous_root = 1;
state = 3;
end
end
/*
* State 3: Decelerate motor -> like if state 1, but calculating the
* previous root instead of the next root
* If the total distance was travelled, change to state 0 and set
* DONE to 1
*/
3: begin
if(delay_count == actual_delay[DELAY_COUNT_BITS + 15:16]) begin
delay_count = 1;
interval_root = root_res;
//Do motor step (current and total)
current_motor_steps = current_motor_steps + 1;
total_motor_steps = direction ? total_motor_steps + 1 : total_motor_steps - 1;
if(current_motor_steps == motor_steps_command) begin
$stop;
state = 0;
if(root_res == 0) begin
next_root = 1;
end
DONE = 1;
end
else begin
previous_root = 1;
end
end
else begin
delay_count = delay_count + 1;
end
end
endcase
end
end
end
end
//Increase current_distance and position (total_distance) according to motor_steps
always @ (current_motor_steps) begin
if(state != 0 & current_motor_steps > 0 & current_motor_steps % steps_per_ctrl == 0) begin
position = direction ? position + 1 : position - 1;
CTRL_STEP_DONE = 1;
end
end
/*
* Take the relevant bits (DELAY_COUNT_BITS integer bits, 16 fractional bits) from the
* calculation of the next delay stored in actual_delay_tmp
*/
always @ (*) begin
actual_delay = actual_delay_tmp[(DELAY_COUNT_BITS + 15) + 16:16];
end
/*
* Calculate actual_delay
*/
always @ (*) begin
if(current_motor_steps > 0 & root_finished) begin
if(state == 1) begin
actual_delay_tmp = {C_0[DELAY_COUNT_BITS - 1:0], 16'b0} * (root_res - interval_root);
end
else if(state == 3) begin
actual_delay_tmp = {C_0[DELAY_COUNT_BITS - 1:0], 16'b0} * (interval_root - root_res);
end
end
end
//Test related mappings
always @ (*) begin
actual_delay_rounded = actual_delay[(DELAY_COUNT_BITS +15):16];
end
//Mapping between motor_steps and the motor signals
always @ (*) begin
case (total_motor_steps % 4)
0: {a_tmp, b_tmp, c_tmp, d_tmp} <= 4'b1001;
1: {a_tmp, b_tmp, c_tmp, d_tmp} <= 4'b1100;
2: {a_tmp, b_tmp, c_tmp, d_tmp} <= 4'b0110;
3: {a_tmp, b_tmp, c_tmp, d_tmp} <= 4'b0011;
endcase
end
/* ====================================================================================*/
endmodule
[/syntax]
Thanks,
Pascal