00001 //
00002 // -------------------------------------------------------------
00003 // Copyright 2004-2008 Synopsys, Inc.
00004 // All Rights Reserved Worldwide
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the
00007 // "License"); you may not use this file except in
00008 // compliance with the License. You may obtain a copy of
00009 // the License at
00010 //
00011 // http://www.apache.org/licenses/LICENSE-2.0
00012 //
00013 // Unless required by applicable law or agreed to in
00014 // writing, software distributed under the License is
00015 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
00016 // CONDITIONS OF ANY KIND, either express or implied. See
00017 // the License for the specific language governing
00018 // permissions and limitations under the License.
00019 // -------------------------------------------------------------
00020 //
00021
00022
00023 `ifndef VMM_MAM__SV
00024 `define VMM_MAM__SV
00025
00026
00027 typedef class vmm_mam_cfg;
00028 typedef class vmm_mam;
00029
00030 typedef class vmm_ral_mem;
00031 typedef class vmm_ral_mem_burst;
00032
00033
00034 class vmm_mam_region;
00035 /*local*/ bit [63:0] Xstart_offsetX; // Can't be local since function
00036 /*local*/ bit [63:0] Xend_offsetX; // calls not supported in constraints
00037
00038 local int unsigned len;
00039 local int unsigned n_bytes;
00040 local vmm_mam parent;
00041
00042 /*local*/ vmm_ral_vreg XvregX;
00043
00044 extern /*local*/ function new(bit [63:0] start_offset,
00045 bit [63:0] end_offset,
00046 int unsigned len,
00047 int unsigned n_bytes,
00048 vmm_mam parent);
00049
00050 extern function bit [63:0] get_start_offset();
00051 extern function bit [63:0] get_end_offset();
00052
00053 extern function int unsigned get_len();
00054 extern function int unsigned get_n_bytes();
00055
00056 extern function string psdisplay(string prefix = "");
00057
00058 extern function void release_region();
00059
00060 extern function vmm_ral_mem get_memory();
00061 extern function vmm_ral_vreg get_virtual_registers();
00062
00063 extern task write(output vmm_rw::status_e status,
00064 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00065 input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00066 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00067 input string domain = "",
00068 input int data_id = -1,
00069 input int scenario_id = -1,
00070 input int stream_id = -1);
00071
00072 extern task read(output vmm_rw::status_e status,
00073 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00074 output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00075 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00076 input string domain = "",
00077 input int data_id = -1,
00078 input int scenario_id = -1,
00079 input int stream_id = -1);
00080
00081 extern task burst_write(output vmm_rw::status_e status,
00082 input vmm_ral_mem_burst burst,
00083 input bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
00084 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00085 input string domain = "",
00086 input int data_id = -1,
00087 input int scenario_id = -1,
00088 input int stream_id = -1);
00089
00090 extern task burst_read(output vmm_rw::status_e status,
00091 input vmm_ral_mem_burst burst,
00092 output bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
00093 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00094 input string domain = "",
00095 input int data_id = -1,
00096 input int scenario_id = -1,
00097 input int stream_id = -1);
00098
00099 extern task poke(output vmm_rw::status_e status,
00100 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00101 input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00102 input int data_id = -1,
00103 input int scenario_id = -1,
00104 input int stream_id = -1);
00105
00106 extern task peek(output vmm_rw::status_e status,
00107 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00108 output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00109 input int data_id = -1,
00110 input int scenario_id = -1,
00111 input int stream_id = -1);
00112 endclass
00113
00114
00115 class vmm_mam_allocator;
00116 int unsigned len;
00117
00118 rand bit [63:0] start_offset;
00119
00120 bit [63:0] min_offset;
00121 bit [63:0] max_offset;
00122
00123 vmm_mam_region in_use[$];
00124
00125 constraint vmam_mam_allocator_valid {
00126 start_offset >= min_offset;
00127 start_offset <= max_offset - len + 1;
00128 }
00129
00130 constraint vmam_mam_allocator_no_overlap {
00131 foreach (in_use[i]) {
00132 !(start_offset <= in_use[i].Xend_offsetX &&
00133 start_offset + len - 1 >= in_use[i].Xstart_offsetX);
00134 }
00135 }
00136
00137 endclass
00138
00139
00140 class vmm_mam;
00141
00142 typedef enum {GREEDY, THRIFTY} alloc_mode_e;
00143 typedef enum {BROAD, NEARBY} locality_e;
00144
00145 vmm_log log;
00146
00147 local vmm_mam_cfg cfg;
00148
00149 vmm_mam_allocator default_alloc;
00150 local vmm_ral_mem memory;
00151
00152 local vmm_mam_region in_use[$];
00153 local int for_each_idx = -1;
00154
00155 extern function new(string name,
00156 vmm_mam_cfg cfg,
00157 vmm_ral_mem mem=null);
00158
00159 extern function vmm_mam_cfg reconfigure(vmm_mam_cfg cfg = null);
00160
00161 extern function vmm_mam_region reserve_region(bit [63:0] start_offset,
00162 int unsigned n_bytes);
00163 extern function vmm_mam_region request_region(int unsigned n_bytes,
00164 vmm_mam_allocator alloc = null);
00165 extern function void release_region(vmm_mam_region region);
00166 extern function void release_all_regions();
00167
00168
00169 extern function string psdisplay(string prefix = "");
00170 extern function vmm_mam_region for_each(bit reset = 0);
00171 extern function vmm_ral_mem get_memory();
00172
00173 endclass: vmm_mam
00174
00175
00176 class vmm_mam_cfg;
00177 rand int unsigned n_bytes;
00178
00179 rand bit [63:0] start_offset;
00180 rand bit [63:0] end_offset;
00181
00182 rand vmm_mam::alloc_mode_e mode;
00183 rand vmm_mam::locality_e locality;
00184
00185 constraint vmm_mam_cfg_valid {
00186 end_offset > start_offset;
00187 n_bytes < 64;
00188 }
00189 endclass
00190
00191
00192
00193 //------------------------------------------------------------------
00194 //
00195 // Implementation
00196 //
00197
00198 function vmm_mam_region::new(bit [63:0] start_offset,
00199 bit [63:0] end_offset,
00200 int unsigned len,
00201 int unsigned n_bytes,
00202 vmm_mam parent);
00203 this.Xstart_offsetX = start_offset;
00204 this.Xend_offsetX = end_offset;
00205 this.len = len;
00206 this.n_bytes = n_bytes;
00207 this.parent = parent;
00208 this.XvregX = null;
00209 endfunction: new
00210
00211
00212 function bit [63:0] vmm_mam_region::get_start_offset();
00213 return this.Xstart_offsetX;
00214 endfunction: get_start_offset
00215
00216
00217 function bit [63:0] vmm_mam_region::get_end_offset();
00218 return this.Xend_offsetX;
00219 endfunction: get_end_offset
00220
00221
00222 function int unsigned vmm_mam_region::get_len();
00223 return this.len;
00224 endfunction: get_len
00225
00226
00227 function int unsigned vmm_mam_region::get_n_bytes();
00228 return this.n_bytes;
00229 endfunction: get_n_bytes
00230
00231
00232 function string vmm_mam_region::psdisplay(string prefix = "");
00233 $sformat(psdisplay, "%s['h%h:'h%h]", prefix,
00234 this.Xstart_offsetX, this.Xend_offsetX);
00235 endfunction: psdisplay
00236
00237
00238 function void vmm_mam_region::release_region();
00239 this.parent.release_region(this);
00240 endfunction
00241
00242
00243 function vmm_ral_mem vmm_mam_region::get_memory();
00244 return this.parent.get_memory();
00245 endfunction: get_memory
00246
00247
00248 function vmm_ral_vreg vmm_mam_region::get_virtual_registers();
00249 return this.XvregX;
00250 endfunction: get_virtual_registers
00251
00252
00253 function vmm_mam::new(string name,
00254 vmm_mam_cfg cfg,
00255 vmm_ral_mem mem = null);
00256 this.log = new("Memory Allocation Manager", name);
00257 this.cfg = cfg;
00258 this.memory = mem;
00259 this.default_alloc = new;
00260 endfunction: new
00261
00262
00263 function vmm_mam_cfg vmm_mam::reconfigure(vmm_mam_cfg cfg = null);
00264 if (cfg == null) return this.cfg;
00265
00266 // Cannot reconfigure n_bytes
00267 if (cfg.n_bytes !== this.cfg.n_bytes) begin
00268 `vmm_error(this.log,
00269 $psprintf("Cannot reconfigure Memory Allocation Manager with a different number of bytes (%0d !== %0d)",
00270 cfg.n_bytes, this.cfg.n_bytes));
00271 return this.cfg;
00272 end
00273
00274 // All currently allocated regions must fall within the new space
00275 foreach (this.in_use[i]) begin
00276 if (this.in_use[i].get_start_offset() < cfg.start_offset ||
00277 this.in_use[i].get_end_offset() > cfg.end_offset) begin
00278 `vmm_error(this.log,
00279 $psprintf("Cannot reconfigure Memory Allocation Manager with a currently allocated region outside of the managed address range ([%0d:%0d] outside of [%0d:%0d])",
00280 this.in_use[i].get_start_offset(),
00281 this.in_use[i].get_end_offset(),
00282 cfg.start_offset, cfg.end_offset));
00283 return this.cfg;
00284 end
00285 end
00286
00287 reconfigure = this.cfg;
00288 this.cfg = cfg;
00289 endfunction: reconfigure
00290
00291
00292 function vmm_mam_region vmm_mam::reserve_region(bit [63:0] start_offset,
00293 int unsigned n_bytes);
00294 bit [63:0] end_offset;
00295
00296 if (n_bytes == 0) begin
00297 `vmm_error(this.log, "Cannot reserve 0 bytes");
00298 return null;
00299 end
00300
00301 if (start_offset < this.cfg.start_offset) begin
00302 `vmm_error(this.log, $psprintf("Cannot reserve before start of memory space: 'h%h < 'h%h",
00303 start_offset, this.cfg.start_offset));
00304 return null;
00305 end
00306
00307 end_offset = start_offset + ((n_bytes-1) / this.cfg.n_bytes);
00308 n_bytes = (end_offset - start_offset + 1) * this.cfg.n_bytes;
00309
00310 if (end_offset > this.cfg.end_offset) begin
00311 `vmm_error(this.log, $psprintf("Cannot reserve past end of memory space: 'h%h > 'h%h",
00312 end_offset, this.cfg.end_offset));
00313 return null;
00314 end
00315
00316 `vmm_trace(this.log, $psprintf("Attempting to reserve ['h%h:'h%h]...",
00317 start_offset, end_offset));
00318
00319 foreach (this.in_use[i]) begin
00320 if (start_offset <= this.in_use[i].get_end_offset() &&
00321 end_offset >= this.in_use[i].get_start_offset()) begin
00322 // Overlap!
00323 `vmm_error(this.log, $psprintf("Cannot reserve ['h%h:'h%h] because it overlaps with %s",
00324 start_offset, end_offset,
00325 this.in_use[i].psdisplay()));
00326 return null;
00327 end
00328
00329 // Regions are stored in increasing start offset
00330 if (start_offset > this.in_use[i].get_start_offset()) begin
00331 reserve_region = new(start_offset, end_offset,
00332 end_offset - start_offset + 1, n_bytes, this);
00333 this.in_use.insert(i, reserve_region);
00334 return reserve_region;
00335 end
00336 end
00337
00338 reserve_region = new(start_offset, end_offset,
00339 end_offset - start_offset + 1, n_bytes, this);
00340 this.in_use.push_back(reserve_region);
00341 endfunction: reserve_region
00342
00343
00344 function vmm_mam_region vmm_mam::request_region(int unsigned n_bytes,
00345 vmm_mam_allocator alloc = null);
00346 if (alloc == null) alloc = this.default_alloc;
00347
00348 alloc.len = (n_bytes-1) / this.cfg.n_bytes + 1;
00349 alloc.min_offset = this.cfg.start_offset;
00350 alloc.max_offset = this.cfg.end_offset;
00351 alloc.in_use = this.in_use;
00352
00353 if (!alloc.randomize()) begin
00354 `vmm_error(this.log, "Unable to randomize allocator");
00355 return null;
00356 end
00357
00358 return reserve_region(alloc.start_offset, n_bytes);
00359 endfunction: request_region
00360
00361
00362 function void vmm_mam::release_region(vmm_mam_region region);
00363
00364 if (region == null) return;
00365
00366 foreach (this.in_use[i]) begin
00367 if (this.in_use[i] == region) begin
00368 this.in_use.delete(i);
00369 return;
00370 end
00371 end
00372 `vmm_error(this.log, region.psdisplay("Attempting to release unallocated region "));
00373 endfunction: release_region
00374
00375
00376 function void vmm_mam::release_all_regions();
00377 `ifdef VCS2006_06
00378 // Work-around for NYI feature in VCS2006.06
00379 // but IEEE 1800-2009 compliant
00380 this.in_use.delete();
00381 `else
00382 // Works in VCS2008.03 or later
00383 // IEEE 1800-2005 compliant
00384 this.in_use = '{};
00385 `endif
00386 endfunction: release_all_regions
00387
00388
00389 function string vmm_mam::psdisplay(string prefix = "");
00390 $sformat(psdisplay, "%sAllocated memory regions:\n", prefix);
00391 foreach (this.in_use[i]) begin
00392 $sformat(psdisplay, "%s%s %s\n", psdisplay, prefix,
00393 this.in_use[i].psdisplay());
00394 end
00395 endfunction: psdisplay
00396
00397
00398 function vmm_mam_region vmm_mam::for_each(bit reset = 0);
00399 if (reset) this.for_each_idx = -1;
00400
00401 this.for_each_idx++;
00402
00403 if (this.for_each_idx >= this.in_use.size()) begin
00404 return null;
00405 end
00406
00407 return this.in_use[this.for_each_idx];
00408 endfunction: for_each
00409
00410
00411 function vmm_ral_mem vmm_mam::get_memory();
00412 return this.memory;
00413 endfunction: get_memory
00414
00415
00416 task vmm_mam_region::write(output vmm_rw::status_e status,
00417 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00418 input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00419 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00420 input string domain = "",
00421 input int data_id = -1,
00422 input int scenario_id = -1,
00423 input int stream_id = -1);
00424
00425 vmm_ral_mem mem = this.parent.get_memory();
00426
00427 if (mem == null) begin
00428 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::write() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00429 status = vmm_rw::ERROR;
00430 return;
00431 end
00432
00433 if (offset > this.len) begin
00434 `vmm_error(this.parent.log,
00435 $psprintf("Attempting to write to an offset outside of the allocated region (%0d > %0d)",
00436 offset, this.len));
00437 status = vmm_rw::ERROR;
00438 return;
00439 end
00440
00441 mem.write(status, offset + this.get_start_offset(), value,
00442 path, domain,
00443 data_id, scenario_id, stream_id);
00444 endtask: write
00445
00446
00447 task vmm_mam_region::read(output vmm_rw::status_e status,
00448 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00449 output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00450 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00451 input string domain = "",
00452 input int data_id = -1,
00453 input int scenario_id = -1,
00454 input int stream_id = -1);
00455 vmm_ral_mem mem = this.parent.get_memory();
00456
00457 if (mem == null) begin
00458 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::read() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00459 status = vmm_rw::ERROR;
00460 return;
00461 end
00462
00463 if (offset > this.len) begin
00464 `vmm_error(this.parent.log,
00465 $psprintf("Attempting to read from an offset outside of the allocated region (%0d > %0d)",
00466 offset, this.len));
00467 status = vmm_rw::ERROR;
00468 return;
00469 end
00470
00471 mem.read(status, offset + this.get_start_offset(), value,
00472 path, domain,
00473 data_id, scenario_id, stream_id);
00474 endtask: read
00475
00476
00477 task vmm_mam_region::burst_write(output vmm_rw::status_e status,
00478 input vmm_ral_mem_burst burst,
00479 input bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
00480 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00481 input string domain = "",
00482 input int data_id = -1,
00483 input int scenario_id = -1,
00484 input int stream_id =-1);
00485 vmm_ral_mem mem = this.parent.get_memory();
00486
00487 if (mem == null) begin
00488 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::burst_write() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00489 status = vmm_rw::ERROR;
00490 return;
00491 end
00492
00493 if (burst.start_offset > this.len ||
00494 burst.max_offset > this.len) begin
00495 `vmm_error(this.parent.log,
00496 $psprintf("Attempting to burst-write to an offset outside of the allocated region ([%0d:%0d] > %0d)",
00497 burst.start_offset, burst.max_offset, this.len));
00498 status = vmm_rw::ERROR;
00499 return;
00500 end
00501
00502 begin
00503 vmm_ral_mem_burst b = new burst;
00504 b.start_offset += this.get_start_offset();
00505 b.max_offset += this.get_start_offset();
00506
00507 mem.burst_write(status, b, value,
00508 path, domain,
00509 data_id, scenario_id, stream_id);
00510 end
00511 endtask: burst_write
00512
00513
00514 task vmm_mam_region::burst_read(output vmm_rw::status_e status,
00515 input vmm_ral_mem_burst burst,
00516 output bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
00517 input vmm_ral::path_e path = vmm_ral::DEFAULT,
00518 input string domain = "",
00519 input int data_id = -1,
00520 input int scenario_id = -1,
00521 input int stream_id = -1);
00522 vmm_ral_mem mem = this.parent.get_memory();
00523
00524 if (mem == null) begin
00525 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::burst_read() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00526 status = vmm_rw::ERROR;
00527 return;
00528 end
00529
00530 if (burst.start_offset > this.len ||
00531 burst.max_offset > this.len) begin
00532 `vmm_error(this.parent.log,
00533 $psprintf("Attempting to burst-read from an offset outside of the allocated region ([%0d:%0d] > %0d)",
00534 burst.start_offset, burst.max_offset, this.len));
00535 status = vmm_rw::ERROR;
00536 return;
00537 end
00538
00539 begin
00540 vmm_ral_mem_burst b = new burst;
00541 b.start_offset += this.get_start_offset();
00542 b.max_offset += this.get_start_offset();
00543
00544 mem.burst_read(status, b, value,
00545 path, domain,
00546 data_id, scenario_id, stream_id);
00547 end
00548 endtask: burst_read
00549
00550
00551 task vmm_mam_region::poke(output vmm_rw::status_e status,
00552 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00553 input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00554 input int data_id = -1,
00555 input int scenario_id = -1,
00556 input int stream_id = -1);
00557 vmm_ral_mem mem = this.parent.get_memory();
00558
00559 if (mem == null) begin
00560 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::poke() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00561 status = vmm_rw::ERROR;
00562 return;
00563 end
00564
00565 if (offset > this.len) begin
00566 `vmm_error(this.parent.log,
00567 $psprintf("Attempting to poke to an offset outside of the allocated region (%0d > %0d)",
00568 offset, this.len));
00569 status = vmm_rw::ERROR;
00570 return;
00571 end
00572
00573 mem.poke(status, offset + this.get_start_offset(), value,
00574 data_id, scenario_id, stream_id);
00575 endtask: poke
00576
00577
00578 task vmm_mam_region::peek(output vmm_rw::status_e status,
00579 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
00580 output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
00581 input int data_id = -1,
00582 input int scenario_id = -1,
00583 input int stream_id = -1);
00584 vmm_ral_mem mem = this.parent.get_memory();
00585
00586 if (mem == null) begin
00587 `vmm_error(this.parent.log, "Cannot use vmm_mam_region::peek() on a region that was allocated by a Memory Allocation Manager that was not associated with a vmm_ral_mem instance");
00588 status = vmm_rw::ERROR;
00589 return;
00590 end
00591
00592 if (offset > this.len) begin
00593 `vmm_error(this.parent.log,
00594 $psprintf("Attempting to peek from an offset outside of the allocated region (%0d > %0d)",
00595 offset, this.len));
00596 status = vmm_rw::ERROR;
00597 return;
00598 end
00599
00600 mem.peek(status, offset + this.get_start_offset(), value,
00601 data_id, scenario_id, stream_id);
00602 endtask: peek
00603
00604
00605 `endif // VMM_MAM__SV