[verilog]

求大神解释一下SPI代码,有几点不太清楚,感谢!

[复制链接]
1323|10
手机看帖
扫描二维码
随时随地手机跟帖
jane4243|  楼主 | 2018-12-13 14:45 | 显示全部楼层 |阅读模式
代码中红色字体的标注是分频器,但是我看不懂是如何分频,分的是多少频
代码中蓝色字体的是读写过程,我想问的是,主机写到mosi的时候,是txdata【7】的数据移到mosi上,但是txdata没有移位啊,那岂不是写到mosi上的一直是txdata【7】的值?我在猜会不会跟testbench联系起来看,我把testbench代码也贴出来了,麻烦有没用过这个代码的大神出来解释一下,感激不尽啊


/***********************************************************************************************
* SPI MASTER
* January 2007
************************************************************************************************/
`timescale 10ns/1ns
module SPI_Master ( miso, mosi, sclk, ss, data_bus, CS, addr, pro_clk, WR, RD);

inout [7:0] data_bus;           // 8 bit bidirectional data bus
input pro_clk;                  // Host Processor clock
input miso;                     // Master in slave out
input [1:0] addr;               // A1 and A0, lower bits of address bus
input CS;                       // Chip Select
input WR, RD;                   // Write and read enables

output mosi;                    // Master out slave in
output sclk;                    // SPI clock
output [7:0] ss;                // 8 slave select lines

reg [7:0] shift_register;        // Shift register
reg [7:0] txdata;                // Transmit buffer
reg [7:0] rxdata;                // Receive buffer
reg [7:0] data_out;              // Data output register
reg [7:0] data_out_en;           // Data output enable
reg [7:0] control, status;       // Control Register COntrols things like ss, CPOL, CPHA, clock divider
                                 // Status Register is a dummy register never used.

reg [7:0] clk_divide;            // Clock divide counter
reg [3:0] count;                 // SPI word length counter
reg sclk;                        
reg slave_cs;                    // Slave cs flag
reg mosi;                                           // Master out slave in
reg spi_word_send;               // Will send a new spi word.

wire [7:0] data_bus;
wire [7:0] data_in = data_bus;
wire spi_clk_gen;
wire [2:0] divide_factor = control[2:0];
wire CPOL = control[3];         
wire CPHA = control[4];
wire [7:0]ss;


/* Slave Select lines */
assign ss[7] = ~( control[7] &  control[6] &  control[5] & (~slave_cs));
assign ss[6] = ~( control[7] &  control[6] & ~control[5] & (~slave_cs));
assign ss[5] = ~( control[7] & ~control[6] &  control[5] & (~slave_cs));
assign ss[4] = ~( control[7] & ~control[6] & ~control[5] & (~slave_cs));
assign ss[3] = ~(~control[7] &  control[6] &  control[5] & (~slave_cs));
assign ss[2] = ~(~control[7] &  control[6] & ~control[5] & (~slave_cs));
assign ss[1] = ~(~control[7] & ~control[6] &  control[5] & (~slave_cs));
assign ss[0] = ~(~control[7] & ~control[6] & ~control[5] & (~slave_cs));

/* clock divide */
assign spi_clk_gen = clk_divide[divide_factor];

/* Clock Divider */
always @ (negedge pro_clk) begin
    clk_divide = clk_divide + 1;     
end

/* Reading the miso line and shifting */
always @ (posedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin
    if (spi_word_send) begin
        shift_register[7:0] = txdata;
    end else begin
        shift_register = shift_register << 1;
        shift_register[0] <= miso;
    end
end

/* Writing the mosi */
always @ (negedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin
    if (spi_word_send) begin
        mosi = txdata[7];
    end else begin
        mosi = shift_register[7];
    end
end

/* Contolling the interrupt bit in the status bit */
always @ (posedge slave_cs or posedge spi_word_send) begin
    if (spi_word_send) begin
        status[0] = 0;
    end else begin
            status = 8'h01;
                rxdata = shift_register;         // updating read buffer
        end
end

/* New SPI wrod starts when the transmit buffer is updated */
always @ (posedge pro_clk) begin
    if (spi_word_send) begin
        slave_cs <= 0;
    end else if ((count == 8) & ~(sclk ^ CPOL)) begin
        slave_cs <= 1;
    end   
end

/* New Spi word is intiated when transmit buffer is updated */
always @ (posedge pro_clk) begin
    if (CS & WR & addr[1] & ~addr[0]) begin
        spi_word_send <=1;
    end else begin
        spi_word_send <=0;
    end
end

/* Generating the SPI clock */
always @ (posedge spi_clk_gen) begin
    if (~slave_cs) begin
        sclk = ~sclk;
    end else if (~CPOL) begin
        sclk = 0;
    end else begin
        sclk = 1;
    end
end

/* Counting SPI word length */
always @ (posedge sclk or posedge slave_cs) begin
    if (slave_cs) begin
        count = 0;
    end else begin   
        count = count + 1;
    end
end

/* Reading, writing SPI registers */
always @ (posedge pro_clk) begin
    if (CS) begin
        case (addr)
        2'b00 : if (WR) control <= data_in;   //wire [7:0] data_in = data_bus;    undestand
        2'b01 : if (RD) data_out <= status;   // Void
                2'b10 : if (WR) txdata <= data_in;    //wire [7:0] data_in = data_bus;    undestand  
                2'b11 : if (RD) data_out <= rxdata;
        endcase
    end
end

/* Controlling the data out enable */
always @ (RD or data_out) begin
    if (RD)
    data_out_en = data_out;
    else
    data_out_en = 8'bz;
end

assign data_bus = data_out_en;

initial
begin
    mosi = 0;
    //sclk = 0;
    control = 0;
    count = 0;
    slave_cs = 1;
    txdata = 0;
    rxdata = 0;
    clk_divide = 0;
    data_out = 0;
end

endmodule

/********************************************** END ******************************************************************/

/******************************************************************************************
* Test Bench for SPI Master
* January 2007
*******************************************************************************************/

`timescale 10ns/1ns

