一、实验要求
要求使用SPI协议实现对flash芯片的页编程、读操作、页擦除等功能。
二、模块划分
大概的时序图:
三、程序设计
(1)接收端模块
`timescale 1ns / 1ps
module uart_rx(
input sys_clk ,
input rst_n ,
(* MARK_DEBUG="true" *)input rx_data ,
(* MARK_DEBUG="true" *)output reg[7:0] uart_data ,
(* MARK_DEBUG="true" *)output reg rx_done
);
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud;
parameter MID = COUNT/2 ;
///start_flag
reg rx_reg1 ;
reg rx_reg2 ;
(* MARK_DEBUG="true" *)wire start_flag ;
always@(posedge sys_clk )
if(!rst_n)begin
rx_reg1 <= 1 ;
rx_reg2 <= 1 ;
end
else
begin
rx_reg1 <= rx_data ;
rx_reg2 <= rx_reg1 ;
end
assign start_flag = ~rx_reg1 & rx_reg2 ;
/rx_flag
(* MARK_DEBUG="true" *)reg rx_flag ;
(* MARK_DEBUG="true" *)reg[4:0] cnt_bit ;
(* MARK_DEBUG="true" *)reg[9:0] cnt ;
always@(posedge sys_clk )
if(!rst_n)
rx_flag <= 0 ;
else if ( start_flag == 1 )
rx_flag <= 1 ;
else if ( cnt_bit == 10 && cnt == MID - 1 )
rx_flag <= 0 ;
else
rx_flag <= rx_flag ;
//cnt
always@(posedge sys_clk )
if(!rst_n)
cnt <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt == COUNT - 1 )
cnt <= 0 ;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
/cnt_bit
always@(posedge sys_clk )
if(!rst_n)
cnt_bit <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt == COUNT - 1 )begin
if( cnt_bit == 10 )
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= cnt_bit ;
end
else
cnt_bit <= 0 ;
///data_reg
(* MARK_DEBUG="true" *)reg[8:0] data_reg ; //data_reg:01234567 [8]
always@(posedge sys_clk ) //cnt_bit:[0]12345678[9][10]
if(!rst_n)
data_reg <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt_bit > 0 && cnt_bit < 10 && cnt == MID - 1)
data_reg[cnt_bit - 1 ] <= rx_data ;
else
data_reg <= data_reg ;
end
else
data_reg <= 0 ;
check
(* MARK_DEBUG="true" *)reg check ;
always@(posedge sys_clk )
if(!rst_n)
check <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt_bit == 10 )
check <= ^data_reg ;
else
check <= 0 ;
end
else
check <= 0 ;
uart_data
parameter MODE_CHECK = 0 ;
always@(posedge sys_clk )
if(!rst_n)
uart_data <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt_bit == 10 && cnt == 10 && check == MODE_CHECK)
uart_data <= data_reg[7:0] ;
else
uart_data <= uart_data ;
end
else
uart_data <= uart_data ;
rx_done
always@(posedge sys_clk )
if(!rst_n)
rx_done <= 0 ;
else if ( rx_flag == 1 )begin
if ( cnt_bit == 10 && cnt == MID/2 - 1 )
rx_done <= 1 ;
else
rx_done <= 0 ;
end
else
rx_done <= 0 ;
endmodule
(2)cmd模块
`timescale 1ns / 1ps
长度 指令 地址 数据
/* 这个模块的功能是把接收到的数据放在寄存器里,这个模块的功能也可以由fifo和ram完成
*/
module cmd_data(
input clk ,
input rst_n ,
(* MARK_DEBUG="true" *)input [7:0] data_in ,
(* MARK_DEBUG="true" *)input rx_done ,
(* MARK_DEBUG="true" *)output reg spi_start,
(* MARK_DEBUG="true" *)output reg [7:0] cmd ,
(* MARK_DEBUG="true" *)output reg [7:0] length ,//长度
(* MARK_DEBUG="true" *)output reg [23:0] addr ,//地址
(* MARK_DEBUG="true" *)output reg [7:0] data_out
);
parameter idle = 1 ;
parameter s0 = 2 ;//长度
parameter s1 = 3 ;//指令
parameter s2 = 4 ;//地址
parameter s3 = 5 ;//地址
parameter s4 = 6 ;//地址
parameter s5 = 7 ;//数据
(* MARK_DEBUG="true" *)reg[3:0] cur_state ;
(* MARK_DEBUG="true" *)reg[3:0] next_state ;
state1
always@(posedge clk)
if(!rst_n)
cur_state <= idle ;
else
cur_state <= next_state ;
///state2
always@(*)
case(cur_state)
idle :
begin
if ( rx_done )//长度
next_state = s0 ;
else
next_state = cur_state ;
end
s0 :
begin
if ( rx_done )//指令
next_state = s1 ;
else
next_state = cur_state ;
end
s1 :
begin
if ( rx_done )//地址
next_state = s2 ;
else
next_state = cur_state ;
end
s2 :
begin
if ( rx_done )//地址
next_state = s3 ;
else
next_state = cur_state ;
end
s3 :
begin
if ( rx_done )//地址
next_state = s4 ;
else
next_state = cur_state ;
end
s4 :
begin
if ( rx_done )//数据
next_state = s5 ;
else
next_state = cur_state ;
end
s5 : next_state = idle ;
default:next_state = idle ;
endcase
//state3
always@(posedge clk )
if(!rst_n)begin
spi_start <= 0 ;
cmd <= 0 ;
length <= 0 ;
addr <= 0 ;
data_out <= 0 ;
end
else
case(cur_state)
idle :
begin
spi_start <= 0 ;
cmd <= cmd ;
addr <= addr ;
data_out <= data_out ;
if ( rx_done )
length <= data_in ;
else
length <= length ;
end
s0 :
begin
spi_start <= 0 ;
length <= length ;
addr <= addr ;///其他保持不变避免自动清零
data_out <= data_out ;
if ( rx_done )
cmd <= data_in ;
else
cmd <= cmd ;
end
s1 :
begin
spi_start <= 0 ;
length <= length ;
data_out <= data_out ;
cmd <= cmd ;
if ( rx_done )
addr[23:16] <= data_in ;
else
addr <= addr ;
end
s2 :
begin
spi_start <= 0 ;
length <= length ;
data_out <= data_out ;
cmd <= cmd ;
if ( rx_done )
addr[15:8] <= data_in ;
else
addr <= addr ;
end
s3 :
begin
spi_start <= 0 ;
length <= length ;
data_out <= data_out ;
cmd <= cmd ;
if ( rx_done )
addr[7:0] <= data_in ;
else
addr <= addr ;
end
s4 :
begin
spi_start <= 0 ;
length <= length ;
cmd <= cmd ;
addr <= addr ;
if ( rx_done )
data_out <= data_in ;
else
data_out <= data_out ;
end
s5 :
begin
spi_start <= 1 ;
cmd <= cmd ;
length <= length ;
addr <= addr ;
data_out <= data_out ;
end
default: begin
spi_start <= 0 ;
cmd <= cmd ;
length <= length ;
addr <= addr ;
data_out <= data_out ;
end
endcase
endmodule
(3)send_data模块
`timescale 1ns / 1ps
/*
将数据发送给主机
指令 地址 数据是不能直接给主机模块的
长度可以直接给主机
*/
module send_data(
input clk ,
input rst_n ,
(* MARK_DEBUG="true" *)input [7:0] cmd ,
(* MARK_DEBUG="true" *)input [23:0] addr ,
(* MARK_DEBUG="true" *)input [7:0] data_in ,
(* MARK_DEBUG="true" *)input done_bit ,///传输完一个字节的结束信号
(* MARK_DEBUG="true" *)input done ,SPI传输完一次数据的结束信号
(* MARK_DEBUG="true" *)output reg[7:0] data_spi
);
parameter idle = 1 ;
parameter s0 = 2 ;
parameter s1 = 3 ;
parameter s2 = 4 ;
parameter s3 = 5 ;
parameter s4 = 6 ;
(* MARK_DEBUG="true" *)reg[3:0] cur_state ;
(* MARK_DEBUG="true" *)reg[3:0] next_state ;
///state1
always@(posedge clk )
if(!rst_n)
cur_state <= idle ;
else
cur_state <= next_state ;
state2
always@(*)
case(cur_state)
idle : next_state = s0 ;
s0 :
begin
if ( done_bit )
next_state = s1 ;
else if ( done )
next_state = idle ; ///不加这个信号的话后面发送数据会锁在s1状态
else
next_state = cur_state ;
end
s1 :
begin
if ( done_bit )
next_state = s2 ;
else if ( done )
next_state = idle ;
else
next_state = cur_state ;
end
s2 :
begin
if ( done_bit )
next_state = s3 ;
else if ( done )
next_state = idle ;
else
next_state = cur_state ;
end
s3 :
begin
if ( done_bit )
next_state = s4 ;
else if ( done )
next_state = idle ;
else
next_state = cur_state ;
end
s4 :
begin
if ( done_bit )
next_state = idle ;
else if ( done )
next_state = idle ;
else
next_state = cur_state ;
end
default:next_state = idle ;
endcase
//state3
always@(posedge clk )
if(!rst_n)
data_spi <= 0 ;
else
case(cur_state)
idle : data_spi <= 0 ;
s0 : data_spi <= cmd ;
s1 : data_spi <= addr[23:16] ;
s2 : data_spi <= addr[15:8] ;
s3 : data_spi <= addr[7:0] ;
s4 : data_spi <= data_in ;
default:data_spi <= 0 ;
endcase
endmodule
(4)SPI_Master模块
`timescale 1ns / 1ps
module
SPI_Master #(
// parameter all_bit = 16 ,
parameter data_LEN = 8 ,
parameter data_bit = 8 ,
parameter delay = 8
)
(
(* MARK_DEBUG="true" *)input clk ,
(* MARK_DEBUG="true" *)input rst_n ,
(* MARK_DEBUG="true" *)input spi_start ,
(* MARK_DEBUG="true" *)input miso ,
(* MARK_DEBUG="true" *)input [7:0] data_in ,
(* MARK_DEBUG="true" *)input [data_LEN-1:0] data_len ,
(* MARK_DEBUG="true" *)output reg mosi ,
(* MARK_DEBUG="true" *)output reg done ,整体的结束信号
(* MARK_DEBUG="true" *)output reg[data_bit -1:0] data_out_tx ,
(* MARK_DEBUG="true" *)output reg sck ,
(* MARK_DEBUG="true" *)output reg cs ,
(* MARK_DEBUG="true" *)output done_bit 字节的结束信号
);
// localparam data_in = 16'b0011_1101_0000_0000 ;
// localparam data_len = 2 ;
// localparam data_in = 16'b0011_1101 ;
// localparam data_len = 1 ;
///状态机/
(* MARK_DEBUG="true" *)reg[31:0] cnt_sck ;
(* MARK_DEBUG="true" *)reg[31:0] cnt_bit ;
(* MARK_DEBUG="true" *)reg[2:0] cur_state ;
(* MARK_DEBUG="true" *)reg[2:0] next_state ;
localparam IDLE = 0 ;
localparam s0 = 1 ;
localparam s1 = 2 ;
localparam s2 = 3 ;
/state1
always@(posedge clk )
if(!rst_n)
cur_state <= IDLE ;
else
cur_state <= next_state ;
///state2
always@(*)
case(cur_state)
IDLE :
begin
if ( spi_start == 1 )
next_state <= s0 ;
else
next_state <= cur_state ;
end
s0 :
begin
if ( cnt_sck == 1 )
next_state = s1 ;
else
next_state = cur_state ;
end
s1 :
begin
if ( (cnt_bit+1)/8 == data_len && cnt_sck == delay -1 )
next_state = s2 ;
else
next_state = cur_state ;
end
s2 :
begin
if ( cnt_sck == delay -1 )
next_state = IDLE ;
else
next_state = cur_state ;
end
default:next_state = IDLE;
endcase
state3
always@(posedge clk )
if(!rst_n)begin
cnt_bit <= 0 ;
cnt_sck <= 0 ;
mosi <= 0 ;
sck <= 0 ;
cs <= 1 ;
data_out_tx<= 0 ;
done <= 0 ;
end
else begin
case (cur_state)
IDLE :
begin
cnt_bit <= 0 ;
cnt_sck <= 0 ;
mosi <= 0 ;
sck <= 0 ;
cs <= 1 ;
data_out_tx<= data_out_tx ;
done <= 0 ;
end
s0 :
begin
cnt_bit <= 0 ;
mosi <= 0 ;
sck <= 0 ;
cs <= 0 ;
data_out_tx<= 0 ;
done <= 0 ;
cnt_sck
if ( cnt_sck == delay -1 )
cnt_sck <= 0 ;
else
cnt_sck <= cnt_sck +1 ;
end
s1 :
begin
cs <= 0 ;
done <= 0 ;
/cnt_sck
if ( cnt_sck == delay -1 )
cnt_sck <= 0 ;
else
cnt_sck <= cnt_sck +1 ;
/sck
if ( cnt_sck == delay/2 -1 || cnt_sck == delay -1 )
sck <= ~sck ;
else
sck <= sck ;
/cnt_bit
if ( cnt_sck == delay -1 )begin
if ( cnt_bit == data_len*data_bit -1 )
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= cnt_bit ;
//mosi
if ( cnt_sck == 2 )
// mosi <= data_in [ (data_len*data_bit - 1) - cnt_bit ] ;
mosi <= data_in [ 7 -(cnt_bit)%8 ] ;根据data_in的不同进行调整
else
mosi <= mosi ;
/data_out
if ( cnt_sck == delay -2 )/上升沿采集数据
data_out_tx <= { data_out_tx [data_bit-2:0] , miso } ;
else
data_out_tx <= data_out_tx ;
end
s2 :
begin
cnt_bit <= 0 ;
mosi <= 0 ;
cs <= 1 ;
sck <= 0 ;
data_out_tx<= data_out_tx ;
/cnt_sck
if ( cnt_sck == delay -1 )
cnt_sck <= 0 ;
else
cnt_sck <= cnt_sck +1 ;
///sck
// if ( cnt_sck == delay/2 -1 || cnt_sck == delay -1 )
// sck <= ~sck ;
// else
// sck <= sck ;
done
if ( cnt_sck == delay -1 )
done <= 1 ;
else
done <= 0 ;
end
default: begin
cnt_bit <= 0 ;
cnt_sck <= 0 ;
mosi <= 0 ;
sck <= sck ;
cs <= 1 ;
data_out_tx<= data_out_tx ;
done <= 0 ;
end
endcase
end
assign done_bit = ( cnt_bit %8 == 7 && cnt_sck == delay -1 )?1:0 ;
endmodule
(5)发送端模块
`timescale 1ns / 1ps
module uart_tx(
input sys_clk ,
input rst_n ,
input tx_start ,
input [7:0] rd_data ,
output reg tx_data ,
output reg tx_done
);
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud;
parameter MID = COUNT/2 ;
//start_flag
reg tx_reg1 ;
reg tx_reg2 ;
wire start_flag ;
always@(posedge sys_clk )
if(!rst_n)begin
tx_reg1 <= 0 ;
tx_reg2 <= 0 ;
end
else
begin
tx_reg1 <= tx_start ;
tx_reg2 <= tx_reg1 ;
end
assign start_flag = tx_reg1 & ~tx_reg2 ;
//tx_flag
reg tx_flag ;
reg [9:0] cnt ;
reg [4:0] cnt_bit ; //0 12345678 9 10
always@(posedge sys_clk )
if(!rst_n)
tx_flag <= 0 ;
else if ( start_flag )
tx_flag <= 1 ;
else if ( cnt == COUNT -1 && cnt_bit == 10 )
tx_flag <= 0 ;
else
tx_flag <= tx_flag ;
//cnt
always@(posedge sys_clk )
if(!rst_n)
cnt <= 0 ;
else if ( tx_flag )begin
if ( cnt == COUNT -1 )
cnt <= 0 ;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
//cnt_bit
always@(posedge sys_clk )
if(!rst_n)
cnt_bit <= 0 ;
else if ( tx_flag )begin
if ( cnt == COUNT -1 )begin
if ( cnt_bit == 10 )
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= cnt_bit ;
end
else
cnt_bit <= 0 ;
//寄存rd_data rd_data随着cur_state变为STOP后清零,在uart_tx模块
//中,cnt_bit == 0 的时候可以捕捉到数据
reg[7:0] data_reg ;
always@(posedge sys_clk )
if(!rst_n)
data_reg <= 0 ;
else if ( tx_flag )begin
if ( cnt_bit == 0 && cnt == MID -1 )
data_reg <= rd_data ;
else
data_reg <= data_reg ;
end
else
data_reg <= data_reg ;
//tx_data
parameter MODE_CHECK = 0 ;
always@(posedge sys_clk )
if(!rst_n) //cnt_bit: 0 12345678 9 10
tx_data <= 0 ; //rd_data: 01234567
else if ( tx_flag )begin
if ( cnt_bit > 0 && cnt_bit <9 )
tx_data <= data_reg [ cnt_bit -1 ] ;
else if ( cnt_bit == 0 )
tx_data <= 0 ;
else if ( cnt_bit == 10 )
tx_data <= 1 ;
else if ( cnt_bit == 9 )
tx_data <= (MODE_CHECK == 0 )? ^rd_data : ~^rd_data ;
else
tx_data <= tx_data ;
end
else
tx_data <= 1 ;
//tx_done
always@(posedge sys_clk )
if(!rst_n)
tx_done <= 0 ;
else if ( tx_flag )begin
if ( cnt == COUNT -1 && cnt_bit == 10 )
tx_done <= 1 ;
else
tx_done <= 0 ;
end
else
tx_done <= 0 ;
endmodule
(6)引脚分配
set_property PACKAGE_PIN K17 [get_ports clk]
set_property PACKAGE_PIN P20 [get_ports cs]
set_property PACKAGE_PIN T20 [get_ports miso]
set_property PACKAGE_PIN V20 [get_ports mosi]
set_property PACKAGE_PIN M20 [get_ports rst_n]
set_property PACKAGE_PIN U15 [get_ports rx_data]
set_property PACKAGE_PIN U20 [get_ports sck]
set_property PACKAGE_PIN W15 [get_ports tx_data]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports cs]
set_property IOSTANDARD LVCMOS33 [get_ports miso]
set_property IOSTANDARD LVCMOS33 [get_ports mosi]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rx_data]
set_property IOSTANDARD LVCMOS33 [get_ports sck]
set_property IOSTANDARD LVCMOS33 [get_ports tx_data]
四、实验结果
FLASH指令集(用到的已经用红框标出来了):
(1)打开写使能
返回FF,捕捉到波形
(2)打开读状态寄存器
返回值02表示打开成功
(3)写入数据
向01这个地址里面写入22 返回FF
(4)读出
返回刚才写入的数字22
(5)擦除
(6)读出
经过刚才的擦除操作,从000001这个地址里看能否读出来数,读不出来就表示擦除成功
返回FF,擦除成功