C2000基于EMIF总线扩展FPGA

2023-10-25 05:10
文章标签 fpga 扩展 总线 c2000 emif

本文主要是介绍C2000基于EMIF总线扩展FPGA,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        看了一下网上基于C2000系列DSP的EMIF扩展FPGA的例子还是比较少的,学习了一下。这里分享一个基于8位EMIF的C2000系列DSP扩展FPGA的例子供大家参考。

        开头说一下我的设备,DSP是C2000系列的F28388D ControlCARD,FPGA用的黑鹰科技的S604模块(淘宝购买,200来块钱不带仿真器,基于Xlinx Spartan6 的 XC6SLX16)。

        另外再说一下28388控制卡为了扩展Ethernet和EtherCAT,失去了一些EMIF总线的Address与Data引脚,只支持8位数据宽度与有限的地址宽度,下图是官方论坛的一个解释。还有关于低成本FPGA的话我更推荐使用Altera的FPGA,因为Spartan6系列的FPGA用的Xlinx旧的开发环境Xlinx ISE,win11不支持,win10下载也比较繁琐,我是装了个win7的虚拟机才把ISE下载成功的。


       OK,开始正文。在我的项目中,我的DSP需要读取AD芯片采样后传给FPGA的采样值。换句话说,可以把FPGA看作一个存储器,FPGA存储了AD芯片的采样值。DSP通过EMIF总线去读取FPGA这个存储器中的数值。

        我们先稍微了解一下EMIF总线的通信。EMIF总线的通信分为同步通信和异步通信,TI手册(SPRAC96A )中明确提到了,与FPGA的通信属于异步通信(很好,可以少看一半的手册)。

        再来看看,异步通信下EMIF总线都有哪些信号(这些信号中DQM和RNW这两个信号具体有什么用我也不是很清楚)。由于只涉及到读(嘿嘿,又能少看不少),所以只需要关注数据线(D),地址线(A),扩展地址线(BA),读使能(OE)、片选信号(CS)以及时钟信号(CLK)即可。

        再来看看手册中正常模式(Normal Mode,还有一个Strobe Mode)下读取操作的时序流程,不管是读操作还是写操作都会分为Setup-Strobe-Hold这样三个序列,Setup阶段,地址总线与扩展地址线的会生成对应地址的信号,在Strobe阶段结束时,DSP会对数据信号对应的引脚采样,读取数据。另外需要注意的时是对小于数据总线宽度的设备发出额外的读取操作,以便完成整个32位数据的访问。像我使用了8位地址总线,那么每次读取操作会自动生成4个Setup-Strobe-Hold序列去完成整个数据的读取。

        那么这些EMIF信号是怎么生成的呢?

         完成初始化配置后直接对对应地址进行进行读写操作就行,可以在下图中看到异步通信的起始地址位是0x100000,那么程序中对0x100000地址读取,DSP自动会在对应的引脚生成片选信号,读信号,地址信号与扩展地址信号的电平状态。

        看到这里,可以大致分析出FPGA要做哪些事了,也就是根据地址信号在把数据总线的二进制电平信号输出在FPGA的引脚上让DSP读取就行
        看一下DSP处的主要代码,也是根据TI的例程修改的,初始化后,将0x100000-0x100009的数据赋值给数组ReadDemo:


