1 引言

本文将通过作者写的程序对avalon总线进行描述,相信会对avalon总线有更加深的认识。

2 模块连接和源代码

图1给了编写的模块和NIOS核之间的互联关系。(图中只示出了与本文相关的模块,同时未示出总线)。代码将实现如下的功能:

1、 NIOS核可直接对master_slave和slave直接进行读写。

2、 NIOS核可以控制master_slave对slave进行读写。

图1 模块连接示意图

直接上代码。分析在文章后半部分

代码1:master_slave模块的硬件源码

module master_slave(

clk,reset_n,

address,write,writedata,read,readdata,

maddress,mwrite,mwritedata,mread,mreaddata,mwaitrequest

);

input clk;

input reset_n;

// 从接口信号

input[1:0] address;

input write;

input [31:0] writedata;

input read;

output [31:0] readdata;

// 主接口信号

output[31:0] maddress;

output mwrite;

output[31:0] mwritedata;

output mread;

input[31:0] mreaddata;

input mwaitrequest;

reg [31:0] start_address;

reg [31:0] tramsfer_num;

reg [31:0] wr_control;

reg [31:0] data_from_slave;

reg start_address_selected;

reg tramsfer_num_selected;

reg wr_control_selected;

reg data_from_slave_selected;

reg [31:0] readdata;

// 地址译码

always @ (address)

begin

start_address_selected<= 0;

tramsfer_num_selected<= 0;

wr_control_selected<= 0;

data_from_slave_selected<= 0;

case(address)

2'b00:start_address_selected<= 1;

2'b01:tramsfer_num_selected<= 1;

2'b10:wr_control_selected<= 1;

2'b11:data_from_slave_selected<= 1;

default:

begin

start_address_selected<= 0;

tramsfer_num_selected<= 0;

wr_control_selected<= 0;

data_from_slave_selected<= 0;

end

endcase

end

// 从接口:写初始地址寄存器

always @ (posedge clk or negedge reset_n)

begin

