本文内容:基于 SPI 协议控制开发板上的 FLASH 进行数据读写操作
SPI_top.v
/*========================================*\
filename : SPI_top.v
description : SPI顶层模块
up file :
reversion :
v1.0 : 2022-8-25 19:09:45
author : 张某某
\*========================================*/
module SPI_top (
input clk ,
input rst_n ,
input [ 2:0] key_in ,
input MISO ,
output SCK ,
output CS ,
output MOSI ,
output [ 5:0] SEL ,
output [ 7:0] DIG
);
// Parameter definition
// Signal definition
wire [ 7:0] data ;
wire [ 7:0] press ;
// Module calls
key_filter U_key_filter(
/*input */ .clk (clk ),
/*input */ .rst_n (rst_n ),
/*input [ 2:0]*/ .key_in (key_in ),
/*output reg [ 2:0]*/ .press (press )
);
spi_m25p16 U_spi_m25p16(
/*input */ .clk (clk ),
/*input */ .rst_n (rst_n ),
/*input [ 2:0]*/ .press (press ),
/*input */ .MISO (MISO ),
/*output reg */ .SCK (SCK ),
/*output reg */ .CS (CS ),
/*output reg */ .MOSI (MOSI ),
/*output reg [ 7:0]*/ .data_out (data )
);
display U_display(
/*input */ .clk (clk ), // 50MHz
/*input */ .rst_n (rst_n ), // 复位信号
/*input [ 7:0]*/ .data (data ),
/*output reg [ 5:0]*/ .SEL (SEL ), // SEL信号
/*output reg [ 7:0]*/ .DIG (DIG ) // DIG信号
);
// Logic description
endmodule
key_filter.v
/*========================================*\
filename : key_filter.v
description : 按键消抖模块
up file :
reversion :
v1.0 : 2022-8-25 19:09:59
author : 张某某
\*========================================*/
module key_filter #(parameter MS_20 = 20'd1000_000)(
input clk ,
input rst_n ,
input [ 2:0] key_in ,
output reg [ 2:0] press
);
// 全局变量定义
// 信号定义
reg [ 2:0] key_0 ; // 按键信号当前时钟周期电平
reg [ 2:0] key_1 ; // 按键信号下一个时钟周期电平
wire [ 2:0] key_nedge ; // 下降沿使能信号
reg add_flag ; // 计数使能信号
reg [19:0] delay_cnt ; // 延时计数器
// 模块功能
//打拍器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_0 <= 'b1;
key_1 <= 'b1;
end
else begin
key_0 <= key_in;
key_1 <= key_0;
end
end
// 检测下降沿
assign key_nedge = ~key_0 & key_1;
// 计数使能信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
add_flag <= 'b0;
end
else if (key_nedge) begin
add_flag <= 'b1;
end
else if (delay_cnt >= MS_20 - 1) begin
add_flag <= 'b0;
end
else begin
add_flag <= add_flag;
end
end
// 计数20ms
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
delay_cnt <= 20'd0;
end
else if (add_flag) begin
if (delay_cnt >= MS_20 - 1) begin
delay_cnt <= 20'd0;
end
else begin
delay_cnt <= delay_cnt + 1;
end
end
else begin
delay_cnt <= 20'd0;
end
end
// 输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
press <= 'd0;
end
else if (delay_cnt >= MS_20 - 1) begin
press <= ~key_in;
end
else begin
press <= 'd0;
end
end
endmodule
spi_m25p16.v
/*========================================*\
filename : spi_m25p16.v
description : 基于SPI控制M25P16
up file : SPI_top.v
reversion :
v1.0 : 2022-8-25 19:08:52
author : 张某某
\*========================================*/
module spi_m25p16 #(parameter S_3 = 28'd50_000_000)(
input clk ,
input rst_n ,
input [ 2:0] press ,
input MISO ,
output reg SCK ,
output reg CS ,
output reg MOSI ,
output reg [ 7:0] data_out
);
// Parameter definition
parameter ORDER_WREN = 8'h06 ,
ORDER_SE = 8'hd8 ,
ORDER_PP = 8'h02 ,
ORDER_READ = 8'h03 ;
parameter IDLE = 8'b0000_0001 ,
WREN = 8'b0000_0010 ,
WAIT = 8'b0000_0100 ,
SEPP = 8'b0000_1000 ,
RDEN = 8'b0001_0000 ,
ADDRESS = 8'b0010_0000 ,
DATA = 8'b0100_0000 ,
FINAL = 8'b1000_0000 ;
// Signal definition
reg [31:0] state_ascll ;
reg [ 7:0] state_c ; // 现态
reg [ 7:0] state_n ; // 次态
wire idle2wren ;
wire idle2rden ;
wire wren2wait ;
wire wait2sepp ;
wire sepp2address ;
wire rden2address ;
wire address2final ;
wire address2data ;
wire data2final ;
wire final2idle ;
reg flag_SE ;
reg flag_PP ;
reg flag_RD ;
reg [ 3:0] cnt_SCK_fre ; // SCK频率计数器
wire add_cnt_SCK_fre ;
wire end_cnt_SCK_fre ;
reg [ 2:0] press_0 ; // press时序逻辑化
reg [ 7:0] send_data ; // 发送的1byte数据
reg [ 7:0] receive_data ; // 接收的1byte数据
reg [ 2:0] cnt_bit ; // bit计数器
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [ 1:0] cnt_byte ; // byte计数器
wire add_cnt_byte ;
wire end_cnt_byte ;
reg [27:0] cnt_3s ; // 计数三秒
wire add_cnt_3s ;
wire end_cnt_3s ;
// Logic description
// 仿真时显示state_c对应的字符串
always @(*) begin
case (state_c)
IDLE : state_ascll = 32'h49444C45;
WREN : state_ascll = 32'h5752454E;
WAIT : state_ascll = 32'h57414954;
SEPP : state_ascll = 32'h53455050;
RDEN : state_ascll = 32'h5244454E;
ADDRESS : state_ascll = 32'h41444452;
DATA : state_ascll = 32'h44415441;
FINAL : state_ascll = 32'h46494E41;
default : state_ascll = 32'h49444C45;
endcase
end
// 第一段 状态转移
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
// 第二段 状态转移规律
always @(*) begin
case (state_c)
IDLE :
begin
if (idle2wren) state_n = WREN;
else if (idle2rden) state_n = RDEN;
else state_n = state_c;
end
RDEN :
begin
if (rden2address) state_n = ADDRESS;
else state_n = state_c;
end
WREN :
begin
if (wren2wait) state_n = WAIT;
else state_n = state_c;
end
WAIT :
begin
if (wait2sepp) state_n = SEPP;
else state_n = state_c;
end
SEPP :
begin
if (sepp2address) state_n = ADDRESS;
else state_n = state_c;
end
ADDRESS :
begin
if (address2final) state_n = FINAL;
else if (address2data) state_n = DATA;
else state_n = state_c;
end
DATA :
begin
if (data2final) state_n = FINAL;
else state_n = state_c;
end
FINAL :
begin
if (final2idle) state_n = IDLE;
else state_n = state_c;
end
default : state_n = state_c;
endcase
end
assign idle2wren = state_c == IDLE && (flag_SE || flag_PP) && cnt_SCK_fre == 'd6;
assign idle2rden = state_c == IDLE && flag_RD && cnt_SCK_fre == 'd6;
assign wren2wait = state_c == WREN && end_cnt_bit;
assign wait2sepp = state_c == WAIT && cnt_SCK_fre == 'd6;
assign sepp2address = state_c == SEPP && end_cnt_bit;
assign address2final = state_c == ADDRESS && flag_SE && end_cnt_byte;
assign address2data = state_c == ADDRESS && (flag_PP || flag_RD) && end_cnt_byte;
assign rden2address = state_c == RDEN && end_cnt_bit;
assign data2final = state_c == DATA && end_cnt_bit;
assign final2idle = state_c == FINAL && end_cnt_3s;
// 第三段 描述输出
// SCK时钟信号频率计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_SCK_fre <= 4'd0;
end
else if (add_cnt_SCK_fre) begin
if (end_cnt_SCK_fre) begin
cnt_SCK_fre <= 4'd0;
end
else begin
cnt_SCK_fre <= cnt_SCK_fre + 4'd1;
end
end
else begin
cnt_SCK_fre <= 4'd0;
end
end
assign add_cnt_SCK_fre = 1'b1;
assign end_cnt_SCK_fre = add_cnt_SCK_fre && cnt_SCK_fre >= 4'd9;
// SCK时钟信号生成
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
SCK <= 1'd0;
end
else begin
case (cnt_SCK_fre)
4'd9 : SCK <= 1'd0;
4'd4 : SCK <= 1'd1;
default: SCK <= SCK;
endcase
end
end
// 对press时序逻辑化
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
press_0 <= 3'd0;
end
else begin
press_0 <= press;
end
end
// 按键控制使能信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag_SE <= 1'b0;
flag_PP <= 1'b0;
flag_RD <= 1'b0;
end
else if (final2idle) begin
flag_SE <= 1'b0;
flag_PP <= 1'b0;
flag_RD <= 1'b0;
end
else begin
case (press_0)
3'b100 : flag_SE <= 1'b1;
3'b010 : flag_PP <= 1'b1;
3'b001 : flag_RD <= 1'b1;
default:
begin
flag_SE <= flag_SE;
flag_PP <= flag_PP;
flag_RD <= flag_RD;
end
endcase
end
end
// CS片选信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
CS <= 1'd1;
end
else begin
case (state_c)
WREN, SEPP, ADDRESS, DATA, RDEN : CS <= 1'd0;
default: CS <= 1'd1;
endcase
end
end
// 待发送的1byte数据
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
send_data <= 8'd0;
end
else begin
case (state_c)
WREN : send_data <= ORDER_WREN;
SEPP :
begin
if (flag_SE) send_data <= ORDER_SE;
else if (flag_PP) send_data <= ORDER_PP;
else send_data <= 8'd0;
end
ADDRESS :
begin
case (cnt_byte)
2'd0 : send_data <= 8'h01;
2'd1 : send_data <= 8'h02;
2'd2 : send_data <= 8'h03;
default: send_data <= 8'h00;
endcase
end
DATA : send_data <= flag_RD ? 8'h00 : 8'h23;
RDEN : send_data <= ORDER_READ;
default: send_data <= 8'd0;
endcase
end
end
// bit计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_bit <= 'd0;
end
else if (add_cnt_bit) begin
if (end_cnt_bit) begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 'd1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = (state_c == WREN || state_c >= SEPP && state_c <= DATA) && cnt_SCK_fre == 4'd6;
assign end_cnt_bit = add_cnt_bit && cnt_bit >= 3'd7;
// byte计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_byte <= 2'd0;
end
else if (add_cnt_byte) begin
if (end_cnt_byte) begin
cnt_byte <= 2'd0;
end
else begin
cnt_byte <= cnt_byte + 2'd1;
end
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = state_c == ADDRESS && end_cnt_bit;
assign end_cnt_byte = add_cnt_byte && cnt_byte >= 2'd2;
// MOSI输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
MOSI <= 1'd0;
end
else if (~CS && cnt_SCK_fre == 4'd1) begin
MOSI <= send_data[7 - cnt_bit];
end
else begin
MOSI <= MOSI;
end
end
// 计数3秒
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_3s <= 'd0;
end
else if (add_cnt_3s) begin
if (end_cnt_3s) begin
cnt_3s <= 'd0;
end
else begin
cnt_3s <= cnt_3s + 'd1;
end
end
else begin
cnt_3s <= 'd0;
end
end
assign add_cnt_3s = state_c == FINAL;
assign end_cnt_3s = add_cnt_3s && cnt_3s >= S_3 - 28'd1;
// 接收1byte数据
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
receive_data <= 8'd0;
end
else if (flag_RD && state_c == DATA) begin
receive_data[7 - cnt_bit] <= MISO;
end
else begin
receive_data <= 8'd0;
end
end
// 将接收的1byte数据送给数码管显示
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_out <= 8'd0;
end
else if (data2final && flag_RD) begin
data_out <= receive_data;
end
else begin
data_out <= data_out;
end
end
endmodule
display.v
/*========================================*\
filename : display.v
description : 滚动显示输入的数据
up file :
reversion :
v1.0 : 2022-7-27 18:49:34
author : 张某某
\*========================================*/
module display #(parameter MS_1= 16'd50000)(
input clk , // 50MHz
input rst_n , // 复位信号
input [ 7:0] data ,
output reg [ 5:0] SEL , // SEL信号
output reg [ 7:0] DIG // DIG信号
);
// 信号定义
reg [15:0] cnt_flicker ; // 计数1ms
wire SEL_change ; // cnt_flicker计满使能信号
reg show_wei ;
reg [ 3:0] tmp_data ; // 当前DIG的值
// 逻辑描述
// 闪烁频率计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_flicker <= 'd0;
end
else if (SEL_change) begin
cnt_flicker <= 'd0;
end
else begin
cnt_flicker <= cnt_flicker + 'd1;
end
end
assign SEL_change = cnt_flicker >= MS_1 - 'd1 ? 1'b1 : 1'b0;
// SEL信号输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
SEL <= 6'b011_111;
end
else if (SEL_change) begin
SEL <= {SEL[4], SEL[5], SEL[3:0]};
end
else begin
SEL <= SEL;
end
end
// tmp_data当前SEL位选所对应的DIG十进制值
always @(*) begin
case (SEL)
6'b011_111 : tmp_data = data[3:0];
6'b101_111 : tmp_data = data[7:4];
default: tmp_data = 'd0;
endcase
end
// DIG输出各数字对应的二进制
always @(*) begin
case (tmp_data)
4'd0 : DIG = 8'b1100_0000;
4'd1 : DIG = 8'b1111_1001;
4'd2 : DIG = 8'b1010_0100;
4'd3 : DIG = 8'b1011_0000;
4'd4 : DIG = 8'b1001_1001;
4'd5 : DIG = 8'b1001_0010;
4'd6 : DIG = 8'b1000_0010;
4'd7 : DIG = 8'b1111_1000;
4'd8 : DIG = 8'b1000_0000;
4'd9 : DIG = 8'b1001_0000;
4'd10: DIG = 8'b1000_1000;
4'd11: DIG = 8'b1000_0011;
4'd12: DIG = 8'b1100_0110;
4'd13: DIG = 8'b1010_0001;
4'd14: DIG = 8'b1000_0110;
4'd15: DIG = 8'b1000_1110;
endcase
end
endmodule
tb_spi_m25p16.v
/*========================================*\
filename :
description :
up file :
reversion :
v1.0 :
author : 张某某
\*========================================*/
`timescale 1ns/1ns
module tb_spi_m25p16;
// Parameter definition
parameter CYC_CLK = 20 ;
// Drive signal
reg tb_clk ;
reg tb_rst_n ;
reg [ 2:0] tb_press ;
reg tb_MISO ;
// Observation signal
wire tb_SCK ;
wire tb_CS ;
wire tb_MOSI ;
// Module calls
spi_m25p16 #(.S_3(150)) U_spi_m25p16(
/*input */ .clk (tb_clk ),
/*input */ .rst_n (tb_rst_n ),
/*input [ 1:0]*/ .press (tb_press ),
/*input */ .MISO (tb_MISO ),
/*output reg */ .SCK (tb_SCK ),
/*output reg */ .CS (tb_CS ),
/*output reg */ .MOSI (tb_MOSI )
);
// System initialization
initial begin
tb_clk = 1'b1;
tb_rst_n = 1'b0;
#(CYC_CLK) tb_rst_n = 1'b1;
end
always #(CYC_CLK >> 1) tb_clk = ~tb_clk;
initial begin
tb_press = 3'b000;
tb_MISO = 1'b0;
#(30 * CYC_CLK);
// 擦除操作
tb_press = 3'b100;
#(CYC_CLK) tb_press = 3'b000;
#(16000);
// 页面编程
tb_press = 3'b010;
#(CYC_CLK) tb_press = 3'b000;
#(20000);
// 读数据
tb_press = 3'b001;
#(CYC_CLK) tb_press = 3'b000;
#(12000);
$stop;
end
endmodule
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_E16 -to key_in[0]
set_location_assignment PIN_M16 -to key_in[1]
set_location_assignment PIN_M15 -to key_in[2]
set_location_assignment PIN_H2 -to MISO
set_location_assignment PIN_H1 -to SCK
set_location_assignment PIN_D2 -to CS
set_location_assignment PIN_C1 -to MOSI
set_location_assignment PIN_A4 -to SEL[0]
set_location_assignment PIN_B4 -to SEL[1]
set_location_assignment PIN_A3 -to SEL[2]
set_location_assignment PIN_B3 -to SEL[3]
set_location_assignment PIN_A2 -to SEL[4]
set_location_assignment PIN_B1 -to SEL[5]
set_location_assignment PIN_B7 -to DIG[0]
set_location_assignment PIN_A8 -to DIG[1]
set_location_assignment PIN_A6 -to DIG[2]
set_location_assignment PIN_B5 -to DIG[3]
set_location_assignment PIN_B6 -to DIG[4]
set_location_assignment PIN_A7 -to DIG[5]
set_location_assignment PIN_B8 -to DIG[6]
set_location_assignment PIN_A5 -to DIG[7]