blob: db4028520d65ccb2d4a1d69e8e00311709a69cb2 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: 2022 , Julien OURY
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileContributor: Created by Julien OURY <julien.oury@outlook.fr>
//
////////////////////////////////////////////////////////////////////////////
module step_motor_controller #(
parameter PSIZE = 32 , // Size of prescaler counter(bits)
parameter DSIZE = 32 // Size of delay counter (bits)
)(
input wire rst_n , // Asynchronous reset (active low)
input wire clk , // Clock (rising edge)
// Wishbone bus
input wire wbs_cyc_i , // Wishbone strobe/request
input wire wbs_stb_i , // Wishbone strobe/request
input wire [31:0] wbs_adr_i , // Wishbone address
input wire wbs_we_i , // Wishbone write (1:write, 0:read)
input wire [31:0] wbs_dat_i , // Wishbone data output
input wire [3:0] wbs_sel_i , // Wishbone byte enable
output wire [31:0] wbs_dat_o , // Wishbone data input
output wire wbs_ack_o , // Wishbone acknowlegement
// Motor outputs
output wire motor_a1 , // A1 moto output
output wire motor_a2 , // A2 moto output
output wire motor_b1 , // B1 moto output
output wire motor_b2 // B2 moto output
);
wire controller_en ;
wire [PSIZE-1:0] multiplier ;
wire [PSIZE-1:0] divider ;
wire tick ;
wire [7:0] duty_cycle ;
wire start ;
wire pwm ;
wire ptype ;
wire mode ;
wire direction ;
wire [DSIZE-1:0] period ;
wire run ;
wire step_strobe ;
prescaler #(
.BITS(PSIZE)
) i_prescaler (
.rst_n (rst_n ),
.clk (clk ),
.clear_n (controller_en ),
.multiplier (multiplier ),
.divider (divider ),
.tick (tick )
);
pwm_generator i_pwm_generator (
.rst_n (rst_n ),
.clk (clk ),
.clear_n (controller_en ),
.duty_cycle (duty_cycle ),
.tick (tick ),
.start (start ),
.pwm (pwm )
);
motor_sequencer #(
.DSIZE(DSIZE)
)i_motor_sequencer (
.rst_n (rst_n ),
.clk (clk ),
.clear_n (controller_en ),
.ptype (ptype ),
.mode (mode ),
.direction (direction ),
.period (period ),
.run (run ),
.step_strobe(step_strobe ),
.start (start ),
.pwm (pwm ),
.motor_a1 (motor_a1 ),
.motor_a2 (motor_a2 ),
.motor_b1 (motor_b1 ),
.motor_b2 (motor_b2 )
);
step_motor_controller_registers #(
.PSIZE(PSIZE),
.DSIZE(DSIZE)
) i_step_motor_controller_registers (
.rst_n (rst_n ),
.clk (clk ),
.controller_en(controller_en),
.multiplier (multiplier ),
.divider (divider ),
.duty_cycle (duty_cycle ),
.ptype (ptype ),
.mode (mode ),
.direction (direction ),
.period (period ),
.run (run ),
.step_strobe (step_strobe ),
.wbs_cyc_i (wbs_cyc_i ),
.wbs_stb_i (wbs_stb_i ),
.wbs_adr_i (wbs_adr_i ),
.wbs_we_i (wbs_we_i ),
.wbs_dat_i (wbs_dat_i ),
.wbs_sel_i (wbs_sel_i ),
.wbs_dat_o (wbs_dat_o ),
.wbs_ack_o (wbs_ack_o )
);
endmodule
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PWM generator
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
module pwm_generator (
input wire rst_n , // Asynchronous reset (active low)
input wire clk , // Clock (rising edge)
input wire clear_n , // Synchronous reset (active low)
input wire [7:0] duty_cycle , // PWM duty cycle
input wire tick , // Input tick (PWM resolution)
output reg start , // Start strobe (One clk pulse at start of PWM period)
output reg pwm // PWM signal
);
wire [7:0] next_counter;
reg [7:0] counter;
assign next_counter = counter + 1'b1;
always @(negedge rst_n or posedge clk) begin
if (rst_n == 1'b0) begin
counter <= 8'h00;
start <= 1'b0;
pwm <= 1'b0;
end else begin
if (clear_n == 1'b0) begin
counter <= 8'h00;
start <= 1'b0;
pwm <= 1'b0;
end else begin
if (tick == 1'b1) begin
counter <= next_counter;
end
if ((tick == 1'b1) && (counter[7] == 1'b1) && (next_counter[7] == 1'b0)) begin
start <= 1'b1;
end else begin
start <= 1'b0;
end
if (counter <= duty_cycle) begin
pwm <= 1'b1;
end else begin
pwm <= 1'b0;
end
end
end
end
endmodule
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Motor_sequencer
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
module motor_sequencer #(
parameter DSIZE = 32 // Number of bits of delay counter
)(
input wire rst_n , // Asynchronous reset (active low)
input wire clk , // Clock (rising edge)
input wire clear_n , // Synchronous reset (active low)
input wire ptype , // Type of motor (1'b0:Unipolar, 1'b1:Bipolar)
input wire mode , // Mode of drive (1'b0:FullStep, 1'b1:HalfStep)
input wire direction , // Direction of motor
input wire [DSIZE-1:0] period , // Period of each motor step
input wire run , // Motor run
output reg step_strobe , // Motor step strobe (one clk pulse by step)
// PWM input
input wire start , // Start strobe (One clk pulse at start of PWM period)
input wire pwm , // PWM signal
// Motor outputs
output reg motor_a1 , // A1 motor output
output reg motor_a2 , // A2 motor output
output reg motor_b1 , // B1 motor output
output reg motor_b2 // B2 motor output
);
reg [2:0] motor_state;
reg [DSIZE-1:0] counter;
reg pwmo;
wire [DSIZE-1:0] next_counter;
wire [3:0] motor_values[7:0];
assign next_counter = counter + 1'b1;
assign motor_values[0] = 4'b1_0_0_1; // b2 b1 a2 a1
assign motor_values[1] = 4'b0_0_0_1; // b2 b1 a2 a1
assign motor_values[2] = 4'b0_0_1_1; // b2 b1 a2 a1
assign motor_values[3] = 4'b0_0_1_0; // b2 b1 a2 a1
assign motor_values[4] = 4'b0_1_1_0; // b2 b1 a2 a1
assign motor_values[5] = 4'b0_1_0_0; // b2 b1 a2 a1
assign motor_values[6] = 4'b1_1_0_0; // b2 b1 a2 a1
assign motor_values[7] = 4'b1_0_0_0; // b2 b1 a2 a1
// Frame decoder
always @(negedge rst_n or posedge clk) begin
if (rst_n == 1'b0) begin
counter <= {(DSIZE){1'b0}};
step_strobe <= 1'b0;
pwmo <= 1'b0;
motor_state <= 3'b000;
motor_a1 <= 1'b0;
motor_a2 <= 1'b0;
motor_b1 <= 1'b0;
motor_b2 <= 1'b0;
end else begin
if (clear_n == 1'b0) begin
counter <= {(DSIZE){1'b0}};
step_strobe <= 1'b0;
pwmo <= 1'b0;
motor_state <= 3'b000;
motor_a1 <= 1'b0;
motor_a2 <= 1'b0;
motor_b1 <= 1'b0;
motor_b2 <= 1'b0;
end else begin
if (run == 1'b0) begin
counter <= {(DSIZE){1'b0}};
end else if (start == 1'b1) begin
if (counter < period) begin
counter <= next_counter;
end else begin
counter <= {(DSIZE){1'b0}};
if (direction == 1'b0) begin
motor_state <= motor_state + 1'b1;
end else begin
motor_state <= motor_state - 1'b1;
end
end
end
if ((run == 1'b1) && (start == 1'b1) && (counter >= period)) begin
step_strobe <= 1'b1;
end else begin
step_strobe <= 1'b0;
end
pwmo <= pwm;
if (ptype == 1'b0) begin // Unipolar
motor_a1 <= motor_values[motor_state & {2'b11, mode}][0] & pwmo ;
motor_a2 <= motor_values[motor_state & {2'b11, mode}][1] & pwmo ;
motor_b1 <= motor_values[motor_state & {2'b11, mode}][2] & pwmo ;
motor_b2 <= motor_values[motor_state & {2'b11, mode}][3] & pwmo ;
end else begin // Bipolar
motor_a1 <= motor_values[motor_state & {2'b11, mode}][0] & pwmo ;
motor_a2 <= motor_values[motor_state & {2'b11, mode}][2] & pwmo ;
motor_b1 <= motor_values[motor_state & {2'b11, mode}][1] & pwmo ;
motor_b2 <= motor_values[motor_state & {2'b11, mode}][3] & pwmo ;
end
end
end
end
endmodule
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Registers
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
module step_motor_controller_registers #(
parameter PSIZE = 32 , // Size of prescaler counter(bits)
parameter DSIZE = 32 // Size of delay counter (bits)
)(
input rst_n , // Asynchronous reset (active low)
input clk , // Clock (rising edge)
// Configuration
output reg controller_en , // Controller enable (active high)
output reg [PSIZE-1:0] multiplier , // frequency multiplier
output reg [PSIZE-1:0] divider , // frequency divider
output reg [7:0] duty_cycle , // PWM duty cycle
output reg ptype , // Type of motor (1'b0:Unipolar, 1'b1:Bipolar)
output reg mode , // Mode of drive (1'b0:FullStep, 1'b1:HalfStep)
output reg direction , // Direction of motor
output reg [DSIZE-1:0] period , // Period of each motor step
output reg run , // Motor run
input wire step_strobe , // Motor step strobe (one clk pulse by step)
// Wishbone bus
input wire wbs_cyc_i , // Wishbone strobe/request
input wire wbs_stb_i , // Wishbone strobe/request
input wire [31:0] wbs_adr_i , // Wishbone address
input wire wbs_we_i , // Wishbone write (1:write, 0:read)
input wire [31:0] wbs_dat_i , // Wishbone data output
input wire [ 3:0] wbs_sel_i , // Wishbone byte enable
output reg [31:0] wbs_dat_o , // Wishbone data input
output wire wbs_ack_o // Wishbone acknowlegement
);
localparam
config_reg_addr = 3'b000,
multiplier_reg_addr = 3'b001,
divider_reg_addr = 3'b010,
period_reg_addr = 3'b011,
step_reg_addr = 3'b100;
wire valid;
wire [31:0] wstrb;
wire [2:0] addr;
wire [23:0] next_cycles;
reg free;
reg ready;
reg [15:0] cycles;
integer i = 0;
assign valid = wbs_cyc_i && wbs_stb_i;
assign wstrb = {{8{wbs_sel_i[3]}}, {8{wbs_sel_i[2]}}, {8{wbs_sel_i[1]}}, {8{wbs_sel_i[0]}}} & {32{wbs_we_i}};
assign addr = wbs_adr_i[4:2];
assign wbs_ack_o = ready;
assign next_cycles = cycles - 1'b1;
always @(negedge rst_n or posedge clk) begin
if (rst_n == 1'b0) begin
ready <= 1'b0;
wbs_dat_o <= 32'h00000000;
controller_en <= 1'b0;
ptype <= 1'b0;
mode <= 1'b0;
duty_cycle <= 8'h00;
multiplier <= {PSIZE{1'b0}};
divider <= {PSIZE{1'b0}};
period <= {DSIZE{1'b0}};
run <= 1'b0;
direction <= 1'b0;
free <= 1'b0;
cycles <= 16'h0000;
end else begin
if (valid && !ready) begin
//Write
case (addr)
config_reg_addr : begin
wbs_dat_o[31] <= controller_en; if (wstrb[31]) controller_en <= wbs_dat_i[31];
wbs_dat_o[30] <= ptype ; if (wstrb[30]) ptype <= wbs_dat_i[30];
wbs_dat_o[29] <= mode ; if (wstrb[29]) mode <= wbs_dat_i[29];
wbs_dat_o[28:8] <= 11'b0;
wbs_dat_o[ 7] <= duty_cycle[7]; if (wstrb[ 7]) duty_cycle[7] <= wbs_dat_i[ 7];
wbs_dat_o[ 6] <= duty_cycle[6]; if (wstrb[ 6]) duty_cycle[6] <= wbs_dat_i[ 6];
wbs_dat_o[ 5] <= duty_cycle[5]; if (wstrb[ 5]) duty_cycle[5] <= wbs_dat_i[ 5];
wbs_dat_o[ 4] <= duty_cycle[4]; if (wstrb[ 4]) duty_cycle[4] <= wbs_dat_i[ 4];
wbs_dat_o[ 3] <= duty_cycle[3]; if (wstrb[ 3]) duty_cycle[3] <= wbs_dat_i[ 3];
wbs_dat_o[ 2] <= duty_cycle[2]; if (wstrb[ 2]) duty_cycle[2] <= wbs_dat_i[ 2];
wbs_dat_o[ 1] <= duty_cycle[1]; if (wstrb[ 1]) duty_cycle[1] <= wbs_dat_i[ 1];
wbs_dat_o[ 0] <= duty_cycle[0]; if (wstrb[ 0]) duty_cycle[0] <= wbs_dat_i[ 0];
end
multiplier_reg_addr : begin
for (i = 0; i < 32; i = i + 1) begin
if (i >= PSIZE) begin
wbs_dat_o[i] <= 1'b0 ;
end else begin
wbs_dat_o[i] <= multiplier[i] ; if (wstrb[i]) multiplier[i] <= wbs_dat_i[i];
end
end
end
divider_reg_addr : begin
for (i = 0; i < 32; i = i + 1) begin
if (i >= PSIZE) begin
wbs_dat_o[i] <= 1'b0 ;
end else begin
wbs_dat_o[i] <= divider[i] ; if (wstrb[i]) divider[i] <= wbs_dat_i[i];
end
end
end
period_reg_addr : begin
for (i = 0; i < 32; i = i + 1) begin
if (i >= DSIZE) begin
wbs_dat_o[i] <= 1'b0 ;
end else begin
wbs_dat_o[i] <= period[i] ; if (wstrb[i]) period[i] <= wbs_dat_i[i];
end
end
end
step_reg_addr : begin
wbs_dat_o[31] <= run;
wbs_dat_o[30] <= direction ; if (wstrb[30]) direction <= wbs_dat_i[30];
wbs_dat_o[29] <= free ; if (wstrb[29]) free <= wbs_dat_i[29];
wbs_dat_o[28:16] <= 13'b0000000000000;
wbs_dat_o[15:0] <= cycles;
end
endcase
ready <= 1'b1;
end else begin
ready <= 1'b0;
end
if (valid && !ready && (addr == step_reg_addr) && wstrb[31]) begin
run <= wbs_dat_i[31];
end else if ((free == 1'b0) && (step_strobe == 1'b1) && (cycles == 16'h0000)) begin
run <= 1'b0;
end
if (valid && !ready && (addr == step_reg_addr) && wbs_we_i) begin
cycles <= wbs_dat_i[15:0];
end else if ((step_strobe == 1'b1) && (cycles != 16'h0000)) begin
cycles <= next_cycles;
end
end
end
endmodule