文章目录
实验环境:正点原子领航者开发板(zynq7010),vivado2020.2版本。
目标:使用BRAM控制器实现PL端寄存器功能效果,最终通过在PS编程,编辑实现由该寄存器某位控制的led灯间隔闪烁。
一、vivado工程建立
点击“Create Project ”来创建一个新的工程
弹出的页面点击Nest
下面输入工程的名称和路径(注意要英文路径)
继续点击next
接下来,这是询问是否添加或创建源文件,即硬件描述语言文件。这里可以将目标语言修改为VHDL(当然这里不修改也可以在工程生成后再修改),继续点击next
接下来是询问是否添加引脚约束文件,我们不用理,点击next,之后在工程中添加。
下一步是选择具体的xilinx产商的芯片型号,因为我们所用的zynq7010,所以在搜索局域栏输入xc7z010,再选择到下面具体的xc7z010clg400-2.
这里的“400”指的是IO引脚数,也就是旁边的“I/O Pin Count”
这里的“-2”是最大可支持的时钟快慢的等级,它越大说明支持的时钟频率越快,这里选中间的等级2就可以了,一般也不会跑很快的时钟。
如果是zynq7020,则是xc7z020clg400-2
这样就建立了一个空白工程了
之前提到的,目标语言没有修改的话,可以在这个界面上点击进行修改
二、设计流程
1.创建顶层文件top
点击Add Sources (添加源文件)
它会出现弹窗:
Add or create constraints(添加或创建约束文件)
Add or create design sources(添加或创建设计源文件)
Add or create simulation sources(添加或创建仿真源文件)
选择第二项,并点击next
点击Create File,输入源文件名字为top,点击ok和finish(注意确认File type是不是VHDL)
这里保持默认,点击OK就好,这个弹窗是在问你是否需要提前添加端口信息(比如端口名称,类型等),我们之后自己用语法添加,不在这里添加。
等待刷新完,点击箭头展开,可以看到多了一个top的文件
2.创建BD设计文件
因为需要用到PS(ARM)端,所以要创建一个BD(block design),名字命名为system,最后点击ok
接着会进入BD设计的界面,因为什么都还没加,所以是空白的。
我们点击+号,输入“zynq”进行搜索,找到这个“ZYNQ7 Processing System”,这个也就是所谓的PS部分,vivado相当于把这部分封装成了类似ip核的东西,只需要用到什么外设功能就选择什么。
得到下面这样的,它默认打开了一些接口功能,如果不需要你可以选择关闭,我们这次实验这些接口都需要用上,所以就不关闭了。
DDR: 是一种动态存储器,会掉电丢失的,PS(arm)的程序,变量都有在DDR的对应地址,我们这次的PL寄存器地址也是用到DDR实现的
FIXED_IO: 是专用的内部固化的外设IO,作用于54个MIO口
M_AXI_GP0和M_AXI_GP0_ACLK是一起的,它是属于AXI协议的一种低速接口
FCLK_CLK0:是PS所生成的时钟信号,这个时钟可以引出给PL使用
FCLK_RESET0_N:是上面时钟对应的复位信号
双击打开它,可以进行设置
1.我们这里先点击第三项Peripheral I/O Pins,可以看到许多图像化显示的外设资源
2.把Bank1的电平由LVCMOS 3.3V改为LVCMOS 1.8V(一般,不管我有没有用到Bank1的资源,我都习惯性的会修改它)
- Bank0和Bank1 全称其实是Bank500和Bank501,这两个都是ps端的资源,在正点原子所提供的核心板原理图上可以看到
在07_FPGA POWER部分可以看出各个BANK的供电电压值大小,其中,500电压是3.3V,501电压是1.8V,所以上面的BANK1需要修改为1.8V的选项。
- 接着下拉列表,找到UART0,并勾选位于MIO14,15的UART0
而为什么是选择MIO14和MIO15呢?
这是由它的原理图所确定的,先观察正点原子的底板原理图,找到UART
画圈的部分是跳帽,决定是哪个UART接入CH340实现串口转USB。结合实物图,找到P5,根据白色丝印显示,上面的是接到PS的串口,此时便是PS的串口连接到CH340最后运用USB线与电脑通讯的:
通过在底板原理图搜索UART1,我们可以知道,串口连接到核心板连接器J2部分的87和89脚
所谓连接器,就是将核心板和底板引脚相连的存在,既然知道它对应在连接器的引脚号,便可以在核心板原理图的Connect部分找到它,即87,89:
再通过搜索,找到位于ZYNQ芯片上的,具体的MIO引脚号,即MIO14,MIO15:
3.回到配置界面,选择DDR Configuration,选择DDR的芯片型号:
XC7Z020 的核心板选择 MT41J256M16RE-125,XC7Z010 的核心板选择 MT41J128M16 HA-125。需要注意的是,我们在这里选择的型号并不是领航者核心板上的 DDR3 型号,而是参数接近的型号,或者兼容的型号。
最后点击OK
关于这一个IP核的配置就结束了。当然,还有一个地方需要记住,所用到的FCLK的时钟频率需要记住,是50MHZ,之后它同样将作为PL的时钟信号:
3.添加BRAM控制器IP核进BD
关于这个实验,除了zynq这个ip核外,还要添加一个BRAM控制器的IP核
配置如下,只需要把BRAM PORT从2修改为1即可,因为本次实验一个BRAM_PORT就行,接着点击ok:
生成之后,先左键选中BRAM_PORTA端口(变色即选中),再右键点击,选择Make External,即BRAM_PORTA端口将外部引用:
生成端口后,需要双击它,修改端口模式为允许读写:
之后,点击Run Connection Automation(进行自动连线),勾选S_AXI,最后点击OK,它会将自己能自动连线的部分进行连线。
连接完成后,在 Diagram 窗口空白处右击,然后选择“Regenerate Layout”对设计进行重新布局,布局后的界面如下图所示:
从上图中可以看到,在执行了自动连接之后,工具自动添加了两个 IP 核,分别是 AXI 互联(AXI Smart Connect)和处理器系统复位(Processor System Reseet)。
AXI Smart Connect IP 核用于将一个(或多个)AXI 存储器映射的主器件连接到一个(或多个)存储器映射的从器件。
Processor System Reseet IP 核为整个处理器系统提供复位信号。它会处理输入端的各种复位条件,并在输出端产生相应的复位信号。
####
之后,点击Run Block Automation,此时 ZYNQ7 PS 模块引出了两组外部接口,分别是 DDR 和 FIXED_IO,引出的接口将会被分配到 ZYNQ器件具体的引脚上。
刷新后,界面如下:
然后,点击Address Editer可以看到这个BRAM控制器所分配的DDR地址空间为0x4000_0000到0x4000_1fff的8k范围,当然这个8k大小也是可以修改的,根据自己的实际需求来。
如果它没有分配地址空间,你也可以右键assign手动让它部署下地址。
之后我们便可以通过在PS上访问DDR对应地址空间来实现PL的寄存器功能
对了,最后差点忘了,还要把FCLK和复位信号也引出来。
FCLK和复位信号与前面的BRAM_PORTA引出的方法不同,FCLK和复位信号因为已经在BD进行了连线,不能直接make external。
需要先建立一个port,在任意空白处右键,选择create port,配置如下:
生成的两个端口如下所示:
左键选中,并长按,将这两个端口与对应信号线相连:
最后点击验证validata design
等待弹出successful的窗口,则说明没有错误,如有问题,请根据报错提示去寻找解决:
最后,点击source窗口,右键system,点击generate output products,这一步就是将我们的BD设计中的各个IP核模块和连线,转为VHDL语言实现例化:
可以看到,原来的system下面是这样的:
generate output products后就变成了这样的,即将我们的连线用VHDL的例化实现了:
4.在top进行例化
在top例化之前,需要再新建一个命名为bram_data_control的VHDL文件,来实现我们的寄存器功能:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_unsigned.ALL;
USE IEEE.STD_LOGIC_arith.ALL;
ENTITY bram_data_control IS
PORT
(
-- BRAM PORT
ram_clk : IN STD_LOGIC;
ram_wea : IN STD_LOGIC;
ram_en : IN STD_LOGIC;
ram_addr : IN STD_LOGIC_VECTOR(12 DOWNTO 0);
ram_dout : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
ram_din : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
-- DATA PORT
Cntl_Reg : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
Fifo_Num_Reg : OUT STD_LOGIC_VECTOR(31 DOWNTO 0)
);
END bram_data_control;
ARCHITECTURE Behavioral OF bram_data_control IS
-- Internal Signal ---------------------------------------------------------
SIGNAL cntl_reg_i : STD_LOGIC_VECTOR(31 DOWNTO 0);
--SIGNAL fifo_num_reg_i : STD_LOGIC_VECTOR(31 DOWNTO 0) :=x"00000000";
BEGIN
-- Write Data to Address --
--为PS(ARM)端口实现写入寄存器功能
PROCESS(ram_clk)
BEGIN
IF RISING_EDGE(ram_clk) THEN
----检测到写使能
IF ram_en = '1' AND ram_wea = '1' THEN
--根据PS给的地址,对应把值写入,每个地址可以命为不同的寄存器
CASE ram_addr(9 DOWNTO 2) IS
WHEN x"00" =>
cntl_reg_i <= ram_dout(31 DOWNTO 0);
WHEN OTHERS =>
NULL;
END CASE;
END IF;
END IF;
END PROCESS;
--为PS(ARM)端口实现读取寄存器值的功能
--检测到对应地址x“00”,其为16进制表示,把值赋值给读取数据通道ram_din
ram_din <= cntl_reg_i WHEN ram_addr(9 DOWNTO 2) = x"00" ELSE
X"00000000";
--将cntl_reg_i这个变量与实际端口相互连接
--为什么不直接用Cntl_Reg?因为Cntl_Reg是输出端口(out port),不能做输入
--而signal(信号)就比端口要灵活多了,既可以读取也可以赋值
Cntl_Reg <= cntl_reg_i;
END Behavioral;
接下来进行例化操作,对于bram_data_control文件的例化方法不多说明
而对于system这个BD设计文件,可以通过右键可以看到关于它的例化模板
通过这个模板,将system在top中例化实现,最终top文件为:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity top is
-----端口
Port
(
DDR_addr : inout STD_LOGIC_VECTOR ( 14 downto 0 );
DDR_ba : inout STD_LOGIC_VECTOR ( 2 downto 0 );
DDR_cas_n : inout STD_LOGIC;
DDR_ck_n : inout STD_LOGIC;
DDR_ck_p : inout STD_LOGIC;
DDR_cke : inout STD_LOGIC;
DDR_cs_n : inout STD_LOGIC;
DDR_dm : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_dq : inout STD_LOGIC_VECTOR ( 31 downto 0 );
DDR_dqs_n : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_dqs_p : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_odt : inout STD_LOGIC;
DDR_ras_n : inout STD_LOGIC;
DDR_reset_n : inout STD_LOGIC;
DDR_we_n : inout STD_LOGIC;
FIXED_IO_ddr_vrn : inout STD_LOGIC;
FIXED_IO_ddr_vrp : inout STD_LOGIC;
FIXED_IO_mio : inout STD_LOGIC_VECTOR ( 53 downto 0 );
FIXED_IO_ps_clk : inout STD_LOGIC;
FIXED_IO_ps_porb : inout STD_LOGIC;
FIXED_IO_ps_srstb : inout STD_LOGIC;
------------------------------------------------------------------------------------
LED_PORT : out std_logic
);
end top;
architecture Behavioral of top is
-------------------------------------------------------------------------------------
-------------------模块的端口声明
---------------------------------------------------------------------------------------
component system is
port
(
--BRAM PL端寄存器
BRAM_PORTA_0_addr : out STD_LOGIC_VECTOR ( 12 downto 0 );
BRAM_PORTA_0_clk : out STD_LOGIC;
BRAM_PORTA_0_din : out STD_LOGIC_VECTOR ( 31 downto 0 );
BRAM_PORTA_0_dout : in STD_LOGIC_VECTOR ( 31 downto 0 );
BRAM_PORTA_0_en : out STD_LOGIC;
BRAM_PORTA_0_rst : out STD_LOGIC;
BRAM_PORTA_0_we : out STD_LOGIC_VECTOR ( 3 downto 0 );
---DDR
DDR_addr : inout STD_LOGIC_VECTOR ( 14 downto 0 );
DDR_ba : inout STD_LOGIC_VECTOR ( 2 downto 0 );
DDR_cas_n : inout STD_LOGIC;
DDR_ck_n : inout STD_LOGIC;
DDR_ck_p : inout STD_LOGIC;
DDR_cke : inout STD_LOGIC;
DDR_cs_n : inout STD_LOGIC;
DDR_dm : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_dq : inout STD_LOGIC_VECTOR ( 31 downto 0 );
DDR_dqs_n : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_dqs_p : inout STD_LOGIC_VECTOR ( 3 downto 0 );
DDR_odt : inout STD_LOGIC;
DDR_ras_n : inout STD_LOGIC;
DDR_reset_n : inout STD_LOGIC;
DDR_we_n : inout STD_LOGIC;
FIXED_IO_ddr_vrn : inout STD_LOGIC;
FIXED_IO_ddr_vrp : inout STD_LOGIC;
FIXED_IO_mio : inout STD_LOGIC_VECTOR ( 53 downto 0 );
FIXED_IO_ps_clk : inout STD_LOGIC;
FIXED_IO_ps_porb : inout STD_LOGIC;
FIXED_IO_ps_srstb : inout STD_LOGIC;
FCLK_50Mhz : out STD_LOGIC;
FCLK_RST_N : out STD_LOGIC
);
end component system;
--BRAM—寄存器
component bram_data_control IS
PORT
(
-- BRAM PORT
ram_clk : IN STD_LOGIC; --时钟输入端口
ram_wea : IN STD_LOGIC; --写使能,高有效
ram_en : IN STD_LOGIC; --RAM使能,高有效
ram_addr : IN STD_LOGIC_VECTOR(12 DOWNTO 0); --地址输入端口
ram_dout : IN STD_LOGIC_VECTOR(31 DOWNTO 0); --写数据通道
ram_din : OUT STD_LOGIC_VECTOR(31 DOWNTO 0); --读数据通道
-- DATA PORT
Cntl_Reg : OUT STD_LOGIC_VECTOR(31 DOWNTO 0) --控制寄存器
);
END component bram_data_control;
---------------------------------------------------------------------------------------------
---------信号声明
---------------------------------------------------------------------------------------------
--BRAM----
signal BRAM0_PORTA_addr_i : STD_LOGIC_VECTOR ( 16 downto 0 );
signal BRAM0_PORTA_clk_i : STD_LOGIC;
signal BRAM0_PORTA_din_i : STD_LOGIC_VECTOR ( 31 downto 0 );
signal BRAM0_PORTA_dout_i : STD_LOGIC_VECTOR ( 31 downto 0 );
signal BRAM0_PORTA_en_i : STD_LOGIC;
signal BRAM0_PORTA_rst_i : STD_LOGIC;
signal BRAM0_PORTA_we_i : STD_LOGIC_VECTOR ( 3 downto 0 );
--data_control
signal cntl_reg_i : STD_LOGIC_VECTOR ( 31 downto 0 );
--PFCLK
signal FCLK_50Mhz : STD_LOGIC;
signal FCLK_RST_N : STD_LOGIC;
begin
--------------------------------------------------------------------------------------------------
----------以begin为界限,后面的内容为:例化以及功能描述
--------------------------------------------------------------------------------------------------
system_inst: system
port map
(
BRAM_PORTA_0_addr(12 downto 0) => BRAM0_PORTA_addr_i(12 downto 0),
BRAM_PORTA_0_clk => BRAM0_PORTA_clk_i,
BRAM_PORTA_0_din(31 downto 0) => BRAM0_PORTA_din_i(31 downto 0),
BRAM_PORTA_0_dout(31 downto 0) => BRAM0_PORTA_dout_i(31 downto 0),
BRAM_PORTA_0_en => BRAM0_PORTA_en_i,
BRAM_PORTA_0_rst => BRAM0_PORTA_rst_i,
BRAM_PORTA_0_we(3 downto 0) => BRAM0_PORTA_we_i(3 downto 0),
DDR_addr(14 downto 0) => DDR_addr(14 downto 0),
DDR_ba(2 downto 0) => DDR_ba(2 downto 0),
DDR_cas_n => DDR_cas_n,
DDR_ck_n => DDR_ck_n,
DDR_ck_p => DDR_ck_p,
DDR_cke => DDR_cke,
DDR_cs_n => DDR_cs_n,
DDR_dm(3 downto 0) => DDR_dm(3 downto 0),
DDR_dq(31 downto 0) => DDR_dq(31 downto 0),
DDR_dqs_n(3 downto 0) => DDR_dqs_n(3 downto 0),
DDR_dqs_p(3 downto 0) => DDR_dqs_p(3 downto 0),
DDR_odt => DDR_odt,
DDR_ras_n => DDR_ras_n,
DDR_reset_n => DDR_reset_n,
DDR_we_n => DDR_we_n,
FIXED_IO_ddr_vrn => FIXED_IO_ddr_vrn,
FIXED_IO_ddr_vrp => FIXED_IO_ddr_vrp,
FIXED_IO_mio(53 downto 0) => FIXED_IO_mio(53 downto 0),
FIXED_IO_ps_clk => FIXED_IO_ps_clk,
FIXED_IO_ps_porb => FIXED_IO_ps_porb,
FIXED_IO_ps_srstb => FIXED_IO_ps_srstb,
FCLK_50Mhz => FCLK_50Mhz,
FCLK_RST_N => FCLK_RST_N
);
--BRAM
bram_data_control_inst: bram_data_control
port map
(
-- BRAM PORT
ram_clk => BRAM0_PORTA_clk_i,
ram_wea => BRAM0_PORTA_we_i(0),
ram_en => BRAM0_PORTA_en_i,
ram_addr => BRAM0_PORTA_addr_i(12 DOWNTO 0),
ram_dout => BRAM0_PORTA_din_i,
ram_din => BRAM0_PORTA_dout_i,
-- DATA PORT
Cntl_Reg => cntl_reg_i
);
--当然,这里一般是直接用组合逻辑LED_PORT <= cntl_reg_i(4);的
--我只是想着这FCLK时钟都引出来了,不用有点浪费了,之后FCLK在拓展功能也会用到,就加了个时序逻辑写的
process(FCLK_50Mhz) begin
if rising_edge(FCLK_50Mhz) then
if(FCLK_RST_N = '0') then
LED_PORT <= '0';
else
LED_PORT <= cntl_reg_i(4);
end if;
end if;
end process;
end Behavioral;
效果如下:
5.添加约束文件
引脚约束则是指将FPGA芯片上的引脚与外部硬件接口进行连接的一种约束,包括引脚映射、I/O标准等
从top文件的端口列表中可以看到,可大致分为DDR,FIXED_IO和LED_PORT三个部分,而其中,DDR和FIXED_IO都是ps部分的引脚,我们只需要对PL部分,即FPGA部分的引脚进行约束,或者说引脚映射。
LED_PORT便是我们所需要对其进行编写约束文件的端口,本次实验计划便是使用PL所连接的LED。
创建引脚约束文件和创建VHDL源文件类似,不过在勾选时需要选择第一项,即constraints(约束)。
最后创建出一个名为top,后缀为xdc的文件:
双击打开它,编写如下代码:
set_property PACKAGE_PIN J16 [get_ports LED_PORT]
set_property IOSTANDARD LVCMOS33 [get_ports LED_PORT]
上面代码的意思就是LED_PORT这个端口对应的是PL(FPGA)的J16引脚,它的IO电平标准为3.3V。
首先,为什么是J16引脚呢,我们可以先翻阅核心板的原理图,搜索led,找到PL部分对应的led,当然底板上如果有led也可以用:
并且我们可以看到,当PL_led引脚为高电平时,该led灯便会发亮,在后面PS部分需要留意:
找到了它,便以他去找到它与zynq芯片的哪个引脚相连了,直接以它的名字PL_led搜索,发现它是连接到了位于Bank35的J16引脚:
然后参看,这个Bank15上面的引脚是什么电平标准,翻阅可知,是3.3V,所以第二句 IOSTANDARD LVCMOS33。
6.添加bit流,导出硬件设计文件
最后点击生成bit流文件,一路点击yes和ok:
点击这个符号,即可看到汇总界面,看到生成bit流的进度情况,等待生成bit流完成并出现弹窗提醒:
关闭这个弹窗,接下来导出硬件设计文件:
导出时需要勾选,包含bit流文件
后面一路next就行
最后它会在你指定的目录下生成一个xsa后缀的文件,这个就是我们的硬件设计文件:
7.利用vitis建立PS工程
Tools-> Lanuch Vitis启动vitis
在工程路径下,再新建一个文件夹放vitis工程,我这文件夹直接命名为了vitis:
接着点击launch
在弹出的界面,点击第二个Create Platfrom Project,点击第一个也可以(第一个application相当于是“platform建立”加上“程序创建”):
进入后,先需要对Platfrom Project命名,我这直接取了“system”:
接着点击Browse,他可能一开始并不是直接识别到你这个vivado工程的路径,然后你要找到你建立的这个vivado工程所在路径,选中之前我们生成的xsa硬件设计文件
特别注意:这里的Opeating system,是确定你这个vitis工程设计是基于裸机(不含操作系统,standalone)或者freertos操作系统,linux操作系统,选择不同,所用到的库等地方都会有不同。这次,先保持默认,选择standalone。
创建成功后,先右键,将库编译一下:
随后创建一个hello world例程:
前面保持默认 -> next -> 为工程命名 -> next -> next -> 选择 hello world ->
finish
创建成功如下界面:
点开src,可以看到我们的c程序部分:
双击helloworld.c 找到mian函数:
增加代码如下:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#define CNTL_REG (*(((unsigned int *)XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR) + 0x00))
#define CNTL_REG_LED (0x01<<4) //第4位
int main()
{
init_platform();
print("Hello World\n\r");
print("Successfully ran Hello World application");
while(1){
CNTL_REG |= CNTL_REG_LED;//PL端的控制寄存器对应位,置一,使led亮
usleep(500000);//500ms延时
//CNTL_REG &= ~(0x01<<4)
CNTL_REG &= ~CNTL_REG_LED;//PL端的控制寄存器对应位,变0,使led熄灭
usleep(500000);//500ms延时
}
cleanup_platform();
return 0;
}
选中右键,open declaration便可以看到它是在哪里被定义的
跳转后,我们可以知道,它其实就是BRAM控制器当时被赋予的DDR地址
而上面命名的CNTL_REG 其实就相当于是0x40000000地址下的对应的地址指针。我们直接对这个地址上所对应的值进行赋值等操作,映射到PL就相当于是寄存器的功能。
需要注意下面这个语句,它涉及到一个指针的加减操作:
#define CNTL_REG (*(((unsigned int *)XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR) + 0x00))
相当于
#define *CNTL_REG (((unsigned int *)XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR) + 0x00)
指针运算原理
在c语言中,指针表示一个内存地址,内存地址是数字类型的值,通常用十六进制数表示。因此,可以对指针进行算数运算,就像int类型的两个变量可以相加一样。有四种算数运算符可以用在指针上,++, – , + , -
为了更好的理解指针的算数运算,可以假设有一个整形的指针变量ip,它指向的内存地址是1000,使用32位的int,下面我们对指针进行算数运算:
ip++
执行完代码ip++;后,指针变量ip指向的内存空间地址是1004。因为指针ip执行++算数操作后,它会指向的下一个int类型的内存地址,而一个32位的int类型的指针变量长度为4个字节,一次自增1,内存地址加4个字节。++算术运算符会移动指针指向下一个内存空间。如果是一个char类型的指针,它的指向也是1000,char类型的指针执行++算数操作后,指针会指向内存地址为1001,因为一个char类型的指针长度为1。
以这个程序为例:
#define XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR 0x40000000U
unsigned int TEST_REG = ( (unsigned int *)XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x01);
//此时TEST_REG为0x40000004,
//将 XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR 转换为 unsigned int * 并加上 0x01 时,
/*你实际上是在进行指针算术,而不是简单的地址加法。
在C语言中,指针算术是基于指针指向的对象的大小的。
由于 TEST_REG 被声明为指向 unsigned int 的指针,
因此加上 0x01 实际上意味着地址增加了
sizeof(unsigned int) 个字节(也就是4个字节),
而不是简单地增加了 1 个字节。*/
而若是
unsigned int TEST_REG = (*(((unsigned int *)XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR) + 0x01));
//则此时运用了解引用指针,
//使得TEST_REG为具体的0x40000004地址下的值,
//即0x00000000(默认值);
所以我们这里看着是加0x01(0000_0001),实际上地址最终是0x04(0000_0100)。
这也就是,为什么在PL中bram_data_control文件中取得是第2位开始,而不是第0位,因为相当于乘以了4:
PROCESS(ram_clk)
BEGIN
IF RISING_EDGE(ram_clk) THEN
----检测到写使能
IF ram_en = '1' AND ram_wea = '1' THEN
--根据PS给的地址,对应把值写入,每个地址可以命为不同的寄存器
CASE ram_addr(9 DOWNTO 2) IS
WHEN x"00" =>
cntl_reg_i <= ram_dout(31 DOWNTO 0);
WHEN x"01" =>
debug <= ram_dout(31 DOWNTO 0);
WHEN OTHERS =>
NULL;
END CASE;
END IF;
END IF;
END PROCESS;
三、使用步骤
对程序进行编译:
编译通过后点击debug,下载程序到板卡上:
下载完成后,跳转到debug,点击运行:
最终能看到板卡上的led灯间隔闪烁: