很久很久没有更新过博客了,今天来扒一扒FPGA上CPU软核的使用。

  主要完成的功能:使用的开发板是nexys 4 DDR,板上有16个switch以及16个LED,需要完成microblaze对led的控制以及将switch作为外部中断源。

  一、自定义GPIO IP核

  还是在Tools里面选择Create and Package IP,新建AXI4外设,本次需要新建两个GPIO外设,一个作为GPIO_IN,一个作为GPIO_OUT。

  GPIO_OUT就是简单的将CPU下发的数据输出到PIN脚,输出需要有高阻态并且三态可通过CPU配置;GPIO_IN需要有中断功能,并且其触发方式(上升沿/下降沿触发,高/低电平触发)需要可配置。

  1.GPIO_OUT:

  首先将CPU下发的数据输出,即

output [:] reg_out,
.
.
.
assign reg_out = slv_reg0;
.
.
.

  其次我们要实现输出三态,最开始的想法是在IP核内部使用原语OBUFT,其原语如下:

OBUFT U_OBUFT1(
.I(I),//输入
.O(O),//输出
.T(T) //三态控制
);

后来经某师兄提醒,使用原语可能不方便后期移植,通用性比较差,所以改成了如下形式:

generate
genvar i;
for(i=;i<C_S_AXI_DATA_WIDTH;i=i+)
assign reg_out[i] = slv_reg1[i] ? 'bz : slv_reg0[i];
endgenerate

生成的电路如图所示:

打包生成IP核,例化的用户接口如图所示:

  2.GPIO_IN:

  在使用IP生成向导的时候注意勾选使能中断,自动生成的模块结构如图所示:

gpio_in_..._AXI没什么好说的,直接将PIN脚信号传递给CPU就可以了。gpio_in_..._AXI_INTR主要实现了中断逻辑,部分代码如下:

