很久很久没有更新过博客了,今天来扒一扒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. 学C日志

    学C历程 这里记录的都是笔记 忘记了好回头看看~ 提示 :如果在双击自己编译的程序运行时一下就消失不见了 可以在程序代码末尾加上 system("pause"); 程序就会等待到输 ...

  2. (转) The major advancements in Deep Learning in 2016

    The major advancements in Deep Learning in 2016 Pablo Tue, Dec 6, 2016 in MACHINE LEARNING DEEP LEAR ...

  3. .gitignore的多级目录配置

    在子目录下仍然可以建立.gitignore文件以用于忽略子目录的文件

  4. 支付宝Andfix 原理解析

    支付宝Andfix 原理解析 使用参考地址: http://blog.csdn.net/qxs965266509/article/details/49802429 原理参考地址: http://blo ...

  5. c#选择文件文件夹

    C#选择文件 OpenFileDialog fileDialog = new OpenFileDialog(); fileDialog.InitialDirectory = "C://&qu ...

  6. golang调用c++的dll库文件

    最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...

  7. Eclipse中web项目部署至Tomcat步骤

    Eclipse的web工程至Tomcat默认的部署目录是在工程空间下,本文旨在将部署目录改为Tomcat安装目录,并解决依赖包输出问题. 1.在Eclipse中添加Tomcat服务器. 2.将web工 ...

  8. log4j.properties 详解与配置步骤

    一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR 为严重错误 主要是程序的错误WARN 为一般警告,比如session丢失IN ...

  9. (转载)iOS开发历程书籍推荐

    http://www.zhihu.com/question/19649679 我来说说我学习的过程:(无耻的使用一个回答 回答了很多问题.^^) 第一步,精读 Stephen G. Kochan 的& ...

  10. Entity Framework 连接低版本数据库

    使用EF6连接SQL2012生成的Edmx,分页时生成的查询语句使用了SQL 2012引入的新特性  OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY,结果在生产环境使用的数 ...