uint32_t ReadDemo[5]={0};
uint32_t a=0;void setupEMIF1PinmuxAsync8Bit(void);void main(void)
{EMIF_AsyncTimingParams tparam;Device_init();                  // Initialize device clock and peripherals.DINT;Device_initGPIO();              // Setup GPIO by disabling pin locks and enabling pullups.Interrupt_initModule();         // Initialize PIE and clear PIE registers. Disables CPU interrupts.Interrupt_initVectorTable();    // Initialize the PIE vector tableSysCtl_setEMIF1ClockDivider(SYSCTL_EMIF1CLK_DIV_2);         // Configure to run EMIF1 on full Rate. (EMIF1CLK = CPU1SYSCLK)EMIF_selectMaster(EMIF1CONFIG_BASE, EMIF_MASTER_CPU1_G);    // Grab EMIF1 For CPU1.EMIF_setAccessProtection(EMIF1CONFIG_BASE, 0x0);            // Disable Access Protection. (CPU_FETCH/CPU_WR/DMA_WR)EMIF_commitAccessConfig(EMIF1CONFIG_BASE);      // Commit the configuration related to protection.EMIF_lockAccessConfig(EMIF1CONFIG_BASE);        // Lock the configuration so that EMIF1COMMIT registersetupEMIF1PinmuxAsync8Bit();                    // Configure GPIO pins for EMIF1.EMIF_setAsyncMode(EMIF1_BASE, EMIF_ASYNC_CS2_OFFSET,EMIF_ASYNC_NORMAL_MODE);             // Configures Normal Asynchronous Mode of Operation.EMIF_disableAsyncExtendedWait(EMIF1_BASE, EMIF_ASYNC_CS2_OFFSET);                        // Disables Extended Wait Mode.EMIF_setAsyncDataBusWidth(EMIF1_BASE, EMIF_ASYNC_CS2_OFFSET,EMIF_ASYNC_DATA_WIDTH_8);   // Configure EMIF1 Data Bus Width.tparam.rSetup = 2;      // Read Setup Cyclestparam.rStrobe = 3;     // Read Strobe Cyclestparam.rHold = 2;       // Read Hold Cyclestparam.turnArnd = 0;    // Write Setup Cyclestparam.wSetup = 0;      // Write Strobe Cyclestparam.wStrobe = 1;     // Write Hold Cyclestparam.wHold = 0;       // TurnAround CyclesEMIF_setAsyncTimingParams(EMIF1_BASE, EMIF_ASYNC_CS2_OFFSET, &tparam);    // Configure the access timing for CS2 space.while(1){for(a=0;a<5;a++)ReadDemo[a]=*(uint32_t *)(0x100000+a*2);}
}//配置EMIF引脚
void setupEMIF1PinmuxAsync8Bit(void)
{uint16_t i;GPIO_setPinConfig(GPIO_30_EMIF1_CLK);GPIO_setPinConfig(GPIO_32_EMIF1_OEN);GPIO_setPinConfig(GPIO_34_EMIF1_CS2N);//GPIO_setPinConfig(GPIO_31_EMIF1_WEN);//GPIO_setPinConfig(GPIO_33_EMIF1_RNW);//// Selecting address lines.//GPIO_setPinConfig(GPIO_35_EMIF1_A0);GPIO_setPinConfig(GPIO_36_EMIF1_A1);GPIO_setPinConfig(GPIO_37_EMIF1_A2);GPIO_setPinConfig(GPIO_38_EMIF1_A3);GPIO_setPinConfig(GPIO_39_EMIF1_A4);GPIO_setPinConfig(GPIO_49_EMIF1_A5);GPIO_setPinConfig(GPIO_50_EMIF1_A6);//// Selecting data lines.//GPIO_setPinConfig(GPIO_77_EMIF1_D7);GPIO_setPinConfig(GPIO_78_EMIF1_D6);GPIO_setPinConfig(GPIO_79_EMIF1_D5);GPIO_setPinConfig(GPIO_80_EMIF1_D4);GPIO_setPinConfig(GPIO_81_EMIF1_D3);GPIO_setPinConfig(GPIO_82_EMIF1_D2);GPIO_setPinConfig(GPIO_83_EMIF1_D1);GPIO_setPinConfig(GPIO_85_EMIF1_D0);//// Setting DQM and Bank Select lines.//GPIO_setPinConfig(GPIO_88_EMIF1_DQM0);GPIO_setPinConfig(GPIO_89_EMIF1_DQM1);GPIO_setPinConfig(GPIO_90_EMIF1_DQM2);GPIO_setPinConfig(GPIO_91_EMIF1_DQM3);GPIO_setPinConfig(GPIO_92_EMIF1_BA1);GPIO_setPinConfig(GPIO_93_EMIF1_BA0);//// Setup async mode and enable pull-ups for Data pins.//for(i=69; i<=85;i++){if(i != 84){GPIO_setPadConfig(i, GPIO_PIN_TYPE_PULLUP);GPIO_setQualificationMode(i, GPIO_QUAL_ASYNC);}}}

FPGA的代码,先初始化存储单元,给5个地址单元赋值分别为0,1,2,3,4,之后根据地址总线与扩展地址线的逻辑值将数据从数据总线输出:

module emif_memory (input wire clk,                 // EMIF 输入时钟信号 (100MHz/200MHz)input wire [6:0] emif_addr,     // EMIF 地址总线输入input wire [1:0] emif_BA,       // EMIF 扩展地址线输入input wire emif_oe_n,           // EMIF 输出使能信号 (低电平有效)input wire emif_cs_n,           // EMIF 片选信号 (低电平有效)output reg [7:0] emif_data      // EMIF 数据总线输出
);reg [31:0] memory [0:99];	// 定义存储器数组,32 位数据位宽,100 个存储位置reg [6:0] memory_index;		// 地址总线中的地址reg [31:0] selected_data;	// 根据地址总线选择的信号reg [7:0] trans_data;			// 根据扩展地址总线选择的发送的8位数据integer i;initial begin		// 初始化存储器,为每个地址写初值for (i = 0; i <= 99; i = i + 1) beginmemory[i] = i;endendalways @(posedge clk) beginif (!emif_cs_n && emif_oe_n) begin				//CS低,OE高时(setup与hold阶段)    memory_index <= emif_addr;selected_data <= memory[memory_index];	// 将地址总线的值作为存储器索引endelse if (!emif_cs_n && !emif_oe_n) begin		//CS低,OE低时(strobe阶段)case (emif_BA)		// 根据扩展地址线输出数据的相应部分2'b00: beginemif_data[7] <= selected_data[0];emif_data[6] <= selected_data[1];emif_data[5] <= selected_data[2];emif_data[4] <= selected_data[3];emif_data[3] <= selected_data[4];emif_data[2] <= selected_data[5];emif_data[1] <= selected_data[6];emif_data[0] <= selected_data[7];end2'b01: beginemif_data[7] <= selected_data[8];emif_data[6] <= selected_data[9];emif_data[5] <= selected_data[10];emif_data[4] <= selected_data[11];emif_data[3] <= selected_data[12];emif_data[2] <= selected_data[13];emif_data[1] <= selected_data[14];emif_data[0] <= selected_data[15];end				2'b10: beginemif_data[7] <= selected_data[16];emif_data[6] <= selected_data[17];emif_data[5] <= selected_data[18];emif_data[4] <= selected_data[19];emif_data[3] <= selected_data[20];emif_data[2] <= selected_data[21];emif_data[1] <= selected_data[22];emif_data[0] <= selected_data[23];end2'b11: beginemif_data[7] <= selected_data[24];emif_data[6] <= selected_data[25];emif_data[5] <= selected_data[26];emif_data[4] <= selected_data[27];emif_data[3] <= selected_data[28];emif_data[2] <= selected_data[29];emif_data[1] <= selected_data[30];emif_data[0] <= selected_data[31];enddefault: emif_data <= 8'b0; // 默认情况下输出零endcase	end else begin			//CS高,OE高时emif_data <= 8'b0; // 如果 EMIF 输出未使能,输出零selected_data <= 32'b0;endendendmodule

看看DSP调试的结果,与在FPGA中初始化的数值一样:

再看看ISE chipscope扫描FPGA引脚的结果:

这篇关于C2000基于EMIF总线扩展FPGA的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/280228

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

PHP7扩展开发之数组处理

前言 这次,我们将演示如何在PHP扩展中如何对数组进行处理。要实现的PHP代码如下: <?phpfunction array_concat ($arr, $prefix) {foreach($arr as $key => $val) {if (isset($prefix[$key]) && is_string($val) && is_string($prefix[$key])) {$arr[

PHP7扩展开发之字符串处理

前言 这次,我们来看看字符串在PHP扩展里面如何处理。 示例代码如下: <?phpfunction str_concat($prefix, $string) {$len = strlen($prefix);$substr = substr($string, 0, $len);if ($substr != $prefix) {return $prefix." ".$string;} else

PHP7扩展开发之类型处理

前言 这次,我们将演示如何在PHP扩展中如何对类型进行一些操作。如,判断变量类型。要实现的PHP代码如下: <?phpfunction get_size ($value) {if (is_string($value)) {return "string size is ". strlen($value);} else if (is_array($value)) {return "array si

PHP7扩展开发之依赖其他扩展

前言 有的时候,我们的扩展要依赖其他扩展。比如,我们PHP的mysqli扩展就依赖mysqlnd扩展。这中情况下,我们怎么使用其他扩展呢?这个就是本文讲述的内容。 我们新建立一个扩展,名字叫 demo_dep , 依赖之前的say扩展。 在demo_dep扩展中,我们实现demo_say方法。这个方法调用say扩展的say方法。 代码 基础代码 确保say扩展的头文件正确安装到了php

PHP7扩展开发之函数方式使用lib库

前言 首先说下什么是lib库。lib库就是一个提供特定功能的一个文件。可以把它看成是PHP的一个文件,这个文件提供一些函数方法。只是这个lib库是用c或者c++写的。 使用lib库的场景。一些软件已经提供了lib库,我们就没必要再重复实现一次。如,原先的mysql扩展,就是使用mysql官方的lib库进行的封装。 在本文,我们将建立一个简单的lib库,并在扩展中进行封装调用。 代码 基础

PHP7扩展开发之对象方式使用lib库

前言 上一篇文章,我们使用的是函数方式调用lib库。这篇文章我们将使用对象的方式调用lib库。调用代码如下: <?php $hello = new hello(); $result = $hello->get(); var_dump($result); ?> 我们将在扩展中实现hello类。hello类中将依赖lib库。 代码 基础代码 这个扩展,我们将在say扩展上增加相关代码。sa

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret