FPGA designer here. Just wanted to point out that “efficiency” is highly context sensitive in FPGA design. Everything is an area / speed / power trade-off. If you only need a ram that is 8-bits wide and 64 words deep then it might be way inefficient to waste a dedicated 18kbit block ram on it when it would fit better into 2 LUTs. This is why Xilinx, for one, provides pragma such as RAM_STYLE to help guide synthesis:
(* ram_style = "distributed" *) reg [data_size-1:0] myram [2**addr_size-1:0];
block: Instructs the tool to infer RAMB type components.
distributed: Instructs the tool to infer the LUT RAMs.
registers: Instructs the tool to infer registers instead of RAMs.
ultra: Instructs the tool to use the UltraScale+TM URAM primitives.
See: https://www.xilinx.com/support/documentation/sw_manuals/xili...
edit: formatting*