module SPI_master_test;

wire [7:0] data_bus;     // Bidirectional
wire mosi;               // Output from main module
wire sclk;               // Output from main module
wire [7:0] ss;           // Output from main module

/* Inputs to main module */
reg miso;               
reg CS;
reg [1:0] addr;
reg pro_clk;
reg WR,RD;

SPI_Master tb ( miso, mosi, sclk, ss, data_bus, CS, addr, pro_clk, WR, RD);

/* Internal registers defined for TB */
reg [7:0] data_send;
reg [7:0] transmit_store;
reg [7:0] data_receive;
reg [7:0] miso_data;
reg [7:0] mosi_data;

assign data_bus = data_send;

initial                         // Generates serial clock of time period 10
  begin
    pro_clk = 0;
    forever #5 pro_clk = !pro_clk;
  end

initial
  begin
    CS = 0;
    RD = 0;
    WR = 0;
    data_send = 0;
    addr = 0;
    miso = 0;

    #20
    /* Updating Control register */
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    data_send = 0;            
    addr = 0;
    /* Updating Transmit buffer */
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    data_send = $random;
    addr = 2'b10;
    #1 transmit_store = data_send;
    @ (negedge pro_clk)
    $display ("Transmit Buffer loaded");
    $display ("SS[0] = 0, CPHA = 0, CPOL = 0 at time:",$time);
    $display ("Observe Waveform for spi clock frequency, spi data changing at falling egde, valid at rising edge");
    CS = 0;
    WR = 0;
    data_send = 8'bz;

    @ (posedge ss[0])
    #20

    /* Checking Status */
    @ (negedge pro_clk)
    CS = 1;
    RD = 1;
    addr = 2'b01;
    @ (negedge pro_clk)
    data_receive = data_bus;
    @ (negedge pro_clk)
    if (data_receive[0]) begin
        $display("Interrupt detected at time:", $time);
        addr = 2'b11;
    end else begin
        $display("Interrupt detect failed at time:", $time);
    end
    @ (negedge pro_clk)
    data_receive = data_bus;
    if (data_bus == miso_data) begin
        $display("Data received from spi slave verified", $time);
    end else begin
        $display("Data receive failed",$time);
    end
    /* Writing new control word */
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    RD = 0;
    data_send = 8'b10001001;            
    addr = 0;
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    data_send = $random;
    addr = 2'b10;
    #1 transmit_store = data_send;
    @ (negedge pro_clk)
    $display ("\n Transmit Buffer reloaded");
    $display ("Observe Waveform for spi clock frequency, spi data changing at rising edge and valid at falling edge");
    $display ("SS[4] = 0, CPHA = 0, CPOL = 1 at time:",$time);
    CS = 0;
    RD = 0;
    WR = 0;
    data_send = 8'bz;

    @ (posedge ss[4])
    #20

    /* Checking Status */
    @ (negedge pro_clk)
    CS = 1;
    RD = 1;
    addr = 2'b01;
    @ (negedge pro_clk)
    data_receive = data_bus;
    @ (negedge pro_clk)
    if (data_receive[0]) begin
        $display("Interrupt detected at time:", $time);
        addr = 2'b11;
    end else begin
        $display("Interrupt detect failed at time:", $time);
    end
    @ (negedge pro_clk)
    data_receive = data_bus;
    if (data_bus == miso_data) begin
        $display("Data received from spi slave verified", $time);
    end else begin
        $display("Data receive failed",$time);
    end
    /* Writing new control word */
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    RD = 0;
    data_send = 8'b11110010;            
    addr = 0;
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    data_send = $random;
    addr = 2'b10;
    #1 transmit_store = data_send;
    @ (negedge pro_clk)
    $display ("\n Transmit Buffer reloaded");
    $display ("Observe Waveform for spi clock frequency, spi data changing at rising edge and valid at falling edge");
    $display ("SS[7] = 0, CPHA = 1, CPOL = 0 at time:",$time);
    CS = 0;
    RD = 0;
    WR = 0;
    data_send = 8'bz;

    @ (posedge ss[7])
    #20

    /* Checking Status */
    @ (negedge pro_clk)
    CS = 1;
    RD = 1;
    addr = 2'b01;
    @ (negedge pro_clk)
    data_receive = data_bus;
    @ (negedge pro_clk)
    if (data_receive[0]) begin
        $display("interrupt detected at time:", $time);
        addr = 2'b11;
    end else begin
        $display("interrupt detect failed at time:", $time);
    end
    @ (negedge pro_clk)
    data_receive = data_bus;
    if (data_bus == miso_data) begin
        $display("Data received from spi slave verified", $time);
    end else begin
        $display("Data receive failed",$time);
    end
    /* Writing new control word */
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    RD = 0;
    data_send = 8'b01111100;            
    addr = 0;
    @ (negedge pro_clk)
    CS = 1;
    WR = 1;
    data_send = $random;
    addr = 2'b10;
    #1 transmit_store = data_send;
    @ (negedge pro_clk)
    $display ("\n Transmit Buffer reloaded");
    $display ("Observe Waveform for spi clock frequency, spi data changing at falling edge and valid at rising edge");
    $display ("SS[3] = 0, CPHA = 1, CPOL = 1 at time:",$time);
    CS = 0;
    RD = 0;
    WR = 0;
    data_send = 8'bz;

    @ (posedge ss[3])
    #20

    /* Checking Status */
    @ (negedge pro_clk)
    CS = 1;
    RD = 1;
    addr = 2'b01;
    @ (negedge pro_clk)
    data_receive = data_bus;
    @ (negedge pro_clk)
    if (data_receive[0]) begin
        $display("Interrupt detected at time:", $time);
        addr = 2'b11;
    end else begin
        $display("Interrupt detect failed at time:", $time);
    end
    @ (negedge pro_clk)
    data_receive = data_bus;
    if (data_bus == miso_data) begin
        $display("Data received from spi slave verified", $time);
    end else begin
        $display("Data receive failed",$time);
    end
    @ (negedge pro_clk)
    $display ("\n PASS: hit break to stop simulation");

