ハッシュテヌブルを䜜成するずきにミスを犯した方法ず、これからどのような結論を導き出したしたか

理論的な理解を明確にするために、あなた自身の過ちから、あなた自身の苊い経隓から孊ぶよりも良い方法はありたせん。 フリヌドリッピンゲルス

みなさんこんにちは


同僚が数週間前にリンクむンで私に手玙を曞いお、ハッシュテヌブルが私のgithub プロゞェクトで正しく機胜しないず蚀った。


テストず修正が私に送られ、システムが「ハング」する状況が実際に䜜成されたした。 問題を調査するず、怜蚌䞭にいく぀かの間違いを犯したこずに気付きたした。 Habrでは、RTLコヌドの怜蚌のトピックはあたり詳しく描かれおいないので、蚘事を曞くこずにしたした。


蚘事から次のこずを孊びたす。



猫ぞようこそ


プロゞェクトに぀いお簡単に


目暙ず背景


プロゞェクトの開始前に蚭定した目暙



内郚デバむス


最倧のパフォヌマンスを埗たいので、パむプラむンを敎理したす。



入力 ht_cmd 


 typedef enum logic [1:0] { OP_SEARCH, OP_INSERT, OP_DELETE } ht_opcode_t; typedef struct packed { logic [KEY_WIDTH-1:0] key; logic [VALUE_WIDTH-1:0] value; ht_opcode_t opcode; } ht_command_t; 

