[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;