10. 示例:设计APB总线接口并驱动时钟信号


前言

APB是AMBA总线的一部分,主要用于低带宽的外设连接,比如UART、SPI等。

需要设计一个APB接口,包含必要的信号,比如PCLK(时钟)、PRESETn(复位)、PADDR(地址)、PWRITE(读写控制)、PWDATA(写数据)、PRDATA(读数据)、PSELx(选择信号)、PENABLE(使能信号)和PREADY(传输完成)。

APB接口应该包括modport定义,区分Master和Slave的端口方向。时钟块用于同步Master和Slave的信号,比如在时钟上升沿驱动和采样。


示例一:‌APB3总线接口设计(1)

1. APB接口定义‌

`timescale 1ns/1ps

// APB3协议接口定义‌:ml-citation{ref="2" data="citationList"}
interface apb3_interface(input logic pclk, presetn);
    logic [31:0] paddr;    // 地址总线
    logic        pwrite;   // 写使能
    logic [31:0] pwdata;   // 写数据
    logic [31:0] prdata;   // 读数据
    logic        psel;     // 外设选择
    logic        penable;  // 传输使能
    logic        pready;   // 传输完成标志

    // 主设备时钟块(驱动端时序控制)‌:ml-citation{ref="2" data="citationList"}
    clocking master_cb @(posedge pclk);
        output #1ns paddr, pwrite, pwdata, psel;
        input  #2ns prdata, pready;
        output penable;
    endclocking

    // 从设备时钟块(采样端时序控制)‌:ml-citation{ref="2" data="citationList"}
    clocking slave_cb @(posedge pclk);
        input  #1ns paddr, pwrite, pwdata, psel;
        output #2ns prdata;
        input  penable;
        output pready;
    endclocking
endinterface

2. 简单Slave设备实现‌

module apb_slave(
    apb3_interface.slave_cb apb
);
    reg [31:0] mem [0:1023];  // 1KB寄存器空间

    always_ff @(posedge apb.pclk or negedge apb.presetn) begin
        if (!apb.presetn) begin
            apb.prdata <= 32'h0;
            apb.pready <= 1'b0;
        end else if (apb.psel && apb.penable) begin
            if (apb.pwrite) begin
                mem[apb.paddr[11:2]] <= apb.pwdata;  // 4字节对齐
                apb.pready <= 1'b1;
            end else begin
                apb.prdata <= mem[apb.paddr[11:2]];
                apb.pready <= 1'b1;
            end
        end
    end
endmodule

3. 测试平台‌

module apb_tb;
    logic pclk = 0;
    logic presetn;
    
    // 生成100MHz时钟‌:ml-citation{ref="3" data="citationList"}
    always #5 pclk = ~pclk;
    
    // 实例化接口和DUT
    apb3_interface apb_if(pclk, presetn);
    apb_slave dut(apb_if.slave_cb);

    // 主设备驱动任务
    task apb_write(input [31:0] addr, data);
        @(apb_if.master_cb);
        apb_if.master_cb.psel    <= 1;
        apb_if.master_cb.paddr   <= addr;
        apb_if.master_cb.pwrite  <= 1;
        apb_if.master_cb.pwdata  <= data;
        apb_if.master_cb.penable <= 0;
        
        @(apb_if.master_cb);
        apb_if.master_cb.penable <= 1;
        
        while (!apb_if.master_cb.pready) @(apb_if.master_cb);
        apb_if.master_cb.psel    <= 0;
        apb_if.master_cb.penable <= 0;
    endtask

    // 测试激励‌:ml-citation{ref="1" data="citationList"}
    initial begin
        presetn = 0;
        #20 presetn = 1;
        
        apb_write(32'h0000_1000, 32'h1234_5678);
        apb_write(32'h0000_1004, 32'h9ABC_DEF0);
        #50 $finish;
    end

    // 波形记录‌:ml-citation{ref="3" data="citationList"}
    initial begin
        $shm_open("apb_waves.shm");
        $shm_probe(apb_tb, "AS");
    end
endmodule

4. Xcelium运行脚本‌

#!/bin/bash
xrun -64bit \
    -access +rwc \
    -xmlibdirpath ./xcelium_apb \
    -input wave_cfg.tcl \
    apb3_interface.sv apb_slave.sv apb_tb.sv

‌波形配置‌ (wave_cfg.tcl)

database -open waves -shm
probe -create -database waves -all -depth all
run

5. 仿真验证‌

‌执行命令‌:

chmod +x xrun_apb.sh
./xrun_apb.sh

预期波形特征‌:

  • Setup Phase‌:PSEL=1且PENABLE=0
  • Access Phase‌:PSEL=1且PENABLE=1
  • 数据传输‌:在PREADY=1时完成写操作

‌时序验证点‌:

  • 地址相位对齐(paddr变化在时钟上升沿后1ns)‌
  • 写数据在penable有效后保持稳定
  • pready响应符合协议时序

‌关键设计原理‌

  • 接口封装‌:通过interface整合APB3协议信号,实现模块化连接‌
  • 时钟块同步‌:使用clocking block确保主从设备时序隔离‌
  • 协议状态机‌:Slave设备实现PSEL→PENABLE→PREADY状态流转
  • 地址对齐‌:paddr[11:2]实现4字节寻址(32-bit数据总线)

‌扩展建议‌

  • 增加覆盖率‌:添加covergroup统计地址分布和操作类型‌
  • 错误注入‌:在Slave中模拟PREADY延迟响应场景
  • 协议检查器‌:使用assert验证APB时序规则‌

示例二:‌APB3总线驱动时钟设计(2)

1. 接口定义与时钟块‌

`timescale 1ns/1ps

