File size: 5,061 Bytes
2166d7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
 * Processing Element (PE) for Systolic Array
 * 
 * Implements a single multiply-accumulate (MAC) unit with pipeline registers.
 * 
 * Functionality:
 * - Multiplies 8-bit input A and B (Q4.4 fixed-point)
 * - Accumulates result with incoming partial sum C_in
 * - Broadcasts A to right neighbor (matrix_A_out)
 * - Broadcasts C (accumulation) to bottom neighbor (matrix_C_out)
 * 
 * Pipeline Stages:
 * Stage 1: Input capture (A, B, C_in, valid_in)
 * Stage 2: Multiply (8x8 -> 16-bit product)
 * Stage 3: Accumulate (16-bit + 16-bit -> 16-bit)
 * 
 * Timing: 3-cycle latency from valid_in to valid_out
 */

module processing_element #(
    parameter DATA_WIDTH = 8,      // Input data width (Q4.4 = 8-bit)
    parameter ACC_WIDTH = 16,      // Accumulator width (to avoid overflow)
    parameter INIT_VALUE = 16'b0   // Initial accumulator value
) (
    input  logic                     clk,
    input  logic                     rst_n,
    
    // Input port (from left and top)
    input  logic                     valid_in,
    input  logic [DATA_WIDTH-1:0]    matrix_A_in,      // Data from left (Q)
    input  logic [DATA_WIDTH-1:0]    matrix_B_in,      // Data from top (K^T)
    input  logic [ACC_WIDTH-1:0]     matrix_C_in,      // Partial sum from top
    
    // Output port (to right and bottom)
    output logic                     valid_out,
    output logic [DATA_WIDTH-1:0]    matrix_A_out,     // Forward A to right
    output logic [ACC_WIDTH-1:0]     matrix_C_out      // Forward sum to bottom
);

    // =====================================================================
    // Stage 1: Input Registers (capture A, B, C_in on valid_in)
    // =====================================================================
    logic                     valid_s1;
    logic [DATA_WIDTH-1:0]    A_s1;
    logic [DATA_WIDTH-1:0]    B_s1;
    logic [ACC_WIDTH-1:0]     C_s1;
    
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_s1 <= 1'b0;
            A_s1     <= {DATA_WIDTH{1'b0}};
            B_s1     <= {DATA_WIDTH{1'b0}};
            C_s1     <= {ACC_WIDTH{1'b0}};
        end else begin
            valid_s1 <= valid_in;
            A_s1     <= matrix_A_in;
            B_s1     <= matrix_B_in;
            C_s1     <= matrix_C_in;
        end
    end
    
    // =====================================================================
    // Stage 2: Multiply (8-bit × 8-bit -> 16-bit)
    // =====================================================================
    // Using signed multiplication for Q4.4 fixed-point
    // Result: 16-bit signed product
    
    logic                     valid_s2;
    logic [ACC_WIDTH-1:0]     product_s2;    // 8-bit × 8-bit = 16-bit
    logic [ACC_WIDTH-1:0]     C_s2;
    
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_s2   <= 1'b0;
            product_s2 <= {ACC_WIDTH{1'b0}};
            C_s2       <= {ACC_WIDTH{1'b0}};
        end else begin
            valid_s2   <= valid_s1;
            // Signed multiplication: 8-bit × 8-bit -> 16-bit
            product_s2 <= $signed(A_s1) * $signed(B_s1);
            C_s2       <= C_s1;
        end
    end
    
    // =====================================================================
    // Stage 3: Accumulate (product + C_s2 -> 16-bit result with saturation)
    // =====================================================================
    logic                     valid_s3;
    logic [ACC_WIDTH-1:0]     accumulator;
    
    // Saturation logic: prevent overflow in accumulation
    logic [ACC_WIDTH:0]       sum_with_overflow;
    
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_s3   <= 1'b0;
            accumulator <= INIT_VALUE;
        end else begin
            valid_s3 <= valid_s2;
            
            // Add product to partial sum with overflow detection
            sum_with_overflow = $signed(product_s2) + $signed(C_s2);
            
            // Saturate to 16-bit range if overflow occurs
            if (sum_with_overflow[ACC_WIDTH] != sum_with_overflow[ACC_WIDTH-1]) begin
                // Overflow detected: saturate
                if (sum_with_overflow[ACC_WIDTH]) begin
                    accumulator <= {1'b0, {(ACC_WIDTH-1){1'b1}}};  // Max positive
                end else begin
                    accumulator <= {1'b1, {(ACC_WIDTH-1){1'b0}}};  // Min negative
                end
            end else begin
                // No overflow: store result
                accumulator <= sum_with_overflow[ACC_WIDTH-1:0];
            end
        end
    end
    
    // =====================================================================
    // Output Assignment
    // =====================================================================
    assign valid_out    = valid_s3;
    assign matrix_A_out = A_s1;        // Forward A to right neighbor
    assign matrix_C_out = accumulator;  // Forward accumulation to bottom neighbor

endmodule : processing_element