深入理解AMBA总线2

上篇文章给大家介绍的APB协议的相关知识点,本篇文章通过一个实际的APB slave的设计帮助大家巩固对APB的掌握。

APB slave设计spec

这个设计实例是在ARM公司的ARM DesignStart的CMSDK开发套件中扒出来的。其链接放在下方

developer.arm.com/documentation/ddi0479/d/apb-components/apb-example-slaves

其框图如上图所示,该模块是一个基于APB协议完成的寄存器配置或者读取的设计实例。

上面的APB相关信号都介绍过,这里不再重复介绍,其中的ECOREVNUM的意思是ECO revision number,如果没有用到ARM的ECO的话,将该信号固定为全0即可。

右边这个slave reg实际上就对应我们设计的IP的寄存器配置部分,这个部分的接口是native interface,也就是没有考虑通用性的原生接口,想要通过APB总线对其进行配置,就需要通过slave_interface这个模块进行协议转换,进而完成APB协议的传输。我们经常能够看到的apb2reg模块,实际上也是做了这件事。该APB slave有以下的特点:

·不支持反压

·不支持错误传输

·支持4个RW类型的寄存器

·支持12个RO类型寄存器

·支持字节选通信号

APB slave设计代码

apb_slave_interface代码

下面是slave_interface的关键代码逻辑。我们对其进行分析:

·首先,由于不支持反压和错误传输,因此将pready固定为1,pslverr固定为0.

·APB传输进来的paddr可以直接赋值给addr,作为读写的地址。

·read_en需要在psel为1且pwrite为0的时候拉高。这实际上是希望在整个读传输过程中都让read_en信号有效。

·write_en则对应着setup phase,实际上在这个场景中修改write_en的逻辑让该信号对应着access phase也是可以的,其他信号直接赋值就可以了,应该很好理解。

// APB interface
assign pready = 1'b1; //always ready. Can be customized to support waitstate if required.
assign pslverr = 1'b0; //always OKAY. Can be customized to support error response if required.


// register read and write signal
assign addr = paddr;
assign read_en = psel & (~pwrite); // assert for whole apb read transfer
assign write_en = psel & (~penable) & pwrite; // assert for 1st cycle of write transfer
// It is also possible to change the design to perform the write in the 2nd
// APB cycle. E.g.
// assign write_en = psel & penable & pwrite;
// However, if the design generate waitstate, this expression will result
// in write_en being asserted for multiple cycles.
assign byte_strobe = pstrb;
assign wdata = pwdata;
assign prdata = rdata;

Apb_slave_reg代码

其关键逻辑如下所示,我们对其进行分析:

·首先由于分为RW寄存器和RO寄存器。这里确定写地址是否在规定区间,同时写使能是否有效,以及byte_strobe信号,来决定要不要写,写哪个字节。

·读的话比较简单,当读使能有效,根据地址信号决定rdata,实际上这就是个MUX选择逻辑。根据read_en加地址从多个寄存器的Q端选出某一个来。

