`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 20:49:28 04/29/2019
// Design Name:
// Module Name: spi_master
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module spi_master(
input clk_40k, //clock 40kHz
input rst_n, //reset signal
input [7:0] data_in, //from master to slave ,width = 8
input send_start, //if this signal is high,start communicating, a clock period (40kHz)
output [7:0] data_out, //from slave to master ,width = 8
output data_out_vld, //output signal is valid,if this signal is high, a clock period (40kHz)
output cs_n, //if this signal is low,slave is selected,contain high while communicating
output sclk, //sync clock,1 kHz,contain low while free
input miso, //serial data form slave to master
output mosi //serial data form master to slave
);
parameter idle = 2'd0,
get_address = 2'd1,
get_data = 2'd2,
wait_next = 2'd3,
cnt_work_end= 19; // cnt_work_end = 40/2 -1 =19
reg [2:0] current_state;
reg [2:0] next_state;
//state shift
always @ (posedge clk_40k or negedge rst_n)
begin
if(!rst_n)
current_state <= idle;
else
current_state <= next_state;
end
//state shifting condition
reg [3:0] cnt_data; //to count the number of data that has been transfered
reg [7:0] data_in_r; //data_in, register type
reg flag_low; //when this signal is valid,start to transfer the r/w and address bits
reg flag_high; //when this signal is valid,start to transfer the r/w and address bits
reg mosi_r; //serial data form master to slave
reg [7:0] cnt_state; //to count every sclk,from 0 to 17*4-1 = 67,used for state shifting
reg work_flag; //to control a whole period that sends 4 data and read 1 data,when this flag is valid,cnt_state count from 0 to 67
reg [5:0] cnt_buff_bit;//current number of slave_buff_bit
reg sclk_r;
always @ (*)
begin
case(current_state)
idle:
begin
if (send_start)
begin
next_state <= get_address; //start work
work_flag <= 1'b1;
data_in_r <= data_in;
flag_low <= 1'b1;
sclk_r <= 1'b1;
end
else
begin
cnt_buff_bit<= 6'b0;
flag_high <= 1'b0;
mosi_r <= 1'b0;
next_state <= idle;
end
end
get_address:
begin
if(cnt_buff_bit == 8)
begin
next_state <= get_data;
flag_low <= 1'b0;
end
else
next_state <= get_address;
end
get_data:
begin
if(cnt_buff_bit == 16)
begin
next_state <= wait_next;
flag_high <= 1'b0;
end
else
next_state <= get_data;
end
wait_next:
begin
case(cnt_state)
8'd17: begin //16*40
next_state <= get_address;
flag_low <= 1'b1;
end
8'd34: begin
next_state <= get_address;
flag_low <= 1'b1;
end
8'd51: begin
next_state <= get_address;
flag_low <= 1'b1;
end
8'd68: begin
next_state <= get_address;
flag_low <= 1'b1;
end
8'd85: begin
next_state <= idle;
flag_low <= 1'b0;
work_flag <= 1'b0;
cnt_state <= 8'b0;
end
default: next_state <= idle;
endcase
end
default:
next_state <= idle;
endcase
end
//sync block,to create sclk
reg [5:0] cnt_work;
assign sclk = sclk_r;
always @ (posedge clk_40k or negedge rst_n)
begin
if(!rst_n)
begin
sclk_r <= 1'b0;
cnt_work <= 6'b0;
end
else
begin
if(work_flag)
begin
if(cnt_work == cnt_work_end)
begin
cnt_work <= 6'b0;
sclk_r <= ~sclk_r;
end
else
cnt_work <= cnt_work +6'b1;
end
else
begin
cnt_work <= 6'b0;
sclk_r <= 1'b0;
end
end
end
//delay the work_flag a sclk period later
reg work_flag2;
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
work_flag2 <= 1'b0;
else
work_flag2 <= work_flag;
end
//to delay work_flag2 a sclk period later
reg work_flag3;
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
work_flag3 <= 1'b0;
else
work_flag3 <= work_flag2;
end
//to delay work_flag3 a sclk period later
reg work_flag4;
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
work_flag4 <= 1'b0;
else
work_flag4 <= work_flag3;
end
//sync block,to create cnt_state
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
cnt_state <= 8'b0;
else
if(work_flag2)
cnt_state <= cnt_state + 8'b1;
else
cnt_state <= 8'b0;
end
//sync block,to create cnt_buff_bit signal,negedge of sclk
always @ (negedge sclk)
begin
if(send_start)
cnt_buff_bit <= 6'b0;
else
begin
if(work_flag4)
begin
if(cnt_buff_bit == 6'd16)
cnt_buff_bit <= 6'b0;
else
cnt_buff_bit <= cnt_buff_bit + 6'b1;
end
else
cnt_buff_bit <= 6'b0;
end
end
//sync block cnt_data,which number has been transfered
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
cnt_data <= 4'b0;
else
begin
if(work_flag)
case(cnt_state)
8'd16: cnt_data <= cnt_data + 4'b1;
8'd33: cnt_data <= cnt_data + 4'b1;
8'd50: cnt_data <= cnt_data + 4'b1;
8'd67: cnt_data <= cnt_data + 4'b1;
default:cnt_data <= cnt_data;
endcase
else
cnt_data <= 4'b0;
end
end
//reset cnt_data
always @ (posedge clk_40k or rst_n)
begin
if(!rst_n)
cnt_data <= 4'b0;
else
if(!work_flag)
cnt_data <= 4'b0;
end
//the data to reg0 reg1 reg2 and reg3
reg [7:0] data_send;
always @ (posedge sclk or negedge rst_n)
begin
if(!rst_n)
data_send <= 8'b00000000;//reset
else
begin
if(work_flag)
case(cnt_state)
8'd0 : data_send <= 8'b00000000; //write reg0 address
8'd7 : data_send <= data_in; //write reg0 data_in
8'd16: data_send <= 8'b00000001; //write reg1 address
8'd24: data_send <= {data_in_r[5:0],data_in_r[7:6]}; //write reg1 rotating left shift 2 data_in
8'd33: data_send <= 8'b00000010; //write reg2 address
8'd41: data_send <= {data_in_r[3:0],data_in_r[7:4]}; //write reg2 rotating left shift 4 data_in
8'd50: data_send <= 8'b00000011; //write reg3 address
8'd58: data_send <= {data_in_r[1:0],data_in_r[7:2]}; //write reg3 rotating left shift 6 data_in
8'd67: data_send <= 8'b10000011; //read reg3 address
8'd75: data_send <= data_out; //read reg3 data_out
default:data_send <= data_send;
endcase
else
data_send <= 8'b0;
end
end
endmodule
|
|