hdl.xfrm: add assignment legalizer.
Co-authored-by: Wanda <wanda@phinode.net>
This commit is contained in:
		
							parent
							
								
									10117607a3
								
							
						
					
					
						commit
						78981232d9
					
				|  | @ -17,7 +17,7 @@ __all__ = ["ValueVisitor", "ValueTransformer", | |||
|            "TransformedElaboratable", | ||||
|            "DomainCollector", "DomainRenamer", "DomainLowerer", | ||||
|            "SwitchCleaner", "LHSGroupAnalyzer", "LHSGroupFilter", | ||||
|            "ResetInserter", "EnableInserter"] | ||||
|            "ResetInserter", "EnableInserter", "AssignmentLegalizer"] | ||||
| 
 | ||||
| 
 | ||||
| class ValueVisitor(metaclass=ABCMeta): | ||||
|  | @ -670,3 +670,85 @@ class EnableInserter(_ControlInserter): | |||
|                 if port._domain in self.controls: | ||||
|                     port._en = Mux(self.controls[port._domain], port._en, Const(0, len(port._en))) | ||||
|         return new_fragment | ||||
| 
 | ||||
| 
 | ||||
| class AssignmentLegalizer(FragmentTransformer, StatementTransformer): | ||||
|     """Ensures all assignments in switches have one of the following on the LHS: | ||||
| 
 | ||||
|     - a `Signal` | ||||
|     - a `Slice` with `value` that is a `Signal` | ||||
|     """ | ||||
|     def emit_assign(self, lhs, rhs, lhs_start=0, lhs_stop=None): | ||||
|         if isinstance(lhs, ArrayProxy): | ||||
|             # Lower into a switch. | ||||
|             cases = {} | ||||
|             for idx, val in enumerate(lhs.elems): | ||||
|                 cases[idx] = self.emit_assign(val, rhs, lhs_start, lhs_stop) | ||||
|             return [Switch(lhs.index, cases)] | ||||
|         elif isinstance(lhs, Part): | ||||
|             offset = lhs.offset | ||||
|             width = lhs.width | ||||
|             if lhs_start != 0: | ||||
|                 width -= lhs_start | ||||
|             if lhs_stop is not None: | ||||
|                 width = lhs_stop - lhs_start | ||||
|             cases = {} | ||||
|             lhs_width = len(lhs.value) | ||||
|             for idx in range(lhs_width): | ||||
|                 start = lhs_start + idx * lhs.stride | ||||
|                 if start >= lhs_width: | ||||
|                     break | ||||
|                 stop = min(start + width, lhs_width) | ||||
|                 cases[idx] = self.emit_assign(lhs.value, rhs, start, stop) | ||||
|             return [Switch(offset, cases)] | ||||
|         elif isinstance(lhs, Slice): | ||||
|             part_start = lhs_start + lhs.start | ||||
|             if lhs_stop is not None: | ||||
|                 part_stop = lhs_stop + lhs.start | ||||
|             else: | ||||
|                 part_stop = lhs_start + lhs.stop | ||||
|             return self.emit_assign(lhs.value, rhs, part_start, part_stop) | ||||
|         elif isinstance(lhs, Cat): | ||||
|             # Split into several assignments. | ||||
|             part_stop = 0 | ||||
|             res = [] | ||||
|             if lhs_stop is None: | ||||
|                 lhs_len = len(lhs) - lhs_start | ||||
|             else: | ||||
|                 lhs_len = lhs_stop - lhs_start | ||||
|             if len(rhs) < lhs_len: | ||||
|                 rhs |= Const(0, Shape(lhs_len, signed=rhs.shape().signed)) | ||||
|             for val in lhs.parts: | ||||
|                 part_start = part_stop | ||||
|                 part_len = len(val) | ||||
|                 part_stop = part_start + part_len | ||||
|                 if lhs_start >= part_stop: | ||||
|                     continue | ||||
|                 if lhs_start < part_start: | ||||
|                     part_lhs_start = 0 | ||||
|                     part_rhs_start = part_start - lhs_start | ||||
|                 else: | ||||
|                     part_lhs_start = lhs_start - part_start | ||||
|                     part_rhs_start = 0 | ||||
|                 if lhs_stop is not None and lhs_stop <= part_start: | ||||
|                     continue | ||||
|                 elif lhs_stop is None or lhs_stop >= part_stop: | ||||
|                     part_lhs_stop = None | ||||
|                 else: | ||||
|                     part_lhs_stop = lhs_stop - part_start | ||||
|                 res += self.emit_assign(val, rhs[part_rhs_start:], part_lhs_start, part_lhs_stop) | ||||
|             return res | ||||
|         elif isinstance(lhs, Signal): | ||||
|             # Already ok. | ||||
|             if lhs_start != 0 or lhs_stop is not None: | ||||
|                 return [Assign(lhs[lhs_start:lhs_stop], rhs)] | ||||
|             else: | ||||
|                 return [Assign(lhs, rhs)] | ||||
|         elif isinstance(lhs, Operator): | ||||
|             assert lhs.operator in ('u', 's') | ||||
|             return self.emit_assign(lhs.operands[0], rhs, lhs_start, lhs_stop) | ||||
|         else: | ||||
|             raise TypeError | ||||
| 
 | ||||
