• 【转载】[笔记].浅析在Nios II中的两种寄存器映射方法的异同.[C][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

    <

    ;p>在这里,使用了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>
    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 //--------------------------------------

    注意:结尾那个</io.h>是发博客发出来的,不属于代码。

    由于是使用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>
    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 //--------------------------------------

    注意:结尾那个</io.h>是发博客发出来的,不属于代码

    代码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 +36>: movhi r3,2048 
    03 0x04000238 <MAIN +40>: addi r3,r3,6144 
    04 0x0400023c <MAIN +44>: movi r2,1 
    05 0x04000240 <MAIN +48>: stwio r2,0(r3)
    06   
    07 LED = 1; 
    08 0x04000224 <MAIN +20>: movhi r3,2048 
    09 0x04000228 <MAIN +24>: addi r3,r3,6144 
    10 0x0400022c <MAIN +28>: movi r2,1 
    11 0x04000230 <MAIN +32>: stw r2,0(r3)

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

    表1 宽数据传输指令

    表1 宽数据传输指令

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

    图1 cache bypass的方法

    图1 Cache Bypass Method

    图2 The Bit-31 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 嵌套结构体示例
    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

  • 相关阅读:
    ocilib(简介2)
    C++ Socket编程步骤 (转载)
    服务端和客户端的疑问
    var和public的区别
    vs2010如何设置能实现输入关键字的时候自动提示呢?
    ocilib(简介)
    iostream.h , iostream
    error C2110: cannot add two pointers
    Array 越界不报错的问题
    char* + int or char or ...
  • 原文地址:https://www.cnblogs.com/nios_ii/p/1735736.html
Copyright © 2020-2023  润新知