ZYNQ学习笔记(一):使用BRAM控制器实现PL端寄存器功能效果


实验环境:正点原子领航者开发板(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灯间隔闪烁:
在这里插入图片描述

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值