end

initial
begin
    /* Writing MISO / Reading MOSI for random values */
    #20
    miso_data = $random;
    @ (negedge ss[0])
    miso = miso_data[7];
    @ (posedge sclk)
    mosi_data[7] = mosi;
    @ (negedge sclk)
    miso = miso_data[6];
    @ (posedge sclk)
    mosi_data[6] = mosi;
    @ (negedge sclk)
    miso = miso_data[5];
    @ (posedge sclk)
    mosi_data[5] = mosi;
    @ (negedge sclk)
    miso = miso_data[4];
    @ (posedge sclk)
    mosi_data[4] = mosi;
    @ (negedge sclk)
    miso = miso_data[3];
    @ (posedge sclk)
    mosi_data[3] = mosi;
    @ (negedge sclk)
    miso = miso_data[2];
    @ (posedge sclk)
    mosi_data[2] = mosi;
    @ (negedge sclk)
    miso = miso_data[1];
    @ (posedge sclk)
    mosi_data[1] = mosi;
    @ (negedge sclk)
    miso = miso_data[0];
    @ (posedge sclk)
    mosi_data[0] = mosi;
    #5
    if(mosi_data == transmit_store) begin
        $display("Data transmitted to spi slave verified", $time );
    end else begin
        $display("Data Transmit to spi slave failed !", $time );
    end

    /* Next set : CPOL = 1, CPHA = 0 */        
    @ (negedge ss[4])
    miso_data = $random;
    miso = miso_data[7];
    @ (negedge sclk)
    mosi_data[7] = mosi;
    @ (posedge sclk)
    miso = miso_data[6];
    @ (negedge sclk)
    mosi_data[6] = mosi;
    @ (posedge sclk)
    miso = miso_data[5];
    @ (negedge sclk)
    mosi_data[5] = mosi;
    @ (posedge sclk)
    miso = miso_data[4];
    @ (negedge sclk)
    mosi_data[4] = mosi;
    @ (posedge sclk)
    miso = miso_data[3];
    @ (negedge sclk)
    mosi_data[3] = mosi;
    @ (posedge sclk)
    miso = miso_data[2];
    @ (negedge sclk)
    mosi_data[2] = mosi;
    @ (posedge sclk)
    miso = miso_data[1];
    @ (negedge sclk)
    mosi_data[1] = mosi;
    @ (posedge sclk)
    miso = miso_data[0];
    @ (negedge sclk)
    mosi_data[0] = mosi;
    #5
    if(mosi_data == transmit_store) begin
        $display("Data transmitted to spi slave verified", $time );
    end else begin
        $display("Data Transmit to spi slave failed !", $time );
    end

    /* Next set : CPOL = 0, CPHA = 1 */        
    @ (negedge ss[7])
    miso_data = $random;
    @ (posedge sclk)
    miso = miso_data[7];
    @ (negedge sclk)
    mosi_data[7] = mosi;
    @ (posedge sclk)
    miso = miso_data[6];
    @ (negedge sclk)
    mosi_data[6] = mosi;
    @ (posedge sclk)
    miso = miso_data[5];
    @ (negedge sclk)
    mosi_data[5] = mosi;
    @ (posedge sclk)
    miso = miso_data[4];
    @ (negedge sclk)
    mosi_data[4] = mosi;
    @ (posedge sclk)
    miso = miso_data[3];
    @ (negedge sclk)
    mosi_data[3] = mosi;
    @ (posedge sclk)
    miso = miso_data[2];
    @ (negedge sclk)
    mosi_data[2] = mosi;
    @ (posedge sclk)
    miso = miso_data[1];
    @ (negedge sclk)
    mosi_data[1] = mosi;
    @ (posedge sclk)
    miso = miso_data[0];
    @ (negedge sclk)
    mosi_data[0] = mosi;
    #5
    if(mosi_data == transmit_store) begin
        $display("Data transmitted to spi slave verified", $time );
    end else begin
        $display("Data Transmit to spi slave failed !", $time );
    end

    /* Next set : CPOL = 1, CPHA = 1 */        
    @ (negedge ss[3])
    miso_data = $random;
    @ (negedge sclk)
    miso = miso_data[7];
    @ (posedge sclk)
    mosi_data[7] = mosi;
    @ (negedge sclk)
    miso = miso_data[6];
    @ (posedge sclk)
    mosi_data[6] = mosi;
    @ (negedge sclk)
    miso = miso_data[5];
    @ (posedge sclk)
    mosi_data[5] = mosi;
    @ (negedge sclk)
    miso = miso_data[4];
    @ (posedge sclk)
    mosi_data[4] = mosi;
    @ (negedge sclk)
    miso = miso_data[3];
    @ (posedge sclk)
    mosi_data[3] = mosi;
    @ (negedge sclk)
    miso = miso_data[2];
    @ (posedge sclk)
    mosi_data[2] = mosi;
    @ (negedge sclk)
    miso = miso_data[1];
    @ (posedge sclk)
    mosi_data[1] = mosi;
    @ (negedge sclk)
    miso = miso_data[0];
    @ (posedge sclk)
    mosi_data[0] = mosi;
    #5
    if(mosi_data == transmit_store) begin
        $display("Data transmitted to spi slave verified", $time );
    end else begin
        $display("Data Transmit to spi slave failed !", $time );
    end

