一个简易的(不完整的)APB4 slave的可以没有PREADY和PSLVERR,这两个信号都被赋予常数,以及没有PPROT。

两种不同类型的寄存器:

图: 普通寄存器电路图

图: 带读写控制寄存器电路图

图:带读写控制寄存器时序图

一般来讲,一个模块的interface到内部reg之间,需要的信号为地址信号addr,读写使能信号(分开),byte_strobe字节选通信号,读写数据信号(分开)。

注意:在传输结束后不要立即更改地址和写信号,保持当前状态知道开始下一个传输,这样可以降低功耗。

interface spec:

寄存器表:

代码:apb4的顶层

 module apb4_slave #(
parameter ADDRWIDTH = )
(
input wire       PCLK,
input wire       PRESETn, input wire PSEL,
input wire[ADDRWIDTH -:] PADDR,
input wire PENABLE,
input wire PWRITE,
input wire[:] PWDATA,
input wire[:] PSTRB, input wire[:] ECOREVNUM, output wire[:] PRDATA,
output wire PREADY,
output wire PSLVERR
);
  wire  [ADDRWIDTH-:]  reg_addr;
  wire             reg_read_en;
  wire             reg_write_en;
  wire  [:]        reg_byte_strobe;
  wire  [:]        reg_wdata;
  wire   [:]        reg_rdata;
apb4_slave #(.ADDRWIDTH  (ADDRWIDTH))
  u_apb_slave_interface(
.pclk        (PCLK),
.presetn      (PRESETn), .psel        (PSEL),
.paddr       (PADDR),
.penable      (PENABLE),
.pwrite       (PWRITE),
.pwdata       (PWDATA),
.pstrb       (PSTRB), .prdata       (PRDATA),
.pready       (PREADY),
.pslverr      (PSLVERR), .addr        (reg_addr),
.read_en       (reg_read_en),
.write_en      (reg_write_en),
.byte_strobe    (reg_byte_strobe),
.wdata        (reg_wdata),
.tdata        (reg_rdata) );

 如何在interface这个模块中确定APB的建立周期信号?

其实APB的建立周期信号可以理解为一个使能信号,在第一周期拉高这个使能信号,那么下个周期就可以判断使能信号,并进入到传输周期了。这里使用组合逻辑去解决使能信号问题,一个是读使能read_en,一个是写使能write_en。read_en = psel &(~pwrite); write_en = psel &(~penable) & pwrite;  这里peanble是主机发过来的输入信号,这里说的从机不用管。

代码:apb4的逻辑接口

u_apb_slave_interfacef #(
parameter ADDRWIDTH = )
(
// IO declaration
input wire pclk,
input wire presetn,
// apb interface input
input wire psel,
input wire[ADDRWIDTH-:] paddr,
input wire penable,
input wire pwite,
input wire[:] pwdata,
input wire[:] pstrb,
// apb interface output
output wire[:] prdata,
output wire pready,
// Register interface
output wire[ADDRWIDTH-:] addr,
output wire read_en,
output wire write_en,
output wire[:] byte_pstrb,
output wire[:] wdata,
output wire[:] rdata
); assign pready = 'b1;
assign pslverr = 'b0; assign addr = paddr;
assign read_en = psel & (~pwrite); // 当pwrite为0,psel为1的时候,才读
assign write_en = psel & (~penable) & pwrite; // 在PCLK中,第一拍为psel有效,penable为低,第二拍才是psel和penable同时有效; assign byte_pstrb = pstrb;
assign wdata = pwdata;
assign prdata = rdata;

 如何设计有等待状态的写传输?

从模块的pready输出之前一直是高,现在想延迟一个时钟周期,做成脉冲形式。

可以使用组合逻辑如:

assgin pready_1 = psel & peanble & pwrite;

pready想延迟多个时钟节拍,可以使用时序电路,建立周期完成后,等待多个clock之后,再讲pready拉高。

代码:apb的寄存器读写