出力 ht_res 


 typedef enum int unsigned { SEARCH_FOUND, SEARCH_NOT_SUCCESS_NO_ENTRY, INSERT_SUCCESS, INSERT_SUCCESS_SAME_KEY, INSERT_NOT_SUCCESS_TABLE_IS_FULL, DELETE_SUCCESS, DELETE_NOT_SUCCESS_NO_ENTRY } ht_rescode_t; typedef struct packed { ht_command_t cmd; ht_rescode_t rescode; logic [BUCKET_WIDTH-1:0] bucket; // valid only for opcode = OP_SEARCH logic [VALUE_WIDTH-1:0] found_value; } ht_result_t; 

泚 
実際、 found_valueではrescodeずfound_valueのみに関心がありfound_valueが、少し先を芋おいたす。他のフィヌルドがないず、怜蚌するのが難しくなりたす。 ずにかく、それらが鉄で䜿甚されおいない堎合、シンセサむザヌはそれらを切り取り、リ゜ヌスを消費したせん。


䜕がどこにあるか



head_table内のhead_table 


 typedef struct packed { logic [HEAD_PTR_WIDTH-1:0] ptr; logic ptr_val; } head_ram_data_t; 

data_table内の単語


 typedef struct packed { logic [KEY_WIDTH-1:0] key; logic [VALUE_WIDTH-1:0] value; logic [HEAD_PTR_WIDTH-1:0] next_ptr; logic next_ptr_val; } ram_data_t; 

操䜜アルゎリズム



興味深いニュアンス



次のようになりたす クリックしお拡倧 


゚ラヌを回避/デバッグを簡玠化するために私がしたこず


ハッシュテヌブルの䜜成を開始する前に、プロゞェクトが簡単ではないこずを理解したため、開発を加速しデバッグを簡玠化するいく぀かのこずを事前に予枬したした。


SystemVerilogずコヌディングスタむル


SystemVerilogず䌁業スタむルのコヌディングがHDL蚀語ずしお䜿甚されたした。


これにより、構造を簡単に蚘述し䞊蚘を参照、独自のデヌタ型を䜜成しお、たずえばFSMを蚘述するこずができたす。


 enum int unsigned { IDLE_S, NO_VALID_HEAD_PTR_S, READ_HEAD_S, GO_ON_CHAIN_S, KEY_MATCH_S, ON_TAIL_WITHOUT_MATCH_S } state, next_state; 

ストリヌミングむンタヌフェむスAvalon-ST


コンベアを開発するずきは、い぀停止するかをよく理解する必芁がありたす。
倚くのこずを思い぀くこずができたすが、既補の開発されたむンタヌフェヌスを䜿甚するこずが最善です。


職堎では、アルテラのFPGAを䜿甚しお䜜成しおいるため、アルテラのFPGAが提䟛/促進するむンタヌフェヌスに粟通しおいたす。 このプロゞェクトでは、 Avalonファミリヌを䜿甚したした。


コマンドは単なるデヌタストリヌム1ワヌドAvalon-Streaming (Avalon-ST)コマンドであるため、 Avalon-Streaming (Avalon-ST)暙準Avalon-Streaming (Avalon-ST)  data 、 ready 、 valid信号を䜿甚したした。


これは、 readyLatency = 0  暙準から取埗 のずきのAvalon-ST䞊のトランザクションの様子です。



この図では、スレヌブが制埡するレディ信号がトランザクションずデヌタ転送をシャットダりンしおいるこずがわかりたす。


ダミヌハッシュ


このハッシュテヌブルをどのように䜜成するのか疑問に思ったずき、最も困難なこずは怜蚌するこずであるこずに気付きたした。 明らかに、最も興味深いニュアンスは、すでにデヌタがあるチェヌンに远加しようずしたり、長いチェヌンから削陀したりするずきに衚瀺されたす。


これは、システムぞの圱響が適甚されたずきに、事前定矩されたバスケット番号を䜿甚しおキヌ key を簡単に生成できる必芁があるこずを意味したす。


実際のハッシュ関数では、これには問題があるため、 HASH_TYPEパラメヌタヌの倀は"dummy hash"等しくHASH_TYPEたす。
このタむプのハッシュが遞択されおいる堎合、バスケット番号は単にキヌの䞊䜍BUCKET_WIDTHビットです。


したがっお、 key = 0x92123456で、 BUCKET_WIDTHが8の堎合、 bucket_num = 0x92です。
これにより、特定の境界ケヌスの生成に必芁なアクションを簡単に䜜成できたす。


シミュレヌションロギング


開発者は、 SignalTapたたはChipScopeを䜿甚しお、ハヌドりェア読み取り、ボヌドでRTLモゞュヌルを盎接デバッグする堎合がありたす 。 このアプロヌチは、垞に最速か぀最も生産的ではありたせん-プロゞェクト党䜓10分から数時間時には耇数回、手元のボヌド、デバッガヌ、入力アクションの生成などを再構築する必芁がありたす。


開発をスピヌドアップするために、 ModelSim 、VCS、Icarus Verilogなどの特別なシミュレヌタヌが䜿甚されたす。タむムチャヌトタむムフレヌムを構築するこずにより、デバッグ䞭にすべおのたたは遞択された信号/ 倉数の倀を远跡できたす。 これらのチャヌトをデバッグするには、膚倧な時間がかかる堎合がありたす。


解決策 
発生したすべおのアクションをログに蚘録し、目を通しおすぐに芋るこずができたす。


このために、 data_table_insert 、 data_table_delete 、 data_table_searchに、ログに出力する関数を远加したした。


 function void print( string s ); $display("%08t: %m: %s", $time, s); endfunction 

display圢匏はprintf䌌おいたす %d 、 %fなどを䜿甚できたす。



FSM遷移のロギング


 function void print_state_transition( ); string msg; if( next_state != state ) begin $sformat( msg, "%s -> %s", state, next_state ); print( msg ); end endfunction 

新しいタスクの受信を印刷したす。


 function string pdata2str( input ht_pdata_t pdata ); string s; $sformat( s, "opcode = %s key = 0x%x value = 0x%x head_ptr = 0x%x head_ptr_val = 0x%x", pdata.cmd.opcode, pdata.cmd.key, pdata.cmd.value, pdata.head_ptr, pdata.head_ptr_val ); return s; endfunction function void print_new_task( ht_pdata_t pdata ); print( pdata2str( pdata ) ); endfunction 

などなど...


シミュレヌションには、ModelSimを䜿甚したす。 画面に衚瀺されるログおよびデフォルトでは、 transcriptファむルにアクセスしたすには、次の行が衚瀺されたす。


 1465: top_tb.dut.d_tbl.sea_eng.g_s_eng[3].search.print: opcode = OP_SEARCH key = 0x04000000 value = 0x0000 head_ptr = 0x000 head_ptr_val = 0x0 1465: top_tb.dut.d_tbl.sea_eng.g_s_eng[3].search.print: IDLE_S -> NO_VALID_HEAD_PTR_S 1475: top_tb.dut.d_tbl.sea_eng.g_s_eng[3].search.print: RES: key = 0x04000000 value = 0x0000 rescode = SEARCH_NOT_SUCCESS_NO_ENTRY 1475: top_tb.dut.d_tbl.sea_eng.g_s_eng[3].search.print: NO_VALID_HEAD_PTR_S -> IDLE_S 1475: ht_tb.print: IN_MONITOR: key = 0x04000000 value = 0x0000 rescode = SEARCH_NOT_SUCCESS_NO_ENTRY 1485: top_tb.dut.d_tbl.del_eng.print: opcode = OP_DELETE key = 0x04111111 value = 0x0000 head_ptr = 0x000 head_ptr_val = 0x0 1485: top_tb.dut.d_tbl.del_eng.print: IDLE_S -> NO_VALID_HEAD_PTR_S 1495: top_tb.dut.d_tbl.del_eng.print: RES: key = 0x04111111 value = 0x0000 rescode = DELETE_NOT_SUCCESS_NO_ENTRY 1495: top_tb.dut.d_tbl.del_eng.print: NO_VALID_HEAD_PTR_S -> IDLE_S 1495: ht_tb.print: IN_MONITOR: key = 0x04111111 value = 0x0000 rescode = DELETE_NOT_SUCCESS_NO_ENTRY 1505: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x04000000 value = 0xb95f head_ptr = 0x000 head_ptr_val = 0x0 1505: top_tb.dut.d_tbl.ins_eng.print: IDLE_S -> NO_HEAD_PTR_WR_HEAD_PTR_S 1515: top_tb.dut.h_tbl.print: addr = 0x04 wr_data.ptr = 0x003 wr_data.ptr_val = 0x1 1515: top_tb.dut.d_tbl.ins_eng.print: NO_HEAD_PTR_WR_HEAD_PTR_S -> NO_HEAD_PTR_WR_DATA_S 1525: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x003 key = 0x04000000 value = 0xb95f next_ptr = 0x000, next_ptr_val = 0x0 1525: top_tb.dut.d_tbl.ins_eng.print: RES: key = 0x04000000 value = 0xb95f rescode = INSERT_SUCCESS 1525: top_tb.dut.d_tbl.ins_eng.print: NO_HEAD_PTR_WR_DATA_S -> IDLE_S 

このようなテキストログは、grepや怜玢の実行が簡単ですたずえば、vim'eで。


ログは時間を倧幅に節玄したした。簡単な䟋を送信するずき、どの順序で曞かれるべきかを知っおいお、ログを芋るだけでした。 ゚ラヌが発生した堎合、タむミング図ではなくコヌドを分析したした。


チャレンゞずしお、タむムアりトなしで1週間以内にRTLコヌドをデバッグするこずをお勧めしたす快適ゟヌンを離れる。


怜蚌


System Verilog for Verificationなどの優れた文献に目を向けるず、優れた正しいテストベンチずしお次の図がありたす。



この蚘事では、Chris Spearからパンを取り䞊げる぀もりはないので、これらすべおのコンポヌネントの意味に぀いおは詳しく説明したせん。


私のテストベンチのスキヌム



top_tb


トップモゞュヌル。


DUTデバむス/テスト察象蚭蚈


テスト察象は、 hash_table_topモゞュヌルのむンスタンスです。


ht_driver



このメヌルボックスにコマンドを配眮するには、階局アクセスが䜿甚されたす。


 task send_to_dut_c( input ht_command_t c ); // using hierarchial access to put command in mailbox env.drv.gen2drv.put( c ); endtask 

テスト


パフォヌマンスをテストするために、3぀のテスト/入力アクションが䜜成されたした。


䜜業を簡玠化するマクロ


 `define CMD( _OP, _KEY, _VALUE ) cmds.push_back( '{ opcode : _OP, key : _KEY, value : _VALUE } ); `define CMD_SEARCH( _KEY ) `CMD( OP_SEARCH, _KEY, 0 ) `define CMD_INSERT( _KEY, _VALUE ) `CMD( OP_INSERT, _KEY, _VALUE ) `define CMD_INSERT_RAND( _KEY ) `CMD_INSERT( _KEY, $urandom() ) `define CMD_DELETE( _KEY ) `CMD( OP_DELETE, _KEY, 0 ) 

テスト䟋


 task test_01( ); ht_command_t cmds[$]; $display("%m:"); `CMD_INSERT( 32'h01_00_00_00, 16'h1234 ) `CMD_INSERT( 32'h01_00_10_00, 16'h1235 ) `CMD_INSERT_RAND( 32'h01_00_00_00 ) `CMD_INSERT_RAND( 32'h01_00_00_01 ) `CMD_DELETE ( 32'h01_00_00_00 ) `CMD_INSERT_RAND( 32'h01_00_00_02 ) `CMD_SEARCH( 32'h01_00_00_00 ) `CMD_SEARCH( 32'h01_00_00_01 ) `CMD_SEARCH( 32'h01_00_00_01 ) `CMD_SEARCH( 32'h01_00_00_03 ) foreach( cmds[i] ) begin send_to_dut_c( cmds[i] ); end endtask 

ht_monitor



ht_scoreboard


DUTの正しい動䜜を確認したす。


次のものが含たれたす。



操䜜アルゎリズム



カバレッゞ


そのため、さたざたな入力の圱響を送信したり、DUTの反応の正確さを分析したりできるシステム必芁に応じお、フレヌムワヌクが既にありたす。 バグがないこずを確認するには、「可胜なすべおの」オプションをカバヌする必芁がありたす。


SystemVerilogは、カバレッゞを評䟡するために、covergroupおよびcoverpointオブゞェクトを導入したした 。 圌らの助けを借りお、サンプリングしたいポむントず、収集する統蚈を説明できたす。


 covergroup cg(); option.per_instance = 1; CMDOP: coverpoint result_locked.cmd.opcode; CMDRES: coverpoint result_locked.rescode; BUCKOCUP: coverpoint bucket_occup[ result_locked.bucket ] { bins zero = { 0 }; bins one = { 1 }; bins two = { 2 }; bins three = { 3 }; bins four = { 4 }; bins other = { [5:$] }; } CMDOP_BUCKOCUP: cross CMDOP, BUCKOCUP; CMDRES_BUCKOCUP: cross CMDRES, BUCKOCUP { // we should ignore SEARCH_FOUND, INSERT_SUCCESS_SAME_KEY, DELETE_SUCCESS // when in bucket was zero elements, because it's not real situation ignore_bins not_real = binsof( CMDRES ) intersect{ SEARCH_FOUND, INSERT_SUCCESS_SAME_KEY, DELETE_SUCCESS } && binsof( BUCKOCUP ) intersect{ 0 }; } endgroup 

説明



シミュレヌションの終了埌、ModelSimコン゜ヌルでレポヌトを取埗できたす。


 coverage save 1.ucdb vcover report 1.ucdb -verbose -cvg 

報告曞


 COVERGROUP COVERAGE: ---------------------------------------------------------------------------------------------------- Covergroup Metric Goal/ Status At Least ---------------------------------------------------------------------------------------------------- TYPE /top_tb/dut/resm/cg 94.0% 100 Uncovered Coverpoint cg::CMDOP 100.0% 100 Covered Coverpoint cg::CMDRES 85.7% 100 Uncovered Coverpoint cg::BUCKOCUP 100.0% 100 Covered Cross cg::CMDOP_BUCKOCUP 100.0% 100 Covered Cross cg::CMDRES_BUCKOCUP 84.6% 100 Uncovered Covergroup instance \/top_tb/dut/resm/cg1 94.0% 100 Uncovered Coverpoint CMDOP 100.0% 100 Covered covered/total bins: 3 3 missing/total bins: 0 3 bin auto[OP_SEARCH] 21 1 Covered bin auto[OP_INSERT] 21 1 Covered bin auto[OP_DELETE] 18 1 Covered Coverpoint CMDRES 85.7% 100 Uncovered covered/total bins: 6 7 missing/total bins: 1 7 bin auto[SEARCH_FOUND] 12 1 Covered bin auto[SEARCH_NOT_SUCCESS_NO_ENTRY] 9 1 Covered bin auto[INSERT_SUCCESS] 14 1 Covered bin auto[INSERT_SUCCESS_SAME_KEY] 7 1 Covered bin auto[INSERT_NOT_SUCCESS_TABLE_IS_FULL] 0 1 ZERO bin auto[DELETE_SUCCESS] 11 1 Covered bin auto[DELETE_NOT_SUCCESS_NO_ENTRY] 7 1 Covered Coverpoint BUCKOCUP 100.0% 100 Covered covered/total bins: 6 6 missing/total bins: 0 6 bin zero 7 1 Covered bin one 13 1 Covered bin two 9 1 Covered bin three 12 1 Covered bin four 8 1 Covered bin other 11 1 Covered Cross CMDOP_BUCKOCUP 100.0% 100 Covered covered/total bins: 18 18 missing/total bins: 0 18 bin <auto[OP_SEARCH],zero> 1 1 Covered bin <auto[OP_INSERT],zero> 5 1 Covered bin <auto[OP_SEARCH],one> 5 1 Covered ... Cross CMDRES_BUCKOCUP 84.6% 100 Uncovered covered/total bins: 33 39 missing/total bins: 6 39 bin <auto[SEARCH_NOT_SUCCESS_NO_ENTRY],zero> 1 1 Covered bin <auto[INSERT_SUCCESS],zero> 5 1 Covered bin <auto[DELETE_NOT_SUCCESS_NO_ENTRY],zero> 1 1 Covered ... 

すべおの可胜な亀差点は自動的に受信されたした-私たちは䜙分なこずを曞きたせんでした。


3぀のテストの埌、次のこずが明らかになりたす。



最埌に



バグは䜕でしたか


この効果を適甚するず、次のこずがわかりたした。


 `CMD_INSERT_RAND( 32'h05_00_00_00 ) `CMD_INSERT_RAND( 32'h05_00_00_01 ) `CMD_DELETE ( 32'h05_00_00_01 ) `CMD_INSERT_RAND( 32'h05_00_00_02 ) `CMD_INSERT_RAND( 32'h05_00_00_03 ) 

これにより、キヌ0x05000003挿入されるず、 0x05000003モゞュヌルが「ハング」するずいう事実に぀ながりたす。




 クリックしお拡倧 


ログに次のメッセヌゞが衚瀺されたした。


 385: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x05000003 value = 0x7e7e head_ptr = 0x000 head_ptr_val = 0x1 385: top_tb.dut.d_tbl.ins_eng.print: IDLE_S -> READ_HEAD_S 415: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x001, next_ptr_val = 0x1 415: top_tb.dut.d_tbl.ins_eng.print: READ_HEAD_S -> GO_ON_CHAIN_S 445: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 475: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 505: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 535: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 ... 

少しログを巻き戻しお分析したす。 興味のある行 data_tableで読み曞きされる行のみを提䟛したした


  75: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x05000000 value = 0x1f62 head_ptr = 0x000 head_ptr_val = 0x0 95: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x000, next_ptr_val = 0x0 115: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x05000001 value = 0x3ff2 head_ptr = 0x000 head_ptr_val = 0x1 145: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x000, next_ptr_val = 0x0 155: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x001 key = 0x05000001 value = 0x3ff2 next_ptr = 0x000, next_ptr_val = 0x0 165: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x001, next_ptr_val = 0x1 185: top_tb.dut.d_tbl.del_eng.print: opcode = OP_DELETE key = 0x05000001 value = 0x0000 head_ptr = 0x000 head_ptr_val = 0x1 215: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x001, next_ptr_val = 0x1 245: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x001 key = 0x05000001 value = 0x3ff2 next_ptr = 0x000, next_ptr_val = 0x0 255: top_tb.dut.d_tbl.del_eng.print: WR: addr = 0x001 key = 0x05000001 value = 0x3ff2 next_ptr = 0x000, next_ptr_val = 0x0 265: top_tb.dut.d_tbl.del_eng.print: WR: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x000, next_ptr_val = 0x0 285: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x05000002 value = 0x5429 head_ptr = 0x000 head_ptr_val = 0x1 315: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x001, next_ptr_val = 0x1 345: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x000, next_ptr_val = 0x0 355: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x001 key = 0x05000002 value = 0x5429 next_ptr = 0x000, next_ptr_val = 0x0 365: top_tb.dut.d_tbl.ins_eng.print: WR: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 385: top_tb.dut.d_tbl.ins_eng.print: opcode = OP_INSERT key = 0x05000003 value = 0x7e7e head_ptr = 0x000 head_ptr_val = 0x1 415: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x000 key = 0x05000000 value = 0x1f62 next_ptr = 0x001, next_ptr_val = 0x1 445: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 475: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 505: top_tb.dut.d_tbl.ins_eng.print: RD: addr = 0x001 key = 0x00000000 value = 0x0000 next_ptr = 0x001, next_ptr_val = 0x1 

255〜265 nsの奇劙なむベントが発生した瞬間に気付くかもしれたせん。最初に、 addr = 0x001が1぀の倀を蚘録し、次に別の倀を蚘録したした。


これにより、 data_tableに誀ったデヌタが含たれるずいう事実に぀ながりたす。



キヌ0x05000002を远加するず、興味深い状況が発生したす。1぀のセル0x001で蚘録が2回発生したす。



キヌ0x05000003を远加しようずするず、チェヌンを通過するずきにルヌプが発生したす。 445 nsからは、チェヌンに沿っお無限に実行し、同じアドレスを読み取りたす。


間違いは䜕でしたか


明らかに、デヌタを削陀したモゞュヌル data_table_delete によっお゚ラヌが発生したす。


255 nsの時点で、 next_ptr_valセルのnext_ptr_valフラグをれロに蚭定し、265 nsの時点で0x001に曞き蟌んですべおのれロを曞き蟌む必芁がありたした。 そのため、このモゞュヌルのコヌドで想定されおいたしたが、これは、芋おのずおり、起こりたせんでした。


事実、チェヌンの最埌から読み取るrd_addrずrd_dataを別々に保存するrd_addrあったため、䜜業したばかりの倀を䜿甚したした。


このような゚ラヌ1クロックサむクルによる䜙分な遅延、間違った時間に䞊曞きされたデヌタなどは、RTLコヌドでは非垞に䞀般的です。 圌らはあたり関心を衚しおいないので、この蚘事では特に説明したせん。


どのような間違いがあったか開発䞭


プロゞェクト管理は「䞍完党」です


事実は、私が想像したようにプロゞェクトを論理的な終わりに至らなかったずいうこずです。 なぜ-今は芚えおいない。


たずえば、 READMEでは、プロゞェクトはこのモゞュヌルがどこでどのように䜿甚され、テストされたかを瀺しおいたせん。


2぀のフレヌズを比范したす。


  1. このプロゞェクトは、 100Gスむッチの実皌働で䜿甚されたす。
  2. このプロゞェクトは数週間にわたっお楜しみのために曞かれたした ビヌル猶 トマトゞュヌス。

週末にこのモゞュヌルを曞いお、どこにも埋め蟌たなかったこずを明確に瀺しおいた堎合、サヌドパヌティの開発者は私のモゞュヌルを䜿甚せず、時間を節玄できなかったでしょうただし、バグがあるこずはわかりたせん 、この蚘事は読みたせん。


問題がコヌド内のどこにあるかをprev_rd_addrし始めたずき、問題がprev_rd_addr倉数にあるこずがわかったずき、動揺したした。 倀が割り圓おられるブロックは次のようになりたす。


 always_ff @( posedge clk_i or posedge rst_i ) if( rst_i ) prev_rd_addr <= '0; else if( rd_en_o ) //FIXME prev_rd_addr <= rd_addr; 

説明のないFIXMEは悪いです。 問題を説明するための䜙分な5分間は、将来的に垞に成果を䞊げたす。


* 結論 



Coverage


, , , :



:


ht_chain_state_t , , :


 typedef enum int unsigned { NO_CHAIN, IN_HEAD, IN_MIDDLE, IN_TAIL, IN_TAIL_NO_MATCH } ht_chain_state_t; //    ht_result_t ... // only for verification ht_chain_state_t chain_state; } ht_result_t; 

. data_table_delete :


 ht_chain_state_t chain_state; always_ff @( posedge clk_i or posedge rst_i ) if( rst_i ) chain_state <= NO_CHAIN; else if( state != next_state ) begin case( next_state ) NO_VALID_HEAD_PTR_S : chain_state <= NO_CHAIN; IN_TAIL_WITHOUT_MATCH_S : chain_state <= IN_TAIL_NO_MATCH; KEY_MATCH_IN_HEAD_S : chain_state <= IN_HEAD; KEY_MATCH_IN_MIDDLE_S : chain_state <= IN_MIDDLE; KEY_MATCH_IN_TAIL_S : chain_state <= IN_TAIL; // no default: just keep old value endcase end //   ,    ```ht_res_monitor``` assign result_o.chain_state = chain_state; 

ht_res_monitor :


 //   ht_result_t result_history [HISTORY_DELAY:1]; always_ff @( posedge clk_i ) begin if( result_locked_val ) begin result_history[1] <= result_locked; for( int i = 2; i <= HISTORY_DELAY; i++ ) begin result_history[i] <= result_history[i-1]; end end end // 1   ,   (bucket)    logic [HISTORY_DELAY:1] bucket_hist_mask; always_comb begin for( int i = 1; i <= HISTORY_DELAY; i++ ) bucket_hist_mask[i] = ( result_history[i].bucket == result_locked.bucket ); end 

covergroup:


 ... CMDOP_D1: coverpoint result_history[1].cmd.opcode; CMDOP_D2: coverpoint result_history[2].cmd.opcode; CMDRES_D1: coverpoint result_history[1].rescode; CMDRES_D2: coverpoint result_history[2].rescode; CHAIN: coverpoint result_locked.chain_state; BUCK_HIST_MASK: coverpoint bucket_hist_mask; CMDOP_HISTORY_D2: cross CMDOP_D2, CMDOP_D1, CMDOP, BUCK_HIST_MASK; CMDRES_HISTORY_D2: cross CMDRES_D2, CMDRES_D1, CMDRES, BUCK_HIST_MASK { ignore_bins not_check_now = binsof( CMDRES ) intersect{ INSERT_NOT_SUCCESS_TABLE_IS_FULL } || binsof( CMDRES_D1 ) intersect{ INSERT_NOT_SUCCESS_TABLE_IS_FULL } || binsof( CMDRES_D2 ) intersect{ INSERT_NOT_SUCCESS_TABLE_IS_FULL }; } CMDOP_CHAIN: cross CMDOP, CHAIN { ignore_bins insert_in_middle = binsof( CMDOP ) intersect { OP_INSERT } && binsof( CHAIN ) intersect { IN_MIDDLE }; ignore_bins insert_in_tail_no_match = binsof( CMDOP ) intersect { OP_INSERT } && binsof( CHAIN ) intersect { IN_TAIL_NO_MATCH }; } 

, bin CMDOP_HISTORY_D2 :


 bin <auto[OP_DELETE],auto[OP_SEARCH],auto[OP_SEARCH],auto['b10]> 

, :



. :


 Coverpoint cg::CMDOP 100.0% 100 Covered Coverpoint cg::CMDRES 85.7% 100 Uncovered Coverpoint cg::CMDOP_D1 100.0% 100 Covered Coverpoint cg::CMDOP_D2 100.0% 100 Covered Coverpoint cg::CMDRES_D1 85.7% 100 Uncovered Coverpoint cg::CMDRES_D2 85.7% 100 Uncovered Coverpoint cg::CHAIN 100.0% 100 Covered Coverpoint cg::BUCK_HIST_MASK 100.0% 100 Covered Coverpoint cg::BUCKOCUP 100.0% 100 Covered Cross cg::CMDOP_BUCKOCUP 100.0% 100 Covered Cross cg::CMDRES_BUCKOCUP 84.6% 100 Uncovered Cross cg::CMDOP_HISTORY_D2 18.5% 100 Uncovered covered/total bins: 20 108 missing/total bins: 88 108 Cross cg::CMDRES_HISTORY_D2 3.1% 100 Uncovered covered/total bins: 27 864 missing/total bins: 837 864 Cross cg::CMDOP_CHAIN 84.6% 100 Uncovered 

( , .. )


, HISTORY (18.5% 3.1%). , , .


, ?
. , , , , 100% .


— . , ( 5 6 ).


:




, .
. — . , : Constrained Random Verification .


, , - , - ( ) , , .


, :


 function bit [KEY_WIDTH-1:0] gen_rand_key( int min_bucket_num = 0, int max_bucket_num = ( 2**BUCKET_WIDTH - 1 ), int max_key_value = ( 2**( KEY_WIDTH - BUCKET_WIDTH ) - 1 ) ); bit [BUCKET_WIDTH-1:0] bucket_num; bit [KEY_WIDTH-1:0] gen_key; if( hash_table::HASH_TYPE != "dummy" ) begin $display("%m: hash_type = %s not supported here!", hash_table::HASH_TYPE ); $fatal(); end bucket_num = $urandom_range( max_bucket_num, min_bucket_num ); gen_key = $urandom_range( max_key_value, 0 ); // replace high bits by bucket_num (is needs in dummy hash) gen_key[ KEY_WIDTH - 1 : KEY_WIDTH - BUCKET_WIDTH ] = bucket_num; return gen_key; endfunction 

[0;15] [0;7] .


 // testing small amount of buckets with random commands task test_05( ); ht_command_t cmds[$]; $display("%m:"); for( int c = 0; c < 5000; c++ ) begin `CMD_SEARCH ( gen_rand_key( 0, 15, 7 ) ) `CMD_INSERT_RAND ( gen_rand_key( 0, 15, 7 ) ) `CMD_DELETE ( gen_rand_key( 0, 15, 7 ) ) end // ,    :) cmds.shuffle( ); foreach( cmds[i] ) begin send_to_dut_c( cmds[i] ); end endtask 

:


 Cross cg::CMDOP_HISTORY_D2 98.1% 100 Uncovered covered/total bins: 106 108 missing/total bins: 2 108 Cross cg::CMDRES_HISTORY_D2 81.1% 100 Uncovered covered/total bins: 701 864 missing/total bins: 163 864 

, bin' , , :


 // testing only one bucket with random commands task test_06( ); ht_command_t cmds[$]; $display("%m:"); for( int c = 0; c < 1000; c++ ) begin `CMD_SEARCH ( gen_rand_key( 0, 0, 7 ) ) `CMD_INSERT_RAND( gen_rand_key( 0, 0, 7 ) ) `CMD_DELETE ( gen_rand_key( 0, 0, 7 ) ) end cmds.shuffle( ); foreach( cmds[i] ) begin send_to_dut_c( cmds[i] ); end endtask 

結果


 Cross cg::CMDOP_HISTORY_D2 100.0% 100 Covered covered/total bins: 108 108 missing/total bins: 0 108 Cross cg::CMDRES_HISTORY_D2 99.1% 100 Uncovered covered/total bins: 857 864 missing/total bins: 7 864 

, ( , rescode=INSERT_NOT_SUCCESS_TABLE_IS_FULL )


泚 



:



""


, .


( ht_res ), "" , . . .


( ) — () .


:



, - .
( ) , /.


, :
head_table :



data_table :



empty_ptr_storage :



:



tables_monitor , head_table , data_table , empty_ptr .
, , . .


:


 ... 1195: top_tb.dut.d_tbl.del_eng.print: opcode = OP_DELETE key = 0x01000000 value = 0x0000 head_ptr = 0x001 head_ptr_val = 0x1 1195: top_tb.dut.d_tbl.del_eng.print: IDLE_S -> READ_HEAD_S 1225: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x001 key = 0x01001000 value = 0x1235 next_ptr = 0x002 next_ptr_val = 0x1 1225: top_tb.dut.d_tbl.del_eng.print: READ_HEAD_S -> GO_ON_CHAIN_S 1255: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x002 key = 0x01000001 value = 0x3ff2 next_ptr = 0x000 next_ptr_val = 0x1 1285: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x000 key = 0x01000002 value = 0x5429 next_ptr = 0x004 next_ptr_val = 0x1 1315: top_tb.dut.d_tbl.del_eng.print: RD: addr = 0x004 key = 0x01000000 value = 0x1cc0 next_ptr = 0x000 next_ptr_val = 0x0 1315: top_tb.dut.d_tbl.del_eng.print: GO_ON_CHAIN_S -> KEY_MATCH_IN_TAIL_S 1325: top_tb.dut.d_tbl.del_eng.print: WR: addr = 0x004 key = 0x01000000 value = 0x1cc0 next_ptr = 0x000 next_ptr_val = 0x0 1325: top_tb.dut.d_tbl.del_eng.print: KEY_MATCH_IN_TAIL_S -> CLEAR_RAM_AND_PTR_S 1335: top_tb.dut.d_tbl.del_eng.print: WR: addr = 0x004 key = 0x00000000 value = 0x0000 next_ptr = 0x000 next_ptr_val = 0x0 1335: top_tb.dut.d_tbl.del_eng.print: RES: key = 0x01000000 value = 0x0000 rescode = DELETE_SUCCESS chain_state = IN_TAIL 1335: top_tb.dut.d_tbl.del_eng.print: CLEAR_RAM_AND_PTR_S -> IDLE_S 1335: ht_tb.print: IN_MONITOR: key = 0x01000000 value = 0x0000 rescode = DELETE_SUCCESS chain_state = IN_TAIL 1335: top_tb.dut.d_tbl.empty_ptr_storage.print: add_empty_ptr: 0x004 ** Error: ERROR: addr = 0x004. This addr is empty, but ptr is val Time: 1340 ns Scope: top_tb.tm.check_one_addr File: ../tb/tables_monitor.sv Line: 119 ** Error: ERROR: addr = 0x004 key=0x00000000 don't match bucket_num = 0x01 Time: 1340 ns Scope: top_tb.tm.check_one_addr File: ../tb/tables_monitor.sv Line: 127 

, ( )!


:



:



おわりに


, , . ( )

IP- ASIC/FPGA -.


:



, . , , .


, RTL-, .


, RTL- :)


ご枅聎ありがずうございたした .


PS
.
, :




Source: https://habr.com/ru/post/J277313/


All Articles