看了一下网上基于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 table
SysCtl_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 register
setupEMIF1PinmuxAsync8Bit(); // 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 Cycles
tparam.rStrobe = 3; // Read Strobe Cycles
tparam.rHold = 2; // Read Hold Cycles
tparam.turnArnd = 0; // Write Setup Cycles
tparam.wSetup = 0; // Write Strobe Cycles
tparam.wStrobe = 1; // Write Hold Cycles
tparam.wHold = 0; // TurnAround Cycles
EMIF_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) begin
memory[i] = i;
end
end
always @(posedge clk) begin
if (!emif_cs_n && emif_oe_n) begin //CS低,OE高时(setup与hold阶段)
memory_index <= emif_addr;
selected_data <= memory[memory_index]; // 将地址总线的值作为存储器索引
end
else if (!emif_cs_n && !emif_oe_n) begin //CS低,OE低时(strobe阶段)
case (emif_BA) // 根据扩展地址线输出数据的相应部分
2'b00: begin
emif_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];
end
2'b01: begin
emif_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: begin
emif_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];
end
2'b11: begin
emif_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];
end
default: emif_data <= 8'b0; // 默认情况下输出零
endcase
end
else begin //CS高,OE高时
emif_data <= 8'b0; // 如果 EMIF 输出未使能,输出零
selected_data <= 32'b0;
end
end
endmodule
看看DSP调试的结果,与在FPGA中初始化的数值一样:
再看看ISE chipscope扫描FPGA引脚的结果:
没有回复内容