|     def on_Assign(self, stmt): | ||||
|         return self.emit_assign(stmt.lhs, stmt.rhs) | ||||
|  |  | |||
|  | @ -547,6 +547,329 @@ class EnableInserterTestCase(FHDLTestCase): | |||
|         """) | ||||
| 
 | ||||
| 
 | ||||
| class AssignmentLegalizerTestCase(FHDLTestCase): | ||||
|     def test_simple(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1.eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((eq (sig s1) (sig s2))) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_slice(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1[2:6].eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((eq (slice (sig s1) 2:6) (sig s2))) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_part(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1.bit_select(s3, 4).eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 0:4) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 1:5) (sig s2))) | ||||
|                 (case 0010 (eq (slice (sig s1) 2:6) (sig s2))) | ||||
|                 (case 0011 (eq (slice (sig s1) 3:7) (sig s2))) | ||||
|                 (case 0100 (eq (slice (sig s1) 4:8) (sig s2))) | ||||
|                 (case 0101 (eq (slice (sig s1) 5:8) (sig s2))) | ||||
|                 (case 0110 (eq (slice (sig s1) 6:8) (sig s2))) | ||||
|                 (case 0111 (eq (slice (sig s1) 7:8) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_part_word(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1.word_select(s3, 4).eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 0:4) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 4:8) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_concat(self): | ||||
|         s1 = Signal(4) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         s4 = Signal(12) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             Cat(s1, s2, s3).eq(s4) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f.add_driver(s3, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ( | ||||
|                 (eq (sig s1) (slice (sig s4) 0:12)) | ||||
|                 (eq (sig s2) (slice (sig s4) 4:12)) | ||||
|                 (eq (sig s3) (slice (sig s4) 8:12)) | ||||
|             ) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_concat_narrow(self): | ||||
|         s1 = Signal(4) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         s4 = Signal(signed(6)) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             Cat(s1, s2, s3).eq(s4) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f.add_driver(s3, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ( | ||||
|                 (eq (sig s1) (slice (| (sig s4) (const 12'sd0)) 0:12)) | ||||
|                 (eq (sig s2) (slice (| (sig s4) (const 12'sd0)) 4:12)) | ||||
|                 (eq (sig s3) (slice (| (sig s4) (const 12'sd0)) 8:12)) | ||||
|             ) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_operator(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(8) | ||||
|         s3 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements("sync", [ | ||||
|             s1.as_signed().eq(s3), | ||||
|             s2.as_unsigned().eq(s3), | ||||
|         ]) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ( | ||||
|                 (eq (sig s1) (sig s3)) | ||||
|                 (eq (sig s2) (sig s3)) | ||||
|             ) | ||||
|         """) | ||||
| 
 | ||||
