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
|