// Address decoding for write operations
assign wr_sel[0] = ((addr[(ADDRWIDTH-1):2]==10'b0000000000)&(write_en)) ? 1'b1: 1'b0;
assign wr_sel[1] = ((addr[(ADDRWIDTH-1):2]==10'b0000000001)&(write_en)) ? 1'b1: 1'b0;
assign wr_sel[2] = ((addr[(ADDRWIDTH-1):2]==10'b0000000010)&(write_en)) ? 1'b1: 1'b0;
assign wr_sel[3] = ((addr[(ADDRWIDTH-1):2]==10'b0000000011)&(write_en)) ? 1'b1: 1'b0;
// register write, byte enable
// Data register: data0
always @(posedge pclk or negedge presetn)
begin
if (~presetn)
begin
data0 <= {32{1'b0}}; // Reset data 0 to 0x00000000
end
else if (wr_sel[0])
begin
if (byte_strobe[0])
data0[ 7: 0] <= wdata[ 7: 0];
if (byte_strobe[1])
data0[15: 8] <= wdata[15: 8];
if (byte_strobe[2])
data0[23:16] <= wdata[23:16];
if (byte_strobe[3])
data0[31:24] <= wdata[31:24];
end
end

// register read

always @ (read_en or addr or data0 or data1 or data2 or data3 or ecorevnum)
begin
case (read_en)
1'b1:
begin
if (addr[11:4] == 8'h00) begin
case(addr[3:2])
2'b00: rdata = data0;
2'b01: rdata = data1;
2'b10: rdata = data2;
2'b11: rdata = data3;
default: rdata = {32{1'bx}};
endcase
end
else if (addr[11:6] == 6'h3F) begin
case(addr[5:2])
// Peripheral IDs and Component IDs.
// AHB example slave has part number of 818
4'b0100: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID4; // 0xFD0 : PID 4
4'b0101: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID5; // 0xFD4 : PID 5
4'b0110: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID6; // 0xFD8 : PID 6
4'b0111: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID7; // 0xFDC : PID 7
4'b1000: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID0; // 0xFE0 : PID 0 APB Example slave part number[7:0]
4'b1001: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID1; // 0xFE4 : PID 1 [7:4] jep106_id_3_0. [3:0] part number [11:8]
4'b1010: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID2; // 0xFE8 : PID 2 [7:4] revision, [3] jedec_used. [2:0] jep106_id_6_4
4'b1011: rdata ={ARM_CMSDK_APB4_EG_SLAVE_PID3[31:8], ecorevnum[3:0], 4'h0};
// 0xFEC : PID 3 [7:4] ECO rev number, [3:0] modification number
4'b1100: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID0; // 0xFF0 : CID 0
4'b1101: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID1; // 0xFF4 : CID 1 PrimeCell class
4'b1110: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID2; // 0xFF8 : CID 2
4'b1111: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID3; // 0xFFC : CID 3
// Note : Customer changing the design should modify
// - jep106 value (www.jedec.org)
// - part number (customer define)
// - Optional revision and modification number (e.g. rXpY)
4'b0000, 4'b0001,4'b0010,4'b0011: rdata = {32'h00000000}; // default
default: rdata = {32{1'bx}}; // x propagation
endcase
end
else begin
rdata = {32'h00000000}; // default
end
end
1'b0:
begin
rdata = {32{1'b0}};
end
default:
begin
rdata = {32{1'bx}};
end
endcase
end

APB slave mux设计

这里再给大家介绍以下APB slave mux的概念,需下图所示,基于APB slave mux我们可以快速将多个apb_slave连接再上面。

其逻辑框图如上图所示,非常简单。就是一个master和多个slave,通过这个额外的DECODER4BIT进行16选1。

其关键代码如下所示

·根据PSEL是否有限以及DECODE4BIT的值,完成16选1,PSEL0~PSEL15有一个或者0个拉高。

·PREADYm默认为1,当PSEL为1的时候,根据译码结果选择相应的PREADY信号

·PSLVERR和PRDATA,选中谁就取谁的。

assign PSEL0 = PSEL & dec[ 0] & en[ 0];
assign PSEL1 = PSEL & dec[ 1] & en[ 1];
assign PSEL2 = PSEL & dec[ 2] & en[ 2];
//省略3~15
assign PREADY = ~PSEL |
( dec[ 0] & (PREADY0 | ~en[ 0]) ) |
( dec[ 1] & (PREADY1 | ~en[ 1]) ) |
( dec[ 2] & (PREADY2 | ~en[ 2]) ) |
//省略3~15
assign PSLVERR = ( PSEL0 & PSLVERR0 ) |
( PSEL1 & PSLVERR1 ) |
( PSEL2 & PSLVERR2 ) |
//省略3~15
assign PRDATA = ( {32{PSEL0 }} & PRDATA0 ) |
( {32{PSEL1 }} & PRDATA1 ) |
( {32{PSEL2 }} & PRDATA2 ) |
//省略3~15

这套代码的缺点或者优点是他是组合逻辑做的,逻辑非常的简单。实际就是多选1,一般来说APB的时钟频率很低,所以增加了一定的组合逻辑级数也不会出现时钟违例。这样做还可以节省一个时钟周期,大家也可以用时序逻辑去做,思路是类似的。

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值