From 54ec54d421e2f0b70f191e461cefb0845265c16e Mon Sep 17 00:00:00 2001 From: FallenSigh Date: Sun, 5 Jul 2026 21:04:25 +0800 Subject: [PATCH] feat: add prng --- rtl/ip/mlkem/top/fft_mlkem_axi_slave.v | 155 ++++++++++++++---- rtl/ip/prng/prng_axi_slave.v | 218 +++++++++++++++++++++++++ rtl/soc_top.v | 1 + 3 files changed, 342 insertions(+), 32 deletions(-) create mode 100644 rtl/ip/prng/prng_axi_slave.v diff --git a/rtl/ip/mlkem/top/fft_mlkem_axi_slave.v b/rtl/ip/mlkem/top/fft_mlkem_axi_slave.v index 60526de..1cb22c1 100644 --- a/rtl/ip/mlkem/top/fft_mlkem_axi_slave.v +++ b/rtl/ip/mlkem/top/fft_mlkem_axi_slave.v @@ -4,7 +4,9 @@ // maps do not overlap: // ML-KEM: 0x0000..0x3fff // FFT: 0x8000..0xcfff -// Route the low ML-KEM block to ML-KEM and the high FFT block to FFT. +// PRNG: 0xd000..0xd0ff +// Route the low ML-KEM block to ML-KEM, the high FFT block to FFT, and the +// following 0xdxxx block to PRNG. module fft_mlkem_axi_slave ( input clk, input resetn, @@ -54,12 +56,23 @@ module fft_mlkem_axi_slave ( output mlkem_intr ); - wire aw_to_mlkem = (s_axi_awaddr[15:12] <= 4'h3); - wire ar_to_mlkem = (s_axi_araddr[15:12] <= 4'h3); + localparam SEL_MLKEM = 2'd0; + localparam SEL_FFT = 2'd1; + localparam SEL_PRNG = 2'd2; - reg write_sel_mlkem; + wire aw_to_mlkem = (s_axi_awaddr[15:12] <= 4'h3); + wire aw_to_prng = (s_axi_awaddr[15:12] == 4'hd); + wire ar_to_mlkem = (s_axi_araddr[15:12] <= 4'h3); + wire ar_to_prng = (s_axi_araddr[15:12] == 4'hd); + + wire [1:0] aw_sel = aw_to_mlkem ? SEL_MLKEM : + aw_to_prng ? SEL_PRNG : SEL_FFT; + wire [1:0] ar_sel = ar_to_mlkem ? SEL_MLKEM : + ar_to_prng ? SEL_PRNG : SEL_FFT; + + reg [1:0] write_sel; reg write_active; - reg read_sel_mlkem; + reg [1:0] read_sel; reg read_active; wire mlkem_awready, mlkem_wready, mlkem_bvalid; @@ -78,39 +91,71 @@ module fft_mlkem_axi_slave ( wire [31:0] fft_rdata; wire [1:0] fft_rresp; - wire sel_w_mlkem = write_active ? write_sel_mlkem : aw_to_mlkem; - wire sel_r_mlkem = read_active ? read_sel_mlkem : ar_to_mlkem; + wire prng_awready, prng_wready, prng_bvalid; + wire [4:0] prng_bid; + wire [1:0] prng_bresp; + wire prng_arready, prng_rvalid, prng_rlast; + wire [4:0] prng_rid; + wire [31:0] prng_rdata; + wire [1:0] prng_rresp; - assign s_axi_awready = !write_active && (aw_to_mlkem ? mlkem_awready : fft_awready); - assign s_axi_wready = sel_w_mlkem ? mlkem_wready : fft_wready; - assign s_axi_bvalid = sel_w_mlkem ? mlkem_bvalid : fft_bvalid; - assign s_axi_bid = sel_w_mlkem ? mlkem_bid : fft_bid; - assign s_axi_bresp = sel_w_mlkem ? mlkem_bresp : fft_bresp; + wire [1:0] sel_w = write_active ? write_sel : aw_sel; + wire [1:0] sel_r = read_active ? read_sel : ar_sel; - assign s_axi_arready = !read_active && (ar_to_mlkem ? mlkem_arready : fft_arready); - assign s_axi_rvalid = sel_r_mlkem ? mlkem_rvalid : fft_rvalid; - assign s_axi_rlast = sel_r_mlkem ? mlkem_rlast : fft_rlast; - assign s_axi_rid = sel_r_mlkem ? mlkem_rid : fft_rid; - assign s_axi_rdata = sel_r_mlkem ? mlkem_rdata : fft_rdata; - assign s_axi_rresp = sel_r_mlkem ? mlkem_rresp : fft_rresp; + assign s_axi_awready = !write_active && ( + (aw_sel == SEL_MLKEM) ? mlkem_awready : + (aw_sel == SEL_PRNG) ? prng_awready : + fft_awready); + assign s_axi_wready = (sel_w == SEL_MLKEM) ? mlkem_wready : + (sel_w == SEL_PRNG) ? prng_wready : + fft_wready; + assign s_axi_bvalid = (sel_w == SEL_MLKEM) ? mlkem_bvalid : + (sel_w == SEL_PRNG) ? prng_bvalid : + fft_bvalid; + assign s_axi_bid = (sel_w == SEL_MLKEM) ? mlkem_bid : + (sel_w == SEL_PRNG) ? prng_bid : + fft_bid; + assign s_axi_bresp = (sel_w == SEL_MLKEM) ? mlkem_bresp : + (sel_w == SEL_PRNG) ? prng_bresp : + fft_bresp; + + assign s_axi_arready = !read_active && ( + (ar_sel == SEL_MLKEM) ? mlkem_arready : + (ar_sel == SEL_PRNG) ? prng_arready : + fft_arready); + assign s_axi_rvalid = (sel_r == SEL_MLKEM) ? mlkem_rvalid : + (sel_r == SEL_PRNG) ? prng_rvalid : + fft_rvalid; + assign s_axi_rlast = (sel_r == SEL_MLKEM) ? mlkem_rlast : + (sel_r == SEL_PRNG) ? prng_rlast : + fft_rlast; + assign s_axi_rid = (sel_r == SEL_MLKEM) ? mlkem_rid : + (sel_r == SEL_PRNG) ? prng_rid : + fft_rid; + assign s_axi_rdata = (sel_r == SEL_MLKEM) ? mlkem_rdata : + (sel_r == SEL_PRNG) ? prng_rdata : + fft_rdata; + assign s_axi_rresp = (sel_r == SEL_MLKEM) ? mlkem_rresp : + (sel_r == SEL_PRNG) ? prng_rresp : + fft_rresp; always @(posedge clk or negedge resetn) begin if (!resetn) begin write_active <= 1'b0; - write_sel_mlkem <= 1'b0; + write_sel <= SEL_MLKEM; read_active <= 1'b0; - read_sel_mlkem <= 1'b0; + read_sel <= SEL_MLKEM; end else begin if (!write_active && s_axi_awvalid && s_axi_awready) begin write_active <= 1'b1; - write_sel_mlkem <= aw_to_mlkem; + write_sel <= aw_sel; end else if (write_active && s_axi_bvalid && s_axi_bready) begin write_active <= 1'b0; end if (!read_active && s_axi_arvalid && s_axi_arready) begin read_active <= 1'b1; - read_sel_mlkem <= ar_to_mlkem; + read_sel <= ar_sel; end else if (read_active && s_axi_rvalid && s_axi_rready && s_axi_rlast) begin read_active <= 1'b0; end @@ -129,20 +174,20 @@ module fft_mlkem_axi_slave ( .s_axi_awlock (s_axi_awlock), .s_axi_awcache (s_axi_awcache), .s_axi_awprot (s_axi_awprot), - .s_axi_awvalid (s_axi_awvalid && !write_active && aw_to_mlkem), + .s_axi_awvalid (s_axi_awvalid && !write_active && (aw_sel == SEL_MLKEM)), .s_axi_awready (mlkem_awready), .s_axi_wid (s_axi_wid), .s_axi_wdata (s_axi_wdata), .s_axi_wstrb (s_axi_wstrb), .s_axi_wlast (s_axi_wlast), - .s_axi_wvalid (s_axi_wvalid && sel_w_mlkem), + .s_axi_wvalid (s_axi_wvalid && (sel_w == SEL_MLKEM)), .s_axi_wready (mlkem_wready), .s_axi_bid (mlkem_bid), .s_axi_bresp (mlkem_bresp), .s_axi_bvalid (mlkem_bvalid), - .s_axi_bready (s_axi_bready && sel_w_mlkem), + .s_axi_bready (s_axi_bready && (sel_w == SEL_MLKEM)), .s_axi_arid (s_axi_arid), .s_axi_araddr (s_axi_araddr), @@ -152,7 +197,7 @@ module fft_mlkem_axi_slave ( .s_axi_arlock (s_axi_arlock), .s_axi_arcache (s_axi_arcache), .s_axi_arprot (s_axi_arprot), - .s_axi_arvalid (s_axi_arvalid && !read_active && ar_to_mlkem), + .s_axi_arvalid (s_axi_arvalid && !read_active && (ar_sel == SEL_MLKEM)), .s_axi_arready (mlkem_arready), .s_axi_rid (mlkem_rid), @@ -160,7 +205,7 @@ module fft_mlkem_axi_slave ( .s_axi_rresp (mlkem_rresp), .s_axi_rlast (mlkem_rlast), .s_axi_rvalid (mlkem_rvalid), - .s_axi_rready (s_axi_rready && sel_r_mlkem), + .s_axi_rready (s_axi_rready && (sel_r == SEL_MLKEM)), .intr (mlkem_intr) ); @@ -177,20 +222,20 @@ module fft_mlkem_axi_slave ( .s_awlock (s_axi_awlock), .s_awcache (s_axi_awcache), .s_awprot (s_axi_awprot), - .s_awvalid (s_axi_awvalid && !write_active && !aw_to_mlkem), + .s_awvalid (s_axi_awvalid && !write_active && (aw_sel == SEL_FFT)), .s_awready (fft_awready), .s_wid (s_axi_wid), .s_wdata (s_axi_wdata), .s_wstrb (s_axi_wstrb), .s_wlast (s_axi_wlast), - .s_wvalid (s_axi_wvalid && !sel_w_mlkem), + .s_wvalid (s_axi_wvalid && (sel_w == SEL_FFT)), .s_wready (fft_wready), .s_bid (fft_bid), .s_bresp (fft_bresp), .s_bvalid (fft_bvalid), - .s_bready (s_axi_bready && !sel_w_mlkem), + .s_bready (s_axi_bready && (sel_w == SEL_FFT)), .s_arid (s_axi_arid), .s_araddr (s_axi_araddr), @@ -200,7 +245,7 @@ module fft_mlkem_axi_slave ( .s_arlock (s_axi_arlock), .s_arcache (s_axi_arcache), .s_arprot (s_axi_arprot), - .s_arvalid (s_axi_arvalid && !read_active && !ar_to_mlkem), + .s_arvalid (s_axi_arvalid && !read_active && (ar_sel == SEL_FFT)), .s_arready (fft_arready), .s_rid (fft_rid), @@ -208,9 +253,55 @@ module fft_mlkem_axi_slave ( .s_rresp (fft_rresp), .s_rlast (fft_rlast), .s_rvalid (fft_rvalid), - .s_rready (s_axi_rready && !sel_r_mlkem), + .s_rready (s_axi_rready && (sel_r == SEL_FFT)), .fft_finish (fft_finish) ); + prng_axi_slave u_prng_axi_slave ( + .clk (clk), + .resetn (resetn), + + .s_axi_awid (s_axi_awid), + .s_axi_awaddr (s_axi_awaddr), + .s_axi_awlen (s_axi_awlen), + .s_axi_awsize (s_axi_awsize), + .s_axi_awburst (s_axi_awburst), + .s_axi_awlock (s_axi_awlock), + .s_axi_awcache (s_axi_awcache), + .s_axi_awprot (s_axi_awprot), + .s_axi_awvalid (s_axi_awvalid && !write_active && (aw_sel == SEL_PRNG)), + .s_axi_awready (prng_awready), + + .s_axi_wid (s_axi_wid), + .s_axi_wdata (s_axi_wdata), + .s_axi_wstrb (s_axi_wstrb), + .s_axi_wlast (s_axi_wlast), + .s_axi_wvalid (s_axi_wvalid && (sel_w == SEL_PRNG)), + .s_axi_wready (prng_wready), + + .s_axi_bid (prng_bid), + .s_axi_bresp (prng_bresp), + .s_axi_bvalid (prng_bvalid), + .s_axi_bready (s_axi_bready && (sel_w == SEL_PRNG)), + + .s_axi_arid (s_axi_arid), + .s_axi_araddr (s_axi_araddr), + .s_axi_arlen (s_axi_arlen), + .s_axi_arsize (s_axi_arsize), + .s_axi_arburst (s_axi_arburst), + .s_axi_arlock (s_axi_arlock), + .s_axi_arcache (s_axi_arcache), + .s_axi_arprot (s_axi_arprot), + .s_axi_arvalid (s_axi_arvalid && !read_active && (ar_sel == SEL_PRNG)), + .s_axi_arready (prng_arready), + + .s_axi_rid (prng_rid), + .s_axi_rdata (prng_rdata), + .s_axi_rresp (prng_rresp), + .s_axi_rlast (prng_rlast), + .s_axi_rvalid (prng_rvalid), + .s_axi_rready (s_axi_rready && (sel_r == SEL_PRNG)) + ); + endmodule diff --git a/rtl/ip/prng/prng_axi_slave.v b/rtl/ip/prng/prng_axi_slave.v new file mode 100644 index 0000000..dff4ead --- /dev/null +++ b/rtl/ip/prng/prng_axi_slave.v @@ -0,0 +1,218 @@ +// 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 diff --git a/rtl/soc_top.v b/rtl/soc_top.v index e45a1f1..e96e434 100644 --- a/rtl/soc_top.v +++ b/rtl/soc_top.v @@ -1448,6 +1448,7 @@ axi_dvi u_axi_dvi ( // slave 6 - shared FFT / ML-KEM accelerator window (0x1f400000, virtual 0xbf400000) // ML-KEM: 0x0000..0x0114 // FFT: 0x1000/0x2000/0x3000/0x4000/0xf000 +// PRNG: 0xd000..0xd010 fft_mlkem_axi_slave u_fft_mlkem_axi_slave ( .clk (sys_clk), .resetn (sys_resetn),