interface apb3_if(input logic pclk, presetn);
    // 核心信号组‌:ml-citation{ref="1,3" data="citationList"}
    logic [31:0] paddr;
    logic        pwrite;
    logic [31:0] pwdata;
    logic [31:0] prdata;
    logic        psel;
    logic        penable;
    logic        pready;

    // 主设备时钟驱动策略‌:ml-citation{ref="2,5" data="citationList"}
    clocking master_cb @(posedge pclk);
        output #1ns paddr, pwrite, pwdata, psel;  // 上升沿后1ns驱动
        input  #2ns pready, prdata;               // 上升沿前2ns采样
        output penable;
    endclocking

    // 从设备时钟采样策略‌:ml-citation{ref="3,4" data="citationList"}
    clocking slave_cb @(posedge pclk);
        input  #1ns paddr, pwrite, pwdata, psel;  // 上升沿前1ns采样
        output #2ns prdata;                        // 上升沿后2ns驱动
        input  penable;
        output pready;
    endclocking
endinterface

2. 从设备实现‌

module apb_slave(
    apb3_if.slave_cb apb
);
    reg [31:0] mem[0:1023];
    
    always_ff @(posedge apb.pclk or negedge apb.presetn) begin
        if (!apb.presetn) begin
            apb.pready <= 0;
            apb.prdata <= 32'h0;
        end else if (apb.psel && apb.penable) begin
            apb.pready <= 1;  // 固定1周期等待‌:ml-citation{ref="4" data="citationList"}
            if (apb.pwrite) 
                mem[apb.paddr[11:0]] <= apb.pwdata;
            else 
                apb.prdata <= mem[apb.paddr[11:0]];
        end
    end
endmodule

3. 测试平台与时钟驱动‌

