0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SoC入门:APBmaster设计主站设计原理与实践

ruikundianzi 来源:知乎 2024-01-24 14:33 次阅读

大家不要以为APB的master和slave很简单,不需要了解。这是大错特错,为什么呢?

不过设计什么模块,你都要让它挂在标准总线上,比如你设计DMA,你就同时需要了解AMBA的master和slave设计。又比如你是设计算法计算模块,你的数据肯定要放到sram,你当然也要了解AMBA的master设计,将数据传输到crossbar上,进而放到指定memory。又比如SOC设计,肯定需要各种bridge,假设一个AHB2APB,你就同时需要了解AHB slave和APB master。

以APB为例,还是因为APB简单,但是我们可以从它学到设计的方法和思路。

既然是设计就需要spec和状态机。

设计spec如下

1、模块规划

5dfad054-ba6e-11ee-8b88-92fbcf53809c.jpg

模块diagram

2、接口描述

5e0c910e-ba6e-11ee-8b88-92fbcf53809c.jpg

接口描述

3、时序描述

读时序

5e32259a-ba6e-11ee-8b88-92fbcf53809c.jpg

读时序

写时序

5e4577c6-ba6e-11ee-8b88-92fbcf53809c.jpg

写时序

4、FSM

就是之前讲的APB协议状态机。如下图

5e5a7360-ba6e-11ee-8b88-92fbcf53809c.png

APB FSM

模块规划有了,接口有了,时序有了,状态机有了,就可以开始设计coding了,代码如下:

module apb
#(
  parameter RD_FLAG        = 8'b0           ,
  parameter WR_FLAG        = 8'b1           ,
  parameter CMD_RW_WIDTH   = 8              ,
  parameter CMD_ADDR_WIDTH = 16             ,
  parameter CMD_DATA_WIDTH = 32             ,
  parameter CMD_WIDTH      = CMD_RW_WIDTH   + 
                             CMD_ADDR_WIDTH + 
                             CMD_DATA_WIDTH
)(
//-- clkrst signal
  input                           pclk_i       ,
  input                           prst_n_i     ,


//-- cmd_in
  input      [CMD_WIDTH-1:0]      cmd_i        ,
  input                           cmd_vld_i    ,
  output reg [CMD_DATA_WIDTH-1:0] cmd_rd_data_o,


//-- apb interface
  output reg [CMD_ADDR_WIDTH-1:0] paddr_o      ,
  output reg                      pwrite_o     ,
  output reg                      psel_o       ,
  output reg                      penable_o    ,
  output reg [CMD_DATA_WIDTH-1:0] pwdata_o     ,
  input      [CMD_DATA_WIDTH-1:0] prdata_i     ,
  input                           pready_i     ,
  input                           pslverr_i
);


//-- FSM state
parameter IDLE   = 3'b001;
parameter SETUP  = 3'b010;
parameter ACCESS = 3'b100;


//-- current state and next state
reg [2:0] cur_state;
reg [2:0] nxt_state;


//-- data buf
reg                      start_flag     ;
reg [CMD_WIDTH-1:0]      cmd_in_buf     ;
reg [CMD_DATA_WIDTH-1:0] cmd_rd_data_buf;




