APB总线详解及手撕代码

这篇具有很好参考价值的文章主要介绍了APB总线详解及手撕代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文的参考资料为官方文档AMBA™3 APB Protocol specification

文档下载地址: https://pan.baidu.com/s/1Vsj4RdyCLan6jE-quAsEuw?pwd=w5bi

提取码:w5bi

APB端口介绍

介绍总线具体握手规则之前,需要先熟悉一下APB总线端口,APB的端口如下:

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

大体可以分为以下三组:

系统信号:PCLK(系统时钟)、PRESETn(系统复位,低有效)

master信号:PADDR(地址信号,确定读写的地址)、PSELx(片选信号,拉出来接给搭载APB总线的slave,选中slave时,PSELx信号拉高)、PNEABLE(使能信号,在PSELx拉高一个周期后,必定拉高)、PWRITE(写使能信号,PWRITE为高时写有效为低时读有效)、PWDATA(写数据)

slave信号:PREADY(ready为高时,代表着一次APB数据传输的结束)、PRDATA(读数据)、PSLVERR(错误数据,由slave发出,具体逻辑由slave内部决定,当slave发现内部逻辑出现故障,譬如状态机状态出错、计数器数字异常等,slave都可以使用内部逻辑把该信号拉高,使得master接收到PSLVERR为高时,哪怕ready拉高表示APB结束了,也可以使master放弃该次传输或做出其他应对策略)。

APB写传输

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

如文档所示,APB的写分为两种情况:①没有等待状态的写②有等待状态的写

APB和AHB最大的不同就是APB不采用pipeline形式的写读方式,因此对于APB协议来说,最快的写入或者读出一个数据的周期是两周期,先给地址,再写数据;或者先给地址,再读数据。APB 协议文档中,将上述这种传输方式分为两个阶段(phase),给地址的阶段称为Set up phase;紧接着下一周期PENABLE信号拉高,标志着进入写/读数据的阶段,该阶段称为Access phase

Write with no wait states

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

一次没有等待状态的写传输如上图所示,计划写数据时,第一周期PSEL拉高,表示选中某个slave,同时给出地址信息Addr1和写入数据信息Data1,紧接着下一周期,PENABLE信号拉高,PREADY信号也拉高,这时数据写入完成。

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

没有等待状态的APB连续写波形如上所示(代码见后文),笔者将数据分为了两组,group1为APB slave的端口信号,group2为APB接的单端口SRAM信号。在第一个周期,也就是Setup phase,psel信号拉高,表示slave被选中,值得注意的是此时要将SRAM的写信号和使能信号同步拉高,因为我们写的是一个no wait states的APB接口,数据要在第二周期写进SRAM的话,就需要提前一拍拉高使能信号和写信号。然后到了第二周期,penable信号拉高,pready信号也拉高标志着这一次APB传输的结束。另外,也正是因为在setup phase我们把SRAM的en信号和we信号拉高了,因此在access phase数据传输结束的同时,数据也被写入到SRAM中。

Write with wait states

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档
apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

在文档中,对有等待周期的APB写传输描述如上,即:

一开始的setup phase和write with no wait没有区别,psel拉高,penable为低;紧跟着第二周期,penable拉高之后,进入access phase,进入access phase之后,penable不会拉低,直到pready为高标志着一次传输结束时,penable才会随着pready一起拉低。penable等待pready拉高的这段等待时间为additional cycles,在这个阶段PADDR、PWRITE、PSEL、PENABLE、PWDATA都应该保持不变,可以说总线被hold住了。

APB读传输

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

APB的读传输也分为两种情况:①没有等待状态的读②有等待状态的读

Read with no wait states

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

一次没有等待状态的读传输如上图所示,读状态和写状态不同,写数据时PWRITE=1,读数据时应该令PWRITE=0计划读数据时,第一周期PSEL拉高,表示选中某个slave,同时给出地址信息Addr1,紧接着下一周期,PENABLE信号拉高,PREADY信号也拉高,这时数据被读出,master接受到读出数据PRDATA。

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

上图为连续读的APB传输波形图,从第一次读数据可以看到,随着psel信号拉高,PWRITE=0标志着为读状态,此时传入地址给APB的SRAM,SRAM端口en=1,we=0标志着SRAM为读模式,数据在下一周期从SRAM给到prdata。

这边还要提一个APB的特点,也是大多人容易忽略的点,APB总线完成一次读传输或者写传输之后,PADDR和PWRITE不会改变,会一直维持到下一次的传输,这可以减少功耗。spec中描述如下:

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

手撕代码

笔者写了一个Write和Read都是with no states的APB SRAM,因为含有SRAM部分,所以在apb_sram中需要例化一个单端口ram,单端口ram代码如下:

dpram

module spram_generic#(
    parameter ADDR_BITS = 7,        //outside input 10
    parameter ADDR_AMOUNT = 128,    //outside input 1024
    parameter DATA_BITS = 32        //outside input 32
)(
    input                      clk     ,
    input                      en      ,
    input                      we      ,
    input      [ADDR_BITS-1:0] addr    ,
    input      [DATA_BITS-1:0] din     ,

    output reg [DATA_BITS-1:0] dout    
);
reg [DATA_BITS-1:0] mem [0:ADDR_AMOUNT-1];

always @(posedge clk)begin
    if(en)begin
        if(we == 1'b1)begin
            mem[addr] <= din;
        end
        else 
            dout      <= mem[addr];
    end
end

endmodule

apb_sram

module apb_sram#(
    parameter ADDR_BITS = 9,
    parameter DATA_BITS = 32,
    parameter MEM_DEPTH = 512
)(
    input                       pclk    ,
    input                       prstn   ,

    input                       psel    ,
    input                       penable ,

    input   [ADDR_BITS-1:0]     paddr   ,
    input                       pwrite  ,
    input   [DATA_BITS-1:0]     pwdata  ,

    output                      pready  ,
    output  [DATA_BITS-1:0]     prdata
);

// write part 
wire apb_write_setup;
reg  apb_ram_write;

assign apb_write_setup = (pwrite) && (!penable) && (psel);

always @(posedge pclk or negedge prstn)begin
    if(!prstn)begin
        apb_ram_write <= 1'b0; 
    end
    else if(apb_write_setup)begin
        apb_ram_write <= 1'b1;
    end
    else if(pready)begin
        apb_ram_write <= 1'b0;
    end
end

// read part
wire apb_read_setup;
reg  apb_ram_read;

assign apb_read_setup = (!pwrite) && (!penable) && (psel);

always @(posedge pclk or negedge prstn)begin
    if(!prstn)begin
        apb_ram_read <= 1'b0; 
    end
    else if(apb_read_setup)begin
        apb_ram_read <= 1'b1;
    end
    else if(pready)begin
        apb_ram_read <= 1'b0;
    end
end

assign pready = pwrite ? apb_ram_write : apb_ram_read;

wire mem_en,mem_we;
assign mem_en = apb_write_setup || apb_read_setup;
assign mem_we = apb_write_setup;

spram_generic #(
    .ADDR_BITS      (ADDR_BITS          ),
    .DATA_BITS      (DATA_BITS          ),
    .ADDR_AMOUNT    (2<<(ADDR_BITS-1)   )
)u_spram_generic(
    .clk    (pclk   ),
    .en     (mem_en ),
    .we     (mem_we ),
    .addr   (paddr  ),
    .din    (pwdata ),
    .dout   (prdata )
);

endmodule

tb

testbench例化apb_sram并给出激励,我这边在tb中发起了10次连续的随机写,然后再发起10次连续读,发现读出来的数据和写入的数据一致。

接着又测试了写和读无缝衔接在一起的apb传输,结果符合spec。tb代码如下:

`timescale 1ns/1ns
`define MEM_PATH u_apb_sram.u_spram_generic
module tb#(
    parameter ADDR_BITS = 9,
    parameter DATA_BITS = 32,
    parameter MEM_DEPTH = 512
)();

reg clk, rstn;
always #5 clk = ~clk;

reg                     psel, penable, pwrite;
reg     [DATA_BITS-1:0] pwdata, ref_data;
reg     [ADDR_BITS-1:0] paddr ;
wire                    pready;
wire    [DATA_BITS-1:0] prdata;

reg     [DATA_BITS-1:0] pwdata_rand;
reg     [DATA_BITS-1:0] prdata_read;

task apb_write;
input [ADDR_BITS-1:0] addr;
input [DATA_BITS-1:0] wdata;
begin
    @(posedge clk);#1;
    penable = 0; psel = 1; pwrite = 1; paddr = addr; pwdata = wdata;
    @(posedge clk);#1;
    penable = 1;
end
endtask

task apb_read;
input [ADDR_BITS-1:0] addr;
output [DATA_BITS-1:0] rdata;
begin
    @(posedge clk); #1;
    penable = 0; psel = 1; pwrite = 0; paddr = addr;
    @(posedge clk); #1;
    penable = 1;
    @(negedge clk); #1;
    rdata = prdata;
end
endtask