module apb4_slave_reg #(
parameter ADDRWIDTH = )
(
input wire pclk,
input wire presetn, input wire[ADDRWIDTH-:] addr,
input wire read_en,
input wire write_en,
input wire[:] byte_pstrb,
input wire[:] wdata,
input wire ecorevnum,
output wire[:] rdata ); localparam ARM_CMSDK_APB4_EG_SLAVE_PID4 = 'h00000004; // 12'hFD0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID5 = 'h00000000; // 12'hFD4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID6 = 'h00000000; // 12'hFD8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID7 = 'h00000000; // 12'hFDC;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID0 = 'h00000019; // 12'hFE0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID1 = 'h000000B8; // 12'hFE4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID2 = 'h0000001B; // 12'hFE8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID3 = 'h00000000; // 12'hFEC; wire [:] wr_sel; // 取地址的高10位出来,地址是4K对其的,之后高12bits是不一样的,从0xfff~0x000,其中的高10位就可以判断出是要操作那个寄存器了
// 地址是32为对其的,末尾都是0(0000)、4(0100)、8(1000)、C(1100)循环的,低两位都是一样的,只有高10位不一样
assign wr_sel[] = ((addr[(ADDRWIDTH-):]=='b0000000000)&(write_en)) ? 1'b1: 'b0;
assign wr_sel[] = ((addr[(ADDRWIDTH-):]=='b0000000001)&(write_en)) ? 1'b1: 'b0;
assign wr_sel[] = ((addr[(ADDRWIDTH-):]=='b0000000010)&(write_en)) ? 1'b1: 'b0;
assign wr_sel[] = ((addr[(ADDRWIDTH-):]=='b0000000011)&(write_en)) ? 1'b1: 'b0; // write_en = psel & (~penable) & pwrite; 时序要求在penable为高这一拍把数据写下去,所以要在其前一拍判断是否要写。 // 寄存器的写操作
// Data register: data0
always @(posedge pclk or negedge presetn) begin
if (~presetn) begin
data0 <= {{'b0}};
end
else if (wr_sel[]) begin
if (byte_strobe[])
data0[ :] <= wdata[:];
if (byte_strobe[])
data0[:] <= wdata[:];
if (byte_strobe[])
data0[:] <= wdata[:];
if (byte_strobe[])
data0[:] <= wdata[:];
end
end
// Data register: data1
always @(posedge pclk or negedge presetn) begin
if (~presetn) begin
data1 <= {{'b0}};
end
else if (wr_sel[]) begin
if (byte_strobe[])
data1[ :] <= wdata[:];
if (byte_strobe[])
data1[:] <= wdata[:];
if (byte_strobe[])
data1[:] <= wdata[:];
if (byte_strobe[])
data1[:] <= wdata[:];
end
end
// Data register: data2
always @(posedge pclk or negedge presetn) begin
if (~presetn) begin
data2 <= {{'b0}};
end
else if (wr_sel[]) begin
if (byte_strobe[])
data2[ :] <= wdata[:];
if (byte_strobe[])
data2[:] <= wdata[:];
if (byte_strobe[])
data2[:] <= wdata[:];
if (byte_strobe[])
data2[:] <= wdata[:];
end
end
// Data register: data3
always @(posedge pclk or negedge presetn) begin
if (~presetn) begin
data3 <= {{'b0}};
end
else if (wr_sel[]) begin
if (byte_strobe[])
data3[ :] <= wdata[:];
if (byte_strobe[])
data3[:] <= wdata[:];
if (byte_strobe[])
data3[:] <= wdata[:];
if (byte_strobe[])
data3[:] <= wdata[:];
end
end // 寄存器的读操作
always @(read_en or addr or data0 or data1 or data2 or data3 or ecorevnum) begin
case (read_en)
'b1: begin
if (addr[:] == 'h00) begin // 判断为RW类型的寄存器
case (addr[:])
'b00: rdata = data0;
'b01: rdata = data1;
'b10: rdata = data2;
'b11: rdata = data3;
default: rdata = {{'bx}};
endcase
end
else if (addr[:] == 'h3F) begin // 判断为RO类型的寄存器
case(addr[:])
'b0100:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID4;
'b0101:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID5;
'b0110:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID6;
'b0111:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID7;
'b1000:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID0;
'b1001:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID1;
'b1010:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID2;
'b1011:rdata = {ARM_CMSDK_APB4_EG_SLAVE_PID3[31:0],ecorevnum[3:0],4'h0};
default: rdata = {{'bx}};
endcase
end
else begin
rdata = {{'b0}};
end
end
'b0: begin
rdata = {{'b0}};
end
default: rdata = {{'bx}};
endcase
end endmodule

简易APB4 slave实践的更多相关文章

  1. php+websocket搭建简易聊天室实践

    1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...

  2. Redis Master/Slave 实践

    本次我们将模拟 Master(1) + Slave(4) 的场景,并通过ASP.NET WEB API进行数据的提交及查询,监控 Redis Master/Slave 数据分发情况,只大致概述,不会按 ...

  3. proto3 不支持内建类型的非空判断即 hasXXX

    proto3 移除了内建类型的非空判断方法 即代码生成工具不会为 bool int 等类型生成has方法 有使用过proto2 或者其它rpc 框架的人都知道使用has 方法去判断消息里的值是否设置, ...

  4. Vue2.0 + Element-UI + WebAPI实践:简易个人记账系统

    最近正在学习Vue2.0相关知识,正好近期饿了么桌面端组件Element-UI发布,便动手做了一款简易个人记账系统,以达到实践及巩固目的. 1.开发环境 Win10 + VS2015 + Sqlser ...

  5. 【原创 Hadoop&Spark 动手实践 13】Spark综合案例:简易电影推荐系统

    [原创 Hadoop&Spark 动手实践 13]Spark综合案例:简易电影推荐系统

  6. docker简易实践

    docker简易实践 实验环境 操作系统:deepin 15.4 安装步骤 1.安装docker sudo apt-get install docker.io 2.启动docker服务 sudo se ...

  7. 基于OVS的VLAN虚拟化简易实践方案

    基于OVS的VLAN虚拟化简易实践方案 前言 本实验基于ovs的vlan流表匹配,根据端口进行vlan标签插入.手工配置ovs,使其具有vlan虚拟化方案. 实验拓扑 ---- ---- | h1 | ...

  8. 《Java 程序设计》课堂实践项目-简易计算器

    <Java 程序设计>课堂实践项目简易计算器 课后学习总结 目录 改变 简易计算器实验要求 课堂实践成果 课后思考 改变 修改了博客整体布局,过去就贴个代码贴个图很草率,这次布局和内容都有 ...

  9. c++开发ocx入门实践三--基于opencv的简易视频播发器ocx

    原文:http://blog.csdn.net/yhhyhhyhhyhh/article/details/51404649  利用opencv做了个简易的视频播放器的ocx,可以在c++/c#/web ...

随机推荐

  1. JS和DOM的关系

    DOM对象 DOM实际上是以面向对象方式描述的文档模型.DOM定义了表示和修改文档所需的对象.这些对象的行为和属性以及这些对象之间的关系. 根据W3C DOM规范,DOM是HTML与XML的应用编程接 ...

  2. Ubuntu下安装配置ScrumWorks

    1) 安装JDK6 Ubuntu默认的是OpenJDK,而ScrumWorks不支持使用OpenJDK哦,一次必须装个Oracle的JDK6   2) 下载安装Mysql5 http://dev.my ...

  3. 更改oracle数据库字符集

    A.oracle server 端 字符集查询  select userenv('language') from dual 其中NLS_CHARACTERSET 为server端字符集 NLS_LAN ...

  4. Local Notification

    大家都知道Push Notification,这个东西需要联网才可以用.iOS4起,苹果引入了一种可以在设备内部引发的notification.不需要复杂的服务器编程,或其他复杂的配置.这个技术就是L ...

  5. (转)一个故事讲完https

    (转)一个故事讲完https 2 1  序言 今天来聊一聊https 安全传输的原理. 在开始之前,我们来虚构两个人物, 一个是位于中国的张大胖(怎么又是你?!), 还有一个是位于米国的Bill (怎 ...

  6. Java概述、环境变量、注释、关键字、标识符、常量

    Java语言的特点        有很多小特点,重点有两个开源,跨平台 Java语言是跨平台的 Java语言的平台        JavaSE        JavaME--Android       ...

  7. 年年岁岁花相似,岁岁年年人不同。——linux课程初探

    写在前面 记得大约两年以前第一次学习linux,当初的目的还仅仅是学习操作系统,后来慢慢开始写linux内核代码,慢慢学会重构与代码的维护.在娄老师课上感觉这些工具是如此亲切和熟悉,没错这些曾经被我抛 ...

  8. ARM汇编中一些重要伪指令

    IMPORT ,定义表示这是一个外部变量的标号,不是在本程序定义的 EXPORT ,表示本程序里面用到的变量提供给其他模块调用的. 以上两个在汇编和C语言混合编程的时候用到 ENDP    表示PRO ...

  9. CodeForces 408E Curious Array(组合数学+差分)

    You've got an array consisting of n integers: a[1], a[2], ..., a[n]. Moreover, there are m queries, ...

  10. Jenkins构建完成之后运行脚本可以杀掉TomCat但是起不来的解决方法

    Jenkins构建完成之后运行脚本可以杀掉TomCat但是起不来的解决方法 写了一个重启tomcat的脚本,让jenkins编译.打包.发布时调用.在本地写好重启tomcat的脚本后,本地执行脚本没有 ...