Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

APB4: PSTRB Byte Writes and PPROT Protection

Verilog Bus Protocols

An APB4 completer with a byte-writable register and a Secure-only key register.

Open in IDE
design.v
// An APB4 completer that uses PSTRB and PPROT.
//   0x0  DATA  read/write; PSTRB selects which bytes a write updates
//   0x4  KEY   secure only: a Non-secure access (PPROT[1] = 1) returns PSLVERR,
//              a Non-secure write is ignored and a Non-secure read returns 0
module apb4_regs (
  input             PCLK,
  input             PRESETn,
  input             PSEL,
  input             PENABLE,
  input             PWRITE,
  input      [11:0] PADDR,
  input      [31:0] PWDATA,
  input      [3:0]  PSTRB,
  input      [2:0]  PPROT,
  output reg [31:0] PRDATA,
  output            PREADY,
  output            PSLVERR
);
  reg [31:0] data_r;
  reg [31:0] key_r;

  wire access    = PSEL && PENABLE;
  wire hit_data  = (PADDR == 12'h000);
  wire hit_key   = (PADDR == 12'h004);
  wire nonsecure = PPROT[1];             // PPROT[1]: 0 = Secure, 1 = Non-secure
  wire denied    = hit_key && nonsecure;

  assign PREADY  = 1'b1;
  assign PSLVERR = access && (denied || !(hit_data || hit_key));

  always @(posedge PCLK) begin
    if (!PRESETn) begin
      data_r <= 32'h0;
      key_r  <= 32'h0;
    end
    else if (access && PWRITE) begin
      if (hit_data) begin
        if (PSTRB[0])
          data_r[7:0]   <= PWDATA[7:0];
        if (PSTRB[1])
          data_r[15:8]  <= PWDATA[15:8];
        if (PSTRB[2])
          data_r[23:16] <= PWDATA[23:16];
        if (PSTRB[3])
          data_r[31:24] <= PWDATA[31:24];
      end
      else if (hit_key && !nonsecure) begin
        key_r <= PWDATA;
      end
    end
  end

  always @(*) begin
    if (hit_data)
      PRDATA = data_r;
    else if (hit_key && !nonsecure)
      PRDATA = key_r;
    else
      PRDATA = 32'h0;
  end
endmodule
testbench.v
// Testbench: an APB4 requester with PSTRB and PPROT
module tb;
  reg         PCLK    = 0;
  reg         PRESETn = 0;
  reg         PSEL    = 0;
  reg         PENABLE = 0;
  reg         PWRITE  = 0;
  reg  [11:0] PADDR   = 0;
  reg  [31:0] PWDATA  = 0;
  reg  [3:0]  PSTRB   = 0;
  reg  [2:0]  PPROT   = 0;
  wire [31:0] PRDATA;
  wire        PREADY;
  wire        PSLVERR;
  reg  [31:0] rdata;

  apb4_regs dut (
    .PCLK    (PCLK),
    .PRESETn (PRESETn),
    .PSEL    (PSEL),
    .PENABLE (PENABLE),
    .PWRITE  (PWRITE),
    .PADDR   (PADDR),
    .PWDATA  (PWDATA),
    .PSTRB   (PSTRB),
    .PPROT   (PPROT),
    .PRDATA  (PRDATA),
    .PREADY  (PREADY),
    .PSLVERR (PSLVERR)
  );

  always #5 PCLK = ~PCLK;

  task apb_write(input [11:0] addr, input [31:0] data, input [3:0] strb, input [2:0] prot);
    begin
      @(posedge PCLK);
      PSEL    <= 1;
      PENABLE <= 0;
      PWRITE  <= 1;
      PADDR   <= addr;
      PWDATA  <= data;
      PSTRB   <= strb;
      PPROT   <= prot;
      @(posedge PCLK);
      PENABLE <= 1;
      @(posedge PCLK);
      while (!PREADY)
        @(posedge PCLK);
      $display("%0t  write 0x%h = 0x%h strb=%b prot=%b%s", $time, addr, data, strb, prot, PSLVERR ? "  PSLVERR" : "");
      PSEL    <= 0;
      PENABLE <= 0;
    end
  endtask

  task apb_read(input [11:0] addr, input [2:0] prot, output [31:0] data);
    begin
      @(posedge PCLK);
      PSEL    <= 1;
      PENABLE <= 0;
      PWRITE  <= 0;
      PADDR   <= addr;
      PSTRB   <= 4'b0000;                // PSTRB must be low for reads
      PPROT   <= prot;
      @(posedge PCLK);
      PENABLE <= 1;
      @(posedge PCLK);
      while (!PREADY)
        @(posedge PCLK);
      data = PRDATA;
      $display("%0t  read  0x%h = 0x%h          prot=%b%s", $time, addr, data, prot, PSLVERR ? "  PSLVERR" : "");
      PSEL    <= 0;
      PENABLE <= 0;
    end
  endtask

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, tb);
    #22 PRESETn = 1;
    apb_write(12'h000, 32'h1111_1111, 4'b1111, 3'b000);   // all four bytes
    apb_write(12'h000, 32'hAABB_CCDD, 4'b0101, 3'b000);   // bytes 0 and 2 only
    apb_read (12'h000, 3'b000, rdata);
    apb_write(12'h004, 32'h5EC0_0001, 4'b1111, 3'b000);   // Secure write to KEY
    apb_write(12'h004, 32'hBAD0_0000, 4'b1111, 3'b010);   // Non-secure write: error
    apb_read (12'h004, 3'b010, rdata);                    // Non-secure read: error
    apb_read (12'h004, 3'b000, rdata);                    // Secure read
    #20 $finish;
  end
endmodule
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.