verilog控制TDC-gp22时间测量功能(二):SPI通信测试

        在进行通信测试前,需要对所用到的管脚进行叙述

        图中高亮标注的为用到的所有功能引脚,SPI通信测试所用到的管脚为:SSN,SCK,SI,SO,RSTN,VCC,GND。

        状态机的转移图如图所示:

        SPI通信测试所用到的寄存器在第一节中已经叙述过,在进行第四步写寄存器1和第五步读寄存器5之前需要对gp22芯片进行上电复位和初始化操作,需要注意的是上电复位操作可通过SPI发送上电复位指令或给RSTN一个复位脉冲,本文选择通过SPI发送上电复位操作,在整个过程中RSTN需要一直置高电平,芯片才能正常工作。

 SPI操作指令如下图所示

          如图表所示,上电复位指令为8’h50,初始化为:8'h70,写入寄存器h8x,读寄存器:hBx,x代表的是寄存器的地址,写寄存器和读寄存器的地址可在芯片手册中查询,也可查询文章第一节,举例说明,若写入寄存器0数据,即8'h80+32'hxxxxxxxx,读寄存器0数据,即8'hb0+receiver16/32bit。

        SPI时序部分:结合GP22芯片手册SPI时序图,VCC电压3.3V情况下,SPI串口最大速率为20MHz,即SCK时钟周期最小为25ns,时钟相位为1,时钟极性为0,gp22芯片发送和接收数据是在SCK下降沿时,也就是说在用fpga发送mosi管脚的数据时,可在SCK上升沿改变数据,保证在SCK下降沿时GP22接收信号的稳定性;接收GP22数据时在SCK下降沿缓存miso管脚的数据。本程序设置SCK时钟周期为30ns,可保证寄存器的正常写入和读取。SSN管脚空闲状态置高,在写入或读取数据时拉低,时序要求:SSN下降沿据第一个SCK上升沿时间最小间隔为10ns,拉高时上升沿距最后一个SCK下降沿时间最小间隔40ns,每次写入新的指令或寄存器时,中间SSN需置高50ns后再拉低写入。

module test_2(
    clk,
    reset_n,
    
    key_one,
    RST,
    
//    //tb test
//    current_state_tb,
//    sck_count_tb,
//    state_count_tb,
    
    //
    spi_ssn,
    spi_mosi,
    spi_miso,
    spi_sck,
    uart_tx
    );
    input clk;
    input reset_n;
    input key_one;
    input spi_miso;
    output wire RST;
    output reg spi_mosi;
    output reg spi_ssn;
    output reg spi_sck;
    output wire uart_tx;
    
//    //tb_test
//    output wire [3:0]current_state_tb;
//    output wire [3:0]sck_count_tb;
//    output wire [7:0]state_count_tb;
    
    assign RST = 1;
    
    reg [2:0]current_state;
    reg [7:0]state_count;
    parameter   IDEL = 3'd0,
                 power_reset = 3'd1,
                 Init = 3'd2,
                 write_reg_1 = 3'd3,
                 read_reg_5 = 3'd4;
                 
       wire clk_100m;
     clk_100m instance_name
   (
    // Clock out ports
    .clk_out1(clk_100m),     // output clk_out1
   // Clock in ports
    .clk_in1(clk));      // input clk_in1                
 /*****************************按键信号*******************************************************/
    reg [2:0]key_one_reg;
    always@(posedge clk_100m or negedge reset_n)
    if(!reset_n)
        key_one_reg <= 3'd0;
    else begin
        key_one_reg <= {key_one_reg[1:0],key_one};
    end
        
     wire key_en;
     assign key_en = ((key_one_reg[2])&&(!key_one_reg[1]))?1:0;   
