本文檔介紹了AG32開(kāi)發(fā)中,MCU與CPLD交互的具體方式以及例子。
如需了解AG32更多資料可發(fā)郵件:sales@agm-micro.com,或直接掃碼加我本人微信。
一、MCU和CPLD直接交互
cpld工程創(chuàng)建及編譯的操作流程,參考文檔《AG32下fpga和cpld的使用入門(mén)》
在工程中,用戶邏輯部分編寫(xiě)是從analog_ip.v的接口下開(kāi)始的。
mcu和cpld之間的交互,可以分為:
1. mcu傳遞信號(hào)給cpld;(如mcu的gpio傳遞高低信號(hào)到cpld)
2. cpld傳遞信號(hào)給mcu;(如:對(duì)mcu產(chǎn)生中斷信號(hào))
3. mcu讀寫(xiě)數(shù)據(jù)到cpld;
4. 不建議,cpld做為主設(shè)備對(duì)mcu寫(xiě)。
也就是說(shuō),在mcu和cpld交互中,cpld更像一個(gè)外設(shè)。
其中,前兩種較為簡(jiǎn)單。后兩種要使用AHB總線來(lái)操作。
下邊針對(duì)四種情況分別說(shuō)明:
1. mcu傳遞信號(hào)給cpld;
這種使用較簡(jiǎn)單。步驟如下:
在ve中定義信號(hào):
表示,用mcu的gpio(gpio4_1)來(lái)輸入信號(hào)到cpld。
然后,prepare LOGIC工程后,可以看到analog_ip.v接口中的信號(hào):
input?????????????iocvt_chn_out_data,
input?????????????iocvt_chn_out_en,
這里的iocvt_chn_out_data,就是對(duì)接到mcu的gpio4_1的信號(hào)。
當(dāng)控制mcu的gpio4_1高低切換時(shí),cpld中的iocvt_chn_out_data,會(huì)對(duì)應(yīng)來(lái)變化。
具體樣例,可以參考網(wǎng)盤(pán)“l(fā)ogic樣例3.mcu信號(hào)到cpld到pin”的樣例,該樣例中,展示了mcu控制cpld繼續(xù)控制led的過(guò)程。
除了gpio信號(hào)輸出到cpld,其他比如pwm輸出信號(hào)等,都可以輸入到cpld。
2. cpld傳遞信號(hào)給mcu;
這種方式和1相近,只不過(guò)是反向。
可以在mcu中定義gpio4_2為輸入并使能中斷,則cpld中設(shè)置信號(hào)高低時(shí),將觸發(fā)?mcu的中斷。
在VE中定義信號(hào):
GPIO4_2
iocvt_chn:INPUT
表示,用mcu的gpio(gpio4_2)信號(hào)來(lái)源于cpld的iocvt_chn。
然后,prepare LOGIC工程后,可以看到analog_ip.v接口中的信號(hào):
output?????????????iocvt_chn_in_data,
這里的iocvt_chn_in_data,就是對(duì)接到mcu的gpio4_2的信號(hào)。
當(dāng)cpld中控制iocvt_chn_in_data信號(hào)高低時(shí),mcu中的gpio4_2對(duì)應(yīng)變化。
這里不再舉例。
3. mcu讀寫(xiě)數(shù)據(jù)到cpld;
在地址設(shè)計(jì)中,cpld的地址區(qū)間是:0x60000000 ~
0x7FFFFFFF
當(dāng)mcu對(duì)這個(gè)區(qū)間內(nèi)的地址訪問(wèn)時(shí),相當(dāng)于訪問(wèn)了cpld的“寄存器”。
mcu是全局尋址,對(duì)這個(gè)空間的訪問(wèn)和對(duì)ram(0x20000000起)空間的訪問(wèn)是一樣的方式,在C代碼中,可以這樣寫(xiě):
讀cpld:int cpRdReg = *((int *)0x60000000);
寫(xiě)cpld:*((int *)0x60000004) = cpWtReg;
Mcu端讀寫(xiě)cpld較為簡(jiǎn)單,直接通過(guò)上述語(yǔ)句就可以了。
當(dāng)mcu讀寫(xiě)動(dòng)作發(fā)生時(shí),cpld端是如何反應(yīng)的?
當(dāng)上述mcu讀寫(xiě)動(dòng)作發(fā)生時(shí),AHB總線會(huì)把動(dòng)作拆解為讀寫(xiě)信號(hào),傳遞到analog_ip.v的接口,用戶cpld程序需要響應(yīng)該信號(hào)。
以下,以寫(xiě)動(dòng)作 *((int *)0x60000004) = cpWtReg 為例,描述cpld端會(huì)發(fā)生的事情。
回顧下analog_ip.v中的接口部分:
其中slave_ahb_開(kāi)頭的一組信號(hào),是cpld作為主端時(shí)用的,暫時(shí)不用理會(huì)。
Mem_ahb_開(kāi)頭的一組信號(hào),是cpld作為從端使用的。
當(dāng)mcu有讀寫(xiě)操作時(shí),mem_ahb_這組信號(hào)將發(fā)生變化。
這部分是遵循標(biāo)準(zhǔn)的AHB總線協(xié)議的。如果對(duì)AHB總線印象不深,請(qǐng)自行百度。
可參考的講解:https://blog.csdn.net/weixin_46022434/article/details/104987905
幾個(gè)信號(hào)的概述(更詳細(xì)的講解請(qǐng)自行百度):
Ahb_htrans: 當(dāng)前傳輸類型(00: IDLE、01: BUSY、10: NONSEQ、11: SEQ)
Ahb_ready:mcu讀時(shí)要mcu要準(zhǔn)備好cpld才會(huì)寫(xiě)
Ahb_hwrite: 要讀還是要寫(xiě)(1為寫(xiě),0為讀)
Ahb_haddr[32]: 要操作的地址
Ahb_hsize:transfer的大小,以字節(jié)為單位
Ahb_hburst:批量傳輸
Ahb_hwdata[32]:寫(xiě)的數(shù)據(jù),32位
Ahb_hreadyout:輸出信號(hào),mcu寫(xiě)時(shí)cpld是否準(zhǔn)備好
Ahb_hresp:輸出信號(hào),響應(yīng)信號(hào)(OK、retry、error、split)
Ahb_hrdata[32]:讀的數(shù)據(jù),32位
根據(jù)AHB時(shí)序,在一次傳輸中,cpld(slave端)會(huì)先拿到addr地址,讀或?qū)懙臉?biāo)記,然后交互ready信號(hào)后,開(kāi)始數(shù)據(jù)傳輸。
大致如下圖(無(wú)等待類型的圖):
比如,mcu要讀0x60000004的寄存器:
mcu端直接C語(yǔ)言這樣調(diào)用:int cpRdReg = *((int
*)0x60000004);
cpld端,可以根據(jù)以上信號(hào)做如下處理:
----------------------------------------------
//mcu的讀操作響應(yīng)
//mcu端用C語(yǔ)言:int value = *((int *)0x60000004);
reg [31:0] hrdata_reg; ?????? //定義32位的hrdata_reg
always @(posedge sys_clock) begin??????? //clk上升沿觸發(fā)
if (mem_ahb_htrans ==
2'b10 &&? ???????? //NONSEQ狀態(tài),第一次傳輸
? mem_ahb_hready && ??????????????? ????????? //master已ready,可以給數(shù)據(jù)線寫(xiě)入了
? !mem_ahb_hwrite &&??????? ???????? //讀(0 讀,1 寫(xiě))
? mem_ahb_haddr[23:0] == 'h04) ??????? //讀地址為0x60000004(cpld用相對(duì)偏移)
begin
hrdata_reg <=
hwdata_reg;????????????? //把另一準(zhǔn)備好的數(shù)據(jù)給到hrdata_reg
end
end
assign mem_ahb_hrdata = hrdata_reg; //綁定hrdata_reg到讀的數(shù)據(jù)線上
-----------------------------------------------
以上代碼,加入到analog_ip.v的module下,就可以完成cpld對(duì)mcu讀動(dòng)作的響應(yīng)。
比如,mcu要寫(xiě)0x60000000的寄存器:
mcu端直接C語(yǔ)言這樣調(diào)用:*((int *)0x60000000)
= value;
cpld端,可以根據(jù)以上信號(hào)做如下處理:
----------------------------------------------
//mcu的寫(xiě)操作響應(yīng)
//mcu端用C語(yǔ)言:*((int *)0x60000000) = value;
reg [31:0] hwdata_reg; ?????? //定義32位的hwdata_reg
always @(posedge sys_clock) begin??????? //clk上升沿觸發(fā)
if (mem_ahb_htrans ==
2'b00 &&? ?????? //IDLE狀態(tài)
? mem_ahb_hreadyout && ????????? ???????? //cpld已ready狀態(tài),ahb上數(shù)據(jù)可以寫(xiě)過(guò)來(lái)
? mem_ahb_hwrite &&??????? ????????? //寫(xiě)(0 讀,1 寫(xiě))
? mem_ahb_haddr[23:0] == 'h00) ??????? //寫(xiě)地址為0x60000000(cpld用相對(duì)偏移)
begin
hwdata_reg <=
mem_ahb_hwdata;???????? //把收到的數(shù)據(jù)給到hwdata_reg
end
end
//這個(gè)過(guò)程,是把mcu寫(xiě)進(jìn)來(lái)的數(shù)據(jù)收到hwdata_reg中
-----------------------------------------------
這部分的實(shí)例代碼,請(qǐng)參考網(wǎng)盤(pán)上cpld樣例工程《5.mcu讀寫(xiě)cpld寄存器》。
注意:這里展示的,僅僅是基于AHB總線上的數(shù)據(jù)交互。
在實(shí)際應(yīng)用中,比如要實(shí)現(xiàn)一個(gè)串口之類的,往往是慢速設(shè)備,這些是要掛載到apb ? 上的。慢速設(shè)備要經(jīng)過(guò)ahb到apb的bridge,才能最終使用。請(qǐng)繼續(xù)往下看。
二、mcu通過(guò)ahb轉(zhuǎn)apb后的數(shù)據(jù)交互
上節(jié)講述了mcu和cpld之間交互數(shù)據(jù)的實(shí)現(xiàn)方式。
但數(shù)據(jù)是在ahb層面的響應(yīng),慢速設(shè)備不能直接使用。
慢速設(shè)備需要ahb轉(zhuǎn)為apb后,使用apb的信號(hào)來(lái)交互。這種情況,轉(zhuǎn)變?yōu)閙cu和apb? 之間的交互。
mcu和apb之間的交互,相比mcu和aph之間的交互,多了一層ahb到apb的轉(zhuǎn)換。這個(gè)轉(zhuǎn)換是借助于ahb2apb.v模塊來(lái)實(shí)現(xiàn)的(在example/analog下找該.v文件)。
該模塊:輸入是ahb的一組信號(hào),輸出是apb的一組信號(hào)。使用如下圖:
如果實(shí)現(xiàn)mcu和apb的交互,則需要操作的是轉(zhuǎn)換后的這組apb信號(hào)。
關(guān)于apb總線的使用,更多信息請(qǐng)自行百度。
這里只是簡(jiǎn)述下apb信號(hào)列表(與ahb略有不同):
apb_psel:片選
apb_penable:表示傳輸進(jìn)入第二周期(準(zhǔn)備好了讀/寫(xiě))
apb_pwrite:傳輸方向(1-寫(xiě);0-讀)
apb_paddr[32]:地址總線,要操作的地址
apb_pwdata[32]:寫(xiě)的數(shù)據(jù),32位
apb_prdata[32]:讀的數(shù)據(jù),32位
以下展示在apb下如何實(shí)現(xiàn)跟mcu的交互,仍以ahb的兩個(gè)寄存器為例。
1. 首先需要增加ahb轉(zhuǎn)apb的信號(hào)關(guān)聯(lián);
如上圖。
Ahb2apb模塊會(huì)把a(bǔ)hb信號(hào)轉(zhuǎn)換為apb信號(hào)。接下來(lái)操作apb信號(hào)即可。
2. 在轉(zhuǎn)換后的apb信號(hào)中,實(shí)現(xiàn)寫(xiě)和讀的操作。
mcu讀操作時(shí):
比如,mcu要讀0x60000004的寄存器:
mcu端直接C語(yǔ)言這樣調(diào)用:int cpRdReg = *((int
*)0x60000004);
cpld端,可以根據(jù)以上信號(hào)做如下處理:
----------------------------------------------
//mcu的讀操作響應(yīng)
//mcu端用C語(yǔ)言:int value = *((int *)0x60000004);
reg [31:0] ardata_reg; ?????? //定義32位的hrdata_reg
always @(posedge apb_clock) begin????? //clk上升沿觸發(fā)
if (!apb_pwrite
&&??????? ???????????????????? //讀 (0 讀,1 寫(xiě))
apb_penable
&&???????????????????????????????? //是否準(zhǔn)備好
apb_paddr[11:0]
== ‘h04)??? //讀地址為0x60000004(cpld內(nèi)部用相對(duì)偏移)
begin
ardata_reg <=
awdata_reg;????????????? //把另一準(zhǔn)備好的數(shù)據(jù)給到hrdata_reg
end
end
assign apb_prdata = ardata_reg; //綁定hrdata_reg到讀的數(shù)據(jù)線上
-----------------------------------------------
mcu寫(xiě)操作時(shí):
比如,mcu要寫(xiě)0x60000000的寄存器:
mcu端直接C語(yǔ)言這樣調(diào)用:*((int *)0x60000000)
= value;
cpld端,可以根據(jù)以上信號(hào)做如下處理:
----------------------------------------------
//mcu的寫(xiě)操作響應(yīng)
//mcu端用C語(yǔ)言:*((int *)0x60000000) = value;
reg [31:0] awdata_reg; ?????? //定義32位的hwdata_reg
always @(posedge apb_clock) begin????? //clk上升沿觸發(fā)
if (apb_pwrite
&&??????? ????????????????????? //寫(xiě) (0 讀,1 寫(xiě))
apb_penable
&&???????????????????????????????? //是否準(zhǔn)備好
apb_paddr[11:0]
== ‘h00)? //寫(xiě)地址為0x60000000(cpld內(nèi)部用相對(duì)偏移)
begin
awdata_reg <=
apb_pwdata;?????????? //把收到的數(shù)據(jù)給到hwdata_reg
end
end
//這個(gè)過(guò)程,是把mcu寫(xiě)進(jìn)來(lái)的數(shù)據(jù)收到hwdata_reg中
-----------------------------------------------
這個(gè)功能實(shí)現(xiàn)后,其實(shí)是個(gè)簡(jiǎn)單的“空外設(shè)”??梢杂盟鰹閷?shí)現(xiàn)復(fù)雜功能外設(shè)的基礎(chǔ)。
這部分的實(shí)例代碼,請(qǐng)參考網(wǎng)盤(pán)上cpld樣例工程《5.mcu讀寫(xiě)cpld寄存器》。
樣例展示到這里,mcu和cpld的交互上:交互信號(hào)、跟ahb交互數(shù)據(jù)、跟apb交互數(shù)據(jù),基本的交互通路已經(jīng)建立。
接下來(lái),用戶根據(jù)自己的需求,在cpld中交互到數(shù)據(jù)后,編寫(xiě)自己需要的功能即可。
三、DMA在CPLD中的使用
cpld中實(shí)現(xiàn)DMA的邏輯:
1. MCU為master,cpld為slave,mcu對(duì)cpld的交互方式為存取寄存器的方式;
2. mcu中配置好DMA(讀取cpld中準(zhǔn)備好的數(shù)據(jù));
3. cpld中準(zhǔn)備好數(shù)據(jù)后,觸發(fā)dma信號(hào),dma自動(dòng)搬運(yùn)到mcu指定的ram;
4. 搬運(yùn)一次后,dma給cpld一個(gè)clear信號(hào),完成一次dma搬運(yùn);
5. 等到cpld中再次準(zhǔn)備好數(shù)據(jù),將再次觸發(fā)dma信號(hào),重復(fù)3和4;
對(duì)于cpld來(lái)說(shuō),mcu來(lái)讀取數(shù)據(jù)和dma來(lái)讀取數(shù)據(jù),是一致的。
dma來(lái)讀取時(shí),只是每次讀完后會(huì)多給cpld一個(gè)clear信號(hào)。
更多細(xì)節(jié),請(qǐng)參考網(wǎng)盤(pán)上《7.cpld中配合實(shí)現(xiàn)mcu的dma讀取》部分的樣例。
在這個(gè)樣例中,展示了兩部分代碼:
1. mcu中,配置dma讀?。粸榱藴y(cè)試,mcu會(huì)在另一地址給cpld寫(xiě)數(shù)據(jù);
2. cpld中,會(huì)對(duì)mcu寫(xiě)進(jìn)來(lái)的數(shù)據(jù)緩存,緩存后觸發(fā)dma的信號(hào),讓dma來(lái)讀取數(shù)據(jù)。而dma從cpld里讀取數(shù)據(jù)后會(huì)給cpld一個(gè)clear信號(hào),標(biāo)志一次dma交互完成。