=========================================================================
=========================================================================
/**************************************************
*
**************************************************/
module zhm_mspi #(
parameter C_SPI_CPHA = 1 ,// clock phase ,0,在 SCLK 的第一个跳变沿进行采样;1,在 SCLK 的第二个跳变沿进行采样
parameter C_SPI_CPOL = 1 ,// clock polarity,0,SCLK 在空闲时为低电平; 1,SCLK 在空闲时为高电平
parameter C_SPI_ADDR_WIDTH = 8 ,// 地址位宽,广义上的
parameter C_SPI_DATA_WIDTH = 8 ,// 数据位宽
parameter C_SPI_DIV_VALUE = 10,// sclk 分频数
// 计数器位宽
parameter C_SPI_CMD_CWIDTH = 4 ,// 地址/数据 -计数器位宽
parameter C_SPI_DIV_CWIDTH = 4 ,// sclk 分频数-计数器位宽
//
parameter C_SPI_CMD_WIDTH = C_SPI_ADDR_WIDTH+C_SPI_DATA_WIDTH
)(
input aclk ,//
input aresetn ,//
output s_tready,//
input s_tvalid,//
input [C_SPI_CMD_WIDTH-1:0] s_tdata ,//
output m_tvalid,//
output [C_SPI_CMD_WIDTH-1:0] m_tdata ,//
output spi_cs ,//
output spi_sclk,//
output spi_mosi,//
input spi_miso,//
output spi_t ,//
output [02:00] cursta ,
output [C_SPI_DIV_CWIDTH-1:0] curcntD ,
output [C_SPI_CMD_CWIDTH-1:0] curcntB
);
localparam TB_EDGE = C_SPI_DIV_VALUE/2-1;//
/**************************************************
* 分频计数器 D
**************************************************/
localparam CntD_End_Value = C_SPI_DIV_VALUE-1;
localparam CntD_Width = C_SPI_DIV_CWIDTH ;
reg [CntD_Width-1:0] cntD = {CntD_Width{1'b0}};
wire cntD_dom;
wire cntD_add;
wire cntD_end;
/**************************************************
* 位计数器 B
**************************************************/
//localparam CntB_End_Value = 100-1;
localparam CntB_Width = C_SPI_CMD_CWIDTH;
reg [CntB_Width-1:0] cntB = {CntB_Width{1'b0}};
wire cntB_dom;
wire cntB_add;
wire cntB_end;
/**************************************************
* 状态机
* IDLE==>S1==>S2==>S3==>S4==>S5==>S6==>S7==>S1
**************************************************/
localparam IDLE = 3'd0;
localparam S1 = 3'd1;
localparam S2 = 3'd2;
localparam S3 = 3'd3;
localparam S4 = 3'd4;
localparam S5 = 3'd5;
localparam S6 = 3'd6;
localparam S7 = 3'd7;
reg [02:00] state_c = IDLE;
reg [02:00] state_n ;
wire idle2s1_start ;
wire s12s2_start ;
wire s22s3_start ;
wire s32s4_start ;
wire s42s5_start ;
wire s52s6_start ;
wire s62s7_start ;
wire s72s1_start ;
always @(posedge aclk)begin
if(!aresetn)state_c <= IDLE;
else state_c <= state_n;
end
always @(*)begin
case(state_c)
IDLE:begin if(idle2s1_start) state_n = S1;else state_n = state_c;end
S1 :begin if(s12s2_start) state_n = S2;else state_n = state_c;end
S2 :begin if(s22s3_start) state_n = S3;else state_n = state_c;end
S3 :begin if(s32s4_start) state_n = S4;else state_n = state_c;end
S4 :begin if(s42s5_start) state_n = S5;else state_n = state_c;end
S5 :begin if(s52s6_start) state_n = S6;else state_n = state_c;end
S6 :begin if(s62s7_start) state_n = S7;else state_n = state_c;end
S7 :begin if(s72s1_start) state_n = S1;else state_n = state_c;end
default:;
endcase
end
assign idle2s1_start = state_c == IDLE && 1'b1 ;// 复位中
assign s12s2_start = state_c == S1 && s_tvalid;// 等待数据中
assign s22s3_start = state_c == S2 && cntD_end;// CS 下降沿前等待
assign s32s4_start = state_c == S3 && cntD_end;// CS 下降沿后等待
assign s42s5_start = state_c == S4 && cntB_end;// 下发地址中
assign s52s6_start = state_c == S5 && cntB_end;// 收发数据中
assign s62s7_start = state_c == S6 && cntD_end;// CS 上升沿前等待
assign s72s1_start = state_c == S7 && cntD_end;// CS 上升沿后等待
assign cursta = state_c;
assign s_tready = (state_c == S1);
/**************************************************
* 分频计数器 D
**************************************************/
wire cntD_flag;
assign cntD_flag = (state_c == S2 ||
state_c == S3 ||
state_c == S4 ||
state_c == S5 ||
state_c == S6 ||
state_c == S7 );
always @(posedge aclk)begin
if(cntD_dom)begin if(cntD_add)begin
if(cntD_end)cntD <= {CntD_Width{1'b0}};
else cntD <= cntD + 1'd1;
end end else cntD <= {CntD_Width{1'b0}};
end
assign cntD_dom = cntD_flag;
assign cntD_add = cntD_dom && 1'b1;
assign cntD_end = cntD_add && cntD == CntD_End_Value;
assign curcntD = cntD;
/**************************************************
* 位计数器 B
**************************************************/
reg [CntB_Width-1:0] cntb_end_value;
always @(*)begin
if(state_c == S4) cntb_end_value = C_SPI_ADDR_WIDTH - 1'b1;
else cntb_end_value = C_SPI_DATA_WIDTH - 1'b1;
end
always @(posedge aclk)begin
if(cntB_dom)begin if(cntB_add)begin
if(cntB_end)cntB <= {CntB_Width{1'b0}};
else cntB <= cntB + 1'd1;
end end else cntB <= {CntB_Width{1'b0}};
end
assign cntB_dom = (state_c == S4 || state_c == S5);
assign cntB_add = cntB_dom && cntD_end;
assign cntB_end = cntB_add && cntB == cntb_end_value;
assign curcntB = cntB;
/**************************************************
* cs
**************************************************/
reg cs_r = 1'b1;
wire cs_w;
assign cs_w = (state_c == S3 ||
state_c == S4 ||
state_c == S5 ||
state_c == S6 );
always @(posedge aclk)begin
cs_r <= !cs_w;
end
assign spi_cs = cs_r;
/**************************************************
* 方向控制
**************************************************/
wire rhwl;
reg [C_SPI_CMD_WIDTH-1:0] data_sc;
assign rhwl = data_sc[C_SPI_CMD_WIDTH-1:0];
wire t_flag;
assign t_flag = (state_c == S4 ||
state_c == S5 );
reg t_r = 1'b1;
always @(posedge aclk)begin
if(rhwl) t_r <= (state_c != S4);
else t_r <= !t_flag;
end
assign spi_t = t_r;
/**************************************************
* sclk
**************************************************/
reg sclk_r;
generate
if(C_SPI_CPHA==0 && C_SPI_CPOL==0)begin: S00
always @(posedge aclk)begin
if(state_c == S4 || state_c == S5)begin
if(cntD == TB_EDGE) sclk_r <= 1'b1;
else if(cntD_end) sclk_r <= 1'b0;
end
else sclk_r <= 1'b0;
end
end
else if(C_SPI_CPHA==0 && C_SPI_CPOL==1)begin: S01
always @(posedge aclk)begin
if(state_c == S4 || state_c == S5)begin
if(cntD == TB_EDGE) sclk_r <= 1'b0;
else if(cntD_end) sclk_r <= 1'b1;
end
else sclk_r <= 1'b1;
end
end
else if(C_SPI_CPHA==1 && C_SPI_CPOL==0)begin: S10
always @(posedge aclk)begin
if(s32s4_start) sclk_r <= 1'b1;
else if(s52s6_start) sclk_r <= 1'b0;
else if(state_c == S4 || state_c == S5)begin
if(cntD == TB_EDGE) sclk_r <= 1'b0;
else if(cntD_end) sclk_r <= 1'b1;
end
else sclk_r <= 1'b0;
end
end
else if(C_SPI_CPHA==1 && C_SPI_CPOL==1)begin: S11
always @(posedge aclk)begin
if(s32s4_start) sclk_r <= 1'b0;
else if(s52s6_start) sclk_r <= 1'b1;
else if(state_c == S4 || state_c == S5)begin
if(cntD == TB_EDGE) sclk_r <= 1'b1;
else if(cntD_end) sclk_r <= 1'b0;
end
else sclk_r <= 1'b1;
end
end
endgenerate
reg sclk_rr;
always @(posedge aclk)begin
sclk_rr <= sclk_r;
end
assign spi_sclk = sclk_rr;
/**************************************************
* 数据发送
**************************************************/
always @(posedge aclk)begin
if(s12s2_start) data_sc <= s_tdata;
end
reg [C_SPI_CMD_WIDTH-1:0] data_yw;
always @(posedge aclk)begin
if(cntD_end)begin
if(s32s4_start) data_yw <= data_sc;
else data_yw <= {data_yw[C_SPI_CMD_WIDTH-2:0],1'b0};
end
end
reg mosi_r;
always @(posedge aclk)begin
mosi_r <= data_yw[C_SPI_CMD_WIDTH-1];
end
assign spi_mosi = mosi_r;
/**************************************************
* 数据接收
**************************************************/
reg [C_SPI_CMD_WIDTH-1:0] rece_yw;
reg resc_sc_edge = 1'b0;
always @(posedge aclk)begin
resc_sc_edge <= t_flag && (cntD == TB_EDGE);
end
always @(posedge aclk)begin
if(resc_sc_edge) rece_yw <= {rece_yw[C_SPI_CMD_WIDTH-2:0],spi_miso};
end
reg [C_SPI_CMD_WIDTH-1:0] rece_sc;
always @(posedge aclk)begin
if(s52s6_start) rece_sc <= rece_yw;
end
reg valid_r = 1'b0;
always @(posedge aclk)begin
valid_r <= s52s6_start && rhwl;
end
assign m_tvalid = valid_r;
assign m_tdata = rece_sc;
endmodule
/**************************************************
zhm_mspi #(
.C_SPI_CPHA (),// clock phase ,0,在 SCLK 的第一个跳变沿进行采样;1,在 SCLK 的第二个跳变沿进行采样
.C_SPI_CPOL (),// clock polarity,0,SCLK 在空闲时为低电平; 1,SCLK 在空闲时为高电平
.C_SPI_ADDR_WIDTH(),// 地址位宽,广义上的
.C_SPI_DATA_WIDTH(),// 数据位宽
.C_SPI_DIV_VALUE (),// sclk 分频数
.C_SPI_CMD_CWIDTH(),// 地址/数据 -计数器位宽
.C_SPI_DIV_CWIDTH())// sclk 分频数-计数器位宽
zhm_mspi_u(
.aclk (),//input
.aresetn (),//input
.s_tready(),//output
.s_tvalid(),//input
.s_tdata (),//input [C_SPI_CMD_WIDTH-1:0]
.m_tvalid(),//output
.m_tdata (),//output [C_SPI_CMD_WIDTH-1:0]
.spi_cs (),//output
.spi_sclk(),//output
.spi_mosi(),//output
.spi_miso(),//input
.spi_t (),//output
.cursta (),//output [02:00]
.curcntD (),//output [C_SPI_DIV_CWIDTH-1:0]
.curcntB () //output [C_SPI_CMD_CWIDTH-1:0]
);
**************************************************/