一、AHB总线学习

1. AHB总线结构

如图所示,AHB总线系统利用中央多路选择机制实现主机与从机的互联问题。从图中可以看出,AHB总线结构主要可分为三部分:主机、从机、控制部分。控制部分由仲裁器、数据多路选择、地址和数据多路选择及地址译码器组成。主机首先需要向仲裁器提出使用总线的请求hbusreq信号,仲裁器通过仲裁(多主机使用总线的优先级)授权(hgrant)给某一主机(注意:一个周期内只能有一个主机接入总线),此时,主机就可以开始进行AHB传输了。主机首先发出地址和控制信号。这些信号主要提供地址信息、传输方向、带宽及burst类型(burst传输并非本文重点,故不作讨论)。由于AHB总线统一给每个从机分配地址,译码器可以根据主机发出的地址选择哪个主机与从机进行互联。

2、AHB总线基本传输

AHB总线的一次传输主要由两部分组成:地址段(开始传输的第一个周期)和数据段(传输开始后的周期)。在hclk上升沿来临时,获得授权的主机驱动地址和控制信号到AHB总线上,在hclk下一周期的上升沿时,slave开始采样地址和控制信息。获取地址和控制信息的slave会返回hresp(回应信号)给master,而在hclk的第三个时钟上升沿hresp被master采样,与此同时,master与slave间完成数据的第一次读写操作。

在进行数据传输时,若从机没有准备好接收下一个数据iketongg将hready信号拉低来插入一个空闲周期,等下一周期hready重新为高时再接收数据。主机在当前周期发送完部分数据,而在下一周期没有准备好发后面的数据,可通过加入BUSY状态来延缓传输。

二、基于AHB总线的读写设计

1、输入输出接口

在设计某个模块时,首先需要理清它有哪些输入输出,从而对设计进行一个整体了解。由于本文的读写模块设计属于比较基础的AHB传输,不涉及突发传输、锁定传输和从机的分块传输。本设计的输入有:hclk_i、irst_n、hgrant_i、hrdata_i、hready_i,输出有:hwdata_o、htrans_o、hwrite_o、haddr_o、hbusreq_o。

1、状态机设计

状态机的设计比较重要,本设计的主状态机是:空闲状态、读状态、写状态,从状态机分为读状态机(rd_fsm_r)和写状态机(wr_fsm_r),读状态机和写状态机的状态转移图如图所示。

根据AHB总线地址段和数据段的特性,可将其分为:空闲状态、请求总线状态、地址段状态、读/写数据状态和读/写最后一个字节状态。注意:在状态机中,何时有效很重要,从图中可以看出,各状态的触发条件都有hready_i信号(由于hready_i信号是一直在变化的,可能前一个状态hready_i信号为高,但后一个状态会变低,不能使用软件思维去思考。),其次,何时开始读/写数据,何时数据读/写完成,这都是由计数器计数来决定的。

2、设计时序图

读写过程比较类似,时序图如图所示:

从图中可以看出,地址与数据并非在同一周期(AHB总线的特性)。当前周期的地址,存储的数据在下一周期才会出现。这种地址和数据交叠出现使总线能进行高性能操作的同时,给从机也提供了足够的时间来响应传输。

3、基本代码

(1)状态机逻辑

