zoukankan      html  css  js  c++  java
  • [笔记].浅析在Nios II中的两种寄存器映射方法的异同

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

    方法1 使用Altera提供的API

    1. 使用HDL描述Avalon-MM接口
    代码1 Amy_S_max7219_avalon_interface.v
    /*-----版权声明-----
     *     艾米电子工作室——让开发变得更简单 
     *     网站:http://www.amy-studio.com
     *     淘宝:http://amy-studio.taobao.com
     *     QQ(邮箱):amy-studio@qq.com
     *-----文件信息-----
     *     文件名称:Amy_S_max7219_avalon_interface.v
     *     最后修改日期:3.20, 2010
     *     描述:Max7219的Avalon接口描述文件
     *------------------
     *     创建者:张亚峰
     *     创建日期:3.20, 2009
     *     版本:1.0
     *     描述:原始版本
     *------------------
     *     修改者:
     *     修改日期:
     *     版本:
     *     描述:
     *-------------------
     */
    
    module Amy_S_max7219_avalon_interface(
      // Clcok Input
      input         csi_clk,
      input         csi_reset_n,
      // Avalon-MM Slave
      input         avs_chipselect,
      input [1:0]   avs_address,
      input         avs_write,
      input [31:0]  avs_writedata,
      // Conduit End 
      output reg    coe_din,
      output reg    coe_cs, 
      output reg    coe_clk
    );
    
    // write
    always@(posedge csi_clk, negedge csi_reset_n)
    begin
      if (!csi_reset_n)
      begin 
        coe_din <= 1'b0;
        coe_cs  <= 1'b0;
        coe_clk <= 1'b0;    
      end
      else if (avs_chipselect & avs_write)
      begin
        case (avs_address)
          0: coe_din <= avs_writedata[0];
          1: coe_cs  <= avs_writedata[0];
          2: coe_clk <= avs_writedata[0];
        endcase
      end
    end
    
    endmodule

    <

    ;p>在这里,使用了3个寄存器,并通过avs_address来寻址。从50~52行,可以看出,这三个寄存器的偏移地址(Offset)分别是0、1和2。

    2. 使用C语言编写寄存器映射文件
    代码2 Amy_S_max7219.h 片段
    //++++++++++++++++++++++++++++++++++++++
    // 寄存器映射 开始
    // 根据HDL编写
    //++++++++++++++++++++++++++++++++++++++
    #include 
    
    #define IOWR_MAX7219_DIN(base, data)   IOWR(base, 0, data)
    #define IOWR_MAX7219_CS(base, data)    IOWR(base, 1, data)
    #define IOWR_MAX7219_CLK(base, data)   IOWR(base, 2, data)
    //--------------------------------------
    // 寄存器映射 结束
    //--------------------------------------

    注意:结尾那个</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 片段

    代码描述:使用上面的已经映射好的函数

    //++++++++++++++++++++++++++++++++++++++
    // 基地址 开始
    // 根据SOPC Builder设置编写
    //++++++++++++++++++++++++++++++++++++++
    #include "system.h"
    
    #define max7219_addr MAX7219_BASE
    //--------------------------------------
    // 基地址 结束
    //--------------------------------------
    
    
    //++++++++++++++++++++++++++++++++++++++
    // 寄存器映射 开始
    // 根据HDL编写
    //++++++++++++++++++++++++++++++++++++++
    #include 
    
    #define IOWR_MAX7219_DIN(base, data)   IOWR(base, 0, data)
    #define IOWR_MAX7219_CS(base, data)    IOWR(base, 1, data)
    #define IOWR_MAX7219_CLK(base, data)   IOWR(base, 2, data)
    //--------------------------------------
    // 寄存器映射 结束
    //--------------------------------------
    
    
    //++++++++++++++++++++++++++++++++++++++
    // 管脚操作 开始
    //++++++++++++++++++++++++++++++++++++++
    #define SET_DIN IOWR_MAX7219_DIN(max7219_addr, 1)
    #define CLR_DIN IOWR_MAX7219_DIN(max7219_addr, 0)
    #define SET_CS  IOWR_MAX7219_CS(max7219_addr, 1)
    #define CLR_CS  IOWR_MAX7219_CS(max7219_addr, 0)
    #define SET_CLK IOWR_MAX7219_CLK(max7219_addr, 1)
    #define CLR_CLK IOWR_MAX7219_CLK(max7219_addr, 0)
    //--------------------------------------
    // 管脚操作 结束
    //--------------------------------------

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

    代码4 Amy_S_max7219.c代码片段

    代码描述:使用Altera API的具体操作

    #include "Amy_S_max7219.h"
    
    /*
     * 发送一个字节的子程序:
     * 上升沿发送数据,
     * MSB first
     */
    void Max7219_WriteByte(alt_u8 byte)
    {
      alt_u8 i;
      for (i=0; i<8; i++)
      {
        CLR_CLK;
        if(byte & 0x80)
          SET_DIN;
        else
          CLR_DIN;
        byte <<= 1;
        SET_CLK;
      }
    }

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

    方法2 使用位域或结构体

    其实这种方法,Altera的API的源代码有时也会用到。但是有一个地方需要注意,后面会提到。

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

    如上。

    2. 使用C语言编写寄存器映射文件
    代码4 Amy_S_max7219.h 片段
    //++++++++++++++++++++++++++++++++++++++
    // 寄存器映射 开始
    // 根据HDL编写
    //++++++++++++++++++++++++++++++++++++++
    #include "system.h"
    #include "alt_types.h"
    
    typedef struct
    {
      alt_u32 DIN : 32;
      alt_u32 CS  : 32;
      alt_u32 CLK : 32;
    }MAX7219_T;
    
    #define m7219 ((MAX7219_T *)(MAX7219_BASE))
    //--------------------------------------
    // 寄存器映射 结束
    //--------------------------------------

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

    代码5 Amy_S_max7219.c代码片段

    代码描述:使用结构体指针寻址示例

    /*
     * 发送一个字节的子程序:
     * 上升沿发送数据,
     * MSB first
     */
    void Max7219_WriteByte(alt_u8 byte)
    {
      alt_u8 i;
      for (i=0; i<8; i++)
      {
        m7219->CLK = 0;
        if(byte & 0x80)
          m7219->DIN = 1;
        else
          m7219->DIN = 0;
        byte <<= 1;
        m7219->CLK = 1;
      }
    }

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

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

    代码6 两种寄存器存储映射所对应的汇编
    IOWR=32DIRECT(GPIO_LED_BASE, 0, 1); 
    0x04000234 : movhi r3,2048 
    0x04000238 : addi r3,r3,6144 
    0x0400023c : movi r2,1 
    0x04000240 : stwio r2,0(r3)
    
    LED = 1; 
    0x04000224 : movhi r3,2048 
    0x04000228 : addi r3,r3,6144 
    0x0400022c : movi r2,1 
    0x04000230 : 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片段
    #define ALT_MODULE_CLASS_max7219 Amy_S_max7219
    #define MAX7219_BASE 0x1002020
    #define MAX7219_IRQ -1
    #define MAX7219_IRQ_INTERRUPT_CONTROLLER_ID -1
    #define MAX7219_NAME "/dev/max7219"
    #define MAX7219_SPAN 16
    #define MAX7219_TYPE "Amy_S_max7219"

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

    代码8 修改后的Amy_S_max7219.h片段
    //++++++++++++++++++++++++++++++++++++++
    // 寄存器映射 开始
    // 根据HDL编写
    //++++++++++++++++++++++++++++++++++++++
    #include "system.h"
    #include "alt_types.h"
    
    typedef struct
    {
      alt_u32 DIN : 32;
      alt_u32 CS  : 32;
      alt_u32 CLK : 32;
    }MAX7219_T;
    
    #define m7219 ((MAX7219_T *)(MAX7219_BASE | 1<<31))
    //--------------------------------------
    // 寄存器映射 结束
    //--------------------------------------

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

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

    3. 一点其他内容

    有时候我们所使用的寄存器并不一定都是32位的,这时要使用嵌套结构体来凑足32位,以防止寄存器寻址错误。

    代码9 嵌套结构体示例
    typedef struct{
      struct{
        alt_u8  DIN : 8;
        alt_u32 NC  : 24;
      }offset_0;  
      struct{
        alt_u8  CS  : 1;
        alt_u32 NC  : 31;
      }offset_1;  
      struct{
        alt_u8  CLK : 1;
        alt_u32 NC  : 31;
      }offset_2;
    }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

  • 相关阅读:
    微信小程序 单选按钮 最佳
    微信小程序 单选按钮的实现
    微信小程序 单选框实现
    Java Code To Create Pyramid and Pattern
    Java language
    npm Err! Unexpected end of JSON input while parsing near
    Node.js Express FrameWork Tutorial
    Higher-Order Function Examples
    Create First HTTP Web Server in Node.js: Complete Tutorial
    Node.js NPM Tutorial: Create, Publish, Extend & Manage
  • 原文地址:https://www.cnblogs.com/yuphone/p/1717779.html
Copyright © 2011-2022 走看看