module apb_tb;
    logic pclk = 0;
    logic presetn;
    
    // 100MHz时钟生成‌:ml-citation{ref="3,5" data="citationList"}
    always #5 pclk = ~pclk;  // 周期10ns
    
    apb3_if apb_if(pclk, presetn);
    apb_slave dut(apb_if.slave_cb);

    // 主设备驱动任务
    task apb_transfer(
        input [31:0] addr,
        input [31:0] data,
        input        write_en
    );
        // Setup Phase‌:ml-citation{ref="4" data="citationList"}
        @(apb_if.master_cb);
        apb_if.master_cb.psel    <= 1;
        apb_if.master_cb.paddr   <= addr;
        apb_if.master_cb.pwrite  <= write_en;
        apb_if.master_cb.pwdata  <= data;
        apb_if.master_cb.penable <= 0;
        
        // Access Phase‌:ml-citation{ref="1,4" data="citationList"}
        @(apb_if.master_cb);
        apb_if.master_cb.penable <= 1;
        
        // 等待传输完成
        wait(apb_if.master_cb.pready);
        @(apb_if.master_cb);
        apb_if.master_cb.psel    <= 0;
        apb_if.master_cb.penable <= 0;
    endtask

    initial begin
        presetn = 0;
        #20 presetn = 1;  // 复位释放‌:ml-citation{ref="5" data="citationList"}
        
        // 写操作示例
        apb_transfer(32'h0000_1000, 32'hA5A5_A5A5, 1);
        // 读操作示例
        apb_transfer(32'h0000_1000, 32'h0, 0);
        #50 $finish;
    end
endmodule

4. 仿真运行命令‌

xrun -64bit \
    -access +rwc \
    -xmlibdirpath ./xcelium_apb \
    -input wave_cfg.tcl \
    apb3_if.sv apb_slave.sv apb_tb.sv

‌波形配置‌ (wave_cfg.tcl):

database -open waves -shm
probe -create -database waves -all -depth all
run

5. 关键时序验证‌

  1. 时钟驱动验证‌
  • 时钟周期10ns(100MHz)‌
  • 复位信号在时钟上升沿后20ns释放‌
  1. 协议时序检查‌
    在这里插入图片描述
  2. 波形特征示例‌
Time(ns) | 事件
------------------------------
25       | PSEL上升沿(Setup开始)
35       | PENABLE上升沿(Access开始)
45       | PREADY变高(传输完成)

6. 设计原理说明‌

‌时钟驱动策略‌

  • 主设备输出信号在时钟上升沿‌后1ns‌变化‌
  • 从设备采样信号在时钟上升沿‌前1ns‌稳定‌

‌协议状态机‌

graph LR
IDLE -->|传输请求| SETUP
SETUP -->|下一个时钟| ACCESS
ACCESS -->|PREADY=1| IDLE

‌地址对齐机制‌

  • 使用paddr[11:0]实现4KB地址空间映射,符合APB外设典型设计‌

扩展建议‌
‌动态时钟控制‌

// 可配置时钟频率
real clock_freq = 100e6;
always #(0.5/clock_freq*1e9) pclk = ~pclk;  // 动态调整‌:ml-citation{ref="5" data="citationList"}

‌协议断言检查‌

