@@ -504,3 +504,115 @@ async def test_fld_faddd_load_use_hazard(dut: Any) -> None:
504504 await drain_pipeline (dut_if , state , use_fp_monitor = True )
505505
506506 cocotb .log .info ("=== PASSED: FLD -> FADD.D load-use hazard ===" )
507+
508+
509+ @cocotb .test ()
510+ async def test_lh_bext_load_use_hazard (dut : Any ) -> None :
511+ """Test LH followed immediately by BEXT after an unrelated stall.
512+
513+ This reproduces a stale load-hit forwarding corner case:
514+ 1. Warm cache with LH from address A (value has bit1=1)
515+ 2. Trigger a multi-cycle DIV stall
516+ 3. Execute LH from uncached address B (value bit1=0)
517+ 4. Immediately consume with BEXT x3, x27, x15
518+
519+ Expected result is x3=0. Any stale forwarding from address A produces x3=1.
520+ """
521+ cocotb .log .info ("=== Test: LH -> BEXT integer load-use hazard ===" )
522+
523+ dut_if , state , mem_model = await setup_test (dut , use_fp_monitor = False )
524+
525+ from encoders .op_tables import I_ALU , LOADS
526+
527+ enc_addi , _ = I_ALU ["addi" ]
528+ enc_lh , _ = LOADS ["lh" ]
529+ enc_bext , eval_bext = R_ALU ["bext" ]
530+ enc_div , eval_div = R_ALU ["div" ]
531+
532+ # Address A (cache warmup, bit1=1) and address B (expected miss, bit1=0)
533+ addr_a = 0x120
534+ addr_b = 0x220
535+ value_a = 0x00000002
536+ value_b = 0x00000000
537+ bit_index = 1
538+
539+ dut .data_memory_for_simulation .memory [addr_a >> 2 ].value = value_a
540+ dut .data_memory_for_simulation .memory [addr_b >> 2 ].value = value_b
541+ mem_model .write_word (addr_a , value_a )
542+ mem_model .write_word (addr_b , value_b )
543+
544+ cocotb .log .info (
545+ f"Memory init: A=0x{ addr_a :08X} ->0x{ value_a :08X} , "
546+ f"B=0x{ addr_b :08X} ->0x{ value_b :08X} "
547+ )
548+
549+ # Setup integer operands/registers.
550+ await execute_instruction (
551+ dut_if ,
552+ state ,
553+ enc_addi (10 , 0 , addr_a ),
554+ 10 ,
555+ addr_a ,
556+ f"ADDI x10, x0, 0x{ addr_a :X} " ,
557+ first_after_warmup = True ,
558+ )
559+ await execute_instruction (
560+ dut_if ,
561+ state ,
562+ enc_addi (11 , 0 , addr_b ),
563+ 11 ,
564+ addr_b ,
565+ f"ADDI x11, x0, 0x{ addr_b :X} " ,
566+ )
567+ await execute_instruction (
568+ dut_if , state , enc_addi (15 , 0 , bit_index ), 15 , bit_index , "ADDI x15, x0, 1"
569+ )
570+ await execute_instruction (
571+ dut_if , state , enc_addi (7 , 0 , 100 ), 7 , 100 , "ADDI x7, x0, 100"
572+ )
573+ await execute_instruction (dut_if , state , enc_addi (8 , 0 , 3 ), 8 , 3 , "ADDI x8, x0, 3" )
574+
575+ # Warm cache entry for address A (first load may miss and fill).
576+ await execute_instruction (
577+ dut_if , state , enc_lh (5 , 10 , 0 ), 5 , 0x00000002 , "LH x5, 0(x10)"
578+ )
579+ # Second load from same address should be served via cache-hit path.
580+ await execute_instruction (
581+ dut_if , state , enc_lh (6 , 10 , 0 ), 6 , 0x00000002 , "LH x6, 0(x10)"
582+ )
583+
584+ # Insert unrelated multi-cycle stall to stress stale forwarding state.
585+ div_expected = eval_div (100 , 3 )
586+ await execute_instruction (
587+ dut_if , state , enc_div (9 , 7 , 8 ), 9 , div_expected , "DIV x9, x7, x8 (100/3)"
588+ )
589+
590+ # Critical pair: load from B then immediate dependent BEXT.
591+ await execute_instruction (
592+ dut_if , state , enc_lh (27 , 11 , 0 ), 27 , 0x00000000 , "LH x27, 0(x11)"
593+ )
594+ bext_expected = eval_bext (0x00000000 , bit_index )
595+ await execute_instruction (
596+ dut_if ,
597+ state ,
598+ enc_bext (3 , 27 , 15 ),
599+ 3 ,
600+ bext_expected ,
601+ "BEXT x3, x27, x15" ,
602+ )
603+
604+ await drain_pipeline (dut_if , state , use_fp_monitor = False )
605+
606+ x27_hw = dut_if .read_register (27 )
607+ x3_hw = dut_if .read_register (3 )
608+ cocotb .log .info (f"Final x27 = 0x{ x27_hw :08X} (expected 0x00000000)" )
609+ cocotb .log .info (f"Final x3 = 0x{ x3_hw :08X} (expected 0x{ bext_expected :08X} )" )
610+
611+ assert (
612+ x27_hw == 0x00000000
613+ ), f"x27 mismatch: got 0x{ x27_hw :08X} , expected 0x00000000"
614+ assert (
615+ x3_hw == bext_expected
616+ ), f"x3 mismatch: got 0x{ x3_hw :08X} , expected 0x{ bext_expected :08X} "
617+
618+ cocotb .log .info ("=== PASSED: LH -> BEXT integer load-use hazard ===" )
0 commit comments