verilog设计——SPI

//spi_master
`timescale 1ns/1ps

module spi_master
#(
	parameter	CLK_FREQUENCE	= 50_000_000,	//system clk frequence
				SPI_FREQUENCE	= 5_000_000,	//spi clk frequence
				DATA_WIDTH		= 8,	        //serial word length
				CPOL			= 0,	        //SPI mode selection (mode 0 default)
				CPHA			= 0				//CPOL = clock polarity, CPHA = clock phase
)
(
	input								clk,	    //system clk
	input								rst_n,	    //system reset
	input		[DATA_WIDTH-1:0]		data_in,	//the data sent by mosi
	input								start,	    //spi transmission flag
	input								miso,	    //spi bus miso input
	output	reg							sclk,	    //spi bus sclk
	output	reg							cs_n,	    //spi bus slave select line
	output								mosi,	    //spi bus mosi output
	output	reg							finish,	    //SPI transmission finish flag
	output	reg [DATA_WIDTH-1:0]		data_out	//the data received by miso
);

localparam	FREQUENCE_CNT	= CLK_FREQUENCE/SPI_FREQUENCE - 1	,
			SHIFT_WIDTH		= log2(DATA_WIDTH)					,
			CNT_WIDTH		= log2(FREQUENCE_CNT)				;

localparam	IDLE	=	3'b000	,
			LOAD	=	3'b001	,
			SHIFT	=	3'b010	,
			DONE	=	3'b100	;

reg		[2:0]				cstate		;	//FSM current state
reg		[2:0]				nstate		;	//FSM next state
reg							clk_cnt_en	;	//start clk_cnt to generate sclk
reg							sclk_a		;	//sclk register to capture the edge of sclk
reg							sclk_b		;	//sclk register to capture the edge of sclk
wire						sclk_posedge;	//posedge of sclk
wire						sclk_negedge;	//negedge of sclk
wire						shift_en	;	//the signal to enable shift register to generate mosi
wire						sampl_en	;	//the signal to sample the data from miso
reg		[CNT_WIDTH-1:0]		clk_cnt		;	//the counter to generate sclk
reg		[SHIFT_WIDTH-1:0]	shift_cnt	;	//the counter to count the number of shifts
reg		[DATA_WIDTH-1:0]	data_reg	;	//the register to latch the data_in

//the counter to generate sclk
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        clk_cnt <= 'd0;
    else if(clk_cnt_en)
        if(clk_cnt == FREQUENCE_CNT)
            clk_cnt <= 'd0;
        else
            clk_cnt <= clk_cnt + 1'b1;
    else
        clk_cnt <= 'd0;
end

//generate sclk
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sclk <= CPOL;
    else if(clk_cnt_en)
        if(clk_cnt_en == FREQUENCE_CNT)
            sclk <= ~sclk;
        else
            sclk <= sclk;
    else
        sclk <= CPOL;
end

//sclk register
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sclk_a <= CPOL;
        sclk_b <= CPOL;
    end
    else if(clk_cnt_en) begin
        sclk_a <= sclk;
        sclk_b <= sclk_a;
    end
end

assign sclk_posedge = ~sclk_b & sclk_a
assign sclk_negedge = ~sclk_a & sclk_b

//generate blocks
generate
    case(CPHA)
        0:assign sample_en = sclk_posedge;
        1:assign sample_en = sclk_negedge;
        default:assign sample_en = sclk_posedge;
    endcase
endgenerate

generate
    case(CPHA)
        0:assign shift_en = sclk_negedge;
        1:assign shift_en = sclk_posedge;
        default:assign shift_en = sclk_posedge;
    endcase
endgenerate

//FSM_1
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cstate <= IDLE;
    else
        cstate <= nstate;
end

//FSM_2
always@(*) begin
    case (cstate)
        IDLE:nstate = start?load:IDLE;
        LOAD:nstate = SHIFT;
        SHIFT:nstate = (shift_cnt == DATA_WIDTH)?DONE:SHIFT;
        DONE:nstate = IDLE;
    endcase
end

//FSM_3
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_cnt_en <= 1'b0;
        data_reg   <= 'd0;
        cs_n       <= 1'b1;
        shift_cnt  <= 'd0;
        finish     <= 1'b0;
    end
    else begin
        case(nstate)
            IDLE:begin
                clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				shift_cnt	<= 'd0	;
				finish 		<= 1'b0	;
			end
			LOAD	: begin
				clk_cnt_en	<= 1'b1		;
				data_reg	<= data_in	;
				cs_n		<= 1'b0		;
				shift_cnt	<= 'd0		;
				finish 		<= 1'b0		;
			end
			SHIFT	: begin
				if (shift_en) begin
					shift_cnt	<= shift_cnt + 1'b1 ;
					data_reg	<= {data_reg[DATA_WIDTH-2:0],1'b0};
				end else begin
					shift_cnt	<= shift_cnt	;
					data_reg	<= data_reg		;
				end
				clk_cnt_en	<= 1'b1	;
				cs_n		<= 1'b0	;
				finish 		<= 1'b0	;
			end
			DONE	: begin
				clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				data_reg	<= 'd0	;
				finish 		<= 1'b1	;
			end
			default	: begin
				clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				data_reg	<= 'd0	;
				finish 		<= 1'b0	;
			end
		endcase
	end
end

//mosi output MSB first
assign mosi = data_reg[DATA_WIDTH-1];
//sample data from the miso line
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_out <= 'd0;
	else if (sampl_en) 
		data_out <= {data_out[DATA_WIDTH-1:0],miso};
	else
		data_out <= data_out;
end

//the function to get the width of data 
function integer log2(input integer v);
  begin
	log2=0;
	while(v>>log2) 
	  log2=log2+1;
  end
endfunction
endmodule
//spi_slave
`timescale 1ns/1ps

