File size: 2,741 Bytes
2b4ae64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# Task 10: IEEE 754 FP16 Adder (Hard)

## Objective

Implement a combinational IEEE 754 half-precision (FP16) floating-point adder.
FP16 arithmetic is the compute primitive of every modern GPU tensor core and
AI accelerator — getting it right is non-trivial and formally verifiable.

## Interface

```verilog
module fp16_adder (
    input  wire [15:0] a,
    input  wire [15:0] b,
    output wire [15:0] result
);
```

## FP16 Format (IEEE 754-2008)

```
Bit 15  : sign       (s)
Bits 14:10 : exponent (e, biased with bias=15)
Bits  9:0  : mantissa (m, implicit leading 1 for normal numbers)

Value = (-1)^s × 2^(e−15) × 1.m    for normal numbers (e = 1..30)
Value = 0                            for e = 0, m = 0  (zero)
```

## Scope (What You Must Handle)

| Case                   | Requirement                                  |
|------------------------|----------------------------------------------|
| Normal + Normal        | Correct result, normalized                   |
| x + 0  or  0 + x       | Return x                                     |
| x + (−x) (cancellation)| Return +0.0 (`16'h0000`)                    |
| Overflow to infinity   | Return `16'h7C00` (+Inf) or `16'hFC00` (−Inf)|
| NaN input (e=31,m≠0)   | Propagate: return `16'h7E00`                 |
| Infinity input (e=31,m=0)| Propagate or handle ∞±∞ as NaN             |

**Rounding**: truncate (round toward zero). Round-to-nearest is not required but earns full area score.

## Algorithm

```
1.  Extract fields: sign, exp (5-bit), mantissa (10-bit)
2.  Prepend implicit 1: full_m = {1, mantissa}  (11 bits; 0 for zero/subnormal)
3.  If |a| < |b|: swap so that |a| >= |b|
4.  Compute alignment shift d = exp_a − exp_b  (≥ 0 after swap)
5.  Shift full_m_b right by d (with 3 guard bits for rounding)
6.  If signs equal:  sum_m = full_m_a + shifted_m_b
    If signs differ: sum_m = full_m_a − shifted_m_b
7.  Normalize: count leading zeros in sum_m, left-shift, adjust exponent
8.  Handle exponent overflow → ±Inf
9.  Pack result: {sign_result, exp_result[4:0], sum_m[9:0]}
```

## Scoring

- Correct compilation: 5%
- Passing simulation tests (normal numbers + zero + special cases): 60%
- Formal verification (SymbiYosys, if available): 15%
- Area efficiency vs reference: 20%

## Useful Constants

```verilog
localparam BIAS    = 15;
localparam INF     = 16'h7C00;
localparam NEG_INF = 16'hFC00;
localparam QNAN    = 16'h7E00;
```

## Hint: Alignment and Normalization

```verilog
// Extended mantissa with guard bits
wire [13:0] m_b_shifted = {1'b1, man_b, 3'b0} >> d;   // 14 bits: 1 hidden + 10 + 3 guard

// After subtraction, find first 1 in result (clz):
// Use a priority encoder or a generate loop to count leading zeros
```