IT评测·应用市场-qidao123.com
标题:
FPGA项目——基于AMBA总线的流水灯控制系统
[打印本页]
作者:
熊熊出没
时间:
2023-4-4 14:28
标题:
FPGA项目——基于AMBA总线的流水灯控制系统
绪论
本文将介绍一个完全用Verilog HDL手写的AMBA片上系统,项目的主题是设计一个
基于AMBA总线的流水灯控制系统
,
项目中所有数字电路逻辑都将通过Verilog进行RTL设计,不会调用成熟IP核,
然后利用Vivado平台对RTL模型进行仿真、综合与布线,最后在FPGA开发板上进行板级验证。
AMBA是ARM公司推出的一种总线架构,目前已非常成熟,在行业内得到广泛的应用,极具实际应用价值,
本项目涉及了AMBA架构中的
AHB协议
&
APB协议
,
系统包括AHB总线、APB总线两个部分,
整个系统的基本架构如下图所示:
总的来说,由AHB总线上的主机————流水灯控制单元(Control Unit),发出控制信号,
进而对APB总线上的从机GPIO模块进行配置,实现对流水灯的流动方式的控制,
本项目是一个完整的AMBA总线系统,包括总线、主机、从机以及外设,
在各章节中,将会介绍如何利用Verilog语言对以下模块进行RTL设计:
1. Control Unit模块
2. AHB2APB Bridge模块
3. GPIO模块
希望本项目能给对AMBA总线架构感兴趣的朋友们一些启发与帮助。
下面,先简要介绍一下AHB/APB总线的基本特性和数据传输时序:
AHB总线协议简介:
AHB总线,全称为Advanced High performance Bus,属于AMBA2.0规范中的一部分,
在SoC片上系统中,AHB主要用于一些高性能模块之间的连接(如CPU、DMA、DSP等),
主要的特性包括:单时钟边沿操作、支持突发传输、支持多个主控制器、可配置总线宽度(32位~128位)、支持字节、半字、字传输。
一个典型的AHB总线系统通常以下图所示的方式进行多主机多从机互联:
可以看出,Master(主机)这边的地址信号HADDR和数据信号HWDATA都是广播的,通过仲裁器arbiter给出的hselx来选择对应的从机,
而Slave(从机)这边的数据,则是通过Decoder来解码给主机这边的。
当HSELx选中对应的从设备后,就可以开始传输了,一个无等待传输的时序如下所示:
图中可以看出,AHB总线具有流水线特性,地址周期和数据周期交替进行
APB总线协议简介:
APB,全称为Advanced Peripheral Bus,主要用于低带宽的周边外设之间的连接,例如UART、1284等,
APB总线架构不像AHB支持多个Master,在APB里面唯一的Master就是APB桥。
APB总线特性包括:
两个时钟周期传输(非流水线,不同于AHB)
无需等待周期和回应信号
控制逻辑简单
只有四个控制信号
下图展示了APB总线上的一次标准的数据传输时序:
其他有关APB总线的介绍,将在下文AHB2APB Bridge模块的设计章节中涉及
1. 系统架构与功能
系统架构:
本项目的系统架构框图上图所示,
其中,蓝色部分为AHB总线以及AHB上的主机从机,粉色部分则是APB总线以及APB上的主机从机,
下面介绍一下总线上各个模块的功能和定义:
流水灯控制单元(Contro Unit)
在AHB总线上,流水灯控制单元作为Master,
控制单元发出的AHB总线信号将经过AHB2APB Bridge,成为APB信号,
APB信号会对GPIO模块的寄存器进行配置,配置后的GPIO就可以对开发板上的按键状态进行观测,并对LED灯进行控制
AHB转APB桥(AHB2APB Bridge)
AHB2APB Bridge作为逻辑连接器,一方面是在AHB的Slave,另一方面也是APB的Master(APB总线有且只有这唯一1个Master)。
弄清这个概念,就可以定义模块的输入输出的端口,以及需要满足的时序。
由于AHB总线具有流水线特性,而APB没有,这就需要让AHB在合适的时候拉低HREADYout添加一个等待周期
通用输入输出模块(General-purpose Input/Output)
GPIO模块作为APB总线上的Slave,具有一个APB Slave接口,
除此之外,也会直接连接到FPGA开发板的按键外设和LED灯外设:
GPIO模块的输入为Key[3:0],用于观测开发板上的按键状态,
GPIO模块的输出为LED[3:0],用于控制开发板上LED灯的亮灭。
由于本项目用到的FPGA开发板的LED外设为共阳极,因此当引脚输出低电平时,对应LED将被点亮;反之,LED将会熄灭。
项目中涉及到的外设在开发板上的位置如下图所示:
流水灯系统工作模式:
初始静止模式:所有LED灯熄灭
,
上电后/按下reset后,进入该状态,所有LED灯熄灭,
此时LED_mode = 4'b0000(LED_mode为Control Unit中流水灯工作模式寄存器,用以控制LED灯闪烁的逻辑)
工作模式0:普通流水灯模式
初始静止模式同时按下KEY0~KEY3
进入工作模式0,
或在工作模式0~3下,按下FPGA开发板上按键KEY1进入工作模式0,
此时LED_mode = 4'b0001,
在工作模式0下,开发板上的LED灯将按照下图所示的方式,做有规律的周期性流动:
LED0~LED4将依次闪烁,每个灯闪烁时长为1s,循环周期为4s
工作模式1:加速流水灯模式
在工作模式0~3下,按下FPGA开发板上按键KEY2,进入该工作状态,
此时LED_mode = 4'b0010,
在工作模式1下,LED灯将加速流动,每个灯闪烁时长为0.5s,循环周期为2s:
!
工作模式2:心跳模式
在工作模式0~3下,按下FPGA开发板上按键KEY3,进入该工作状态,
此时LED_mode = 4'b0100,
该模式下LED等将模仿心跳的节奏进行闪烁(详见最后一章中FPGA运行视频)
工作模式3:呼吸灯模式
在工作模式0~3下,按下FPGA开发板上按键KEY4,进入该工作状态,
该模式下LED将周期性进行
逐渐由亮到暗再到亮
的变化(周期为4s,其中前2s由暗变量,后2由亮变暗),
此时LED_mode = 4'b1000,
由于FPGA开发板上的LED灯亮度无法直接配置,
该模式是通过
调节LED灯的占空比
来实现的,占空比越高,小灯的平均功率越大,我们人眼看到的亮度也就越高
2. AHB2APB Bridge模块设计
首先介绍AHB2APB Bridge模块的设计方法,
该模块是AHB与APB总线之间的桥梁,负责两种协议信号的相互转换,
AHB2APB Bridge的模块框图和信号定义如下:
AHB2APB Bridge既是AHB总线上的一个Slave,也作为APB总线上唯一的Master,
其任务是将来自于AHB总线上的信号转化为APB信号,实现AHB系统和APB的互联,
那么如何实现一个可以完成上述功能的Bridge模块呢?
答案是通过一个
有限状态机
(FSM,Finite State Machine)来进行控制,
看看一次典型的AHB到APB信号转换时序图(以Read为例):
在T1-T2期间,Bridge作为AHB总线上的Slave被HADDR选中,
在T2-T4两个时钟周期内,Bridge将AHB的HADDR上的地址Addr1放在APB总线的PADDR上,
其中T2-T3,PENABLE尚未被拉高,且HREADY会被Bridge拉低,因为APB的传输速度是低于AHB的,需要AHB总线上的Master进行等待,
而T3-T4,PENABLE被Bridge使能,数据信息被放上PRDATA并且由Bridge转交给HRDATA,
我们将T1-T2,T2-T3,T3-T4这个周期分别称为
IDLE
,
SETUP
和
ENABLE
,
于是我们得到AHB2APB Bridge的状态机转换示意图:
注意:在T2-T4传输Data1后,APB总线又在T4-T6两个周期内完成了Data2的传输,
这两次传输之间并没有经历IDLE状态,这表明当AHB总线上仍然有传输任务时,ENABLE状态结束后会直接再次跳往SETUP状态以准备下一个数据的传输,
AHB2APB Bridge模块完整RTL如下:
module AHB2APB_bridge #(
//HRANS Parameters
parameter IDLE = 2'b00 ,
parameter BUSY = 2'b01 ,
parameter SEQ = 2'b10 ,
parameter NONSEQ = 2'b11 ,
//HRSP Parameters
parameter OKAY = 2'b00 ,
parameter ERROR = 2'b01 ,
parameter SPLIT = 2'b10 ,
parameter RETRY = 2'b11 ,
//bridge_state Parameters
parameter BRIDGE_IDLE = 2'b00,
parameter BRIDGE_SETUP = 2'b01,
parameter BRIDGE_ENABLE = 2'b10,
//ADDR Parameters
parameter ADDR_GPIO_0 = 32'h0000_0000,
parameter ADDR_GPIO_1 = 32'h0000_8000
) (
//------------ AHB ------------
//input
input iHCLK,
input iHRESETn,
input iHSEL,
input [1:0] iHTRANS,
input [3:0] iHSIZE ,
input [2:0] iHBURST,
input iHWRITE,
input [31:0] iHWDATA,
input [31:0] iHADDR ,
//output
output oHREADY,
output [1:0] oHRESP ,
output [31:0] oHRDATA,
//------------ APB ------------
//input
input [31:0] iPRDATA,
//output
output oPSEL0,
output oPSEL1,
output oPWRITE,
output oPENABLE,
output [31:0] oPADDR,
output [31:0] oPWDATA
);
/*————————————————————————————————————————————————————————————————————————*\
/ AHB Signal Register \
\*————————————————————————————————————————————————————————————————————————*/
reg iHWRITE_r ;
reg [31:0] iHADDR_r ;
reg [31:0] iHWDATA_r ;
always@( posedge iHCLK) begin
if(!iHRESETn) begin
iHWRITE_r <= 1'b0;
iHADDR_r <= 32'b0;
iHWDATA_r <= 32'b0;
end
else if( iHSEL && (bridge_state == BRIDGE_IDLE || bridge_state == BRIDGE_ENABLE))begin
iHWRITE_r <= iHWRITE; // ahb reg change when bridge_state is going to change:
iHADDR_r <= iHADDR ; // from IDLE to SETUP
iHWDATA_r <= iHWDATA; // from ENABLE to SETUP
end
end
/*————————————————————————————————————————————————————————————————————————*\
/ Bridge FSM \
\*————————————————————————————————————————————————————————————————————————*/
reg [1:0] bridge_state;
always @(posedge iHCLK ) begin
if (!iHRESETn) begin
bridge_state <= BRIDGE_IDLE;
end else begin
case ( bridge_state )
// IDLE
BRIDGE_IDLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
// SETUP
BRIDGE_SETUP: begin
bridge_state <= BRIDGE_ENABLE;
end
// ENABLE
BRIDGE_ENABLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
//DEFAULT
default: bridge_state <= BRIDGE_IDLE;
endcase
end
end
/*————————————————————————————————————————————————————————————————————————*\
/ AHB Slave Output \
\*————————————————————————————————————————————————————————————————————————*/
assign oHREADY = ( bridge_state == BRIDGE_SETUP ) ? 1'b0 : 1'b1;
assign oHRESP = OKAY ;
assign oHRDATA = iPRDATA;
/*————————————————————————————————————————————————————————————————————————*\
/ APB Master Output \
\*————————————————————————————————————————————————————————————————————————*/
assign oPSEL0 = ( iHADDR_r == ADDR_GPIO_0) ? 1'b1 : 1'b0;
assign oPSEL1 = ( iHADDR_r == ADDR_GPIO_1) ? 1'b1 : 1'b0;
assign oPWRITE = iHWRITE_r ;
assign oPENABLE = ( bridge_state == BRIDGE_ENABLE ) ? 1'b1 : 1'b0;
assign oPADDR = iHADDR_r;
assign oPWDATA = iHWDATA_r;
endmodule
复制代码
最后是
呼吸灯模式
(工作模式3),
该模式相对复杂一些,前面提到过是通过改变占空比控制LED灯亮度的,
占空比的改变是通过对timer的判断实现的,
举个例子:
reg [31:0] oGPIOout ;
always @(*) begin
for ( i=0 ; i<32 ; i=i+1 ) begin
if( reg_DIRM[i] & reg_OEN[i] ) begin //output mode
oGPIOout[i] = reg_DATA[i] ;
end else begin
oGPIOout[i] = 1'bz;
end
end
end
复制代码
(timer[1:0] == 2'b00 )
的判定,在timer的值每增加4的过程中,
必定出现且只出现1次
,
这样在
(timer >= 32'd89_999_999 )
的这段期间内,
LED灯的驱动引脚就会在25%的时间里处于低电平(点亮),剩下的75%时间里处于高电平(熄灭),
同理,
(timer[2:0] == 3'b0 )
就能得到1/8的占空比,也就是12.5%,
这样就有:
for ( i=0 ; i<32 ; i=i+1 ) begin
if ( reg_DIRM[i] ) begin
reg_DATA_RO[i] <= oGPIOout[i] ;// output mode
end else begin
reg_DATA_RO[i] <= iGPIOin[i] ;// input mode
end
end
复制代码
可以看出上述代码实现了占空比从0% → 1.56% → 3.12% → 6.25% → 12.5% → 25% → 12.5% → 6.25% → 3.12% → 1.56% → 0%的等时间间隔变化,
由于开发板LED灯功率较高,在25%到100%之间的占空比,LED灯亮度都会很高,看不出太大变化,
因此我们将25%设定为了最高的占空比,对应LED灯最亮的时刻。
至此,Control Unit的设计就介绍完了,下面我们将演示系统在FPGA开发板上的实际运行视频
5. FPGA验证
本项目所用开发板型号:XC7A35T-2FGG484I
上板验证视频:
(视频制作中,录制完会第一时间更新视频链接)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4