首页 > 其他分享 >AMBA总线(2)—— APB代码设计

AMBA总线(2)—— APB代码设计

时间:2023-04-02 16:16:28浏览次数:40  
标签:pready cmd 总线 vld reg wDATA APB parameter AMBA

1 前言

(没错,本文只是个学习笔记,只是把实验过程记录一下,并非原创~)

2 代码设计

【四人独行】的原版代码也是跑得通的,但是我感觉有些冗余,而且不能像手册一样连续不断的2拍传1个数据,所以我稍微改变了下。

2.1 APB设计

module apb
//========================< parameter >=====================================
#(
parameter WR                = 1'b1                  ,
parameter RD                = 1'b0                  ,
//---------------------------------------------------
parameter wRW               = 1                     ,
parameter wADDR             = 16                    ,
parameter wDATA             = 32                    ,
parameter wCMD              = wRW + wADDR + wDATA
)
//========================< port >==========================================
(
//-- clk rst ----------------------------------------
input                       pclk_i                  ,
input                       prst_n_i                ,
//-- cmd_in -----------------------------------------
input      [wCMD-1:0]       cmd_i                   ,
input                       cmd_vld_i               ,
//-- apb --------------------------------------------
output reg [wADDR-1:0]      paddr_o                 ,
output reg                  pwrite_o                ,
output reg                  psel_o                  ,
output reg                  penable_o               ,
output reg [wDATA-1:0]      pwdata_o                ,
input      [wDATA-1:0]      prdata_i                ,
input                       pready_i                ,
input                       pslverr_i
);
//========================< parameter >=====================================
parameter IDLE              = 3'b001                ;
parameter SETUP             = 3'b010                ;
parameter ACCESS            = 3'b100                ;
//========================< signal >========================================
reg [2:0]                   cur_state               ;
reg [2:0]                   nxt_state               ;
wire                        start_flag = cmd_vld_i && pready_i;
//==========================================================================
//==    FSM
//==========================================================================
always @ (posedge pclk_i or negedge prst_n_i) begin
  if (!prst_n_i) begin
    cur_state <= IDLE;
  end
  else begin
    cur_state <= nxt_state;
  end
end

always @ (*) begin
    case(cur_state)
    IDLE  : begin
                if(start_flag)
                    nxt_state = SETUP;
                else
                    nxt_state = IDLE;
            end
    SETUP : begin
                nxt_state = ACCESS;
            end
          
    ACCESS: begin
                if (!pready_i)
                    nxt_state = ACCESS;
                else if(start_flag)
                    nxt_state = SETUP;
                else if(!cmd_vld_i && pready_i)
                    nxt_state = IDLE;
            end
    endcase