module axi_user_logic_gpio_in_v1_0_S_AXI_INTR #
(
// Users to add parameters here // User parameters ends
// Do not modify the parameters beyond this line // Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = ,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = ,
// Number of Interrupts
parameter integer C_NUM_OF_INTR = ,
// Each bit corresponds to Sensitivity of interrupt : 0 - EDGE, 1 - LEVEL
parameter C_INTR_SENSITIVITY = 'hFFFFFFFF,
// Each bit corresponds to Sub-type of INTR: [0 - FALLING_EDGE, 1 - RISING_EDGE : if C_INTR_SENSITIVITY is EDGE(0)] and [ 0 - LEVEL_LOW, 1 - LEVEL_LOW : if C_INTR_SENSITIVITY is LEVEL(1) ]
parameter C_INTR_ACTIVE_STATE = 'hFFFFFFFF,
// Sensitivity of IRQ: 0 - EDGE, 1 - LEVEL
parameter integer C_IRQ_SENSITIVITY = ,
// Sub-type of IRQ: [0 - FALLING_EDGE, 1 - RISING_EDGE : if C_IRQ_SENSITIVITY is EDGE(0)] and [ 0 - LEVEL_LOW, 1 - LEVEL_LOW : if C_IRQ_SENSITIVITY is LEVEL(1) ]
parameter integer C_IRQ_ACTIVE_STATE =
)
(
// Users to add ports here
input [:] sig_in,
// User ports ends
.
.
.
//-- Number of Slave Registers 5
reg [ : ] reg_global_intr_en; //全局中断使能
reg [C_NUM_OF_INTR- :] reg_intr_en; //具体到某一个中断的使能
reg [C_NUM_OF_INTR- :] reg_intr_sts; //中断状态查询
reg [C_NUM_OF_INTR- :] reg_intr_ack; //清除中断
reg [C_NUM_OF_INTR- :] reg_intr_pending;//中断等待
reg [C_NUM_OF_INTR- :] intr; //中断源
reg [C_NUM_OF_INTR- :] det_intr; //检测中断信号
wire intr_reg_rden;
wire intr_reg_wren;
reg [C_S_AXI_DATA_WIDTH-:] reg_data_out;
// reg [3:0] intr_counter;
genvar i;
integer j;
reg [C_NUM_OF_INTR- :] intr_all_tmp; //Xilinx自动生成的IP核intr_all存在多驱动的问题,所以添加了该信号
wire intr_all = |intr_all_tmp;
reg [C_NUM_OF_INTR- :] intr_ack_all_tmp; //同样是因为intr_ack_all多驱动
wire intr_ack_all = |intr_ack_all_tmp;
wire s_irq;
reg intr_all_ff;
reg intr_ack_all_ff;
.
.
.
reg [:] sig_in_ff1,sig_in_ff2; //对输入信号打拍以消除亚稳态
always @(posedge S_AXI_ACLK) begin
sig_in_ff1 <= sig_in;
sig_in_ff2 <= sig_in_ff1;
end generate
for(i=; i<= C_NUM_OF_INTR-; i=i+)//对输入信号消抖,采样16个时钟周期,全一则认为触发了中断
begin : debounce
reg [:] intr_counter;
always @ ( posedge S_AXI_ACLK )
if ( S_AXI_ARESETN == 'b0 )
intr_counter[:] <= 'hF;
else if (sig_in_ff2[i] == 'b1) begin
if(intr_counter [:] != 'h0)
intr_counter[:] <= intr_counter[:] - ;
end
else
intr_counter[:] <= 'hF; always @ ( posedge S_AXI_ACLK )
if ( S_AXI_ARESETN == 'b0)
intr[i] <= 'b0;
else
begin
if (intr_counter[:] == )
intr[i] <= 'b1;
else
intr[i] <= 'b0;
end
end
endgenerate generate
for(i=; i<= C_NUM_OF_INTR-; i=i+)
begin : gen_intrall // detects interrupt in any intr input
always @ ( posedge S_AXI_ACLK)
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all_ff == 1'b1)
begin
intr_all_tmp[i] <= 'b0;
end
else
begin
// for(j=0; j<= C_NUM_OF_INTR-1; j=j+1)
if (reg_intr_pending[i])
begin
intr_all_tmp[i] <= 'b1;
end
end
end // detects intr ack in any reg_intr_ack reg bits
always @ ( posedge S_AXI_ACLK)
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all_ff==1'b1)
begin
intr_ack_all_tmp[i] <= 'b0;
end
else
begin
// for(j=0; j<= C_NUM_OF_INTR-1; j=j+1)
if (reg_intr_ack[i])
begin
intr_ack_all_tmp[i] <= 'b1;
end
end
end end
endgenerate
.
.
.
// detect interrupts for user selected number of interrupts generate
for(i=; i<= C_NUM_OF_INTR-; i=i+)
begin : gen_intr_detection if (C_INTR_SENSITIVITY[i] == 'b1)
begin: gen_intr_level_detect if (C_INTR_ACTIVE_STATE[i] == 'b1)
begin: gen_intr_active_high_detect always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 | reg_intr_ack[i] == 1'b1)
begin
det_intr[i] <= 'b0;
end
else
begin
if (intr[i] == 'b1)
begin
det_intr[i] <= 'b1;
end
end
end end
else
begin: gen_intr_active_low_detect always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 | reg_intr_ack[i] == 1'b1)
begin
det_intr[i] <= 'b0;
end
else
begin
if (intr[i] == 'b0)
begin
det_intr[i] <= 'b1;
end
end
end end end
else
begin:gen_intr_edge_detect wire [C_NUM_OF_INTR- :] intr_edge;
reg [C_NUM_OF_INTR- :] intr_ff;
reg [C_NUM_OF_INTR- :] intr_ff2; if (C_INTR_ACTIVE_STATE[i] == )
begin: gen_intr_rising_edge_detect always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 || reg_intr_ack[i] == 1'b1)
begin
intr_ff[i] <= 'b0;
intr_ff2[i] <= 'b0;
end
else
begin
intr_ff[i] <= intr[i];
intr_ff2[i] <= intr_ff[i];
end
end assign intr_edge[i] = intr_ff[i] && (!intr_ff2); always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 | reg_intr_ack[i] == 1'b1)
begin
det_intr[i] <= 'b0;
end
else if (intr_edge[i] == 'b1)
begin
det_intr[i] <= 'b1;
end
end end
else
begin: gen_intr_falling_edge_detect always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 | reg_intr_ack[i] == 1'b1)
begin
intr_ff[i] <= 'b1;
intr_ff2[i] <= 'b1;
end
else
begin
intr_ff[i] <= intr[i];
intr_ff2[i] <= intr_ff[i];
end
end assign intr_edge[i] = intr_ff2[i] && (!intr_ff); always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 | reg_intr_ack[i] == 1'b1)
begin
det_intr[i] <= 'b0;
end
else if (intr_edge[i] == 'b1)
begin
det_intr[i] <= 'b1;
end
end end end // IRQ generation logic reg s_irq_lvl; if (C_IRQ_SENSITIVITY == )
begin: gen_irq_level if (C_IRQ_ACTIVE_STATE == )
begin: irq_level_high always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all == 1'b1)
begin
s_irq_lvl <= 'b0;
end
else if (intr_all == 'b1 && reg_global_intr_en[0] ==1'b1)
begin
s_irq_lvl <= 'b1;
end
end
assign s_irq = s_irq_lvl;
end
else
begin:irq_level_low always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all == 1'b1)
begin
s_irq_lvl <= 'b1;
end
else if (intr_all == 'b1 && reg_global_intr_en[0] ==1'b1)
begin
s_irq_lvl <= 'b0;
end
end
assign s_irq = s_irq_lvl;
end end else begin: gen_irq_edge reg s_irq_lvl_ff; if (C_IRQ_ACTIVE_STATE == )
begin: irq_rising_edge always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all == 1'b1)
begin
s_irq_lvl <= 'b0;
s_irq_lvl_ff <= 'b0;
end
else if (intr_all == 'b1 && reg_global_intr_en[0] ==1'b1)
begin
s_irq_lvl <= 'b1;
s_irq_lvl_ff <= s_irq_lvl;
end
end assign s_irq = s_irq_lvl && (!s_irq_lvl_ff); end
else
begin:irq_falling_edge always @ ( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 'b0 || intr_ack_all == 1'b1 )
begin
s_irq_lvl <= 'b1;
s_irq_lvl_ff <= 'b1;
end
else if (intr_all == 'b1 && reg_global_intr_en[0] ==1'b1)
begin
s_irq_lvl <= 'b0;
s_irq_lvl_ff <= s_irq_lvl;
end
end assign s_irq = !(s_irq_lvl_ff && (!s_irq_lvl)); end
end assign irq = s_irq; end
endgenerate
.
.
.

打包生成IP核,其用户接口如下:

  至此,IP核的生成就做完了。

  二、创建BD块

  首先将生成的IP核添加到IP Catalog里:

  添加IP模块

对模块进行例化配置:

本次开启了16个GPIO中断,中断的检测方式是高电平,触发方式也是高电平

开启了32个GPIO输出,均初始化为高阻态

先run block automation,勾选CPU中断控制器

对时钟模块进行配置,注意这里默认的是差分时钟,根据需要,我选择了单端时钟

在run connection automation的时候,注意复位信号。两个复位信号一个默认是高有效,一个低有效,如果你把这两个连到一个外部复位,需要使其复位电平保持一致。

最后,CPU的测试少不了串口,当然,如果你只做仿真的话,就不用添加串口了,如果要上板,最好是把串口也放进来,下面是总图:

分配一下地址,由于我在SDK里面建立了hello world工程,对CPU存储的要求略高,所以将两个mem都改成了256k。如果你建立的是空的工程并且不开启串口的话,估计使用默认的8KB存储空间也可以。

保存BD块,validate一下,没有错误的话就generate output product并且创建wrapper,然后可以直接导出到SDK,并且打开SDK进行CPU开发,然后将生成的ELF文件关联到vivado里,到此,就可以使用CPU核FPGA联合仿真了。

在BD块上右键关联elf文件,成功后会在design source里看到ELF文件。仿真如下:

如果你要上板的话,需要在vivado中生成bit流,并将其导入到SDK里,使用SDK进行程序的烧录。

  SDK的主要代码如下:

 #include <stdio.h>
#include "platform.h"
#include "xil_printf.h" #include "xintc.h"
//#include "intc_header.h"
#include "AXI_USER_LOGIC_GPIO_OUT.H"
#include "AXI_USER_LOGIC_GPIO_IN.H" #include "xintc_test.h" int user_intr_flag = ; int main()
{
init_platform();
IntcInit(INTC_DEVICE_ID);//中断初始化
print("Hello World\n\r"); AXI_USER_LOGIC_GPIO_OUT_mWriteReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, , 0xffff0000);//配置gpio_out低16位为输出 int i;
int intr_cnt=;
unsigned int intr_status; for (i=; i>-;i++)
{
AXI_USER_LOGIC_GPIO_OUT_mWriteReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, AXI_USER_LOGIC_GPIO_OUT_S00_AXI_SLV_REG0_OFFSET, i);//循环向GPIO_OUT输出数据
printf("reg_out:%x\n\r",AXI_USER_LOGIC_GPIO_OUT_mReadReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, AXI_USER_LOGIC_GPIO_OUT_S00_AXI_SLV_REG0_OFFSET));//反向读出GPIO PIN的状态
intr_status = AXI_USER_LOGIC_GPIO_IN_mReadReg(INTR_BaseAddr,REG_INTR_STS);//查询GPIO IN中断的状态
if(intr_status){
printf("intr:%x,cnt:%d,intr_flag:%d\n\r",intr_status,++intr_cnt,user_intr_flag);
if(user_intr_flag){
AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr,REG_INTR_ACK,intr_status);//清除中断
user_intr_flag = ;
} }
int delay_cnt = ;//
while(delay_cnt--);
} cleanup_platform();
return ;
} void IntcInit(u16 DeviceId)
{ AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr, REG_Global_INTR_EN, );
AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr, REG_INTR_EN, 0xffffffff); XIntc_Initialize(&InterruptController, DeviceId); XIntc_Connect(&InterruptController, INTC_DEVICE_ID,
(XInterruptHandler)DeviceDriverHandler,
(void *));
XIntc_Enable(&InterruptController, INTC_DEVICE_ID); microblaze_register_handler(XIntc_DeviceInterruptHandler, INTC_DEVICE_ID);
microblaze_enable_interrupts();
XIntc_Start(&InterruptController, XIN_REAL_MODE); // XGpio_InterruptEnable(&InterruptController, 1);
// XGpio_InterruptGlobalEnable(&InterruptController); print("intr config done!\n\r"); } void DeviceDriverHandler(void *CallbackRef)
{
print("Entering interrup!\n\r");
user_intr_flag = ;
}

上板后串口接收到的数据:

  

在xilinxFPGA上使用microblaze及自写GPIO中断的更多相关文章

  1. webuploader 跨域上传demo(还没有写记录一下)

    webuploader 跨域上传demo(还没有写记录一下)

  2. 开源一个Mac漂亮的小工具 PPRows for Mac, 在Mac上优雅的计算你写了多少行代码

    开源一个Mac漂亮的小工具 PPRows for Mac, 在Mac上优雅的计算你写了多少行代码. 开源地址: https://github.com/jkpang/PPRows

  3. linux通用GPIO驱动,写GPIO文件不立即生效问题解决

    Linux开发平台实现了通用GPIO的驱动,用户通过,SHell或者系统调用能控制GPIO的输出和读取其输入值.其属性文件均在/sys/class/gpio/目录下,该目录下有export和unexp ...

  4. tinymce4.x 上传本地图片(自己写个插件)

    tinymce是一款挺不错的html文本编辑器.但是添加图片是直接添加链接,不能直接选择本地图片. 下面我写了一个插件用于直接上传本地图片. 在tinymce的plugins目录下新建一个upload ...

  5. servlet3.0的文件上传代码配置怎么写

    之前学习过xml配置servlet3.0的文件上传,但是变成code方式一直不知道怎么弄,相比较起来apache的文件上传配置和xml倒是没什么太大区别. 直接上代码:无需依赖,只要一个方法就好了cu ...

  6. 3. 懂了这些,方敢在简历上说会用Jackson写JSON

    你必须非常努力,才能看起来毫不费力.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众 ...

  7. TQ2440在Ubuntu16.04上如何搭建DNW烧写环境

    八月份国赛比完,原计划开始的嵌入式Linux学习一直拖到了现在:由于之前所有的开发全在Windows下进行的,对各种底层完全不清楚,刚好这段时间开始学习Linux,我就在想能不能把开发环境给迁移到Li ...

  8. 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用

    Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...

  9. 在服务器上log4net没写日志

    登录到服务器上,发现log4net没写日志 在相应文件夹加上User用户的写权限后恢复正常了.

随机推荐

  1. PetaPoco 批量插入数据

    网上找的代码,还没经过验证 /// <summary> /// Bulk inserts multiple rows to SQL /// </summary> /// < ...

  2. android studio入门

    目前的工作是蓝牙相关的,所以最近心血来潮想折腾下Android开发,方便调试自己的固件, 装好Android Studio跟SDK之后,感谢nordic的公开代码,研究了一会,感觉并没有多费劲 相关的 ...

  3. win版本对比

    Win+R 输入:slmgr.vbs -dlv 显示:最为详尽的激活信息,包括:激活ID.安装ID.激活截止日期slmgr.vbs -dli 显示:操作系统版本.部分产品密钥.许可证状态slmgr.v ...

  4. oracle 职业学习指引

    风哥 它是阿里巴巴造出的概念.其本意是,在阿里巴巴的IT架构中,去掉IBM的小型机.Oracle数据库.EMC存储设备,代之以自己在开源软件基础上开发的系统. 思科.IBM.谷歌.高通.英特尔.苹果. ...

  5. unity, reduce android size

    参考: https://www.youtube.com/watch?v=TYSmf_zgtZo http://stackoverflow.com/questions/41087220/how-to-u ...

  6. Java BigDecimal和double

    BigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数 ...

  7. ASP.NET webform基于Jquery,AJAX的三级联动

    主要html代码 <select id="province"> <option value="0">--请选择省份--</opti ...

  8. Install NukeX v7.0v6 in CentOS 7

    - download THE_FOUNDRY_NUKEX_V7.0V6_LNX64-XFORCE - unzip and untar to /home/user0/tools/foundry/nuke ...

  9. delphi locate多字段查询

    简单格式: IF MSQ_NewBillQuantity.Locate('FStockID;FMarchID', VarArrayOf([FStockID, FMarchID]), []) = Fal ...

  10. git基本用法说明(原创+验证)

      关于文件状态   一般仓库中的文件可能存在于这4种状态: 1)Untracked files                     → 文件未被跟踪(A)  2)Untracked but no ...