end

endmodule

/*************************************** END OF TB ***********************************************************************/

相关帖子

zhangmangui| | 2018-12-13 22:14 | 显示全部楼层
assign spi_clk_gen = clk_divide[divide_factor];

/* Clock Divider */
always @ (negedge pro_clk) begin
    clk_divide = clk_divide + 1;     
end

就是一个计数器     比如是reg [3:0]  x,    x[3] 作为时钟    那就是16/2  8分频

使用特权

评论回复
jane4243|  楼主 | 2018-12-14 09:26 | 显示全部楼层
zhangmangui 发表于 2018-12-13 22:14
assign spi_clk_gen = clk_divide[divide_factor];

/* Clock Divider */

你好,我还是不太明白意思,你的意思是x[3]如果作为时钟,剩下的x[2:0]计数,,所以是8分频对吗?

使用特权

评论回复
ococ| | 2018-12-14 12:22 | 显示全部楼层
看仿真波形

使用特权

评论回复
zhangmangui| | 2018-12-15 23:08 | 显示全部楼层
jane4243 发表于 2018-12-14 09:26
你好,我还是不太明白意思,你的意思是x[3]如果作为时钟,剩下的x[2:0]计数,,所以是8分频对吗? ...

你想一下     0000  0001  0010  0011...0111 1000 1001 1010 1000 ... 1111
你看一下    x[3]的变化   不就是多少个周是0   多少个周期是1吗
就是分频