integer i,j;
initial begin
    clk     <= 1'b0;
    rstn    <= 1'b0;
    pwrite  <= 1'b1;
    psel    <= 1'b0;
    penable <= 1'b0;
    pwdata  <= 32'd0;
    repeat(2) @(posedge clk);
    rstn    <= 1'b1;
    repeat(3) @(posedge clk);
    // SRAM data initial
    for (i = 0; i < MEM_DEPTH; i = i + 1)begin
        pwdata = $random();
        `MEM_PATH.mem[i] = pwdata;
    end
    repeat(5) @(posedge clk); #1;
    $display("\ncontinuous writing");
    // SRAM data continuous writing
    fork 
        begin
            @(posedge clk);#1
            paddr = 32'd0; 
            for (j = 0; j < 10; j = j + 1)begin
                repeat(2) @(posedge clk) #1;
                paddr = paddr + 1;
                @(negedge clk) #1;
                ref_data = `MEM_PATH.mem[paddr-1];
                $display("ref_data = %d, addr = %d", ref_data, paddr-1);
            end
        end
        begin 
            for (i = 0; i < 10; i = i + 1)begin
                pwdata_rand = $random();
                apb_write(paddr, pwdata_rand);
                $display("pwdata = %d", pwdata);
            end
        end
    join_none
    
    
    repeat(21) @(posedge clk);#1;
    penable = 1'b0;psel = 1'b0;pwrite = 1'b0;
    
    repeat(5) @(posedge clk);#1;
    $display("\ncontinuous reading");
    //SRAM continuous reading
    fork 
        begin
            @(posedge clk);#1;
            paddr = 32'd0;
            for (j = 0; j < 10; j = j + 1)begin
                repeat(2) @(posedge clk);#1;
                paddr = paddr + 1;
            end
        end
        begin
            for (i = 0; i < 10; i = i + 1)begin
                apb_read(paddr, prdata_read);
                $display("prdata_read = %d", prdata_read);
            end
        end
    join
    penable = 0;psel = 0;
    
    repeat(5) @(posedge clk);#1;
    $display("\ncontinuos writing and reading");
    //SRAM continuous write and read
    fork 
        begin
            @(posedge clk);#1;
            paddr = 32'd0;
            for (j = 0; j < 10; j = j + 1)begin
                repeat (4) @(posedge clk); #1;
                paddr = paddr + 1;
            end
        end
        begin
            for (i = 0; i < 10; i = i + 1)begin
                pwdata_rand = $random();
                apb_write(paddr, pwdata_rand);
                apb_read(paddr, prdata_read);
                $display("write data is %d, read data is %d", pwdata_rand, prdata_read);
            end
        end
    join
    penable = 0;psel = 0;

    // finish simulation
    repeat(20) @(posedge clk);
    $finish();
end


initial begin
    $fsdbDumpfile("apb_sram.fsdb");
    $fsdbDumpvars(0);
end

apb_sram #(
    .ADDR_BITS(ADDR_BITS),
    .DATA_BITS(DATA_BITS),
    .MEM_DEPTH(MEM_DEPTH)
) u_apb_sram(
    .pclk   (clk    ),
    .prstn  (rstn   ),
    
    .psel   (psel   ),
    .penable(penable),
    .paddr  (paddr  ),
    .pwrite (pwrite ),
    .pwdata (pwdata ),

    .pready (pready ),
    .prdata (prdata )
);

endmodule

vcs仿真结果如下:

continuous writing
pwdata = 620927818
ref_data = 620927818, addr = 0
pwdata = 1557269945
ref_data = 1557269945, addr = 1
pwdata = 160312595
ref_data = 160312595, addr = 2
pwdata = 164115731
ref_data = 164115731, addr = 3
pwdata = 853295461
ref_data = 853295461, addr = 4
pwdata = 684074833
ref_data = 684074833, addr = 5
pwdata = 3684186807
ref_data = 3684186807, addr = 6
pwdata = 3432517785
ref_data = 3432517785, addr = 7
pwdata = 2635204666
ref_data = 2635204666, addr = 8
pwdata = 3102358129
ref_data = 3102358129, addr = 9

continuous reading
prdata_read = 620927818
prdata_read = 1557269945
prdata_read = 160312595
prdata_read = 164115731
prdata_read = 853295461
prdata_read = 684074833
prdata_read = 3684186807
prdata_read = 3432517785
prdata_read = 2635204666
prdata_read = 3102358129

continuos writing and reading
write data is 830211938, read data is 830211938
write data is 4063587044, read data is 4063587044
write data is 353623338, read data is 353623338
write data is 3201975421, read data is 3201975421
write data is 753819481, read data is 753819481
write data is 1925424101, read data is 1925424101
write data is 1994288109, read data is 1994288109
write data is 3836215497, read data is 3836215497
write data is 2695810113, read data is 2695810113
write data is 1472319919, read data is 1472319919

波形图

连续10次写、连续10次读、连续10次读写波形如下文章来源地址https://www.toymoban.com/news/detail-822767.html

