This repository has been archived on 2026-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
ciciec2026/rtl/ip/prng/prng_axi_slave.v
2026-07-05 21:04:25 +08:00

219 lines
7.6 KiB
Verilog

// 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