SPI master

`timescale 1ns/1ns
//为适应菊花链dummy需求,状态机中增加dummy状态来做sck输出
module spi_master
#(
    parameter   CLK_FREQUENCE   = 50_000_000    ,   //系统时钟50M
                SPI_FREQUENCE   = 12_500_000    ,   //SPI 速率不超过15M,分区数少建议频率低一点
                WDATA           = 16            ,           
                CPOL            = 1'b0,
                CPHA            = 1'b0
    
)
(
	input	                    clk         ,               //system_clock
	input	                    rst_n       ,               //system_rst   rst=0;
    input                       spi_wr      ,               //SPI传输开始使能,1脉冲
    input [WDATA -1  :0]        data_in     ,               //输入数据
    // input                       data_vild   ,
    input [WDATA -1  :0]        dummy_num   ,               //控制输出延时空白
    input                       d_start  ,
    output               reg    finish      ,
    output reg [WDATA -1  :0]   data_out    ,
       
    output               reg    sck         ,
	output	                    mosi        ,
	input	                    miso        ,
	output	                    csb         
);
        
    parameter   CNT             = (CLK_FREQUENCE/SPI_FREQUENCE)*2;
    parameter   CNT_WDITH       = clogb2(CLK_FREQUENCE/SPI_FREQUENCE);
    parameter   SHIFT_WDITH     = clogb2(WDATA);
    
    //SPI_TX状态机
	reg [2:0]                       tx_state_c,tx_state_n;
	reg [SHIFT_WDITH - 1 :0]        dummy_cnt   ;                       //    
	reg [SHIFT_WDITH - 1 :0]        tx_shift_cnt;
	reg [WDATA + CPHA -1 :0]        tx_shift_r;
	reg                             mosi_out;
	reg                             cs;
    
    
	localparam	 IDLE   = 3'b000;
	localparam	 START  = 3'b001;
	localparam   SHIFT  = 3'b010;
	localparam   DUMMY  = 3'b011;
	localparam   FINISH = 3'b100;
    
//==============================================================================
//  sck时钟产生
//==============================================================================    
    reg [CNT_WDITH - 1: 0]      clk_cnt;
    reg                         clk_cnt_en;
    always@ (posedge clk or negedge rst_n)            //sck时钟计数
    begin
        if(!rst_n)
            clk_cnt <= 'd0;
        else if(clk_cnt_en == 1'b1)
            if(clk_cnt == CNT - 1)
                clk_cnt <= 'd0;
            else
                clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 'd0;
    end
    
    always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            clk_cnt_en <= 1'b0;
        else
            if(clk_cnt == CNT - 1)
                if(tx_state_n == FINISH)
                    clk_cnt_en <= 1'b0;
                else
                    clk_cnt_en <= clk_cnt_en;
            else
                if(tx_state_c == SHIFT || tx_state_c == DUMMY)
                    clk_cnt_en <= 1'b1;
                else
                    clk_cnt_en <= clk_cnt_en;
                    
    end
    
    always@ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            sck <= CPOL[0];
        else if(clk_cnt_en == 1'b1)
            if(clk_cnt == CNT/2 - 1)
                sck <= ~CPOL[0];
            else if(clk_cnt == CNT - 1)
                sck <= CPOL[0];
            else
                 sck <=  sck;
        else
            sck <= CPOL[0];
    end
    
    wire                        sck_posedge;
    wire                        sck_negedge;
    reg                         sck0;
    reg                         sck1;
    
    always@ (posedge clk or negedge rst_n) 
    begin
        if(!rst_n)begin
            sck0    <= CPOL[0];
            sck1    <= CPOL[0];
        end
        else if(clk_cnt_en == 1'b1)begin
            sck0    <= sck;
            sck1    <= sck0;
        end
        else begin
            sck0    <= CPOL[0];
            sck1    <= CPOL[0];
        end
    end
    
    assign  sck_posedge = sck0 && ~sck1;
    assign  sck_negedge = sck1 && ~sck0;
    
    wire                        shift_en;                 
    wire                        sampl_en;                 
    generate
        case({CPOL[0],CPHA[0]})
            2'b00:begin
                assign shift_en = sck_negedge;
                assign sampl_en = sck_negedge;
            end
            
            2'b01:begin
                assign shift_en = sck_posedge;
                assign sampl_en = sck_posedge;
            end
            
            2'b10:begin
                assign shift_en = sck_posedge;
                assign sampl_en = sck_posedge;
            end
            
            2'b11:begin
                assign shift_en = sck_negedge;
                assign sampl_en = sck_negedge;
            end
            default:begin
                assign shift_en = sck_negedge;
                assign sampl_en = sck_negedge;
            end
        endcase
    endgenerate
    
   

//  1	
	always@(posedge clk or negedge rst_n)
	begin
		if(rst_n == 1'b0)begin
			tx_state_c <= IDLE;
		end
		else begin
			tx_state_c <= tx_state_n;
		end	
	end
//  2	
	always@(*)
	begin
		case(tx_state_c)
			IDLE:begin
                if(spi_wr == 1'b1)
                    tx_state_n = START;
                else if(d_start == 1'b1)
                    tx_state_n = DUMMY;
                else 
                    tx_state_n = IDLE;
			end
			
			START:begin
				tx_state_n = SHIFT;
			end
            
            SHIFT:begin
                if((tx_shift_cnt == WDATA -1 + CPHA)&&(clk_cnt == CNT - 1))begin
                    tx_state_n = FINISH;
                end 
                else begin
                    tx_state_n = SHIFT;
                end
            end
            
            DUMMY:begin
                if((dummy_cnt == dummy_num - 1 + CPHA)&&(clk_cnt == CNT - 1))
                    tx_state_n = FINISH;
                else
                    tx_state_n = DUMMY;
            end
            
            FINISH:begin
                tx_state_n = IDLE;
            end
			
			default:begin
				tx_state_n = IDLE;
			end
		endcase
	end
//  3	
	always@(posedge clk or negedge rst_n)
	begin
		if(rst_n == 1'b0)begin
            // clk_cnt_en      <= 1'b0;
			tx_shift_cnt    <= 4'd0;
			tx_shift_r      <= 16'd0;
            dummy_cnt       <= 4'd0;
			mosi_out        <= 1'b0;        //输出寄存器
			cs              <= 1'b1;
            finish          <= 1'b0;
		end
		else case(tx_state_c)
			IDLE:begin
				// clk_cnt_en      <= 1'b0;
                tx_shift_cnt    <= 4'd0;
                tx_shift_r      <= 16'd0;
                dummy_cnt       <= 4'd0;
                mosi_out        <= 1'b0;        //输出寄存器
                cs              <= 1'b1;
                finish          <= 1'b0;
			end
			
			START:begin
				// clk_cnt_en                  <= 1'b1;
                tx_shift_r[WDATA-1 :0]      <= data_in;
                cs                          <= 1'b0;
			end
            
			SHIFT:begin
                if(shift_en)begin
                    // mosi_out        <= tx_shift_r[WDATA + CPHA -1];
                    tx_shift_r      <= {tx_shift_r[WDATA-1 :0],1'b0};
                    tx_shift_cnt    <= tx_shift_cnt + 1'b1;
                end
                // clk_cnt_en      <= 1'b1;
                cs              <= 1'b0;
			end
            
            DUMMY:begin
                if(shift_en)begin
                    mosi_out  <= 1'b0;
                    dummy_cnt <= dummy_cnt + 1'b1;
                end
                // clk_cnt_en      <= 1'b1;
                cs              <= 1'b0;
            end
            
            FINISH:begin
                // clk_cnt_en      <= 1'b0;
                cs              <= 1'b1;
                finish          <= 1'b1;
                tx_shift_r      <= 'd0;
            end
            
			default:begin
                
			end
		endcase
	end

    assign  mosi = tx_shift_r[WDATA + CPHA -1];
    assign  csb  = cs;    
    
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_out <= 'd0;
	else if (sampl_en) 
		data_out <= {data_out[WDATA-1:0],miso};
	else
		data_out <= data_out;
end

    
//位宽计算函数
    function integer clogb2 (input integer depth);
    begin
        for (clogb2=0; depth>0; clogb2=clogb2+1) 
            depth = depth >>1;                          
    end
    endfunction

    
    
endmodule

 

tb

`timescale 1ns/1ns

