zynq编程相关知识 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
#include "xil_cache.h"
void Xil_DCacheEnable(void)
void Xil_DCacheDisable(void)
void Xil_DCacheInvalidate(void)
void Xil_DCacheInvalidateRange(INTPTR adr, u32 len)
void Xil_DCacheFlush(void)
void Xil_DCacheFlushRange(INTPTR adr, u32 len)
void Xil_ICacheEnable(void)
void Xil_ICacheDisable(void)
void Xil_ICacheInvalidate(void)
void Xil_ICacheInvalidateLine(u32 adr)
void Xil_ICacheInvalidateRange(INTPTR adr, u32 len)
说明,Xil_DCacheFlush是将cache中的数据推送到ddr中。一般用于像DAC这样的发送机。CPU填充buffer后,在执行DMA之前,先用Xil_DCacheFlush。
Xil_DCacheInvalidate是将cache的数据无效,也就是将数据从ddr推送到cache。一般用于像ADC这样的接收机。DMA读取传感器数据后,执行Xil_DCacheInvalidate,将数据推送到cache,然后cpu再去访问。
因此,Flush和Invalidate是2个相反的操作。
对于zynq,adr和len需要32bytes对其,因为Cache Line是32bytes。
#include "xil_mmu.h"
void Xil_SetTlbAttributes(INTPTR Addr, u32 attrib);
void Xil_EnableMMU(void);
void Xil_DisableMMU(void);
void* Xil_MemMap(UINTPTR PhysAddr, size_t size, u32 flags);
例如:
//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
Xil_SetTlbAttributes(SHARE_BASE,0x14de2); //禁用Cache缓存
#include "xil_exception.h"
Xil_ExceptionEnable()
Xil_ExceptionDisable()
extern void Xil_ExceptionInit(void);
extern void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data);
void Xil_Remap_OCM(void)
{
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg
Xil_Out32(0xf8000000+0x08,0xdf0d); //slcr.unlock
Xil_Out32(0xf8000910,0x000f); //slcr.ocm_cfg(remap ocm from 0x00000000->0xfffc0000)
Xil_Out32(0xf8000000+0x08,0x0); //slcr_unlock
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg again
}
说明:ocm_remap到高地址,必须由zynq完成,才能生效。才能被其它master(例如microblaze)使用。因此如果是microblaze的程序,如果要使用ocm_remap功能,必须和zynq程序一起烧录运行才可以。
void Xil_Disable_Cache_On_OCM(void)
{
//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
Xil_SetTlbAttributes(0xfffc0000,0x14de2); //禁用Cache缓存
}
这个只适用于zynq自己本身。Xil_SetTlbAttributes函数是zynq才有的。
#include "xtime_l.h"
XTime_SetTime(0);
XTime t1,t2;
function1();
XTime_GetTime(&t1);
function2();
XTime_GetTime(&t2);
//XTime dt1 = ((t1) * 1000000) / (COUNTS_PER_SECOND);
//XTime dt2 = ((t2-t1) * 1000000) / (COUNTS_PER_SECOND);
//xil_printf("dt1=%d, dt2=%d\r\n", dt1,dt2);
//以上语句打印的结果不正确,用下面的方法是ok的
u32 dt1=(u32)t1*(1000000.0/COUNTS_PER_SECOND);
u32 dt2=(u32)(t2-t1)*(1000000.0/COUNTS_PER_SECOND);
printf("dt1=%d, dt2=%d\r\n", dt1,dt2);
说明:以下展示的是ocm_remap后的访问。如果不做remap, 原始地址是可以随便访问的。
#include <stdio.h>
#include "xparameters.h"
#include "xgpio.h"
#include "sleep.h"
#include "xil_cache.h"
#include "xil_exception.h"
#include "xil_mmu.h"
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID // 根据实际硬件配置修改
#define LED_CHANNEL 1
void test_ddr(UINTPTR addr, u32 length, u8 mode)
{
for(UINTPTR p=addr; p<addr+length; p+=4){
Xil_Out32(p,0x11223344);
}
for(UINTPTR p=addr; p<addr+length; p+=4){
int v= Xil_In32(p);
if(mode==0){
xil_printf("%p->%x\r\n",p,v);
}
else if(mode==1 && v!=11223344){
xil_printf("%p\r\n",p);
}
}
}
void Xil_Remap_OCM(void)
{
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg
Xil_Out32(0xf8000000+0x08,0xdf0d); //slcr.unlock
Xil_Out32(0xf8000910,0x000f); //slcr.ocm_cfg(remap ocm from 0x00000000->0xfffc0000)
Xil_Out32(0xf8000000+0x08,0x0); //slcr_unlock
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg again
}
void Xil_Disable_Cache_On_OCM(void)
{
//S=b1 TEX=b100 AP=b11, Domain=b1111, C=b0, B=b0
Xil_SetTlbAttributes(0xfffc0000,0x14de2); //禁用Cache缓存
}
int main() {
XGpio gpio;
int Status;
Xil_DCacheDisable();
Xil_Remap_OCM();
Xil_Disable_Cache_On_OCM();
xil_printf("Zynq GPIO test!\r\n");
// 初始化GPIO
Status = XGpio_Initialize(&gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
printf("GPIO Initialization failed\n");
return XST_FAILURE;
}
// 设置GPIO为输出
XGpio_SetDataDirection(&gpio, LED_CHANNEL, 0x00);
while (1) {
xil_printf("Zynq Print Now!\r\n");
test_ddr(0x10000000,16,0); //test ddr
test_ddr(0xfffc0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xfffd0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xfffe0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xffff0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0x00000000,16,0); //test ocm0, 默认非remap模式可以访问
test_ddr(0x00010000,16,0); //test ocm1, 默认非remap模式可以访问
test_ddr(0x00020000,16,0); //test ocm2, 默认非remap模式可以访问
test_ddr(0xFFFF0000,16,0); //test ocm3, 默认非remap模式可以访问
// 依次翻转四个GPIO引脚
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0001);
usleep(500000); // 等待500毫秒
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0010);
usleep(500000);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0100);
usleep(500000);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b1000);
usleep(500000);
}
return 0;
}
以上是remap成功后,对ocm的读写访问。可见,当remap成功后,可以对ocm0/1/2/3_high_addr的4个bank进行成功的读写。但是不能对ocm0/1/2_low_addr进行读写了,程序已经卡死了。
当注释掉remap后,对ocm进行读写访问。可以对ocm_lower_addr和ocm4_high_addr进行读写,这是默认地址。如果对ocm0/1/2_high_addr进行读写,则程序卡死。
#include <stdio.h>
#include "xparameters.h"
#include "xgpio.h"
#include "sleep.h"
#include "xil_cache.h"
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID // 根据实际硬件配置修改
#define LED_CHANNEL 1
void test_ddr(UINTPTR addr, u32 length, u8 mode)
{
for(UINTPTR p=addr; p<addr+length; p+=4){
Xil_Out32(p,0x11223344);
}
for(UINTPTR p=addr; p<addr+length; p+=4){
int v= Xil_In32(p);
if(mode==0){
xil_printf("%p->%x\r\n",p,v);
}
else if(mode==1 && v!=11223344){
xil_printf("%p\r\n",p);
}
}
}
int main() {
XGpio gpio;
int Status;
Xil_DCacheDisable();
xil_printf("Microblaze GPIO test!\r\n");
// 初始化GPIO
Status = XGpio_Initialize(&gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
printf("GPIO Initialization failed\n");
return XST_FAILURE;
}
// 设置GPIO为输出
XGpio_SetDataDirection(&gpio, LED_CHANNEL, 0x00);
while (1) {
// scanf("%c",&c);
// while(c!='c');
xil_printf("Microblaze Print Now!\r\n");
test_ddr(0x10000000,16,0); //test ddr
// test_ddr(0xfffc0000,16,0); //test ocm, remap模式才可以访问
// test_ddr(0xfffd0000,16,0); //test ocm, remap模式才可以访问
// test_ddr(0xfffe0000,16,0); //test ocm, remap模式才可以访问
// test_ddr(0xffff0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0x00000000,16,0); //test ocm0, 默认非remap模式可以访问
test_ddr(0x00010000,16,0); //test ocm1, 默认非remap模式可以访问
test_ddr(0x00020000,16,0); //test ocm2, 默认非remap模式可以访问
test_ddr(0xFFFF0000,16,0); //test ocm3, 默认非remap模式可以访问
// 依次翻转四个GPIO引脚
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0001);
usleep(500000/10); // 等待500毫秒
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0010);
usleep(500000/10);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0100);
usleep(500000/10);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b1000);
usleep(500000/10);
}
return 0;
}
microblaze的程序
#include <stdio.h>
#include "xparameters.h"
#include "xgpio.h"
#include "sleep.h"
#include "xil_cache.h"
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID // 根据实际硬件配置修改
#define LED_CHANNEL 1
void test_ddr(UINTPTR addr, u32 length, u8 mode)
{
for(UINTPTR p=addr; p<addr+length; p+=4){
Xil_Out32(p,0x11223344);
}
for(UINTPTR p=addr; p<addr+length; p+=4){
int v= Xil_In32(p);
if(mode==0){
xil_printf("%p->%x\r\n",p,v);
}
else if(mode==1 && v!=11223344){
xil_printf("%p\r\n",p);
}
}
}
int main() {
XGpio gpio;
int Status;
Xil_DCacheDisable();
xil_printf("Microblaze GPIO test!\r\n");
// 初始化GPIO
Status = XGpio_Initialize(&gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
printf("GPIO Initialization failed\n");
return XST_FAILURE;
}
// 设置GPIO为输出
XGpio_SetDataDirection(&gpio, LED_CHANNEL, 0x00);
while (1) {
// scanf("%c",&c);
// while(c!='c');
xil_printf("Microblaze Print Now!\r\n");
test_ddr(0x10000000,16,0); //test ddr
test_ddr(0xfffc0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xfffd0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xfffe0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0xffff0000,16,0); //test ocm, remap模式才可以访问
test_ddr(0x00000000,16,0); //test ocm0, 默认非remap模式可以访问
test_ddr(0x00010000,16,0); //test ocm1, 默认非remap模式可以访问
test_ddr(0x00020000,16,0); //test ocm2, 默认非remap模式可以访问
test_ddr(0xFFFF0000,16,0); //test ocm3, 默认非remap模式可以访问
// 依次翻转四个GPIO引脚
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0001);
usleep(500000/10); // 等待500毫秒
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0010);
usleep(500000/10);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b0100);
usleep(500000/10);
XGpio_DiscreteWrite(&gpio, LED_CHANNEL, 0b1000);
usleep(500000/10);
}
return 0;
}
zynq程序,只负责配置ocm_remap
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xgpio.h"
void Xil_Remap_OCM(void)
{
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg
Xil_Out32(0xf8000000+0x08,0xdf0d); //slcr.unlock
Xil_Out32(0xf8000910,0x000f); //slcr.ocm_cfg(remap ocm from 0x00000000->0xfffc0000)
Xil_Out32(0xf8000000+0x08,0x0); //slcr_unlock
xil_printf("%x\r\n",Xil_In32(0xf8000910)); //read slcr.ocm_cfg again
}
int main()
{
init_platform();
Xil_Remap_OCM();
cleanup_platform();
return 0;
}
2个核一起烧录,一起运行
这个结果很有意思:
1)最刚开始,microblaze先运行,因为microblaze简单,初始化速度快。这个时候ocm还没有remap,因此读出得ocm_higher_addr都是0
2)接着zynq开始运行,执行了ocm_remap
3)到了3和4的时候,ocm开始生效了,这个时候microblaze读出的是正确的值。
4)从这里看,zynq与mb的启动先后顺序还是有要求的。这里,要求zynq先起来,这种情况创建一个fsbl可能比较合适。或者,让mb程序先sleep(1),这样后执行。