文章目錄
- 一、SPI
- 二、看spi--flash手冊找關鍵
-
- 1.描述
- 2.flash接口信号
- 3.SPI模式選擇
- 4.高位元組MSB
- 5.指令
- 6. 寫使能時序
- 7.讀ID時序
- 8.讀寄存器時序(我沒用到)
- 9.讀資料時序
- 10.頁程式設計
- 11.扇區擦除
- 12.重要的時間
- 三、狀态機設計
-
- 1.spi接口狀态機
- 2.flash讀狀态機
- 3.flash寫狀态機
- 四、代碼部分
-
- 1.==spi_interface.v==
- 2.==spi_read_ctrl.v==
- 3.==spi_write_ctrl.v==
- 4.==spi_control.v==
- 5.==top.v==
- 6.其他子產品
- 五、仿真驗證
- 六、上闆驗證
- 七、總結
一、SPI
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主裝置和一個或多個從裝置,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基于SPI的裝置共有的,它們是MISO(主裝置資料輸入)、MOSI(主裝置資料輸出)、SCLK(時鐘)、CS(片選)。
(1)MISO– Master Input Slave Output,主裝置資料輸入,從裝置資料輸出;
(2)MOSI– Master Output Slave Input,主裝置資料輸出,從裝置資料輸入;
(3)SCLK – Serial Clock,時鐘信号,由主裝置産生;
(4)CS – Chip Select,從裝置使能信号,由主裝置控制。