module tb_spi();

parameter	CLK_FREQUENCE	= 50_000_000		,
			SPI_FREQUENCE	= 12_500_000			,
			DATA_WIDTH		= 16					,
			CPOL			= 1'b1					,
			CPHA			= 1'b0					;

reg         xtal_clk;
reg         clk;
reg         rst_n;
reg [15:0]  data_in;
reg         spi_wr;
reg [15:0]  dummy_num;
reg         d_start;
reg         miso = 1'b0;


wire        finish;
wire        sck;


initial begin
    xtal_clk = 0;
    forever #10 xtal_clk= ~xtal_clk;
end

initial begin
    clk = 0;
    forever #10 clk = ~clk;
end

initial begin
    rst_n = 0;
    #200;
    rst_n = 1;  
end

initial fork
    datain_generate;
    spiwr_generate;
    debug_information;
    dstart_generate;
    dummynum_generate;
join

task datain_generate;
begin
    data_in = 0;
    @(posedge rst_n)
    data_in = 16'b1001_1101_1110_1011;
    @(posedge finish)
    data_in = 16'b1010_0011_1100_0110;
    @(posedge finish)
    ;
    @(posedge finish)
    #200 $stop;
end
endtask

task spiwr_generate;
begin
    spi_wr = 0;
    @(posedge rst_n)
    #20 spi_wr = 1;
    #20 spi_wr = 0;
    @(posedge finish)
    #200 spi_wr = 1;
    #20 spi_wr = 0;
