| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #pragma once |
| |
| #include <map> |
| #include <string> |
| #include <svdpi.h> |
| #include <vector> |
| |
| #include "ranged_map.h" |
| |
| enum MemImageType { |
| kMemImageUnknown = 0, |
| kMemImageElf, |
| kMemImageVmem, |
| }; |
| |
| // The "load" location of a memory area. base is the lowest address in |
| // the area, and should correspond to an ELF file's LMA. size is the |
| // length of the area in bytes. |
| struct MemAreaLoc { |
| uint32_t base; |
| uint32_t size; |
| }; |
| |
| struct MemArea { |
| std::string name; // Unique identifier |
| std::string location; // Design scope location |
| uint32_t width_byte; // Memory width in bytes |
| MemAreaLoc addr_loc; // Address location. If !size, location is unknown. |
| }; |
| |
| // Staged data for a given memory area. |
| // |
| // This is represented as an ordered list of disjoint segments (as loaded from |
| // an ELF file). |
| // |
| // Once it is nonempty, the class maintains the invariant that min_addr_ / |
| // max_addr_ is the smallest / largest byte offset with valid data. |
| class StagedMem { |
| public: |
| StagedMem() : min_addr_(~(uint32_t)0), max_addr_(0) {} |
| |
| // Add a segment to the tracked memory |
| void AddSegment(uint32_t offset, std::vector<uint8_t> &&seg); |
| |
| // Glob together the tracked segments, interspersing them with |
| // zeros, and return as a single flat array. |
| std::vector<uint8_t> GetFlat() const; |
| |
| typedef RangedMap<uint32_t, std::vector<uint8_t>> SegMap; |
| |
| std::pair<uint32_t, uint32_t> GetBounds() const { |
| return std::make_pair(min_addr_, max_addr_); |
| } |
| const SegMap &GetSegs() const { return segs_; } |
| |
| private: |
| uint32_t min_addr_, max_addr_; |
| SegMap segs_; |
| }; |
| |
| /** |
| * Provide various memory loading utilities for verilog simulations |
| * |
| * These utilities require the corresponding DPI functions: |
| * simutil_memload() |
| * simutil_set_mem() |
| * to be defined somewhere as SystemVerilog functions. |
| */ |
| class DpiMemUtil { |
| public: |
| /** |
| * Register a memory as instantiated by generic ram |
| * |
| * The |name| must be a unique identifier. The function will return false if |
| * |name| is already used. |location| is the path to the scope of the |
| * instantiated memory, which needs to support the DPI-C interfaces |
| * 'simutil_memload' and 'simutil_set_mem' used for 'vmem' and 'elf' files, |
| * respectively. |
| * |
| * The |width_bit| argument specifies the with in bits of the target memory |
| * instance (used for packing data). This must be a multiple of 8. If |
| * |addr_loc| is not null, it gives the base and size of the memory for |
| * loading in the address space (corresponding to LMAs in an ELF file). |
| * |
| * Memories must be registered before command arguments are parsed by |
| * ParseCommandArgs() in order for them to be known. |
| */ |
| bool RegisterMemoryArea(const std::string name, const std::string location, |
| size_t width_bit, const MemAreaLoc *addr_loc); |
| |
| /** |
| * Register a memory with default width (32bits) |
| */ |
| bool RegisterMemoryArea(const std::string name, const std::string location); |
| |
| /** |
| * Guess the type of the file at |path|. |
| * |
| * If |type| is non-null, it is the name of an image type and will be used. |
| * Otherwise, the check is based on |path|. If |type| is not a valid name or |
| * if the function can't guess from the path, throws a std::runtime_error |
| * with a message about what went wrong. |
| * |
| * Never returns kMemImageUnknown. |
| */ |
| static MemImageType GetMemImageType(const std::string &path, |
| const char *type); |
| |
| /** |
| * Print a list of all registered memory regions |
| * |
| * @see RegisterMemoryArea() |
| */ |
| void PrintMemRegions() const; |
| |
| /** |
| * Load the file at filepath into the named memory. If type is |
| * kMemImageUnknown, the file type is determined from the path. |
| */ |
| void LoadFileToNamedMem(bool verbose, const std::string &name, |
| const std::string &filepath, MemImageType type); |
| |
| /** |
| * Load an ELF file, placing segments in memories by LMA. |
| * |
| * Replaces any data currently in the staging area. |
| */ |
| void LoadElfToMemories(bool verbose, const std::string &filepath); |
| |
| /** |
| * Load an ELF file into a staging area in this object, which can then be |
| * accessed with GetMemoryData(). |
| * |
| * If the load fails, raises a std::exception with information about what |
| * happened. |
| */ |
| void StageElf(bool verbose, const std::string &path); |
| |
| /** |
| * Get the contents of the staging area by memory name |
| */ |
| const StagedMem &GetMemoryData(const std::string &mem_name) const; |
| |
| private: |
| // Memory area registry |
| std::map<std::string, MemArea> name_to_mem_; |
| RangedMap<uint32_t, MemArea *> addr_to_mem_; |
| |
| // Staging area, loaded by StageElf. The map is keyed by names of memories |
| // stored in name_to_mem_. We also ensure that every segment in a StagedMem |
| // for a memory starts at an address that's aligned for the word width of |
| // that memory. Note: we don't also check segments' lengths are aligned. |
| std::map<std::string, StagedMem> staging_area_; |
| const StagedMem empty_; |
| |
| /** |
| * Find a region containing for the given segment's addresses. |
| * Raises a std::exception if none is found. |
| */ |
| const MemArea &GetRegionForSegment(const std::string &path, int seg_idx, |
| uint32_t lma, uint32_t mem_sz) const; |
| }; |