/*****************************************状态切换******************************************/
    always@(posedge clk_100m or negedge reset_n)
    if(!reset_n)begin
          current_state <= IDEL;
          state_count <= 8'd0;
          end
    else begin
        case(current_state)
        IDEL:begin
                if(key_en)
                    current_state <= power_reset;
                else
                    current_state <= current_state;
             end
        power_reset:begin
                if(state_count == 53)begin
                    current_state <= Init;
                    state_count <= 0;
                    end
                else begin
                    current_state <= current_state;
                    state_count <= state_count + 1'd1;
                    end
             end
        Init:begin
                if(state_count == 53)begin
                    current_state <= write_reg_1;
                    state_count <= 0;
                    end
                else begin
                    current_state <= current_state;
                    state_count <= state_count + 1'd1;
                    end
             end 
        write_reg_1:begin
                if(state_count == 244)begin
                    current_state <= read_reg_5;
                    state_count <= 0;
                    end
                else begin
                    current_state <= current_state;
                    state_count <= state_count + 1'd1;
                    end
             end 
        read_reg_5:begin
                if(state_count == 100)begin
                    current_state <= IDEL;
                    state_count <= 0;
                    end
                else begin
                    current_state <= current_state;
                    state_count <= state_count + 1'd1;
                    end
             end  
        endcase
    end     
/****************************************************SPI信号***********************************************************/
     reg spi_sck_en;
     always@(posedge clk_100m or negedge reset_n)
     if(!reset_n)begin
            spi_ssn <= 1;
            spi_sck_en <= 0;
        end
     else begin
        case(current_state)
        IDEL:begin
                spi_ssn <= 1;
                spi_sck_en <= 0;
             end
        power_reset:begin
                if(state_count == 2)
                    spi_sck_en <= 1;
                else if(state_count == 3)
                    spi_ssn <= 0;
                else if(state_count == 49)
                    spi_sck_en <= 0;
                else if(state_count == 52)
                    spi_ssn <= 1;
                else begin
                    spi_ssn <= spi_ssn;
                    spi_sck_en <= spi_sck_en;
                    end
             end
        Init:begin
               if(state_count == 2)
                    spi_sck_en <= 1;
                else if(state_count == 3)
                    spi_ssn <= 0;
                else if(state_count == 49)
                    spi_sck_en <= 0;
                else if(state_count == 52)
                    spi_ssn <= 1;
                else begin
                    spi_ssn <= spi_ssn;
                    spi_sck_en <= spi_sck_en;
                    end 
             end 
        write_reg_1:begin
                if(state_count == 2)
                    spi_sck_en <= 1;
                else if(state_count == 3)
                    spi_ssn <= 0;
                else if(state_count == 241)
                    spi_sck_en <= 0;
                else if(state_count == 244)
                    spi_ssn <= 1;
                else begin
                    spi_ssn <= spi_ssn;
                    spi_sck_en <= spi_sck_en;
                    end 
              end
        read_reg_5:begin
                if(state_count == 2)
                    spi_sck_en <= 1;
                else if(state_count == 3)
                    spi_ssn <= 0;
                else if(state_count == 97)
                    spi_sck_en <= 0;
                else if(state_count == 100)
                    spi_ssn <= 1;
                else begin
                    spi_ssn <= spi_ssn;
                    spi_sck_en <= spi_sck_en;
                    end 
            end
        endcase
     end
    //sck信号
    reg [3:0]sck_count;
    always@(posedge clk_100m)
    if(!spi_sck_en)
        sck_count <= 0;
    else if(sck_count == 5)
        sck_count <= 0;
    else
        sck_count <= sck_count + 4'd1;
    
    always@(posedge clk_100m)
    if(!spi_sck_en)
        spi_sck <= 0;
    else if(sck_count == 1)
        spi_sck <= 1;
    else if(sck_count == 4)
        spi_sck <= 0;