if(reset_n==1'b0)

start_address<= 0;

else

begin

if(write& start_address_selected)

begin

start_address<= writedata;

end

end

end

// 从接口:写读写个数寄存器

always @ (posedge clk or negedge reset_n)

begin

if(reset_n==1'b0)

tramsfer_num<= 0;

else

begin

if(write& tramsfer_num_selected)

begin

tramsfer_num<= writedata;

end

end

end

// 从接口:写读写控制寄存器

always @ (posedge clk or negedge reset_n)

begin

if(reset_n==1'b0)

wr_control<= 0;

else

begin

if(write& wr_control_selected)

begin

wr_control<= writedata;

end

end

end

// 从接口:读寄存器

always @ (address or read or tramsfer_numor start_address)

begin

if(read)

case(address)

2'b00:readdata<= start_address;

2'b01:readdata<= tramsfer_num;

2'b10:readdata<= wr_control;

2'b11:readdata<= data_from_slave;

default:readdata<= 32'h8888;

endcase

end

reg[31:0] maddress;

reg mwrite;

reg [31:0] mwritedata;

reg mread;

reg state_read;

reg state_write;

reg[1:0] num_read_down;

reg[1:0] num_write_down;

wire read_enable;

wire write_enable;

assign read_enable = wr_control[0];

assign write_enable = wr_control[1];

// 主接口读,写 : 地址信号,读信号,写信号

always @ (posedge clk or negedge reset_n)

begin

if(!reset_n)

begin

mread<= 1'b0;

mwrite<= 1'b0;

maddress<= 32'd0;

state_read<= 1'b0;

state_write<= 1'b0;

num_read_down<= 2'd0;

num_write_down<= 2'd0;

end

else

begin

if(read_enable& (num_read_down < {tramsfer_num[1],tramsfer_num[0]}))//主接口读

begin

case(state_read)

1'b0:

begin

mread<= 1'b1;

maddress<= start_address + {num_read_down,2'b00};

state_read<= 1'b1;

end

1'b1:

begin

data_from_slave= mreaddata;

mread<= 1'b0;

maddress<= 32'd0;

state_read<= 1'b0;

num_read_down<= num_read_down + 1;

end

endcase

end

if(write_enable& (num_write_down < {tramsfer_num[1],tramsfer_num[0]}))//主接口写

begin

case(state_write)

1'b0:

begin

mwrite<= 1'b1;

maddress<= start_address + {num_write_down,2'b00};

mwritedata<= 32'd158;

state_write<= 1'b1;

end

1'b1:

begin

mwrite<= 1'b0;

maddress<= 32'd0;

mwritedata<= 32'd0;

state_write<= 1'b0;

num_write_down<= num_write_down + 1;

end

endcase

end

end

end

endmodule

代码2:slave模块的硬件源码

module slave(

clk,reset_n,

address,write,writedata,read,readdata,

);

input clk;

input reset_n;

// 从接口

input address;

input write;

input [31:0] writedata;

input read;

output [31:0] readdata;

reg [31:0] first_reg;

reg [31:0] second_reg;

reg first_reg_selected;

reg second_reg_selected;

reg [31:0] readdata;

// 地址译码

always @ (address)

begin

first_reg_selected<=0;

second_reg_selected<=0;

case(address)

1'b0:first_reg_selected<=1;

1'b1:second_reg_selected<=1;

default:

begin

first_reg_selected<=0;

second_reg_selected<=0;

end

endcase

end

// 从接口:写第一个寄存器

always @ (posedge clk or negedge reset_n)

begin

if(reset_n==1'b0)

first_reg=0;

else

begin

if(write& first_reg_selected)

begin

first_reg=writedata;

end

end

end

// 从接口:写第二个寄存器

always @ (posedge clk or negedge reset_n)

begin

if(reset_n==1'b0)

second_reg=0;

else

begin

if(write& second_reg_selected)

begin

second_reg=writedata;

end

end

end

// 从接口:读寄存器

always @ (address or read or second_reg orfirst_reg)

begin

if(read)

case(address)

2'b00:readdata<=first_reg;

2'b01:readdata<=second_reg;

default:readdata=32'h0000;

endcase

end

endmodule

代码3:NIOS读写软件源代码

#include <stdio.h>

#include "system.h"

#include <unistd.h>

typedef struct{

unsigned int start_address;

unsigned int tramsfer_num;

unsigned int wr_control;//wr_control[0]控制读(1为开始读),wr_control[1]控制写(1为开始写)

unsigned int data_from_slave;

}MASTER;

typedef struct{

unsigned int first_reg;

unsigned int second_reg; //32位

}SLAVE;

int main()

{

int temp1;

int temp2;

MASTER *master = (MASTER *) TEST3_0_BASE;

SLAVE *slave = (SLAVE *) TEST3_SLAVE_0_BASE;

//初始化从接口寄存器(slave)

slave->first_reg = 0xf3;

slave->second_reg = 0xf1;

//主接口(master_slave)读从接口(slave)

master->start_address = TEST3_SLAVE_0_BASE;//给主接口需要读的地址

master->tramsfer_num = 1;//给主接口需要读的数据的个数

master->wr_control = 1;//读使能置位

master->wr_control = 0;//读使能清零

temp1 = master->data_from_slave;//读master_slave从slave读到的数据

printf(“%d”,temp1);

//主接口写从接口

master->start_address = TEST3_SLAVE_0_BASE;/ 给主接口需要写的地址

master->tramsfer_num = 2;// 给主接口需要写的数据的个数

master->wr_control= 2;//写使能置位

master->wr_control= 0;//写使能清零

temp1 = slave->first_reg;//读master_slave从slave读到的数据

temp2 = slave->second_reg;// 读master_slave从slave读到的数据

return 0;

}

3 建立工程

具体工程建立的过程再次仅仅做简单的介绍,把重点放在对代码关键部分讲解上。在QuartusII,SOPC Builder中搭建好硬件,在硬件中添加之前提到的master_slave和slave。

图2 SOPC Builder建立的硬件系统

在NIOS II IDE中建立新工程,在主程序中写入NIOS读写软件源代码,编译运行能看到如下的结果:

图3 程序运行结果

4 关键代码讲解

代码2的slave是一个从接口,可以实现读写。代码1的master_slave有一个从接口,一个主接口,从接口几乎和代码2相同,实现的效果也相同。同时,代码1还有一个主接口,可以实现对从设备的读写。因此将对代码1进行解读。需要指出的是两个代码的读写都是典型的读写,没有用到流传输或者是突发传输。

master_slave中有四个寄存器,可以在图2中看出,master_slave的地址是从0x00001800-0x0000180f。Master_slave从接口有如下信号:address,write,writedata,read,readdata。主接口有如下信号:address,write,writedata,read,readdata,waitrequest。两个接口都是32位的。需要指出的是主接口有waitrequest,但是程序中并没有用到这个信号,如果不加这个信号在自定义IP核时,SOPC Builder会报错,不知道这是不是一个bug。所以在信号中加入了waitrequest信号,同是该信号始终接地,不会影响读写过程。

图4 master_slave模块信号

4.1 地址译码段

avalon总线传输过来地址,当地址偏移为0时选中第一个寄存器(start_address_reg),当偏移为1时选中第二个寄存器(tramsfer_num_reg)……

4.2 从接口写传输段

在模块中的4个寄存器只有前3个寄存器可以被写,在时钟上升沿,并且write信号有效,寄存器被选中,则写入信号。

4.3 从接口读传输段

模块中4个寄存器都可以被读,当读信号有效时,给总线数据。

4.4 主接口读写传输段

主接口读写传输需要符合avalon总线的时序。再本节中给出时序图,根据时序图能够较为清晰得看出为什么从接口读写过程中的敏感信号不同。

图5读传输过程时序图

图6 写传输过程时序图

4.5 软件代码部分

软件代码主要是实现对从设备的读写,采用结构体能够很好的对地址的偏移进行处理。如MASTER结构体将模块中的四个寄存器进行了定义,在赋值和读取时只要用到指针,可以使代码简单易读,编写上也会方便不少。

本篇错误在所难免,希望大家指出,有一些写得不是特别清楚的地方欢迎谈论,

【Avalon总线】4.avalon总线MM设备读写程序实例及分析相关推荐

  1. iic获取salve设备地址_Linux下使用IIC总线读写EEPROM(读写i2c从设备通用程序)

    Linux 下使用IIC总线 读写 EEPROM by 韩大卫 @吉林师范大学 handawei@jusontech.com 转载请务必表明出处 ******************* ******* ...

  2. Linux I2C总线(二)I2C设备驱动编写方法

    Linux I2C总线 Linux I2C总线(一)I2C驱动框架 Linux I2C总线(二)I2C设备驱动编写方法 Linux I2C总线(二)I2C设备驱动编写方法 文章目录 Linux I2C ...

  3. Linux内核总线系统 —— 通用总线和平台设备

    在 Linux内核输入子系统框架_Bin Watson的博客-CSDN博客 这篇文章中,我们详细分析了输入子系统.了解到了 dev 和 handler 分层的思想.而在 jz2440_输入子系统驱动程 ...

  4. USB总线-Linux内核USB设备驱动之UAC2驱动分析(十)

    1.概述 UVC(USB Audio Class)定义了使用USB协议播放或采集音频数据的设备应当遵循的规范.目前,UAC协议有UAC1.0和UAC2.0.UAC2.0协议相比UAC1.0协议,提供了 ...

  5. Cortex-M3 I-Code,D-Code,系统总线及其他总线接口

    Cortex-M3 的总线接口 下图是Cortex-M3的连接方式样板图,大家可以根据需求自行更改, CM3 处理器的总线接口是基于 AHB-Lite 和 APB 协议.下图中总线复用器的作用是,让指 ...

  6. AB计算机总线,什么是总线?简要说明AB、DB、CB的含义及其性能?

    总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线.地址总线和控制总线,分别用来传输数据.数据地址和控 ...

  7. 以太网扫盲(一)各种网络总线 mii总线,mdio总线介绍

    本文主要介绍以太网的MAC(Media Access Control,即媒体访问控制子层协议)和PHY(物理层)之间的MII(Media Independent Interface ,媒体独立接口), ...

  8. 内部总线、系统总线、外部总线

    内部总线:在CPU内部,寄存器之间和算术逻辑部件ALU与控制部件之间传输数据所用的总线称为片内总线(即芯片内部的总线). 1.I2C总线 (Inter-IC)总线:10多年前由Philips公司推出, ...

  9. 总线与接口(内部总线、系统总线、外部总线)

    这种分类方式是根据离芯片远近等级分类的. 1.1 内部总线 内部总线是微机内部各外围芯片与处理器之间的总线,用于芯片一级的互连. 1.1.1 I2C总线 I2C(Inter-IC)总线10多年前由Ph ...

最新文章

  1. 五 Android Studio打包Eegret App (包名和签名,打出正式包)
  2. Oracle分页(limit方式的运用)
  3. nuxt 过滤 query 参数
  4. Python入门100题 | 第038题
  5. Android 出现“此用户无法使用开发者选项”问题
  6. 【Python】Python 远程连接服务器,用它就够了
  7. 爱创课堂每日一题第四十天- 说说你对语义化的理解?
  8. SpringCloud熔断器介绍
  9. 设计模式(Design Patterns)详解
  10. 极乐科技CEO应邀出席2017微信小程序生态课
  11. 怎么隐藏Windows11开始菜单中的推荐面板
  12. Spring-Data-JPA--增删改查2——自定义接口查询
  13. C++_类和对象_封装_访问权限_C++中struct和class的区别---C++语言工作笔记037
  14. 实现Kubernetes跨集群服务应用的高可用
  15. mysql-debug: Thread stack overrun
  16. 面向对象中多个对象之间的关系
  17. php选择不同店发送不同邮件,php – WooCommerce电子邮件通知:不同城市的不同电子邮件收件人...
  18. Java基础(三):常用对象
  19. 研究生能合作发表论文吗?
  20. Bootstrap练习:百度登录框

热门文章

  1. Java Solr全文搜索引擎的实现
  2. python音频频谱_Python读取WAV音频文件 画频谱的方法
  3. word中“居中”,“标准”不能解决(公式/图片)和文字不在一行的处理办法
  4. Python 图片合并 pdf
  5. 交叉表查询中的多列显示。
  6. c++俄罗斯方块游戏学习
  7. 怎么把百度云人脸识别添加到百度云人脸库中
  8. vue中input绑定事件
  9. 学习记录:安卓苹果移动端口测点
  10. javascript的Promis对象