Skip to content

[rtl] RRAM otp hw plug#30412

Open
gautschimi wants to merge 5 commits into
lowRISC:masterfrom
gautschimi:rram_ctrl_otp_hw_plug
Open

[rtl] RRAM otp hw plug#30412
gautschimi wants to merge 5 commits into
lowRISC:masterfrom
gautschimi:rram_ctrl_otp_hw_plug

Conversation

@gautschimi

@gautschimi gautschimi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

As per RFC , Earlgrey will be extended with support for RRAM based non-volatile memory.

This 8th PR adds the otp hw module for the RRAM controller.

  • rram_ctrl_otp.sv: A FSM that performs the read-set-write algorithm and supports all commands issued by otp_ctrl.

This PR also adds the DV for this module with a new testcase:

  • dvsim hw/ip/rram_ctrl/dv/rram_ctrl_sim_cfg.hjson -i rram_ctrl_otp

The smoke test is also extended to issue OTP transactions.

This PR also enables the lint flow for the full module that was disabled in the first PR (see #30366 (comment))

This PR is based on #30411

@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch 3 times, most recently from 43435b1 to cb9246e Compare June 16, 2026 11:24
@gautschimi gautschimi self-assigned this Jun 16, 2026
@gautschimi
gautschimi marked this pull request as ready for review June 16, 2026 11:35
@gautschimi
gautschimi requested review from a team and cfrantz as code owners June 16, 2026 11:35
@gautschimi
gautschimi requested review from martin-velay, meisnere, moidx, nasahlpa, rswarbrick, thommythomaso, vogelpi and zi-v and removed request for a team, cfrantz and martin-velay June 16, 2026 11:35

@vogelpi vogelpi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @gautschimi this matches the original PR which I reviewed in depth + the requested comments. These are very valuable, thanks for adding them.

I mostly have some questions. We can also discuss in person if preferred.

I would appreciate if also @andrea-caforio and @elliotb-lowrisc could review this PR as they have experience with OTP_CTRL and @andrea-caforio for example implemented the zeroization feature originally.

Comment thread hw/ip/rram_ctrl/rtl/rram_ctrl_otp.sv Outdated
Comment on lines +281 to +282
intg_addr_d = OtpIntgStartAddr + (otp_byte_addr >> vbits(OtpIntgDataWidth/8));
intg_ind_d = otp_byte_addr[vbits(OtpIntgDataWidth/8) +: OtpIntgIndWidth];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Historically, some OTP partitions did not have integrity because it was truly OTP. One example are the strike counters in Darjeeling to prevent firmware rollback. Software then implemented some form of integrity itself (e.g. by using multiple bits to increment the counter by one).

I think now that the OTP behavior is logically emulated using RSW, we can only apply this to the data part of the emulated OTP and leave the additional integrity part without RSW. This would mean we can use the additional integrity for all OTP partitions, thereby reducing hardware design and verification complexity, as well as software complexity (since software won't have to take care of integrity for strike counters etc.). What is your view on this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting to include the integrity in all cases and remove the case differentiation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah you are talking about software. Yes we could have hardware integrity for all partitions and get rid of the extra software integrity.

Comment thread hw/ip/rram_ctrl/rtl/rram_ctrl_otp.sv Outdated
wvalid_o = 1'b1;
end
if (done_i) begin
if (mubi4_test_true_strict(mubi4_or_hi(zer_en_q, intg_en_q))) begin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that test_true_strict() is okay here because if with a single bit flip in intg_en_q or zer_en_q one would manage to bypass the integrity update, this would result in integrity errors down the line. Does this makes sense? Is it worth documenting this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the intention indeed. I can add a comment in the code

end
if (done_i) begin
if (mubi4_test_true_strict(mubi4_or_hi(zer_en_q, intg_en_q))) begin
state_d = StReqIntgWords;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we read the integrity words before we have confirmed the write went though? Shouldn't we first wait for the write to conclude before starting with the integrity?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both pages need to be read, updated and written. It was easier to first modify the integrity page, but it could also be done the other way round.

Comment thread hw/ip/rram_ctrl/rtl/rram_ctrl_otp.sv Outdated
Comment on lines +504 to +525
if (rmw_en) begin
for (int k = 0; k <= otp_size_q; k++) begin
rram_word_d[(otp_off_q + k)*OtpWidth +: OtpWidth] = wdata_mod[k];
if (mubi4_test_false_strict(zer_en_q)) begin
// Inconsistent write data. Detect if operation tried to clear a bit to zero.
if ((otp_wdata_q[k] & rdata[k]) != rdata[k]) begin
wdata_inconsistent = 1'b1;
end
end
end
end else if (intg_update) begin
rram_word_d[intg_ind_q*OtpIntgWidth +: OtpIntgWidth] = intg_q;
end else if (clr_buf) begin
rram_word_d = '0;
end else if (rvalid_i && rready_o) begin
if (data_err) begin
rram_word_d[bus_cnt_q*BusWidth +: BusWidth] = '1;
end else begin
rram_word_d[bus_cnt_q*BusWidth +: BusWidth] = rdata_i[BusWidth-1:0];
end
end
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block of code effectively defines what is going to be written to the RRAM macro or returned to OTP_CTRL. The hardening is not ideal right now (multiple single-bit control signals are evaluated such as rmw_en, intg_update, data_err, clr_buf). But properly hardening that is going to be challenging because it's effectively a big multiplexer.

We need to reason why this is sufficient and document that. For example if we can say that for every OTP write operation, a read back is done afterwards to confirm the intended write went through, this is fine. Otherwise we have to come up with a smart countermeasure overarching this multiplexers, see https://opentitan.org/book/doc/security/implementation_guidelines/hardware/#recommendation-3-fault-injection-countermeasures for ideas. For example for a write operation, we could load the buffer with the right value (as we do now) and then re-check that value at the end of the operation is still the same (in StWaitWrite). WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that RRAM reads are 3 orders of magnitude faster than RRAM writes, so the overhead of read-verify in RRAM is not big, plus it has better coverage than consistency checks within the controller because it also covers inter-RRAM write tampering. I think we should consider read-verify after every RRAM write (regardless of OTP or not).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could recheck at the end of the StWaitWrite. Or maybe just readback the written words.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all integrity enabled partitions (all but the test partition) we would have integrity mismatches. Tampering with this mux would get detected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I think, we can store a local copy of the address and read back the data to confirm it has been properly written. I will add this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I have added the read-back for all write operations.
In the beginning of the operation we store two addresses: addr_q (address of the OTP data word), addr_intg_q (address of the integrity word).

We then read the data and integrity word, update them with RSM and write them back. In the end of the operation the data word is read again and its integrity is compared to the previously computed integrity word.

Please note: In addition to the implemented read-back otp_ctrl itself performs regular read operations to verify the partition integrity.

@elliotb-lowrisc elliotb-lowrisc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need a bit more context. Could you write some high-level documentation, perhaps including a graphical or textural breakdown of the partitions/regions/pages and how they relate to the higher-level address spaces (e.g. otp_ctrl partitions) and the underlying RRAM macros?

Comment on lines +1136 to +1137
All errors except for multi-bit ECC errors (!!FAULT_STATUS.PHY_RELBL_ERR).
Once set, they remain set until reset.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify these two lines a bit? I think it is supposed to convey that these are all un-clearable errors except for the multi-bit ECC condition which may or may not be clearable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not related to this PR.

The explanation can be found a bit below in the description of the phy_relbl_err bit:

Note that this error bit can be cleared to allow firmware dealing with multi-bit ECC errors during firmware selection and verification. After passing this stage, it is recommended that firmware classifies the corresponding alert as fatal on the receiver end, i.e, inside the alert handler.

@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch from cb9246e to 81aa182 Compare July 10, 2026 15:26
@gautschimi

Copy link
Copy Markdown
Contributor Author

I think I need a bit more context. Could you write some high-level documentation, perhaps including a graphical or textural breakdown of the partitions/regions/pages and how they relate to the higher-level address spaces (e.g. otp_ctrl partitions) and the underlying RRAM macros?

A first documentation can be found here:
#30437

and there is also information in the RFC ,

@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch 5 times, most recently from dfc76c9 to 2c58837 Compare July 14, 2026 11:50
@gautschimi gautschimi mentioned this pull request Jul 14, 2026
@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch from 2c58837 to bcac448 Compare July 14, 2026 14:44
wvalid_o = 1'b1;
end
if (done_i) begin
if (mubi4_test_true_strict(zer_en_q)) begin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a dumb question: Up until this point the sequence of operations for writing an integrity protected word or a zeroized word is identical, why do we immediately skip into the read state when writing the zeroized integrity bits but wait for this write to complete in the other case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question. The reason is that the write is a blocking operation. The read will also wait for the write to complete. We could also jump to the wait state and then go to the read, it would be the same

@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch from bcac448 to 930c56f Compare July 16, 2026 10:47
@rswarbrick
rswarbrick removed their request for review July 16, 2026 11:26
@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch from b89448f to 17c6cbb Compare July 16, 2026 13:31
@gautschimi gautschimi added the CI:Rerun Rerun failed CI jobs label Jul 17, 2026
@github-actions github-actions Bot removed the CI:Rerun Rerun failed CI jobs label Jul 17, 2026
This commit adds the plug between rram_ctrl and otp_ctrl and will
replace otp_macro.

It converts otp_ctrl transactions to rram read/write operations.

The following operations are supported:
- Read, ReadRaw
- Write, WriteRaw
- Init
- Zeroize

All values are protected with an additional integrity check value that
must match (except for ReadRaw/WriteRaw)

Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
The requests from otp_ctrl to rram_ctrl come from the otp_clk domain
(clk_io4) and need to be synchronized to the main clock domain (system
clock).
Two FIFOs (prim_fifo_async_simple) have been added for the request and
response path.

Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
And fix remaining issues in rram_ctrl_otp and top. rram_ctrl_otp
required a waifer for FOR_LOOP_BOUNDS, VAR_INDEX_RANGE, LOOP_VAR_OP.

Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
After manufacturing the RRAM is unprogrammed and OTP read commands will
result in garbage and integrity mismatches if the read path tries to
remove the address infection.
Addr-infection has been disabled for the full OTP region. Integrity is
guaranteed with the separate integrity word stored in the integrity page.

Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
RRAM write operations are less protected than read operations.
Therefore, every OTP write is verified with a subsequent read-back.
If the readback does not match the original data, an alert is created.
(same behaviour as for an OTP read with integrity)

Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
@gautschimi
gautschimi force-pushed the rram_ctrl_otp_hw_plug branch from 17c6cbb to 20dca29 Compare July 17, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants