FPGA项目——基于AMBA总线的流水灯控制系统

打印 上一主题 下一主题

主题 996|帖子 996|积分 2988

绪论

本文将介绍一个完全用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这个周期分别称为IDLESETUPENABLE
于是我们得到AHB2APB Bridge的状态机转换示意图:

注意:在T2-T4传输Data1后,APB总线又在T4-T6两个周期内完成了Data2的传输,
这两次传输之间并没有经历IDLE状态,这表明当AHB总线上仍然有传输任务时,ENABLE状态结束后会直接再次跳往SETUP状态以准备下一个数据的传输,
AHB2APB Bridge模块完整RTL如下:
  1. module AHB2APB_bridge #(
  2.     //HRANS Parameters
  3.     parameter    IDLE   = 2'b00  ,
  4.     parameter    BUSY   = 2'b01  ,
  5.     parameter    SEQ    = 2'b10  ,
  6.     parameter    NONSEQ = 2'b11  ,
  7.     //HRSP Parameters
  8.     parameter    OKAY  = 2'b00   ,
  9.     parameter    ERROR = 2'b01   ,
  10.     parameter    SPLIT = 2'b10   ,
  11.     parameter    RETRY = 2'b11   ,
  12.     //bridge_state Parameters
  13.     parameter    BRIDGE_IDLE   = 2'b00,
  14.     parameter    BRIDGE_SETUP  = 2'b01,
  15.     parameter    BRIDGE_ENABLE = 2'b10,
  16.     //ADDR Parameters
  17.     parameter    ADDR_GPIO_0   = 32'h0000_0000,
  18.     parameter    ADDR_GPIO_1   = 32'h0000_8000
  19.    
  20. ) (
  21.     //------------ AHB ------------
  22.     //input
  23.     input iHCLK,
  24.     input iHRESETn,
  25.     input iHSEL,
  26.     input  [1:0]  iHTRANS,
  27.     input  [3:0]  iHSIZE ,
  28.     input  [2:0]  iHBURST,
  29.     input         iHWRITE,
  30.     input  [31:0] iHWDATA,
  31.     input  [31:0] iHADDR ,
  32.     //output
  33.     output        oHREADY,
  34.     output [1:0]  oHRESP ,
  35.     output [31:0] oHRDATA,
  36.     //------------ APB ------------
  37.     //input
  38.     input [31:0] iPRDATA,
  39.     //output
  40.     output oPSEL0,
  41.     output oPSEL1,
  42.     output oPWRITE,
  43.     output oPENABLE,
  44.     output [31:0] oPADDR,
  45.     output [31:0] oPWDATA
  46. );
  47.     /*————————————————————————————————————————————————————————————————————————*\
  48.     /                            AHB Signal Register                           \
  49.     \*————————————————————————————————————————————————————————————————————————*/
  50.     reg         iHWRITE_r ;
  51.     reg  [31:0] iHADDR_r  ;
  52.     reg  [31:0] iHWDATA_r ;
  53.     always@( posedge iHCLK)  begin
  54.         if(!iHRESETn) begin
  55.             iHWRITE_r <= 1'b0;
  56.             iHADDR_r  <= 32'b0;
  57.             iHWDATA_r <= 32'b0;
  58.         end
  59.         else if( iHSEL && (bridge_state == BRIDGE_IDLE || bridge_state == BRIDGE_ENABLE))begin
  60.             iHWRITE_r <= iHWRITE; // ahb reg change when bridge_state is going to change:
  61.             iHADDR_r  <= iHADDR ; // from IDLE   to SETUP                     
  62.             iHWDATA_r <= iHWDATA; // from ENABLE to SETUP
  63.         end
  64.     end
  65.     /*————————————————————————————————————————————————————————————————————————*\
  66.     /                                 Bridge FSM                               \
  67.     \*————————————————————————————————————————————————————————————————————————*/
  68.     reg [1:0] bridge_state;
  69.     always @(posedge iHCLK ) begin
  70.         if (!iHRESETn) begin
  71.             bridge_state <= BRIDGE_IDLE;
  72.         end else begin
  73.             case ( bridge_state )
  74.                 // IDLE
  75.                 BRIDGE_IDLE: begin
  76.                     if(iHSEL) begin
  77.                         bridge_state <= BRIDGE_SETUP;
  78.                     end
  79.                     else begin
  80.                         bridge_state <= BRIDGE_IDLE;
  81.                     end                     
  82.                 end
  83.                 // SETUP
  84.                 BRIDGE_SETUP: begin
  85.                     bridge_state <= BRIDGE_ENABLE;
  86.                 end
  87.                 // ENABLE
  88.                 BRIDGE_ENABLE: begin
  89.                     if(iHSEL) begin
  90.                         bridge_state <= BRIDGE_SETUP;
  91.                     end
  92.                     else begin
  93.                         bridge_state <= BRIDGE_IDLE;
  94.                     end
  95.                 end
  96.                 //DEFAULT
  97.                 default: bridge_state <= BRIDGE_IDLE;
  98.             endcase
  99.         end
  100.     end
  101.     /*————————————————————————————————————————————————————————————————————————*\
  102.     /                               AHB Slave Output                           \
  103.     \*————————————————————————————————————————————————————————————————————————*/
  104.     assign oHREADY = ( bridge_state == BRIDGE_SETUP ) ? 1'b0 : 1'b1;
  105.     assign oHRESP  = OKAY ;
  106.     assign oHRDATA = iPRDATA;
  107.     /*————————————————————————————————————————————————————————————————————————*\
  108.     /                               APB Master Output                          \
  109.     \*————————————————————————————————————————————————————————————————————————*/
  110.     assign oPSEL0   = ( iHADDR_r == ADDR_GPIO_0) ? 1'b1 : 1'b0;
  111.     assign oPSEL1   = ( iHADDR_r == ADDR_GPIO_1) ? 1'b1 : 1'b0;
  112.     assign oPWRITE  = iHWRITE_r ;
  113.     assign oPENABLE = ( bridge_state == BRIDGE_ENABLE ) ? 1'b1 : 1'b0;
  114.     assign oPADDR   = iHADDR_r;
  115.     assign oPWDATA  = iHWDATA_r;
  116. endmodule
复制代码
最后是呼吸灯模式(工作模式3),
该模式相对复杂一些,前面提到过是通过改变占空比控制LED灯亮度的,
占空比的改变是通过对timer的判断实现的,
举个例子:
  1.     reg [31:0] oGPIOout ;
  2.     always @(*) begin
  3.         for ( i=0 ; i<32 ; i=i+1 ) begin
  4.             if( reg_DIRM[i] & reg_OEN[i] ) begin //output mode
  5.                 oGPIOout[i] = reg_DATA[i] ;
  6.             end else begin
  7.                 oGPIOout[i] = 1'bz;
  8.             end
  9.         end
  10.     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%,
这样就有:
  1.             for ( i=0 ; i<32 ; i=i+1 ) begin
  2.                 if ( reg_DIRM[i] ) begin
  3.                     reg_DATA_RO[i] <= oGPIOout[i] ;// output mode
  4.                 end else begin
  5.                     reg_DATA_RO[i] <= iGPIOin[i] ;// input mode      
  6.                 end  
  7.             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
上板验证视频:
(视频制作中,录制完会第一时间更新视频链接)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表