end
endtask

task dstart_generate;
begin
    d_start = 0;
    @(posedge rst_n)
    ;
    @(posedge finish)
    ;
    @(posedge finish)
    #200 d_start = 1;
    #20 d_start = 0;
    
end
endtask

task dummynum_generate;
begin
    dummy_num = 0;
    @(posedge rst_n)
    dummy_num = 12;
end
endtask

task debug_information;
begin
    $display("----------------------------------------------");  
    $display("------------------   -----------------------");  
    $display("----------- SIMULATION RESULT ----------------");  
    $display("--------------       -------------------");  
    $display("----------------     ---------------------");  
    $display("----------------------------------------------");  
    $monitor("TIME = %d, mosi = %b, miso = %b, data_in = %b",$time, mosi, miso, data_in);  
end
endtask

//the generate block to generate the miso
generate
	if(CPOL == 0)
		always @(posedge sck) begin
			miso = $random;
		end
	else
		always @(negedge sck) begin
			miso = $random;
		end
endgenerate

spi_master 
    #(
        .CLK_FREQUENCE(CLK_FREQUENCE)    ,   //系统时钟50M
        .SPI_FREQUENCE(SPI_FREQUENCE)    ,   //SPI 速率不超过15M,分区数少建议频率低一点
        .WDATA        (DATA_WIDTH)            ,
        .CPOL         (CPOL),
        .CPHA         (CPHA)        
    
)
spi_master(
        .clk            (xtal_clk),
		.rst_n          (rst_n),
		.spi_wr         (spi_wr) ,
		.data_in        (data_in) ,
        .dummy_num      (dummy_num),
        .d_start        (d_start),
        .finish         (finish),

		.sck            (sck) ,
		.mosi           (mosi) ,
		.miso           (miso) ,
		.csb            (csb)


);

endmodule

CPOL=0,CPHA=0

CPOL=0,CPHA=1

CPOL=1,CPHA=0

CPOL=1,CPHA=1

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值