上篇文章给大家介绍了apb协议相关的知识点,本篇文章通过一个实际的apb slave的设计帮助大家巩固对apb的掌握。
apb slave设计spec
其框图如上图所示,这里提一嘴,大家在做数字ic设计的时候,都应该像这样规划好各个模块的连接关系,确定好以后再写代码。该模块是一个基于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信号有效。读者可能就想问了,读应该对应着两个阶段吗?不需要判断吗?实际上读的话,master那边自己控制好就行了,对于slave而言完全可以在第一拍和第二拍都把rdata提供好,这个是没有关系的。 write_en则对应着setup phase,实际上在这个场景中修改write_en的逻辑让该信号对应着access phase也是可以的,其它的信号直接赋值就可以,应该很好理解。 // apb interfaceassign 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 signalassign addr = paddr;assign read_en = psel & (~pwrite); // assert for whole apb read transferassign 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 operationsassign 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: data0always @(posedge pclk or negedge presetn)beginif (~presetn)begindata0 <= {32{1'b0}}; // reset data 0 to 0x00000000endelse if (wr_sel[0])beginif (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];endend// register readalways @ (read_en or addr or data0 or data1 or data2 or data3 or ecorevnum)begincase (read_en)1'b1:beginif (addr[11:4] == 8'h00) begincase(addr[3:2])2'b00: rdata = data0;2'b01: rdata = data1;2'b10: rdata = data2;2'b11: rdata = data3;default: rdata = {32{1'bx}};endcaseendelse if (addr[11:6] == 6'h3f) begincase(addr[5:2])// peripheral ids and component ids.// ahb example slave has part number of 8184'b0100: rdata = arm_cmsdk_apb4_eg_slave_pid4; // 0xfd0 : pid 44'b0101: rdata = arm_cmsdk_apb4_eg_slave_pid5; // 0xfd4 : pid 54'b0110: rdata = arm_cmsdk_apb4_eg_slave_pid6; // 0xfd8 : pid 64'b0111: rdata = arm_cmsdk_apb4_eg_slave_pid7; // 0xfdc : pid 74'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_44'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 number4'b1100: rdata = arm_cmsdk_apb4_eg_slave_cid0; // 0xff0 : cid 04'b1101: rdata = arm_cmsdk_apb4_eg_slave_cid1; // 0xff4 : cid 1 primecell class4'b1110: rdata = arm_cmsdk_apb4_eg_slave_cid2; // 0xff8 : cid 24'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}; // defaultdefault: rdata = {32{1'bx}}; // x propagationendcaseendelse beginrdata = {32'h00000000}; // defaultendend1'b0:beginrdata = {32{1'b0}};enddefault:beginrdata = {32{1'bx}};endendcaseend apb slave mux设计 这里再给大家介绍一下apb slave mux的概念,如下图所示,基于apb slave mux我们可以快速地将多个apb slave连接在上面。在实际的设计当中都是采用这样的方式,连接多个slave的。一般我们管这种模块叫做interconnect,顾名思义,将不同的模块连接起来。而apb的interconnect只能连接一个master,因此继续管他叫interconnect感觉差了点意思。所以一般就叫它slave mux了。
其逻辑框图如上图所示,非常的简单啊。就是一个master和多个slave,通过这个额外的decode4bit进行16选1。其关键代码如下所示 根据psel是否有效以及decode4bit的值,完成16选1,psel0~psel15有一个或者0个拉高。 preadym默认为1,当psel为1的时候,根据译码结果选择相应的pready信号(当端口没有使能的时候en[x] == 0, 对应的preadyx信号不会被选择)。 pslverr和prdata,选中谁就取谁的。 assign psel0 = psel & dec[ 0] & en[ 0];assign psel1 = psel & dec[ 1] & en[ 1];assign psel2 = psel & dec[ 2] & en[ 2];//省略3~15assign pready = ~psel |( dec[ 0] & (pready0 | ~en[ 0]) ) |( dec[ 1] & (pready1 | ~en[ 1]) ) |( dec[ 2] & (pready2 | ~en[ 2]) ) |//省略3~15assign pslverr = ( psel0 & pslverr0 ) |( psel1 & pslverr1 ) |( psel2 & pslverr2 ) |//省略3~15assign prdata = ( {32{psel0 }} & prdata0 ) |( {32{psel1 }} & prdata1 ) |( {32{psel2 }} & prdata2 ) |//省略3~15 这套代码的缺点或者说优点是,它是用组合逻辑做的,逻辑非常的简单。实际上就是多选1,一般来说apb的时钟频率很低,所以增加了一定的组合逻辑级数也不会出现时钟违例。这样做还可以节省一个时钟周期,大家也可以用时序逻辑去做,思路是类似的。还有一个问题就是这个模块没有penable信号,这个其实挺致命,大家可以手动加上,跟psel的逻辑基本是一模一样的,非常简单。
减少接地反弹的方法概述
三菱FX系列PLC指令:LD/LDI/OUT介绍
一个操作把MySQL主从复制整崩了
特斯拉为华搭载新国标充电接口,并在年底设1000个充电桩
ST NFC技术助力“带外”无线配对,提高用户体验
AMBA总线中APB slave设计介绍
WMA怎么转换成MP3,简单实用的音频转换技巧
机器视觉正在加速向产业端渗透
锁相环CD4046的原理_CD4046的引脚图及功能_CD4046典型应用电路
尖端可穿戴式与传感器技术,可窥见医疗保健的未来
中环开始试制单晶硅晶圆,年底将达到2万片/月
晶闸管的电极怎么判断
五种车载充电器电路分析对比——电路图天天读(283)
根元科技首推鸿蒙FA功能产品,未来有迹可循!
一箭41颗星,刷新我国一次发射卫星数量最多的纪录
松下宣布在多国起诉小米和OPPO 涉及通信标准必要专利
江苏移动携手捷普电子与爱立信联合打造出了5G﹢工业互联网创新应用
iPhone X新配色曝光:中国红
两款自制单端胆机的工作原理
面对AirPods霸权!Bragi无奈退出可穿戴设备市场