//spi_mosi信号
    reg [7:0]spi_opcode;
    reg [31:0]spi_write_data;
    reg [39:0]write;
    reg [7:0]receive_8bit;
    always@(posedge clk_100m or negedge reset_n)
    if(!reset_n)begin
            spi_opcode <= 8'd0;
            spi_write_data <= 32'd0;
            spi_mosi <= 0;
            receive_8bit <= 0;
          end
    else begin
        case(current_state)
        IDEL:begin
                spi_mosi <= 0;
             end
        power_reset:begin
                if(state_count == 1)
                    spi_opcode <= 8'h50;
                else if(sck_count == 1)begin  
                        spi_opcode <= {spi_opcode[6:0],1'b0};
                        spi_mosi <= spi_opcode[7];
                     end
             end
        Init:begin
            if(state_count == 1)
                    spi_opcode <= 8'h70;
                else if(sck_count == 1)begin  
                        spi_opcode <= {spi_opcode[6:0],1'b0};
                        spi_mosi <= spi_opcode[7];
                     end
             end  
        write_reg_1:begin
               if(state_count == 1)begin
                    spi_opcode <= 8'h81;
                    spi_write_data <= 32'b1111_1010_10_001_001_10_000_000_10101010;
                    end
               else if(state_count == 2)
                    write <= {spi_opcode,spi_write_data};
               else if(sck_count == 1)begin 
                        write <= {write[38:0],1'b0};
                        spi_mosi <= write[39];
                     end
             end 
        read_reg_5:begin
              if(state_count == 1)
                    spi_opcode <= 8'hB5;
                else if(state_count < 50)begin
                        if(sck_count == 1)begin  
                            spi_opcode <= {spi_opcode[6:0],1'b0};
                            spi_mosi <= spi_opcode[7];
                         end
                  end
                else begin
                        if(sck_count == 4)begin 
                            spi_mosi <= 0; 
                            receive_8bit <= {receive_8bit[6:0],spi_miso};
                         end
                        else 
                            receive_8bit <= receive_8bit;
                     end
             end    
        endcase
    end   
    
    ila_0 your_instance_name (
	.clk(clk_100m), // input wire clk


	.probe0(spi_miso), // input wire [0:0]  probe0  
	.probe1(spi_mosi), // input wire [0:0]  probe1 
	.probe2(spi_sck), // input wire [0:0]  probe2 
	.probe3(spi_ssn), // input wire [0:0]  probe3 
	.probe4(key_en), // input wire [0:0]  probe4 
	.probe5(spi_sck_en), // input wire [0:0]  probe5 
	.probe6(receive_8bit) // input wire [7:0]  probe6
);
    
//    assign current_state_tb = current_state;
//    assign sck_count_tb = sck_count;
//    assign state_count_tb = state_count;
endmodule

        进行程序仿真时候需要加上代码中注释的几行代码,要自己加上PLL时钟倍频,输出时钟频率为100MHz,加入ILA探针观察是否正常读取寄存器数据。

tb程序

`timescale 1ns / 1ps
module test_2_tb();
    reg reset_n;
    reg clk;
    reg key_one;
    wire uart_tx;
    
    wire spi_ssn ;
    wire spi_sck ;
    wire spi_mosi;
    reg spi_miso;
    wire RST;
    
    wire [3:0]current_state_tb;
    wire [3:0]sck_count_tb;
    wire [7:0]state_count_tb;
    
    test_2 test_2(
    .clk(clk),
    .reset_n(reset_n),
    
    .key_one(key_one),
    .RST(RST),
    
    .current_state_tb(current_state_tb),
    .sck_count_tb(sck_count_tb),
    .state_count_tb(state_count_tb),
    
    .spi_ssn(spi_ssn),
    .spi_mosi(spi_mosi),
    .spi_miso(spi_miso),
    .spi_sck(spi_sck),
    .uart_tx(uart_tx)
    );
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
    reset_n = 0;
    key_one = 0;
    spi_miso = 0;
    #21;
    reset_n = 1;
    #200;
    key_one = 1;
    #2000;
    key_one = 0;
    result;
    #6000;
    $stop;
    end
    
    task result;
    begin
        wait((current_state_tb == 4)&&(state_count_tb >= 50))
            repeat(4)
                begin
                @(sck_count_tb == 2);
                spi_miso = 1;
                #20;
                @(sck_count_tb == 2);
                spi_miso = 0;
                #20;
                end
        end
       endtask
endmodule

仿真波形如下图所示


        本程序是为了便于ILA抓取查看信号,通过按键信号使从IDEL状态进入写状态,若板子无预留按键接口,也可稍微修改程序循环写入读取。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值