`timescale 1ns / 1ps `include "cam_params.svh" module cam_write_noise #( parameter bit WRITE_NOISE_EN = 1'b1, parameter int WRITE_NOISE_RATE_NUM = 1, parameter int WRITE_NOISE_RATE_DEN = 100, parameter logic [63:0] WRITE_NOISE_SEED = 64'hB504_F32D_B504_F32D ) ( input logic clk, input logic rst_n, input logic wr_valid, output logic wr_ready, input logic [(`ROW_BITS)-1:0] wr_row, input logic [(`HASH_BITS)-1:0] wr_hash, output logic core_wr_valid, output logic [(`ROW_BITS)-1:0] core_wr_row, output logic [(`HASH_BITS)-1:0] core_wr_hash ); localparam int PROB_BITS = 8; localparam int SAMPLE_RANGE = 1 << PROB_BITS; localparam int WRITE_NOISE_THRESHOLD_RAW = (WRITE_NOISE_RATE_NUM * SAMPLE_RANGE) / WRITE_NOISE_RATE_DEN; localparam int WRITE_NOISE_THRESHOLD = (WRITE_NOISE_THRESHOLD_RAW > (SAMPLE_RANGE - 1)) ? (SAMPLE_RANGE - 1) : WRITE_NOISE_THRESHOLD_RAW; wire noise_active = WRITE_NOISE_EN && (WRITE_NOISE_RATE_NUM > 0) && (WRITE_NOISE_THRESHOLD > 0); typedef enum logic [1:0] { STATE_IDLE, STATE_WAIT_MASK } state_t; state_t state_q; logic mask_start_q; logic mask_busy; logic mask_done; logic [(`HASH_BITS)-1:0] flip_mask; logic [(`ROW_BITS)-1:0] row_q; logic [(`HASH_BITS)-1:0] hash_q; assign wr_ready = (state_q == STATE_IDLE); noise_mask_bernoulli #( .HASH_BITS (`HASH_BITS), .PROB_BITS (PROB_BITS), .PRNG_WORDS (2), .BITS_PER_CYCLE (32), .SEED (WRITE_NOISE_SEED) ) u_bernoulli_mask ( .clk (clk), .rst_n (rst_n), .start_i (mask_start_q), .threshold_i (PROB_BITS'(WRITE_NOISE_THRESHOLD)), .busy_o (mask_busy), .done_o (mask_done), .mask_o (flip_mask) ); `ifndef SYNTHESIS initial begin if (WRITE_NOISE_SEED == 64'd0) $fatal(1, "WRITE_NOISE_SEED must be nonzero"); end `endif always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin state_q <= STATE_IDLE; mask_start_q <= 1'b0; row_q <= '0; hash_q <= '0; core_wr_valid <= 1'b0; core_wr_row <= '0; core_wr_hash <= '0; end else begin core_wr_valid <= 1'b0; mask_start_q <= 1'b0; unique case (state_q) STATE_IDLE: begin if (wr_valid && wr_ready) begin row_q <= wr_row; hash_q <= wr_hash; if (noise_active) begin mask_start_q <= 1'b1; state_q <= STATE_WAIT_MASK; end else begin // Noise inactive: pass through immediately (one-cycle) core_wr_valid <= 1'b1; core_wr_row <= wr_row; core_wr_hash <= wr_hash; end end end STATE_WAIT_MASK: begin if (mask_done) begin core_wr_valid <= 1'b1; core_wr_row <= row_q; core_wr_hash <= hash_q ^ flip_mask; state_q <= STATE_IDLE; end end default: begin state_q <= STATE_IDLE; end endcase end end endmodule