SPI(Serial Peripheral Interface--串行外設接口)總線系統是一種同步串行外設接口,它可以使MCU與各種外圍設備以串行方式進行通信以交換信息。本項目我們通過SPI接口實現FPGA與EEPROM芯片——AT93C46的通信。示意圖如圖3-16。
圖3-16 SPI接口傳輸結構圖
sk:時鐘信號,由主器件產生;
do:主器件數據輸入,從器件數據輸出;
di:主器件數據輸出,從器件數據輸入。
(1)EWEN時序(圖3-17)
圖3-17 EWEN時序圖
EWEN可以理解為激活設備,寫、擦除(這里沒有用到,有興趣的讀者可以自行查閱datasheet)操作執行之前必須要先執行完EWEN操作。其操作過程為:
1.cs在開始操作時拉高,等到讀操作完成,拉低;
2.sk在CS拉高時,產生10個脈沖,脈沖周期大于400ns;
3.di在SK的周期內輸出數據,前面輸出固定的指令“10011”,指示是EWEN操作,后面則繼續輸出5比特的“0”。
(2)WRITE時序(圖3-18)
圖3-18 WRITE時序圖
寫操作:
(1)cs先拉高一段時間,然后再拉低TCS時間,最后再拉高CS,直至檢測到DO為1,拉低;
(2)sk在CS第一段拉高時,產生18個脈沖,脈沖周期大于400ns;
(3)di在sk的周期內輸出數據,前面輸出固定的指令“101”,指示是寫操作,后面則繼續輸出地址“AN~A0”和數據“DN~D0”。
(3)READ時序(圖3-19)
圖3-19 ERAD時序圖
(1)cs在開始操作時拉高,等到讀操作完成,拉低;
(2)sk在cs第一段拉高時,產生18個脈沖,脈沖周期大于400ns;
(3)di在sk的周期內輸出數據,前面輸出固定的指令“110”,指示是讀操作,后面則繼續輸出地址“AN~A0”和固定的“0”;
(4)模塊從sk的第11個周期開始從do中讀取數據,每個周期讀取1比特,共8比特。
明德揚在變化范圍內取了1us作為tcs的值;AT93C46時鐘取1MHz。
上游模塊在rdy=1時,給出start命令,開始進行EWEN、WRITE或者READ操作。在rdy=0期間,start命令無效。
當start有效時,如果mode=0表示進行EWEN操作;mode=1表示進行WRITE操作;mode=2表示進行READ操作。
2. 設計過程
(1)明確功能
根據題目可以畫出設備與AT93C46之間的具體通信框圖。如圖3-20。
圖3-20 信號傳輸架構圖
表3.5 信號列表
(2)輸出分析
di:根據操作的不同輸出相應的值
(3)狀態劃分
(4)狀態轉移
狀態轉移圖請見3-21。
圖3-21 狀態轉移圖
(5)轉移條件
確定了狀態轉移圖后,我們需要明確狀態轉移條件:
wr_rd_start:在IDLE狀態下收到start有效。
tcs_start:在WR_RD狀態結束。
idle_start1:,處于EWEN或READ模式,在TCS狀態結束(1us)。
do_start:處于WRITE模式,在TCS結束(1us)。
idle_start2:處于WRITE模式,在TCS狀態下,收到do==1。
(6)完整性檢查
(7)狀態機代碼
第二段,用組合邏輯描述狀態轉移條件。注意轉移條件用信號來表示,信號名要按明德揚規則來命名。
1 always @(*)begin
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定義轉移條件。注意條件一定要加上現態。
1 assign idl2wrd_start = state_c == IDLE && start == 1;
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
第四段,則是輸出信號設計,在功能代碼部分。
(8)功能代碼
1 //根據第六步第1點,寫出cnt的代碼
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 //根據第六步第2點,寫出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 //根據第六步第3點,寫出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 //根據第六步第4點,寫出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 //根據第六步第5點,寫出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 //根據第六步第6點,寫出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 //根據第六步第7點,寫出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 //根據第六步第8點,寫出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 //根據第六步第9點,寫出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 //根據第六步第10點,寫出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
溫馨提示:明德揚2023推出了全新課程——邏輯設計基本功修煉課,降低學習FPGA門檻的同時,增加了學習的趣味性,并組織了考試贏積分活動
http://www.cqqtmy.cn/ffkc/415.html
(點擊→了解課程詳情?)感興趣請聯系易老師:13112063618(微信同步)