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