end
//==========================================================================
//==    update output
//==========================================================================
always @ (posedge pclk_i or negedge prst_n_i) begin
    if (!prst_n_i) begin
        pwrite_o  <= 1'b0;
        psel_o    <= 1'b0;
        penable_o <= 1'b0;
        paddr_o   <= {(wADDR){1'b0}};
        pwdata_o  <= {(wDATA){1'b0}};
    end
    
    else if (cur_state == IDLE) begin
        psel_o    <= 1'b0;
        penable_o <= 1'b0;
    end
    
    else if(cur_state == SETUP)begin
        psel_o    <= 1'b1;
        penable_o <= 1'b0;
        paddr_o   <= cmd_i[wCMD-wRW-1:wDATA];
        //-- read
        if(cmd_i[wCMD-1] == RD)begin
            pwrite_o <= 1'b0;
        end
        //-- write
        else if(cmd_i[wCMD-1] == WR) begin
            pwrite_o <= 1'b1;
            pwdata_o <= cmd_i[wDATA-1:0];
        end
    end
    else if(cur_state == ACCESS)begin
        penable_o <= 1'b1;
    end
end



endmodule

2.2 Testbench设计

testbench里用上了原文本来注释掉的task,使得设计更为简单,task里小小修改了 pready_i 的激励,使得只需2个周期就能发送一个数据。整个testbench发送了2读2写,然后空闲2个周期,再发送2读2写。

`timescale 1ns/1ps  //时间精度
`define    Clock 20 //时钟周期

module apb_tb;
//========================< parameter >=====================================
parameter WR                = 1'b1                  ;
parameter RD                = 1'b0                  ;
//---------------------------------------------------
parameter wRW               = 1                     ;
parameter wADDR             = 16                    ;
parameter wDATA             = 32                    ;
parameter wCMD              = wRW + wADDR + wDATA   ;
//========================< signal >========================================
reg                         pclk_i                  ;
reg                         prst_n_i                ;
//---------------------------------------------------
reg  [wCMD-1:0]             cmd_i                   ;
reg                         cmd_vld_i               ;
//---------------------------------------------------
wire [wADDR-1:0]            paddr_o                 ;
wire                        pwrite_o                ;
wire                        psel_o                  ;
wire                        penable_o               ;
wire [wDATA-1:0]            pwdata_o                ;
reg  [wDATA-1:0]            prdata_i                ;
reg                         pready_i                ;
reg                         pslverr_i               ;
//==========================================================================
//==    instantiation
//==========================================================================
apb
#(
    .WR                     (WR                     ),
    .RD                     (RD                     ),
    .wRW                    (wRW                    ),
    .wADDR                  (wADDR                  ),
    .wDATA                  (wDATA                  ),
    .wCMD                   (wCMD                   )
)
u_apb
(
    .pclk_i                 (pclk_i                 ),
    .prst_n_i               (prst_n_i               ),
    .cmd_i                  (cmd_i                  ),
    .cmd_vld_i              (cmd_vld_i              ),
    .paddr_o                (paddr_o                ),
    .pwrite_o               (pwrite_o               ),
    .psel_o                 (psel_o                 ),
    .penable_o              (penable_o              ),
    .pwdata_o               (pwdata_o               ),
    .prdata_i               (prdata_i               ),
    .pready_i               (pready_i               ),
    .pslverr_i              (pslverr_i              )
);
//==========================================================================
//==    clock
//==========================================================================
initial begin
    pclk_i = 1;
    forever
        #(`Clock/2) pclk_i = ~pclk_i;
end
//==========================================================================
//==    simulation
//==========================================================================
initial begin
    cmd_rst;
    cmd_wr(49'h1_1122_11223344);
    cmd_wr(49'h1_3344_55667788);
    cmd_rd(49'h0_5566_a1a2a3a4);
    cmd_rd(49'h0_7788_b5b6b7b8);
    #(`Clock*2);
    cmd_wr(49'h1_1122_11223344);
    cmd_wr(49'h1_3344_55667788);
    cmd_rd(49'h0_5566_a1a2a3a4);
    cmd_rd(49'h0_7788_b5b6b7b8);
    #(`Clock*2);
    $finish;
end
//==========================================================================
//==    task
//==========================================================================
task cmd_rst;
    begin
        cmd_i           = 0;
        cmd_vld_i       = 0;
        prdata_i        = 0;
        pready_i        = 1;
        pslverr_i       = 0;
        prst_n_i        = 0; #(`Clock);
        prst_n_i        = 1;
    end
endtask

task cmd_wr;
    input [wCMD-1:0] data;
    begin
                    cmd_i       = data;
                    cmd_vld_i   = 1;
        #(`Clock)   cmd_vld_i   = 0;
                    pready_i    = 0;
        #(`Clock)   pready_i    = 1;
    end
endtask

task cmd_rd;
    input [wCMD-1:0] data;
    begin
                    cmd_i       = data;
                    cmd_vld_i   = 1;
        #(`Clock)   cmd_vld_i   = 0;
                    pready_i    = 0;
        #(`Clock)   pready_i    = 1;
                    prdata_i    = cmd_i[wDATA-1:0];
    end
endtask
//==========================================================================
//==    fsdb
//==========================================================================
initial begin
  $fsdbDumpfile("tb_top.fsdb");
  $fsdbDumpvars;
  $fsdbDumpMDA;
end


endmodule

3 EDA环境

3.1 EDA环境设置

EDA虚拟机直接用的【芯王国】的 IC_EDA_lite虚拟机(精简版本),安装后直接能用,出处:https://blog.csdn.net/weixin_40377195/article/details/124899571

根据自己习惯,我对.bashrc文件进行了小小修改,增加了terminal颜色和一些alias文件,如下所示:

最终实现的效果如下所示:

3.2 Win与Linux文件共享

有些代码是在windows下写的,需要传到Linux里。或者Linux里写好的,需要传到Windows下,可以用这个方法:

(1)windows下找个喜欢的位置新建文件夹,命名按自己喜好即可。

(2)点击VMware的菜单栏:虚拟机 -- 设置。

(3)点击选项--共享文件夹,设置为总是启用,修改文件夹位置,选至刚刚创建的文件夹即可。

(4)Linux中该文件夹的路径如下所示。我在设置时选择的文件夹名称为ExShare,如果你选的是别的名称,那这里会相应的改变。

3.3 makefile

makefile文件没有用博主【四人独行】的版本,因为没跑通......于是拿着EDA虚拟机自带的例程里的makefile改了下。由于这次实验比较简单,就2个.v文件,我不想新增一个filelist文件,于是直接在makefile第一行指明文件路径。如下所示:

filelist = ./*.v
#----------------------------------------------------------------------------------------------------
all: clean vcs verdi
#----------------------------------------------------------------------------------------------------
vcs: 
	vcs  \
		${filelist} \
		-timescale=1ns/1ps \
		-full64  -R  +vc  +v2k  -sverilog -debug_access+all \
		|tee vcs.log
#----------------------------------------------------------------------------------------------------
verdi:
	verdi ${filelist} -ssf ./*.fsdb &

clean:
	 rm  -rf  *~  core  csrc  simv*  vc_hdrs.h  ucli.key  urg* *.log  novas.* *.fsdb* verdiLog  64* DVEfiles *.vpd

该makefile文件在执行 make 后,会自动执行 clean、vcs、verdi。如果不想自动执行verdi,可以把 all:后的verdi字去掉,想执行了就再在terminal敲 make verdi。

3.4 EDA仿真

整个实验就用到了如下3个文件:

然后在terminal执行 make,根据上面的makefile文件,系统就自动调用vcs编译和仿真,之后自动打开verdi并加载fsdb文件了。如果编译仿真出错,terminal中会显示错误原因,有vcs的log文件可以查看。同时因为没有生成fsdb文件,verdi打开也无法正常加载波形,也会报错。如果一切正常,verdi的波形显示如下:

蓝色部分是输入的激励,黄色部分是用来看写传输,粉色部分是用来看读传输。从波形中可以看出,两次的两写两读和APB数据手册中描述的一致。

整个实验结束后,文件夹中多了很多相关文件,看起来很乱,可以执行一下 make clean ,将这些文件删除,回到最初的样子。也可以不管,反正下次执行 make 时,第一步就会自动执行一下 clean。

 

参考资料:

[1] 芯王国:https://blog.csdn.net/weixin_40377195/article/details/124899571

[2] 四人独行:https://zhuanlan.zhihu.com/p/607964532

标签:pready,cmd,总线,vld,reg,wDATA,APB,parameter,AMBA
From: https://www.cnblogs.com/xianyufpga/p/17279235.html

相关文章

  • Linux系统下Samba服务器的配置
     实训目的:掌握Samba服务器的主配置文件的设置;掌握Samba服务用户的添加及权限设置;掌握Samba客户机的应用。实训环境:操作系统为CentOS7的网络服务器。实训步骤:第1步:将目录/home/media设置为允许所有用户访问,但仅允许用户mary具有修改该目录的权限。其配置步骤简述如下。1)......
  • AMBA总线(1)—— APB手册翻译
    APB是最简单的AMBA总线了,它多用于低速外围设备。相比AHB和AXI,有两个很不一样的点:不能outstanding传输,数据有效时,其地址必然是当前数据的对应地址。不能流水线式传输,必须至少2个周期传输一个数据,PSEL起来然后PENABLE起来。1前言1.2APB版本1998年发布的APBSpecification......
  • C#上位机开发源码 上位机项目源代码 采用基于RS485通讯总线的ModbusRtu协议
    C#上位机开发源码上位机项目源代码采用基于RS485通讯总线的ModbusRtu协议,支持用户权限管理、sqlite数据库、实时曲线、历史曲线、历史报表、导出Excel、主界面布局可调带记忆等功能YID:81150611746679046......
  • Mapboxgl draw 自定义标绘:圆、矩形、自由多边形、上传读取geojson
    还没做文字标绘,累了,以后有需要有机会再说自定义标绘方法Mapboxgl标绘相关库我当前使用的版本是:"@mapbox/mapbox-gl-draw":"^1.4.1","@mapbox/mapbox-gl-draw-static-mode":"^1.0.1","mapbox-gl-draw-circle":"^1.1.2",&quo......
  • Ambari 服务配置以及 Alert 详解
    AmbariAlert(告警)简介Ambari告警的基础概念Ambari为了帮助用户鉴别以及定位集群的问题,实现了告警(Alert)机制。在Ambari中预定了很多的告警,这些告警被用于监测集群的各......
  • mapbox-gl实战教程:加载各种底图技巧
    在地图开发中,加载底图是一个最基本的操作,使用在线开放底图、自己发布的底图、或是客户提供的给底图服务,都存在一定的规律,掌握规律就能顺利的进行底图加载,本文通过以下四个方......
  • lambad表达式案例
    案例1:  代码实现:package黑马程序员;importjava.util.Arrays;importjava.util.Comparator;publicclasstext{publicstaticvoidmain(String[]args){......
  • 在Vue3+TypeScript 前端项目中使用事件总线Mitt
    事件总线Mitt使用非常简单,本篇随笔介绍在Vue3+TypeScript前端项目中使用的一些场景和思路。我们在Vue的项目中,经常会通过emits 触发事件来通知组件或者页面进行相应的处......
  • AMBA总线基本知识
    AMBA:AdvancedMicrocontrollerBusArchitecture,高级微控制器总线架构,是ARM提出的一种片上高速总线架构,包括AHB、APB、AXIAHB:AdvancedHigh-performanceBus,高级高......
  • 关于CAN总线杂记之一
     1、关于显性dominant隐形recessive。  can总线上挂了很多can设备,只有当左右设备都不发送信息的时候,can总线才显示隐形。否则,只要有任何一个设备发送信息,can总......