上一節我們觀察了AXI匯流排的訊號,瞭解了基於AXI匯流排讀寫的時序,
這一節我們繼續探索基於AXI匯流排的設計,來看一看ZYNQ系列開發板的獨特優勢,
PS可以控制PL產生定製化的行為,而不需要去動硬體程式碼。
這次實驗是產生頻率和佔空比可調的PWM(Pulse Width Modulation)訊號,
呼叫8次,產生8路PWM波,並用這些訊號去控制8路LED燈,觀察實驗效果。後面會做一個比較。
用的板子是zc702。
新建一個工程,命名為PWM_AXI_Lite
建立基於AXI匯流排的PWM波IP
IP設計為一個暫存器負責控制頻率,一個暫存器負責控制佔空比。
建立一個IP核,tools-->Create and Package ,這裡需要16個暫存器。
建立方法見系列(六)、系列(七),這裡命名為PWM_AXI_Lite。
在IP核工程裡,新建一個PWM模組檔案,這裡佔空比設計的比較糙,直接就用一個計數值代替功能,後面的軟體設計要注意:
新建一個PWM模組檔案
module PWM(
input clk,
input rst_n,
input cnt_set,
input fre_set,
output pwm_o
);
wire[31:0] cnt_set;
wire[31:0] fre_set;
reg [31:0] fre_cnt;
always @(posedge clk) begin
if(!rst_n) begin
fre_cnt <= 32'd0;
end
else begin
if(fre_cnt < fre_set)
fre_cnt <= fre_cnt+1'b1;
else
fre_cnt <= 32'd0;
end
end
assign pwm_o=(cnt_set>fre_cnt);
endmodule
在自動產生的例項檔案裡,新增埠訊號和自定義功能,後面要約束到LED上:
自定義的功能
自定義的功能就是一個暫存器控制頻率(fre_set),一個暫存器控制佔空比(cnt_set):
// Add user logic here
PWM PWM0(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg1),
.fre_set(slv_reg0),
.pwm_o(PWM_o[0])
);
PWM PWM1(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg3),
.fre_set(slv_reg2),
.pwm_o(PWM_o[1])
);
PWM PWM2(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg5),
.fre_set(slv_reg4),
.pwm_o(PWM_o[2])
);
PWM PWM3(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg7),
.fre_set(slv_reg6),
.pwm_o(PWM_o[3])
);
PWM PWM4(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg9),
.fre_set(slv_reg8),
.pwm_o(PWM_o[4])
);
PWM PWM5(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg11),
.fre_set(slv_reg10),
.pwm_o(PWM_o[5])
);
PWM PWM6(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg13),
.fre_set(slv_reg12),
.pwm_o(PWM_o[6])
);
PWM PWM7(
.clk(S_AXI_ACLK),
.rst_n(S_AXI_ARESETN),
.cnt_set(slv_reg15),
.fre_set(slv_reg14),
.pwm_o(PWM_o[7])
);
// User logic ends
在頂層模組新增好使用者訊號,一個是埠裡的,一個是呼叫裡的:
新增ILA
修改完後重新打包好。
回到原先建的工程,將這個IP新增到IP庫裡,然後Create Block Design,
新增ZYNQ核和PWM_AXI_Lite,為了觀察PWM波訊號。
這裡又新增了一個ILA(為了簡化,可以去掉),配置如下:
連線CLK 和 FCLK_CLK0 ,連線 Probe0 和 PWM_o,最後建立的系統如下:
新增約束檔案
新增約束檔案,將8路PWM波繫結到8個LED上:
#GPIO PMOD1
set_property PACKAGE_PIN E15 [get_ports {PWM_o[7]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[7]}]
set_property PACKAGE_PIN D15 [get_ports {PWM_o[6]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[6]}]
set_property PACKAGE_PIN W17 [get_ports {PWM_o[5]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[5]}]
set_property PACKAGE_PIN W5 [get_ports {PWM_o[4]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[4]}]
#GPIO PMOD2
set_property PACKAGE_PIN V7 [get_ports {PWM_o[3]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[3]}]
set_property PACKAGE_PIN W10 [get_ports {PWM_o[2]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[2]}]
set_property PACKAGE_PIN P18 [get_ports {PWM_o[1]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[1]}]
set_property PACKAGE_PIN P17 [get_ports {PWM_o[0]}]
set_property IOSTANDARD LVCMOS25 [get_ports {PWM_o[0]}]
一系列常規操作,生成位元流檔案後,Lanch到SDK。
SDK部分設計
在BSP包裡找到xparameter.h檔案:
在xparameters.h檔案裡找到系統為我們的PWM IP配置的地址,待會我們要操作它的暫存器:
開啟xparameters.h檔案,Ctrl+F:
這個基地址就是我們的暫存器0的地址,然後我們將各路PWM波的頻率和佔空比寫入:
#include <stdio.h>
#include "xparameters.h"
#include "xil_io.h"
#include "sleep.h"
#include "xil_types.h"
int main(){
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+4,30000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+8,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+12,20000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+16,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+20,10000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+24,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+28,8000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+32,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+36,6000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+40,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+44,4000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+48,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+52,2000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+56,40000000);
Xil_Out32(XPAR_PWM_AXI_LITE_V1_0_0_BASEADDR+60,100000);
return 0;
}
板子上電,然後Program FPGA,debug as後,
在vivado裡會自動開啟除錯介面,觸發後能看到8路波形,
在板子上我們可以看到LED燈依次閃爍!
總結:
這裡我們達到了led依次閃爍的效果,如同系列(六)達到的效果,
但是這裡有本質的區別,系列(六)需要CPU一直髮送指令控制LED燈,
而這次試驗CPU寫入頻率和佔空比後,現在8路PWM波自己工作,是PL完成的,不需要CPU發命令,
CPU可以去幹其他事情。這就是SOPC的優勢!