搜索
bottom↓
回复: 8

新手问一个NIOS II的端口操作问题

[复制链接]

出0入0汤圆

发表于 2010-10-14 08:31:46 | 显示全部楼层 |阅读模式
新手开始搞NIOS II,很多磕磕碰碰
我使用的硬件开发板是 DE2-70
当我按照 "NIOSII那些事儿"中所说的那样子
定义一个端口
#ifndef SOPC_H_
#define SOPC_H_

#include "system.h"

#define _LED

typedef struct
{
    unsigned long int DATA;
    unsigned long int DIRECTION;
    unsigned long int INTERRUPT_MASK;
    unsigned long int EDGE_CAPTURE;
   
}PIO_STR;

#ifdef _LED
#define LED ((PIO_STR*)GREENLEDS_BASE)
#endif

//       //IOWR_ALTERA_AVALON_PIO_DATA(GREENLEDS_BASE,1<<i); //语句A
//       //LED->DATA = (1<<i);                                //语句B
         usleep(500000);

发生了以下情况
语句A,无论是使用run as hardware ,还是debug as hardware都工作得很好
但是语句B, 则会在run as hardware中没有响应的样子,有其他语句证明 肯定是运行到语句B的
但是如果使用debug as hardware单步调试,就可以有响应

百思不得其解, 请大家指点一下

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2010-10-14 09:06:10 | 显示全部楼层
[笔记].浅析在Nios II中的两种寄存器映射方法的异同.[Nios II]
http://www.cnblogs.com/yuphone/archive/2010/04/22/1717779.html

此处以我所写的MAX7219为范例,从HDL接口描述到C语言软件编程,分析两种表面不一样、但实质是一样的寄存器映射方法,找出其中联系与区别。

方法1 使用Altera提供的API

1. 使用HDL描述Avalon-MM接口

代码1 Amy_S_max7219_avalon_interface.v
01        /*-----版权声明-----
02         *     艾米电子工作室——让开发变得更简单
03         *     网站:http://www.amy-studio.com
04         *     淘宝:http://amy-studio.taobao.com
05         *     QQ(邮箱):amy-studio@qq.com
06         *-----文件信息-----
07         *     文件名称:Amy_S_max7219_avalon_interface.v
08         *     最后修改日期:3.20, 2010
09         *     描述:Max7219的Avalon接口描述文件
10         *------------------
11         *     创建者:张亚峰
12         *     创建日期:3.20, 2009
13         *     版本:1.0
14         *     描述:原始版本
15         *------------------
16         *     修改者:
17         *     修改日期:
18         *     版本:
19         *     描述:
20         *-------------------
21         */
22         
23        module Amy_S_max7219_avalon_interface(
24          // Clcok Input
25          input         csi_clk,
26          input         csi_reset_n,
27          // Avalon-MM Slave
28          input         avs_chipselect,
29          input [1:0]   avs_address,
30          input         avs_write,
31          input [31:0]  avs_writedata,
32          // Conduit End
33          output reg    coe_din,
34          output reg    coe_cs,
35          output reg    coe_clk
36        );
37         
38        // write
39        always@(posedge csi_clk, negedge csi_reset_n)
40        begin
41          if (!csi_reset_n)
42          begin
43            coe_din <= 1'b0;
44            coe_cs  <= 1'b0;
45            coe_clk <= 1'b0;   
46          end
47          else if (avs_chipselect & avs_write)
48          begin
49            case (avs_address)
50              0: coe_din <= avs_writedata[0];
51              1: coe_cs  <= avs_writedata[0];
52              2: coe_clk <= avs_writedata[0];
53            endcase
54          end
55        end
56         
57        endmodule
在这里,使用了3个寄存器,并通过avs_address来寻址。从50~52行,可以看出,这三个寄存器的偏移地址(Offset)分别是0、1和2。

2. 使用C语言编写寄存器映射文件

代码2 Amy_S_max7219.h 片段
01        //++++++++++++++++++++++++++++++++++++++
02        // 寄存器映射 开始
03        // 根据HDL编写
04        //++++++++++++++++++++++++++++++++++++++
05        #include <io.h></io.h>
06         
07        #define IOWR_MAX7219_DIN(base, data)   IOWR(base, 0, data)
08        #define IOWR_MAX7219_CS(base, data)    IOWR(base, 1, data)
09        #define IOWR_MAX7219_CLK(base, data)   IOWR(base, 2, data)
10        //--------------------------------------
11        // 寄存器映射 结束
12        //--------------------------------------

由于是使用ALtera的API——IOWR(),因此第5行,就得加上#include <io.h>。IOWR(base, offset, data)的3个输入参数,分别是IP的基地址,所使用寄存器的偏移地址,欲给所使用寄存器赋的值。寄存器的存储映射所使用的偏移地址,是有HDL中 avs_address决定的。(avs avalon slave 阿窝龙从设备)

