当前位置: 首页 > article >正文

使用AXI4总线控制MMCM时钟模块

        最近笔者因工作需要开始使用锁相环。我使用的是Xilinx XC7Z015 FPGA主控下的MMCM IP核。之前在其它项目中使用MMCM时,没有深入了解过这个IP核,只当做普通的PLL来使用。但这次仔细研究后发现,该IP核的功能非常强大。例如,ZYNQ系列还可以使用AXI4总线进行时钟动态调节和监控如图所示位MMCM模块结构图。最近我恰好需要这些功能,于是随手记录下了笔记。

 

一、 PL端IP核配置

1、PL端的配置相对比较简单,主要就是开启动态调节和写入DRP寄存器即可。

2、在MMCM Settings这个标签中可以勾选上允许覆盖模式,这样我们就可以自己去调节锁相环中的VCO的倍频系数和输出时钟的分频系数。如图所示。

3、最后在DRP Registers选卡中可以查看到根据我们设置的值而同步的相关寄存器值,这个寄存器都是什么在后续PS端会有详解。

        最后将AXI4总线连接到RAM核中至此PL端就设置完成了。

二、PS端 

         关于PS端简单的说就是使用AXI4总线对MMCM模块的寄存器进行读写就可以了。这里可以参考一下官方给的文档。我这里也是参考的官方文档做修改的。

 2、首先是初始化MMCM模块,这里主要就是获取MMCM模块的地址指针以便于后面的操作。当然MMCM作为ARM核的外设也可以产生中断例如失锁中断等,但我这里没有用到所以就没有设置也没有打开中断功能,需要的同志们也可以查看官方文档里面相对比较详细。

u32 ClkWiz_IntrExample(INTC *IntcInstancePtr, u32 DeviceId)
{
	XClk_Wiz_Config *CfgPtr_Dynamic;
	ULONG Exit_Count = 0;
	u32 Status = XST_SUCCESS;

	/*
	 * Get the CLK_WIZ Dynamic reconfiguration driver instance
	 */
	CfgPtr_Dynamic = XClk_Wiz_LookupConfig(XCLK_WIZ_DYN_DEVICE_ID);
	if (!CfgPtr_Dynamic) {
		return XST_FAILURE;
	}

	/*
	 * Initialize the CLK_WIZ Dynamic reconfiguration driver
	 */
	Status = XClk_Wiz_CfgInitialize(&ClkWiz_Dynamic, CfgPtr_Dynamic,
		 CfgPtr_Dynamic->BaseAddr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/* 调用时钟向导动态重新配置 */
	Clk_Wiz_Reconfig(CfgPtr_Dynamic);

	return XST_SUCCESS;
}

 2、初始化完毕之后根据PG065手册,可以向(BaseAddr + 0x00) 地址中写入0X0A来复位MMCM模块。

         复位完成后,可以通过检测 (BaseAddr + 0x04) 地址中的第0位来判断锁相环是否已经锁定。

3 、通过PS端设置MMCM模块的时钟倍频和分频参数

1、设置VCO的倍频系数根据手册可以可以向(C_BASEADDR + 0x200)地址和(C_BASEADDR + 0x204)写入参数即可。其中(C_BASEADDR + 0x200)地址是用来设置VCO的倍频系数,(C_BASEADDR + 0x204)是用来设置VCO的相位。如图所示

        同理设置OUT0输出的分频系数可以通过配置C_BASEADDR + 0x208和C_BASEADDR + 0x20C即可。重点需要说明的是,使用这几个地址配置后的参数不会立即生效,需要先对C_BASEADDR + 0x25C地址的[0] [1] 位写入1后,等待MMCM复位锁定,然后再向该地址的[0]位写入0才可以完成设置。 

 关于设置官方的历程代码如下:

int Clk_Wiz_Reconfig(XClk_Wiz_Config *CfgPtr_Dynamic)
{
    u32 Count = 0;
    u32 Error = 0;
    u32 Fail  = 0;
    u32 Frac_en = 0;
    u32 Frac_divide = 0;
    u32 Divide = 0;
    float Freq = 0.0;

    Fail = Wait_For_Lock(CfgPtr_Dynamic);    //这个函数是用来检测锁相环是否已经锁定的
    if(Fail) {
	Error++;
        xil_printf("\n ERROR: Clock is not locked for default frequency" \
	" : 0x%x\n\r", *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK);
     }

    /* SW reset applied */
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x00) = 0xA;    //在这里复位了一下锁相环

    if(*(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK) {
	Error++;
        xil_printf("\n ERROR: Clock is locked : 0x%x \t expected "\
	  "0x00\n\r", *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK);
    }

    /* Wait cycles after SW reset */
    for(Count = 0; Count < 2000; Count++);

    Fail = Wait_For_Lock(CfgPtr_Dynamic);
    if(Fail) {
	  Error++;
          xil_printf("\n ERROR: Clock is not locked after SW reset :"
	      "0x%x \t Expected  : 0x1\n\r",
	      *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK);
    }

    /* Calculation of Input Freq and Divide factors*/
    Freq = ((float) CLK_WIZ_VCO_FACTOR/ DYNAMIC_INPUT_FREQ_FACTOR);

    Divide = Freq;
    Freq = (float)(Freq - Divide);

    Frac_divide = Freq * 10000;

    if(Frac_divide % 10 > 5) {
	   Frac_divide = Frac_divide + 10;
    }
    Frac_divide = Frac_divide/10;

    if(Frac_divide > 1023 ) {
	   Frac_divide = Frac_divide / 10;
    }

    if(Frac_divide) {
	   /* if fraction part exists, Frac_en is shifted to 26
	    * for input Freq */
	   Frac_en = (CLK_FRAC_EN << 26);
    }
    else {
	   Frac_en = 0;
    }

    /* Configuring Multiply and Divide values */
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x200) = \
	Frac_en | (Frac_divide << 16) | (Divide << 8) | 0x01;
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x204) = 0x00;

    /* Calculation of Output Freq and Divide factors*/
    Freq = ((float) CLK_WIZ_VCO_FACTOR / DYNAMIC_OUTPUT_FREQFACTOR);

    Divide = Freq;
    Freq = (float)(Freq - Divide);

    Frac_divide = Freq * 10000;

    if(Frac_divide%10 > 5) {
	Frac_divide = Frac_divide + 10;
    }
    Frac_divide = Frac_divide / 10;

    if(Frac_divide > 1023 ) {
        Frac_divide = Frac_divide / 10;
    }

    if(Frac_divide) {
	/* if fraction part exists, Frac_en is shifted to 18 for output Freq */
	Frac_en = (CLK_FRAC_EN << 18);
    }
    else {
	Frac_en = 0;
    }

    /* Configuring Multiply and Divide values */
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x208) =
	    Frac_en | (Frac_divide << 8) | (Divide);
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x20C) = 0x00;

    /* Load Clock Configuration Register values */
    *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x25C) = 0x07;

    if(*(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK) {
	Error++;
        xil_printf("\n ERROR: Clock is locked : 0x%x \t expected "
	    "0x00\n\r", *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK);
     }

     /* Clock Configuration Registers are used for dynamic reconfiguration */
     *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x25C) = 0x02;

    Fail = Wait_For_Lock(CfgPtr_Dynamic);
    if(Fail) {
	Error++;
        xil_printf("\n ERROR: Clock is not locked : 0x%x \t Expected "\
	": 0x1\n\r", *(u32 *)(CfgPtr_Dynamic->BaseAddr + 0x04) & CLK_LOCK);
    }
	return Error;
}

         第二种动态调节的办法是可以通过写DRP相关寄存器来达到动态调节的目的。例如下图是来自官方手册XAP888介绍的DRP寄存器(部分)

        由图可以看出对DRP该方法与上述方法大同小异。需要注意的是这里这是的参数和上述的不太一样,例如CLKREG1寄存器中对VCO的设置是通过设置VCO输出的高低电平时间来设置VCO的频率的。

