#ifndef __BS_PRIM_MOD_REGFILE_H__ #define __BS_PRIM_MOD_REGFILE_H__ #include #include #include "bluesim_kernel_api.h" #include "bluesim_probes.h" #include "bs_mem_file.h" #include "bs_module.h" #include "bs_vcd.h" #include "bs_range_tracker.h" // forward declaration template class MOD_RegFile; // Handler for binary mem files template class BinFormatHandler : public FormatHandler { public: BinFormatHandler(MOD_RegFile* reg_file, bool check_ranges, unsigned int addr_width, unsigned int data_width, const AT& range_start, const AT& range_end) : FormatHandler(), rf(reg_file), addr_bits(addr_width), data_bits(data_width), start(range_start), end(range_end), check(check_ranges) { addr = start; decreasing = (start > end); } virtual ~BinFormatHandler() {} virtual tMemFileStatus updateAddress(const char* addr_str) { if (!parse_hex(&addr, addr_str, addr_bits)) return MF_BAD_FORMAT; if (check && (addr < start || addr > end)) return MF_OUT_OF_BOUNDS; return MF_ACCEPTED; } virtual tMemFileStatus setEntry(const char* data_str) { tMemFileStatus status; if (addr < start || addr > end) { status = MF_IGNORED; } else { DT value; init_val(value, data_bits); if (!parse_bin(&value, data_str, data_bits)) status = MF_BAD_FORMAT; else { rf->METH_upd(addr, value, true); status = MF_ACCEPTED; rt.setAddr(addr); } } if (decreasing) --addr; else ++addr; return status; } virtual void checkRange(const char* filename, const char* memname) { rt.checkRange(filename, memname, start, end); } private: MOD_RegFile* rf; unsigned int addr_bits; unsigned int data_bits; AT start; AT end; AT addr; bool check; bool decreasing; RangeTracker rt; }; // Handler for hex mem files template class HexFormatHandler : public FormatHandler { public: HexFormatHandler(MOD_RegFile* reg_file, bool check_ranges, unsigned int addr_width, unsigned int data_width, const AT& range_start, const AT& range_end) : FormatHandler(), rf(reg_file), addr_bits(addr_width), data_bits(data_width), start(range_start), end(range_end), check(check_ranges) { addr = start; decreasing = (start > end); } virtual ~HexFormatHandler() {} virtual tMemFileStatus updateAddress(const char* addr_str) { if (!parse_hex(&addr, addr_str, addr_bits)) return MF_BAD_FORMAT; if (check && (addr < start || addr > end)) return MF_OUT_OF_BOUNDS; else return MF_ACCEPTED; } virtual tMemFileStatus setEntry(const char* data_str) { tMemFileStatus status; if (addr < start || addr > end) { status = MF_IGNORED; } else { DT value; init_val(value, data_bits); if (!parse_hex(&value, data_str, data_bits)) status = MF_BAD_FORMAT; else { rf->METH_upd(addr, value, true); status = MF_ACCEPTED; rt.setAddr(addr); } } if (decreasing) --addr; else ++addr; return status; } virtual void checkRange(const char* filename, const char* memname) { rt.checkRange(filename, memname, start, end); } private: MOD_RegFile* rf; unsigned int addr_bits; unsigned int data_bits; AT start; AT end; AT addr; bool check; bool decreasing; RangeTracker rt; }; template const unsigned int* index_rf_fn(void* base, tUInt64 addr); // This is the definition of the RegFile primitive template class MOD_RegFile : public Module { public: MOD_RegFile(tSimStateHdl simHdl, const char* name, Module* parent, unsigned int addr_width, unsigned int data_width, const AT& lo, const AT& hi) : Module(simHdl, name, parent), addr_bits(addr_width), data_bits(data_width), lo_addr(lo), hi_addr(hi), upd_at(~bk_now(sim_hdl)), proxy(NULL) { if (vcd_is_backing_instance(sim_hdl)) return; // do not allocate storage for backing instance init_storage(); init_symbols(); } MOD_RegFile(tSimStateHdl simHdl, const char* name, Module* parent, const std::string& memfile, unsigned int addr_width, unsigned int data_width, const AT& lo, const AT& hi, bool bin_format) : Module(simHdl, name, parent), addr_bits(addr_width), data_bits(data_width), lo_addr(lo), hi_addr(hi), upd_at(~bk_now(sim_hdl)), proxy(NULL) { if (vcd_is_backing_instance(sim_hdl)) return; // do not allocate storage for backing instance init_storage(); init_from_file(memfile, bin_format); init_symbols(); } ~MOD_RegFile() { delete_blocks(top_level,0); delete proxy; } // shared initialization routines private: void init_storage() { last_word = hi_addr - lo_addr; // partition address space for sparse storage num_levels = (addr_bits + 9) / 10; if ((num_levels > 1) && (addr_bits % 10 > 0) && (addr_bits % 10 < 5)) --num_levels; level_bits = new unsigned char[num_levels]; unsigned int bits_remaining = addr_bits; for (unsigned int i = num_levels; i > 0; --i) { if (bits_remaining < 15) { level_bits[i-1] = bits_remaining; bits_remaining = 0; } else if (bits_remaining > 16) { level_bits[i-1] = 10; bits_remaining -= 10; } else { level_bits[i-1] = 8; bits_remaining -= 8; } } // allocate top-level storage block top_level = new_block(0); // initialize address and data storage init_val(upd_addr, addr_bits); write_undet(&upd_addr, addr_bits); init_val(upd_prev, data_bits); write_undet(&upd_prev, data_bits); } void init_symbols() { // initialize symbols symbol_count = 3; symbols = new tSym[symbol_count]; range.lo = (unsigned long long) lo_addr; range.hi = (unsigned long long) hi_addr; range.base = (void*) this; range.fetch = index_rf_fn; symbols[0].key = ""; symbols[0].info = SYM_RANGE | data_bits << 4; symbols[0].value = (void*)(&range); symbols[1].key = "high_addr"; symbols[1].info = SYM_PARAM | addr_bits << 4; symbols[1].value = (void*)(&hi_addr); symbols[2].key = "low_addr"; symbols[2].info = SYM_PARAM | addr_bits << 4; symbols[2].value = (void*)(&lo_addr); } void init_from_file(const std::string& memfile, bool bin_format) { FormatHandler* reader; if (bin_format) reader = new BinFormatHandler(this, true, addr_bits, data_bits, lo_addr, hi_addr); else reader = new HexFormatHandler(this, true, addr_bits, data_bits, lo_addr, hi_addr); read_mem_file(memfile.c_str(), inst_name, reader); } void* new_block(unsigned int level) { unsigned int nEntries = 1 << level_bits[level]; if (level == (num_levels - 1)) { DT* data = new DT[nEntries]; for (unsigned int n = 0; n < nEntries; ++n) { init_val(data[n], data_bits); write_undet(&(data[n]), data_bits); } return (void*) data; } else { void** ptrs = new void*[nEntries]; for (unsigned int n = 0; n < nEntries; ++n) ptrs[n] = NULL; return (void*) ptrs; } } void delete_blocks(void* ptr, unsigned int level) { if (level == (num_levels - 1)) { DT* data = (DT*) ptr; delete[] data; } else { void** ptrs = (void**) ptr; unsigned int nEntries = 1 << level_bits[level]; for (unsigned int n = 0; n < nEntries; ++n) { if (ptrs[n] != NULL) delete_blocks(ptrs[n], level+1); } delete[] ptrs; } } DT* lookup_value(const AT& addr, bool alloc) { // figure out the target index and which bits of the address to use unsigned long long idx = addr - lo_addr; unsigned int shift = addr_bits; void* ptr = top_level; unsigned int level = 0; while (true) { shift -= level_bits[level]; unsigned int mask = (1 << level_bits[level]) - 1; unsigned int n = (idx >> shift) & mask; if (level == (num_levels - 1)) { DT* data = (DT*) ptr; return &(data[n]); } else { void** ptrs = (void**) ptr; ++level; if (ptrs[n] == NULL) { if (alloc) ptrs[n] = new_block(level); else return NULL; } ptr = ptrs[n]; } } } public: // Note: there is RegFileWCF variant of RegFile that // allows upd before sub, but sub should be able to read the // value from the beginning of the cycle. const DT METH_sub(const AT& addr) { if (addr < lo_addr || addr > hi_addr) { FileTarget dest(stdout); printf("Warning: RegFile '"); write_name(&dest); printf("' -- Read address is out of bounds: "); dump_val(addr, addr_bits); putchar('\n'); DT v; init_val(v, data_bits); write_undet(&v, data_bits); return v; } else if ((upd_addr == addr) && bk_is_same_time(sim_hdl, upd_at)) { return upd_prev; } else { DT* value_ptr = lookup_value(addr, false); if (value_ptr != NULL) { return *value_ptr; } else { DT v; init_val(v, data_bits); write_undet(&v, data_bits); return v; } } } void METH_upd(const AT& addr, const DT& val, bool immediate = false) { DT* value_ptr = lookup_value(addr, true); if (value_ptr != NULL) { if (!immediate) { upd_at = bk_now(sim_hdl); upd_addr = addr; upd_prev = *value_ptr; } *value_ptr = val; } else { FileTarget dest(stdout); printf("Warning: RegFile '"); write_name(&dest); printf("' -- Write address is out of bounds: "); dump_val(addr, addr_bits); putchar('\n'); } } public: const unsigned int* data_index(tUInt64 addr) { DT* value_ptr = lookup_value(addr, true); if (value_ptr != NULL) return symbol_value(value_ptr, data_bits); else return NULL; } void dump_state(unsigned int indent) { printf("%*s%s = ", indent, "", inst_name); if (last_word < 16) { DT* data = (DT*) top_level; printf("{ "); for (unsigned long long n = 0llu; n <= last_word; ++n) { if (n > 0) printf(", "); dump_val(lo_addr + n, addr_bits); printf(": "); dump_val(data[n], data_bits); } printf(" }"); } else { printf("", (unsigned long long) last_word + 1); } // dump write address and data if (upd_at == bk_now(sim_hdl)) { printf(" (Wrote "); dump_val(upd_addr, addr_bits); printf(": "); DT* value_ptr = lookup_value(upd_addr,true); dump_val(*value_ptr, data_bits); printf(")"); } putchar('\n'); } unsigned int dump_VCD_defs(unsigned int num) { // Memory contents are not dumped // Please update ../lib/tcllib/bluespec/Waves.tcl proc correct_regfile_names // when vcd dumping is enabled. return (num); } void dump_VCD(tVCDDumpType /* unused */, MOD_RegFile& /* unused */) { // Memory contents are not dumped } // RegFile data members private: unsigned int addr_bits; unsigned int data_bits; AT lo_addr; AT hi_addr; AT last_word; unsigned int num_levels; unsigned char* level_bits; void* top_level; tTime upd_at; AT upd_addr; DT upd_prev; // range structure for symbolic access to RegFile data Range range; // proxy access facility private: BluespecProbe* proxy; public: BluespecProbe& getProbe() { if (proxy == NULL) proxy = new BluespecProbe(this, bounds, has_elem, read_rf, write_rf); return (*proxy); } // static helper functions for probe implementation private: static AT bounds(void* obj, bool hi) { MOD_RegFile* rf = (MOD_RegFile*) obj; return (hi ? rf->hi_addr : rf->lo_addr); } static bool has_elem(void* obj, AT addr) { MOD_RegFile* rf = (MOD_RegFile*) obj; return ((addr >= rf->lo_addr) && (addr <= rf->hi_addr)); } static const DT& read_rf(void* obj, AT addr) { MOD_RegFile* rf = (MOD_RegFile*) obj; DT* value_ptr = rf->lookup_value(addr, true); if (value_ptr != NULL) return *value_ptr; else return read_rf(obj, rf->lo_addr); // out-of-bounds } static bool write_rf(void* obj, AT addr, const DT& data) { MOD_RegFile* rf = (MOD_RegFile*) obj; rf->METH_upd(addr,data,true); return has_elem(obj,addr); } public: friend class BinFormatHandler; friend class HexFormatHandler; }; // Function to index into RegFile data array template const unsigned int* index_rf_fn(void* base, tUInt64 addr) { MOD_RegFile* rf = (MOD_RegFile*) base; return rf->data_index(addr); } #endif /* __BS_PRIM_MOD_REGFILE_H__ */