tb_axi_lite_regs.sv 13.3 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
// Copyright (c) 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>

// Directed Random Verification Testbench for `axi_lite_regs`.

`include "axi/assign.svh"

/// Testbench for validating the module `axi_lite_regs`.
module tb_axi_lite_regs #(
  /// Define the parameter `RegNumBytes` of the DUT.
  parameter int unsigned              TbRegNumBytes  = 32'd200,
  /// Define the parameter `AxiReadOnly` of the DUT.
  parameter logic [TbRegNumBytes-1:0] TbAxiReadOnly  = {{TbRegNumBytes-18{1'b0}}, 18'b101110111111000000},
  /// Define the parameter `PrivProtOnly` of the DUT.
  parameter bit                       TbPrivProtOnly = 1'b0,
  /// Define the parameter `SecuProtOnly` of the DUT.
  parameter bit                       TbSecuProtOnly = 1'b0,
  /// Number of random writes generated by the testbench AXI4-Lite random master.
  parameter int unsigned              TbNoWrites     = 32'd1000,
  /// Number of random reads generated by the testbench AXI4-Lite random master.
  parameter int unsigned              TbNoReads      = 32'd1500
);
  // AXI configuration
  localparam int unsigned AxiAddrWidth = 32'd32;    // Axi Address Width
  localparam int unsigned AxiDataWidth = 32'd32;    // Axi Data Width
  localparam int unsigned AxiStrbWidth = AxiDataWidth / 32'd8;
  // timing parameters
  localparam time CyclTime = 10ns;
  localparam time ApplTime =  2ns;
  localparam time TestTime =  8ns;

  typedef logic [7:0]              byte_t;
  typedef logic [AxiAddrWidth-1:0] axi_addr_t;
  typedef logic [AxiDataWidth-1:0] axi_data_t;
  typedef logic [AxiStrbWidth-1:0] axi_strb_t;

  localparam axi_addr_t StartAddr = axi_addr_t'(0);
  localparam axi_addr_t EndAddr   =
      axi_addr_t'(StartAddr + TbRegNumBytes + TbRegNumBytes/5);

  localparam byte_t [TbRegNumBytes-1:0] RegRstVal = '0;

  typedef axi_test::axi_lite_rand_master #(
    // AXI interface parameters
    .AW ( AxiAddrWidth ),
    .DW ( AxiDataWidth ),
    // Stimuli application and test time
    .TA ( ApplTime  ),
    .TT ( TestTime  ),
    .MIN_ADDR ( StartAddr ),
    .MAX_ADDR ( EndAddr   ),
    .MAX_READ_TXNS  ( 10 ),
    .MAX_WRITE_TXNS ( 10 )
  ) rand_lite_master_t;

  // -------------
  // DUT signals
  // -------------
  logic clk;
  // DUT signals
  logic rst_n;
  logic end_of_sim;
  // Register signals
  byte_t [TbRegNumBytes-1:0] reg_d,     reg_q;
  logic  [TbRegNumBytes-1:0] wr_active, rd_active, reg_load;

  // -------------------------------
  // AXI Interfaces
  // -------------------------------
  AXI_LITE #(
    .AXI_ADDR_WIDTH ( AxiAddrWidth      ),
    .AXI_DATA_WIDTH ( AxiDataWidth      )
  ) master ();
  AXI_LITE_DV #(
    .AXI_ADDR_WIDTH ( AxiAddrWidth      ),
    .AXI_DATA_WIDTH ( AxiDataWidth      )
  ) master_dv (clk);
  `AXI_LITE_ASSIGN(master, master_dv)

  // -------------------------------
  // AXI Rand Masters and Slaves
  // -------------------------------
  // Masters control simulation run time
  initial begin : proc_generate_axi_traffic
    automatic rand_lite_master_t lite_axi_master = new ( master_dv, "Lite Master");
    automatic axi_data_t      data = '0;
    automatic axi_pkg::resp_t resp = '0;
    end_of_sim <= 1'b0;
    lite_axi_master.reset();
    @(posedge rst_n);
    repeat (5) @(posedge clk);
    // Test known register.
    // Write to it.
    lite_axi_master.write(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0),
        axi_data_t'(64'hDEADBEEFDEADBEEF), axi_strb_t'(8'hFF), resp);
    if (TbPrivProtOnly || TbSecuProtOnly) begin
      assert (resp == axi_pkg::RESP_SLVERR) else
          $fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted.");
    end else begin
      assert (resp == axi_pkg::RESP_OKAY) else
          $fatal(1, "Access should be granted");
    end
    // Read from it.
    lite_axi_master.read(axi_addr_t'(32'h0000_0000), axi_pkg::prot_t'('0), data, resp);
    if (TbPrivProtOnly || TbSecuProtOnly) begin
      // Expect error response
      assert (resp == axi_pkg::RESP_SLVERR) else
          $fatal(1, "PrivProtOnly || SecuProtOnly: Unprivileged access was falsely granted.");
      assert (data == axi_data_t'(32'hBA5E1E55)) else
          $fatal(1, "Data is unexpected, should be axi_data_t'(32'hBA5E1E55).");
    end else begin
      assert (resp == axi_pkg::RESP_OKAY) else
          $fatal(1, "Access should be granted.");
      // Checking of the expecetd read data is handled in `proc_check_read_data`.
    end

    // Let random stimuli application checking is separate.
    lite_axi_master.run(TbNoReads, TbNoWrites);
    end_of_sim <= 1'b1;
  end

  initial begin : proc_generate_direct_load
    for (int unsigned i = 0; i < TbRegNumBytes; i++) begin
      automatic int unsigned j = i;
      fork
        toggle_load(j);
      join_none
    end
  end

  task automatic toggle_load(int unsigned byte_i);
    byte_t       rand_val;
    int unsigned rand_wait;
    @(posedge rst_n);
    reg_load[byte_i] = 1'b0;
    reg_d[byte_i]    = 8'h00;
    @(posedge clk);
    forever begin
      rand_wait = $urandom_range(0, 20);
      repeat (rand_wait) @(posedge clk);
      rand_val          = byte_t'($urandom());
      reg_d[byte_i]    <= #ApplTime rand_val;
      reg_load[byte_i] <= #ApplTime 1'b1;
      @(posedge clk);
      reg_d[byte_i]    <= #ApplTime '0;
      reg_load[byte_i] <= #ApplTime 1'b0;
    end
  endtask : toggle_load

  initial begin : proc_check_read_data
    automatic axi_data_t      exp_rdata[$];
    automatic axi_pkg::resp_t exp_resp[$];

    @(posedge rst_n);
    forever begin
      @(posedge clk);
      #(TestTime);

      // Push the expected data back.
      if (master.ar_valid && master.ar_ready) begin
        automatic int unsigned ar_idx = ((master.ar_addr - StartAddr)
            >> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth));
        automatic axi_data_t      r_data = axi_data_t'(0);
        automatic axi_pkg::resp_t r_resp = axi_pkg::RESP_SLVERR;
        for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
          if ((ar_idx+i) < TbRegNumBytes) begin
            r_data[8*i+:8] = reg_q[ar_idx+i];
            r_resp         = axi_pkg::RESP_OKAY;
          end else begin
            r_data[8*i+:8] = 8'hxx;
          end
        end
        // check the right protection access
        if (TbPrivProtOnly && !master.ar_prot[0]) begin
          r_resp = axi_pkg::RESP_SLVERR;
        end
        if (TbSecuProtOnly && !master.ar_prot[1]) begin
          r_resp = axi_pkg::RESP_SLVERR;
        end
        exp_resp.push_back(r_resp);
        exp_rdata.push_back(r_data);
      end

      // compare data if there is a value expected
      if (master.r_valid && master.r_ready) begin
        automatic axi_data_t r_data = exp_rdata.pop_front();
        if (master.r_resp == axi_pkg::RESP_OKAY) begin
          for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
            automatic byte_t exp_byte = r_data[8*i+:8];
            if (exp_byte !== 8'hxx) begin
              assert (master.r_data[8*i+:8] == exp_byte) else
                  $error("Unexpected read data: exp: %0h observed: %0h", r_data, master.r_data);
              assert (master.r_resp == axi_pkg::RESP_OKAY);
            end
          end
        end else if (master.r_resp == axi_pkg::RESP_SLVERR) begin
          assert (master.r_data == axi_data_t'(32'hBA5E1E55));
        end else begin
          $error("Slave responded with false response: %0h", master.r_resp);
        end
      end
    end
  end

  axi_pkg::resp_t b_resp_queue[$];

  // Only works as module expects both AW & W valid before it does something
  initial begin : proc_check_write_data
    @(posedge rst_n);
    forever begin
      #TestTime;
      // AW and W is launched, setup the test tasks
      if (master.aw_valid && master.aw_ready && master.w_valid && master.w_ready) begin
        automatic int unsigned aw_idx = ((master.aw_addr - StartAddr)
            >> $clog2(AxiStrbWidth) << $clog2(AxiStrbWidth));
        automatic axi_pkg::resp_t exp_b_resp = (aw_idx < TbRegNumBytes) ?
            axi_pkg::RESP_OKAY : axi_pkg::RESP_SLVERR;
        automatic bit all_ro = 1'b1;
        // check for errors from wrong access protection
        if (TbPrivProtOnly && !master.aw_prot[0]) begin
          exp_b_resp = axi_pkg::RESP_SLVERR;
        end
        if (TbSecuProtOnly && !master.aw_prot[1]) begin
          exp_b_resp = axi_pkg::RESP_SLVERR;
        end
        // Check if all accesses bytes are read only
        for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
          if (!TbAxiReadOnly[aw_idx+i]) begin
            all_ro = 1'b0;
          end
        end
        if (all_ro) begin
          exp_b_resp = axi_pkg::RESP_SLVERR;
        end

        // do the actual write checking
        if (exp_b_resp == axi_pkg::RESP_OKAY) begin
          // go through every byte
          for (int unsigned i = 0; i < AxiStrbWidth; i++) begin
            if ((aw_idx+i) < TbRegNumBytes) begin
              if (master.w_strb[i]) begin
                automatic int unsigned        j = aw_idx + i;
                automatic byte_t       exp_byte = master.w_data[8*i+:8];
                fork
                  check_q(j, exp_byte);
                join_none
              end
              assert (master.w_strb[i] == wr_active[aw_idx+i]);
            end
          end
        end
        b_resp_queue.push_back(exp_b_resp);
      end
      @(posedge clk);
    end
  end

  initial begin : proc_check_b
    automatic axi_pkg::resp_t exp_b_resp;
    @(posedge rst_n);
    forever begin
      #TestTime;
      if (master.b_valid && master.b_ready) begin
        if (b_resp_queue.size()) begin
          exp_b_resp = b_resp_queue.pop_front();
          assert (exp_b_resp == master.b_resp) else
              $error("Unexpected B response: EXP: %b ACT: %b", exp_b_resp, master.b_resp);
        end else begin
          $error("B response even no AW was sent.");
        end
      end
      @(posedge clk);
    end
  end

  // check that the expected register byte has been written
  task automatic check_q(int unsigned byte_i, byte_t exp_byte);
    automatic byte_t save_byte = (reg_load[byte_i]) ? reg_d[byte_i] : reg_q[byte_i];
    @(posedge clk);
    #TestTime;
    if (!TbAxiReadOnly[byte_i]) begin
      assert(exp_byte == reg_q[byte_i]) else
          $error("AXI write was not registered at byte: %0d", byte_i);
    end else begin
      assert (reg_q[byte_i] == save_byte) else
          $error("AXI write onto read only byte: %0d SAVE: %h EXP: %h ACT %h",
              byte_i, save_byte, exp_byte, reg_q[byte_i]);
    end
  endtask : check_q

  // Some assertions for additional checking.
  default disable iff (~rst_n);
  for (genvar i = 0; i < TbRegNumBytes; i++) begin : gen_check_ro_bytes
    if (TbAxiReadOnly[i]) begin : gen_check_ro
      ro_assert_no_load: assert property (@(posedge clk)
          ((wr_active[i] && !reg_load[i]) |=> $stable(reg_q[i]))) else
          $fatal(1, "ReadOnly byte %0d changed from AXI write, while not direct loaded.", i);
      ro_assert_load: assert property (@(posedge clk)
          ((wr_active[i] && reg_load[i]) |=> (reg_q[i] === $past(reg_d[i])))) else
          $fatal(1, "ReadOnly byte %0d changed from AXI write, while direct loaded.", i);
    end
    load_assert_no_axi: assert property (@(posedge clk)
        (reg_load[i] && !TbAxiReadOnly[i] |-> !wr_active[i])) else
        $fatal(1, "Byte %0d, AXI write onto non read-only and direct load are both active!", i);
    load_assert_data:   assert property (@(posedge clk)
        (reg_load[i] |=> reg_q[i] === $past(reg_d[i]))) else
        $fatal(1, "Byte %0d, direct load was executed falsely.", i);
    stable_assert:      assert property (@(posedge clk)
        (!reg_load[i] && !wr_active[i]) |=> $stable(reg_q[i])) else
        $fatal(1, "Byte %0d is unstable, when no AXI write or direct load.", i);
  end

  initial begin : proc_stop_sim
    wait (end_of_sim);
    repeat (1000) @(posedge clk);
    $display("Simulation stopped as Master transferred its data.");
    $stop();
  end

  //-----------------------------------
  // Clock generator
  //-----------------------------------
  clk_rst_gen #(
    .ClkPeriod    ( CyclTime ),
    .RstClkCycles ( 5        )
  ) i_clk_gen (
    .clk_o (clk),
    .rst_no(rst_n)
  );

  //-----------------------------------
  // DUT
  //-----------------------------------
  axi_lite_regs_intf #(
    .REG_NUM_BYTES  ( TbRegNumBytes  ),
    .AXI_ADDR_WIDTH ( AxiAddrWidth   ),
    .AXI_DATA_WIDTH ( AxiDataWidth   ),
    .PRIV_PROT_ONLY ( TbPrivProtOnly ),
    .SECU_PROT_ONLY ( TbSecuProtOnly ),
    .AXI_READ_ONLY  ( TbAxiReadOnly  ),
    .REG_RST_VAL    ( RegRstVal      )
  ) i_axi_lite_regs (
    .clk_i       ( clk       ),
    .rst_ni      ( rst_n     ),
    .slv         ( master    ),
    .wr_active_o ( wr_active ),
    .rd_active_o ( rd_active ),
    .reg_d_i     ( reg_d     ),
    .reg_load_i  ( reg_load  ),
    .reg_q_o     ( reg_q     )
  );
endmodule