module ram_top_341426151397261906 #(
	parameter integer W = 4
)(
	input  wire   [3:0] waddr,
	input  wire [W-1:0] wdata,
	input  wire         we,
	input  wire   [3:0] raddr,
	input  wire         re,
	output wire [W-1:0] rdata,
	input  wire         clk
);

	genvar i;

	// Signals
	// -------

	// Write path
	wire   [3:0] waddr_r;
	wire [W-1:0] wdata_r;
	wire         we_r;

	wire  [15:0] waddr_dec;

	// Storage
	wire [W-1:0] store_in;
	wire [W-1:0] store_data[0:15];
	wire  [15:0] store_gate;

	// Read path
	wire   [3:0] raddr_r;
	wire         re_r;

	wire   [3:0] raddr_msb_dec;

	wire [W-1:0] rdata_tbuf;
	wire [W-1:0] rdata_inv;
	wire         rdata_le_n;
	wire         clk_dly;


	// Write path
	// ----------

	// Register wdata, waddr, we

		// Maybe we could get away with some latches, but for timing
		// analysis PoV it's easier to have termination point that are FFs

	sky130_fd_sc_hd__dfxtp_1 waddr_reg_I[3:0] (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (waddr),
		.Q    (waddr_r)
	);

	sky130_fd_sc_hd__dfxtp_1 wdata_reg_I[W-1:0] (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (wdata),
		.Q    (wdata_r)
	);

	sky130_fd_sc_hd__dfxtp_1 we_reg_I (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (we),
		.Q    (we_r)
	);

	// Decode address to one hot signals
	ram_dec4_341426151397261906 waddr_dec_I (
		.in  (waddr_r),
		.out (waddr_dec)
	);

	// Generate write pulses
	ram_wpulse_341426151397261906 #(
		.N(16)
	) wpulse_I (
		.clk      (clk),
		.we       (we_r),
		.addr_dec (waddr_dec),
		.dly      (2'b11),	// FIXME
		.wpulse   (store_gate)
	);

	// Send data to store array
	assign store_in = wdata_r;


	// Storage array
	// -------------

	generate
		for (i=0; i<16; i=i+1)
		begin : store_cell_loop

			wire [W-1:0] l_store_data;

			sky130_fd_sc_hd__dlxtp_1 store_cell_I[W-1:0] (
`ifdef WITH_POWER
				.VPWR   (1'b1),
				.VGND   (1'b0)
`endif
				.D      (store_in),
				.GATE   (store_gate[i]),
				.Q      (l_store_data)
			);

			assign store_data[i] = l_store_data;

		end
	endgenerate


	// Read path
	// ---------

	// Register wdata, waddr, we

		// Maybe we could get away with some latches, but for timing
		// analysis PoV it's easier to have termination point that are FFs

	sky130_fd_sc_hd__dfxtp_1 raddr_reg_msb_I[1:0] (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (raddr[3:2]),
		.Q    (raddr_r[3:2])
	);

	sky130_fd_sc_hd__dfxtp_4 raddr_reg_lsb_I[1:0] (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (raddr[1:0]),
		.Q    (raddr_r[1:0])
	);

	sky130_fd_sc_hd__dfxtp_1 re_reg_I (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.CLK  (clk),
		.D    (re),
		.Q    (re_r)
	);

	// Decode MSB
	ram_dec2_341426151397261906 raddr_dec_I (
		.in (raddr_r[3:2]),
		.out(raddr_msb_dec)
	);

	// Select path by group of 4
	generate
		for (i=0; i<16; i=i+4)
		begin : rdata_sel_loop

			wire [W-1:0] l_in0_mux;
			wire [W-1:0] l_in1_mux;
			wire [W-1:0] l_in2_mux;
			wire [W-1:0] l_in3_mux;
			wire [W-1:0] l_out_mux;

			// Pickup data
			assign l_in0_mux = store_data[i+0];
			assign l_in1_mux = store_data[i+1];
			assign l_in2_mux = store_data[i+2];
			assign l_in3_mux = store_data[i+3];

			// Generate mux4 for each group
			sky130_fd_sc_hd__mux4_1 rdata_mux_I[W-1:0] (
`ifdef WITH_POWER
				.VPWR (1'b1),
				.VGND (1'b0),
`endif
				.A0   (l_in0_mux),
				.A1   (l_in1_mux),
				.A2   (l_in2_mux),
				.A3   (l_in3_mux),
				.X    (l_out_mux),
				.S0   (raddr_r[0]),
				.S1   (raddr_r[1])
			);

			// Then use tristate buffers
			sky130_fd_sc_hd__einvp_2 rdata_tbuf_I[W-1:0] (
`ifdef WITH_POWER
				.VPWR (1'b1),
				.VGND (1'b0),
`endif
				.A    (l_out_mux),
				.TE   (raddr_msb_dec[i/4]),
				.Z    (rdata_tbuf)
			);

		end
	endgenerate

	// And final output latch/buffer
`ifdef SIM
	assign #1.5 clk_dly = clk;
`else
	sky130_fd_sc_hd__clkdlybuf4s50_1 clk_dly_I (
`ifdef WITH_POWER
		.VPWR (1'b1),
		.VGND (1'b0),
`endif
		.A    (clk),
		.X    (clk_dly)
	);
`endif

	sky130_fd_sc_hd__nand3_2 data_le_n_I (
`ifdef WITH_POWER
		.VPWR   (1'b1),
		.VGND   (1'b0)
`endif
		.A      (clk),
		.B      (clk_dly),
		.C      (re_r),
		.Y      (rdata_le_n)
	);

	sky130_fd_sc_hd__inv_1 rdata_inv_I[W-1:0] (
`ifdef WITH_POWER
		.VPWR   (1'b1),
		.VGND   (1'b0)
`endif
		.A      (rdata_tbuf),
		.Y      (rdata_inv)
	);

	sky130_fd_sc_hd__dlxtn_4 rdata_latch_I[W-1:0] (
`ifdef WITH_POWER
		.VPWR   (1'b1),
		.VGND   (1'b0)
`endif
		.D      (rdata_inv),
		.GATE_N (rdata_le_n),
		.Q      (rdata)
	);

endmodule // ram_top_341426151397261906