使用特权

评论回复
jianzhimail501| | 2018-12-21 20:53 | 显示全部楼层
一个spi整的这么复杂?我帮你搞定,写地址32位,数据16位,转化成并行总线

使用特权

评论回复
zhangmangui| | 2018-12-22 17:31 | 显示全部楼层
这个看了一下  真是个新手所为   太复杂了
先别看module SPI_master_test;

使用特权

评论回复
jane4243|  楼主 | 2018-12-25 14:26 | 显示全部楼层
zhangmangui 发表于 2018-12-22 17:31
这个看了一下  真是个新手所为   太复杂了
先别看module SPI_master_test;

大神,发送mosi txdata那个问题你懂吗?懂的话,麻烦讲一下

使用特权

评论回复
uglyuglyl| | 2019-1-4 15:53 | 显示全部楼层
看仔细了哦,只有在spi_word_send 有效的情况下才mosi = txdata[7];
其余情况下都是输出txdata移位以后的值的。
这样刚刚好输出从[7]开始向后移。没有问题。
/* Reading the miso line and shifting */
always @ (posedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin
    if (spi_word_send) begin
        shift_register[7:0] = txdata;
    end else begin
        shift_register = shift_register << 1;
        shift_register[0] <= miso;
    end
end

/* Writing the mosi */
always @ (negedge (sclk ^ (CPHA ^ CPOL)) or posedge spi_word_send) begin
    if (spi_word_send) begin
        mosi = txdata[7];
    end else begin
        mosi = shift_register[7];
    end
end

使用特权

评论回复
cyberbill| | 2019-2-11 09:29 | 显示全部楼层
我也在折腾这个代码,还是没搞懂。。。

使用特权

评论回复
aaa456| | 2019-3-14 13:43 | 显示全部楼层
spi哪里需要这么复杂。。。。。。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

3

帖子

0

粉丝