很久很久没有更新过博客了,今天来扒一扒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. 移动web

    1.分辨率 1900*1200这就是一个分辨率, 因为pt的存在,因此还会有一个逻辑分辨率的概念 2.ppi = pixels per inch这里inch不是平方英尺,是英尺 3.dp = devi ...

  2. connections

    recv(), recvfrom() http://lxr.free-electrons.com/source/net/socket.c http://grepcode.com/file/reposi ...

  3. iOS 标识

    通常情况下,iOS系统用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码.证书等等,就需要使用更为安全的keychain了. keychain里保存的信息不会因App被删除而丢 ...

  4. GitLab常见使用方法

    1.GitLab配置 git config --global user.name "xxx" -- 配置用户名,上传本地 repository 到服务器上的时候,在 Github ...

  5. <<软件测试实战>>读书笔记

    软件测试基础 软件的复杂度已经超越了人的理解能力 1. 虽然高抽象的层次语言,程序框架,程序库等提高了人的生产力,但是还是需要开发者深入理解细节,可以减少开发时间,但是无法减少开发者学习整个技术栈的时 ...

  6. Mysql 客户端查询结果如何保存到本地而不是服务端?

    应用场景:知道某台DB服务器的IP和账户,登录上去查询了10W条记录,需要把这些记录拉到本地做分析 方法1,远程连接到DB服务器执行OUTFILE命令,文件存储在DB机器上,只有mysql账户的情况下 ...

  7. FLASH结构

    mergedir/code.tmp.bin 压缩未加密文件的FLASH文件 mergedir/sfs/nvm.bin   shareData数据文件,未加密.未压缩 数据克隆:DATA/ALL APP ...

  8. oracle之集合操作函数---minus、union、intersect

    集合操作符专门用于合并多条select语句的结果,包括:UNION,UNION ALL,INTERSECT,MINUS.当使用集合操作函数时,需保证数据集的字段数据类型和数目一致. 使用集合操作符需要 ...

  9. PBR实现2.0

    之前的错误和欠缺 1. 过于简单的划分diffuse和specular,非常光滑的非金属材料也是很能反光的2. 费奈尔效应的处理,F0的选取也比较随意3. 没有GI,更不支持AO 正确划分diffus ...

  10. 清除系统日志及数据库(sql server)日志最佳实践

    在一个项目中遇到的问题:系统日志过大,后来用delete语句删除了(相当的慢),结果数据库日志又变成很大了(差不多10G),所以又得把数据库日志删除. 方法: --备份系统中的部份日志--SELECT ...