module spi_ctrl(
input wire sclk,//主机给从机的系统时钟50Mhz
input wire rst_n,
input wire work_en,//触发配置操作的使能,属于引入的一个让状态机是否工作的工作使能。
output reg conf_end,//配置结束,为的是和其他模块相连的时候有个标志
output wire spi_clk,//50-60Mhz
output wire spi_sdi,//SDI主机输出给从机的数据。
output wire spi_csn,//片选信号可以使一个主机控制多个从机,此信号有效表示此从机被选中通信。
input wire spi_sdo//从机输出给主机的数据;;读输入管脚不进行编程
);
parameter IDLE = 5'b0_0001;
parameter WAIT = 5'b0_0010;
parameter R_MEM = 5'b0_0100;
parameter W_REG = 5'b0_1000;
parameter STOP = 5'b1_0000;
parameter H_DIV_CYC = 5'd25 - 1;
//先来一个分频
reg [4:0] state;//状态机的寄存器变量编码方式采用独热码
reg [4:0] div_cnt;//分频器
reg clk_p=1'b0;//产生数据
wire clk_n;//提供spi的从机作为数据采集,clk_n上升沿前边用于建立时间,后边用于保持时间,达到时序最优化
reg pose_flag;//标志上升沿
reg [3:0] wait_cnt;
reg [3:0] shift_cnt;
reg [4:0] r_addr=0;
wire [15:0] r_data;
wire wren;
reg [15:0] shift_buf;//存储读出来的16位数据,
reg data_end;
reg sdi;
reg csn;
reg tck;
//分频计数器
always @(posedge sclk or rst_n)
if(rst_n == 1'b0)
div_cnt <= 5'd0;
else if(div_cnt == H_DIV_CYC)
div_cnt <= 'd0;
else
div_cnt <=div_cnt + 1'b1;//当sclk震了24次之后div_cnt出现高电平,然后减到0,在震24次出现下一个高电平,一次把原来的sclk分频了50倍
//分频时钟不允许做寄存器的触发时钟,也就是不能写在always块的触发列表中
always @(posedge sclk or rst_n)
if(rst_n == 1'b0)
clk_p <= 1'b0;
else if(div_cnt == H_DIV_CYC)
clk_p <= ~clk_p;
assign clk_n =~clk_p;
//产生一个标志信号,用于分频同步的标志信号
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
pose_flag <= 1'b0;
else if(clk_p == 1'b0 && div_cnt == H_DIV_CYC)
pose_flag <= 1'b1;
else
pose_flag <= 1'b0;
//等待计数器
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wait_cnt <= 'd0;
else if(state == WAIT && pose_flag ==1'b1)
wait_cnt <= wait_cnt + 1'b1;
else if(state !=WAIT)
wait_cnt <= 4'd0;
//fsm
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <=IDLE;
else case(state)
IDLE : if(work_en ==1'b1)
state <=WAIT;
WAIT : if(wait_cnt[3] == 1'b1)//当计数到第八次的时候
state <=R_MEM;
R_MEM: state <= W_REG;
W_REG: if(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end != 1'b1)
state <=WAIT;
else if(shift_cnt == 4'd15 && pose_flag == 1'b1 && data_end == 1'b1)
state <= STOP;
STOP: state <= STOP;
default: state <= IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
shift_cnt <= 'd0;
else if(state == W_REG && pose_flag == 1'b1)
shift_cnt <= shift_cnt + 1'b1;
else if(state != W_REG)
shift_cnt <=4'd0;
//读memory的地址产生
always @(posedge sclk or negedge rst_n)
if(rst_n ==1'b0)
r_addr <= 'd0;
else if(state == R_MEM)
r_addr <= r_addr + 1'b1;
//data_end 最后一个需要移位的数据
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_end <= 1'b0;
else if(state == R_MEM && (&r_addr)==1'b1 )// 11111按位与等效于r_addr == 5'd31
data_end <= 1'b1;
assign wren =1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
shift_buf <= 'd0;
else if(state == R_MEM)
shift_buf <= r_data;
else if(state == W_REG && pose_flag == 1'b1)
shift_buf <={shift_buf[14:0],1'b1};
//数据输出
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sdi <=1'b0;
else if(state == W_REG && pose_flag == 1'b1)
sdi <=shift_buf[15];
else if(state != W_REG)
sdi <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
csn <= 1'b1;
else if(state == W_REG)
csn <=1'b0;
else
csn <=1'b1;
always @ (posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
tck <= 1'b0;
else if(state == W_REG)
tck <= clk_n;
else
tck <=1'b0;
assign spi_clk = tck;
assign spi_csn = csn;
assign spi_sdi = sdi;
always @ (posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
conf_end <= 1'b0;
else if(state == STOP)
conf_end <= 1'b1;
ram_16x32_sr ram_16x32_sr_inst (
.address ( r_addr ),//读地址
.clock ( sclk ),
.data ( 16'd0 ),//写数据
.wren ( wren ),//写使能高有效,读使能低有效
.q ( r_data )//读数据
);
endmodule
评论0