1// 2// Copyright 2019 Ettus Research, A National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: sim_axi_ram 7// 8// Description: 9// 10// Simulation model for a basic AXI4 memory mapped memory. A few notes on its 11// behavior: 12// 13// - This model does not reorder requests (regardless of WID/RID). All 14// requests are evaluated strictly in order. 15// - The only supported response is OKAY 16// - This model supports misaligned memory accesses, which cause a 17// simulation warning. 18// - A reset does not clear the memory contents 19// - The memory itself is implemented using an associative array (sparse 20// matrix) so that large memories can be supported. 21// - This model is half duplex, meaning read and write data transfers won't 22// happen at the same time. A new data transfer won't begin until the 23// previous one has completed. 24// 25 26module sim_axi_ram #( 27 parameter AWIDTH = 32, 28 parameter DWIDTH = 64, 29 parameter IDWIDTH = 2, 30 parameter BIG_ENDIAN = 0, 31 parameter STALL_PROB = 25 32) ( 33 input logic s_aclk, 34 input logic s_aresetn, 35 36 // Write Address Channel 37 input logic [IDWIDTH-1:0] s_axi_awid, 38 input logic [ AWIDTH-1:0] s_axi_awaddr, 39 input logic [ 7:0] s_axi_awlen, 40 input logic [ 2:0] s_axi_awsize, 41 input logic [ 1:0] s_axi_awburst, 42 input logic s_axi_awvalid, 43 output logic s_axi_awready, 44 45 // Write Data Channel 46 input logic [ DWIDTH-1:0] s_axi_wdata, 47 input logic [DWIDTH/8-1:0] s_axi_wstrb, 48 input logic s_axi_wlast, 49 input logic s_axi_wvalid, 50 output logic s_axi_wready, 51 52 // Write Response Channel 53 output logic [IDWIDTH-1:0] s_axi_bid, 54 output logic [ 1:0] s_axi_bresp, 55 output logic s_axi_bvalid, 56 input logic s_axi_bready, 57 58 // Read Address Channel 59 input logic [IDWIDTH-1:0] s_axi_arid, 60 input logic [ AWIDTH-1:0] s_axi_araddr, 61 input logic [ 7:0] s_axi_arlen, 62 input logic [ 2:0] s_axi_arsize, 63 input logic [ 1:0] s_axi_arburst, 64 input logic s_axi_arvalid, 65 output logic s_axi_arready, 66 67 // Read Data Channel 68 output logic [ 0:0] s_axi_rid, 69 output logic [DWIDTH-1:0] s_axi_rdata, 70 output logic [ 1:0] s_axi_rresp, 71 output logic s_axi_rlast, 72 output logic s_axi_rvalid, 73 input logic s_axi_rready 74); 75 76 localparam DEBUG = 0; 77 78 // Define a mask that can be used to tell which 4K window is being addressed 79 localparam [AWIDTH-1:0] MASK_4K = {AWIDTH{1'b1}} << 12; 80 81 82 //--------------------------------------------------------------------------- 83 // Data Types 84 //--------------------------------------------------------------------------- 85 86 typedef enum logic [1:0] { FIXED, INCR, WRAP } burst_t; 87 typedef enum logic [1:0] { OKAY, EXOKAY, SLVERR, DECERR } resp_t; 88 89 typedef struct packed { 90 longint count; // Number of requests to wait for before executing 91 logic [IDWIDTH-1:0] id; 92 logic [AWIDTH-1:0] addr; 93 logic [8:0] len; // Add an extra bit, since actual true length is +1 94 logic [7:0] size; // Add extra bits to store size in bytes, instead of clog2(size) 95 burst_t burst; 96 } req_t; 97 98 // Make the address type an extra bit wide so that we can detect 99 // out-of-bounds accesses easily. 100 typedef bit [AWIDTH:0] addr_t; 101 102 // Data word type 103 typedef logic [DWIDTH-1:0] data_t; 104 105 // Mask to indicate which bits should be written. 106 typedef bit [DWIDTH/8-1:0] mask_t; 107 108 109 //--------------------------------------------------------------------------- 110 // Data Structures 111 //--------------------------------------------------------------------------- 112 113 byte memory [addr_t]; // Byte addressable memory 114 mailbox #(req_t) read_req = new(); // Read request queue 115 mailbox #(req_t) write_req = new(); // Write request queue 116 mailbox #(req_t) write_resp = new(); // Write response queue 117 118 longint req_count; // Number of requests received 119 longint compl_count; // Number of requests completed 120 121 122 //--------------------------------------------------------------------------- 123 // External Configuration Interface 124 //--------------------------------------------------------------------------- 125 126 int waddr_stall_prob = STALL_PROB; 127 int wdata_stall_prob = STALL_PROB; 128 int wresp_stall_prob = STALL_PROB; 129 int raddr_stall_prob = STALL_PROB; 130 int rdata_stall_prob = STALL_PROB; 131 132 // Set ALL stall probabilities to the same value 133 function void set_stall_prob(int probability); 134 assert(probability >= 0 && probability <= 100) else begin 135 $error("Probability must be from 0 to 100"); 136 end 137 waddr_stall_prob = probability; 138 wdata_stall_prob = probability; 139 wresp_stall_prob = probability; 140 raddr_stall_prob = probability; 141 rdata_stall_prob = probability; 142 endfunction : set_stall_prob 143 144 // Set WRITE stall probabilities to the same value 145 function void set_write_stall_prob(int probability); 146 assert(probability >= 0 && probability <= 100) else begin 147 $error("Probability must be from 0 to 100"); 148 end 149 waddr_stall_prob = probability; 150 wdata_stall_prob = probability; 151 wresp_stall_prob = probability; 152 endfunction : set_write_stall_prob 153 154 // Set READ stall probabilities to the same value 155 function void set_read_stall_prob(int probability); 156 assert(probability >= 0 && probability <= 100) else begin 157 $error("Probability must be from 0 to 100"); 158 end 159 raddr_stall_prob = probability; 160 rdata_stall_prob = probability; 161 endfunction : set_read_stall_prob 162 163 // Set Write Address Channel stall probability 164 function void set_waddr_stall_prob(int probability); 165 assert(probability >= 0 && probability <= 100) else begin 166 $error("Probability must be from 0 to 100"); 167 end 168 waddr_stall_prob = probability; 169 endfunction : set_waddr_stall_prob 170 171 // Set Write Data Channel stall probability 172 function void set_wdata_stall_prob(int probability); 173 assert(probability >= 0 && probability <= 100) else begin 174 $error("Probability must be from 0 to 100"); 175 end 176 wdata_stall_prob = probability; 177 endfunction : set_wdata_stall_prob 178 179 // Set Write Response Channel stall probability 180 function void set_wresp_stall_prob(int probability); 181 assert(probability >= 0 && probability <= 100) else begin 182 $error("Probability must be from 0 to 100"); 183 end 184 wresp_stall_prob = probability; 185 endfunction : set_wresp_stall_prob 186 187 // Set Read Address Channel stall probability 188 function void set_raddr_stall_prob(int probability); 189 assert(probability >= 0 && probability <= 100) else begin 190 $error("Probability must be from 0 to 100"); 191 end 192 raddr_stall_prob = probability; 193 endfunction : set_raddr_stall_prob 194 195 // Set Read Data Channel stall probability 196 function void set_rdata_stall_prob(int probability); 197 assert(probability >= 0 && probability <= 100) else begin 198 $error("Probability must be from 0 to 100"); 199 end 200 rdata_stall_prob = probability; 201 endfunction : set_rdata_stall_prob 202 203 // Get Write Address Channel stall probability 204 function int get_waddr_stall_prob(); 205 return waddr_stall_prob; 206 endfunction : get_waddr_stall_prob 207 208 // Get Write Data Channel stall probability 209 function int get_wdata_stall_prob(); 210 return wdata_stall_prob; 211 endfunction : get_wdata_stall_prob 212 213 // Get Write Response Channel stall probability 214 function int get_wresp_stall_prob(); 215 return wresp_stall_prob; 216 endfunction : get_wresp_stall_prob 217 218 // Get Read Address Channel stall probability 219 function int get_raddr_stall_prob(); 220 return raddr_stall_prob; 221 endfunction : get_raddr_stall_prob 222 223 // Get Read Data Channel stall probability 224 function int get_rdata_stall_prob(); 225 return rdata_stall_prob; 226 endfunction : get_rdata_stall_prob 227 228 229 230 //--------------------------------------------------------------------------- 231 // Helper Functions 232 //--------------------------------------------------------------------------- 233 234 function data_t read_mem(addr_t byte_addr, int num_bytes); 235 data_t data; 236 addr_t incr; 237 238 if (BIG_ENDIAN) begin 239 byte_addr = byte_addr + num_bytes-1; 240 incr = -1; 241 end else begin 242 incr = 1; 243 end 244 245 for (int i = 0; i < num_bytes; i++) begin 246 if (byte_addr >= 2**AWIDTH) begin 247 $fatal(1, "Read extends beyond memory range"); 248 end 249 if (memory.exists(byte_addr)) data[i*8 +: 8] = memory[byte_addr]; 250 else data[i*8 +: 8] = 'X; 251 byte_addr += incr; 252 end 253 254 return data; 255 endfunction : read_mem 256 257 258 function void write_mem(addr_t byte_addr, int num_bytes, data_t data, mask_t mask); 259 addr_t incr; 260 261 if (BIG_ENDIAN) begin 262 byte_addr = byte_addr + num_bytes-1; 263 incr = -1; 264 end else begin 265 incr = 1; 266 end 267 268 for (int i = 0; i < num_bytes; i++) begin 269 if (mask[i]) begin 270 if (byte_addr >= 2**AWIDTH) begin 271 $fatal(1, "Write extends beyond memory range"); 272 end 273 memory[byte_addr] = data[i*8 +: 8]; 274 end 275 byte_addr += incr; 276 end 277 endfunction : write_mem 278 279 280 //--------------------------------------------------------------------------- 281 // Write Requests 282 //--------------------------------------------------------------------------- 283 284 initial begin : write_req_proc 285 req_t req; 286 burst_t burst; 287 288 s_axi_awready <= 0; 289 290 forever begin 291 @(posedge s_aclk); 292 if (!s_aresetn) continue; 293 294 if (s_axi_awvalid) begin 295 if (s_axi_awready) begin 296 req.count = req_count; 297 req.id = s_axi_awid; 298 req.addr = s_axi_awaddr; 299 req.len = s_axi_awlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1 300 req.size = 2**s_axi_awsize; // Store as true size in bytes, not clog2(size) 301 req.burst = burst_t'(s_axi_awburst); 302 303 // Check that the request is valid 304 assert (!$isunknown(req)) else begin 305 $fatal(1, "Write request signals are unknown"); 306 end 307 assert (s_axi_araddr % (DWIDTH/8) == 0) else begin 308 $warning("Unaligned memory write"); 309 end 310 assert (2**s_axi_awsize <= DWIDTH/8) else begin 311 $fatal(1, "AWSIZE must not be larger than DWIDTH"); 312 end 313 assert ($cast(burst, s_axi_awburst)) else begin 314 $fatal(1, "Invalid AWBURST value"); 315 end 316 assert ((s_axi_awaddr & MASK_4K) == 317 ((s_axi_awaddr + (s_axi_awlen+1)*(2**s_axi_awsize) - 1) & MASK_4K)) else begin 318 $fatal(1, "Memory write burst crosses 4 KiB boundary"); 319 end 320 321 if (DEBUG) begin 322 $display("WRITE REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m", 323 req.id, req.addr, req.len, req.size, req.burst.name, $realtime); 324 end 325 326 req_count++; 327 write_req.put(req); 328 end 329 330 // Randomly deassert ready 331 s_axi_awready <= $urandom_range(99) < waddr_stall_prob ? 0 : 1; 332 end 333 end 334 end : write_req_proc 335 336 337 //--------------------------------------------------------------------------- 338 // Read Requests 339 //--------------------------------------------------------------------------- 340 341 initial begin : read_req_proc 342 req_t req; 343 burst_t burst; 344 345 s_axi_arready <= 0; 346 347 forever begin 348 @(posedge s_aclk); 349 if (!s_aresetn) continue; 350 351 if (s_axi_arvalid) begin 352 if (s_axi_arready) begin 353 req.count = req_count; 354 req.id = s_axi_arid; 355 req.addr = s_axi_araddr; 356 req.len = s_axi_arlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1 357 req.size = 2**s_axi_arsize; // Store as true size in bytes, not clog2(size) 358 req.burst = burst_t'(s_axi_arburst); 359 360 // Check that the request is valid 361 assert(!$isunknown(req)) else begin 362 $fatal(1, "Read request signals are unknown"); 363 end 364 assert(s_axi_araddr % (DWIDTH/8) == 0) else begin 365 $warning("Unaligned memory read"); 366 end 367 assert(2**s_axi_arsize <= DWIDTH/8) else begin 368 $fatal(1, "ARSIZE must not be larger than DWIDTH"); 369 end 370 assert ($cast(burst, s_axi_awburst)) else begin 371 $fatal(1, "Invalid ARBURST value"); 372 end 373 assert ((s_axi_araddr & MASK_4K) == 374 ((s_axi_araddr + (s_axi_arlen+1)*(2**s_axi_arsize) - 1) & MASK_4K)) else begin 375 $fatal(1, "Memory read burst crosses 4 KiB boundary"); 376 end 377 378 if (DEBUG) begin 379 $display("READ REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m", 380 req.id, req.addr, req.len, req.size, req.burst.name, $realtime); 381 end 382 383 req_count++; 384 read_req.put(req); 385 end 386 387 // Randomly deassert ready to cause a stall 388 s_axi_arready <= $urandom_range(99) < raddr_stall_prob ? 0 : 1; 389 end 390 end 391 end : read_req_proc 392 393 394 //--------------------------------------------------------------------------- 395 // Write Data 396 //--------------------------------------------------------------------------- 397 398 initial begin : write_data_proc 399 req_t req; 400 bit [AWIDTH-1:0] addr; 401 402 forever begin 403 // Wait for the next write request 404 s_axi_wready <= 0; 405 write_req.get(req); 406 407 // Wait for previous requests to complete 408 while (compl_count < req.count) begin 409 @(posedge s_aclk); 410 if (!s_aresetn) break; 411 end 412 413 // If reset was asserted, clear the request queue and start over 414 if (!s_aresetn) begin 415 while(write_req.try_get(req)); 416 continue; 417 end 418 419 // Iterate over the number of words in the request 420 for (int i = 0; i < req.len; ) begin 421 @(posedge s_aclk); 422 if (!s_aresetn) break; 423 424 // Check if we have a new data word 425 if (s_axi_wvalid) begin 426 if (s_axi_wready) begin 427 // Check the inputs 428 if ($isunknown(s_axi_wstrb)) begin 429 $fatal(1, "WSTRB is unknown"); 430 end 431 if ($isunknown(s_axi_wdata)) begin 432 $warning(1, "WDATA is unknown; data will be changed to zero"); 433 end 434 435 case (req.burst) 436 FIXED : begin 437 addr = req.addr; 438 end 439 INCR : begin 440 // If the address rolls over, we've reached the end of the 441 // memory and we should stop here. 442 addr = req.addr + i*req.size; 443 if (addr < req.addr) break; 444 end 445 WRAP : begin 446 // Allow roll-over 447 addr = req.addr + i*req.size; 448 end 449 endcase 450 451 write_mem(addr, req.size, s_axi_wdata, s_axi_wstrb); 452 453 if (DEBUG) begin 454 $display("WRITE: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, STRB=%X, %t, %m", 455 i, addr, s_axi_wdata, req.size, s_axi_wstrb, $realtime); 456 end 457 458 i++; 459 end 460 461 // Randomly deassert ready to cause a stall 462 s_axi_wready <= $urandom_range(99) < wdata_stall_prob ? 0 : 1; 463 end 464 end // for 465 466 // If reset was asserted, clear the request queue and start over 467 if (!s_aresetn) begin 468 while(write_req.try_get(req)); 469 continue; 470 end 471 472 compl_count++; 473 474 // Enqueue write response 475 write_resp.put(req); 476 477 // Make sure WLAST asserted for the last word. If not we report an error. 478 // Per the AXI4 standard, "a slave is not required to use the WLAST 479 // signal" because "a slave can calculate the last write data transfer 480 // from the burst length AWLEN". 481 if (s_axi_wlast != 1'b1) begin 482 $error("WLAST not asserted on last word of burst"); 483 end 484 485 end // forever 486 end : write_data_proc 487 488 489 //--------------------------------------------------------------------------- 490 // Write Response 491 //--------------------------------------------------------------------------- 492 493 initial begin : write_resp_proc 494 req_t resp; 495 bit [AWIDTH-1:0] addr; 496 497 forever begin 498 s_axi_bid <= 'X; 499 s_axi_bresp <= 'X; 500 s_axi_bvalid <= 0; 501 502 // Wait for the next write response 503 write_resp.get(resp); 504 @(posedge s_aclk); 505 506 // If there's a reset, clear the response queue and start over 507 if (!s_aresetn) begin 508 while(write_resp.try_get(resp)); 509 continue; 510 end 511 512 // Randomly keep bvalid deasserted for next word to cause a stall 513 if ($urandom_range(99) < wresp_stall_prob) begin 514 do begin 515 @(posedge s_aclk); 516 if (!s_aresetn) break; 517 end while ($urandom_range(99) < wresp_stall_prob); 518 519 // If reset was asserted, clear the response queue and start over 520 if (!s_aresetn) begin 521 while(write_resp.try_get(resp)); 522 continue; 523 end 524 end 525 526 // Output the next response 527 s_axi_bid <= resp.id; 528 s_axi_bresp <= OKAY; 529 s_axi_bvalid <= 1; 530 531 if (DEBUG) begin 532 $display("WRITE RESP: ID=%X, %t, %m", resp.id, $realtime); 533 end 534 535 // Wait for the response to be accepted 536 do begin 537 @(posedge s_aclk); 538 if (!s_aresetn) break; 539 end while (!s_axi_bready); 540 541 // Output the next response 542 s_axi_bid <= 'X; 543 s_axi_bresp <= 'X; 544 s_axi_bvalid <= 0; 545 546 // If reset was asserted, clear the response queue and start over 547 if (!s_aresetn) begin 548 while(write_resp.try_get(resp)); 549 continue; 550 end 551 end // forever 552 end : write_resp_proc 553 554 555 //--------------------------------------------------------------------------- 556 // Read Data 557 //--------------------------------------------------------------------------- 558 559 initial begin : read_data_proc 560 req_t req; 561 bit [AWIDTH-1:0] addr; 562 logic [DWIDTH-1:0] data; 563 564 forever begin 565 s_axi_rid <= 'X; 566 s_axi_rdata <= 'X; 567 s_axi_rresp <= 'X; 568 s_axi_rlast <= 'X; 569 s_axi_rvalid <= 0; 570 571 // Wait for the next read request 572 read_req.get(req); 573 574 // Wait for previous requests to complete 575 do begin 576 @(posedge s_aclk); 577 if (!s_aresetn) break; 578 end while (compl_count < req.count); 579 580 // If reset was asserted, clear the request queue and start over 581 if (!s_aresetn) begin 582 while(read_req.try_get(req)); 583 continue; 584 end 585 586 for (int i = 0; i < req.len; i++) begin 587 // Randomly keep rvalid deasserted for next word to cause a stall 588 if ($urandom_range(99) < rdata_stall_prob) begin 589 do begin 590 @(posedge s_aclk); 591 if (!s_aresetn) break; 592 end while ($urandom_range(99) < rdata_stall_prob); 593 if (!s_aresetn) break; 594 end 595 596 case (req.burst) 597 FIXED : begin 598 addr = req.addr; 599 end 600 INCR : begin 601 // If the address rolls over, we've reached the end of the memory 602 // and we should stop here. 603 addr = req.addr + i*req.size; 604 if (addr < req.addr) break; 605 end 606 WRAP : begin 607 // Allow roll-over 608 addr = req.addr + i*req.size; 609 end 610 endcase 611 612 // Read the memory 613 data = read_mem(addr, req.size); 614 615 // Output the next word 616 s_axi_rid <= req.id; 617 s_axi_rdata <= data; 618 s_axi_rresp <= OKAY; 619 s_axi_rlast <= (i == req.len-1); 620 s_axi_rvalid <= 1; 621 622 if (DEBUG) begin 623 $display("READ: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, %t, %m", i, addr, data, req.size, $realtime); 624 end 625 626 // Wait for the word to be captured 627 do begin 628 @(posedge s_aclk); 629 if (!s_aresetn) break; 630 end while (!s_axi_rready); 631 632 s_axi_rid <= 'X; 633 s_axi_rdata <= 'X; 634 s_axi_rresp <= 'X; 635 s_axi_rlast <= 'X; 636 s_axi_rvalid <= 0; 637 end // for 638 639 // If reset was asserted, clear the request queue and start over 640 if (!s_aresetn) begin 641 while(read_req.try_get(req)); 642 end 643 644 compl_count++; 645 646 end // forever 647 end : read_data_proc 648 649endmodule 650