总结

         这篇文章主要记录了笔者在使用AXI控制MMCM模块的一些笔记和过程,当然MMCM模块还有好多功能本文并未体现。

        笔者水平有限,如有错误尽请指正。


http://www.kler.cn/a/5835.html

相关文章:

  • Qt重写webrtc的demo peerconnection
  • vue 导出excel接口请求和axios返回值blob类型处理
  • Vue.config.productionTip = false 不起作用的问题及解决
  • YOLOv5改进 | CARAFE提高精度的上采样方法
  • 【STM32】利用SysTick定时器定时1s
  • 世优波塔数字人 AI 大屏再升级:让智能展厅讲解触手可及
  • python编程:判断一个数是否是超级素数
  • 解决AJAX在后台使用PHP,ASP,JSP返回数据出现中文乱码的问题
  • 算法训练:贪心与回溯
  • debian 10编译安装gcc 9.5.0
  • 剪枝与重参第一课:修剪结构
  • Python类和对象的命名空间
  • 简述vue生命周期,以及每个周期做的事情?
  • 简单谈谈图形界面和命令行的区别
  • 大一被忽悠进了培训班
  • Java题目训练——统计每个月兔子的总数和字符串通配符
  • 2022年河南省高等职业教育技能大赛软件测试赛项竞赛任务书
  • 使用RegQueryValueEx作为可能为REG_DWORD或REG_SZ的注册表值
  • 蓝桥杯·3月份刷题集训Day03
  • GPT4国内镜像站
  • 第四十八天打卡
  • Date Time组件(下)
  • 【JavaEE】线程池
  • flutter安装自用笔记
  • 第一批00后,已经变成“职场老油条”了
  • TCP流套接字编程