1// 2// Copyright 2018 Ettus Research, A National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: kv_map 7 8module kv_map #( 9 parameter KEY_WIDTH = 16, 10 parameter VAL_WIDTH = 32, 11 parameter SIZE = 6 12) ( 13 // Clock and reset 14 input wire clk, 15 input wire reset, 16 // Insert port 17 input wire insert_stb, 18 input wire [KEY_WIDTH-1:0] insert_key, 19 input wire [VAL_WIDTH-1:0] insert_val, 20 output wire insert_busy, 21 // Find port 22 input wire find_key_stb, 23 input wire [KEY_WIDTH-1:0] find_key, 24 output wire find_res_stb, 25 output wire find_res_match, 26 output wire [VAL_WIDTH-1:0] find_res_val, 27 // Count 28 output reg [SIZE-1:0] count = {SIZE{1'b0}} 29); 30 31 //------------------------------------------------- 32 // Instantiate a CAM and a RAM 33 //------------------------------------------------- 34 // The CAM serves as a "set" and the RAM serves as a 35 // random addressable "array". Using thse two data structures 36 // we can build a map. The role of the CAM is to compress 37 // the key to an address that can be used to lookup data 38 // stored in the RAM 39 40 wire cam_wr_en, cam_wr_busy, cam_rd_match; 41 wire [SIZE-1:0] cam_wr_addr, cam_rd_addr; 42 wire [KEY_WIDTH-1:0] cam_wr_data, cam_rd_key; 43 44 wire ram_wr_en; 45 wire [SIZE-1:0] ram_wr_addr; 46 reg [SIZE-1:0] ram_rd_addr; 47 wire [VAL_WIDTH-1:0] ram_wr_data, ram_rd_data; 48 49 cam #( 50 .DATA_WIDTH (KEY_WIDTH), 51 .ADDR_WIDTH (SIZE), 52 .CAM_STYLE (SIZE > 8 ? "BRAM" : "SRL"), 53 .SLICE_WIDTH (SIZE > 8 ? 9 : 5) 54 ) cam_i ( 55 .clk (clk), 56 .rst (reset), 57 .write_addr (cam_wr_addr), 58 .write_data (cam_wr_data), 59 .write_delete(1'b0), 60 .write_enable(cam_wr_en), 61 .write_busy (cam_wr_busy), 62 .compare_data(cam_rd_key), 63 .match_addr (cam_rd_addr), 64 .match (cam_rd_match), 65 .match_many (), 66 .match_single() 67 ); 68 69 ram_2port #( 70 .DWIDTH(VAL_WIDTH), 71 .AWIDTH(SIZE) 72 ) mem_i ( 73 .clka (clk), 74 .ena (ram_wr_en), 75 .wea (1'b1), 76 .addra (ram_wr_addr), 77 .dia (ram_wr_data), 78 .doa (/* Write port only */), 79 .clkb (clk), 80 .enb (1'b1), 81 .web (1'b0), 82 .addrb (ram_rd_addr), 83 .dib (/* Read port only */), 84 .dob (ram_rd_data) 85 ); 86 87 // Pipeline read address into RAM 88 always @(posedge clk) 89 ram_rd_addr <= cam_rd_addr; 90 91 //------------------------------------------------- 92 // Find state machine 93 //------------------------------------------------- 94 // The lookup process has three cycles of latency 95 // - CAM lookup has a 1 cycle latency 96 // - The lookup address into the RAM is delayed by 1 cycle for timing 97 // - The RAM takes 1 cycle to produce data 98 99 localparam FIND_CYC = 3; 100 101 reg [FIND_CYC-1:0] find_key_stb_shreg = {FIND_CYC{1'b0}}; 102 reg [FIND_CYC-2:0] find_match_shreg = {(FIND_CYC-1){1'b0}}; 103 reg find_pending = 1'b0; 104 105 wire find_busy = find_pending | find_key_stb; 106 107 // Delay the find valid signal to account for the latency 108 // of the CAM and RAM 109 always @(posedge clk) begin 110 find_key_stb_shreg <= reset ? {FIND_CYC{1'b0}} : 111 {find_key_stb_shreg[FIND_CYC-2:0], find_key_stb}; 112 end 113 assign find_res_stb = find_key_stb_shreg[FIND_CYC-1]; 114 115 // Latch the find signal to compute pending 116 always @(posedge clk) begin 117 if (find_key_stb) 118 find_pending <= 1'b1; 119 else if (find_pending) 120 find_pending <= ~find_res_stb; 121 end 122 123 // Delay the match signal to account for the latency of the RAM 124 always @(posedge clk) begin 125 find_match_shreg <= reset ? {(FIND_CYC-1){1'b0}} : 126 {find_match_shreg[FIND_CYC-3:0], cam_rd_match}; 127 end 128 assign find_res_match = find_match_shreg[FIND_CYC-2]; 129 130 131 //------------------------------------------------- 132 // Insert state machine 133 //------------------------------------------------- 134 135 localparam [2:0] ST_IDLE = 3'd0; 136 localparam [2:0] ST_WAIT_FIND = 3'd1; 137 localparam [2:0] ST_CAM_READ = 3'd2; 138 localparam [2:0] ST_CAM_CHECK_MATCH = 3'd3; 139 localparam [2:0] ST_CAM_RAM_WRITE = 3'd4; 140 localparam [2:0] ST_CAM_WRITE_WAIT = 3'd5; 141 localparam [2:0] ST_RAM_WRITE = 3'd6; 142 143 reg [2:0] ins_state = ST_IDLE; 144 145 reg [KEY_WIDTH-1:0] ins_key_cached; 146 reg [VAL_WIDTH-1:0] ins_val_cached; 147 reg [SIZE-1:0] write_addr = {SIZE{1'b0}}; 148 reg [SIZE-1:0] next_addr = {SIZE{1'b0}}; 149 150 151 always @(posedge clk) begin 152 if (reset) begin 153 ins_state <= ST_IDLE; 154 next_addr <= {SIZE{1'b0}}; 155 end else begin 156 case (ins_state) 157 158 // Idle and waiting for an insert transaction 159 // 160 ST_IDLE: begin 161 // Cache insertion parameters 162 if (insert_stb) begin 163 ins_key_cached <= insert_key; 164 ins_val_cached <= insert_val; 165 // Wait for find to finish 166 ins_state <= find_busy ? ST_WAIT_FIND : ST_CAM_READ; 167 end 168 end 169 170 // Wait for a find transaction to finish 171 // 172 ST_WAIT_FIND: begin 173 // Wait for find to finish 174 if (~find_busy) 175 ins_state <= ST_CAM_READ; 176 end 177 178 // Read the CAM to check if the key to insert already exists 179 // 180 ST_CAM_READ: begin 181 // Ensure that find always has priority 182 if (~find_key_stb) 183 ins_state <= ST_CAM_CHECK_MATCH; 184 end 185 186 // Look at the CAM match signal to evaluate if we skip writing the CAM 187 // 188 ST_CAM_CHECK_MATCH: begin 189 // If the CAM already has this key, then overwrite it 190 if (cam_rd_match) begin 191 ins_state <= ST_RAM_WRITE; 192 write_addr <= cam_rd_addr; 193 end else if (~cam_wr_busy) begin 194 ins_state <= ST_CAM_RAM_WRITE; 195 write_addr <= next_addr; 196 next_addr <= next_addr + 1'b1; 197 end 198 end 199 200 // Write the specified key to the CAM and value to the RAM 201 // 202 ST_CAM_RAM_WRITE: begin 203 ins_state <= ST_CAM_WRITE_WAIT; 204 end 205 206 // Wait for CAM write to finish 207 // 208 ST_CAM_WRITE_WAIT: begin 209 if (~cam_wr_busy) begin 210 ins_state <= ST_IDLE; 211 count <= next_addr; 212 end 213 end 214 215 // Write the specified value to the RAM 216 // 217 ST_RAM_WRITE: begin 218 ins_state <= ST_IDLE; 219 count <= next_addr; 220 end 221 222 default: begin 223 // We should not get here 224 ins_state <= ST_IDLE; 225 end 226 endcase 227 end 228 end 229 230 // CAM Read Port: 231 // - Find has priority so it can interrupt an insert 232 assign cam_rd_key = 233 (ins_state != ST_CAM_READ || find_key_stb) ? find_key : ins_key_cached; 234 235 // RAM Write Port: 236 // - The RAM write enable is held high for 1 cycle 237 // - The address may come from a CAM lookup or could generated 238 assign ram_wr_en = (ins_state == ST_RAM_WRITE || ins_state == ST_CAM_RAM_WRITE); 239 assign ram_wr_addr = write_addr; 240 assign ram_wr_data = ins_val_cached; 241 242 // CAM Write Port: 243 // - The CAM write enable is held high for 1 cycle 244 // - The address may come from a CAM lookup or could generated (same as RAM) 245 assign cam_wr_en = (ins_state == ST_CAM_RAM_WRITE); 246 assign cam_wr_addr = write_addr; 247 assign cam_wr_data = ins_key_cached; 248 249 // Outputs 250 assign insert_busy = (ins_state != ST_IDLE); 251 assign find_res_val = ram_rd_data; 252 253endmodule 254