blob: 298a6d38699c5ab1c441f7e4736cf545da417bcd [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module prim_generic_otp
import prim_otp_pkg::*;
#(
// Native OTP word size. This determines the size_i granule.
parameter int Width = 16,
parameter int Depth = 1024,
// This determines the maximum number of native words that
// can be transferred accross the interface in one cycle.
parameter int SizeWidth = 2,
// Width of the power sequencing signal.
parameter int PwrSeqWidth = 2,
// Width of vendor-specific test control signal
parameter int TestCtrlWidth = 32,
parameter int TestStatusWidth = 32,
parameter int TestVectWidth = 8,
// Derived parameters
localparam int AddrWidth = prim_util_pkg::vbits(Depth),
localparam int IfWidth = 2**SizeWidth*Width,
// VMEM file to initialize the memory with
parameter MemInitFile = "",
// Vendor test partition offset and size (both in bytes)
parameter int VendorTestOffset = 0,
parameter int VendorTestSize = 0
) (
input clk_i,
input rst_ni,
// Observability
input ast_pkg::ast_obs_ctrl_t obs_ctrl_i,
output logic [7:0] otp_obs_o,
// Macro-specific power sequencing signals to/from AST
output logic [PwrSeqWidth-1:0] pwr_seq_o,
input [PwrSeqWidth-1:0] pwr_seq_h_i,
// External programming voltage
inout wire ext_voltage_io,
// Test interfaces
input [TestCtrlWidth-1:0] test_ctrl_i,
output logic [TestStatusWidth-1:0] test_status_o,
output logic [TestVectWidth-1:0] test_vect_o,
input tlul_pkg::tl_h2d_t test_tl_i,
output tlul_pkg::tl_d2h_t test_tl_o,
// Other DFT signals
input prim_mubi_pkg::mubi4_t scanmode_i, // Scan Mode input
input scan_en_i, // Scan Shift
input scan_rst_ni, // Scan Reset
// Alert indication (to be connected to alert sender in the instantiating IP)
output logic fatal_alert_o,
output logic recov_alert_o,
// Ready valid handshake for read/write command
output logic ready_o,
input valid_i,
input [SizeWidth-1:0] size_i, // #(Native words)-1, e.g. size == 0 for 1 native word.
input cmd_e cmd_i, // 00: read command, 01: write command, 11: init command
input [AddrWidth-1:0] addr_i,
input [IfWidth-1:0] wdata_i,
// Response channel
output logic valid_o,
output logic [IfWidth-1:0] rdata_o,
output err_e err_o
);
import prim_mubi_pkg::MuBi4False;
// This is only restricted by the supported ECC poly further
// below, and is straightforward to extend, if needed.
localparam int EccWidth = 6;
`ASSERT_INIT(SecDecWidth_A, Width == 16)
// Not supported in open-source emulation model.
logic [PwrSeqWidth-1:0] unused_pwr_seq_h;
assign unused_pwr_seq_h = pwr_seq_h_i;
assign pwr_seq_o = '0;
logic unused_obs;
assign unused_obs = |obs_ctrl_i;
assign otp_obs_o = '0;
wire unused_ext_voltage;
assign unused_ext_voltage = ext_voltage_io;
logic unused_test_ctrl_i;
assign unused_test_ctrl_i = ^test_ctrl_i;
logic unused_scan;
assign unused_scan = ^{scanmode_i, scan_en_i, scan_rst_ni};
logic intg_err, fsm_err;
assign fatal_alert_o = intg_err || fsm_err;
assign recov_alert_o = 1'b0;
assign test_vect_o = '0;
assign test_status_o = '0;
////////////////////////////////////
// TL-UL Test Interface Emulation //
////////////////////////////////////
otp_ctrl_reg_pkg::otp_ctrl_prim_reg2hw_t reg2hw;
otp_ctrl_reg_pkg::otp_ctrl_prim_hw2reg_t hw2reg;
otp_ctrl_prim_reg_top u_reg_top (
.clk_i,
.rst_ni,
.tl_i (test_tl_i ),
.tl_o (test_tl_o ),
.reg2hw (reg2hw ),
.hw2reg (hw2reg ),
.intg_err_o(intg_err ),
.devmode_i (1'b1 )
);
logic unused_reg_sig;
assign unused_reg_sig = ^reg2hw;
assign hw2reg = '0;
///////////////////
// Control logic //
///////////////////
// Encoding generated with:
// $ ./util/design/sparse-fsm-encode.py -d 5 -m 9 -n 10 \
// -s 2599950981 --language=sv
//
// Hamming distance histogram:
//
// 0: --
// 1: --
// 2: --
// 3: --
// 4: --
// 5: |||||||||||||||||||| (52.78%)
// 6: ||||||||||||||| (41.67%)
// 7: | (2.78%)
// 8: | (2.78%)
// 9: --
// 10: --
//
// Minimum Hamming distance: 5
// Maximum Hamming distance: 8
// Minimum Hamming weight: 3
// Maximum Hamming weight: 8
//
localparam int StateWidth = 10;
typedef enum logic [StateWidth-1:0] {
ResetSt = 10'b1100000110,
InitSt = 10'b1000110011,
IdleSt = 10'b0101110000,
ReadSt = 10'b0010011111,
ReadWaitSt = 10'b1001001101,
WriteCheckSt = 10'b1111101011,
WriteWaitSt = 10'b0011000010,
WriteSt = 10'b0110100101,
ErrorSt = 10'b1110011000
} state_e;
state_e state_d, state_q;
err_e err_d, err_q;
logic valid_d, valid_q;
logic req, wren, rvalid;
logic [1:0] rerror;
logic [AddrWidth-1:0] addr_q;
logic [SizeWidth-1:0] size_q;
logic [SizeWidth-1:0] cnt_d, cnt_q;
logic cnt_clr, cnt_en;
logic read_ecc_on;
logic wdata_inconsistent;
assign cnt_d = (cnt_clr) ? '0 :
(cnt_en) ? cnt_q + 1'b1 : cnt_q;
assign valid_o = valid_q;
assign err_o = err_q;
always_comb begin : p_fsm
// Default
state_d = state_q;
ready_o = 1'b0;
valid_d = 1'b0;
err_d = err_q;
req = 1'b0;
wren = 1'b0;
cnt_clr = 1'b0;
cnt_en = 1'b0;
read_ecc_on = 1'b1;
fsm_err = 1'b0;
unique case (state_q)
// Wait here until we receive an initialization command.
ResetSt: begin
err_d = NoError;
ready_o = 1'b1;
if (valid_i) begin
if (cmd_i == Init) begin
state_d = InitSt;
end
end
end
// Wait for some time until the OTP macro is ready.
InitSt: begin
state_d = IdleSt;
valid_d = 1'b1;
err_d = NoError;
end
// In the idle state, we basically wait for read or write commands.
IdleSt: begin
ready_o = 1'b1;
err_d = NoError;
if (valid_i) begin
cnt_clr = 1'b1;
err_d = NoError;
unique case (cmd_i)
Read: state_d = ReadSt;
Write: state_d = WriteCheckSt;
default: ;
endcase // cmd_i
end
end
// Issue a read command to the macro.
ReadSt: begin
state_d = ReadWaitSt;
req = 1'b1;
end
// Wait for response from macro.
ReadWaitSt: begin
if (rvalid) begin
cnt_en = 1'b1;
// Uncorrectable error, bail out.
if (rerror[1]) begin
state_d = IdleSt;
valid_d = 1'b1;
err_d = MacroEccUncorrError;
end else begin
if (cnt_q == size_q) begin
state_d = IdleSt;
valid_d = 1'b1;
end else begin
state_d = ReadSt;
end
// Correctable error, carry on but signal back.
if (rerror[0]) begin
err_d = MacroEccCorrError;
end
end
end
end
// First, read out to perform the write blank check and
// read-modify-write operation.
WriteCheckSt: begin
state_d = WriteWaitSt;
req = 1'b1;
// Register raw memory contents without correction
read_ecc_on = 1'b0;
end
// Wait for readout to complete first.
WriteWaitSt: begin
// Register raw memory contents without correction
read_ecc_on = 1'b0;
if (rvalid) begin
cnt_en = 1'b1;
if (cnt_q == size_q) begin
cnt_clr = 1'b1;
state_d = WriteSt;
end else begin
state_d = WriteCheckSt;
end
end
end
// If the write data attempts to clear an already programmed bit,
// the MacroWriteBlankError needs to be asserted.
WriteSt: begin
req = 1'b1;
wren = 1'b1;
cnt_en = 1'b1;
if (wdata_inconsistent) begin
err_d = MacroWriteBlankError;
end
if (cnt_q == size_q) begin
valid_d = 1'b1;
state_d = IdleSt;
end
end
// If the FSM is glitched into an invalid state.
ErrorSt: begin
fsm_err = 1'b1;
end
default: begin
state_d = ErrorSt;
fsm_err = 1'b1;
end
endcase // state_q
end
///////////////////////////////////////////
// Emulate using ECC protected Block RAM //
///////////////////////////////////////////
logic [AddrWidth-1:0] addr;
assign addr = addr_q + AddrWidth'(cnt_q);
logic [Width-1:0] rdata_corr;
logic [Width+EccWidth-1:0] rdata_d, wdata_ecc, rdata_ecc, wdata_rmw;
logic [2**SizeWidth-1:0][Width-1:0] wdata_q, rdata_reshaped;
logic [2**SizeWidth-1:0][Width+EccWidth-1:0] rdata_q;
// Use a standard Hamming ECC for OTP.
prim_secded_hamming_22_16_enc u_enc (
.data_i(wdata_q[cnt_q]),
.data_o(wdata_ecc)
);
prim_secded_hamming_22_16_dec u_dec (
.data_i (rdata_ecc),
.data_o (rdata_corr),
.syndrome_o ( ),
.err_o (rerror)
);
assign rdata_d = (read_ecc_on) ? {{EccWidth{1'b0}}, rdata_corr}
: rdata_ecc;
// Read-modify-write (OTP can only set bits to 1, but not clear to 0).
assign wdata_rmw = wdata_ecc | rdata_q[cnt_q];
// This indicates if the write data is inconsistent (i.e., if the operation attempts to
// clear an already programmed bit to zero).
assign wdata_inconsistent = (rdata_q[cnt_q] & wdata_ecc) != rdata_q[cnt_q];
// Output data without ECC bits.
always_comb begin : p_output_map
for (int k = 0; k < 2**SizeWidth; k++) begin
rdata_reshaped[k] = rdata_q[k][Width-1:0];
end
rdata_o = rdata_reshaped;
end
prim_ram_1p_adv #(
.Depth (Depth),
.Width (Width + EccWidth),
.MemInitFile (MemInitFile),
.EnableInputPipeline (1),
.EnableOutputPipeline (1)
) u_prim_ram_1p_adv (
.clk_i,
.rst_ni,
.req_i ( req ),
.write_i ( wren ),
.addr_i ( addr ),
.wdata_i ( wdata_rmw ),
.wmask_i ( {Width+EccWidth{1'b1}} ),
.rdata_o ( rdata_ecc ),
.rvalid_o ( rvalid ),
.rerror_o ( ),
.cfg_i ( '0 )
);
// Currently it is assumed that no wrap arounds can occur.
`ASSERT(NoWrapArounds_A, req |-> (addr >= addr_q))
//////////
// Regs //
//////////
`PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, state_e, ResetSt)
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
valid_q <= '0;
err_q <= NoError;
addr_q <= '0;
wdata_q <= '0;
rdata_q <= '0;
cnt_q <= '0;
size_q <= '0;
end else begin
valid_q <= valid_d;
err_q <= err_d;
cnt_q <= cnt_d;
if (ready_o && valid_i) begin
addr_q <= addr_i;
wdata_q <= wdata_i;
size_q <= size_i;
end
if (rvalid) begin
rdata_q[cnt_q] <= rdata_d;
end
end
end
////////////////
// Assertions //
////////////////
// Check that the otp_ctrl FSMs only issue legal commands to the wrapper.
`ASSERT(CheckCommands0_A, state_q == ResetSt && valid_i && ready_o |-> cmd_i == Init)
`ASSERT(CheckCommands1_A, state_q != ResetSt && valid_i && ready_o |-> cmd_i inside {Read, Write})
endmodule : prim_generic_otp