Title: Combinational way of implementing a CAM in verilog
Tags: verilog;system-verilog;fpga
Question: I'm trying to implement a cache and index lookup memory in SystemVerilog. It's a simple CAM + circular buffer. The interface is:
input rst_n;
input clk;
input [WORD_BITS-1:0] inp;
input rd_en;
input wr_en;
output logic [DEPTH_BITS-1:0] index;
output logic index_valid;
reg [WORD_BITS-1:0] buffer[$pow(2, DEPTH_BITS)];
reg [DEPTH_BITS-1:0] next;
There's basic async reset code. There's a synchronous block that stores inp in buffer and advances next whenever wr_en is high.
Now I'm trying to come up with an efficient and readable way of finding the index of inp when rd_en is high. It seems this could be completely combinational except when clocking the result into the index output. The way I'm visualizing it in my head is to xor inp with all of the buffer locations (it will be fairly small, perhaps 64 entries) then if that is equal to 0 the entry was found. Then a block to arbitrarily choose one of the indices with a 0 value. This is where is differs from a traditional CAM, there could be multiple entries for the same value but I really only need the index of one of those and it doesn't matter which one.
Any thoughts on how to do this in System Verilog (2012)? I know I can loop through all the memory locations synchronously and save a bunch of area but I'd rather it be fast than small. I'm targeting FPGAs. (initially inexpensive Lattice and maybe Xilinx parts) I know a few of the Lattice parts actually have CAM blocks but this is for cases where that isn't available.

Here is the accepted answer: Following up on a suggestion from another forum, the following seems to work well.
always_comb begin
index_valid = 0;
for (int i=0; i < 64; i=i+1) begin
if (rd_en) begin
if (inp == buffer[i]) begin
index = i;
index_valid = 1'b1;
end
end
end
end
Comment for this answer: This looks good. You probably want to have a default assignment for `index` as well (next to `index_valid = 0;`), even though it is qualified by `index_valid`. This avoid latches in your synthesis.
Comment for this answer: I had to hard code the depth as as least Icarus didn't like using a localparam in the for loop for an always_comb.


{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2021:00:12/06/21', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<verilog><system-verilog><fpga>', 'Title': 'Combinational way of implementing a CAM in verilog', 'ViewCount': 164, 'id': 70249794, 'user_AboutMe': '', 'user_AccountId': 172349.0, 'user_DisplayName': 'Trevor', 'user_Reputation': 1349.0, 'user_Views': 82.0, 'user_id': 400327.0}}