module SPI_Slave
#(
	parameter	CLK_FREQUENCE	= 50_000_000		,	//system clk frequence
				SPI_FREQUENCE	= 5_000_000			,	//spi clk frequence
				DATA_WIDTH		= 8					,	//serial word length
				CPOL			= 1					,	//SPI mode selection (mode 0 default)
				CPHA			= 1					 	//CPOL = clock polarity, CPHA = clock phase
)
(
	input								clk			,	//system clk
	input								rst_n		,	//system reset
	input		[DATA_WIDTH-1:0]		data_in		,	//the data sent by miso
	input								sclk		,	//spi bus sclk
	input								cs_n		,	//spi bus slave select line
	input								mosi		,	//spi bus mosi input
	output								miso		,	//spi bus miso output
	output								data_valid	,	//the data received by mosi valid
	output	reg	[DATA_WIDTH-1:0]		data_out	 	//the data received by mosi,valid when data_valid is high
);

localparam	SFIFT_NUM = log2(DATA_WIDTH);

reg	[DATA_WIDTH-1:0]		data_reg	;	//the register to latch the data_in,also the shift register
reg	[ SFIFT_NUM-1:0]		sampl_num	;	//the counter to count the number of sample
reg							sclk_a		;	//sclk register to capture the edge of sclk
reg							sclk_b		;	//sclk register to capture the edge of sclk
wire						sclk_posedge;	//posedge of sclk
wire						sclk_negedge;	//negedge of sclk
reg							cs_n_a		;	//cs_n register to capture the edge of cs_n
reg							cs_n_b		;	//cs_n register to capture the edge of cs_n
wire						cs_n_negedge;	//negedge of cs_n to latch the data
wire						shift_en	;	//the signal to enable shift register to generate mosi
wire						sampl_en	;	//the signal to sample the data from miso
//------------------------------------------
//to capture the edge of sclk
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		sclk_a <= CPOL;
		sclk_b <= CPOL;
	end else if (!cs_n) begin
		sclk_a <= sclk;
		sclk_b <= sclk_a;
	end
end

assign sclk_posedge = ~sclk_b & sclk_a;
assign sclk_negedge = ~sclk_a & sclk_b;
//------------------------------------------
//to capture the edge of sclk
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		cs_n_a	<= 1'b1;
		cs_n_b	<= 1'b1;
	end else begin
		cs_n_a	<= cs_n		;
		cs_n_b	<= cs_n_a	;
	end
end

assign cs_n_negedge = ~cs_n_a & cs_n_b;
//------------------------------------------
//==============================================
//==============GENERATE BLOCKS=================
generate
	case (CPHA)
		0: assign sampl_en = sclk_posedge;
		1: assign sampl_en = sclk_negedge;
		default: assign sampl_en = sclk_posedge;
	endcase
endgenerate

generate
 	case (CPHA)
		0: assign shift_en = sclk_negedge;
 		1: assign shift_en = sclk_posedge;
		default: assign shift_en = sclk_posedge;
	endcase
endgenerate
//==================================================
//the register to latch the data_in
//also the shift register to generate the miso
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_reg <= 'd0;
	else if(cs_n_negedge)
		data_reg <= data_in;
	else if (!cs_n & shift_en) 
		data_reg <= {data_reg[DATA_WIDTH-2:0],1'b0};
	else
		data_reg <= data_reg;
end
//miso output MSB first
assign miso = !cs_n ? data_reg[DATA_WIDTH-1] : 1'd0;
//==================================================
//sample data from the mosi line
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_out <= 'd0;
	else if (!cs_n & sampl_en) 
		data_out <= {data_out[DATA_WIDTH-2:0],mosi};
	else
		data_out <= data_out;
end
//the counter to count the number of sampled data bit
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		sampl_num <= 'd0;
	else if (cs_n)
		sampl_num <= 'd0;
	else if (!cs_n & sampl_en) 
		if (sampl_num == DATA_WIDTH)
			sampl_num <= 'd1;
		else
			sampl_num <= sampl_num + 1'b1;
	else
		sampl_num <= sampl_num;
end
//the received data valid
assign data_valid = sampl_num == DATA_WIDTH;
//the function to get the width of data 
function integer log2(input integer v);
  begin
	log2=0;
	while(v>>log2) 
	  log2=log2+1;
  end
endfunction
endmodule

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值