Skip to content

Commit 6d674d2

Browse files
author
Oron Port
committed
big documentation update through generation
1 parent d0f35c8 commit 6d674d2

File tree

4 files changed

+1279
-10
lines changed

4 files changed

+1279
-10
lines changed
Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
# Conditionals
2+
3+
DFHDL supports two main types of conditional constructs: `if` expressions and `match` expressions. Both can be used for control flow and value selection in hardware designs.
4+
5+
## If Expressions
6+
7+
### Basic Syntax
8+
9+
```scala
10+
if (condition) consequent
11+
else alternative
12+
```
13+
14+
- `condition` must be a DFHDL `Boolean` or `Bit` value
15+
- `consequent` and `alternative` are the code blocks executed based on the condition
16+
- The `else` clause is optional
17+
18+
### Examples
19+
20+
```scala
21+
// Basic if-else
22+
if (enable)
23+
output := input
24+
else
25+
output := 0
26+
27+
// Chained if-else
28+
if (state == State.Idle)
29+
output := 0
30+
else if (state == State.Active)
31+
output := input
32+
else
33+
output := lastValue
34+
35+
// If without else
36+
if (reset)
37+
counter := 0
38+
```
39+
40+
### Rules
41+
42+
1. **Type Consistency**: Both branches must produce compatible types if used as an expression
43+
2. **Scope**: Variables declared inside if blocks are only visible within that block
44+
3. **Hardware Generation**: Each branch generates hardware - both paths exist in parallel
45+
4. **Constant Conditions**: If conditions are constant, unused branches may be optimized out during elaboration
46+
47+
## Match Expressions
48+
49+
Match expressions provide pattern matching capabilities similar to Scala's match expressions but optimized for hardware description.
50+
51+
### Basic Syntax
52+
53+
```scala
54+
value match
55+
case pattern1 => expression1
56+
case pattern2 => expression2
57+
case _ => defaultExpression
58+
```
59+
60+
### Pattern Types
61+
62+
1. **Literal Patterns**:
63+
```scala
64+
x match
65+
case 0 => y := 0
66+
case 1 => y := 1
67+
case _ => y := x
68+
```
69+
70+
2. **Multiple Values**:
71+
```scala
72+
x match
73+
case 1 | 2 | 4 => y := 0
74+
case 3 | 5 | 6 => y := 1
75+
case _ => y := 2
76+
```
77+
78+
3. **Struct Patterns**:
79+
```scala
80+
pixel match
81+
case Pixel(0, y) => output := y
82+
case Pixel(x, 0) => output := x
83+
case Pixel(x, y) => output := x + y
84+
```
85+
86+
4. **Guard Patterns**:
87+
```scala
88+
x match
89+
case n if n > 10 => y := 1
90+
case n if n < 0 => y := 0
91+
case _ => y := x
92+
```
93+
94+
5. **Bit Patterns with Wildcards**:
95+
```scala
96+
bits match
97+
case b"1??0" => y := 1
98+
case b"0??1" => y := 0
99+
case _ => y := x
100+
```
101+
102+
### Match to If Conversion
103+
104+
During compilation, match expressions are typically converted to if-else chains for hardware implementation. For example:
105+
106+
```scala
107+
// Original match
108+
x match
109+
case 22 => y := 0
110+
case 11 | 33 | 44 => y := 1
111+
case _ if x == 55 => y := 2
112+
case _ => y := 3
113+
114+
// Converts to
115+
if (x == sd"16'22")
116+
y := sd"16'0"
117+
else if ((x == sd"16'11") || (x == sd"16'33") || (x == sd"16'44"))
118+
y := sd"16'1"
119+
else if (x == sd"16'55")
120+
y := sd"16'2"
121+
else
122+
y := sd"16'3"
123+
```
124+
125+
### Complex Selectors
126+
127+
When using expressions as match selectors, a temporary variable is created:
128+
129+
```scala
130+
// Original
131+
(x + 1) match
132+
case 22 => y := 0
133+
case _ => y := 1
134+
135+
// Converts to
136+
val match_sel = x + sd"16'1"
137+
if (match_sel == sd"16'22")
138+
y := sd"16'0"
139+
else
140+
y := sd"16'1"
141+
```
142+
143+
### Rules
144+
145+
1. **Exhaustiveness**: Match expressions must cover all possible cases
146+
2. **Pattern Order**: Patterns are evaluated in order, first match wins
147+
3. **Type Safety**: All case branches must produce compatible types if used as an expression
148+
4. **Hardware Implementation**:
149+
- Converts to if-else chains for most cases
150+
- Optimizes bit pattern matching into efficient comparisons
151+
- Extracts struct fields into temporary variables when needed
152+
153+
### Best Practices
154+
155+
1. **Use Match for Multi-Way Branching**: When dealing with multiple cases, match is often clearer than nested if-else
156+
2. **State Machines**: Match expressions are ideal for state machine implementations
157+
3. **Pattern Priority**: Put more specific patterns before general ones
158+
4. **Wildcards**: Use `_` for catch-all cases
159+
5. **Guards**: Use sparingly as they may generate more complex hardware
160+
161+
## Hardware Implications
162+
163+
Both `if` and `match` expressions generate multiplexer circuits in hardware. The choice between them should consider:
164+
165+
1. **Readability**: Match expressions are often clearer for multiple cases
166+
2. **Hardware Efficiency**: Simple if-else may generate simpler hardware
167+
3. **Timing**: Complex conditions may impact critical paths
168+
4. **Resource Usage**: Each branch generates hardware, even if mutually exclusive
169+
170+
## Examples
171+
172+
### State Machine
173+
```scala
174+
class StateMachine extends RTDesign:
175+
val state = State <> VAR.REG init State.Idle
176+
177+
state match
178+
case State.Idle =>
179+
if (start) state := State.Active
180+
case State.Active =>
181+
if (done) state := State.Idle
182+
```
183+
184+
### Decoder
185+
```scala
186+
class Decoder extends DFDesign:
187+
val input = UInt(4) <> IN
188+
val output = Bits(16) <> OUT
189+
190+
output := input match
191+
case 0 => b"0001"
192+
case 1 => b"0010"
193+
case 2 => b"0100"
194+
case _ => b"0000"
195+
```
196+
197+
### Complex Pattern Matching
198+
```scala
199+
class PixelProcessor extends DFDesign:
200+
val pixel = Pixel <> IN
201+
val result = UInt(8) <> OUT
202+
203+
result := pixel match
204+
case Pixel(x, y) if x == y => x
205+
case Pixel(x, 0) => x * 2
206+
case Pixel(0, y) => y * 2
207+
case Pixel(x, y) => (x + y) / 2
208+
```
209+
210+
### Usage Modes
211+
212+
DFHDL conditionals can be used in two ways: as statements and as expressions.
213+
214+
#### Statement Usage
215+
When used as statements, `if` and `match` are used for their side effects (like assignments) rather than producing a value:
216+
217+
```scala
218+
// If statement
219+
if (i) x := 1
220+
else if (!i) x := 2
221+
222+
// Match statement
223+
x match
224+
case 77 | 11 => x := 1
225+
case _ =>
226+
x := 3
227+
x := 4
228+
```
229+
230+
#### Expression Usage
231+
When used as expressions, `if` and `match` produce values that can be assigned or used in computations:
232+
233+
```scala
234+
// If expression
235+
val res: UInt[8] <> VAL =
236+
if (i) 1
237+
else if (!i) x.bits.uint
238+
else 2
239+
240+
// Match expression
241+
val res: UInt[8] <> VAL =
242+
x match
243+
case 0 | 1 | 2 | 3 => 77
244+
case _ => 22
245+
```
246+
247+
Key differences:
248+
1. **Type Requirements**:
249+
- Statements: No return type consistency required between branches
250+
- Expressions: All branches must return compatible types
251+
252+
2. **Assignment Context**:
253+
- Statements: Can contain multiple assignments per branch
254+
- Expressions: Must evaluate to a single value
255+
256+
3. **Variable Declaration**:
257+
```scala
258+
// Direct assignment from expression
259+
val res2 = UInt(8) <> VAR
260+
res2 := (if (i) 1 else if (!i) x.bits.uint else 2)
261+
262+
// Using statement form for multiple assignments
263+
if (i)
264+
x := 1
265+
y := 2
266+
else
267+
x := 3
268+
y := 4
269+
```
270+
271+
### Expression vs. Statement Form
272+
273+
DFHDL conditionals can be written in two equivalent forms that generate different hardware structures:
274+
275+
#### Expression Form
276+
In expression form, the conditional is part of the right-hand side of an assignment:
277+
278+
```scala
279+
// If as expression
280+
output := (if (cond) a else b)
281+
282+
// Match as expression
283+
output := (sel match
284+
case A => a
285+
case B => b)
286+
```
287+
288+
#### Statement Form
289+
In statement form, the assignments are inside the conditional branches:
290+
291+
```scala
292+
// If as statement
293+
if (cond)
294+
output := a
295+
else
296+
output := b
297+
298+
// Match as statement
299+
sel match
300+
case A => output := a
301+
case B => output := b
302+
```
303+
304+
#### Key Differences
305+
306+
1. **Hardware Structure**:
307+
- Expression form: Generates a single multiplexer with the condition as select
308+
- Statement form: Generates multiple assignments with enable signals derived from conditions
309+
310+
2. **Code Organization**:
311+
- Expression form: Better when the only difference between branches is the value being assigned
312+
- Statement form: Better when branches perform multiple operations or have different side effects
313+
314+
3. **Timing Implications**:
315+
- Expression form: All values are computed in parallel, then selected
316+
- Statement form: Each branch's logic is gated by its condition
317+
318+
4. **Common Use Cases**:
319+
```scala
320+
// Expression form - simple value selection
321+
val result = UInt(8) <> OUT
322+
result := (if (valid) computed_value else 0)
323+
324+
// Statement form - multiple operations
325+
if (valid)
326+
result := computed_value
327+
status := VALID
328+
counter := counter + 1
329+
else
330+
result := 0
331+
status := INVALID
332+
```
333+
334+
Choose the form that best matches your design's requirements:
335+
- Use expression form for simple value selection
336+
- Use statement form for complex control flow or multiple operations per branch
337+
338+
339+
---
340+
341+

0 commit comments

Comments
 (0)