本实验讲究实用性,故设计思想为:主机先向从机发送地址,若是向从机写入数据,则向从机发送数据,若是读取从机数据,则向从机发送时钟,然后在时钟下降沿读取数据即可。cs信号上升沿作为SPI通信的结束信号。rom程序只是做测试使用。
每次发送16个时钟信号,前八个是地址和命令,后八个是数据。其中:前8个时钟接受的数据的最高位决定着这次通信是读取数据还是写入数据,最高位为1,则是读取数据,为0则是写入数据。
程序:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :spi_slave_2.v ** CreateDate :2015.004 ** Funtions :spi通信试验。FPGA作为从机,与主机进行通信。先接收主机发来的地址,再根据地址最高位来判断是读数据还是些数据, 然后从机是接收数据还是送出数据。地址最高位为高则是读取数据,否则为写数据.上升沿接收数据,下降沿发送数据 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module spi_slave_2 ( clk, rst_n, spi_cs, spi_sck, spi_miso, spi_mosi, spi_over ); input clk; input rst_n; input spi_cs; input spi_sck; input spi_mosi; output reg spi_miso; output spi_over; //-----------------------------// reg spi_cs_2,spi_cs_1; reg spi_sck_2,spi_sck_1; reg spi_mosi_2,spi_mosi_1; wire spi_cs_pos; wire spi_cs_flag; wire spi_sck_neg; wire spi_sck_pos; wire spi_mosi_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin {spi_cs_2,spi_cs_1} <= 2‘b11; {spi_sck_2,spi_sck_1} <= 2‘b00; {spi_mosi_2,spi_mosi_1} <= 2‘b00; end else begin {spi_cs_2,spi_cs_1} <= {spi_cs_1,spi_cs}; {spi_sck_2,spi_sck_1} <= {spi_sck_1,spi_sck}; {spi_mosi_2,spi_mosi_1} <= {spi_mosi_1,spi_mosi}; end end assign spi_cs_pos = ~spi_cs_2 &spi_cs_1; assign spi_cs_flag = spi_cs_2; assign spi_sck_neg = ~spi_sck_1&spi_sck_2; assign spi_sck_pos = ~spi_sck_2&spi_sck_1; assign spi_mosi_flag = spi_mosi_2; assign spi_over = spi_cs_pos; //----------------------------------------// localparam idel = 4‘d0; localparam rxd_addr = 4‘d1; localparam jude_wr_rd = 4‘d2; localparam rxd_data = 4‘d3; localparam rxd_over = 4‘d4; localparam txd_data = 4‘d5; localparam txd_over = 4‘d6; localparam end_sta = 4‘d7; reg [3:0] state; reg [3:0] cnt; reg [7:0] raddr; reg [7:0] rdata; reg [7:0] tdata; reg rover_flag; reg wover_flag; reg rd_flag; wire [7:0] data_out; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= 4‘d0; cnt <= 0; raddr <= 8‘d0; rdata <= 8‘d0; tdata <= 8‘d0; rover_flag <= 0; wover_flag <= 0; rd_flag <= 0; spi_miso <= 1; end else if(!spi_cs_flag) begin case(state) idel: begin state <= rxd_addr; cnt <= 0; raddr <= 8‘d0; rdata <= 8‘d0; tdata <= 8‘d0; rover_flag <= 0; wover_flag <= 0; rd_flag <= 0; spi_miso <= 1; end rxd_addr: begin if(cnt == 8) begin cnt <= 0; state <= jude_wr_rd; end else if(spi_sck_pos) begin cnt <= cnt + 1; raddr[7 - cnt[2:0]] <= spi_mosi_flag; end end jude_wr_rd: begin if(raddr[7] == 1) state <= rxd_data; else begin state <= txd_data; rd_flag <= 1; end end rxd_data: begin if(cnt == 8) begin cnt <= 0; state <= rxd_over; end else if(spi_sck_pos) begin cnt <= cnt + 1; rdata[7 - cnt[2:0]] <= spi_mosi_flag; end end rxd_over: begin rover_flag <= 1; state <= end_sta; end txd_data: begin tdata <= data_out; if(cnt == 8) begin cnt <= 0; state <= txd_over; end else if(spi_sck_pos) begin cnt <= cnt + 1; spi_miso <= tdata[7 - cnt[2:0]]; end end txd_over: begin wover_flag <= 1; state <= end_sta; end end_sta: begin rover_flag <= 0; wover_flag <= 0; state <= end_sta; end default:state <= 4‘d0; endcase end else begin state <= 4‘d0; cnt <= 0; raddr <= 8‘d0; rdata <= 8‘d0; tdata <= 8‘d0; rover_flag <= 0; wover_flag <= 0; rd_flag <= 0; spi_miso <= 1; end end data_rom data_rom_1 ( .clk(clk), .rst_n(rst_n), .wr(rover_flag), .rd(rd_flag), .addr(raddr[6:0]), .data_in(rdata), .data_out(data_out) ); endmodule
ROM:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :data_rom.v ** CreateDate :2015.04 ** Funtions : 简单的数据读写存储程序,配合测试 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module data_rom ( clk, rst_n, wr, rd, addr, data_in, data_out ); input clk; input rst_n; input wr; input rd; input [6:0] addr; input [7:0] data_in; output reg [7:0] data_out; reg [7:0] table_1 [7:0]; wire [7:0] table_2 [7:0]; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin table_1[7] <= 0; table_1[6] <= 0; table_1[5] <= 0; table_1[4] <= 0; table_1[3] <= 0; table_1[2] <= 0; table_1[1] <= 0; table_1[0] <= 0; data_out <= 0; end else if(wr) begin table_1[addr] <= data_in; end else if(rd) data_out <= table_1[addr]; else begin table_1[7] <= table_1[7]; table_1[6] <= table_1[6]; table_1[5] <= table_1[5]; table_1[4] <= table_1[4]; table_1[3] <= table_1[3]; table_1[2] <= table_1[2]; table_1[1] <= table_1[1]; table_1[0] <= table_1[0]; data_out <= data_out; end end assign table_2[7] = table_1[7]; assign table_2[6] = table_1[6]; assign table_2[5] = table_1[5]; assign table_2[4] = table_1[4]; assign table_2[3] = table_1[3]; assign table_2[2] = table_1[2]; assign table_2[1] = table_1[1]; assign table_2[0] = table_1[0]; endmodule
测试程序:
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :spi_slave_tb.v ** CreateDate :2015.04 ** Funtions :测试文件 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ `timescale 1 ns/1 ns module spi_slave_tb ; reg clk; reg rst_n; reg spi_cs; reg spi_sck; wire spi_miso; reg spi_mosi; wire spi_over; spi_slave_2 spi_slave_2_1( .clk, .rst_n, .spi_cs, .spi_sck, .spi_miso, .spi_mosi, .spi_over ); parameter tck = 24; parameter t = 1000/tck; always #(t/2) clk = ~clk; //------------------------------- /* 模仿spi主机的发送程序,这个task很好,仿顺序操作,可以直观的显示过程 */ task spi_sd; input [7:0] data_in; begin #(5*t); spi_sck = 0; spi_mosi= data_in[7]; #(5*t); spi_sck = 1; #(5*t); //send bit[7] spi_sck = 0; spi_mosi= data_in[6]; #(5*t); spi_sck = 1; #(5*t); //send bit[6] spi_sck = 0; spi_mosi= data_in[5]; #(5*t); spi_sck = 1; #(5*t); //send bit[5] spi_sck = 0; spi_mosi= data_in[4]; #(5*t); spi_sck = 1; #(5*t); //send bit[4] spi_sck = 0; spi_mosi= data_in[3]; #(5*t); spi_sck = 1; #(5*t); //send bit[3] spi_sck = 0; spi_mosi= data_in[2]; #(5*t); spi_sck = 1; #(5*t); //send bit[2] spi_sck = 0; spi_mosi= data_in[1]; #(5*t); spi_sck = 1; #(5*t); //send bit[1] spi_sck = 0; spi_mosi= data_in[0]; #(5*t); spi_sck = 1; #(5*t); //send bit[0] spi_sck = 0; end endtask initial begin clk = 0; rst_n = 0; spi_cs = 1; spi_sck = 0; spi_mosi = 1; #(20*t) rst_n = 1; #(10*t); spi_cs = 0; spi_sd(8‘h81); #(50*t); spi_sd(8‘h04); #(50*t); #(50*t); spi_cs = 1; #(20*t); spi_cs = 0; spi_sd(8‘h01); #(50*t); spi_sd(8‘h00); #(50*t); spi_cs = 1; end endmodule
仿真图:
图中可以看出,第一次输入8‘h81,意味着向01的地址写入数据。第二个数8’h04,则是要写入的数据。然后写入数据8‘h01,则意味着要读取01地址的数据,然后发送8个时钟则是再读取数据。
时间: 2024-10-13 21:52:50