一个简易的(不完整的)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. beecloud resrful api test(nodejs)

    直接上代码 /** * Created by wyh on 2015/10/8. * 参数说明:https://beecloud.cn/doc/ */ var https = require('htt ...

  2. JavaScript词法作用域经典练习题

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...

  3. up7-文件保存位置

    asp.net 默认位置:项目/upload/年/月/日/guid/ 代码截图: 位置截图:   jsp 默认位置:tomcat/webapps/Uploader7Oracle/upload/年/月/ ...

  4. mybatis 存储过程调用

    接口 UserInfoMapper.java xml   UserInfoMapper.xml 如何关联 <?xml version="1.0" encoding=" ...

  5. C#函数式程序设计之泛型(上)

    在面向对象语言中,我们可以编写一个元素为某个专用类型(可能需要为此创建一个ListElement)的List类,或者使用一个非常通用.允许添加任何类型元素的基类(在.NET中,首先想到的是System ...

  6. http 错误代码

    2xx  成功  200  正常:请求已完成.  201  正常:紧接 POST 命令.  202  正常:已接受用于处理,但处理尚未完成.  203  正常:部分信息 — 返回的信息只是一部分.   ...

  7. NetBeans找不到C/C++编译器

    如果您已经安装 NetBeans IDE 6.9,但其中不包括 C/C++ 插件 如果在选择“文件”>“新建项目”时,NetBeans IDE 未显示 "C/C++" 项目类 ...

  8. C#中遇到的方法总结

    1.Select(string filterExpression, string sort)  // 获取按照指定的排序顺序且与筛选条件相匹配的所有 System.Data.DataRow 对象的数组 ...

  9. CodeForces 499D. Name That Tune(概率dp)

    It turns out that you are a great fan of rock band AC/PE. Peter learned that and started the followi ...

  10. Linux daemon与service 学习笔记

    service 常驻在内存中的进程,且可以提供一些系统或网络功能,就是服务.   daemon service的提供需要进程的运行,所以实现service的程序我们称为daemon.   eg: 实现 ...