/*-----------------------------------------------
 --             update cmd_in_buf              --
-----------------------------------------------*/
always @ (posedge pclk_i or negedge prst_n_i) begin
  if (!prst_n_i) begin
    cmd_in_buf <= {(CMD_WIDTH){1'b0}};
  end
  else if (cmd_vld_i && pready_i) begin
    cmd_in_buf <= cmd_i;
  end
end


/*-----------------------------------------------
 --             start flag of transfer         --
-----------------------------------------------*/
always @ (posedge pclk_i or negedge prst_n_i) begin
  if (!prst_n_i) begin
    start_flag <= 1'b0;
  end
  else if (cmd_vld_i && pready_i) begin
    start_flag <= 1'b1;
  end
  else begin
    start_flag <= 1'b0;
  end
end


/*-----------------------------------------------
 --           update current state             --
-----------------------------------------------*/
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


/*-----------------------------------------------
 --               update next state            --
-----------------------------------------------*/
always @ (*) begin
  case(cur_state)
    IDLE  :if(start_flag)begin
             nxt_state = SETUP;
           end
           else begin
             nxt_state = IDLE;
           end


    SETUP :nxt_state = ACCESS;
          
    ACCESS:if (!pready_i)begin
             nxt_state = ACCESS;
           end
           else if(start_flag)begin
             nxt_state = SETUP;
           end
           else if(!cmd_vld_i && pready_i)begin
             nxt_state = IDLE;
           end
  endcase
end


/*-----------------------------------------------
 --         update signal of 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   <= {(CMD_ADDR_WIDTH){1'b0}};
    pwdata_o  <= {(CMD_DATA_WIDTH){1'b0}};
  end
  
  else if (nxt_state == IDLE) begin
    psel_o    <= 1'b0;
    penable_o <= 1'b0;
  end


  else if(nxt_state == SETUP)begin
    psel_o    <= 1'b1;
    penable_o <= 1'b0;
    paddr_o   <= cmd_in_buf[CMD_WIDTH-CMD_RW_WIDTH-1:CMD_DATA_WIDTH];
    //-- read
    if(cmd_in_buf[CMD_WIDTH-1:CMD_WIDTH-8] == RD_FLAG)begin
      pwrite_o <= 1'b0;
    end
    //-- write
    else begin
      pwrite_o  <= 1'b1;
      pwdata_o  <= cmd_in_buf[CMD_DATA_WIDTH-1:0];
    end
  end


  else if(nxt_state == ACCESS)begin
    penable_o <= 1'b1;
  end
end


/*-----------------------------------------------
 --            update cmd_rd_data_buf          --
-----------------------------------------------*/
always @ (posedge pclk_i or negedge prst_n_i) begin
  if (!prst_n_i) begin
    cmd_rd_data_buf <= {(CMD_DATA_WIDTH){1'b0}};
  end
  else if (pready_i && psel_o && penable_o) begin
    cmd_rd_data_buf <= prdata_i;
  end
end


/*-----------------------------------------------
 --            update cmd_rd_data_o            --
-----------------------------------------------*/
always @ (posedge pclk_i or negedge prst_n_i) begin
  if (!prst_n_i) begin
    cmd_rd_data_o <= {(CMD_DATA_WIDTH){1'b0}};
  end
  else begin
    cmd_rd_data_o <= cmd_rd_data_buf;
  end
end


endmodule

模块设计的比较简单,只是实现APB的基本功能。下面讲一下设计重点:

·一定要做好功课在开始coding。

·Flow control,APB的上级模块,需要给到流控信号,告知APB master什么时候开始传输,什么时候结束。

·FSM,必须完全遵循AMBA的datasheet。

·时序对齐,和FSM一样,接口时序要和APB协议对齐。

·重点中的重点,pready的反压一定要逐级反压,不能直接送到APB master的上次模块,这样会丢数据。

testbench如下

`timescale 1ns/1ns
module tb_apb;
  reg         pclk_i       ;
  reg         prst_n_i     ;
                          
  reg  [55:0] cmd_i        ;
  reg         cmd_vld_i    ;
  wire [31:0] cmd_rd_data_o;
                          
  wire [15:0] paddr_o      ;
  wire        pwrite_o     ;
  wire        psel_o       ;
  wire        penable_o    ;
  wire [31:0] pwdata_o     ;
  reg  [31:0] prdata_i     ;
  reg         pready_i     ;
  reg         pslverr_i    ;


initial begin
 // rst; 
  pclk_i   = 0;
  prst_n_i = 1;
  pslverr_i = 0;
  cmd_i = 56'b0;
  cmd_vld_i = 0;
  prdata_i = 32'b0;
  pready_i = 1;
  #20 prst_n_i = 0;
  #20 prst_n_i = 1;


 // cmd_in_wr(cmd_i,56'h01_FF_EE_DD_CC_BB_AA);
    cmd_i     = 56'h01_FF_EE_DD_CC_BB_AA;
    cmd_vld_i = 1   ;
    #20 cmd_vld_i = 0;
    #31 pready_i = 0;
    #80 pready_i = 1;


  #90;
  //cmd_in_rd(cmd_i,56'h00_AA_BB_CC_DD_EE_FF,prdata_i,32'h12_34_56_78);
    cmd_i = 56'h00_AA_BB_CC_DD_EE_FF;
    cmd_vld_i = 1;
    #20 cmd_vld_i = 0;
    #30 pready_i = 0;


    #60 pready_i = 1;
        prdata_i = 32'h12_34_56_78;


    cmd_i = 56'h00_AA_BB_CC_DD_EE_FF;
    cmd_vld_i = 1;
    #20 cmd_vld_i = 0;
    #30 pready_i = 0;


    #50 pready_i = 1;
        prdata_i = 32'h11_22_33_44;




end


always #10 pclk_i = ~pclk_i;


//-- RST
task rst;
  begin
    pclk_i   = 1;
    prst_n_i = 1;
    pslverr_i = 0;
    cmd_i = 56'b0;
    cmd_vld_i = 0;
    prdata_i = 32'b0;
    pready_i = 1;
    #20 prst_n_i = 0;
    #10 prst_n_i = 1;
    //cmd_i = 56'h01_FF_EE_DD_CC_BB_Ab;
  end
endtask


//-- write
task cmd_in_wr;
  output [55:0] cmd;
  input  [55:0] data;


  begin
    cmd     = data;
    cmd_vld_i = 1   ;
    #20 cmd_vld_i = 0;
    #20 pready_i = 0;
    #40 pready_i = 1;
  end
endtask


//-- read
task cmd_in_rd;
  output [55:0] cmd;
  input  [55:0] data ;
  output [31:0] prdata;
  input  [31:0] rd_data;


  begin
    cmd = data;
    cmd_vld_i = 1;
    #20 cmd_vld_i = 0;
    #20 pready_i = 0;
    #40 pready_i = 1;
        prdata = rd_data;
  end
endtask
initial begin
  #1000 $finish;
end
apb tb_apb(
            .pclk_i       (pclk_i       ),
            .prst_n_i     (prst_n_i     ),
            .cmd_i        (cmd_i        ),
            .cmd_vld_i    (cmd_vld_i    ),
            .cmd_rd_data_o(cmd_rd_data_o),
            .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    )
          );




initial begin
  $fsdbDumpfile("apb.fsdb");
  $fsdbDumpvars            ;
  $fsdbDumpMDA             ;
end


endmodule

makefile如下:

LAB_DIR = /home/*/apb




DFILES = $(LAB_DIR)/*.v 


all:clean elab rung
elab:
  vcs -full64 -LDFLAGS -Wl,-no-as-needed -debug_acc+all -timescale=1ns/1ns 
  -fsdb -sverilog -l comp.log 
  ${DFILES}


run:
  ./simv -l run.log


rung:
  ./simv -gui -l run.log


verdi:
  verdi ${DFILES} 
  -ssf ./*.fsdb &


clean:
  rm -rf  AN.DB 
  rm -rf  DVEfiles 
  rm -rf  csrc 
  rm -rf  simv.* 
  rm -rf  *simv 
  rm -rf  inter.vpd 
  rm -rf  ucli.key 
  rm -rf  *.log 
  rm -rf  verdiLog 
  rm -rf  novas* 
rm-rf*.fsdb

下面是仿真结果

5e7808da-ba6e-11ee-8b88-92fbcf53809c.jpg

好了,今天讲的主要就这么多,这个是基础,但也是干货,对以后设计AHB,AXI乃至NOC都非常有帮助。

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据传输
    +关注

    关注

    9

    文章

    1830

    浏览量

    64465
  • soc
    soc
    +关注

    关注

    38

    文章

    4116

    浏览量

    217903
  • 状态机
    +关注

    关注

    2

    文章

    492

    浏览量

    27470

原文标题:SoC设计入门 - APB master设计(接口类基础思维)

文章出处:【微信号:IP与SoC设计,微信公众号:IP与SoC设计】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Modbus多问题

    ModbusRTU两之间如何交互数据? 工业控制中,一般都是主从通讯方式居多,有时也会碰到两个之间通讯。例如某工作站上位机(
    发表于 11-24 16:36

    PLC和从分享

    总站是主控制单元,含有CPU,从可以不加CPU,可以作为远程,用控制。
    发表于 07-02 08:20

    CAN与从的功能是什么

    基于 STM32 和 CAN总线的温度监控系统的设计,通过上位机与下位机的通信,实现对温度数据的监控,并经初步实验达到了设计的要求。1 系统总体方案概述系统总体框图如图 1 所示,本系统采用+从
    发表于 08-19 07:47

    PLC和从具有哪些功能

    PLC具有哪些功能?PLC从具有哪些功能?
    发表于 09-29 07:22

    通过Mbus给从发送信息,无法收到信息怎么解决?

    通过Mbus给从发送信息,无法收到信息怎么解决?
    发表于 01-20 06:42

    lightech mbus完整指令库

    lightech mbus完整指令库
    发表于 10-09 06:20

    Profibus-DP通信平台的设计方案

    Profibus协议的结构以OSI为参考模型,DP使用第1层、第2层和用户接口,第3层到第7层未加以描述。一个DP系统既可以是一个单结构,也可以是一个多结构,本文涉及的是多
    的头像 发表于 12-30 10:04 5845次阅读
    Profibus-DP<b class='flag-5'>主</b><b class='flag-5'>站</b>通信平台的设计方案

    GitHub入门实践

    GitHub入门实践
    发表于 11-21 10:40 0次下载

    MELSEC iQ R CC Link系统/本地模块用户手册(入门篇)

    MELSEC iQ-R CC-link系统/本地模块用户手册(入门篇) 产品规格书
    发表于 08-25 09:30 0次下载
    MELSEC iQ R CC Link系统<b class='flag-5'>主</b><b class='flag-5'>站</b>/本地<b class='flag-5'>站</b>模块用户手册(<b class='flag-5'>入门</b>篇)

    MELSEC iQ R AnyWireASlink模块用户手册(入门篇)

    MELSEC iQ-R AnyWireASlink模块用户手册(入门篇) 产品规格书
    发表于 08-26 15:26 0次下载
    MELSEC iQ R AnyWireASlink<b class='flag-5'>主</b><b class='flag-5'>站</b>模块用户手册(<b class='flag-5'>入门</b>篇)

    MELSEC iQ R DeviceNet/从模块用户手册(入门篇)

    MELSEC iQ-R DeviceNet /从模块用户手册(入门篇) 产品规格书
    发表于 08-26 12:01 0次下载
    MELSEC iQ R DeviceNet<b class='flag-5'>主</b><b class='flag-5'>站</b>/从<b class='flag-5'>站</b>模块用户手册(<b class='flag-5'>入门</b>篇)

    EtherCAT 方案横向对比

    函数库即可快速架构。 使用对象 强研发能力的团队   对成本考量不多,但对稳定性和售后服务看重的使用者。   想快速入门EtherCAT的初学者; 驱动器厂商,想降低
    的头像 发表于 04-25 16:01 1753次阅读

    EtherCAT方案之横向比较

    众所周知,EtherCAT是开源协议,使用者可以根据倍福提供的相关资料做产品开发,而目前主流的EtherCAT解决方案包括:软,系统型
    的头像 发表于 05-29 09:38 4116次阅读
    EtherCAT<b class='flag-5'>主</b><b class='flag-5'>站</b>方案之横向比较

    linmodbus RTU程序

    linmodbus RTU 程序
    发表于 08-09 15:37 0次下载

    通过Profinet转EtherCAT协议网关把profient从设备接入到EtherCAT设备中

    远创智控YC-PNM-ECT,一款基于西门子1200PLC的PROFINET通讯网关。 远创智控YC-PNM-ECT网关的主要功能是将ETHERCAT总线和PROFINET网络连接起来。它
    的头像 发表于 10-10 17:54 626次阅读
    通过Profinet<b class='flag-5'>主</b><b class='flag-5'>站</b>转EtherCAT协议网关把profient从<b class='flag-5'>站</b>设备接入到EtherCAT<b class='flag-5'>主</b><b class='flag-5'>站</b>设备中