blob: 3ff7249acc4c1668456b1ed644d15f950a7b88cd [file]
//
// -------------------------------------------------------------
// Copyright 2004-2008 Synopsys, Inc.
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the 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.
// -------------------------------------------------------------
//
`ifndef VMM_MAM__SV
`define VMM_MAM__SV
typedef class vmm_mam_cfg;
typedef class vmm_mam;
typedef class vmm_ral_mem;
typedef class vmm_ral_mem_burst;
class vmm_mam_region;
/*local*/ bit [63:0] Xstart_offsetX; // Can't be local since function
/*local*/ bit [63:0] Xend_offsetX; // calls not supported in constraints
local int unsigned len;
local int unsigned n_bytes;
local vmm_mam parent;
/*local*/ vmm_ral_vreg XvregX;
extern /*local*/ function new(bit [63:0] start_offset,
bit [63:0] end_offset,
int unsigned len,
int unsigned n_bytes,
vmm_mam parent);
extern function bit [63:0] get_start_offset();
extern function bit [63:0] get_end_offset();
extern function int unsigned get_len();
extern function int unsigned get_n_bytes();
extern function string psdisplay(string prefix = "");
extern function void release_region();
extern function vmm_ral_mem get_memory();
extern function vmm_ral_vreg get_virtual_registers();
extern task write(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
extern task read(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
extern task burst_write(output vmm_rw::status_e status,
input vmm_ral_mem_burst burst,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
extern task burst_read(output vmm_rw::status_e status,
input vmm_ral_mem_burst burst,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
extern task poke(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
extern task peek(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
endclass
class vmm_mam_allocator;
int unsigned len;
rand bit [63:0] start_offset;
bit [63:0] min_offset;
bit [63:0] max_offset;
vmm_mam_region in_use[$];
constraint vmam_mam_allocator_valid {
start_offset >= min_offset;
start_offset <= max_offset - len + 1;
}
constraint vmam_mam_allocator_no_overlap {
foreach (in_use[i]) {
!(start_offset <= in_use[i].Xend_offsetX &&
start_offset + len - 1 >= in_use[i].Xstart_offsetX);
}
}
endclass
class vmm_mam;
typedef enum {GREEDY, THRIFTY} alloc_mode_e;
typedef enum {BROAD, NEARBY} locality_e;
vmm_log log;
local vmm_mam_cfg cfg;
vmm_mam_allocator default_alloc;
local vmm_ral_mem memory;
local vmm_mam_region in_use[$];
local int for_each_idx = -1;
extern function new(string name,
vmm_mam_cfg cfg,
vmm_ral_mem mem=null);
extern function vmm_mam_cfg reconfigure(vmm_mam_cfg cfg = null);
extern function vmm_mam_region reserve_region(bit [63:0] start_offset,
int unsigned n_bytes);
extern function vmm_mam_region request_region(int unsigned n_bytes,
vmm_mam_allocator alloc = null);
extern function void release_region(vmm_mam_region region);
extern function void release_all_regions();
extern function string psdisplay(string prefix = "");
extern function vmm_mam_region for_each(bit reset = 0);
extern function vmm_ral_mem get_memory();
endclass: vmm_mam
class vmm_mam_cfg;
rand int unsigned n_bytes;
rand bit [63:0] start_offset;
rand bit [63:0] end_offset;
rand vmm_mam::alloc_mode_e mode;
rand vmm_mam::locality_e locality;
constraint vmm_mam_cfg_valid {
end_offset > start_offset;
n_bytes < 64;
}
endclass
//------------------------------------------------------------------
//
// Implementation
//
function vmm_mam_region::new(bit [63:0] start_offset,
bit [63:0] end_offset,
int unsigned len,
int unsigned n_bytes,
vmm_mam parent);
this.Xstart_offsetX = start_offset;
this.Xend_offsetX = end_offset;
this.len = len;
this.n_bytes = n_bytes;
this.parent = parent;
this.XvregX = null;
endfunction: new
function bit [63:0] vmm_mam_region::get_start_offset();
return this.Xstart_offsetX;
endfunction: get_start_offset
function bit [63:0] vmm_mam_region::get_end_offset();
return this.Xend_offsetX;
endfunction: get_end_offset
function int unsigned vmm_mam_region::get_len();
return this.len;
endfunction: get_len
function int unsigned vmm_mam_region::get_n_bytes();
return this.n_bytes;
endfunction: get_n_bytes
function string vmm_mam_region::psdisplay(string prefix = "");
$sformat(psdisplay, "%s['h%h:'h%h]", prefix,
this.Xstart_offsetX, this.Xend_offsetX);
endfunction: psdisplay
function void vmm_mam_region::release_region();
this.parent.release_region(this);
endfunction
function vmm_ral_mem vmm_mam_region::get_memory();
return this.parent.get_memory();
endfunction: get_memory
function vmm_ral_vreg vmm_mam_region::get_virtual_registers();
return this.XvregX;
endfunction: get_virtual_registers
function vmm_mam::new(string name,
vmm_mam_cfg cfg,
vmm_ral_mem mem = null);
this.log = new("Memory Allocation Manager", name);
this.cfg = cfg;
this.memory = mem;
this.default_alloc = new;
endfunction: new
function vmm_mam_cfg vmm_mam::reconfigure(vmm_mam_cfg cfg = null);
if (cfg == null) return this.cfg;
// Cannot reconfigure n_bytes
if (cfg.n_bytes !== this.cfg.n_bytes) begin
`vmm_error(this.log,
$psprintf("Cannot reconfigure Memory Allocation Manager with a different number of bytes (%0d !== %0d)",
cfg.n_bytes, this.cfg.n_bytes));
return this.cfg;
end
// All currently allocated regions must fall within the new space
`foreach (this.in_use,i) begin
if (this.in_use[i].get_start_offset() < cfg.start_offset ||
this.in_use[i].get_end_offset() > cfg.end_offset) begin
`vmm_error(this.log,
$psprintf("Cannot reconfigure Memory Allocation Manager with a currently allocated region outside of the managed address range ([%0d:%0d] outside of [%0d:%0d])",
this.in_use[i].get_start_offset(),
this.in_use[i].get_end_offset(),
cfg.start_offset, cfg.end_offset));
return this.cfg;
end
end
reconfigure = this.cfg;
this.cfg = cfg;
endfunction: reconfigure
function vmm_mam_region vmm_mam::reserve_region(bit [63:0] start_offset,
int unsigned n_bytes);
bit [63:0] end_offset;
if (n_bytes == 0) begin
`vmm_error(this.log, "Cannot reserve 0 bytes");
return null;
end
if (start_offset < this.cfg.start_offset) begin
`vmm_error(this.log, $psprintf("Cannot reserve before start of memory space: 'h%h < 'h%h",
start_offset, this.cfg.start_offset));
return null;
end
end_offset = start_offset + ((n_bytes-1) / this.cfg.n_bytes);
n_bytes = (end_offset - start_offset + 1) * this.cfg.n_bytes;
if (end_offset > this.cfg.end_offset) begin
`vmm_error(this.log, $psprintf("Cannot reserve past end of memory space: 'h%h > 'h%h",
end_offset, this.cfg.end_offset));
return null;
end
`vmm_trace(this.log, $psprintf("Attempting to reserve ['h%h:'h%h]...",
start_offset, end_offset));
`foreach (this.in_use,i) begin
if (start_offset <= this.in_use[i].get_end_offset() &&
end_offset >= this.in_use[i].get_start_offset()) begin
// Overlap!
`vmm_error(this.log, $psprintf("Cannot reserve ['h%h:'h%h] because it overlaps with %s",
start_offset, end_offset,
this.in_use[i].psdisplay()));
return null;
end
// Regions are stored in increasing start offset
if (start_offset > this.in_use[i].get_start_offset()) begin
reserve_region = new(start_offset, end_offset,
end_offset - start_offset + 1, n_bytes, this);
this.in_use.insert(i, reserve_region);
return reserve_region;
end
end
reserve_region = new(start_offset, end_offset,
end_offset - start_offset + 1, n_bytes, this);
this.in_use.push_back(reserve_region);
endfunction: reserve_region
function vmm_mam_region vmm_mam::request_region(int unsigned n_bytes,
vmm_mam_allocator alloc = null);
if (alloc == null) alloc = this.default_alloc;
alloc.len = (n_bytes-1) / this.cfg.n_bytes + 1;
alloc.min_offset = this.cfg.start_offset;
alloc.max_offset = this.cfg.end_offset;
alloc.in_use = this.in_use;
if (!alloc.randomize()) begin
`vmm_error(this.log, "Unable to randomize allocator");
return null;
end
return reserve_region(alloc.start_offset, n_bytes);
endfunction: request_region
function void vmm_mam::release_region(vmm_mam_region region);
if (region == null) return;
`foreach (this.in_use,i) begin
if (this.in_use[i] == region) begin
this.in_use.delete(i);
return;
end
end
`vmm_error(this.log, region.psdisplay("Attempting to release unallocated region "));
endfunction: release_region
function void vmm_mam::release_all_regions();
`ifdef VCS2006_06
// Work-around for NYI feature in VCS2006.06
// but IEEE 1800-2009 compliant
this.in_use.delete();
`else
`ifdef INCA
this.in_use.delete();
`else
// Works in VCS2008.03 or later
// IEEE 1800-2005 compliant
this.in_use = '{};
`endif
`endif
endfunction: release_all_regions
function string vmm_mam::psdisplay(string prefix = "");
$sformat(psdisplay, "%sAllocated memory regions:\n", prefix);
`foreach (this.in_use,i) begin
$sformat(psdisplay, "%s%s %s\n", psdisplay, prefix,
this.in_use[i].psdisplay());
end
endfunction: psdisplay
function vmm_mam_region vmm_mam::for_each(bit reset = 0);
if (reset) this.for_each_idx = -1;
this.for_each_idx++;
if (this.for_each_idx >= this.in_use.size()) begin
return null;
end
return this.in_use[this.for_each_idx];
endfunction: for_each
function vmm_ral_mem vmm_mam::get_memory();
return this.memory;
endfunction: get_memory
task vmm_mam_region::write(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to write to an offset outside of the allocated region (%0d > %0d)",
offset, this.len));
status = vmm_rw::ERROR;
return;
end
mem.write(status, offset + this.get_start_offset(), value,
path, domain,
data_id, scenario_id, stream_id);
endtask: write
task vmm_mam_region::read(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to read from an offset outside of the allocated region (%0d > %0d)",
offset, this.len));
status = vmm_rw::ERROR;
return;
end
mem.read(status, offset + this.get_start_offset(), value,
path, domain,
data_id, scenario_id, stream_id);
endtask: read
task vmm_mam_region::burst_write(output vmm_rw::status_e status,
input vmm_ral_mem_burst burst,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id =-1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (burst.start_offset > this.len ||
burst.max_offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to burst-write to an offset outside of the allocated region ([%0d:%0d] > %0d)",
burst.start_offset, burst.max_offset, this.len));
status = vmm_rw::ERROR;
return;
end
begin
vmm_ral_mem_burst b = new burst;
b.start_offset += this.get_start_offset();
b.max_offset += this.get_start_offset();
mem.burst_write(status, b, value,
path, domain,
data_id, scenario_id, stream_id);
end
endtask: burst_write
task vmm_mam_region::burst_read(output vmm_rw::status_e status,
input vmm_ral_mem_burst burst,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value[],
input vmm_ral::path_e path = vmm_ral::DEFAULT,
input string domain = "",
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (burst.start_offset > this.len ||
burst.max_offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to burst-read from an offset outside of the allocated region ([%0d:%0d] > %0d)",
burst.start_offset, burst.max_offset, this.len));
status = vmm_rw::ERROR;
return;
end
begin
vmm_ral_mem_burst b = new burst;
b.start_offset += this.get_start_offset();
b.max_offset += this.get_start_offset();
mem.burst_read(status, b, value,
path, domain,
data_id, scenario_id, stream_id);
end
endtask: burst_read
task vmm_mam_region::poke(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
input bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to poke to an offset outside of the allocated region (%0d > %0d)",
offset, this.len));
status = vmm_rw::ERROR;
return;
end
mem.poke(status, offset + this.get_start_offset(), value,
data_id, scenario_id, stream_id);
endtask: poke
task vmm_mam_region::peek(output vmm_rw::status_e status,
input bit [`VMM_RAL_ADDR_WIDTH-1:0] offset,
output bit [`VMM_RAL_DATA_WIDTH-1:0] value,
input int data_id = -1,
input int scenario_id = -1,
input int stream_id = -1);
vmm_ral_mem mem = this.parent.get_memory();
if (mem == null) begin
`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");
status = vmm_rw::ERROR;
return;
end
if (offset > this.len) begin
`vmm_error(this.parent.log,
$psprintf("Attempting to peek from an offset outside of the allocated region (%0d > %0d)",
offset, this.len));
status = vmm_rw::ERROR;
return;
end
mem.peek(status, offset + this.get_start_offset(), value,
data_id, scenario_id, stream_id);
endtask: peek
`endif // VMM_MAM__SV