二、看spi–flash手冊找關鍵
1.描述
16Mbit的存儲空間
單扇區擦除或者整塊擦除
用spi協定與flash讀寫
2.flash接口信号
C是串行時鐘
D是資料
S是片選信号
3.SPI模式選擇
flash隻支援mode0和mode3兩種模式
CPOL時鐘相位
時鐘的極性(CPOL)用來決定在總線空閑時,同步時鐘(SCK)信号線上的電位是高電平還是低電平。當時鐘極性為0時(CPOL=0),SCK信号線在空閑時為低電平;當時鐘極性為1時(CPOL=1),SCK信号線在空閑時為高電平;
CPHA時鐘極性
當時鐘相位為1時(CPHA=1),在SCK信号線的第二個跳變沿進行采樣;這裡的跳變沿究竟是上升沿還是下降沿?取決于時鐘的極性。當時鐘極性為0時,取下降沿;當時鐘極性為1時,取上升沿
- CPOL=0,CPHA=0
- CPOL=0,CPHA=1
【FPGA】FPGA基于spi的flash讀寫一、SPI二、看spi–flash手冊找關鍵三、狀态機設計四、代碼部分五、仿真驗證六、上闆驗證七、總結 - CPOL=1,CPHA=0
【FPGA】FPGA基于spi的flash讀寫一、SPI二、看spi–flash手冊找關鍵三、狀态機設計四、代碼部分五、仿真驗證六、上闆驗證七、總結 - CPOL=1,CPHA=1
【FPGA】FPGA基于spi的flash讀寫一、SPI二、看spi–flash手冊找關鍵三、狀态機設計四、代碼部分五、仿真驗證六、上闆驗證七、總結
4.高位元組MSB
MSB先,就是高位元組先
5.指令
Instruction | Code | 說明 |
---|---|---|
RDID | 8’h9F | 讀ID |
RDSR | 8’h05 | 讀寄存器判斷最後一位是0(但我其實沒有用過) |
READ | 8’h03 | 讀資料 |
PP | 8’h02 | 頁擦除 |
SE | 8’hD8 | 扇區擦除 |
6. 寫使能時序
- 1位元組的指令
7.讀ID時序
- 1位元組的指令
- 3位元組的ID資料
8.讀寄存器時序(我沒用到)
- 1位元組的指令
- 2位元組的資料
判斷WIP BIT是否為0才能進行下一步(我的代碼裡沒有用到)
9.讀資料時序
- 1位元組的指令
- 3位元組的位址
- 1位元組的資料
10.頁程式設計
- 1位元組的指令
- 3位元組的位址
- 1位元組的資料
11.扇區擦除
- 1位元組的指令
- 3位元組的位址
12.重要的時間
時間名稱 | ||
---|---|---|
C_TIME | 100ns | 一個操作結束後到下一個操作片需要100ns,比如一個指令到下一個指令 |
PP_TIME | 1.4-5ms | 頁程式設計的所需要的時間5ms |
SE_TIME | 1-3s | 擦除所需要的時間3s |
三、狀态機設計
1.spi接口狀态機
2.flash讀狀态機
3.flash寫狀态機
四、代碼部分
1.spi_interface.v
module spi_interface(
input clk,
input rst_n,
// 接口與主機
input [7:0] din,
input req,
output [7:0] dout,
output done,
// 接口與flash
input miso,// 主機采樣從機發送
output mosi,// 主機發送從機
output sclk,// 串行時鐘
output cs_n // 片選信号
);
parameter CPHA = 1,// 空閑狀态高電平
CPOL = 1;// 下降沿發送,上升沿采樣
// 16分頻或8分頻或4分頻 不能2分頻
parameter SCLK = 16,
SCLK_BEFORE = SCLK/4,
SCLK_AFTER = SCLK*3/4;
// 狀态機
localparam IDLE = 4'b0001,
WAIT = 4'b0010,
DATA = 4'b0100,
DONE = 4'b1000;
reg [3:0] state_c;
reg [3:0] state_n;
wire idle2wait;
wire wait2data;
wire data2done;
wire done2idle;
// bit計數器
reg [2:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
// 分頻串行時鐘計數器
reg [4:0] cnt_sclk;
wire add_cnt_sclk;
wire end_cnt_sclk;
// 寄存要發送的資料
reg spi_sclk;
reg [7:0] rx_data;
reg [7:0] tx_data;
reg spi_cn_n;
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(idle2wait)begin
state_n = WAIT;
end
else begin
state_n = state_c;
end
end
WAIT :begin
if(wait2data)begin
state_n = DATA;
end
else begin
state_n = state_c;
end
end
DATA :begin
if(data2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
DONE :begin
if(done2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2wait = state_c == IDLE && (req);
assign wait2data = state_c == WAIT && (1'b1);
assign data2done = state_c == DATA && (end_cnt_bit);
assign done2idle = state_c == DONE && (1'b1);
// bit計數器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = end_cnt_sclk;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 8 - 1;
// sclk計數器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_sclk <= 0;
end
else if(add_cnt_sclk)begin
if(end_cnt_sclk)begin
cnt_sclk <= 0;
end
else begin
cnt_sclk <= cnt_sclk + 1;
end
end
else begin
cnt_sclk <= cnt_sclk;
end
end
assign add_cnt_sclk = (state_c == DATA);
assign end_cnt_sclk = add_cnt_sclk && cnt_sclk == SCLK - 1;
// 16分頻串行時鐘 CPHA=1,CPOL=1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
if(CPHA == 0)begin
spi_sclk <= 1'b0;
end
else if(CPHA == 1)begin
spi_sclk <= 1'b1;
end
end
else if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin
if(CPHA == 0)begin
spi_sclk <= 1'b1;
end
else if(CPHA == 1)begin
spi_sclk <= 1'b0;
end
end
else if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin
if(CPHA == 0)begin
spi_sclk <= 1'b0;
end
else if(CPHA == 1)begin
spi_sclk <= 1'b1;
end
end
end
// 發送的資料mosi 高位MSB先 CPHA=1,CPOL=1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0;
end
else if(CPOL == 0)begin
if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin
tx_data <= din;
end
end
else if(CPOL == 1)begin
if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin
tx_data <= din;
end
end
end
// 接收的資料miso 高位MSB先 CPHA=1,CPOL=1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_data <= 0;
end
else if(CPOL == 0)begin
if(add_cnt_sclk && cnt_sclk == SCLK_BEFORE - 1)begin
rx_data[7-cnt_bit] <= miso;
end
end
else if(CPOL == 1)begin
if(add_cnt_sclk && cnt_sclk == SCLK_AFTER - 1)begin
rx_data[7-cnt_bit] <= miso;
end
end
end
assign mosi = tx_data[7-cnt_bit];
assign sclk = spi_sclk;
assign cs_n = ~req;
assign dout = rx_data;
assign done = (state_c == DONE);
endmodule
2.spi_read_ctrl.v
module spi_read_ctrl(
input clk,
input rst_n,
input [2:0] key_out,
input [7:0] din,
input done,
output reg req,
output [7:0] dout,
output reg [23:0] seg_data
);
localparam RDID_CMD = 8'h9F,// 讀ID指令
RDDA_CMD = 8'h03,// 讀資料指令
RDDA_ADD = 24'h0;// 讀資料位址
localparam IDLE = 7'b000_0001,
RDIDCMD = 7'b000_0010,
RDID = 7'b000_0100,
RDDACMD = 7'b000_1000,
RDDAADD = 7'b001_0000,
RDDATA = 7'b010_0000,
DONE = 7'b100_0000;
reg [6:0] state_c;
reg [6:0] state_n;
wire idle2rdidcmd ;
wire idle2rddacmd ;
wire rdidcmd2rdid ;
wire rdid2done ;
wire rddacmd2rddaadd;
wire rddaadd2rddata ;
wire rddata2done ;
wire done2idle ;
// 位元組計數器
reg [2:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
// 讀id和讀資料請求
reg rdid_req;
reg rdda_req;
reg [7:0] tx_data;
// 狀态機
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(idle2rdidcmd)begin
state_n = RDIDCMD;
end
else if(idle2rddacmd)begin
state_n = RDDACMD;
end
else begin
state_n = state_c;
end
end
RDIDCMD :begin
if(rdidcmd2rdid)begin
state_n = RDID;
end
else begin
state_n = state_c;
end
end
RDID :begin
if(rdid2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
RDDACMD :begin
if(rddacmd2rddaadd)begin
state_n = RDDAADD;
end
else begin
state_n = state_c;
end
end
RDDAADD :begin
if(rddaadd2rddata)begin
state_n = RDDATA;
end
else begin
state_n = state_c;
end
end
RDDATA :begin
if(rddata2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
DONE :begin
if(done2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2rdidcmd = state_c == IDLE && (rdid_req);
assign idle2rddacmd = state_c == IDLE && (rdda_req);
assign rdidcmd2rdid = state_c == RDIDCMD && (end_cnt_byte);
assign rdid2done = state_c == RDID && (end_cnt_byte);
assign rddacmd2rddaadd = state_c == RDDACMD && (end_cnt_byte);
assign rddaadd2rddata = state_c == RDDAADD && (end_cnt_byte);
assign rddata2done = state_c == RDDATA && (end_cnt_byte);
assign done2idle = state_c == DONE && (1'b1);
// 位元組計數器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte + 1;
end
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = (state_c != IDLE) && done;
assign end_cnt_byte = add_cnt_byte && cnt_byte == (((state_c == RDIDCMD) || (state_c == RDDACMD) || (state_c == RDDATA))?(1-1):(3-1));
// 讀id和讀資料請求
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rdid_req <= 0;
rdda_req <= 0;
req <= 0;
end
else if(key_out[0])begin
rdid_req <= 1'b1;
req <= 1'b1;
end
else if(key_out[1])begin
rdda_req <= 1'b1;
req <= 1'b1;
end
else if(state_c == DONE)begin
req <= 1'b0;
rdid_req <= 1'b0;
rdda_req <= 1'b0;
end
end
// 指令
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0;
end
else if(idle2rdidcmd)begin
tx_data <= RDID_CMD;
end
else if(idle2rddacmd)begin
tx_data <= RDDA_CMD;
end
else if(rddacmd2rddaadd)begin
tx_data <= RDDA_ADD;
end
end
// seg_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_data <= 0;
end
else if(state_c == RDID && add_cnt_byte)begin
case(cnt_byte)
0 : seg_data[23:16] <= din;
1 : seg_data[15:8] <= din;
2 : seg_data[7:0] <= din;
default: seg_data <= seg_data;
endcase
end
else if(state_c == RDDATA && add_cnt_byte)begin
case(cnt_byte)
0 : seg_data[23:16] <= din;
default: seg_data <= seg_data;
endcase
end
else begin
seg_data <= seg_data;
end
end
// assign req = rdid_req || rdda_req;
assign dout = tx_data;
endmodule
3.spi_write_ctrl.v
module spi_write_ctrl(
input clk,
input rst_n,
input [2:0] key_out,
input [7:0] din,
input done,
output req,
output [7:0] dout
);
parameter CMD_TIME = 10,// 第一個指令到下一個指令200ns等待時間
PP_TIME = 250_000,// PP可程式設計時間5ms
SE_TIME = 150_000_000;// SE擦除時間3s
parameter WREN_CMD = 8'h06,
SE_CMD = 8'hD8,
SE_ADD = 24'h000000,
RDSR_CMD = 8'h05,
PP_CMD = 8'h02,
PP_ADD = 24'h000000,
DATA = 8'h78;
// 狀态機
localparam IDLE =10'b00000_00001,
FIRWRENCMD =10'b00000_00010,
SECMD =10'b00000_00100,
SEADD =10'b00000_01000,
RDSRCMD =10'b00000_10000,
SECWRENCMD =10'b00001_00000,
PPCMD =10'b00010_00000,
PPADD =10'b00100_00000,
PPDATA =10'b01000_00000,
DONE =10'b10000_00000;
reg [9:0] state_c;
reg [9:0] state_n;
wire idle2firwrencmd ;
wire firwrencmd2secmd ;
wire secmd2seadd ;
wire seadd2rdsrcmd ;
wire rdsrcmd2secwrencmd ;
wire secwrencmd2ppcmd ;
wire ppcmd2ppadd ;
wire ppadd2ppdata ;
wire ppdata2done ;
wire done2idle ;
// 位元組計數器
reg [1:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
// 100ms一個指令到下一個指令的等待時間
reg [3:0] cnt_200ns;
wire add_cnt_200ns;
wire end_cnt_200ns;
// se擦除等待時間
reg [27:0] cnt_3s;
wire add_cnt_3s;
wire end_cnt_3s;
// pp頁程式設計等待時間
// reg cnt_5ms;
// wire add_cnt_5ms;
// wire end_cnt_5ms;
// 一個指令到下一個指令的等待标志
reg delay_flag;
// 寄存req
reg req_r;
// 寄存要發送的資料
reg [7:0] tx_data;
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(idle2firwrencmd)begin
state_n = FIRWRENCMD;
end
else begin
state_n = state_c;
end
end
FIRWRENCMD :begin
if(firwrencmd2secmd)begin
state_n = SECMD;
end
else begin
state_n = state_c;
end
end
SECMD :begin
if(secmd2seadd)begin
state_n = SEADD;
end
else begin
state_n = state_c;
end
end
SEADD :begin
if(seadd2rdsrcmd)begin
state_n = RDSRCMD;
end
else begin
state_n = state_c;
end
end
RDSRCMD :begin
if(rdsrcmd2secwrencmd)begin
state_n = SECWRENCMD;
end
else begin
state_n = state_c;
end
end
SECWRENCMD :begin
if(secwrencmd2ppcmd)begin
state_n = PPCMD;
end
else begin
state_n = state_c;
end
end
PPCMD :begin
if(ppcmd2ppadd)begin
state_n = PPADD;
end
else begin
state_n = state_c;
end
end
PPADD :begin
if(ppadd2ppdata)begin
state_n = PPDATA;
end
else begin
state_n = state_c;
end
end
PPDATA :begin
if(ppdata2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
DONE :begin
if(done2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2firwrencmd = state_c == IDLE && (key_out[2]);
assign firwrencmd2secmd = state_c == FIRWRENCMD && (end_cnt_200ns);
assign secmd2seadd = state_c == SECMD && (end_cnt_byte);
assign seadd2rdsrcmd = state_c == SEADD && (end_cnt_3s);
assign rdsrcmd2secwrencmd = state_c == RDSRCMD && (end_cnt_200ns);
assign secwrencmd2ppcmd = state_c == SECWRENCMD && (end_cnt_200ns);
assign ppcmd2ppadd = state_c == PPCMD && (end_cnt_byte);
assign ppadd2ppdata = state_c == PPADD && (end_cnt_byte);
assign ppdata2done = state_c == PPDATA && (end_cnt_byte);
assign done2idle = state_c == DONE && (1'b1);
// 位元組計數器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte + 1;
end
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = ((state_c != IDLE) && done);
assign end_cnt_byte = add_cnt_byte && cnt_byte == (((state_c == FIRWRENCMD) || (state_c == SECMD) || (state_c == RDSRCMD) || (state_c == SECWRENCMD) || (state_c == PPCMD) || (state_c == PPDATA))?(1-1):(3-1));
// 100ms一個指令到下一個指令的等待時間計數器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_200ns <= 0;
end
else if(add_cnt_200ns)begin
if(end_cnt_200ns)begin
cnt_200ns <= 0;
end
else begin
cnt_200ns <= cnt_200ns + 1;
end
end
else begin
cnt_200ns <= cnt_200ns;
end
end
assign add_cnt_200ns = (((state_c == FIRWRENCMD) || (state_c == RDSRCMD) || (state_c == SECWRENCMD)) && delay_flag);
assign end_cnt_200ns = add_cnt_200ns && cnt_200ns == CMD_TIME - 1;
// SE擦除時間2s
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_3s <= 0;
end
else if(add_cnt_3s)begin
if(end_cnt_3s)begin
cnt_3s <= 0;
end
else begin
cnt_3s <= cnt_3s + 1;
end
end
else begin
cnt_3s <= cnt_3s;
end
end
assign add_cnt_3s = ((state_c == SEADD) && delay_flag);
assign end_cnt_3s = add_cnt_3s && cnt_3s == SE_TIME - 1;
// 一個指令到下一個指令的等待延長标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
delay_flag <= 0;
end
else if(end_cnt_byte)begin
delay_flag <= 1'b1;
end
else if(end_cnt_200ns || end_cnt_3s)begin
delay_flag <= 1'b0;
end
else begin
delay_flag <= delay_flag;
end
end
// req信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
req_r <= 1'b0;
end
else if(idle2firwrencmd)begin
req_r <= 1'b1;
end
else if((state_c == FIRWRENCMD) && end_cnt_byte)begin
req_r <= 1'b0;
end
else if(firwrencmd2secmd)begin
req_r <= 1'b1;
end
else if(secmd2seadd)begin
req_r <= 1'b1;
end
else if((state_c == SEADD) && end_cnt_byte)begin
req_r <= 1'b0;
end
else if(seadd2rdsrcmd)begin
req_r <= 1'b1;
end
else if((state_c == RDSRCMD) && end_cnt_byte)begin
req_r <= 1'b0;
end
else if(rdsrcmd2secwrencmd)begin
req_r <= 1'b1;
end
else if((state_c == SECWRENCMD) && end_cnt_byte)begin
req_r <= 1'b0;
end
else if(secwrencmd2ppcmd)begin
req_r <= 1'b1;
end
else if(ppcmd2ppadd)begin
req_r <= 1'b1;
end
else if(ppadd2ppdata)begin
req_r <= 1'b1;
end
else if(ppdata2done)begin
req_r <= 1'b0;
end
end
// dout傳輸的資料
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0;
end
else if(state_c == FIRWRENCMD)begin
tx_data <= WREN_CMD;
end
else if(state_c == SECMD)begin
tx_data <= SE_CMD;
end
else if(state_c == SEADD)begin
tx_data <= SE_ADD;
end
else if(state_c == RDSRCMD)begin
tx_data <= RDSR_CMD;
end
else if(state_c == SECWRENCMD)begin
tx_data <= WREN_CMD;
end
else if(state_c == PPCMD)begin
tx_data <= PP_CMD;
end
else if(state_c == PPADD)begin
tx_data <= PP_ADD;
end
else if(state_c == PPDATA)begin
tx_data <= DATA;
end
end
assign req = req_r;
assign dout = tx_data;
endmodule
4.spi_control.v
module spi_control(
input clk,
input rst_n,
input [2:0] key_out,
input [7:0] din,
input done,
output [7:0] dout,
output req,
output [23:0] seg_data
);
wire rd_req;
wire wr_req;
wire [7:0] rd_tx_data;
wire [7:0] wr_tx_data;
assign req = rd_req | wr_req;
assign dout = ({8{rd_req}} & rd_tx_data) | ({8{wr_req}} & wr_tx_data);
// 讀控制子產品
spi_read_ctrl u_spi_read_ctrl(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [2:0] */.key_out (key_out ),
/* input [7:0] */.din (din ),
/* input */.done (done ),
/* output */.req (rd_req ),
/* output [7:0] */.dout (rd_tx_data),
/* output reg [23:0]*/.seg_data(seg_data)
);
// 寫控制子產品
spi_write_ctrl u_spi_write_ctrl(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [2:0] */.key_out (key_out),
/* input [7:0] */.din (din ),
/* input */.done (done ),
/* output */.req (wr_req ),
/* output [7:0] */.dout (wr_tx_data)
);
endmodule
5.top.v
module top(
input clk,
input rst_n,
input [2:0] key_in,
output [7:0] seg_dig,
output [5:0] seg_sel,
input miso,// 主機采樣從機發送
output mosi,// 主機發送從機
output sclk,// 串行時鐘
output cs_n // 片選信号
);
wire [2:0] key_out;
wire req;
wire done;
wire [7:0] rx_data;
wire [7:0] tx_data;
wire [23:0] seg_data;
// 按鍵消抖子產品
key_filter u_key_filter(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [KEY_W-1:0] */.key_in (key_in ),
/* output reg [KEY_W-1:0] */.key_out (key_out)
);
// 數位管驅動
seg_driver u_seg_driver(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [23:0] */.data (seg_data),
/* output reg [7:0] */.seg_dig (seg_dig),
/* output reg [5:0] */.seg_sel (seg_sel)
);
spi_control u_spi_control(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [2:0] */.key_out (key_out ),
/* input [7:0] */.din (rx_data ),
/* input */.done (done ),
/* output [7:0] */.dout (tx_data ),
/* output */.req (req ),
/* output [23:0] */.seg_data (seg_data)
);
spi_interface u_spi_interface(
/* input */.clk (clk ),
/* input */.rst_n (rst_n),
/* // 接口與主機 */
/* input [7:0] */.din (tx_data),
/* input */.req (req ),
/* output [7:0] */.dout (rx_data ),
/* output */.done (done ),
/* // 接口與flash */
/* input */.miso (miso ),// 主機采樣從機發送
/* output */.mosi (mosi ),// 主機發送從機
/* output */.sclk (sclk ),// 串行時鐘
/* output */.cs_n (cs_n ) // 片選信号
);
endmodule
6.其他子產品
按鍵消抖子產品
數位管驅動子產品
五、仿真驗證
這個還沒仿真,仿真驗證也是驗證接口狀态轉移是否正确
六、上闆驗證
- 按下key[2]寫資料
- 按下key[1]讀資料
- 按下key[0]讀ID
讀id資料
讀資料
七、總結
這個spi我是自己實作的,但是實作的是最基本的三個功能,讀id,讀資料,寫資料,但是每次功能按鍵還要複位一下,肯定有bug,但是不改了,哈哈。這個我隻是給讀者提供思路,但是不要直接拿我這個bug代碼,日後我會上傳正确的好的代碼。
記得Verilog代碼要看時序,有時候一個時序對不上,真的,調試半天。像我寫資料的時候,因為串行時鐘的時序沒有對上相對應的狀态,晚了一個周期,按照spi協定的說明,應該是在邊沿發送資料,晚了一個周期,就導緻資料不對了,是以無論咋樣都沒寫進資料。
我寫部落格是我的思路,我的了解,也可能是我與老師的結合,不完全是老師的,是以有問題很正常。