|     def test_simple_array(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(8) | ||||
|         s3 = Signal(8) | ||||
|         s4 = Signal(8) | ||||
|         s5 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements("sync", [ | ||||
|             Array([s1, s2, s3])[s4].eq(s5), | ||||
|         ]) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f.add_driver(s3, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s4) | ||||
|                 (case 00000000 (eq (sig s1) (sig s5))) | ||||
|                 (case 00000001 (eq (sig s2) (sig s5))) | ||||
|                 (case 00000010 (eq (sig s3) (sig s5))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_slice(self): | ||||
|         s1 = Signal(12) | ||||
|         s2 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1[1:11][2:6].eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((eq (slice (sig s1) 3:7) (sig s2))) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_concat(self): | ||||
|         s1 = Signal(4) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         s4 = Signal(4) | ||||
|         s5 = Signal(4) | ||||
|         s6 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             Cat(s1, s2, s3, s4, s5)[5:14].eq(s6) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f.add_driver(s3, "sync") | ||||
|         f.add_driver(s4, "sync") | ||||
|         f.add_driver(s5, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ( | ||||
|                 (eq (slice (sig s2) 1:4) (slice (| (sig s6) (const 9'd0)) 0:9)) | ||||
|                 (eq (sig s3)             (slice (| (sig s6) (const 9'd0)) 3:9)) | ||||
|                 (eq (slice (sig s4) 0:2) (slice (| (sig s6) (const 9'd0)) 7:9)) | ||||
|             ) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_part(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1.bit_select(s3, 4)[1:3].eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 1:3) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 2:4) (sig s2))) | ||||
|                 (case 0010 (eq (slice (sig s1) 3:5) (sig s2))) | ||||
|                 (case 0011 (eq (slice (sig s1) 4:6) (sig s2))) | ||||
|                 (case 0100 (eq (slice (sig s1) 5:7) (sig s2))) | ||||
|                 (case 0101 (eq (slice (sig s1) 6:8) (sig s2))) | ||||
|                 (case 0110 (eq (slice (sig s1) 7:8) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_part_word(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1.word_select(s3, 4)[1:3].eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 1:3) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 5:7) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_array(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(8) | ||||
|         s3 = Signal(8) | ||||
|         s4 = Signal(8) | ||||
|         s5 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements("sync", [ | ||||
|             Array([s1, s2, s3])[s4][2:7].eq(s5), | ||||
|         ]) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f.add_driver(s3, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s4) | ||||
|                 (case 00000000 (eq (slice (sig s1) 2:7) (sig s5))) | ||||
|                 (case 00000001 (eq (slice (sig s2) 2:7) (sig s5))) | ||||
|                 (case 00000010 (eq (slice (sig s3) 2:7) (sig s5))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_part_slice(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1[1:7].bit_select(s3, 4).eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 1:5) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 2:6) (sig s2))) | ||||
|                 (case 0010 (eq (slice (sig s1) 3:7) (sig s2))) | ||||
|                 (case 0011 (eq (slice (sig s1) 4:7) (sig s2))) | ||||
|                 (case 0100 (eq (slice (sig s1) 5:7) (sig s2))) | ||||
|                 (case 0101 (eq (slice (sig s1) 6:7) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
|     def test_sliced_part_slice(self): | ||||
|         s1 = Signal(12) | ||||
|         s2 = Signal(4) | ||||
|         s3 = Signal(4) | ||||
|         f = Fragment() | ||||
|         f.add_statements( | ||||
|             "sync", | ||||
|             s1[3:9].bit_select(s3, 4)[1:3].eq(s2) | ||||
|         ) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ((switch (sig s3) | ||||
|                 (case 0000 (eq (slice (sig s1) 4:6) (sig s2))) | ||||
|                 (case 0001 (eq (slice (sig s1) 5:7) (sig s2))) | ||||
|                 (case 0010 (eq (slice (sig s1) 6:8) (sig s2))) | ||||
|                 (case 0011 (eq (slice (sig s1) 7:9) (sig s2))) | ||||
|                 (case 0100 (eq (slice (sig s1) 8:9) (sig s2))) | ||||
|             )) | ||||
|         """) | ||||
| 
 | ||||
| 
 | ||||
|     def test_sliced_operator(self): | ||||
|         s1 = Signal(8) | ||||
|         s2 = Signal(8) | ||||
|         s3 = Signal(8) | ||||
|         f = Fragment() | ||||
|         f.add_statements("sync", [ | ||||
|             s1.as_signed()[2:7].eq(s3), | ||||
|             s2.as_unsigned()[2:7].eq(s3), | ||||
|         ]) | ||||
|         f.add_driver(s1, "sync") | ||||
|         f.add_driver(s2, "sync") | ||||
|         f = AssignmentLegalizer()(f) | ||||
|         self.assertRepr(f.statements["sync"], """ | ||||
|             ( | ||||
|                 (eq (slice (sig s1) 2:7) (sig s3)) | ||||
|                 (eq (slice (sig s2) 2:7) (sig s3)) | ||||
|             ) | ||||
|         """) | ||||
| 
 | ||||
| 
 | ||||
| class _MockElaboratable(Elaboratable): | ||||
|     def __init__(self): | ||||
|         self.s1 = Signal() | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Catherine
						Catherine