// Lightweight AXI4 register endpoint for a pseudo-random generator. // // Register map, byte offsets within the shared accelerator window: // 0xd000 CTRL W bit0=mix software seed, bit1=clear counters // 0xd004 STATUS R bit0=ready // 0xd008 DATA R next 32-bit random word // 0xd00c COUNT R number of DATA reads // 0xd010 SEED W software seed/mix value // // This block is deterministic unless software mixes in an external seed. It // intentionally uses only flops, XORs, shifts, and adders. module prng_axi_slave ( input clk, input resetn, input [4:0] s_axi_awid, input [31:0] s_axi_awaddr, input [7:0] s_axi_awlen, input [2:0] s_axi_awsize, input [1:0] s_axi_awburst, input s_axi_awlock, input [3:0] s_axi_awcache, input [2:0] s_axi_awprot, input s_axi_awvalid, output s_axi_awready, input [4:0] s_axi_wid, input [31:0] s_axi_wdata, input [3:0] s_axi_wstrb, input s_axi_wlast, input s_axi_wvalid, output s_axi_wready, output [4:0] s_axi_bid, output [1:0] s_axi_bresp, output reg s_axi_bvalid, input s_axi_bready, input [4:0] s_axi_arid, input [31:0] s_axi_araddr, input [7:0] s_axi_arlen, input [2:0] s_axi_arsize, input [1:0] s_axi_arburst, input s_axi_arlock, input [3:0] s_axi_arcache, input [2:0] s_axi_arprot, input s_axi_arvalid, output s_axi_arready, output reg [4:0] s_axi_rid, output reg [31:0] s_axi_rdata, output [1:0] s_axi_rresp, output reg s_axi_rlast, output reg s_axi_rvalid, input s_axi_rready ); localparam ADDR_CTRL = 12'h000; localparam ADDR_STATUS = 12'h004; localparam ADDR_DATA = 12'h008; localparam ADDR_COUNT = 12'h00c; localparam ADDR_SEED = 12'h010; assign s_axi_bresp = 2'b00; assign s_axi_rresp = 2'b00; reg [4:0] awid_reg; reg [31:0] awaddr_reg; reg [7:0] awleft_reg; reg write_active; assign s_axi_awready = !write_active && !s_axi_bvalid; assign s_axi_wready = write_active && !s_axi_bvalid; assign s_axi_bid = awid_reg; reg [31:0] araddr_reg; reg [7:0] arleft_reg; reg read_active; assign s_axi_arready = !read_active && !s_axi_rvalid; reg [127:0] rng_state; reg [63:0] free_counter; reg [31:0] seed_mix; reg [31:0] data_reads; wire lfsr_fb = rng_state[127] ^ rng_state[125] ^ rng_state[100] ^ rng_state[98] ^ rng_state[27] ^ rng_state[0] ^ free_counter[0]; wire [127:0] rng_next = {rng_state[126:0], lfsr_fb} ^ {free_counter, free_counter ^ {32'h0, seed_mix}}; wire [31:0] rng_word = rng_state[31:0] ^ rng_state[63:32] ^ rng_state[95:64] ^ rng_state[127:96] ^ free_counter[31:0] ^ free_counter[63:32] ^ seed_mix; function [31:0] apply_wstrb; input [31:0] old_value; input [31:0] new_value; input [3:0] strobe; begin apply_wstrb = old_value; if (strobe[0]) apply_wstrb[7:0] = new_value[7:0]; if (strobe[1]) apply_wstrb[15:8] = new_value[15:8]; if (strobe[2]) apply_wstrb[23:16] = new_value[23:16]; if (strobe[3]) apply_wstrb[31:24] = new_value[31:24]; end endfunction function [31:0] read_reg; input [31:0] addr; begin case (addr[11:0]) ADDR_STATUS: read_reg = 32'h00000001; ADDR_DATA: read_reg = rng_word; ADDR_COUNT: read_reg = data_reads; ADDR_SEED: read_reg = seed_mix; default: read_reg = 32'h00000000; endcase end endfunction wire write_fire = s_axi_wvalid && s_axi_wready; wire read_fire = s_axi_rvalid && s_axi_rready; wire data_read_fire = read_fire && (araddr_reg[11:0] == ADDR_DATA); wire [31:0] next_araddr = araddr_reg + 32'd4; wire [31:0] next_awaddr = awaddr_reg + 32'd4; always @(posedge clk or negedge resetn) begin if (!resetn) begin awid_reg <= 5'b0; awaddr_reg <= 32'b0; awleft_reg <= 8'b0; write_active <= 1'b0; s_axi_bvalid <= 1'b0; araddr_reg <= 32'b0; arleft_reg <= 8'b0; read_active <= 1'b0; s_axi_rid <= 5'b0; s_axi_rdata <= 32'b0; s_axi_rlast <= 1'b0; s_axi_rvalid <= 1'b0; rng_state <= 128'h6d5a_56c3_9e37_79b9_a5a5_3c3c_c001_c0de; free_counter <= 64'h1; seed_mix <= 32'h31415927; data_reads <= 32'b0; end else begin free_counter <= free_counter + 64'h9e37_79b9_7f4a_7c15; rng_state <= rng_next; if (s_axi_awvalid && s_axi_awready) begin awid_reg <= s_axi_awid; awaddr_reg <= s_axi_awaddr; awleft_reg <= s_axi_awlen; write_active <= 1'b1; end if (write_fire) begin case (awaddr_reg[11:0]) ADDR_CTRL: begin if (s_axi_wdata[0]) begin seed_mix <= seed_mix ^ s_axi_wdata ^ free_counter[31:0]; rng_state <= rng_next ^ {4{s_axi_wdata}}; end if (s_axi_wdata[1]) data_reads <= 32'b0; end ADDR_SEED: begin seed_mix <= apply_wstrb(seed_mix, s_axi_wdata, s_axi_wstrb); rng_state <= rng_next ^ {s_axi_wdata, free_counter, ~s_axi_wdata}; end default: begin end endcase if (s_axi_wlast || awleft_reg == 8'b0) begin write_active <= 1'b0; s_axi_bvalid <= 1'b1; end else begin awleft_reg <= awleft_reg - 8'd1; awaddr_reg <= next_awaddr; end end if (s_axi_bvalid && s_axi_bready) s_axi_bvalid <= 1'b0; if (s_axi_arvalid && s_axi_arready) begin read_active <= 1'b1; araddr_reg <= s_axi_araddr; arleft_reg <= s_axi_arlen; s_axi_rid <= s_axi_arid; s_axi_rdata <= read_reg(s_axi_araddr); s_axi_rlast <= (s_axi_arlen == 8'b0); s_axi_rvalid <= 1'b1; end else if (read_fire) begin if (data_read_fire) begin data_reads <= data_reads + 32'd1; rng_state <= rng_next ^ {rng_word, rng_word ^ free_counter[31:0], free_counter[63:32], ~rng_word}; end if (s_axi_rlast) begin read_active <= 1'b0; s_axi_rvalid <= 1'b0; end else begin araddr_reg <= next_araddr; arleft_reg <= arleft_reg - 8'd1; s_axi_rdata <= read_reg(next_araddr); s_axi_rlast <= (arleft_reg == 8'd1); end end end end endmodule