代码3 Amy_S_max7219.h 片段
代码描述:使用上面的已经映射好的函数
01        //++++++++++++++++++++++++++++++++++++++
02        // 基地址 开始
03        // 根据SOPC Builder设置编写
04        //++++++++++++++++++++++++++++++++++++++
05        #include "system.h"
06         
07        #define max7219_addr MAX7219_BASE
08        //--------------------------------------
09        // 基地址 结束
10        //--------------------------------------
11         
12         
13        //++++++++++++++++++++++++++++++++++++++
14        // 寄存器映射 开始
15        // 根据HDL编写
16        //++++++++++++++++++++++++++++++++++++++
17        #include <io.h></io.h>
18         
19        #define IOWR_MAX7219_DIN(base, data)   IOWR(base, 0, data)
20        #define IOWR_MAX7219_CS(base, data)    IOWR(base, 1, data)
21        #define IOWR_MAX7219_CLK(base, data)   IOWR(base, 2, data)
22        //--------------------------------------
23        // 寄存器映射 结束
24        //--------------------------------------
25         
26         
27        //++++++++++++++++++++++++++++++++++++++
28        // 管脚操作 开始
29        //++++++++++++++++++++++++++++++++++++++
30        #define SET_DIN IOWR_MAX7219_DIN(max7219_addr, 1)
31        #define CLR_DIN IOWR_MAX7219_DIN(max7219_addr, 0)
32        #define SET_CS  IOWR_MAX7219_CS(max7219_addr, 1)
33        #define CLR_CS  IOWR_MAX7219_CS(max7219_addr, 0)
34        #define SET_CLK IOWR_MAX7219_CLK(max7219_addr, 1)
35        #define CLR_CLK IOWR_MAX7219_CLK(max7219_addr, 0)
36        //--------------------------------------
37        // 管脚操作 结束
38        //--------------------------------------


代码4 Amy_S_max7219.c代码片段
代码描述:使用Altera API的具体操作
01        #include "Amy_S_max7219.h"
02         
03        /*
04         * 发送一个字节的子程序:
05         * 上升沿发送数据,
06         * MSB first
07         */
08        void Max7219_WriteByte(alt_u8 byte)
09        {
10          alt_u8 i;
11          for (i=0; i<8; i++)
12          {
13            CLR_CLK;
14            if(byte & 0x80)
15              SET_DIN;
16            else
17              CLR_DIN;
18            byte <<= 1;
19            SET_CLK;
20          }
21        }

至此,使用Altera的API来描述寄存器存储映射的方法,告一段落。


方法2 使用位域或结构体

其实这种方法,Altera的API的源代码有时也会用到。但是有一个地方需要注意,后面会提到。
1. 使用HDL描述Avalon-MM接口
如上。

2. 使用C语言编写寄存器映射文件
代码4 Amy_S_max7219.h 片段
01        //++++++++++++++++++++++++++++++++++++++
02        // 寄存器映射 开始
03        // 根据HDL编写
04        //++++++++++++++++++++++++++++++++++++++
05        #include "system.h"
06        #include "alt_types.h"
07         
08        typedef struct
09        {
10          alt_u32 DIN : 32;
11          alt_u32 CS  : 32;
12          alt_u32 CLK : 32;
13        }MAX7219_T;
14         
15        #define m7219 ((MAX7219_T *)(MAX7219_BASE))
16        //--------------------------------------
17        // 寄存器映射 结束
18        //--------------------------------------

因为Nios II是32位的处理器,所以之前定义了3个寄存器,都是32位的。此处为了表达这种关系,我们使用了位域。将这个位域(或结构体)重定义为一个类型,然后定义一个该类型的指针变量,起始地址是所需的基地址。这样做,就可以很好地为从基地址开始的连续的3x32位数据寻址(此处为3个寄存器,故数据总长3x32)。

代码5 Amy_S_max7219.c代码片段
代码描述:使用结构体指针寻址示例
01        /*
02         * 发送一个字节的子程序:
03         * 上升沿发送数据,
04         * MSB first
05         */
06        void Max7219_WriteByte(alt_u8 byte)
07        {
08          alt_u8 i;
09          for (i=0; i<8; i++)
10          {
11            m7219->CLK = 0;
12            if(byte & 0x80)
13              m7219->DIN = 1;
14            else
15              m7219->DIN = 0;
16            byte <<= 1;
17            m7219->CLK = 1;
18          }
19        }

哈哈,是不是可以直接赋值了,更加像单片机了吧。其实Nios II就是单片机,32位的单片机。

做到这里,有些实验者在开发板上演练时,确实成功了;然而有些没有成功?这是为什么呢?我们先看参考资料1。

