forked from libre-chip/cpu
		
	add formal proof for unit_free_regs_tracker
This commit is contained in:
		
							parent
							
								
									cb5855589f
								
							
						
					
					
						commit
						aaa2cb193e
					
				
					 2 changed files with 342 additions and 17 deletions
				
			
		
							
								
								
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -279,7 +279,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" | |||
| [[package]] | ||||
| name = "fayalite" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3d0f95cfe5a910f79177babf8fe12483f83e96f9" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "blake3", | ||||
|  | @ -300,7 +300,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-proc-macros" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3d0f95cfe5a910f79177babf8fe12483f83e96f9" | ||||
| dependencies = [ | ||||
|  "fayalite-proc-macros-impl", | ||||
| ] | ||||
|  | @ -308,7 +308,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-proc-macros-impl" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3d0f95cfe5a910f79177babf8fe12483f83e96f9" | ||||
| dependencies = [ | ||||
|  "base16ct", | ||||
|  "num-bigint", | ||||
|  | @ -323,7 +323,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-visit-gen" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3d0f95cfe5a910f79177babf8fe12483f83e96f9" | ||||
| dependencies = [ | ||||
|  "indexmap", | ||||
|  "prettyplease", | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::util::tree_reduce::tree_reduce; | ||||
| use fayalite::{prelude::*, util::ready_valid::ReadyValid}; | ||||
| use fayalite::{module::wire_with_loc, prelude::*, util::ready_valid::ReadyValid}; | ||||
| use std::{num::NonZeroUsize, ops::Range}; | ||||
| 
 | ||||
| #[hdl_module] | ||||
|  | @ -17,7 +17,7 @@ pub fn unit_free_regs_tracker( | |||
|         m.input(Array[ReadyValid[UInt[reg_num_width]]][free_at_once.get()]); | ||||
|     #[hdl] | ||||
|     let alloc_out: Array<ReadyValid<UInt>> = | ||||
|         m.input(Array[ReadyValid[UInt[reg_num_width]]][alloc_at_once.get()]); | ||||
|         m.output(Array[ReadyValid[UInt[reg_num_width]]][alloc_at_once.get()]); | ||||
|     assert!( | ||||
|         reg_num_width <= isize::MAX.ilog2() as usize, | ||||
|         "too many registers" | ||||
|  | @ -47,7 +47,7 @@ pub fn unit_free_regs_tracker( | |||
|     }) = tree_reduce( | ||||
|         (0..reg_count).map(|index| Summary { | ||||
|             range: index..index + 1, | ||||
|             count: allocated_reg[index] | ||||
|             count: (!allocated_reg[index]) | ||||
|                 .cast_to_static::<UInt<1>>() | ||||
|                 .cast_to(count_ty), | ||||
|             count_overflowed: false.to_expr(), | ||||
|  | @ -55,25 +55,35 @@ pub fn unit_free_regs_tracker( | |||
|         }), | ||||
|         |l, r| { | ||||
|             let range = l.range.start..r.range.end; | ||||
|             #[hdl] | ||||
|             let reduced_count = wire(count_ty); | ||||
|             let reduced_count = wire_with_loc( | ||||
|                 &format!("reduced_count_{}_{}", range.start, range.end), | ||||
|                 SourceLocation::caller(), | ||||
|                 count_ty, | ||||
|             ); | ||||
|             connect_any(reduced_count, l.count + r.count); | ||||
|             #[hdl] | ||||
|             let reduced_count_overflowed = wire(); | ||||
|             let reduced_count_overflowed = wire_with_loc( | ||||
|                 &format!("reduced_count_overflowed_{}_{}", range.start, range.end), | ||||
|                 SourceLocation::caller(), | ||||
|                 Bool, | ||||
|             ); | ||||
|             connect( | ||||
|                 reduced_count_overflowed, | ||||
|                 (l.count + r.count).cmp_ne(reduced_count) | l.count_overflowed | r.count_overflowed, | ||||
|             ); | ||||
|             #[hdl] | ||||
|             let reduced_alloc_nums = wire( | ||||
|             let reduced_alloc_nums = wire_with_loc( | ||||
|                 &format!("reduced_alloc_nums_{}_{}", range.start, range.end), | ||||
|                 SourceLocation::caller(), | ||||
|                 Array[UInt[Expr::ty(l.alloc_nums).element().width() + 1]][alloc_at_once.get()], | ||||
|             ); | ||||
|             for alloc_index in 0..alloc_at_once.get() { | ||||
|                 #[hdl] | ||||
|                 if l.count_overflowed | l.count.cmp_ge(alloc_at_once) { | ||||
|                 if l.count_overflowed | l.count.cmp_gt(alloc_index) { | ||||
|                     connect(reduced_alloc_nums[alloc_index], l.alloc_nums[alloc_index]); | ||||
|                 } else { | ||||
|                     connect(reduced_alloc_nums[alloc_index], r.alloc_nums[alloc_index]); | ||||
|                     connect( | ||||
|                         reduced_alloc_nums[alloc_index], | ||||
|                         r.alloc_nums[alloc_index.to_expr() - l.count] + l.range.len(), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|             Summary { | ||||
|  | @ -93,7 +103,7 @@ pub fn unit_free_regs_tracker( | |||
|             connect(allocated_reg[alloc_num], true); | ||||
|         } | ||||
|         #[hdl] | ||||
|         if count_overflowed | count.cmp_ge(alloc_index) { | ||||
|         if count_overflowed | count.cmp_gt(alloc_index) { | ||||
|             connect( | ||||
|                 alloc_out[alloc_index].data, | ||||
|                 HdlSome(alloc_nums[alloc_index]), | ||||
|  | @ -107,4 +117,319 @@ pub fn unit_free_regs_tracker( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO: add formal proof of unit_free_regs_tracker
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use fayalite::{ | ||||
|         cli::FormalMode, firrtl::ExportOptions, | ||||
|         module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal, | ||||
|     }; | ||||
|     use std::num::NonZero; | ||||
| 
 | ||||
|     fn test_unit_free_regs_tracker( | ||||
|         free_at_once: NonZeroUsize, | ||||
|         alloc_at_once: NonZeroUsize, | ||||
|         reg_num_width: usize, | ||||
|     ) { | ||||
|         // enough time to completely allocate all regs, free whatever we please,
 | ||||
|         // allocate one, then completely free the rest
 | ||||
|         let history_len = (3 + 2 * (1 << reg_num_width)).max(5); | ||||
|         assert_formal( | ||||
|             format_args!( | ||||
|                 "test_unit_free_regs_tracker_{free_at_once}_{alloc_at_once}_{reg_num_width}" | ||||
|             ), | ||||
|             unit_free_regs_tracker_test(free_at_once, alloc_at_once, reg_num_width, history_len), | ||||
|             FormalMode::Prove, | ||||
|             history_len as u64 + 2, | ||||
|             None, | ||||
|             ExportOptions { | ||||
|                 simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), | ||||
|                 ..ExportOptions::default() | ||||
|             }, | ||||
|         ); | ||||
|         #[hdl_module] | ||||
|         fn unit_free_regs_tracker_test( | ||||
|             free_at_once: NonZeroUsize, | ||||
|             alloc_at_once: NonZeroUsize, | ||||
|             reg_num_width: usize, | ||||
|             history_len: usize, | ||||
|         ) { | ||||
|             #[hdl] | ||||
|             let clk: Clock = m.input(); | ||||
|             #[hdl] | ||||
|             let cd = wire(); | ||||
|             connect( | ||||
|                 cd, | ||||
|                 #[hdl] | ||||
|                 ClockDomain { | ||||
|                     clk, | ||||
|                     rst: formal_reset().to_reset(), | ||||
|                 }, | ||||
|             ); | ||||
|             #[hdl] | ||||
|             let dut = instance(unit_free_regs_tracker( | ||||
|                 free_at_once, | ||||
|                 alloc_at_once, | ||||
|                 reg_num_width, | ||||
|             )); | ||||
|             connect(dut.cd, cd); | ||||
|             let reg_count = 1 << reg_num_width; | ||||
|             let reg_num_ty = UInt[reg_num_width]; | ||||
|             #[hdl] | ||||
|             let free_reg = reg_builder().clock_domain(cd).reset(vec![true; reg_count]); | ||||
|             for free_index in 0..free_at_once.get() { | ||||
|                 #[hdl] | ||||
|                 if any_seq(Bool) { | ||||
|                     let data = any_seq(reg_num_ty); | ||||
|                     connect(dut.free_in[free_index].data, HdlSome(data)); | ||||
|                 } else { | ||||
|                     connect( | ||||
|                         dut.free_in[free_index].data, | ||||
|                         HdlOption[reg_num_ty].HdlNone(), | ||||
|                     ); | ||||
|                 } | ||||
|                 #[hdl] | ||||
|                 if let HdlSome(free) = ReadyValid::firing_data(dut.free_in[free_index]) { | ||||
|                     hdl_assume(clk, !free_reg[free], ""); | ||||
|                     connect(free_reg[free], true); | ||||
|                 } | ||||
|             } | ||||
|             #[hdl] | ||||
|             let free_before_alloc_array = wire(Array[Expr::ty(free_reg)][alloc_at_once.get() + 1]); | ||||
|             connect(free_before_alloc_array[0], free_reg); | ||||
|             #[hdl] | ||||
|             let expected_alloc = wire(Array[HdlOption[reg_num_ty]][alloc_at_once.get()]); | ||||
|             for alloc_index in 0..alloc_at_once.get() { | ||||
|                 let free_before_alloc = free_before_alloc_array[alloc_index]; | ||||
|                 let free_after_alloc = free_before_alloc_array[alloc_index + 1]; | ||||
|                 connect(free_after_alloc, free_before_alloc); | ||||
|                 connect(dut.alloc_out[alloc_index].ready, any_seq(Bool)); | ||||
|                 connect(expected_alloc[alloc_index], HdlOption[reg_num_ty].HdlNone()); | ||||
|                 for reg_index in (0..reg_count).rev() { | ||||
|                     #[hdl] | ||||
|                     if free_before_alloc[reg_index] { | ||||
|                         connect( | ||||
|                             expected_alloc[alloc_index], | ||||
|                             HdlSome(reg_index.cast_to(reg_num_ty)), | ||||
|                         ); | ||||
|                     } | ||||
|                     #[hdl] | ||||
|                     if let HdlSome(alloc) = expected_alloc[alloc_index] { | ||||
|                         #[hdl] | ||||
|                         if alloc.cmp_eq(reg_index) { | ||||
|                             connect(free_after_alloc[reg_index], false); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 #[hdl] | ||||
|                 if let HdlSome(expected_alloc) = expected_alloc[alloc_index] { | ||||
|                     #[hdl] | ||||
|                     if let HdlSome(alloc) = dut.alloc_out[alloc_index].data { | ||||
|                         hdl_assert(clk, expected_alloc.cmp_eq(alloc), ""); | ||||
|                     } else { | ||||
|                         hdl_assert(clk, false.to_expr(), ""); | ||||
|                     } | ||||
|                 } else { | ||||
|                     hdl_assert(clk, HdlOption::is_none(dut.alloc_out[alloc_index].data), ""); | ||||
|                 } | ||||
|                 #[hdl] | ||||
|                 if let HdlSome(alloc) = ReadyValid::firing_data(dut.alloc_out[alloc_index]) { | ||||
|                     connect(free_reg[alloc], false); | ||||
|                 } | ||||
|             } | ||||
|             #[hdl] | ||||
|             let free_fired_history_reg = reg_builder() | ||||
|                 .clock_domain(cd) | ||||
|                 .reset(repeat(!UInt[history_len].zero(), free_at_once.get())); | ||||
|             for free_index in 0..free_at_once.get() { | ||||
|                 connect_any( | ||||
|                     free_fired_history_reg[free_index], | ||||
|                     (free_fired_history_reg[free_index] << 1) | ||||
|                         | ReadyValid::firing(dut.free_in[free_index]).cast_to(UInt[1]), | ||||
|                 ); | ||||
|                 hdl_assume(clk, free_fired_history_reg[free_index].any_one_bits(), ""); | ||||
|             } | ||||
|             #[hdl] | ||||
|             let alloc_fired_history_reg = reg_builder() | ||||
|                 .clock_domain(cd) | ||||
|                 .reset(repeat(!UInt[history_len].zero(), alloc_at_once.get())); | ||||
|             for alloc_index in 0..alloc_at_once.get() { | ||||
|                 connect_any( | ||||
|                     alloc_fired_history_reg[alloc_index], | ||||
|                     (alloc_fired_history_reg[alloc_index] << 1) | ||||
|                         | ReadyValid::firing(dut.alloc_out[alloc_index]).cast_to(UInt[1]), | ||||
|                 ); | ||||
|                 if alloc_index < reg_count { | ||||
|                     hdl_assume(clk, alloc_fired_history_reg[alloc_index].any_one_bits(), ""); | ||||
|                 } | ||||
|             } | ||||
|             #[hdl] | ||||
|             let any_free_history_reg = reg_builder() | ||||
|                 .clock_domain(cd) | ||||
|                 .reset(UInt[history_len].zero()); | ||||
|             #[hdl] | ||||
|             let any_allocated_history_reg = reg_builder() | ||||
|                 .clock_domain(cd) | ||||
|                 .reset(UInt[history_len].zero()); | ||||
|             #[hdl] | ||||
|             let any_free = wire(); | ||||
|             connect(any_free, false); | ||||
|             #[hdl] | ||||
|             let any_allocated = wire(); | ||||
|             connect(any_allocated, false); | ||||
|             for reg_index in 0..reg_count { | ||||
|                 #[hdl] | ||||
|                 if free_reg[reg_index] { | ||||
|                     connect(any_free, true); | ||||
|                 } else { | ||||
|                     connect(any_allocated, true); | ||||
|                 } | ||||
|             } | ||||
|             connect_any( | ||||
|                 any_free_history_reg, | ||||
|                 (any_free_history_reg << 1) | any_free.cast_to(UInt[1]), | ||||
|             ); | ||||
|             connect_any( | ||||
|                 any_allocated_history_reg, | ||||
|                 (any_allocated_history_reg << 1) | any_allocated.cast_to(UInt[1]), | ||||
|             ); | ||||
|             hdl_assume(clk, any_free_history_reg.any_zero_bits(), ""); | ||||
|             hdl_assume(clk, any_allocated_history_reg.any_zero_bits(), ""); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_1_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(1).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_1_1() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(1).unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_1_2() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(1).unwrap(), 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_1_3() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(1).unwrap(), 3); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_1_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(1).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_2_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(2).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_2_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(2).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_3_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(3).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_3_1() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(3).unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_3_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(3).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_4_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(4).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_4_1() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(4).unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_4_2() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(4).unwrap(), 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_1_4_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(1).unwrap(), NonZero::new(4).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_2_1_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(2).unwrap(), NonZero::new(1).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_2_2_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(2).unwrap(), NonZero::new(2).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_2_2_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(2).unwrap(), NonZero::new(2).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_3_1_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(3).unwrap(), NonZero::new(1).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_3_3_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(3).unwrap(), NonZero::new(3).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_3_3_1() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(3).unwrap(), NonZero::new(3).unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_3_3_2() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(3).unwrap(), NonZero::new(3).unwrap(), 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_3_3_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(3).unwrap(), NonZero::new(3).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_4_1_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(4).unwrap(), NonZero::new(1).unwrap(), 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_4_4_0() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(4).unwrap(), NonZero::new(4).unwrap(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_4_4_1() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(4).unwrap(), NonZero::new(4).unwrap(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_4_4_2() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(4).unwrap(), NonZero::new(4).unwrap(), 2); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_unit_free_regs_tracker_4_4_4() { | ||||
|         test_unit_free_regs_tracker(NonZero::new(4).unwrap(), NonZero::new(4).unwrap(), 4); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue