`default_nettype none
module our_ram
#( int awid = 5, int dwid = 15, int size = 1 << awid )
( output uwire [dwid:1] out_data,
input uwire [awid:1] addr,
input uwire [dwid:1] in_data,
input uwire write, clk );
logic [dwid:1] storage[size];
assign out_data = storage[ addr ];
always_ff @( posedge clk ) if ( write ) storage[ addr ] <= in_data;
endmodule
typedef enum logic [1:0] { CMD_INS, CMD_RD, CMD_DEL, CMD_NOP } Cam_Command;
module cam
#( int kwid = 10, int dwid = 10, int ssize = 16, int ssize_lg = $clog2(ssize) )
( output uwire [dwid:1] out_data,
output uwire out_valid,
output uwire ready,
input uwire [kwid:1] key,
input uwire [dwid:1] in_data,
input uwire Cam_Command cmd,
input uwire clk );
logic [dwid:1] storage_data [ssize];
logic [kwid:1] storage_key [ssize];
logic [ssize-1:0] storage_full;
logic [ssize_lg:1] midx, eidx;
logic mmatch, ematch;
cadence initial for ( int i=0; i<ssize; i++ ) storage_full[i] = 0;
initial begin
if ( ssize > ( 1 << kwid ) ) begin
$display("Size of cam too large for key size.");
$fatal(1);
end
end
cadence
always_comb begin
mmatch = 0; midx = 0; for ( int i=0; i<ssize; i++ )
if ( storage_full[i] && storage_key[i] == key )
begin mmatch = 1; midx = i; end
end
assign out_data = storage_data[midx];
assign out_valid =
cmd == CMD_RD && mmatch ||
cmd == CMD_INS && ( mmatch || ematch );
always_comb begin
ematch = 0; eidx = 0; for ( int i=0; i<ssize; i++ )
if ( !storage_full[i] )
begin ematch = 1; eidx = i; end
end
assign ready = 1;
always @( posedge clk )
case ( cmd )
CMD_DEL: if ( mmatch ) storage_full[midx] <= 0;
CMD_INS:
if ( ematch && !mmatch ) begin
storage_full[eidx] <= 1;
storage_data[eidx] <= in_data;
storage_key[eidx] <= key;
end else if ( mmatch ) begin
storage_data[midx] <= in_data;
end
default:;
endcase
endmodule
module hash #(int key_bits = 8, int hash_bits = 2)
(output logic [hash_bits-1:0] hash, input uwire [key_bits-1:0] key);
always_comb begin
hash = 0;
for ( int i=0; i < key_bits; i += hash_bits ) hash ^= key >> i;
end
endmodule
module cam_hash
#( int kwid = 9,
int dwid = 10,
int ssize = 16,
int num_sets_lg = 1,
bit iterate = 0,
bit hash_early = 1 )
( output uwire [dwid:1] out_data,
output uwire out_valid,
output uwire ready,
input uwire [kwid:1] in_key,
input uwire [dwid:1] in_data,
input uwire Cam_Command in_cmd,
input uwire clk);
localparam int num_sets = 1 << num_sets_lg;
localparam int set_size = ssize >> num_sets_lg;
localparam int set_size_lg = $clog2(set_size);
localparam int hkey_size = num_sets_lg ? num_sets_lg : 1;
logic [dwid:1] storage_data [num_sets][set_size];
logic [kwid:1] storage_key [num_sets][set_size];
logic [0:num_sets-1][0:set_size-1] storage_full;
logic [set_size_lg:1] midx, eidx;
logic mmatch, ematch;
logic [kwid:1] b_key;
logic [dwid:1] b_data;
logic [hkey_size-1:0] b_hash;
Cam_Command b_cmd;
cadence initial
for ( int j=0; j<num_sets; j++ ) for ( int i=0; i<set_size; i++ )
storage_full[j][i] = 0;
initial begin
if ( ssize > ( 1 << kwid ) ) begin
$display("Size of cam too large for key size.");
$fatal(1);
end
end
cadence
uwire [hkey_size-1:0] ohm_key_out;
always_ff @( posedge clk ) begin
b_key <= in_key;
b_data <= in_data;
b_cmd <= in_cmd;
end
if ( hash_early )
always_ff @( posedge clk ) b_hash <= ohm_key_out;
uwire [kwid:1] ohm_key_in = hash_early ? in_key : b_key;
uwire [hkey_size-1:0] hashed_key = hash_early ? b_hash : ohm_key_out;
if ( num_sets_lg > 0 )
hash #(kwid,num_sets_lg) our_hash_module( ohm_key_out, ohm_key_in );
else
assign ohm_key_out = 0;
always_comb begin
mmatch = 0; midx = 0; for ( int i=0; i<set_size; i++ )
if ( storage_full[hashed_key][i]
&& storage_key[hashed_key][i] == b_key )
begin mmatch = 1; midx = i; end
end
assign out_data = storage_data[hashed_key][midx];
assign out_valid =
mmatch && ( b_cmd == CMD_RD || b_cmd == CMD_DEL )
|| ( mmatch || ematch ) && b_cmd == CMD_INS;
assign ready = 1;
always_comb begin
ematch = 0; eidx = 0; for ( int i=0; i<set_size; i++ )
if ( !storage_full[hashed_key][i] )
begin ematch = 1; eidx = i; end
end
always @( posedge clk )
case ( b_cmd )
CMD_DEL: if ( mmatch ) storage_full[hashed_key][midx] <= 0;
CMD_INS: if ( ematch && !mmatch ) begin
storage_full[hashed_key][eidx] <= 1;
storage_data[hashed_key][eidx] <= b_data;
storage_key[hashed_key][eidx] <= b_key;
end else if ( mmatch ) begin
storage_data[hashed_key][midx] <= b_data;
end
default:;
endcase
endmodule
module cam_multicycle
#( int kwid = 9,
int dwid = 10,
int ssize = 16,
int num_sets_lg = 1,
bit use_hash = 1 )
( output uwire [dwid:1] out_data,
output uwire out_valid,
output uwire ready,
input uwire [kwid:1] in_key,
input uwire [dwid:1] in_data,
input uwire Cam_Command in_cmd,
input uwire clk);
localparam int num_sets = 1 << num_sets_lg;
localparam int set_size = ssize >> num_sets_lg;
localparam int set_size_lg = $clog2(set_size);
logic [dwid:1] storage_data [num_sets][set_size];
logic [kwid:1] storage_key [num_sets][set_size];
logic [0:num_sets-1][0:set_size-1] storage_full;
logic [set_size_lg:1] midx, eidx;
logic mmatch, ematch;
logic [num_sets_lg:1] iter;
logic [kwid:1] b_key; logic [dwid:1] b_data;
Cam_Command b_cmd;
logic [set_size_lg:1] b_eidx;
logic [num_sets_lg:1] b_sidx;
logic b_eidx_full;
uwire [(num_sets_lg?num_sets_lg-1:0):0] hashed_key;
cadence initial begin
for ( int j=0; j<num_sets; j++ ) for ( int i=0; i<set_size; i++ )
storage_full[j][i] = 0;
iter = num_sets - 1;
end
initial begin
if ( ssize > ( 1 << kwid ) ) begin
$display("Size of cam too large for key size.");
$fatal(1);
end
end
cadence
uwire start = ready && in_cmd != CMD_NOP;
always @( posedge clk ) if ( start ) begin
b_key <= in_key;
b_data <= in_data;
b_cmd <= in_cmd;
iter <= 0;
end else begin
iter <= ready ? iter : iter + 1;
end
if ( use_hash && num_sets_lg > 0 )
hash #(kwid,num_sets_lg) our_hash_module(hashed_key,b_key);
else
assign hashed_key = 0;
uwire [num_sets_lg:1] set_idx = hashed_key ^ iter;
always_comb begin
mmatch = 0;
midx = 0;
for ( int i=0; i<set_size; i++ )
if ( storage_full[set_idx][i] && storage_key[set_idx][i] == b_key )
begin mmatch = 1; midx = i; end
end
uwire found = ( b_cmd != CMD_NOP ) && mmatch;
uwire last_iter = iter == num_sets - 1;
assign ready = found || last_iter;
assign out_data = storage_data[set_idx][midx];
always_comb begin
ematch = 0;
eidx = 0;
for ( int i=0; i<set_size; i++ )
if ( !storage_full[set_idx][i] ) begin ematch = 1; eidx = i; end
end
uwire [set_size_lg:1] b_eidx_next = b_eidx_full ? b_eidx : eidx;
uwire [num_sets_lg:1] b_sidx_next = b_eidx_full ? b_sidx : set_idx;
uwire b_eidx_full_next = b_eidx_full || ematch;
always_ff @( posedge clk ) begin
if ( !b_eidx_full ) begin
b_eidx <= b_eidx_next;
b_sidx <= b_sidx_next;
end
b_eidx_full <= b_eidx_full_next && !start;
end
assign out_valid =
( b_cmd == CMD_RD || b_cmd == CMD_DEL ) && mmatch
|| b_cmd == CMD_INS && ( mmatch || b_eidx_full_next );
always @( posedge clk ) begin
if ( found )
case ( b_cmd )
CMD_DEL: storage_full[set_idx][midx] <= 0;
CMD_INS: storage_data[set_idx][midx] <= b_data;
default:;
endcase
else if ( last_iter && b_eidx_full_next )
case ( b_cmd )
CMD_INS: begin
storage_key[b_sidx_next][b_eidx_next] <= b_key;
storage_full[b_sidx_next][b_eidx_next] <= 1;
storage_data[b_sidx_next][b_eidx_next] <= b_data;
end
default:;
endcase
end
endmodule
cadence
module test_cam();
localparam int kwid = 16;
localparam int dwid = 20;
localparam int ssize_lg = 5;
localparam int ssize = 1 << ssize_lg;
localparam int num_sets_lg = 4;
localparam int num_sets = 1 << num_sets_lg;
localparam int idx_bits = $clog2(ssize);
localparam int num_pairs = ssize + 5;
localparam int num_tests = 40000;
localparam bit trace = 0;
localparam logic [kwid:1] trace_key = 'hx;
uwire [dwid:1] out_data;
uwire out_valid, ready;
logic [kwid:1] key;
logic [dwid:1] in_data;
Cam_Command cmd;
int cycles;
logic clk;
cam_hash #(kwid,dwid,ssize,num_sets_lg,0,1) c1
(out_data, out_valid, ready, key, in_data, cmd, clk);
initial begin
clk = 0; cycles = 0;
forever begin
#1;
if ( clk == 0 ) cycles <= cycles + 1;
clk <= !clk;
end
end
bit keys[int];
struct { logic [kwid:1] k; logic [dwid:1] d; bit in; } pairs[num_pairs];
initial begin
#( num_tests * num_sets * 2 * 2 );
$display("Tests taking too long, stopping.");
$finish(2);
end
initial begin
logic [dwid:1] out_expect;
logic valid_expect;
automatic int derr_count = 0;
automatic int verr_count = 0;
automatic int ins_count = 0;
automatic int ins_reject_occ_sum = 0;
automatic int ins_reject_space = 0;
automatic int ins_reject_full = 0;
automatic int rd_in_cycles_sum = 0;
automatic int rd_in_count = 0;
automatic int del_count = 0;
automatic int occ_expect = 0;
$display("\n\n");
$display("Running testbench on module with n = %0d, m = %0d.\n",
ssize_lg, num_sets_lg);
for ( int i=0; i<num_pairs; i++ ) begin
logic [kwid:1] k;
do k = $random(); while ( keys.exists( k ) );
keys[k] = 1;
pairs[i].k = k;
pairs[i].d = $random();
pairs[i].in = 0;
end
cmd = CMD_NOP;
repeat ( 2 ) @( negedge clk );
while ( !ready ) @( negedge clk );
for ( int i=0; i<num_tests; i++ ) begin
automatic int cycles_start = cycles;
automatic int idx = {$random} % num_pairs;
key = pairs[idx].k;
if ( pairs[idx].in ) begin
valid_expect = 1;
if ( {$random} % 16 < 15 ) begin
cmd = {$random} % 16 < 8 ? CMD_INS : CMD_RD;
if ( cmd == CMD_INS ) begin
pairs[idx].d++;
out_expect = 'hx;
end else begin
out_expect = pairs[idx].d;
end
in_data = pairs[idx].d;
end else begin
cmd = CMD_DEL;
occ_expect--;
out_expect = 'hx;
pairs[idx].in = 0;
del_count++;
end
end else begin
if ( {$random} % 16 < 8 ) begin
cmd = CMD_RD;
out_expect = 'hx;
valid_expect = 0;
end else begin
cmd = CMD_INS;
out_expect = 'hx;
valid_expect = 'hx;
in_data = pairs[idx].d;
end
end
do @( negedge clk ); while ( !ready );
if ( cmd == CMD_RD && valid_expect === 1 )
begin
rd_in_count++;
rd_in_cycles_sum += cycles - cycles_start;
end
if ( ( trace || trace_key === key )
&& ( derr_count + verr_count ) < 2 )
$display("Command %d, key %h, expect %h in data %h out_valid %d",
cmd, key, out_expect, in_data, out_valid);
if ( cmd == CMD_INS && !pairs[idx].in )
begin
ins_count++;
if ( out_valid ) begin
occ_expect++;
pairs[idx].in = 1;
out_expect = 'hx;
end else begin
ins_reject_occ_sum += occ_expect;
if ( occ_expect < ssize )
ins_reject_space++;
else
ins_reject_full++;
out_expect = 'hx;
end
end
if ( out_expect !== (dwid)'('hx) && out_data !== out_expect ) begin
derr_count++;
if ( derr_count < 20 )
$display(" i %4d %h != %h (correct) cmd %d key %h",
i, out_data, out_expect, cmd, key);
end
if ( valid_expect !== 1'hx && valid_expect !== out_valid ) begin
verr_count++;
if ( verr_count < 20 )
$display(" i %4d %h != %h (correct) output valid bit cmd %d key %h",
i, out_valid, valid_expect, cmd, key);
end
end
$display("Done with %1d tests (%1d ins, %1d del), %1d+%1d = %1d errors.",
num_tests, ins_count, del_count,
derr_count, verr_count, derr_count + verr_count);
$display("Insert rejected with space %.2f%%, when full %.2f%%",
100 * real'(ins_reject_space) / ins_count,
100 * real'(ins_reject_full) / ins_count);
$display("Average occupancy at reject %.2f%%",
100.0 * real'(ins_reject_occ_sum)
/ ( ssize * ( ins_reject_space + ins_reject_full )));
$display("Avg read time when in: %.2f cyc",
real'(rd_in_cycles_sum) / rd_in_count);
$display("\n\n");
$finish(2);
end
endmodule
cadence
`ifdef XXX
Module Name Area Clock Total
Period Delay
cam_multicycle 8 8 8 0 0 145908 2562 2562
cam_multicycle 8 8 8 1 0 168744 2715 2715
cam_multicycle 8 8 8 2 0 150592 2697 5394
cam_multicycle 8 8 8 3 0 140592 2844 11376
cam_multicycle 8 8 16 0 0 283312 2833 2833
cam_multicycle 8 8 16 1 0 294476 2922 2922
cam_multicycle 8 8 16 2 0 286760 3067 6134
cam_multicycle 8 8 16 3 0 288160 3094 12376
cam_multicycle 8 8 16 4 0 259476 3386 27088
cam_multicycle 8 8 32 0 0 545844 3386 3386
cam_multicycle 8 8 32 1 0 557788 3259 3259
cam_multicycle 8 8 32 2 0 533648 3234 6468
cam_multicycle 8 8 32 3 0 521468 3400 13600
cam_multicycle 8 8 32 4 0 517164 3430 27440
cam_multicycle 8 8 32 5 0 488280 3612 57792
cam 8 8 8 134172 2160 2160
cam_hash 8 8 8 0 140228 2398 2398
cam_hash 8 8 8 1 161888 3495 3495
cam_hash 8 8 8 2 146108 3314 3314
cam_hash 8 8 8 3 127784 3150 3150
cam 8 8 16 272228 2500 2500
cam_hash 8 8 16 0 275828 2870 2870
cam_hash 8 8 16 1 282612 3617 3617
cam_hash 8 8 16 2 262696 3641 3641
cam_hash 8 8 16 3 259892 3473 3473
cam_hash 8 8 16 4 211512 3191 3191
cam 8 8 32 537956 3072 3072
cam_hash 8 8 32 0 549620 3418 3418
cam_hash 8 8 32 1 547092 4182 4182
cam_hash 8 8 32 2 508092 3838 3838
cam_hash 8 8 32 3 517492 3720 3720
cam_hash 8 8 32 4 432868 3650 3650
cam_hash 8 8 32 5 373464 3348 3348
cam 8 8 8 134172 2160 2160
cam 8 8 16 272228 2500 2500
cam 8 8 32 537956 3072 3072
cam_hash 8 8 16 0 275828 2870 2870
cam_hash 8 8 16 1 282612 3617 3617
cam_hash 8 8 16 2 262696 3641 3641
cam_hash 8 8 16 3 259892 3473 3473
cam_hash 8 8 16 4 211512 3191 3191
cam_hash 8 8 32 0 549620 3418 3418
cam_hash 8 8 32 1 547092 4182 4182
cam_hash 8 8 32 2 508092 3838 3838
cam_hash 8 8 32 3 517492 3720 3720
cam_hash 8 8 32 4 432868 3650 3650
cam_hash 8 8 32 5 373464 3348 3348
cam_hash 8 8 8 0 140228 2398 2398
cam_hash 8 8 8 1 161888 3495 3495
cam_hash 8 8 8 2 146108 3314 3314
cam_hash 8 8 8 3 127784 3150 3150
Data for early hash version of cam_hash.
Module Name Area Clock Total
Period Delay
cam 8 8 8 134172 2160 2160
cam_hash 8 8 8 0 139544 2396 2396
cam_hash 8 8 8 1 151044 2650 2650
cam_hash 8 8 8 2 139524 2830 2830
cam_hash 8 8 8 3 128896 2594 2594
cam 8 8 16 272228 2500 2500
cam_hash 8 8 16 0 286792 2859 2859
cam_hash 8 8 16 1 277720 2988 2988
cam_hash 8 8 16 2 270144 3100 3100
cam_hash 8 8 16 3 280248 3098 3098
cam_hash 8 8 16 4 232020 2920 2920
cam 8 8 32 537956 3072 3072
cam_hash 8 8 32 0 540360 3491 3491
cam_hash 8 8 32 1 559960 3346 3346
cam_hash 8 8 32 2 512124 3258 3258
cam_hash 8 8 32 3 494716 3471 3471
cam_hash 8 8 32 4 473444 3436 3436
cam_hash 8 8 32 5 402104 3190 3190
storage_data_reg[2][1][1]/D DFFPOSX1 +0 2621
storage_data_reg[2][1][1]/CLK setup 0 +260 2881 R
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(clock my_clk) capture 2500 R
----------------------------------------------------------------------------
Timing slack : -381ps (TIMING VIOLATION)
Start-point : b_hash_reg[0]/CLK
End-point : storage_data_reg[2][1][1]/D
`endif