代码6 两种寄存器存储映射所对应的汇编
01        IOWR=32DIRECT(GPIO_LED_BASE, 0, 1);
02        0x04000234 <main></main>: movhi r3,2048
03        0x04000238 <main></main>: addi r3,r3,6144
04        0x0400023c <main></main>: movi r2,1
05        0x04000240 <main></main>: stwio r2,0(r3)
06         
07        LED = 1;
08        0x04000224 <main></main>: movhi r3,2048
09        0x04000228 <main></main>: addi r3,r3,6144
10        0x0400022c <main></main>: movi r2,1
11        0x04000230 <main></main>: stw r2,0(r3)

看到没有,两种寄存器存储映射所对应的汇编不一样。看关键字,一个是stwio,一个是stw。接下来打开手册,Table 3-36。

表1 宽数据传输指令


手册上清楚地写到,I/O外设的数据传输应该使用ldwio和stwio;这两条指令在传输时,是没有cache和buffer的。那怎样让结构体指针的寄存器映射方式也能使用ldwio和stwio呢。接着看手册,在98页,Cache Memory小节,写到
。那还有其他方法来实现cache bypass吗?第38页写到:


图1 Cache Bypass Method


图2 The Bit-31 Cache Bypass Method

好的,看代码。

代码7 system.h片段
1        #define ALT_MODULE_CLASS_max7219 Amy_S_max7219
2        #define MAX7219_BASE 0x1002020
3        #define MAX7219_IRQ -1
4        #define MAX7219_IRQ_INTERRUPT_CONTROLLER_ID -1
5        #define MAX7219_NAME "/dev/max7219"
6        #define MAX7219_SPAN 16
7        #define MAX7219_TYPE "Amy_S_max7219"

MAX7219_BASE=0x1002020,第31位是0;因此我们用结构体指针来寄存器存储映射时,传输的数据因数据缓存而出错。那我们就把这个Cache给Bypass(旁路)了。怎么办?把地址总线的第31位置一。

代码8 修改后的Amy_S_max7219.h片段
01        //++++++++++++++++++++++++++++++++++++++
02        // 寄存器映射 开始
03        // 根据HDL编写
04        //++++++++++++++++++++++++++++++++++++++
05        #include "system.h"
06        #include "alt_types.h"
07         
08        typedef struct
09        {
10          alt_u32 DIN : 32;
11          alt_u32 CS  : 32;
12          alt_u32 CLK : 32;
13        }MAX7219_T;
14         
15        #define m7219 ((MAX7219_T *)(MAX7219_BASE | 1<<31))
16        //--------------------------------------
17        // 寄存器映射 结束
18        //--------------------------------------

在这里,我们直接通过或(1<<31)的方式,把第31位给置一了;这样从该基地址传输的数据就把数据缓存给旁路了;因为是I/O外设传输嘛。

好了,至此大功告成。我们可以自由切换喜欢的寄存器映射方式。


3. 一点其他内容

有时候我们所使用的寄存器并不一定都是32位的,这时要使用嵌套结构体来凑足32位,以防止寄存器寻址错误。
代码9 嵌套结构体示例
view source
print?
01        typedef struct{
02          struct{
03            alt_u8  DIN : 8;
04            alt_u32 NC  : 24;
05          }offset_0;
06          struct{
07            alt_u8  CS  : 1;
08            alt_u32 NC  : 31;
09          }offset_1;
10          struct{
11            alt_u8  CLK : 1;
12            alt_u32 NC  : 31;
13          }offset_2;
14        }MAX7219_T;

关于嵌套结构体,此处不解析,请读者自行分析。


对比

两种方法都不错,大家爱用什么就用什么。


参考

1. http://www.edaboard.com/ftopic354136.html
2. Altera.Nios II Processor Reference Handbook
http://www.altera.com/literature/hb/nios2/n2cpu_nii5v1.pdf

出0入0汤圆

 楼主| 发表于 2010-10-14 09:12:50 | 显示全部楼层
先谢谢

期待细说部分

根据提供的关键词,好像可以找到解决方法了
Thanks :)

出0入0汤圆

 楼主| 发表于 2010-10-14 09:54:36 | 显示全部楼层
嗯,我是根据 提供的关键词 找到
http://www.cnblogs.com/kingst/archive/2010/05/15/1735886.html

建议你的博文也加上这个 关键词

出0入0汤圆

发表于 2010-10-14 10:04:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-14 11:18:16 | 显示全部楼层
跟NiosII类型也有关系的
把在SOPC里面配置NiosII的时候把数据缓存去掉就可以。。

出0入0汤圆

发表于 2010-11-9 21:28:01 | 显示全部楼层
1<<i写成0x0001<<i

出0入0汤圆

发表于 2011-7-1 14:51:01 | 显示全部楼层
马瑞卡

出0入0汤圆

发表于 2011-7-2 19:01:20 | 显示全部楼层
好像有人提到我了哦,呵呵
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 13:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表