SPI(Serial Peripheral Interface--串行外設(shè)接口)總線系統(tǒng)是一種同步串行外設(shè)接口,它可以使MCU與各種外圍設(shè)備以串行方式進(jìn)行通信以交換信息。本項(xiàng)目我們通過(guò)SPI接口實(shí)現(xiàn)FPGA與EEPROM芯片——AT93C46的通信。示意圖如圖3-16。
圖3-16 SPI接口傳輸結(jié)構(gòu)圖
先查閱AT93C46的datasheet,了解各引腳的作用。
cs:從器件使能信號(hào),由主器件控制;
sk:時(shí)鐘信號(hào),由主器件產(chǎn)生;
do:主器件數(shù)據(jù)輸入,從器件數(shù)據(jù)輸出;
di:主器件數(shù)據(jù)輸出,從器件數(shù)據(jù)輸入。
再明確實(shí)現(xiàn)相互通信需要哪些操作。
1. 三種基本的操作
(1)EWEN時(shí)序(圖3-17)
圖3-17 EWEN時(shí)序圖
EWEN可以理解為激活設(shè)備,寫、擦除(這里沒有用到,有興趣的讀者可以自行查閱datasheet)操作執(zhí)行之前必須要先執(zhí)行完EWEN操作。其操作過(guò)程為:
1) cs在開始操作時(shí)拉高,等到讀操作完成,拉低;
2) sk在CS拉高時(shí),產(chǎn)生10個(gè)脈沖,脈沖周期大于400ns;
3) di在SK的周期內(nèi)輸出數(shù)據(jù),前面輸出固定的指令“10011”,指示是EWEN操作,后面則繼續(xù)輸出5比特的“0”。
(2)WRITE時(shí)序(圖3-18)
圖3-18 WRITE時(shí)序圖
寫操作:
1) cs先拉高一段時(shí)間,然后再拉低TCS時(shí)間,最后再拉高CS,直至檢測(cè)到DO為1,拉低;
2) sk在CS第一段拉高時(shí),產(chǎn)生18個(gè)脈沖,脈沖周期大于400ns;
3) di在sk的周期內(nèi)輸出數(shù)據(jù),前面輸出固定的指令“101”,指示是寫操作,后面則繼續(xù)輸出地址“AN~A0”和數(shù)據(jù)“DN~D0”。
(3)READ時(shí)序(圖3-19)
圖3-19 ERAD時(shí)序圖
1) cs在開始操作時(shí)拉高,等到讀操作完成,拉低;
2) sk在cs第一段拉高時(shí),產(chǎn)生18個(gè)脈沖,脈沖周期大于400ns;
3) di在sk的周期內(nèi)輸出數(shù)據(jù),前面輸出固定的指令“110”,指示是讀操作,后面則繼續(xù)輸出地址“AN~A0”和固定的“0”;
4) 模塊從sk的第11個(gè)周期開始從do中讀取數(shù)據(jù),每個(gè)周期讀取1比特,共8比特。
實(shí)現(xiàn)一個(gè)AT93C46的接口,該接口能夠根據(jù)命令,實(shí)現(xiàn)EWEN、WRITE和READ功能。具體功能如下:
明德?lián)P在變化范圍內(nèi)取了1us作為tcs的值;AT93C46時(shí)鐘取1MHz。
上游模塊在rdy=1時(shí),給出start命令,開始進(jìn)行EWEN、WRITE或者READ操作。在rdy=0期間,start命令無(wú)效。
當(dāng)start有效時(shí),如果mode=0表示進(jìn)行EWEN操作;mode=1表示進(jìn)行WRITE操作;mode=2表示進(jìn)行READ操作。
當(dāng)start有效時(shí),addr和wdata有效。
當(dāng)進(jìn)行EWEN操作時(shí),模塊將按at93c46 EWEN的要求,將addr寫入at93c46。
當(dāng)進(jìn)行WRITE操作時(shí),模塊將按at93c46 WRITE的要求,將addr和wdata寫入at93c46。
當(dāng)進(jìn)行READ操作時(shí),模塊將按at93c46 READ的要求,將addr寫入at93c46,并從at93c46讀到數(shù)據(jù),通過(guò)rdata和rdata_vld返回給上游模塊。
2. 設(shè)計(jì)過(guò)程
(1)明確功能
根據(jù)題目可以畫出設(shè)備與AT93C46之間的具體通信框圖。如圖3-20。
圖3-20 信號(hào)傳輸架構(gòu)圖
表3.5 信號(hào)列表
(2)輸出分析
cs:在WRITE操作時(shí),寫入一個(gè)地址和數(shù)據(jù)后拉低,間隔tcs拉高,等待寫操作完成;READ操作和EWEN操作都是在操作期間為高,操作結(jié)束拉低。
sk:引入計(jì)數(shù)器,通過(guò)計(jì)數(shù)產(chǎn)生1MHz的芯片時(shí)鐘。
rdy:從操作開始到結(jié)束一直為低電平,其他時(shí)刻為高電平。
di:根據(jù)操作的不同輸出相應(yīng)的值
rdata:僅在READ操作時(shí)do的值從高位到低位,一比特一比特地給rdata賦值。
rdata_vld:在rdata賦值結(jié)束后,拉高一個(gè)時(shí)鐘周期,表示此時(shí)rdata有效。
(3)狀態(tài)劃分
從EWEN、READ和WRITE的時(shí)序圖我們可以發(fā)現(xiàn)在不同操作中有很多階段是相似的,總結(jié)起來(lái)有4個(gè)狀態(tài):
IDLE:初始狀態(tài),模塊在等待start信號(hào)有效。
WR_RD:讀寫狀態(tài)。
TCS:片選信號(hào)拉低。
DO:等待寫入操作完成。
(4)狀態(tài)轉(zhuǎn)移
狀態(tài)轉(zhuǎn)移圖請(qǐng)見3-21。
圖3-21 狀態(tài)轉(zhuǎn)移圖
(5)轉(zhuǎn)移條件
確定了狀態(tài)轉(zhuǎn)移圖后,我們需要明確狀態(tài)轉(zhuǎn)移條件:
wr_rd_start:在IDLE狀態(tài)下收到start有效。
tcs_start:在WR_RD狀態(tài)結(jié)束。
idle_start1:,處于EWEN或READ模式,在TCS狀態(tài)結(jié)束(1us)。
do_start:處于WRITE模式,在TCS結(jié)束(1us)。
idle_start2:處于WRITE模式,在TCS狀態(tài)下,收到do==1。
(6)完整性檢查
(7)狀態(tài)機(jī)代碼
第一段,用同步時(shí)序,將次態(tài)的值賦給現(xiàn)態(tài)。注意此時(shí)直接套用模塊,不要做任何更改。
2 if(rst_n==1'b0)begin
3 state_c <= IDLE;
4 end
5 else begin
6 state_c <=state_n;
7 end
8 end
第二段,用組合邏輯描述狀態(tài)轉(zhuǎn)移條件。注意轉(zhuǎn)移條件用信號(hào)來(lái)表示,信號(hào)名要按明德?lián)P規(guī)則來(lái)命名。
2 case(state_c)
3 IDLE:begin
4 if(idl2wrd_start)begin
5 state_n = WR_RD;
6 end
7 else begin
8 state_n = state_c;
9 end
10 end
11 WR_RD:begin
12 if(wrd2tcs_start)begin
13 state_n = TCS;
14 end
15 else begin
16 state_n = state_c;
17 end
18 end
19 TCS:begin
20 if(tcs2do_start)begin
21 state_n = DO;
22 end
23 else if(tcs2idl_start)begin
24 state_n = IDLE;
25 end
26 else begin
27 state_n = state_c;
28 end
29 end
30 DO:begin
31 if(do2idl_start)begin
32 state_n = IDLE;
33 end
34 else begin
35 state_n = state_c;
36 end
37 end
38 default:begin
39 state_n = IDLE;
40 end
41 endcase
42 end
43
第三段,用assign定義轉(zhuǎn)移條件。注意條件一定要加上現(xiàn)態(tài)。
2 assign wrd2tcs_start = state_c == WR_RD&& end_cnt1;
3 assign tcs2idl_start = state_c == TCS && end_cnt
4 && (mode_reg==EWEN||mode_reg==READ);
5 assign tcs2do_start = state_c == TCS && mode_reg == WRITE && end_cnt;
6 assign do2idl_start = state_c == DO && mode_reg == WRITE && do_ff1 == 1;
7
第四段,則是輸出信號(hào)設(shè)計(jì),在功能代碼部分。
(8)功能代碼
2 always @(posedge clk or negedge rst_n)begin
3 if(rst_n==1'b0)begin
4 cnt <= 0;
5 end
6 else if(add_cnt) begin
7 if(end_cnt)
8 cnt <= 0;
9 else
10 cnt <= cnt + 1;
11 end
12 end
13 assign add_cnt = state_c == WR_RD || state_c == TCS;
14 assign end_cnt = add_cnt && cnt==100 - 1 ;
15
16 //根據(jù)第六步第2點(diǎn),寫出cnt1的代碼
17 always @(posedge clk or negedge rst_n)begin
18 if(rst_n==1'b0)begin
19 cnt1<= 0;
20 end
21 else if(add_cnt1) begin
22 if(end_cnt1)
23 cnt1 <= 0;
24 else
25 cnt1 <= cnt1 + 1;
26 end
27 end
28 assign add_cnt1 = end_cnt;
29 assign end_cnt1 = add_cnt1 && cnt1==x -1 ;
30
31 always @(*)begin
32 if(mode_reg == EWEN)begin
33 x = 10;
34 end
35 else if(mode_reg == WRITE)begin
36 x = 18;
37 end
38 else if(mode_reg == READ)begin
39 x = 18;
40 end
41 else begin
42 x = 0;
43 end
44 end
45 //根據(jù)第六步第3點(diǎn),寫出do的代碼
46 always @(posedge clk or negedge rst_n)begin
47 if(rst_n==1'b0)begin
48 do_ff0<=0;
49 do_ff1<=0;
50 end
51 else begin
52 do_ff0<=d0;
53 do_ff1<=do_ff0;
54 end
55 end
56
57 //根據(jù)第六步第4點(diǎn),寫出sk的代碼
58 always @(posedge clk or negedge rst_n)begin
59 if(rst_n==1'b0)begin
60 sk <= 0;
61 end
62 else if(sk_high)begin
63 sk <= 1;
64 end
65 else if(sk_low)begin
66 sk <= 0;
67 end
68 end
69 assign sk_high = state_c == WR_RD && add_cnt && cnt == 50-1;
70 assign sk_low = state_c == WR_RD && end_cnt;
71
72 //根據(jù)第六步第5點(diǎn),寫出di的代碼
73 always @(posedge clk or negedge rst_n)begin
74 if(rst_n==1'b0)begin
75 di <= 0;
76 end
77 else if(di_en)begin
78 di <= dout[17-cnt1];
79 end
80 end
81 assign di_en = cnt==0 && state_c == WR_RD;
82
83 //根據(jù)第六步第6點(diǎn),寫出cs的代碼
84 always @(posedge clk or negedge rst_n)begin
85 if(rst_n==1'b0)begin
86 cs <= 0;
87 end
88 else if(cs_high)begin
89 cs <= 1;
90 end
91 else if(cs_low)begin
92 cs <= 0;
93 end
94 end
95 assign cs_high = idl2wrd_start || tcs2do_start;
96 assign cs_low = wrd2tcs_start || do2idl_start;
97
98 //根據(jù)第六步第7點(diǎn),寫出rdy的代碼
99 always @(*)begin
100 if(rdy_low)
101 rdy = 0;
102 else
103 rdy = 1;
104 end
105 assign rdy_low = start || state_c != IDLE;
106
107 //根據(jù)第六步第8點(diǎn),寫出rdata的代碼
108 always @(posedge clk or negedge rst_n)begin
109 if(rst_n==1'b0)begin
110 rdata <= 0;
111 end
112 else if(rdata_en)begin
113 rdata <= {rdata[6:0],do};
114 end
115 end
116 assign rdata_en = mode_reg == READ && state_c == WR_RD && end_cnt
117 && cnt1 >= 10 && cnt1 < 18;
118
119 //根據(jù)第六步第9點(diǎn),寫出rdata_vld的代碼
120 always @(posedge clk or negedge rst_n)begin
121 if(rst_n==1'b0)begin
122 rdata_vld <= 0;
123 end
124 else if(rdata_vld_en)begin
125 rdata_vld <= 1;
126 end
127 else begin
128 rdata_vld <= 0;
129 end
130 end
131 assign rdata_vld_en = mode_reg == READ && wrd2tcs_start;
132
133 //根據(jù)第六步第10點(diǎn),寫出dout的代碼
134 always @(posedge clk or negedge rst_n)begin
135 if(rst_n==1'b0)begin
136 dout <= 0;
137 end
138 else if(start && mode==0)begin
139 dout <= {3'b100,2’b11,13'b0};
140 end
141 else if(start && mode==1)begin
142 dout <= {3'b101,addr,wdata};
143 end
144 else if(start && mode==2)begin
145 dout <= {3'b110,addr,8'b0};
146 end
147 end
148
149 always @(posedge clk or negedge rst_n)begin
150 if(rst_n==1'b0)begin
151 mode_reg <= 0;
152 end
153 else if(start && mode==0)begin
154 mode_reg <= EWEN;
155 end
156 else if(start && mode==1)begin
157 mode_reg <= WRITE;
158 end
159 else if(start && mode==2)begin
160 mode_reg <= READ;
161 end
162 end
163