module ahb_test(hbusreq_o,haddr_o,htrans_o,hwdata_o,hwrite_o,
hclk_i,irst_n,hgrant_i,hready_i,hrdata_i,we_i,re_i); input hclk_i,irst_n,we_i,re_i,hgrant_i,hready_i;
input [:] hrdata_i;
output hbusreq_o,hwrite_o;
output [:] hwdata_o;
output [:] htrans_o;
output [:] haddr_o; reg [:] main_fsm_r;
reg [:] rd_fsm_r;
reg [:] wr_fsm_r;
reg [: haddr_r;
reg [:] rd_cnt_r;
reg [:] wr_cnt_r; parameter data_size = ; //读写4个字节数据
parameter rd_base_addr = 'h1A00;
parameter wr_base_addr = 'h1B00; //the status of main fsm
parameter S0 = 'd0;
parameter S1 = 'd1;
parameter S2 = 'd2; //the status of read fsm
parameter RD_IDLE = 'b000;
parameter RD_BUSREQ = 'b001;
parameter RD_ADDR = 'b010;
parameter RD_RD = 'b011;
parameter RD_LRD = 'b100; wire fsm_rd_idle = rd_fsm_r == RD_IDLE;
wire fsm_rd_busreq = rd_fsm_r == RD_BUSREQ;
wire fsm_rd_addr = rd_fsm_r ==RD_ADDR;
wire fsm_rd_rd = rd_fsm_r == RD_RD;
wire fsm_rd_lrd = rd_fsm_r === RD_LRD;
wire rd_last_data = rd_cnt_r == data_size - 'd1; //the status of write fsm
parameter WR_IDLE = 'b000;
parameter WR_BUSREQ = 'b001;
parameter WR_ADDR = 'b010;
parameter WR_WD = 'b011;
parameter WR_LWD = 'b100; wire fsm_wr_idle = wr_fsm_r == WR_IDLE;
wire fsm_wr_busreq = wr_fsm_r == WR_BUSREQ;
wire fsm_wr_addr = wr_fsm_r ==WR_ADDR;
wire fsm_wr_wd = wr_fsm_r == WR_WD;
wire fsm_wr_lwd = wr_fsm_r === WR_LWD;
wire wr_last_data = wr_cnt_r == data_size - 'd1; //Main FSM
wire rd_done;
wire wr_done;
reg we_r,re_r;
reg [:] main_fsm_r; always @(posedge hclk_i)
if(~irst_n)
main_fsm_r <=S0;
else
case(main_fsm_r)
S0: if(we_r | re_r)
main_fsm_r <= S1;
S1: if(rd_done)
main_fsm_r <=S2;
S2: if(wr_done)
main_fsm_r <=S0;
default:
main_fsm_r <= S0;
endcase //Sub Read FSM
always @(posedge hclk_i)
if(~irst_n)
rd_fsm_r <= RD_IDLE;
else
case(rd_fsm_r)
RD_IDLE : if((we_r | re_r) | (rd_done))
rd_fsm_r <= RD_BUSREQ;
RD_BUSREQ : if(hgrant_i & hready_i)
rd_fsm_r <= RD_ADDR;
RD_ADDR : if(hready_i)
rd_fsm_r <= RD_RD;
RD_RD : if(rd_cnt_r == data_size- & hready_i)
rd_fsm_r <= RD_LRD;
RD_LRD : if(hready_i & rd_last_data)
rd_fsm_r <= RD_IDLE;
default:
rd_fsm_r <= RD_IDLE;
endcase //Sub Write FSM
always @(posedge hclk_i)
if(~irst_n)
wr_fsm_r <= WR_IDLE;
else
case(wr_fsm_r)
WR_IDLE : if(rd_done)
wr_fsm_r <= WR_BUSREQ;
WR_BUSREQ : if(hgrant_i & hready_i)
wr_fsm_r <= WR_ADDR;
WR_ADDR : if(hready_i)
wr_fsm_r <= WR_WD;
WR_WD : if(wr_cnt_r == data_size- & hready_i)
wr_fsm_r <= WR_LWD;
WR_LWD : if(hready_i & wr_last_data)
wr_fsm_r <= WR_IDLE;
default:
wr_fsm_r <= WR_IDLE;
endcase

(2)寄存器逻辑

//we_r
always @(posedge hclk_i)
if(~irst_n | we_r)
we_r <= 'b0;
else(we_i)
we_r <='b1; //re_r
always @(posedge hclk_i)
if(~irst_n | re_r)
re_r <= 'b0;
else(re_i)
re_r <='b1; assign rd_done = main_fsm_r == S1 & hready_i & rd_last_data; assign wr_done = main_fsm_r == S2 & hready_i & wr_last_data; assign hwrite_o = (main_fsm_r == S2) ? 'd1 : 'd0; assign hbusreq_o = (fsm_rd_busreq || fsm_wr_busreq) ? 'd1 : 'd0; //rd_done_r
always @(posedge hclk_i)
if(~irst_n || rd_done_r)
rd_done_r <= 'd0;
else if(rd_done)
rd_done_r <= 'd1; //wr_done_r
always @(posedge hclk_i)
if(~irst_n || wr_done_r)
wr_done_r <= 'd0;
else if(wr_done)
wr_done_r <= 'd1; assign htrans_o = (fsm_rd_addr || fsm_wr_addr) ? 'b10 : 2'b11;
wire addr_add_en = (main_fsm_r == S1 || main_fsm_r == S2) &&
(fsm_rd_addr || fsm_rd_rd || fsm_wr_addr || fsm_wr_wd); //haddr_r
always @(posedge hclk_i)
if(~irst_n)
haddr_r <= 'd0;
else if(main_fsm_r == S1 & fsm_rd_busreq & hready_i)
haddr_r <= rd_base_addr;
else if(main_fsm_r == S2 & fsm_wr_busreq & hready_i)
haddr_r <= wr_base_addr;
else if(addr_add_en)
haddr_r <= haddr_r + 'd4; //rd_cnt_r
always @(posedge hclk_i)
if (~irst_n)
rd_cnt_r <= 'd0;
else if (hready_i & fsm_rd_addr)
rd_cnt_r <= 'd0;
else if (hready_i & fsm_rd_rd)
rd_cnt_r <= rd_cnt_r + 'd1;
else if (hready_i & rd_last_data)
rd_cnt_r <= 'd0; //wr_cnt_r
always @(posedge hclk_i)
if (~irst_n)
wr_cnt_r <= 'd0;
else if (hready_i & fsm_wr_addr)
wr_cnt_r <= 'd0;
else if (hready_i & fsm_wr_wd)
wr_cnt_r <= wr_cnt_r + 'd1;
else if (hready_i & wr_last_data)
wr_cnt_r <= 'd0; reg [:] rd_data_r [ : data_size-];
//rd_data_r
always @(posedge hclk_i)
if(~irst_n)
{rd_data_r[],rd_data_r[],rd_data_r[],rd_data_r[]} <= 'd0;
else if(main_fsm_r == S1 & (fsm_rd_rd || fsm_rd_lrd) & hready_i)
rd_data_r <= hrdata_i; assign hwdata_o = (main_fsm_r == S2 & (fsm_wr_wd || fsm_wr_lwd) & hready_i) ? rd_data_r[wr_cnt_r] : 'b0;
assign haddr_o = haddr_r; endmodule

至此,本文基于AHB总线的master读写设计就完成了。在设计过程中,重要的是画出状态机,并理解每个状态的逻辑及状态与状态间跳转的触发条件。需要理解阻塞赋值和非阻塞赋值。在这里说一下我对阻塞赋值和非阻塞赋值的理解:

(1)非阻塞赋值(需要使用寄存器将值存储起来,使用always块赋值):当前周期时钟上升沿时存储值,下一周期时钟上升沿才会进行赋值操作。(和下一周期的时序也有关系)。使用非阻塞赋值,各个赋值语句在块结束后(下一周期)同步赋值。

(2)阻塞赋值(组合逻辑,assign赋值):当前周期时钟上升沿赋值生效,不存储值。使用assign能实时给wire型信号赋值。(在当前周期完成操作)。阻塞语句是顺序执行的。在当前周期,前一个赋值语句执行完才能执行下一个赋值语句,即前一赋值语句的结果能影响下一赋值语句的值。

模块输入输出一般都是wire型,内部逻辑可以是wire也可以是reg,一般先对一些内部逻辑信号进行各种操作,最后再将其赋值给输出信号。如本文assign haddr_o = haddr_r;中间对haddr_r进行操作,最后将其赋值给haddr_o。

基于AHB总线的master读写设计(Verilog)的更多相关文章

  1. AHB总线协议

    https://blog.csdn.net/linton1/article/details/79649249 1. 简介 AHB(Advanced High Performance Bus)总线规范是 ...

  2. SOA实践之基于服务总线的设计

    在上文中,主要介绍了SOA的概念,什么叫做“服务”,“服务”应该具备哪些特性.本篇中,我将介绍SOA的一种很常见的设计实践--基于服务总线的设计. 基于服务总线的设计 基于总线的设计,借鉴了计算机内部 ...

  3. AHB总线和APB总线

    AHB主要用于高性能模块(如CPU.DMA和DSP等)之间的连接,作为SoC的片上系统总线,它包括以下一些特性:单个时钟边沿操作:非三态的实现方式:支持突发传输:支持分段传输:支持多个主控制器:可配置 ...

  4. 基于FPGA的XPT2046触摸控制器设计

    基于FPGA的XPT2046触摸控制器设计 小梅哥编写,未经许可,文章内容和所涉及代码不得用于其他商业销售的板卡 本实例所涉及代码均可通过向 xiaomeige_fpga@foxmail.com  发 ...

  5. AHB 总线问答(转)

    AHB总线问答 http://blog.163.com/huanhuan_hdu/blog/static/1352981182011625916845/ 仲裁:主设备可以在一个突发传输中解除HLOCK ...

  6. 基于 EntityFramework 的数据库主从读写分离架构(1) - 原理概述和基本功能实现

        回到目录,完整代码请查看(https://github.com/cjw0511/NDF.Infrastructure)中的目录:      src\ NDF.Data.EntityFramew ...

  7. 基于ZigBee的家居控制系统的设计与应用

    基于ZigBee的家居控制系统的设计与应用 PPT简介:http://pan.baidu.com/s/1i38PC6D 摘  要 智能家居是未来家居的发展方向,其利用先进的网络技术.计算机技术和无线通 ...

  8. 基于SMB协议的共享文件读写 博客分类: Java

    基于SMB协议的共享文件读写 博客分类: Java   一.SMB协议 SMB协议是基于TCP-NETBIOS下的,一般端口使用为139,445. 服务器信息块(SMB)协议是一种IBM协议,用于在计 ...

  9. 1.3 PCI总线的存储器读写总线事务

    总线的基本任务是实现数据传送,将一组数据从一个设备传送到另一个设备,当然总线也可以将一个设备的数据广播到多个设备.在处理器系统中,这些数据传送都要依赖一定的规则,PCI总线并不例外. PCI总线使用单 ...

随机推荐

  1. 从矩阵(matrix)角度讨论PCA(Principal Component Analysis 主成分分析)、SVD(Singular Value Decomposition 奇异值分解)相关原理

    0. 引言 本文主要的目的在于讨论PAC降维和SVD特征提取原理,围绕这一主题,在文章的开头从涉及的相关矩阵原理切入,逐步深入讨论,希望能够学习这一领域问题的读者朋友有帮助. 这里推荐Mit的Gilb ...

  2. FreeSql (八)插入数据时指定列

    插入数据时指定列,和忽略列对应,未被指定的列将被忽略. var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Passwor ...

  3. 正确应用Java数组

    一.数组的特点 数组与其他容器的区别有三方面:效率.类型和保存基本类型的能力. 1.效率.数组是一种效率最高的存储和随机访问对象引用序列的方式.数组是一段连续地址空间内的线性序列,所以访问非常快.但也 ...

  4. Redis常用命令(key、string、List)

    1.Key 1.keys *   查询所有数据 2.exists key名   判断key名是否存在 3.move key名  数据库号(0-15)  移动数据key名到相应的数据库 4.expire ...

  5. apache ignite系列(五):分布式计算

    ignite分布式计算 在ignite中,有传统的MapReduce模型的分布式计算,也有基于分布式存储的并置计算,当数据分散到不同的节点上时,根据提供的并置键,计算会传播到数据所在的节点进行计算,再 ...

  6. 装系统 ------ 使用微PE 做系统盘

    1.什么是PE系统 pe系统是一种装系统的系统,也就是预装系统的系统,它是一种系统预装环境和工具. 可以放在U盘或光盘里随身携带,可以用来给电脑装系统 2.常见的制作pe 系统的工具 大白菜,U启动, ...

  7. DirectX12 3D 游戏开发与实战第二章内容

    矩阵代数 学习目标 理解矩阵及其相关运算的定义 探究为何能把向量和矩阵的乘法视为一种线性组合 学习单位矩阵.转置矩阵.行列式以及矩阵的逆等概念 逐步熟悉DirectXMath库中提供的关于矩阵计算的类 ...

  8. C# 微信接口认证

    public void valid() { string echostr = Request.QueryString["echostr"]; if (!string.IsNullO ...

  9. maven的settings.xml详解

    <?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://mav ...

  10. Promise.all结合数组Map用法

    Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise  ...