[dv] Add a ReadWithIntegrity method to Ecc32MemArea The existing Read() method returns a vector of bytes. ReadWithIntegrity returns a vector of 32-bit words, together with booleans showing whether the integrity bits for these words are valid. Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/dv/verilator/cpp/ecc32_mem_area.cc b/hw/dv/verilator/cpp/ecc32_mem_area.cc index 0124170..230d448 100644 --- a/hw/dv/verilator/cpp/ecc32_mem_area.cc +++ b/hw/dv/verilator/cpp/ecc32_mem_area.cc
@@ -29,6 +29,29 @@ "vmem files are not supported for memories with ECC bits"); } +Ecc32MemArea::EccWords Ecc32MemArea::ReadWithIntegrity( + uint32_t word_offset, uint32_t num_words) const { + assert(word_offset + num_words <= num_words_); + + // See MemArea::Write for an explanation for this buffer. + uint8_t minibuf[SV_MEM_WIDTH_BYTES]; + memset(minibuf, 0, sizeof minibuf); + assert(width_byte_ <= sizeof minibuf); + + EccWords ret; + ret.reserve(num_words); + + for (uint32_t i = 0; i < num_words; ++i) { + uint32_t src_word = word_offset + i; + uint32_t phys_addr = ToPhysAddr(src_word); + + ReadToMinibuf(minibuf, phys_addr); + ReadBufferWithIntegrity(ret, minibuf, src_word); + } + + return ret; +} + // Add bits to buf at bit_idx // // buf is assumed to be little-endian, so bit_idx 0 will refer to the bottom @@ -116,3 +139,23 @@ } } } + +void Ecc32MemArea::ReadBufferWithIntegrity( + EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const { + for (int i = 0; i < width_byte_ / 4; ++i) { + uint8_t buf32[4]; + uint32_t w32 = 0; + for (int j = 0; j < 4; ++j) { + uint8_t byte = extract_bits(buf, 39 * i + 8 * j, 8); + buf32[j] = byte; + w32 |= (uint32_t)byte << 8 * j; + } + + uint8_t exp_check_bits = enc_secded_39_32(buf32); + uint8_t check_bits = extract_bits(buf, 39 * i + 32, 7); + bool good = check_bits == exp_check_bits; + + data.push_back(std::make_pair(good, w32)); + } +}
diff --git a/hw/dv/verilator/cpp/ecc32_mem_area.h b/hw/dv/verilator/cpp/ecc32_mem_area.h index 9b0523b..d92948d 100644 --- a/hw/dv/verilator/cpp/ecc32_mem_area.h +++ b/hw/dv/verilator/cpp/ecc32_mem_area.h
@@ -24,6 +24,22 @@ void LoadVmem(const std::string &path) const override; + typedef std::pair<bool, uint32_t> EccWord; + typedef std::vector<EccWord> EccWords; + + /** Read data with validity bits, starting at the given offset. + * + * This is equivalent to MemArea's Read method, but returns 32 bit + * words, each with a boolean saying whether the integrity bits for + * that word are valid or not. + * + * @param word_offset The offset, in words, of the first word that should be + * read. + * + * @param num_words The number of words to read. + */ + EccWords ReadWithIntegrity(uint32_t word_offset, uint32_t num_words) const; + protected: void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], const std::vector<uint8_t> &data, size_t start_idx, @@ -32,6 +48,20 @@ void ReadBuffer(std::vector<uint8_t> &data, const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const override; + + /** Extract the logical words corresponding to the physical memory contents + * in \p buf, together with validity bits. Append them to \p data. + * + * @param data The target, onto which the extracted memory words should + * be appended. + * + * @param buf Source buffer (physical memory bits) + * + * @param src_word Logical address of the location being read + */ + virtual void ReadBufferWithIntegrity(EccWords &data, + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const; }; #endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_
diff --git a/hw/dv/verilator/cpp/mem_area.cc b/hw/dv/verilator/cpp/mem_area.cc index 1687840..e84082b 100644 --- a/hw/dv/verilator/cpp/mem_area.cc +++ b/hw/dv/verilator/cpp/mem_area.cc
@@ -81,21 +81,7 @@ uint32_t src_word = word_offset + i; uint32_t phys_addr = ToPhysAddr(src_word); - { - // Both ToPhysAddr and ReadBuffer might set the scope with `SVScoped`. - // Keep the `SVScoped` here confined to an inner scope so they don't - // interact causing incorrect relative path behaviour. If this fails to - // set scope, it will throw an error which should be caught at this - // function's callsite. - SVScoped scoped(scope_); - if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) { - std::ostringstream oss; - oss << "Could not read memory at byte offset 0x" << std::hex - << src_word * width_byte_ << "."; - throw std::runtime_error(oss.str()); - } - } - + ReadToMinibuf(minibuf, phys_addr); ReadBuffer(ret, minibuf, src_word); } @@ -126,3 +112,13 @@ std::copy_n(reinterpret_cast<const char *>(buf), width_byte_, std::back_inserter(data)); } + +void MemArea::ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const { + SVScoped scoped(scope_); + if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) { + std::ostringstream oss; + oss << "Could not read memory word at physical index 0x" << std::hex + << phys_addr << "."; + throw std::runtime_error(oss.str()); + } +}
diff --git a/hw/dv/verilator/cpp/mem_area.h b/hw/dv/verilator/cpp/mem_area.h index f1c8a6b..7f642a8 100644 --- a/hw/dv/verilator/cpp/mem_area.h +++ b/hw/dv/verilator/cpp/mem_area.h
@@ -109,10 +109,11 @@ * across. Other implementations might undo scrambling, remove ECC bits or * similar. * - * @param data The target, onto which the extracted memory contents should - * be appended. + * @param data The target, onto which the extracted memory contents + * should be appended. * * @param buf Source buffer (physical memory bits) + * * @param src_word Logical address of the location being read */ virtual void ReadBuffer(std::vector<uint8_t> &data, @@ -129,6 +130,13 @@ virtual uint32_t ToPhysAddr(uint32_t logical_addr) const { return logical_addr; } + + /** Read the memory word at phys_addr into minibuf + * + * minibuf should be at least SV_MEM_WIDTH_BYTES in size. See the + * implementation of MemArea::Write() for the details. + */ + void ReadToMinibuf(uint8_t *minibuf, uint32_t phys_addr) const; }; #endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_
diff --git a/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.cc b/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.cc index 87069a7..37ea6b1 100644 --- a/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.cc +++ b/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.cc
@@ -169,20 +169,30 @@ std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]); } +std::vector<uint8_t> ScrambledEcc32MemArea::ReadUnscrambled( + const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const { + std::vector<uint8_t> scrambled_data(buf, buf + GetPhysWidthByte()); + return scramble_decrypt_data( + scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_), + addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_); +} + void ScrambledEcc32MemArea::ReadBuffer(std::vector<uint8_t> &data, const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const { - // Unscramble data from read buffer - std::vector<uint8_t> scrambled_data = - std::vector<uint8_t>(buf, buf + GetPhysWidthByte()); - std::vector<uint8_t> unscrambled_data = scramble_decrypt_data( - scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_), - addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_); - + std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word); // Strip integrity to give final result Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word); } +void ScrambledEcc32MemArea::ReadBufferWithIntegrity( + EccWords &data, const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const { + std::vector<uint8_t> unscrambled_data = ReadUnscrambled(buf, src_word); + // Strip integrity to give final result + Ecc32MemArea::ReadBufferWithIntegrity(data, &unscrambled_data[0], src_word); +} + uint32_t ScrambledEcc32MemArea::ToPhysAddr(uint32_t logical_addr) const { // Scramble logical address to get physical address return AddrBytesToInt(scramble_addr(AddrIntToBytes(logical_addr, addr_width_),
diff --git a/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.h b/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.h index 98a5a1c..51cf247 100644 --- a/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.h +++ b/hw/dv/verilator/cpp/scrambled_ecc32_mem_area.h
@@ -37,10 +37,17 @@ const std::vector<uint8_t> &data, size_t start_idx, uint32_t dst_word) const override; + std::vector<uint8_t> ReadUnscrambled(const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const; + void ReadBuffer(std::vector<uint8_t> &data, const uint8_t buf[SV_MEM_WIDTH_BYTES], uint32_t src_word) const override; + void ReadBufferWithIntegrity(EccWords &data, + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const override; + uint32_t ToPhysAddr(uint32_t logical_addr) const override; uint32_t GetPhysWidth() const;