assert property (@(posedge apb_if.pclk) 
    apb_if.psel |-> ##1 apb_if.penable) 
else $error("协议违规:PENABLE未在PSEL后置位");  // ‌:ml-citation{ref="4,5" data="citationList"}

示例三:APB总线接口设计(3)

// apb_interface.sv
interface apb_if(input logic pclk);
    logic [31:0] paddr;
    logic        psel;
    logic        penable;
    logic        pwrite;
    logic [31:0] pwdata;
    logic [31:0] prdata;
    logic        pready;
    logic        pslverr;
    
    modport master (
        output paddr, psel, penable, pwrite, pwdata,
        input  prdata, pready, pslverr
    );
    
    modport slave (
        input  paddr, psel, penable, pwrite, pwdata,
        output prdata, pready, pslverr
    );
endinterface

// apb_master.sv
module apb_master(apb_if.master bus);
    typedef enum {IDLE, SETUP, ACCESS} state_t;
    state_t state = IDLE;
    
    task automatic write(input [31:0] addr, data);
        bus.paddr   = addr;
        bus.pwrite  = 1'b1;
        bus.pwdata  = data;
        bus.psel    = 1'b1;
        bus.penable = 1'b0;
        state = SETUP;
        
        @(posedge bus.pclk);
        bus.penable = 1'b1;
        state = ACCESS;
        
        wait(bus.pready);
        @(posedge bus.pclk);
        bus.psel    = 1'b0;
        bus.penable = 1'b0;
        state = IDLE;
    endtask
    
    task automatic read(input [31:0] addr, output [31:0] data);
        bus.paddr   = addr;
        bus.pwrite  = 1'b0;
        bus.psel    = 1'b1;
        bus.penable = 1'b0;
        state = SETUP;
        
        @(posedge bus.pclk);
        bus.penable = 1'b1;
        state = ACCESS;
        
        wait(bus.pready);
        data = bus.prdata;
        @(posedge bus.pclk);
        bus.psel    = 1'b0;
        bus.penable = 1'b0;
        state = IDLE;
    endtask
endmodule

// apb_slave.sv
module apb_slave(apb_if.slave bus);
    logic [31:0] mem [0:255];
    
    always @(posedge bus.pclk) begin
        if(bus.psel && bus.penable) begin
            bus.pready <= 1'b1;
            if(bus.pwrite) begin
                mem[bus.paddr] <= bus.pwdata;
                bus.pslverr <= 1'b0;
            end else begin
                bus.prdata <= mem[bus.paddr];
                bus.pslverr <= (bus.paddr > 255) ? 1'b1 : 1'b0;
            end
        end else begin
            bus.pready <= 1'b0;
        end
    end
endmodule

// tb.sv
module tb;
    logic pclk;
    
    // 生成50MHz时钟
    initial begin
        pclk = 0;
        forever #5ns pclk = ~pclk;
    end
    
    // 实例化接口
    apb_if bus(pclk);
    
    // 连接DUT
    apb_master master(bus);
    apb_slave  slave(bus);
    
    initial begin
        logic [31:0] rd_data;
        
        // 初始化
        master.bus.psel = 0;
        master.bus.penable = 0;
        
        // 写操作测试
        master.write(32'h0000_0010, 32'h1234_5678);
        master.write(32'h0000_0020, 32'hdead_beef);
        
        // 读操作测试
        master.read(32'h0000_0010, rd_data);
        master.read(32'h0000_0020, rd_data);
        
        // 错误测试
        master.read(32'h0000_1000, rd_data); // 触发slverr
        
        #100ns;
        $finish;
    end
endmodule

1. 仿真运行指令:

使用Cadence Xcelium(xrun):

xrun -sv tb.sv apb_interface.sv apb_master.sv apb_slave.sv -access +rw -input run.tcl

创建run.tcl文件:

# run.tcl
run
exit

使用Synopsys VCS:

vcs -sverilog tb.sv apb_interface.sv apb_master.sv apb_slave.sv -debug_all
./simv
  1. 关键设计说明:

接口定义(apb_if)包含完整的APB4信号:

  • pclk: APB时钟(由测试平台生成)
  • paddr: 32位地址总线
  • psel: 选择信号
  • penable: 使能信号
  • pwrite: 写使能
  • pwdata/prdata: 写/读数据总线
  • pready: 传输完成指示
  • pslverr: 错误指示

主设备(apb_master)实现:

  • 状态机控制(IDLE->SETUP->ACCESS)
  • 支持基本的读写任务
  • 符合APB协议时序要求

-从设备(apb_slave)实现:

  • 256x32位存储阵列
  • 自动响应PSEL和PENABLE
  • 地址越界错误检测

波形查看建议:

verdi -ssf tb.fsdb   # 使用Verdi
dve -vpd vcdplus.vpd # 使用Cadence DVE

示例四:APB总线接口设计(4)

1. APB接口定义(SystemVerilog)

// APB接口定义(支持APB3协议)
interface apb_if #(parameter ADDR_WIDTH=32, DATA_WIDTH=32) (
  input logic pclk,
  input logic presetn
);
  logic [ADDR_WIDTH-1:0] paddr;
  logic pwrite;
  logic psel;
  logic penable;
  logic [DATA_WIDTH-1:0] pwdata;
  logic [DATA_WIDTH-1:0] prdata;
  logic pready;
  logic pslverr;

  // Master驱动时钟块
  clocking master_cb @(posedge pclk);
    default input #1step output #2;
    output paddr, pwrite, psel, penable, pwdata;
    input  prdata, pready, pslverr;
  endclocking

  // Slave采样时钟块
  clocking slave_cb @(posedge pclk);
    default input #1step;
    input paddr, pwrite, psel, penable, pwdata;
    output prdata, pready, pslverr;
  endclocking

  modport master (clocking master_cb);
  modport slave  (clocking slave_cb);
endinterface

2. APB Master设计

module apb_master (
  apb_if.master apb,
  input logic [31:0] wr_addr,
  input logic [31:0] wr_data,
  input logic        wr_en,
  input logic [31:0] rd_addr,
  output logic [31:0] rd_data,
  output logic        rd_ready
);
  enum {IDLE, SETUP, ACCESS} state;

  always_ff @(posedge apb.pclk or negedge apb.presetn) begin
    if (!apb.presetn) begin
      state <= IDLE;
      apb.master_cb.psel <= 0;
      apb.master_cb.penable <= 0;
    end else begin
      case(state)
        IDLE: 
          if (wr_en || rd_ready) begin
            apb.master_cb.paddr <= wr_en ? wr_addr : rd_addr;
            apb.master_cb.pwrite <= wr_en;
            apb.master_cb.psel <= 1;
            state <= SETUP;
          end

        SETUP: 
          begin
            apb.master_cb.penable <= 1;
            state <= ACCESS;
          end

        ACCESS: 
          if (apb.master_cb.pready) begin
            apb.master_cb.psel <= 0;
            apb.master_cb.penable <= 0;
            if (!apb.master_cb.pwrite)
              rd_data <= apb.master_cb.prdata;
            state <= IDLE;
          end
      endcase
    end
  end
endmodule

3. APB Slave设计(带寄存器)

module apb_slave (
  apb_if.slave apb,
  input logic [3:0] reg_sel,
  output logic [31:0] reg_data_out,
  input logic [31:0] reg_data_in
);
  logic [31:0] mem [0:15];

  always_ff @(posedge apb.pclk or negedge apb.presetn) begin
    if (!apb.presetn) begin
      foreach(mem[i]) mem[i] <= 32'h0;
    end else if (apb.slave_cb.psel && apb.slave_cb.penable) begin
      if (apb.slave_cb.pwrite) 
        mem[apb.slave_cb.paddr[5:2]] <= apb.slave_cb.pwdata;
      else
        reg_data_out <= mem[apb.slave_cb.paddr[5:2]];
    end
  end

  assign apb.slave_cb.prdata = reg_data_in;
  assign apb.slave_cb.pready = 1'b1; // 无等待
  assign apb.slave_cb.pslverr = 1'b0; // 无错误
endmodule

4. 测试平台

module tb;
  logic pclk = 0;
  logic presetn;
  logic [31:0] wr_addr = 32'h1000_0000;
  logic [31:0] wr_data = 32'h1234_5678;
  logic wr_en = 0;
  logic [31:0] rd_data;
  
  apb_if apb(pclk, presetn);
  apb_master master(.*);
  apb_slave slave(.reg_data_in(rd_data), .*);

  // 时钟生成
  always #5 pclk = ~pclk;

  initial begin
    $dumpfile("apb_wave.vcd");
    $dumpvars(0, tb);
    
    // 复位
    presetn = 0;
    #20 presetn = 1;
    
    // 写操作
    wr_en = 1;
    #30 wr_en = 0;
    
    // 读操作
    #30 wr_addr = 32'h1000_0000;
    #30;
    
    $finish;
  end
endmodule

5. 仿真指令

# 使用Synopsys Xrun
xrun -sv -access +rwc apb_if.sv apb_master.sv apb_slave.sv tb.sv
# 或使用VCS
vcs -full64 -sverilog apb_if.sv apb_master.sv apb_slave.sv tb.sv
./simv

6. 关键时序说明

​写时序:

  • T0-T1:Master在IDLE状态检测到wr_en有效
  • T1-T2:SETUP阶段(PSEL=1,PENABLE=0)
  • T2-T3:ACCESS阶段(PENABLE=1,Slave采样PWData)
  • 地址相位和数据相位对齐
    读时序:
  • 类似写时序,但PWRITE=0
  • Slave在ACCESS阶段返回PRDATA

7. 波形验证要点

  1. 观察PSEL/PENABLE的相位关系
  2. 验证地址与数据的同步性
  3. 检查PREADY始终为1(本设计无等待)
  4. 确认写数据在ACCESS阶段被Slave存储

示例五:APB总线接口设计(5)

1. 完整代码示例

1.1 APB总线接口定义 (apb_if.sv)

// =================================================
// APB总线接口定义(符合AMBA APB协议)
// =================================================
interface apb_if(input logic pclk, input logic presetn);
  // -----------------------------------------------
  // 信号定义
  // -----------------------------------------------
  logic [31:0] paddr;     // 地址总线
  logic        pwrite;     // 写使能(1=写,0=读)
  logic [31:0] pwdata;     // 写数据
  logic [31:0] prdata;     // 读数据
  logic        psel;       // 外设选择信号
  logic        penable;    // 传输使能
  logic        pready;     // 外设就绪信号(简化设计,恒为1)

  // -----------------------------------------------
  // 时钟块定义(用于Testbench同步驱动和采样)
  // -----------------------------------------------
  clocking driver_cb @(posedge pclk);
    default input #1ns output #1ns; // 输入采样在时钟沿后1ns,输出驱动在时钟沿前1ns
    output paddr, pwrite, pwdata, psel, penable;
    input  prdata, pready;
  endclocking

  clocking monitor_cb @(posedge pclk);
    input paddr, pwrite, pwdata, psel, penable, prdata;
  endclocking

  // -----------------------------------------------
  // Modport定义(模块视图)
  // -----------------------------------------------
  modport master (
    clocking driver_cb,  // Testbench或Master驱动
    output presetn       // 复位信号由Master控制
  );

  modport slave (
    input  pclk, presetn,
    input  paddr, pwrite, pwdata, psel, penable,
    output prdata,
    output pready = 1'b1  // 简化设计,外设始终就绪
  );
endinterface

1.2 APB Slave模块 (apb_slave.sv)

// =================================================
// APB Slave模块:实现简单的寄存器读写
// =================================================
module apb_slave (
  apb_if.slave apb
);
  // -----------------------------------------------
  // 内部寄存器定义
  // -----------------------------------------------
  logic [31:0] mem[0:255]; // 256个32位寄存器

  // -----------------------------------------------
  // APB协议逻辑
  // -----------------------------------------------
  always_ff @(posedge apb.pclk or negedge apb.presetn) begin
    if (!apb.presetn) begin
      apb.prdata <= 32'h0;
    end else if (apb.psel && apb.penable) begin
      if (apb.pwrite) begin
        // 写操作
        mem[apb.paddr[7:0]] <= apb.pwdata;
        $display("[SLAVE] Write Addr=0x%08h, Data=0x%08h", apb.paddr, apb.pwdata);
      end else begin
        // 读操作
        apb.prdata <= mem[apb.paddr[7:0]];
        $display("[SLAVE] Read Addr=0x%08h, Data=0x%08h", apb.paddr, apb.prdata);
      end
    end
  end
endmodule

1.3 测试平台 (tb.sv)

// =================================================
// Testbench模块:驱动APB总线并验证功能
// =================================================
module tb;
  // -----------------------------------------------
  // 生成时钟和复位信号
  // -----------------------------------------------
  logic pclk;
  logic presetn;

  initial begin
    pclk = 0;
    forever #5 pclk = ~pclk; // 100MHz时钟(周期10ns)
  end

  initial begin
    presetn = 0; // 复位初始为低
    #20 presetn = 1; // 20ns后释放复位
  end

  // -----------------------------------------------
  // 实例化APB接口和Slave模块
  // -----------------------------------------------
  apb_if apb0(pclk, presetn);
  apb_slave slave(apb0);

  // -----------------------------------------------
  // 测试逻辑
  // -----------------------------------------------
  initial begin
    // 等待复位完成
    @(posedge presetn);
    $display("[TB] Reset released at %0t ns", $time);

    // 通过时钟块驱动APB信号
    fork
      begin: WRITE_OP
        // 写操作:地址0x1000,数据0x12345678
        apb0.driver_cb.psel    <= 1;
        apb0.driver_cb.paddr   <= 32'h1000;
        apb0.driver_cb.pwrite  <= 1;
        apb0.driver_cb.pwdata  <= 32'h12345678;
        @(apb0.driver_cb);
        apb0.driver_cb.penable <= 1;
        @(apb0.driver_cb);
        apb0.driver_cb.psel    <= 0;
        apb0.driver_cb.penable <= 0;
        $display("[TB] Write operation completed at %0t ns", $time);
      end

      begin: READ_OP
        // 读操作:地址0x1000
        #30; // 等待写操作完成
        apb0.driver_cb.psel    <= 1;
        apb0.driver_cb.paddr   <= 32'h1000;
        apb0.driver_cb.pwrite  <= 0;
        @(apb0.driver_cb);
        apb0.driver_cb.penable <= 1;
        @(apb0.driver_cb);
        apb0.driver_cb.psel    <= 0;
        apb0.driver_cb.penable <= 0;
        $display("[TB] Read operation completed, Data=0x%08h", apb0.driver_cb.prdata);
      end
    join

    #50;
    $finish;
  end
endmodule

2. 仿真步骤

2.1 运行仿真

xrun -sv apb_if.sv apb_slave.sv tb.sv +access+r
  • 命令解析:
    • -sv:启用SystemVerilog模式。
    • +access+r:生成波形数据库(用于查看波形)。

2.2 预期输出日志

[TB] Reset released at 20 ns
[SLAVE] Write Addr=0x00001000, Data=0x12345678
[TB] Write operation completed at 40 ns
[SLAVE] Read Addr=0x00001000, Data=0x12345678
[TB] Read operation completed, Data=0x12345678

2.3 查看波形

使用SimVision打开生成的波形数据库(默认名为tb.shm),查看以下信号:

  • 时钟和复位:pclk、presetn
  • APB控制信号:psel、penable、pwrite
  • 数据信号:paddr、pwdata、prdata

3. 关键代码解析

3.1 APB接口时钟块

clocking driver_cb @(posedge pclk);
  default input #1ns output #1ns;
  output paddr, pwrite, pwdata, psel, penable;
  input  prdata, pready;
endclocking

作用:在时钟上升沿同步驱动和采样信号,避免时序冲突。

3.2 APB Slave读写逻辑

always_ff @(posedge apb.pclk or negedge apb.presetn) begin
  if (!apb.presetn) begin
    apb.prdata <= 32'h0;
  end else if (apb.psel && apb.penable) begin
    if (apb.pwrite) begin
      mem[apb.paddr[7:0]] <= apb.pwdata;
    end else begin
      apb.prdata <= mem[apb.paddr[7:0]];
    end
  end
end

复位时:清除读数据。
传输阶段:当psel和penable同时有效时,执行读写操作。

3.3 测试平台驱动

// 写操作流程
apb0.driver_cb.psel    <= 1;
apb0.driver_cb.paddr   <= 32'h1000;
apb0.driver_cb.pwrite  <= 1;
apb0.driver_cb.pwdata  <= 32'h12345678;
@(apb0.driver_cb);
apb0.driver_cb.penable <= 1;
@(apb0.driver_cb);
apb0.driver_cb.psel    <= 0;
apb0.driver_cb.penable <= 0;

Setup阶段:设置地址、数据和控制信号(psel=1)。
Access阶段:拉高penable完成传输。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值