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); 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); 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); 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 // *NOT* IEEE compliant :-( 00380 this.in_use.delete(); 00381 `else 00382 // Works in VCS2008.03 00383 this.in_use = '{}; 00384 `endif 00385 endfunction: release_all_regions 00386 00387 00388 function string vmm_mam::psdisplay(string prefix); 00389 $sformat(psdisplay, "%sAllocated memory regions:\n", prefix); 00390 foreach (this.in_use[i]) begin 00391 $sformat(psdisplay, "%s%s %s\n", psdisplay, prefix, 00392 this.in_use[i].psdisplay()); 00393 end 00394 endfunction: psdisplay 00395 00396 00397 function vmm_mam_region vmm_mam::for_each(bit reset); 00398 if (reset) this.for_each_idx = -1; 00399 00400 this.for_each_idx++; 00401 00402 if (this.for_each_idx >= this.in_use.size()) begin 00403 return null; 00404 end 00405 00406 return this.in_use[this.for_each_idx]; 00407 endfunction: for_each 00408 00409 00410 function vmm_ral_mem vmm_mam::get_memory(); 00411 return this.memory; 00412 endfunction: get_memory 00413 00414 00415 task vmm_mam_region::write(output vmm_rw::status_e status, 00416 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset, 00417 input bit [`VMM_RAL_DATA_WIDTH-1:0] value, 00418 input vmm_ral::path_e path, 00419 input string domain, 00420 input int data_id, 00421 input int scenario_id, 00422 input int stream_id); 00423 00424 vmm_ral_mem mem = this.parent.get_memory(); 00425 00426 if (mem == null) begin 00427 `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"); 00428 status = vmm_rw::ERROR; 00429 return; 00430 end 00431 00432 if (offset > this.len) begin 00433 `vmm_error(this.parent.log, 00434 $psprintf("Attempting to write to an offset outside of the allocated region (%0d > %0d)", 00435 offset, this.len)); 00436 status = vmm_rw::ERROR; 00437 return; 00438 end 00439 00440 mem.write(status, offset + this.get_start_offset(), value, 00441 path, domain, 00442 data_id, scenario_id, stream_id); 00443 endtask: write 00444 00445 00446 task vmm_mam_region::read(output vmm_rw::status_e status, 00447 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset, 00448 output bit [`VMM_RAL_DATA_WIDTH-1:0] value, 00449 input vmm_ral::path_e path, 00450 input string domain, 00451 input int data_id, 00452 input int scenario_id, 00453 input int stream_id); 00454 vmm_ral_mem mem = this.parent.get_memory(); 00455 00456 if (mem == null) begin 00457 `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"); 00458 status = vmm_rw::ERROR; 00459 return; 00460 end 00461 00462 if (offset > this.len) begin 00463 `vmm_error(this.parent.log, 00464 $psprintf("Attempting to read from an offset outside of the allocated region (%0d > %0d)", 00465 offset, this.len)); 00466 status = vmm_rw::ERROR; 00467 return; 00468 end 00469 00470 mem.read(status, offset + this.get_start_offset(), value, 00471 path, domain, 00472 data_id, scenario_id, stream_id); 00473 endtask: read 00474 00475 00476 task vmm_mam_region::burst_write(output vmm_rw::status_e status, 00477 input vmm_ral_mem_burst burst, 00478 input bit [`VMM_RAL_DATA_WIDTH-1:0] value[], 00479 input vmm_ral::path_e path, 00480 input string domain, 00481 input int data_id, 00482 input int scenario_id, 00483 input int stream_id); 00484 vmm_ral_mem mem = this.parent.get_memory(); 00485 00486 if (mem == null) begin 00487 `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"); 00488 status = vmm_rw::ERROR; 00489 return; 00490 end 00491 00492 if (burst.start_offset > this.len || 00493 burst.max_offset > this.len) begin 00494 `vmm_error(this.parent.log, 00495 $psprintf("Attempting to burst-write to an offset outside of the allocated region ([%0d:%0d] > %0d)", 00496 burst.start_offset, burst.max_offset, this.len)); 00497 status = vmm_rw::ERROR; 00498 return; 00499 end 00500 00501 begin 00502 vmm_ral_mem_burst b = new burst; 00503 b.start_offset += this.get_start_offset(); 00504 b.max_offset += this.get_start_offset(); 00505 00506 mem.burst_write(status, b, value, 00507 path, domain, 00508 data_id, scenario_id, stream_id); 00509 end 00510 endtask: burst_write 00511 00512 00513 task vmm_mam_region::burst_read(output vmm_rw::status_e status, 00514 input vmm_ral_mem_burst burst, 00515 output bit [`VMM_RAL_DATA_WIDTH-1:0] value[], 00516 input vmm_ral::path_e path, 00517 input string domain, 00518 input int data_id, 00519 input int scenario_id, 00520 input int stream_id); 00521 vmm_ral_mem mem = this.parent.get_memory(); 00522 00523 if (mem == null) begin 00524 `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"); 00525 status = vmm_rw::ERROR; 00526 return; 00527 end 00528 00529 if (burst.start_offset > this.len || 00530 burst.max_offset > this.len) begin 00531 `vmm_error(this.parent.log, 00532 $psprintf("Attempting to burst-read from an offset outside of the allocated region ([%0d:%0d] > %0d)", 00533 burst.start_offset, burst.max_offset, this.len)); 00534 status = vmm_rw::ERROR; 00535 return; 00536 end 00537 00538 begin 00539 vmm_ral_mem_burst b = new burst; 00540 b.start_offset += this.get_start_offset(); 00541 b.max_offset += this.get_start_offset(); 00542 00543 mem.burst_read(status, b, value, 00544 path, domain, 00545 data_id, scenario_id, stream_id); 00546 end 00547 endtask: burst_read 00548 00549 00550 task vmm_mam_region::poke(output vmm_rw::status_e status, 00551 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset, 00552 input bit [`VMM_RAL_DATA_WIDTH-1:0] value, 00553 input int data_id, 00554 input int scenario_id, 00555 input int stream_id); 00556 vmm_ral_mem mem = this.parent.get_memory(); 00557 00558 if (mem == null) begin 00559 `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"); 00560 status = vmm_rw::ERROR; 00561 return; 00562 end 00563 00564 if (offset > this.len) begin 00565 `vmm_error(this.parent.log, 00566 $psprintf("Attempting to poke to an offset outside of the allocated region (%0d > %0d)", 00567 offset, this.len)); 00568 status = vmm_rw::ERROR; 00569 return; 00570 end 00571 00572 mem.poke(status, offset + this.get_start_offset(), value, 00573 data_id, scenario_id, stream_id); 00574 endtask: poke 00575 00576 00577 task vmm_mam_region::peek(output vmm_rw::status_e status, 00578 input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset, 00579 output bit [`VMM_RAL_DATA_WIDTH-1:0] value, 00580 input int data_id, 00581 input int scenario_id, 00582 input int stream_id); 00583 vmm_ral_mem mem = this.parent.get_memory(); 00584 00585 if (mem == null) begin 00586 `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"); 00587 status = vmm_rw::ERROR; 00588 return; 00589 end 00590 00591 if (offset > this.len) begin 00592 `vmm_error(this.parent.log, 00593 $psprintf("Attempting to peek from an offset outside of the allocated region (%0d > %0d)", 00594 offset, this.len)); 00595 status = vmm_rw::ERROR; 00596 return; 00597 end 00598 00599 mem.peek(status, offset + this.get_start_offset(), value, 00600 data_id, scenario_id, stream_id); 00601 endtask: peek 00602 00603 00604 `endif // VMM_MAM__SV