apb总线代码,数字IC手撕代码,AMBA总线,数字IC,IC设计,手撕代码,FPGA,AMBA,Powered by 金山文档

到了这里,关于APB总线详解及手撕代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA-AMBA协议、APB协议、AHB规范、AXI4协议规范概述及它们之间的关系

    笔记记录,AMBA协议、APB协议、AHB规范、AXI4协议规范概述,只是概述描述,具体详细的协议地址传输、数据传输等内容将在下一章节详细说明。 AMBA(Advanced Microcontroller Bus Architecture)是一种由ARM公司提出的处理器总线架构,它定义了处理器、内存和外设之间的通信标准 。

    2024年02月04日
    浏览(28)
  • 数字IC手撕代码-平头哥技术终面手撕真题

     前言:         本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手撕代码-分频器(任意奇数分频) 3.数字IC手撕代码-分

    2024年02月05日
    浏览(24)
  • 【数字IC精品文章收录】近500篇文章|学习路线|基础知识|接口|总线|脚本语言|芯片求职|安全|EDA|工具|低功耗设计|Verilog|低功耗|STA|设计|验证|FPGA|架构|AMBA|书籍|

    1.1 索引目的 本篇索引旨在 收藏CSDN全站中有关数字IC领域高价值文章 ,在数字芯片领域中,就算将架构,设计,验证,DFT,后端诸多岗位加在一起的数量,都不及软件类一个细分方向的岗位数量多,反映在社区氛围或是开源资料的丰富度而言,数字IC领域相较于软件/互联网领

    2024年02月03日
    浏览(108)
  • 【数字IC/FPGA】百度昆仑芯手撕代码--累加器

    已知一个加法器IP,其功能是计算两个数的和,但这个和延迟两个周期才会输出。现在有一串连续的数据输入,每个周期都不间断,试问最少需要例化几个上述的加法器IP,才可以实现累加的功能。 由于加法器两个周期后才能得到结果(再将该结果作为加法器的输入进行累加

    2024年02月09日
    浏览(29)
  • 【数字IC手撕代码】Verilog无毛刺时钟切换电路|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区 联合力荐 !近500篇 数字IC精品文章收录 ! 【数字IC精品文章收录】学习路线·基础知识·总线·脚本语言·芯片求职·EDA工具·低功耗设计Verilog·STA·设计·验证·FPGA·架构·AMBA·书籍 本系列旨在提

    2023年04月08日
    浏览(27)
  • 数字IC手撕代码-流水握手(利用握手解决流水线断流、反压问题)

     前言:         本专栏旨在记录高频笔面试手撕代码题,以备数字前端秋招,本专栏所有文章提供原理分析、代码及波形,所有代码均经过本人验证。 目录如下: 1.数字IC手撕代码-分频器(任意偶数分频) 2.数字IC手撕代码-分频器(任意奇数分频) 3.数字IC手撕代码-分

    2024年02月02日
    浏览(31)
  • 【数字IC手撕代码】Verilog模三检测器(判断输入序列能否被三整除)|题目|原理|设计|仿真

    芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区 联合力荐 !近500篇 数字IC精品文章收录 ! 【数字IC精品文章收录】学习路线·基础知识·总线·脚本语言·芯片求职·EDA工具·低功耗设计Verilog·STA·设计·验证·FPGA·架构·AMBA·书籍 本系列旨在提

    2024年02月16日
    浏览(27)
  • APB协议及APB_Slave设计

    原文: 翻译: APB协议是一个 低成本 的接口,经过了优化, 降低了功耗和接口复杂性 。APB接口是 简单 , 同步 , 无流水线 的协议。 每次传输花费至少两个周期完成。 APB接口设计是为了访问外围设备中的可编程控制寄存器。APB外设通常使用APB桥连接到内存系统。例如,A

    2024年02月05日
    浏览(30)
  • stm32 APB1和APB2的区别

    APB1外设时钟使能寄存器(RCC_APB1ENR) 低速APB使能,最大允许频率36MHz APB2外设时钟使能寄存器(RCC_APB2ENR) 高速APB使能,最大允许频率72MHz 这里的外设是相对Cortex M3的内核来讲的。这两个寄存器都是32的,占用四个字节。这两个寄存器都位于结构体RCC_TypeDef中,名字叫APB1ENR和APB2ENR。操

    2024年02月11日
    浏览(22)
  • APB 基本原理

    APB是AMBA总线体系中的一员,因其具备低功耗、接口和控制简单的特点,广泛被运用于低带宽和不带总线的高性能外设接口,ASIC芯片中寄存器访问。 APB的传输信号只与时钟上升沿有关,故其具备以下优点: 易于实现高频操作; 性能与时钟占空比无关; 静态时序分析简单;

    2024年02月06日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包